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