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