Implement SSLKEYLOGFILE for OpenSSL.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_entry_screenshot_manager.cc
blob9b3f02078bee79b8164891009e04a507c4757130
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 screenshot_factory_(this),
68 min_screenshot_interval_ms_(kMinScreenshotIntervalMS) {
71 NavigationEntryScreenshotManager::~NavigationEntryScreenshotManager() {
74 void NavigationEntryScreenshotManager::TakeScreenshot() {
75 static bool overscroll_enabled = base::CommandLine::ForCurrentProcess()->
76 GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0";
77 if (!overscroll_enabled)
78 return;
80 NavigationEntryImpl* entry =
81 NavigationEntryImpl::FromNavigationEntry(owner_->GetLastCommittedEntry());
82 if (!entry)
83 return;
85 if (!owner_->delegate()->CanOverscrollContent())
86 return;
88 RenderViewHost* render_view_host =
89 owner_->delegate()->GetRenderViewHost();
90 content::RenderWidgetHostView* view = render_view_host->GetView();
91 if (!view)
92 return;
94 // Make sure screenshots aren't taken too frequently.
95 base::Time now = base::Time::Now();
96 if (now - last_screenshot_time_ <
97 base::TimeDelta::FromMilliseconds(min_screenshot_interval_ms_)) {
98 return;
101 last_screenshot_time_ = now;
103 TakeScreenshotImpl(render_view_host, entry);
106 // Implemented here and not in NavigationEntry because this manager keeps track
107 // of the total number of screen shots across all entries.
108 void NavigationEntryScreenshotManager::ClearAllScreenshots() {
109 int count = owner_->GetEntryCount();
110 for (int i = 0; i < count; ++i) {
111 ClearScreenshot(NavigationEntryImpl::FromNavigationEntry(
112 owner_->GetEntryAtIndex(i)));
114 DCHECK_EQ(GetScreenshotCount(), 0);
117 void NavigationEntryScreenshotManager::TakeScreenshotImpl(
118 RenderViewHost* host,
119 NavigationEntryImpl* entry) {
120 DCHECK(host && host->GetView());
121 DCHECK(entry);
122 host->CopyFromBackingStore(
123 gfx::Rect(),
124 host->GetView()->GetViewBounds().size(),
125 base::Bind(&NavigationEntryScreenshotManager::OnScreenshotTaken,
126 screenshot_factory_.GetWeakPtr(),
127 entry->GetUniqueID()),
128 kAlpha_8_SkColorType);
131 void NavigationEntryScreenshotManager::SetMinScreenshotIntervalMS(
132 int interval_ms) {
133 DCHECK_GE(interval_ms, 0);
134 min_screenshot_interval_ms_ = interval_ms;
137 void NavigationEntryScreenshotManager::OnScreenshotTaken(int unique_id,
138 bool success,
139 const SkBitmap& bitmap) {
140 NavigationEntryImpl* entry = NULL;
141 int entry_count = owner_->GetEntryCount();
142 for (int i = 0; i < entry_count; ++i) {
143 NavigationEntry* iter = owner_->GetEntryAtIndex(i);
144 if (iter->GetUniqueID() == unique_id) {
145 entry = NavigationEntryImpl::FromNavigationEntry(iter);
146 break;
150 if (!entry) {
151 LOG(ERROR) << "Invalid entry with unique id: " << unique_id;
152 return;
155 if (!success || bitmap.empty() || bitmap.isNull()) {
156 if (!ClearScreenshot(entry))
157 OnScreenshotSet(entry);
158 return;
161 scoped_refptr<ScreenshotData> screenshot = new ScreenshotData();
162 screenshot->EncodeScreenshot(
163 bitmap,
164 base::Bind(&NavigationEntryScreenshotManager::OnScreenshotEncodeComplete,
165 screenshot_factory_.GetWeakPtr(),
166 unique_id,
167 screenshot));
170 int NavigationEntryScreenshotManager::GetScreenshotCount() const {
171 int screenshot_count = 0;
172 int entry_count = owner_->GetEntryCount();
173 for (int i = 0; i < entry_count; ++i) {
174 NavigationEntryImpl* entry =
175 NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(i));
176 if (entry->screenshot().get())
177 screenshot_count++;
179 return screenshot_count;
182 void NavigationEntryScreenshotManager::OnScreenshotEncodeComplete(
183 int unique_id,
184 scoped_refptr<ScreenshotData> screenshot) {
185 NavigationEntryImpl* entry = NULL;
186 int entry_count = owner_->GetEntryCount();
187 for (int i = 0; i < entry_count; ++i) {
188 NavigationEntry* iter = owner_->GetEntryAtIndex(i);
189 if (iter->GetUniqueID() == unique_id) {
190 entry = NavigationEntryImpl::FromNavigationEntry(iter);
191 break;
194 if (!entry)
195 return;
196 entry->SetScreenshotPNGData(screenshot->data());
197 OnScreenshotSet(entry);
200 void NavigationEntryScreenshotManager::OnScreenshotSet(
201 NavigationEntryImpl* entry) {
202 if (entry->screenshot().get())
203 PurgeScreenshotsIfNecessary();
206 bool NavigationEntryScreenshotManager::ClearScreenshot(
207 NavigationEntryImpl* entry) {
208 if (!entry->screenshot().get())
209 return false;
211 entry->SetScreenshotPNGData(NULL);
212 return true;
215 void NavigationEntryScreenshotManager::PurgeScreenshotsIfNecessary() {
216 // Allow only a certain number of entries to keep screenshots.
217 const int kMaxScreenshots = 10;
218 int screenshot_count = GetScreenshotCount();
219 if (screenshot_count < kMaxScreenshots)
220 return;
222 const int current = owner_->GetCurrentEntryIndex();
223 const int num_entries = owner_->GetEntryCount();
224 int available_slots = kMaxScreenshots;
225 if (NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(current))
226 ->screenshot().get()) {
227 --available_slots;
230 // Keep screenshots closer to the current navigation entry, and purge the ones
231 // that are farther away from it. So in each step, look at the entries at
232 // each offset on both the back and forward history, and start counting them
233 // to make sure that the correct number of screenshots are kept in memory.
234 // Note that it is possible for some entries to be missing screenshots (e.g.
235 // when taking the screenshot failed for some reason). So there may be a state
236 // where there are a lot of entries in the back history, but none of them has
237 // any screenshot. In such cases, keep the screenshots for |kMaxScreenshots|
238 // entries in the forward history list.
239 int back = current - 1;
240 int forward = current + 1;
241 while (available_slots > 0 && (back >= 0 || forward < num_entries)) {
242 if (back >= 0) {
243 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
244 owner_->GetEntryAtIndex(back));
245 if (entry->screenshot().get())
246 --available_slots;
247 --back;
250 if (available_slots > 0 && forward < num_entries) {
251 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
252 owner_->GetEntryAtIndex(forward));
253 if (entry->screenshot().get())
254 --available_slots;
255 ++forward;
259 // Purge any screenshot at |back| or lower indices, and |forward| or higher
260 // indices.
261 while (screenshot_count > kMaxScreenshots && back >= 0) {
262 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
263 owner_->GetEntryAtIndex(back));
264 if (ClearScreenshot(entry))
265 --screenshot_count;
266 --back;
269 while (screenshot_count > kMaxScreenshots && forward < num_entries) {
270 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
271 owner_->GetEntryAtIndex(forward));
272 if (ClearScreenshot(entry))
273 --screenshot_count;
274 ++forward;
276 CHECK_GE(screenshot_count, 0);
277 CHECK_LE(screenshot_count, kMaxScreenshots);
280 } // namespace content