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