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