Home | History | Annotate | Download | only in thumbnails
      1 // Copyright (c) 2012 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 "chrome/browser/thumbnails/render_widget_snapshot_taker.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "content/public/browser/notification_details.h"
     10 #include "content/public/browser/notification_source.h"
     11 #include "content/public/browser/notification_types.h"
     12 #include "content/public/browser/render_process_host.h"
     13 #include "content/public/browser/render_view_host.h"
     14 #include "content/public/browser/render_widget_host_view.h"
     15 #include "ui/base/layout.h"
     16 #include "ui/gfx/size.h"
     17 #include "ui/gfx/size_conversions.h"
     18 #include "ui/surface/transport_dib.h"
     19 
     20 using content::RenderWidgetHost;
     21 
     22 struct RenderWidgetSnapshotTaker::AsyncRequestInfo {
     23   SnapshotReadyCallback callback;
     24   scoped_ptr<TransportDIB> thumbnail_dib;
     25   RenderWidgetHost* renderer;  // Not owned.
     26 };
     27 
     28 RenderWidgetSnapshotTaker::RenderWidgetSnapshotTaker() {
     29   // The BrowserProcessImpl creates this non-lazily. If you add nontrivial
     30   // stuff here, be sure to convert it to being lazily created.
     31   //
     32   // We don't register for notifications here since BrowserProcessImpl creates
     33   // us before the NotificationService is.
     34 }
     35 
     36 RenderWidgetSnapshotTaker::~RenderWidgetSnapshotTaker() {
     37 }
     38 
     39 void RenderWidgetSnapshotTaker::AskForSnapshot(
     40     RenderWidgetHost* renderer,
     41     const SnapshotReadyCallback& callback,
     42     gfx::Size page_size,
     43     gfx::Size desired_size) {
     44   // We are going to render the thumbnail asynchronously now, so keep
     45   // this callback for later lookup when the rendering is done.
     46   static int sequence_num = 0;
     47   sequence_num++;
     48   float scale_factor = ui::GetImageScale(ui::GetScaleFactorForNativeView(
     49       renderer->GetView()->GetNativeView()));
     50   gfx::Size desired_size_in_pixel = gfx::ToFlooredSize(
     51       gfx::ScaleSize(desired_size, scale_factor));
     52   scoped_ptr<TransportDIB> thumbnail_dib(TransportDIB::Create(
     53       desired_size_in_pixel.GetArea() * 4, sequence_num));
     54 
     55 #if defined(OS_LINUX)
     56   // TODO: IPC a handle to the renderer like Windows.
     57   // http://code.google.com/p/chromium/issues/detail?id=89777
     58   NOTIMPLEMENTED();
     59   return;
     60 #else
     61 
     62 #if defined(OS_WIN)
     63   // Duplicate the handle to the DIB here because the renderer process does not
     64   // have permission. The duplicated handle is owned by the renderer process,
     65   // which is responsible for closing it.
     66   TransportDIB::Handle renderer_dib_handle;
     67   DuplicateHandle(GetCurrentProcess(), thumbnail_dib->handle(),
     68                   renderer->GetProcess()->GetHandle(), &renderer_dib_handle,
     69                   STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ | FILE_MAP_WRITE,
     70                   FALSE, 0);
     71   if (!renderer_dib_handle) {
     72     LOG(WARNING) << "Could not duplicate dib handle for renderer";
     73     return;
     74   }
     75 #else
     76   TransportDIB::Handle renderer_dib_handle = thumbnail_dib->handle();
     77 #endif
     78 
     79   linked_ptr<AsyncRequestInfo> request_info(new AsyncRequestInfo);
     80   request_info->callback = callback;
     81   request_info->thumbnail_dib.reset(thumbnail_dib.release());
     82   request_info->renderer = renderer;
     83   SnapshotCallbackMap::value_type new_value(sequence_num, request_info);
     84   std::pair<SnapshotCallbackMap::iterator, bool> result =
     85       callback_map_.insert(new_value);
     86   if (!result.second) {
     87     NOTREACHED() << "Callback already registered?";
     88     return;
     89   }
     90 
     91   MonitorRenderer(renderer, true);
     92   renderer->PaintAtSize(
     93       renderer_dib_handle, sequence_num, page_size, desired_size);
     94 
     95 #endif  // defined(USE_X11)
     96 }
     97 
     98 void RenderWidgetSnapshotTaker::CancelSnapshot(
     99     content::RenderWidgetHost* renderer) {
    100   SnapshotCallbackMap::iterator iterator = callback_map_.begin();
    101   while (iterator != callback_map_.end()) {
    102     if (iterator->second->renderer == renderer) {
    103       SnapshotCallbackMap::iterator nuked = iterator;
    104       ++iterator;
    105       callback_map_.erase(nuked);
    106       MonitorRenderer(renderer, false);
    107       continue;
    108     }
    109     ++iterator;
    110   }
    111 }
    112 
    113 void RenderWidgetSnapshotTaker::WidgetDidReceivePaintAtSizeAck(
    114     RenderWidgetHost* widget,
    115     int sequence_num,
    116     const gfx::Size& size) {
    117   // Lookup the callback, run it, and erase it.
    118   SnapshotCallbackMap::iterator item = callback_map_.find(sequence_num);
    119   if (item != callback_map_.end()) {
    120     DCHECK_EQ(widget, item->second->renderer);
    121     TransportDIB* dib = item->second->thumbnail_dib.get();
    122     DCHECK(dib);
    123     if (!dib || !dib->Map()) {
    124       return;
    125     }
    126 
    127     // Create an SkBitmap from the DIB.
    128     SkBitmap non_owned_bitmap;
    129     SkBitmap result;
    130 
    131     // Fill out the non_owned_bitmap with the right config.  Note that
    132     // this code assumes that the transport dib is a 32-bit ARGB
    133     // image.
    134     non_owned_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
    135                                size.width(), size.height());
    136     if (dib->size() < non_owned_bitmap.getSafeSize())
    137       return;
    138     non_owned_bitmap.setPixels(dib->memory());
    139 
    140     // Now alloc/copy the memory so we own it and can pass it around,
    141     // and the memory won't go away when the DIB goes away.
    142     // TODO: Figure out a way to avoid this copy?
    143     non_owned_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config);
    144 
    145     item->second->callback.Run(result);
    146 
    147     // We're done with the callback, and with the DIB, so delete both.
    148     callback_map_.erase(item);
    149     MonitorRenderer(widget, false);
    150   }
    151 }
    152 
    153 void RenderWidgetSnapshotTaker::Observe(
    154     int type,
    155     const content::NotificationSource& source,
    156     const content::NotificationDetails& details) {
    157   switch (type) {
    158     case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK: {
    159       std::pair<int, gfx::Size>* size_ack_details =
    160           content::Details<std::pair<int, gfx::Size> >(details).ptr();
    161       WidgetDidReceivePaintAtSizeAck(
    162           content::Source<RenderWidgetHost>(source).ptr(),
    163           size_ack_details->first,
    164           size_ack_details->second);
    165       break;
    166     }
    167 
    168     default:
    169       NOTREACHED() << "Unexpected notification type: " << type;
    170   }
    171 }
    172 
    173 void RenderWidgetSnapshotTaker::MonitorRenderer(RenderWidgetHost* renderer,
    174                                                 bool monitor) {
    175   content::Source<RenderWidgetHost> renderer_source =
    176       content::Source<RenderWidgetHost>(renderer);
    177   if (monitor) {
    178     int new_count = ++host_monitor_counts_[renderer];
    179     if (new_count == 1) {
    180       registrar_.Add(
    181           this,
    182           content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK,
    183           renderer_source);
    184     }
    185   } else {
    186     int new_count = --host_monitor_counts_[renderer];
    187     if (new_count == 0) {
    188       host_monitor_counts_.erase(renderer);
    189       registrar_.Remove(
    190           this,
    191           content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK,
    192           renderer_source);
    193     }
    194   }
    195 }
    196