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