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