aeropeek_manager.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2010 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/aeropeek_manager.h"
6
7#include <dwmapi.h>
8#include <shobjidl.h>
9
10#include "app/win_util.h"
11#include "base/command_line.h"
12#include "base/scoped_comptr_win.h"
13#include "base/scoped_handle_win.h"
14#include "base/scoped_native_library.h"
15#include "base/waitable_event.h"
16#include "base/win/windows_version.h"
17#include "chrome/browser/app_icon_win.h"
18#include "chrome/browser/browser_list.h"
19#include "chrome/browser/browser_process.h"
20#include "chrome/browser/browser_thread.h"
21#include "chrome/browser/renderer_host/backing_store.h"
22#include "chrome/browser/renderer_host/render_view_host.h"
23#include "chrome/browser/tab_contents/tab_contents.h"
24#include "chrome/browser/tab_contents/tab_contents_delegate.h"
25#include "chrome/browser/tab_contents/tab_contents_view.h"
26#include "chrome/browser/tab_contents/thumbnail_generator.h"
27#include "chrome/browser/tabs/tab_strip_model.h"
28#include "chrome/common/chrome_constants.h"
29#include "chrome/common/chrome_switches.h"
30#include "chrome/installer/util/browser_distribution.h"
31#include "gfx/gdi_util.h"
32#include "gfx/icon_util.h"
33#include "gfx/window_impl.h"
34#include "skia/ext/image_operations.h"
35#include "skia/ext/platform_canvas.h"
36#include "third_party/skia/include/core/SkBitmap.h"
37
38namespace {
39
40// Macros and COM interfaces used in this file.
41// These interface declarations are copied from Windows SDK 7.
42// TODO(hbono): Bug 16903: to be deleted when we use Windows SDK 7.
43
44// Windows SDK 7 defines these macros only when _WIN32_WINNT >= 0x0601.
45// Since Chrome currently sets _WIN32_WINNT to 0x0600, copy these defines here
46// so we can use them.
47#ifndef WM_DWMSENDICONICTHUMBNAIL
48#define WM_DWMSENDICONICTHUMBNAIL           0x0323
49#endif
50#ifndef WM_DWMSENDICONICLIVEPREVIEWBITMAP
51#define WM_DWMSENDICONICLIVEPREVIEWBITMAP   0x0326
52#endif
53
54// COM interfaces defined only in Windows SDK 7.
55#ifndef __ITaskbarList2_INTERFACE_DEFINED__
56#define __ITaskbarList2_INTERFACE_DEFINED__
57
58// EXTERN_C const IID IID_ITaskbarList2;
59MIDL_INTERFACE("602D4995-B13A-429b-A66E-1935E44F4317")
60ITaskbarList2 : public ITaskbarList {
61 public:
62  virtual HRESULT STDMETHODCALLTYPE MarkFullscreenWindow(
63      /* [in] */ __RPC__in HWND hwnd,
64      /* [in] */ BOOL fFullscreen) = 0;
65};
66
67#endif  /* __ITaskbarList2_INTERFACE_DEFINED__ */
68
69#ifndef __ITaskbarList3_INTERFACE_DEFINED__
70#define __ITaskbarList3_INTERFACE_DEFINED__
71
72typedef struct tagTHUMBBUTTON {
73  DWORD dwMask;
74  UINT iId;
75  UINT iBitmap;
76  HICON hIcon;
77  WCHAR szTip[ 260 ];
78  DWORD dwFlags;
79} THUMBBUTTON;
80
81typedef struct tagTHUMBBUTTON *LPTHUMBBUTTON;
82
83// THUMBBUTTON flags
84#define THBF_ENABLED             0x0000
85#define THBF_DISABLED            0x0001
86#define THBF_DISMISSONCLICK      0x0002
87#define THBF_NOBACKGROUND        0x0004
88#define THBF_HIDDEN              0x0008
89// THUMBBUTTON mask
90#define THB_BITMAP          0x0001
91#define THB_ICON            0x0002
92#define THB_TOOLTIP         0x0004
93#define THB_FLAGS           0x0008
94#define THBN_CLICKED        0x1800
95
96typedef /* [v1_enum] */ enum TBPFLAG {
97  TBPF_NOPROGRESS = 0,
98  TBPF_INDETERMINATE = 0x1,
99  TBPF_NORMAL = 0x2,
100  TBPF_ERROR = 0x4,
101  TBPF_PAUSED = 0x8
102} TBPFLAG;
103
104// EXTERN_C const IID IID_ITaskbarList3;
105
106MIDL_INTERFACE("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")
107ITaskbarList3 : public ITaskbarList2 {
108 public:
109  virtual HRESULT STDMETHODCALLTYPE SetProgressValue(
110      /* [in] */ __RPC__in HWND hwnd,
111      /* [in] */ ULONGLONG ullCompleted,
112      /* [in] */ ULONGLONG ullTotal) = 0;
113  virtual HRESULT STDMETHODCALLTYPE SetProgressState(
114      /* [in] */ __RPC__in HWND hwnd,
115      /* [in] */ TBPFLAG tbpFlags) = 0;
116  virtual HRESULT STDMETHODCALLTYPE RegisterTab(
117      /* [in] */ __RPC__in HWND hwndTab,
118      /* [in] */ __RPC__in HWND hwndMDI) = 0;
119  virtual HRESULT STDMETHODCALLTYPE UnregisterTab(
120      /* [in] */ __RPC__in HWND hwndTab) = 0;
121  virtual HRESULT STDMETHODCALLTYPE SetTabOrder(
122      /* [in] */ __RPC__in HWND hwndTab,
123      /* [in] */ __RPC__in HWND hwndInsertBefore) = 0;
124  virtual HRESULT STDMETHODCALLTYPE SetTabActive(
125      /* [in] */ __RPC__in HWND hwndTab,
126      /* [in] */ __RPC__in HWND hwndMDI,
127      /* [in] */ DWORD dwReserved) = 0;
128  virtual HRESULT STDMETHODCALLTYPE ThumbBarAddButtons(
129      /* [in] */ __RPC__in HWND hwnd,
130      /* [in] */ UINT cButtons,
131      /* [size_is][in] */ __RPC__in_ecount_full(cButtons)
132      LPTHUMBBUTTON pButton) = 0;
133  virtual HRESULT STDMETHODCALLTYPE ThumbBarUpdateButtons(
134      /* [in] */ __RPC__in HWND hwnd,
135      /* [in] */ UINT cButtons,
136      /* [size_is][in] */ __RPC__in_ecount_full(cButtons)
137      LPTHUMBBUTTON pButton) = 0;
138  virtual HRESULT STDMETHODCALLTYPE ThumbBarSetImageList(
139      /* [in] */ __RPC__in HWND hwnd,
140      /* [in] */ __RPC__in_opt HIMAGELIST himl) = 0;
141  virtual HRESULT STDMETHODCALLTYPE SetOverlayIcon(
142      /* [in] */ __RPC__in HWND hwnd,
143      /* [in] */ __RPC__in HICON hIcon,
144      /* [string][in] */ __RPC__in_string LPCWSTR pszDescription) = 0;
145  virtual HRESULT STDMETHODCALLTYPE SetThumbnailTooltip(
146      /* [in] */ __RPC__in HWND hwnd,
147      /* [string][in] */ __RPC__in_string LPCWSTR pszTip) = 0;
148  virtual HRESULT STDMETHODCALLTYPE SetThumbnailClip(
149      /* [in] */ __RPC__in HWND hwnd,
150      /* [in] */ __RPC__in RECT *prcClip) = 0;
151};
152#endif  // __ITaskbarList3_INTERFACE_DEFINED__
153
154// END OF WINDOWS SDK 7.0
155
156}  // namespace
157
158namespace {
159
160// Sends a thumbnail bitmap to Windows. Windows assumes this function is called
161// when a WM_DWMSENDICONICTHUMBNAIL message sent to a place-holder window. We
162// can use DwmInvalidateIconicBitmap() to force Windows to send the message.
163HRESULT CallDwmSetIconicThumbnail(HWND window, HBITMAP bitmap, DWORD flags) {
164  FilePath dwmapi_path(base::GetNativeLibraryName(L"dwmapi"));
165  base::ScopedNativeLibrary dwmapi(dwmapi_path);
166
167  typedef HRESULT (STDAPICALLTYPE *DwmSetIconicThumbnailProc)(
168      HWND, HBITMAP, DWORD);
169  DwmSetIconicThumbnailProc dwm_set_iconic_thumbnail =
170      static_cast<DwmSetIconicThumbnailProc>(
171      dwmapi.GetFunctionPointer("DwmSetIconicThumbnail"));
172
173  if (!dwm_set_iconic_thumbnail)
174    return E_FAIL;
175
176  return dwm_set_iconic_thumbnail(window, bitmap, flags);
177}
178
179// Sends a preview bitmap to Windows. Windows assumes this function is called
180// when a WM_DWMSENDICONICLIVEPREVIEWBITMAP message sent to a place-holder
181// window.
182HRESULT CallDwmSetIconicLivePreviewBitmap(HWND window,
183                                          HBITMAP bitmap,
184                                          POINT* client,
185                                          DWORD flags) {
186  FilePath dwmapi_path(base::GetNativeLibraryName(L"dwmapi"));
187  base::ScopedNativeLibrary dwmapi(dwmapi_path);
188
189  typedef HRESULT (STDAPICALLTYPE *DwmSetIconicLivePreviewBitmapProc)(
190      HWND, HBITMAP, POINT*, DWORD);
191  DwmSetIconicLivePreviewBitmapProc dwm_set_live_preview_bitmap =
192      static_cast<DwmSetIconicLivePreviewBitmapProc>(
193      dwmapi.GetFunctionPointer("DwmSetIconicLivePreviewBitmap"));
194
195  if (!dwm_set_live_preview_bitmap)
196    return E_FAIL;
197
198  return dwm_set_live_preview_bitmap(window, bitmap, client, flags);
199}
200
201// Invalidates the thumbnail image of the specified place-holder window. (See
202// the comments in CallDwmSetIconicThumbnai()).
203HRESULT CallDwmInvalidateIconicBitmaps(HWND window) {
204  FilePath dwmapi_path(base::GetNativeLibraryName(L"dwmapi"));
205  base::ScopedNativeLibrary dwmapi(dwmapi_path);
206
207  typedef HRESULT (STDAPICALLTYPE *DwmInvalidateIconicBitmapsProc)(HWND);
208  DwmInvalidateIconicBitmapsProc dwm_invalidate_iconic_bitmaps =
209      static_cast<DwmInvalidateIconicBitmapsProc>(
210      dwmapi.GetFunctionPointer("DwmInvalidateIconicBitmaps"));
211
212  if (!dwm_invalidate_iconic_bitmaps)
213    return E_FAIL;
214
215  return dwm_invalidate_iconic_bitmaps(window);
216}
217
218}  // namespace
219
220namespace {
221
222// Tasks used in this file.
223// This file uses three I/O tasks to implement AeroPeek:
224// * RegisterThumbnailTask
225//   Register a tab into the thumbnail list of Windows.
226// * SendThumbnailTask
227//   Create a thumbnail image and send it to Windows.
228// * SendLivePreviewTask
229//   Create a preview image and send it to Windows.
230// These I/O tasks indirectly access the specified tab through the
231// AeroPeekWindowDelegate interface to prevent these tasks from accessing the
232// deleted tabs.
233
234// A task that registers a thumbnail window as a child of the specified
235// browser application.
236class RegisterThumbnailTask : public Task {
237 public:
238  RegisterThumbnailTask(HWND frame_window, HWND window, bool active)
239      : frame_window_(frame_window),
240        window_(window),
241        active_(active) {
242  }
243
244 private:
245  void Run() {
246    // Set the App ID of the browser for this place-holder window to tell
247    // that this window is a child of the browser application, i.e. to tell
248    // that this thumbnail window should be displayed when we hover the
249    // browser icon in the taskbar.
250    // TODO(mattm): This should use ShellIntegration::GetChromiumAppId to work
251    // properly with multiple profiles.
252    win_util::SetAppIdForWindow(
253        BrowserDistribution::GetDistribution()->GetBrowserAppId(), window_);
254
255    // Register this place-holder window to the taskbar as a child of
256    // the browser window and add it to the end of its tab list.
257    // Correctly, this registration should be called after this browser window
258    // receives a registered window message "TaskbarButtonCreated", which
259    // means that Windows creates a taskbar button for this window in its
260    // taskbar. But it seems to be OK to register it without checking the
261    // message.
262    // TODO(hbono): we need to check this registered message?
263    ScopedComPtr<ITaskbarList3> taskbar;
264    if (FAILED(taskbar.CreateInstance(CLSID_TaskbarList, NULL,
265                                      CLSCTX_INPROC_SERVER)) ||
266        FAILED(taskbar->HrInit()) ||
267        FAILED(taskbar->RegisterTab(window_, frame_window_)) ||
268        FAILED(taskbar->SetTabOrder(window_, NULL)))
269      return;
270    if (active_)
271      taskbar->SetTabActive(window_, frame_window_, 0);
272  }
273
274 private:
275  // An application window to which we are going to register a tab window.
276  // This "application window" is a browser frame in terms of Chrome.
277  HWND frame_window_;
278
279  // A tab window.
280  // After we register this window as a child of the above application window,
281  // Windows sends AeroPeek events to this window.
282  // It seems this window MUST be a tool window.
283  HWND window_;
284
285  // Whether or not we need to activate this tab by default.
286  bool active_;
287};
288
289// A task which creates a thumbnail image used by AeroPeek and sends it to
290// Windows.
291class SendThumbnailTask : public Task {
292 public:
293  SendThumbnailTask(HWND aeropeek_window,
294                    const gfx::Rect& content_bounds,
295                    const gfx::Size& aeropeek_size,
296                    const SkBitmap& tab_bitmap,
297                    base::WaitableEvent* ready)
298      : aeropeek_window_(aeropeek_window),
299        content_bounds_(content_bounds),
300        aeropeek_size_(aeropeek_size),
301        tab_bitmap_(tab_bitmap),
302        ready_(ready) {
303  }
304
305  ~SendThumbnailTask() {
306    if (ready_)
307      ready_->Signal();
308  }
309
310 private:
311  void Run() {
312    // Calculate the size of the aeropeek thumbnail and resize the tab bitmap
313    // to the size. When the given bitmap is an empty bitmap, we create a dummy
314    // bitmap from the content-area rectangle to create a DIB. (We don't need to
315    // allocate pixels for this case since we don't use them.)
316    gfx::Size thumbnail_size;
317    SkBitmap thumbnail_bitmap;
318
319    if (tab_bitmap_.isNull() || tab_bitmap_.empty()) {
320      GetThumbnailSize(content_bounds_.width(), content_bounds_.height(),
321                       &thumbnail_size);
322
323      thumbnail_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
324                                 thumbnail_size.width(),
325                                 thumbnail_size.height());
326    } else {
327      GetThumbnailSize(tab_bitmap_.width(), tab_bitmap_.height(),
328                       &thumbnail_size);
329
330      thumbnail_bitmap = skia::ImageOperations::Resize(
331          tab_bitmap_,
332          skia::ImageOperations::RESIZE_LANCZOS3,
333          thumbnail_size.width(),
334          thumbnail_size.height());
335    }
336
337    // Create a DIB, copy the resized image, and send the DIB to Windows.
338    // We can delete this DIB after sending it to Windows since Windows creates
339    // a copy of the DIB and use it.
340    ScopedHDC hdc(CreateCompatibleDC(NULL));
341    if (!hdc.Get()) {
342      LOG(ERROR) << "cannot create a memory DC: " << GetLastError();
343      return;
344    }
345
346    BITMAPINFOHEADER header;
347    gfx::CreateBitmapHeader(thumbnail_size.width(), thumbnail_size.height(),
348                            &header);
349
350    void* bitmap_data = NULL;
351    ScopedBitmap bitmap(CreateDIBSection(hdc,
352                                         reinterpret_cast<BITMAPINFO*>(&header),
353                                         DIB_RGB_COLORS,
354                                         &bitmap_data,
355                                         NULL,
356                                         0));
357
358    if (!bitmap.Get() || !bitmap_data) {
359      LOG(ERROR) << "cannot create a bitmap: " << GetLastError();
360      return;
361    }
362
363    SkAutoLockPixels lock(thumbnail_bitmap);
364    int* content_pixels = reinterpret_cast<int*>(bitmap_data);
365    for (int y = 0; y < thumbnail_size.height(); ++y) {
366      for (int x = 0; x < thumbnail_size.width(); ++x) {
367        content_pixels[y * thumbnail_size.width() + x] =
368            GetPixel(thumbnail_bitmap, x, y);
369      }
370    }
371
372    HRESULT result = CallDwmSetIconicThumbnail(aeropeek_window_, bitmap, 0);
373    if (FAILED(result))
374      LOG(ERROR) << "cannot set a tab thumbnail: " << result;
375  }
376
377  // Calculates the thumbnail size sent to Windows so we can preserve the pixel
378  // aspect-ratio of the source bitmap. Since Windows returns an error when we
379  // send an image bigger than the given size, we decrease either the thumbnail
380  // width or the thumbnail height so we can fit the longer edge of the source
381  // window.
382  void GetThumbnailSize(int width, int height, gfx::Size* output) const {
383    float thumbnail_width = static_cast<float>(aeropeek_size_.width());
384    float thumbnail_height = static_cast<float>(aeropeek_size_.height());
385    float source_width = static_cast<float>(width);
386    float source_height = static_cast<float>(height);
387    DCHECK(source_width && source_height);
388
389    float ratio_width = thumbnail_width / source_width;
390    float ratio_height = thumbnail_height / source_height;
391    if (ratio_width > ratio_height) {
392      thumbnail_width = source_width * ratio_height;
393    } else {
394      thumbnail_height = source_height * ratio_width;
395    }
396
397    output->set_width(static_cast<int>(thumbnail_width));
398    output->set_height(static_cast<int>(thumbnail_height));
399  }
400
401  // Returns a pixel of the specified bitmap. If this bitmap is a dummy bitmap,
402  // this function returns an opaque white pixel instead.
403  int GetPixel(const SkBitmap& bitmap, int x, int y) const {
404    const int* tab_pixels = reinterpret_cast<const int*>(bitmap.getPixels());
405    if (!tab_pixels)
406      return 0xFFFFFFFF;
407    return tab_pixels[y * bitmap.width() + x];
408  }
409
410 private:
411  // A window handle to the place-holder window used by AeroPeek.
412  HWND aeropeek_window_;
413
414  // The bounding rectangle of the user-perceived content area.
415  // This rectangle is used only for creating a fall-back bitmap.
416  gfx::Rect content_bounds_;
417
418  // The size of an output image to be sent to Windows.
419  gfx::Size aeropeek_size_;
420
421  // The source bitmap.
422  SkBitmap tab_bitmap_;
423
424  // An event to notify when this task finishes.
425  base::WaitableEvent* ready_;
426};
427
428// A task which creates a preview image used by AeroPeek and sends it to
429// Windows.
430// This task becomes more complicated than SendThumbnailTask because this task
431// calculates the rectangle of the user-perceived content area (infobars +
432// content area) so Windows can paste the preview image on it.
433// This task is used if an AeroPeek window receives a
434// WM_DWMSENDICONICLIVEPREVIEWBITMAP message.
435class SendLivePreviewTask : public Task {
436 public:
437  SendLivePreviewTask(HWND aeropeek_window,
438                      const gfx::Rect& content_bounds,
439                      const SkBitmap& tab_bitmap)
440      : aeropeek_window_(aeropeek_window),
441        content_bounds_(content_bounds),
442        tab_bitmap_(tab_bitmap) {
443  }
444
445  ~SendLivePreviewTask() {
446  }
447
448 private:
449  void Run() {
450    // Create a DIB for the user-perceived content area of the tab, copy the
451    // tab image into the DIB, and send it to Windows.
452    // We don't need to paste this tab image onto the frame image since Windows
453    // automatically pastes it for us.
454    ScopedHDC hdc(CreateCompatibleDC(NULL));
455    if (!hdc.Get()) {
456      LOG(ERROR) << "cannot create a memory DC: " << GetLastError();
457      return;
458    }
459
460    BITMAPINFOHEADER header;
461    gfx::CreateBitmapHeader(content_bounds_.width(), content_bounds_.height(),
462                            &header);
463
464    void* bitmap_data = NULL;
465    ScopedBitmap bitmap(CreateDIBSection(hdc.Get(),
466                                         reinterpret_cast<BITMAPINFO*>(&header),
467                                         DIB_RGB_COLORS, &bitmap_data,
468                                         NULL, 0));
469    if (!bitmap.Get() || !bitmap_data) {
470      LOG(ERROR) << "cannot create a bitmap: " << GetLastError();
471      return;
472    }
473
474    // Copy the tab image onto the DIB.
475    SkAutoLockPixels lock(tab_bitmap_);
476    int* content_pixels = reinterpret_cast<int*>(bitmap_data);
477    for (int y = 0; y < content_bounds_.height(); ++y) {
478      for (int x = 0; x < content_bounds_.width(); ++x)
479        content_pixels[y * content_bounds_.width() + x] = GetTabPixel(x, y);
480    }
481
482    // Send the preview image to Windows.
483    // We can set its offset to the top left corner of the user-perceived
484    // content area so Windows can paste this bitmap onto the correct
485    // position.
486    POINT content_offset = {content_bounds_.x(), content_bounds_.y()};
487    HRESULT result = CallDwmSetIconicLivePreviewBitmap(
488        aeropeek_window_, bitmap, &content_offset, 0);
489    if (FAILED(result))
490      LOG(ERROR) << "cannot send a content image: " << result;
491  }
492
493  int GetTabPixel(int x, int y) const {
494    // Return the opaque while pixel to prevent old foreground tab from being
495    // shown when we cannot get the specified pixel.
496    const int* tab_pixels = reinterpret_cast<int*>(tab_bitmap_.getPixels());
497    if (!tab_pixels || x >= tab_bitmap_.width() || y >= tab_bitmap_.height())
498      return 0xFFFFFFFF;
499
500    // DWM uses alpha values to distinguish opaque colors and transparent ones.
501    // Set the alpha value of this source pixel to prevent the original window
502    // from being shown through.
503    return 0xFF000000 | tab_pixels[y * tab_bitmap_.width() + x];
504  }
505
506 private:
507  // A window handle to the AeroPeek window.
508  HWND aeropeek_window_;
509
510  // The bounding rectangle of the user-perceived content area. When a tab
511  // hasn't been rendered since a browser window is resized, this size doesn't
512  // become the same as the bitmap size as shown below.
513  //     +----------------------+
514  //     |     frame window     |
515  //     | +---------------------+
516  //     | |     tab contents    |
517  //     | +---------------------+
518  //     | | old tab contents | |
519  //     | +------------------+ |
520  //     +----------------------+
521  // This rectangle is used for clipping the width and height of the bitmap and
522  // cleaning the old tab contents.
523  //     +----------------------+
524  //     |     frame window     |
525  //     | +------------------+ |
526  //     | |   tab contents   | |
527  //     | +------------------+ |
528  //     | |      blank       | |
529  //     | +------------------+ |
530  //     +----------------------+
531  gfx::Rect content_bounds_;
532
533  // The bitmap of the source tab.
534  SkBitmap tab_bitmap_;
535};
536
537}  // namespace
538
539// A class which implements a place-holder window used by AeroPeek.
540// The major work of this class are:
541// * Updating the status of Tab Thumbnails;
542// * Receiving messages from Windows, and;
543// * Translating received messages for TabStrip.
544// This class is used by the AeroPeekManager class, which is a proxy
545// between TabStrip and Windows 7.
546class AeroPeekWindow : public gfx::WindowImpl {
547 public:
548  AeroPeekWindow(HWND frame_window,
549                 AeroPeekWindowDelegate* delegate,
550                 int tab_id,
551                 bool tab_active,
552                 const std::wstring& title,
553                 const SkBitmap& favicon_bitmap);
554  ~AeroPeekWindow();
555
556  // Activates or deactivates this window.
557  // This window uses this information not only for highlighting the selected
558  // tab when Windows shows the thumbnail list, but also for preventing us
559  // from rendering AeroPeek images for deactivated windows so often.
560  void Activate();
561  void Deactivate();
562
563  // Updates the image of this window.
564  // When the AeroPeekManager class calls this function, this window starts
565  // a task which updates its thumbnail image.
566  // NOTE: to prevent sending lots of tasks that update the thumbnail images
567  // and hurt the system performance, we post a task only when |is_loading| is
568  // false for non-active tabs. (On the other hand, we always post an update
569  // task for an active tab as IE8 does.)
570  void Update(bool is_loading);
571
572  // Destroys this window.
573  // This function removes this window from the thumbnail list and deletes
574  // all the resources attached to this window, i.e. this object is not valid
575  // any longer after calling this function.
576  void Destroy();
577
578  // Updates the title of this window.
579  // This function just sends a WM_SETTEXT message to update the window title.
580  void SetTitle(const std::wstring& title);
581
582  // Updates the icon used for AeroPeek. Unlike SetTitle(), this function just
583  // saves a copy of the given bitmap since it takes time to create a Windows
584  // icon from this bitmap set it as the window icon. We will create a Windows
585  // when Windows sends a WM_GETICON message to retrieve it.
586  void SetFavIcon(const SkBitmap& favicon);
587
588  // Returns the tab ID associated with this window.
589  int tab_id() { return tab_id_; }
590
591  // Message handlers.
592  BEGIN_MSG_MAP_EX(TabbedThumbnailWindow)
593    MESSAGE_HANDLER_EX(WM_DWMSENDICONICTHUMBNAIL, OnDwmSendIconicThumbnail)
594    MESSAGE_HANDLER_EX(WM_DWMSENDICONICLIVEPREVIEWBITMAP,
595                       OnDwmSendIconicLivePreviewBitmap)
596
597    MSG_WM_ACTIVATE(OnActivate)
598    MSG_WM_CLOSE(OnClose)
599    MSG_WM_CREATE(OnCreate)
600    MSG_WM_GETICON(OnGetIcon)
601  END_MSG_MAP()
602
603 private:
604  // Updates the thumbnail image of this window.
605  // This function is a wrapper function of CallDwmInvalidateIconicBitmaps()
606  // but it invalidates the thumbnail only when |ready_| is signaled to prevent
607  // us from posting two or more tasks.
608  void UpdateThumbnail();
609
610  // Returns the user-perceived content area.
611  gfx::Rect GetContentBounds() const;
612
613  // Message-handler functions.
614  // Called when a window has been created.
615  LRESULT OnCreate(LPCREATESTRUCT create_struct);
616
617  // Called when this thumbnail window is activated, i.e. a user clicks this
618  // thumbnail window.
619  void OnActivate(UINT action, BOOL minimized, HWND window);
620
621  // Called when this thumbnail window is closed, i.e. a user clicks the close
622  // button of this thumbnail window.
623  void OnClose();
624
625  // Called when Windows needs a thumbnail image for this thumbnail window.
626  // Windows can send a WM_DWMSENDICONICTHUMBNAIL message anytime when it
627  // needs the thumbnail bitmap for this place-holder window (e.g. when we
628  // register this place-holder window to Windows, etc.)
629  // When this window receives a WM_DWMSENDICONICTHUMBNAIL message, it HAS TO
630  // create a thumbnail bitmap and send it to Windows through a
631  // DwmSendIconicThumbnail() call. (Windows shows a "page-loading" animation
632  // while it waits for a thumbnail bitmap.)
633  LRESULT OnDwmSendIconicThumbnail(UINT message,
634                                   WPARAM wparam,
635                                   LPARAM lparam);
636
637  // Called when Windows needs a preview image for this thumbnail window.
638  // Same as above, Windows can send a WM_DWMSENDICONICLIVEPREVIEWBITMAP
639  // message anytime when it needs a preview bitmap and we have to create and
640  // send the bitmap when it needs it.
641  LRESULT OnDwmSendIconicLivePreviewBitmap(UINT message,
642                                           WPARAM wparam,
643                                           LPARAM lparam);
644
645  // Called when Windows needs an icon for this thumbnail window.
646  // Windows sends a WM_GETICON message with ICON_SMALL when it needs an
647  // AeroPeek icon. we handle WM_GETICON messages by ourselves so we can create
648  // a custom icon from a favicon only when Windows need it.
649  HICON OnGetIcon(UINT index);
650
651 private:
652  // An application window which owns this tab.
653  // We show this thumbnail image of this window when a user hovers a mouse
654  // cursor onto the taskbar icon of this application window.
655  HWND frame_window_;
656
657  // An interface which dispatches events received from Window.
658  // This window notifies events received from Windows to TabStrip through
659  // this interface.
660  // We should not directly access TabContents members since Windows may send
661  // AeroPeek events to a tab closed by Chrome.
662  // To prevent such race condition, we get access to TabContents through
663  // AeroPeekManager.
664  AeroPeekWindowDelegate* delegate_;
665
666  // A tab ID associated with this window.
667  int tab_id_;
668
669  // A flag that represents whether or not this tab is active.
670  // This flag is used for preventing us from updating the thumbnail images
671  // when this window is not active.
672  bool tab_active_;
673
674  // An event that represents whether or not we can post a task which updates
675  // the thumbnail image of this window.
676  // We post a task only when this event is signaled.
677  base::WaitableEvent ready_to_update_thumbnail_;
678
679  // The title of this tab.
680  std::wstring title_;
681
682  // The favicon for this tab.
683  SkBitmap favicon_bitmap_;
684  ScopedHICON favicon_;
685
686  // The icon used by the frame window.
687  // This icon is used when this tab doesn't have a favicon.
688  HICON frame_icon_;
689
690  DISALLOW_COPY_AND_ASSIGN(AeroPeekWindow);
691};
692
693AeroPeekWindow::AeroPeekWindow(HWND frame_window,
694                               AeroPeekWindowDelegate* delegate,
695                               int tab_id,
696                               bool tab_active,
697                               const std::wstring& title,
698                               const SkBitmap& favicon_bitmap)
699    : frame_window_(frame_window),
700      delegate_(delegate),
701      tab_id_(tab_id),
702      tab_active_(tab_active),
703      ready_to_update_thumbnail_(false, true),
704      title_(title),
705      favicon_bitmap_(favicon_bitmap),
706      frame_icon_(NULL) {
707  // Set the class styles and window styles for this thumbnail window.
708  // An AeroPeek window should be a tool window. (Otherwise,
709  // Windows doesn't send WM_DWMSENDICONICTHUMBNAIL messages.)
710  set_initial_class_style(0);
711  set_window_style(WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION);
712  set_window_ex_style(WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE);
713}
714
715AeroPeekWindow::~AeroPeekWindow() {
716}
717
718void AeroPeekWindow::Activate() {
719  tab_active_ = true;
720
721  // Create a place-holder window and add it to the tab list if it has not been
722  // created yet. (This case happens when we re-attached a detached window.)
723  if (!IsWindow(hwnd())) {
724    Update(false);
725    return;
726  }
727
728  // Notify Windows to set the thumbnail focus to this window.
729  ScopedComPtr<ITaskbarList3> taskbar;
730  HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
731                                          CLSCTX_INPROC_SERVER);
732  if (FAILED(result)) {
733    LOG(ERROR) << "failed creating an ITaskbarList3 interface.";
734    return;
735  }
736
737  result = taskbar->HrInit();
738  if (FAILED(result)) {
739    LOG(ERROR) << "failed initializing an ITaskbarList3 interface.";
740    return;
741  }
742
743  result = taskbar->ActivateTab(hwnd());
744  if (FAILED(result)) {
745    LOG(ERROR) << "failed activating a thumbnail window.";
746    return;
747  }
748
749  // Update the thumbnail image to the up-to-date one.
750  UpdateThumbnail();
751}
752
753void AeroPeekWindow::Deactivate() {
754  tab_active_ = false;
755}
756
757void AeroPeekWindow::Update(bool is_loading) {
758  // Create a place-holder window used by AeroPeek if it has not been created
759  // so Windows can send events used by AeroPeek to this window.
760  // Windows automatically sends a WM_DWMSENDICONICTHUMBNAIL message after this
761  // window is registered to Windows. So, we don't have to invalidate the
762  // thumbnail image of this window now.
763  if (!hwnd()) {
764    gfx::Rect bounds;
765    WindowImpl::Init(frame_window_, bounds);
766    return;
767  }
768
769  // Invalidate the thumbnail image of this window.
770  // When we invalidate the thumbnail image, we HAVE TO handle a succeeding
771  // WM_DWMSENDICONICTHUMBNAIL message and update the thumbnail image with a
772  // DwmSetIconicThumbnail() call. So, we should not call this function when
773  // we don't have enough information to create a thumbnail.
774  if (tab_active_ || !is_loading)
775    UpdateThumbnail();
776}
777
778void AeroPeekWindow::Destroy() {
779  if (!IsWindow(hwnd()))
780    return;
781
782  // Remove this window from the tab list of Windows.
783  ScopedComPtr<ITaskbarList3> taskbar;
784  HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
785                                          CLSCTX_INPROC_SERVER);
786  if (FAILED(result))
787    return;
788
789  result = taskbar->HrInit();
790  if (FAILED(result))
791    return;
792
793  result = taskbar->UnregisterTab(hwnd());
794
795  // Destroy this window.
796  DestroyWindow(hwnd());
797}
798
799void AeroPeekWindow::SetTitle(const std::wstring& title) {
800  title_ = title;
801}
802
803void AeroPeekWindow::SetFavIcon(const SkBitmap& favicon) {
804  favicon_bitmap_ = favicon;
805}
806
807void AeroPeekWindow::UpdateThumbnail() {
808  // We post a task to actually create a new thumbnail. So, this function may
809  // be called while we are creating a thumbnail. To prevent this window from
810  // posting two or more tasks, we don't invalidate the current thumbnail
811  // when this event is not signaled.
812  if (ready_to_update_thumbnail_.IsSignaled())
813    CallDwmInvalidateIconicBitmaps(hwnd());
814}
815
816gfx::Rect AeroPeekWindow::GetContentBounds() const {
817  RECT content_rect;
818  GetClientRect(frame_window_, &content_rect);
819
820  gfx::Insets content_insets;
821  delegate_->GetContentInsets(&content_insets);
822
823  gfx::Rect content_bounds(content_rect);
824  content_bounds.Inset(content_insets.left(),
825                       content_insets.top(),
826                       content_insets.right(),
827                       content_insets.bottom());
828  return content_bounds;
829}
830
831// message handlers
832
833void AeroPeekWindow::OnActivate(UINT action,
834                                BOOL minimized,
835                                HWND window) {
836  // Windows sends a WM_ACTIVATE message not only when a user clicks this
837  // window (i.e. this window gains the thumbnail focus) but also a user clicks
838  // another window (i.e. this window loses the thumbnail focus.)
839  // Return when this window loses the thumbnail focus since we don't have to
840  // do anything for this case.
841  if (action == WA_INACTIVE)
842    return;
843
844  // Ask Chrome to activate the tab associated with this thumbnail window.
845  // Since TabStripModel calls AeroPeekManager::TabSelectedAt() when it
846  // finishes activating the tab. We will move the tab focus of AeroPeek there.
847  if (delegate_)
848    delegate_->ActivateTab(tab_id_);
849}
850
851LRESULT AeroPeekWindow::OnCreate(LPCREATESTRUCT create_struct) {
852  // Initialize the window title now since WindowImpl::Init() always calls
853  // CreateWindowEx() with its window name NULL.
854  if (!title_.empty()) {
855    SendMessage(hwnd(), WM_SETTEXT, 0,
856                reinterpret_cast<LPARAM>(title_.c_str()));
857  }
858
859  // Window attributes for DwmSetWindowAttribute().
860  // These enum values are copied from Windows SDK 7 so we can compile this
861  // file with or without it.
862  // TODO(hbono): Bug 16903: to be deleted when we use Windows SDK 7.
863  enum {
864    DWMWA_NCRENDERING_ENABLED = 1,
865    DWMWA_NCRENDERING_POLICY,
866    DWMWA_TRANSITIONS_FORCEDISABLED,
867    DWMWA_ALLOW_NCPAINT,
868    DWMWA_CAPTION_BUTTON_BOUNDS,
869    DWMWA_NONCLIENT_RTL_LAYOUT,
870    DWMWA_FORCE_ICONIC_REPRESENTATION,
871    DWMWA_FLIP3D_POLICY,
872    DWMWA_EXTENDED_FRAME_BOUNDS,
873    DWMWA_HAS_ICONIC_BITMAP,
874    DWMWA_DISALLOW_PEEK,
875    DWMWA_EXCLUDED_FROM_PEEK,
876    DWMWA_LAST
877  };
878
879  // Set DWM attributes to tell Windows that this window can provide the
880  // bitmaps used by AeroPeek.
881  BOOL force_iconic_representation = TRUE;
882  DwmSetWindowAttribute(hwnd(),
883                        DWMWA_FORCE_ICONIC_REPRESENTATION,
884                        &force_iconic_representation,
885                        sizeof(force_iconic_representation));
886
887  BOOL has_iconic_bitmap = TRUE;
888  DwmSetWindowAttribute(hwnd(),
889                        DWMWA_HAS_ICONIC_BITMAP,
890                        &has_iconic_bitmap,
891                        sizeof(has_iconic_bitmap));
892
893  // Post a task that registers this thumbnail window to Windows because it
894  // may take some time. (For example, when we create an ITaskbarList3
895  // interface for the first time, Windows loads DLLs and we need to wait for
896  // some time.)
897  BrowserThread::PostTask(
898      BrowserThread::IO,
899      FROM_HERE,
900      new RegisterThumbnailTask(frame_window_, hwnd(), tab_active_));
901
902  return 0;
903}
904
905void AeroPeekWindow::OnClose() {
906  // Unregister this window from the tab list of Windows and destroy this
907  // window.
908  // The resources attached to this object will be deleted when TabStrip calls
909  // AeroPeekManager::TabClosingAt(). (Please read the comment in TabClosingAt()
910  // for its details.)
911  Destroy();
912
913  // Ask AeroPeekManager to close the tab associated with this thumbnail
914  // window.
915  if (delegate_)
916    delegate_->CloseTab(tab_id_);
917}
918
919LRESULT AeroPeekWindow::OnDwmSendIconicThumbnail(UINT message,
920                                                 WPARAM wparam,
921                                                 LPARAM lparam) {
922  // Update the window title to synchronize the title.
923  SendMessage(hwnd(), WM_SETTEXT, 0, reinterpret_cast<LPARAM>(title_.c_str()));
924
925  // Create an I/O task since it takes long time to resize these images and
926  // send them to Windows. This task signals |ready_to_update_thumbnail_| in
927  // its destructor to notify us when this task has been finished. (We create an
928  // I/O task even when the given thumbnail is empty to stop the "loading"
929  // animation.)
930  DCHECK(delegate_);
931
932  SkBitmap thumbnail;
933  delegate_->GetTabThumbnail(tab_id_, &thumbnail);
934
935  gfx::Size aeropeek_size(HIWORD(lparam), LOWORD(lparam));
936  BrowserThread::PostTask(BrowserThread::IO,
937                          FROM_HERE,
938                          new SendThumbnailTask(hwnd(),
939                                                GetContentBounds(),
940                                                aeropeek_size,
941                                                thumbnail,
942                                                &ready_to_update_thumbnail_));
943  return 0;
944}
945
946LRESULT AeroPeekWindow::OnDwmSendIconicLivePreviewBitmap(UINT message,
947                                                         WPARAM wparam,
948                                                         LPARAM lparam) {
949  // Same as OnDwmSendIconicThumbnail(), we create an I/O task which creates
950  // a preview image used by AeroPeek and send it to Windows. Unlike
951  // OnDwmSendIconicThumbnail(), we don't have to use events for preventing this
952  // window from sending two or more tasks because Windows doesn't send
953  // WM_DWMSENDICONICLIVEPREVIEWBITMAP messages before we send the preview image
954  // to Windows.
955  DCHECK(delegate_);
956
957  SkBitmap preview;
958  delegate_->GetTabPreview(tab_id_, &preview);
959
960  BrowserThread::PostTask(
961      BrowserThread::IO,
962      FROM_HERE,
963      new SendLivePreviewTask(hwnd(), GetContentBounds(), preview));
964
965  return 0;
966}
967
968HICON AeroPeekWindow::OnGetIcon(UINT index) {
969  // Return the application icon if this window doesn't have favicons.
970  // We save this application icon to avoid calling LoadIcon() twice or more.
971  if (favicon_bitmap_.isNull()) {
972    if (!frame_icon_) {
973      frame_icon_ = GetAppIcon();
974    }
975    return frame_icon_;
976  }
977
978  // Create a Windows icon from SkBitmap and send it to Windows. We set this
979  // icon to the ScopedIcon object to delete it in the destructor.
980  favicon_.Set(IconUtil::CreateHICONFromSkBitmap(favicon_bitmap_));
981  return favicon_.Get();
982}
983
984AeroPeekManager::AeroPeekManager(HWND application_window)
985    : application_window_(application_window) {
986}
987
988AeroPeekManager::~AeroPeekManager() {
989  // Delete all AeroPeekWindow objects.
990  for (std::list<AeroPeekWindow*>::iterator i = tab_list_.begin();
991       i != tab_list_.end(); ++i) {
992    AeroPeekWindow* window = *i;
993    delete window;
994  }
995}
996
997void AeroPeekManager::SetContentInsets(const gfx::Insets& insets) {
998  content_insets_ = insets;
999}
1000
1001// static
1002bool AeroPeekManager::Enabled() {
1003  // We enable our custom AeroPeek only when:
1004  // * Chrome is running on Windows 7 and Aero is enabled,
1005  // * Chrome is not launched in application mode, and
1006  // * Chrome is launched with the "--enable-aero-peek-tabs" option.
1007  // TODO(hbono): Bug 37957 <http://crbug.com/37957>: find solutions that avoid
1008  // flooding users with tab thumbnails.
1009  const CommandLine* command_line = CommandLine::ForCurrentProcess();
1010  return base::win::GetVersion() >= base::win::VERSION_WIN7 &&
1011      win_util::ShouldUseVistaFrame() &&
1012      !command_line->HasSwitch(switches::kApp) &&
1013      command_line->HasSwitch(switches::kEnableAeroPeekTabs);
1014}
1015
1016void AeroPeekManager::DeleteAeroPeekWindow(int tab_id) {
1017  // This function does NOT call AeroPeekWindow::Destroy() before deleting
1018  // the AeroPeekWindow instance.
1019  for (std::list<AeroPeekWindow*>::iterator i = tab_list_.begin();
1020       i != tab_list_.end(); ++i) {
1021    AeroPeekWindow* window = *i;
1022    if (window->tab_id() == tab_id) {
1023      tab_list_.erase(i);
1024      delete window;
1025      return;
1026    }
1027  }
1028}
1029AeroPeekWindow* AeroPeekManager::GetAeroPeekWindow(int tab_id) const {
1030  size_t size = tab_list_.size();
1031  for (std::list<AeroPeekWindow*>::const_iterator i = tab_list_.begin();
1032       i != tab_list_.end(); ++i) {
1033    AeroPeekWindow* window = *i;
1034    if (window->tab_id() == tab_id)
1035      return window;
1036  }
1037  return NULL;
1038}
1039
1040TabContents* AeroPeekManager::GetTabContents(int tab_id) const {
1041  for (TabContentsIterator iterator; !iterator.done(); ++iterator) {
1042    TabContents* target_contents = *iterator;
1043    if (target_contents->controller().session_id().id() == tab_id)
1044      return target_contents;
1045  }
1046  return NULL;
1047}
1048
1049int AeroPeekManager::GetTabID(TabContents* contents) const {
1050  if (!contents)
1051    return -1;
1052  return contents->controller().session_id().id();
1053}
1054
1055///////////////////////////////////////////////////////////////////////////////
1056// AeroPeekManager, TabStripModelObserver implementation:
1057
1058void AeroPeekManager::TabInsertedAt(TabContents* contents,
1059                                    int index,
1060                                    bool foreground) {
1061  // If there are not any AeroPeekWindow objects associated with the given
1062  // tab, Create a new AeroPeekWindow object and add it to the list.
1063  if (GetAeroPeekWindow(GetTabID(contents)))
1064    return;
1065
1066  AeroPeekWindow* window = new AeroPeekWindow(application_window_,
1067                                              this,
1068                                              GetTabID(contents),
1069                                              foreground,
1070                                              contents->GetTitle(),
1071                                              contents->GetFavIcon());
1072  if (!window)
1073    return;
1074
1075  tab_list_.push_back(window);
1076}
1077
1078void AeroPeekManager::TabClosingAt(TabStripModel* tab_strip_model,
1079                                   TabContents* contents,
1080                                   int index) {
1081  // |tab_strip_model| is NULL when this is being called from TabDetachedAt
1082  // below.
1083  // Delete the AeroPeekWindow object associated with this tab and all its
1084  // resources. (AeroPeekWindow::Destory() also removes this tab from the tab
1085  // list of Windows.)
1086  AeroPeekWindow* window = GetAeroPeekWindow(GetTabID(contents));
1087  if (!window)
1088    return;
1089
1090  window->Destroy();
1091  DeleteAeroPeekWindow(GetTabID(contents));
1092}
1093
1094void AeroPeekManager::TabDetachedAt(TabContents* contents, int index) {
1095  // Same as TabClosingAt(), we remove this tab from the tab list and delete
1096  // its AeroPeekWindow.
1097  // Chrome will call TabInsertedAt() when this tab is inserted to another
1098  // TabStrip. We will re-create an AeroPeekWindow object for this tab and
1099  // re-add it to the tab list there.
1100  TabClosingAt(NULL, contents, index);
1101}
1102
1103void AeroPeekManager::TabSelectedAt(TabContents* old_contents,
1104                                    TabContents* new_contents,
1105                                    int index,
1106                                    bool user_gesture) {
1107  // Deactivate the old window in the thumbnail list and activate the new one
1108  // to synchronize the thumbnail list with TabStrip.
1109  AeroPeekWindow* old_window = GetAeroPeekWindow(GetTabID(old_contents));
1110  if (old_window)
1111    old_window->Deactivate();
1112
1113  AeroPeekWindow* new_window = GetAeroPeekWindow(GetTabID(new_contents));
1114  if (new_window)
1115    new_window->Activate();
1116}
1117
1118void AeroPeekManager::TabMoved(TabContents* contents,
1119                               int from_index,
1120                               int to_index,
1121                               bool pinned_state_changed) {
1122  // TODO(hbono): we need to reorder the thumbnail list of Windows here?
1123  // (Unfortunately, it is not so trivial to reorder the thumbnail list when
1124  // we detach/attach tabs.)
1125}
1126
1127void AeroPeekManager::TabChangedAt(TabContents* contents,
1128                                   int index,
1129                                   TabChangeType change_type) {
1130  // Retrieve the AeroPeekWindow object associated with this tab, update its
1131  // title, and post a task that update its thumbnail image if necessary.
1132  AeroPeekWindow* window = GetAeroPeekWindow(GetTabID(contents));
1133  if (!window)
1134    return;
1135
1136  // Update the title, the favicon, and the thumbnail used for AeroPeek.
1137  // These function don't actually update the icon and the thumbnail until
1138  // Windows needs them (e.g. when a user hovers a taskbar icon) to avoid
1139  // hurting the rendering performance. (These functions just save the
1140  // information needed for handling update requests from Windows.)
1141  window->SetTitle(contents->GetTitle());
1142  window->SetFavIcon(contents->GetFavIcon());
1143  window->Update(contents->is_loading());
1144}
1145
1146///////////////////////////////////////////////////////////////////////////////
1147// AeroPeekManager, AeroPeekWindowDelegate implementation:
1148
1149void AeroPeekManager::ActivateTab(int tab_id) {
1150  // Ask TabStrip to activate this tab.
1151  // We don't have to update thumbnails now since TabStrip will call
1152  // TabSelectedAt() when it actually activates this tab.
1153  TabContents* contents = GetTabContents(tab_id);
1154  if (contents && contents->delegate())
1155    contents->delegate()->ActivateContents(contents);
1156}
1157
1158void AeroPeekManager::CloseTab(int tab_id) {
1159  // Ask TabStrip to close this tab.
1160  // TabStrip will call TabClosingAt() when it actually closes this tab. We
1161  // will delete the AeroPeekWindow object attached to this tab there.
1162  TabContents* contents = GetTabContents(tab_id);
1163  if (contents && contents->delegate())
1164    contents->delegate()->CloseContents(contents);
1165}
1166
1167void AeroPeekManager::GetContentInsets(gfx::Insets* insets) {
1168  *insets = content_insets_;
1169}
1170
1171bool AeroPeekManager::GetTabThumbnail(int tab_id, SkBitmap* thumbnail) {
1172  DCHECK(thumbnail);
1173
1174  // Copy the thumbnail image and the favicon of this tab. We will resize the
1175  // images and send them to Windows.
1176  TabContents* contents = GetTabContents(tab_id);
1177  if (!contents)
1178    return false;
1179
1180  ThumbnailGenerator* generator = g_browser_process->GetThumbnailGenerator();
1181  DCHECK(generator);
1182  *thumbnail = generator->GetThumbnailForRenderer(contents->render_view_host());
1183
1184  return true;
1185}
1186
1187bool AeroPeekManager::GetTabPreview(int tab_id, SkBitmap* preview) {
1188  DCHECK(preview);
1189
1190  // Retrieve the BackingStore associated with the given tab and return its
1191  // SkPlatformCanvas.
1192  TabContents* contents = GetTabContents(tab_id);
1193  if (!contents)
1194    return false;
1195
1196  RenderViewHost* render_view_host = contents->render_view_host();
1197  if (!render_view_host)
1198    return false;
1199
1200  BackingStore* backing_store = render_view_host->GetBackingStore(false);
1201  if (!backing_store)
1202    return false;
1203
1204  // Create a copy of this BackingStore image.
1205  // This code is just copied from "thumbnail_generator.cc".
1206  skia::PlatformCanvas canvas;
1207  if (!backing_store->CopyFromBackingStore(gfx::Rect(backing_store->size()),
1208                                           &canvas))
1209    return false;
1210
1211  const SkBitmap& bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
1212  bitmap.copyTo(preview, SkBitmap::kARGB_8888_Config);
1213  return true;
1214}
1215