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.
5 #include "chrome/browser/thumbnails/render_widget_snapshot_taker.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"
20 using content::RenderWidgetHost
;
22 struct RenderWidgetSnapshotTaker::AsyncRequestInfo
{
23 SnapshotReadyCallback callback
;
24 scoped_ptr
<TransportDIB
> thumbnail_dib
;
25 RenderWidgetHost
* renderer
; // Not owned.
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.
32 // We don't register for notifications here since BrowserProcessImpl creates
33 // us before the NotificationService is.
36 RenderWidgetSnapshotTaker::~RenderWidgetSnapshotTaker() {
39 void RenderWidgetSnapshotTaker::AskForSnapshot(
40 RenderWidgetHost
* renderer
,
41 const SnapshotReadyCallback
& callback
,
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;
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
));
56 // TODO: IPC a handle to the renderer like Windows.
57 // http://code.google.com/p/chromium/issues/detail?id=89777
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
,
71 if (!renderer_dib_handle
) {
72 LOG(WARNING
) << "Could not duplicate dib handle for renderer";
76 TransportDIB::Handle renderer_dib_handle
= thumbnail_dib
->handle();
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
);
87 NOTREACHED() << "Callback already registered?";
91 MonitorRenderer(renderer
, true);
92 renderer
->PaintAtSize(
93 renderer_dib_handle
, sequence_num
, page_size
, desired_size
);
95 #endif // defined(USE_X11)
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
;
105 callback_map_
.erase(nuked
);
106 MonitorRenderer(renderer
, false);
113 void RenderWidgetSnapshotTaker::WidgetDidReceivePaintAtSizeAck(
114 RenderWidgetHost
* widget
,
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();
123 if (!dib
|| !dib
->Map()) {
127 // Create an SkBitmap from the DIB.
128 SkBitmap non_owned_bitmap
;
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
134 non_owned_bitmap
.setConfig(SkBitmap::kARGB_8888_Config
,
135 size
.width(), size
.height());
136 if (dib
->size() < non_owned_bitmap
.getSafeSize())
138 non_owned_bitmap
.setPixels(dib
->memory());
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
);
145 item
->second
->callback
.Run(result
);
147 // We're done with the callback, and with the DIB, so delete both.
148 callback_map_
.erase(item
);
149 MonitorRenderer(widget
, false);
153 void RenderWidgetSnapshotTaker::Observe(
155 const content::NotificationSource
& source
,
156 const content::NotificationDetails
& details
) {
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
);
169 NOTREACHED() << "Unexpected notification type: " << type
;
173 void RenderWidgetSnapshotTaker::MonitorRenderer(RenderWidgetHost
* renderer
,
175 content::Source
<RenderWidgetHost
> renderer_source
=
176 content::Source
<RenderWidgetHost
>(renderer
);
178 int new_count
= ++host_monitor_counts_
[renderer
];
179 if (new_count
== 1) {
182 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK
,
186 int new_count
= --host_monitor_counts_
[renderer
];
187 if (new_count
== 0) {
188 host_monitor_counts_
.erase(renderer
);
191 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK
,