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