Home | History | Annotate | Download | only in browser
      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