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