Home | History | Annotate | Download | only in frame_host
      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
    277