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