Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / frame_host / navigation_entry_screenshot_manager.cc
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.
4
5 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
6
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 "third_party/skia/include/core/SkCanvas.h"
16 #include "third_party/skia/include/core/SkPaint.h"
17 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
18 #include "ui/gfx/codec/png_codec.h"
19
20 namespace {
21
22 // Minimum delay between taking screenshots.
23 const int kMinScreenshotIntervalMS = 1000;
24
25 }
26
27 namespace content {
28
29 // Converts SkBitmap to grayscale and encodes to PNG data in a worker thread.
30 class ScreenshotData : public base::RefCountedThreadSafe<ScreenshotData> {
31  public:
32   ScreenshotData() {
33   }
34
35   void EncodeScreenshot(const SkBitmap& bitmap, base::Closure callback) {
36     if (!base::WorkerPool::PostTaskAndReply(FROM_HERE,
37             base::Bind(&ScreenshotData::EncodeOnWorker,
38                        this,
39                        bitmap),
40             callback,
41             true)) {
42       callback.Run();
43     }
44   }
45
46   scoped_refptr<base::RefCountedBytes> data() const { return data_; }
47
48  private:
49   friend class base::RefCountedThreadSafe<ScreenshotData>;
50   virtual ~ScreenshotData() {
51   }
52
53   void EncodeOnWorker(const SkBitmap& bitmap) {
54     std::vector<unsigned char> data;
55     // Paint |bitmap| to a kA8_Config SkBitmap
56     SkBitmap a8Bitmap;
57     a8Bitmap.setConfig(SkBitmap::kA8_Config,
58                        bitmap.width(),
59                        bitmap.height(),
60                        0);
61     a8Bitmap.allocPixels();
62     SkCanvas canvas(a8Bitmap);
63     SkPaint paint;
64     SkColorFilter* filter = SkLumaColorFilter::Create();
65     paint.setColorFilter(filter);
66     filter->unref();
67     canvas.drawBitmap(bitmap, SK_Scalar1, SK_Scalar1, &paint);
68     // Encode the a8Bitmap to grayscale PNG treating alpha as color intensity
69     if (gfx::PNGCodec::EncodeA8SkBitmap(a8Bitmap, &data))
70       data_ = new base::RefCountedBytes(data);
71   }
72
73   scoped_refptr<base::RefCountedBytes> data_;
74
75   DISALLOW_COPY_AND_ASSIGN(ScreenshotData);
76 };
77
78 NavigationEntryScreenshotManager::NavigationEntryScreenshotManager(
79     NavigationControllerImpl* owner)
80     : owner_(owner),
81       screenshot_factory_(this),
82       min_screenshot_interval_ms_(kMinScreenshotIntervalMS) {
83 }
84
85 NavigationEntryScreenshotManager::~NavigationEntryScreenshotManager() {
86 }
87
88 void NavigationEntryScreenshotManager::TakeScreenshot() {
89   static bool overscroll_enabled = CommandLine::ForCurrentProcess()->
90       GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0";
91   if (!overscroll_enabled)
92     return;
93
94   NavigationEntryImpl* entry =
95       NavigationEntryImpl::FromNavigationEntry(owner_->GetLastCommittedEntry());
96   if (!entry)
97     return;
98
99   RenderViewHost* render_view_host =
100       owner_->delegate()->GetRenderViewHost();
101   if (!static_cast<RenderViewHostImpl*>
102       (render_view_host)->overscroll_controller()) {
103     return;
104   }
105   content::RenderWidgetHostView* view = render_view_host->GetView();
106   if (!view)
107     return;
108
109   // Make sure screenshots aren't taken too frequently.
110   base::Time now = base::Time::Now();
111   if (now - last_screenshot_time_ <
112           base::TimeDelta::FromMilliseconds(min_screenshot_interval_ms_)) {
113     return;
114   }
115
116   last_screenshot_time_ = now;
117
118   TakeScreenshotImpl(render_view_host, entry);
119 }
120
121 // Implemented here and not in NavigationEntry because this manager keeps track
122 // of the total number of screen shots across all entries.
123 void NavigationEntryScreenshotManager::ClearAllScreenshots() {
124   int count = owner_->GetEntryCount();
125   for (int i = 0; i < count; ++i) {
126     ClearScreenshot(NavigationEntryImpl::FromNavigationEntry(
127         owner_->GetEntryAtIndex(i)));
128   }
129   DCHECK_EQ(GetScreenshotCount(), 0);
130 }
131
132 void NavigationEntryScreenshotManager::TakeScreenshotImpl(
133     RenderViewHost* host,
134     NavigationEntryImpl* entry) {
135   DCHECK(host && host->GetView());
136   DCHECK(entry);
137   host->CopyFromBackingStore(gfx::Rect(),
138       host->GetView()->GetViewBounds().size(),
139       base::Bind(&NavigationEntryScreenshotManager::OnScreenshotTaken,
140                  screenshot_factory_.GetWeakPtr(),
141                  entry->GetUniqueID()));
142 }
143
144 void NavigationEntryScreenshotManager::SetMinScreenshotIntervalMS(
145     int interval_ms) {
146   DCHECK_GE(interval_ms, 0);
147   min_screenshot_interval_ms_ = interval_ms;
148 }
149
150 void NavigationEntryScreenshotManager::OnScreenshotTaken(int unique_id,
151                                                      bool success,
152                                                      const SkBitmap& bitmap) {
153   NavigationEntryImpl* entry = NULL;
154   int entry_count = owner_->GetEntryCount();
155   for (int i = 0; i < entry_count; ++i) {
156     NavigationEntry* iter = owner_->GetEntryAtIndex(i);
157     if (iter->GetUniqueID() == unique_id) {
158       entry = NavigationEntryImpl::FromNavigationEntry(iter);
159       break;
160     }
161   }
162
163   if (!entry) {
164     LOG(ERROR) << "Invalid entry with unique id: " << unique_id;
165     return;
166   }
167
168   if (!success || bitmap.empty() || bitmap.isNull()) {
169     if (!ClearScreenshot(entry))
170       OnScreenshotSet(entry);
171     return;
172   }
173
174   scoped_refptr<ScreenshotData> screenshot = new ScreenshotData();
175   screenshot->EncodeScreenshot(
176       bitmap,
177       base::Bind(&NavigationEntryScreenshotManager::OnScreenshotEncodeComplete,
178                  screenshot_factory_.GetWeakPtr(),
179                  unique_id,
180                  screenshot));
181 }
182
183 int NavigationEntryScreenshotManager::GetScreenshotCount() const {
184   int screenshot_count = 0;
185   int entry_count = owner_->GetEntryCount();
186   for (int i = 0; i < entry_count; ++i) {
187     NavigationEntryImpl* entry =
188         NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(i));
189     if (entry->screenshot().get())
190       screenshot_count++;
191   }
192   return screenshot_count;
193 }
194
195 void NavigationEntryScreenshotManager::OnScreenshotEncodeComplete(
196     int unique_id,
197     scoped_refptr<ScreenshotData> screenshot) {
198   NavigationEntryImpl* entry = NULL;
199   int entry_count = owner_->GetEntryCount();
200   for (int i = 0; i < entry_count; ++i) {
201     NavigationEntry* iter = owner_->GetEntryAtIndex(i);
202     if (iter->GetUniqueID() == unique_id) {
203       entry = NavigationEntryImpl::FromNavigationEntry(iter);
204       break;
205     }
206   }
207   if (!entry)
208     return;
209   entry->SetScreenshotPNGData(screenshot->data());
210   OnScreenshotSet(entry);
211 }
212
213 void NavigationEntryScreenshotManager::OnScreenshotSet(
214     NavigationEntryImpl* entry) {
215   if (entry->screenshot().get())
216     PurgeScreenshotsIfNecessary();
217 }
218
219 bool NavigationEntryScreenshotManager::ClearScreenshot(
220     NavigationEntryImpl* entry) {
221   if (!entry->screenshot().get())
222     return false;
223
224   entry->SetScreenshotPNGData(NULL);
225   return true;
226 }
227
228 void NavigationEntryScreenshotManager::PurgeScreenshotsIfNecessary() {
229   // Allow only a certain number of entries to keep screenshots.
230   const int kMaxScreenshots = 10;
231   int screenshot_count = GetScreenshotCount();
232   if (screenshot_count < kMaxScreenshots)
233     return;
234
235   const int current = owner_->GetCurrentEntryIndex();
236   const int num_entries = owner_->GetEntryCount();
237   int available_slots = kMaxScreenshots;
238   if (NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(current))
239           ->screenshot().get()) {
240     --available_slots;
241   }
242
243   // Keep screenshots closer to the current navigation entry, and purge the ones
244   // that are farther away from it. So in each step, look at the entries at
245   // each offset on both the back and forward history, and start counting them
246   // to make sure that the correct number of screenshots are kept in memory.
247   // Note that it is possible for some entries to be missing screenshots (e.g.
248   // when taking the screenshot failed for some reason). So there may be a state
249   // where there are a lot of entries in the back history, but none of them has
250   // any screenshot. In such cases, keep the screenshots for |kMaxScreenshots|
251   // entries in the forward history list.
252   int back = current - 1;
253   int forward = current + 1;
254   while (available_slots > 0 && (back >= 0 || forward < num_entries)) {
255     if (back >= 0) {
256       NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
257           owner_->GetEntryAtIndex(back));
258       if (entry->screenshot().get())
259         --available_slots;
260       --back;
261     }
262
263     if (available_slots > 0 && forward < num_entries) {
264       NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
265           owner_->GetEntryAtIndex(forward));
266       if (entry->screenshot().get())
267         --available_slots;
268       ++forward;
269     }
270   }
271
272   // Purge any screenshot at |back| or lower indices, and |forward| or higher
273   // indices.
274   while (screenshot_count > kMaxScreenshots && back >= 0) {
275     NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
276         owner_->GetEntryAtIndex(back));
277     if (ClearScreenshot(entry))
278       --screenshot_count;
279     --back;
280   }
281
282   while (screenshot_count > kMaxScreenshots && forward < num_entries) {
283     NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
284         owner_->GetEntryAtIndex(forward));
285     if (ClearScreenshot(entry))
286       --screenshot_count;
287     ++forward;
288   }
289   CHECK_GE(screenshot_count, 0);
290   CHECK_LE(screenshot_count, kMaxScreenshots);
291 }
292
293 }  // namespace content