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