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