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