• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 namespace {
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;
62 MIDL_INTERFACE("602D4995-B13A-429b-A66E-1935E44F4317")
63 ITaskbarList2 : 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 
75 typedef struct tagTHUMBBUTTON {
76   DWORD dwMask;
77   UINT iId;
78   UINT iBitmap;
79   HICON hIcon;
80   WCHAR szTip[ 260 ];
81   DWORD dwFlags;
82 } THUMBBUTTON;
83 
84 typedef 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 
99 typedef /* [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 
109 MIDL_INTERFACE("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")
110 ITaskbarList3 : 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 
161 namespace {
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.
166 HRESULT 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.
185 HRESULT 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()).
206 HRESULT 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 
223 namespace {
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.
239 class 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.
294 class 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.
439 class 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.
551 class 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 
698 AeroPeekWindow::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 
720 AeroPeekWindow::~AeroPeekWindow() {
721 }
722 
723 void 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 
758 void AeroPeekWindow::Deactivate() {
759   tab_active_ = false;
760 }
761 
762 void 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 
783 void 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 
804 void AeroPeekWindow::SetTitle(const std::wstring& title) {
805   title_ = title;
806 }
807 
808 void AeroPeekWindow::SetFavicon(const SkBitmap& favicon) {
809   favicon_bitmap_ = favicon;
810 }
811 
812 void 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 
821 gfx::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 
838 void 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 
856 LRESULT 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 
910 void 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 
924 LRESULT 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 
951 LRESULT 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 
973 HICON 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 
989 AeroPeekManager::AeroPeekManager(HWND application_window)
990     : application_window_(application_window),
991       border_left_(0),
992       border_top_(0),
993       toolbar_top_(0) {
994 }
995 
996 AeroPeekManager::~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 
1005 void AeroPeekManager::SetContentInsets(const gfx::Insets& insets) {
1006   content_insets_ = insets;
1007 }
1008 
1009 // static
1010 bool 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 
1024 void 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 
1038 void 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 
1050 AeroPeekWindow* 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 
1061 void 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 
1076 TabContents* 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 
1085 int 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 
1094 void AeroPeekManager::TabInsertedAt(TabContentsWrapper* contents,
1095                                     int index,
1096                                     bool foreground) {
1097   if (!contents)
1098     return;
1099 
1100   CreateAeroPeekWindowIfNecessary(contents, foreground);
1101 }
1102 
1103 void 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 
1113 void 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 
1137 void 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 
1149 void 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 
1158 void 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 
1184 void 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 
1193 void 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 
1202 void AeroPeekManager::GetContentInsets(gfx::Insets* insets) {
1203   *insets = content_insets_;
1204 }
1205 
1206 bool 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 
1222 bool 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