| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/aeropeek_manager.h" |
| |
| #include <dwmapi.h> |
| #include <shobjidl.h> |
| |
| #include "app/win/shell.h" |
| #include "base/command_line.h" |
| #include "base/memory/scoped_native_library.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/win/scoped_comptr.h" |
| #include "base/win/scoped_gdi_object.h" |
| #include "base/win/scoped_hdc.h" |
| #include "base/win/windows_version.h" |
| #include "chrome/browser/app_icon_win.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/tab_contents/thumbnail_generator.h" |
| #include "chrome/browser/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/installer/util/browser_distribution.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/renderer_host/backing_store.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/browser/tab_contents/tab_contents_delegate.h" |
| #include "content/browser/tab_contents/tab_contents_view.h" |
| #include "skia/ext/image_operations.h" |
| #include "skia/ext/platform_canvas.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/base/win/window_impl.h" |
| #include "ui/gfx/gdi_util.h" |
| #include "ui/gfx/icon_util.h" |
| #include "views/widget/widget_win.h" |
| |
| namespace { |
| |
| // Macros and COM interfaces used in this file. |
| // These interface declarations are copied from Windows SDK 7. |
| // TODO(hbono): Bug 16903: to be deleted when we use Windows SDK 7. |
| |
| // Windows SDK 7 defines these macros only when _WIN32_WINNT >= 0x0601. |
| // Since Chrome currently sets _WIN32_WINNT to 0x0600, copy these defines here |
| // so we can use them. |
| #ifndef WM_DWMSENDICONICTHUMBNAIL |
| #define WM_DWMSENDICONICTHUMBNAIL 0x0323 |
| #endif |
| #ifndef WM_DWMSENDICONICLIVEPREVIEWBITMAP |
| #define WM_DWMSENDICONICLIVEPREVIEWBITMAP 0x0326 |
| #endif |
| |
| // COM interfaces defined only in Windows SDK 7. |
| #ifndef __ITaskbarList2_INTERFACE_DEFINED__ |
| #define __ITaskbarList2_INTERFACE_DEFINED__ |
| |
| // EXTERN_C const IID IID_ITaskbarList2; |
| MIDL_INTERFACE("602D4995-B13A-429b-A66E-1935E44F4317") |
| ITaskbarList2 : public ITaskbarList { |
| public: |
| virtual HRESULT STDMETHODCALLTYPE MarkFullscreenWindow( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [in] */ BOOL fFullscreen) = 0; |
| }; |
| |
| #endif /* __ITaskbarList2_INTERFACE_DEFINED__ */ |
| |
| #ifndef __ITaskbarList3_INTERFACE_DEFINED__ |
| #define __ITaskbarList3_INTERFACE_DEFINED__ |
| |
| typedef struct tagTHUMBBUTTON { |
| DWORD dwMask; |
| UINT iId; |
| UINT iBitmap; |
| HICON hIcon; |
| WCHAR szTip[ 260 ]; |
| DWORD dwFlags; |
| } THUMBBUTTON; |
| |
| typedef struct tagTHUMBBUTTON *LPTHUMBBUTTON; |
| |
| // THUMBBUTTON flags |
| #define THBF_ENABLED 0x0000 |
| #define THBF_DISABLED 0x0001 |
| #define THBF_DISMISSONCLICK 0x0002 |
| #define THBF_NOBACKGROUND 0x0004 |
| #define THBF_HIDDEN 0x0008 |
| // THUMBBUTTON mask |
| #define THB_BITMAP 0x0001 |
| #define THB_ICON 0x0002 |
| #define THB_TOOLTIP 0x0004 |
| #define THB_FLAGS 0x0008 |
| #define THBN_CLICKED 0x1800 |
| |
| typedef /* [v1_enum] */ enum TBPFLAG { |
| TBPF_NOPROGRESS = 0, |
| TBPF_INDETERMINATE = 0x1, |
| TBPF_NORMAL = 0x2, |
| TBPF_ERROR = 0x4, |
| TBPF_PAUSED = 0x8 |
| } TBPFLAG; |
| |
| // EXTERN_C const IID IID_ITaskbarList3; |
| |
| MIDL_INTERFACE("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf") |
| ITaskbarList3 : public ITaskbarList2 { |
| public: |
| virtual HRESULT STDMETHODCALLTYPE SetProgressValue( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [in] */ ULONGLONG ullCompleted, |
| /* [in] */ ULONGLONG ullTotal) = 0; |
| virtual HRESULT STDMETHODCALLTYPE SetProgressState( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [in] */ TBPFLAG tbpFlags) = 0; |
| virtual HRESULT STDMETHODCALLTYPE RegisterTab( |
| /* [in] */ __RPC__in HWND hwndTab, |
| /* [in] */ __RPC__in HWND hwndMDI) = 0; |
| virtual HRESULT STDMETHODCALLTYPE UnregisterTab( |
| /* [in] */ __RPC__in HWND hwndTab) = 0; |
| virtual HRESULT STDMETHODCALLTYPE SetTabOrder( |
| /* [in] */ __RPC__in HWND hwndTab, |
| /* [in] */ __RPC__in HWND hwndInsertBefore) = 0; |
| virtual HRESULT STDMETHODCALLTYPE SetTabActive( |
| /* [in] */ __RPC__in HWND hwndTab, |
| /* [in] */ __RPC__in HWND hwndMDI, |
| /* [in] */ DWORD dwReserved) = 0; |
| virtual HRESULT STDMETHODCALLTYPE ThumbBarAddButtons( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [in] */ UINT cButtons, |
| /* [size_is][in] */ __RPC__in_ecount_full(cButtons) |
| LPTHUMBBUTTON pButton) = 0; |
| virtual HRESULT STDMETHODCALLTYPE ThumbBarUpdateButtons( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [in] */ UINT cButtons, |
| /* [size_is][in] */ __RPC__in_ecount_full(cButtons) |
| LPTHUMBBUTTON pButton) = 0; |
| virtual HRESULT STDMETHODCALLTYPE ThumbBarSetImageList( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [in] */ __RPC__in_opt HIMAGELIST himl) = 0; |
| virtual HRESULT STDMETHODCALLTYPE SetOverlayIcon( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [in] */ __RPC__in HICON hIcon, |
| /* [string][in] */ __RPC__in_string LPCWSTR pszDescription) = 0; |
| virtual HRESULT STDMETHODCALLTYPE SetThumbnailTooltip( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [string][in] */ __RPC__in_string LPCWSTR pszTip) = 0; |
| virtual HRESULT STDMETHODCALLTYPE SetThumbnailClip( |
| /* [in] */ __RPC__in HWND hwnd, |
| /* [in] */ __RPC__in RECT *prcClip) = 0; |
| }; |
| #endif // __ITaskbarList3_INTERFACE_DEFINED__ |
| |
| // END OF WINDOWS SDK 7.0 |
| |
| } // namespace |
| |
| namespace { |
| |
| // Sends a thumbnail bitmap to Windows. Windows assumes this function is called |
| // when a WM_DWMSENDICONICTHUMBNAIL message sent to a place-holder window. We |
| // can use DwmInvalidateIconicBitmap() to force Windows to send the message. |
| HRESULT CallDwmSetIconicThumbnail(HWND window, HBITMAP bitmap, DWORD flags) { |
| FilePath dwmapi_path(base::GetNativeLibraryName(L"dwmapi")); |
| base::ScopedNativeLibrary dwmapi(dwmapi_path); |
| |
| typedef HRESULT (STDAPICALLTYPE *DwmSetIconicThumbnailProc)( |
| HWND, HBITMAP, DWORD); |
| DwmSetIconicThumbnailProc dwm_set_iconic_thumbnail = |
| static_cast<DwmSetIconicThumbnailProc>( |
| dwmapi.GetFunctionPointer("DwmSetIconicThumbnail")); |
| |
| if (!dwm_set_iconic_thumbnail) |
| return E_FAIL; |
| |
| return dwm_set_iconic_thumbnail(window, bitmap, flags); |
| } |
| |
| // Sends a preview bitmap to Windows. Windows assumes this function is called |
| // when a WM_DWMSENDICONICLIVEPREVIEWBITMAP message sent to a place-holder |
| // window. |
| HRESULT CallDwmSetIconicLivePreviewBitmap(HWND window, |
| HBITMAP bitmap, |
| POINT* client, |
| DWORD flags) { |
| FilePath dwmapi_path(base::GetNativeLibraryName(L"dwmapi")); |
| base::ScopedNativeLibrary dwmapi(dwmapi_path); |
| |
| typedef HRESULT (STDAPICALLTYPE *DwmSetIconicLivePreviewBitmapProc)( |
| HWND, HBITMAP, POINT*, DWORD); |
| DwmSetIconicLivePreviewBitmapProc dwm_set_live_preview_bitmap = |
| static_cast<DwmSetIconicLivePreviewBitmapProc>( |
| dwmapi.GetFunctionPointer("DwmSetIconicLivePreviewBitmap")); |
| |
| if (!dwm_set_live_preview_bitmap) |
| return E_FAIL; |
| |
| return dwm_set_live_preview_bitmap(window, bitmap, client, flags); |
| } |
| |
| // Invalidates the thumbnail image of the specified place-holder window. (See |
| // the comments in CallDwmSetIconicThumbnai()). |
| HRESULT CallDwmInvalidateIconicBitmaps(HWND window) { |
| FilePath dwmapi_path(base::GetNativeLibraryName(L"dwmapi")); |
| base::ScopedNativeLibrary dwmapi(dwmapi_path); |
| |
| typedef HRESULT (STDAPICALLTYPE *DwmInvalidateIconicBitmapsProc)(HWND); |
| DwmInvalidateIconicBitmapsProc dwm_invalidate_iconic_bitmaps = |
| static_cast<DwmInvalidateIconicBitmapsProc>( |
| dwmapi.GetFunctionPointer("DwmInvalidateIconicBitmaps")); |
| |
| if (!dwm_invalidate_iconic_bitmaps) |
| return E_FAIL; |
| |
| return dwm_invalidate_iconic_bitmaps(window); |
| } |
| |
| } // namespace |
| |
| namespace { |
| |
| // Tasks used in this file. |
| // This file uses three I/O tasks to implement AeroPeek: |
| // * RegisterThumbnailTask |
| // Register a tab into the thumbnail list of Windows. |
| // * SendThumbnailTask |
| // Create a thumbnail image and send it to Windows. |
| // * SendLivePreviewTask |
| // Create a preview image and send it to Windows. |
| // These I/O tasks indirectly access the specified tab through the |
| // AeroPeekWindowDelegate interface to prevent these tasks from accessing the |
| // deleted tabs. |
| |
| // A task that registers a thumbnail window as a child of the specified |
| // browser application. |
| class RegisterThumbnailTask : public Task { |
| public: |
| RegisterThumbnailTask(HWND frame_window, HWND window, bool active) |
| : frame_window_(frame_window), |
| window_(window), |
| active_(active) { |
| } |
| |
| private: |
| void Run() { |
| // Set the App ID of the browser for this place-holder window to tell |
| // that this window is a child of the browser application, i.e. to tell |
| // that this thumbnail window should be displayed when we hover the |
| // browser icon in the taskbar. |
| // TODO(mattm): This should use ShellIntegration::GetChromiumAppId to work |
| // properly with multiple profiles. |
| app::win::SetAppIdForWindow( |
| BrowserDistribution::GetDistribution()->GetBrowserAppId(), window_); |
| |
| // Register this place-holder window to the taskbar as a child of |
| // the browser window and add it to the end of its tab list. |
| // Correctly, this registration should be called after this browser window |
| // receives a registered window message "TaskbarButtonCreated", which |
| // means that Windows creates a taskbar button for this window in its |
| // taskbar. But it seems to be OK to register it without checking the |
| // message. |
| // TODO(hbono): we need to check this registered message? |
| base::win::ScopedComPtr<ITaskbarList3> taskbar; |
| if (FAILED(taskbar.CreateInstance(CLSID_TaskbarList, NULL, |
| CLSCTX_INPROC_SERVER)) || |
| FAILED(taskbar->HrInit()) || |
| FAILED(taskbar->RegisterTab(window_, frame_window_)) || |
| FAILED(taskbar->SetTabOrder(window_, NULL))) |
| return; |
| if (active_) |
| taskbar->SetTabActive(window_, frame_window_, 0); |
| } |
| |
| private: |
| // An application window to which we are going to register a tab window. |
| // This "application window" is a browser frame in terms of Chrome. |
| HWND frame_window_; |
| |
| // A tab window. |
| // After we register this window as a child of the above application window, |
| // Windows sends AeroPeek events to this window. |
| // It seems this window MUST be a tool window. |
| HWND window_; |
| |
| // Whether or not we need to activate this tab by default. |
| bool active_; |
| }; |
| |
| // A task which creates a thumbnail image used by AeroPeek and sends it to |
| // Windows. |
| class SendThumbnailTask : public Task { |
| public: |
| SendThumbnailTask(HWND aeropeek_window, |
| const gfx::Rect& content_bounds, |
| const gfx::Size& aeropeek_size, |
| const SkBitmap& tab_bitmap, |
| base::WaitableEvent* ready) |
| : aeropeek_window_(aeropeek_window), |
| content_bounds_(content_bounds), |
| aeropeek_size_(aeropeek_size), |
| tab_bitmap_(tab_bitmap), |
| ready_(ready) { |
| } |
| |
| ~SendThumbnailTask() { |
| if (ready_) |
| ready_->Signal(); |
| } |
| |
| private: |
| void Run() { |
| // Calculate the size of the aeropeek thumbnail and resize the tab bitmap |
| // to the size. When the given bitmap is an empty bitmap, we create a dummy |
| // bitmap from the content-area rectangle to create a DIB. (We don't need to |
| // allocate pixels for this case since we don't use them.) |
| gfx::Size thumbnail_size; |
| SkBitmap thumbnail_bitmap; |
| |
| if (tab_bitmap_.isNull() || tab_bitmap_.empty()) { |
| GetThumbnailSize(content_bounds_.width(), content_bounds_.height(), |
| &thumbnail_size); |
| |
| thumbnail_bitmap.setConfig(SkBitmap::kARGB_8888_Config, |
| thumbnail_size.width(), |
| thumbnail_size.height()); |
| } else { |
| GetThumbnailSize(tab_bitmap_.width(), tab_bitmap_.height(), |
| &thumbnail_size); |
| |
| thumbnail_bitmap = skia::ImageOperations::Resize( |
| tab_bitmap_, |
| skia::ImageOperations::RESIZE_LANCZOS3, |
| thumbnail_size.width(), |
| thumbnail_size.height()); |
| } |
| |
| // Create a DIB, copy the resized image, and send the DIB to Windows. |
| // We can delete this DIB after sending it to Windows since Windows creates |
| // a copy of the DIB and use it. |
| base::win::ScopedHDC hdc(CreateCompatibleDC(NULL)); |
| if (!hdc.Get()) { |
| LOG(ERROR) << "cannot create a memory DC: " << GetLastError(); |
| return; |
| } |
| |
| BITMAPINFOHEADER header; |
| gfx::CreateBitmapHeader(thumbnail_size.width(), thumbnail_size.height(), |
| &header); |
| |
| void* bitmap_data = NULL; |
| base::win::ScopedBitmap bitmap( |
| CreateDIBSection(hdc, |
| reinterpret_cast<BITMAPINFO*>(&header), |
| DIB_RGB_COLORS, |
| &bitmap_data, |
| NULL, |
| 0)); |
| |
| if (!bitmap.Get() || !bitmap_data) { |
| LOG(ERROR) << "cannot create a bitmap: " << GetLastError(); |
| return; |
| } |
| |
| SkAutoLockPixels lock(thumbnail_bitmap); |
| int* content_pixels = reinterpret_cast<int*>(bitmap_data); |
| for (int y = 0; y < thumbnail_size.height(); ++y) { |
| for (int x = 0; x < thumbnail_size.width(); ++x) { |
| content_pixels[y * thumbnail_size.width() + x] = |
| GetPixel(thumbnail_bitmap, x, y); |
| } |
| } |
| |
| HRESULT result = CallDwmSetIconicThumbnail(aeropeek_window_, bitmap, 0); |
| if (FAILED(result)) |
| LOG(ERROR) << "cannot set a tab thumbnail: " << result; |
| } |
| |
| // Calculates the thumbnail size sent to Windows so we can preserve the pixel |
| // aspect-ratio of the source bitmap. Since Windows returns an error when we |
| // send an image bigger than the given size, we decrease either the thumbnail |
| // width or the thumbnail height so we can fit the longer edge of the source |
| // window. |
| void GetThumbnailSize(int width, int height, gfx::Size* output) const { |
| float thumbnail_width = static_cast<float>(aeropeek_size_.width()); |
| float thumbnail_height = static_cast<float>(aeropeek_size_.height()); |
| float source_width = static_cast<float>(width); |
| float source_height = static_cast<float>(height); |
| DCHECK(source_width && source_height); |
| |
| float ratio_width = thumbnail_width / source_width; |
| float ratio_height = thumbnail_height / source_height; |
| if (ratio_width > ratio_height) { |
| thumbnail_width = source_width * ratio_height; |
| } else { |
| thumbnail_height = source_height * ratio_width; |
| } |
| |
| output->set_width(static_cast<int>(thumbnail_width)); |
| output->set_height(static_cast<int>(thumbnail_height)); |
| } |
| |
| // Returns a pixel of the specified bitmap. If this bitmap is a dummy bitmap, |
| // this function returns an opaque white pixel instead. |
| int GetPixel(const SkBitmap& bitmap, int x, int y) const { |
| const int* tab_pixels = reinterpret_cast<const int*>(bitmap.getPixels()); |
| if (!tab_pixels) |
| return 0xFFFFFFFF; |
| return tab_pixels[y * bitmap.width() + x]; |
| } |
| |
| private: |
| // A window handle to the place-holder window used by AeroPeek. |
| HWND aeropeek_window_; |
| |
| // The bounding rectangle of the user-perceived content area. |
| // This rectangle is used only for creating a fall-back bitmap. |
| gfx::Rect content_bounds_; |
| |
| // The size of an output image to be sent to Windows. |
| gfx::Size aeropeek_size_; |
| |
| // The source bitmap. |
| SkBitmap tab_bitmap_; |
| |
| // An event to notify when this task finishes. |
| base::WaitableEvent* ready_; |
| }; |
| |
| // A task which creates a preview image used by AeroPeek and sends it to |
| // Windows. |
| // This task becomes more complicated than SendThumbnailTask because this task |
| // calculates the rectangle of the user-perceived content area (infobars + |
| // content area) so Windows can paste the preview image on it. |
| // This task is used if an AeroPeek window receives a |
| // WM_DWMSENDICONICLIVEPREVIEWBITMAP message. |
| class SendLivePreviewTask : public Task { |
| public: |
| SendLivePreviewTask(HWND aeropeek_window, |
| const gfx::Rect& content_bounds, |
| const SkBitmap& tab_bitmap) |
| : aeropeek_window_(aeropeek_window), |
| content_bounds_(content_bounds), |
| tab_bitmap_(tab_bitmap) { |
| } |
| |
| ~SendLivePreviewTask() { |
| } |
| |
| private: |
| void Run() { |
| // Create a DIB for the user-perceived content area of the tab, copy the |
| // tab image into the DIB, and send it to Windows. |
| // We don't need to paste this tab image onto the frame image since Windows |
| // automatically pastes it for us. |
| base::win::ScopedHDC hdc(CreateCompatibleDC(NULL)); |
| if (!hdc.Get()) { |
| LOG(ERROR) << "cannot create a memory DC: " << GetLastError(); |
| return; |
| } |
| |
| BITMAPINFOHEADER header; |
| gfx::CreateBitmapHeader(content_bounds_.width(), content_bounds_.height(), |
| &header); |
| |
| void* bitmap_data = NULL; |
| base::win::ScopedBitmap bitmap( |
| CreateDIBSection(hdc.Get(), |
| reinterpret_cast<BITMAPINFO*>(&header), |
| DIB_RGB_COLORS, &bitmap_data, |
| NULL, 0)); |
| if (!bitmap.Get() || !bitmap_data) { |
| LOG(ERROR) << "cannot create a bitmap: " << GetLastError(); |
| return; |
| } |
| |
| // Copy the tab image onto the DIB. |
| SkAutoLockPixels lock(tab_bitmap_); |
| int* content_pixels = reinterpret_cast<int*>(bitmap_data); |
| for (int y = 0; y < content_bounds_.height(); ++y) { |
| for (int x = 0; x < content_bounds_.width(); ++x) |
| content_pixels[y * content_bounds_.width() + x] = GetTabPixel(x, y); |
| } |
| |
| // Send the preview image to Windows. |
| // We can set its offset to the top left corner of the user-perceived |
| // content area so Windows can paste this bitmap onto the correct |
| // position. |
| POINT content_offset = {content_bounds_.x(), content_bounds_.y()}; |
| HRESULT result = CallDwmSetIconicLivePreviewBitmap( |
| aeropeek_window_, bitmap, &content_offset, 0); |
| if (FAILED(result)) |
| LOG(ERROR) << "cannot send a content image: " << result; |
| } |
| |
| int GetTabPixel(int x, int y) const { |
| // Return the opaque while pixel to prevent old foreground tab from being |
| // shown when we cannot get the specified pixel. |
| const int* tab_pixels = reinterpret_cast<int*>(tab_bitmap_.getPixels()); |
| if (!tab_pixels || x >= tab_bitmap_.width() || y >= tab_bitmap_.height()) |
| return 0xFFFFFFFF; |
| |
| // DWM uses alpha values to distinguish opaque colors and transparent ones. |
| // Set the alpha value of this source pixel to prevent the original window |
| // from being shown through. |
| return 0xFF000000 | tab_pixels[y * tab_bitmap_.width() + x]; |
| } |
| |
| private: |
| // A window handle to the AeroPeek window. |
| HWND aeropeek_window_; |
| |
| // The bounding rectangle of the user-perceived content area. When a tab |
| // hasn't been rendered since a browser window is resized, this size doesn't |
| // become the same as the bitmap size as shown below. |
| // +----------------------+ |
| // | frame window | |
| // | +---------------------+ |
| // | | tab contents | |
| // | +---------------------+ |
| // | | old tab contents | | |
| // | +------------------+ | |
| // +----------------------+ |
| // This rectangle is used for clipping the width and height of the bitmap and |
| // cleaning the old tab contents. |
| // +----------------------+ |
| // | frame window | |
| // | +------------------+ | |
| // | | tab contents | | |
| // | +------------------+ | |
| // | | blank | | |
| // | +------------------+ | |
| // +----------------------+ |
| gfx::Rect content_bounds_; |
| |
| // The bitmap of the source tab. |
| SkBitmap tab_bitmap_; |
| }; |
| |
| } // namespace |
| |
| // A class which implements a place-holder window used by AeroPeek. |
| // The major work of this class are: |
| // * Updating the status of Tab Thumbnails; |
| // * Receiving messages from Windows, and; |
| // * Translating received messages for TabStrip. |
| // This class is used by the AeroPeekManager class, which is a proxy |
| // between TabStrip and Windows 7. |
| class AeroPeekWindow : public ui::WindowImpl { |
| public: |
| AeroPeekWindow(HWND frame_window, |
| AeroPeekWindowDelegate* delegate, |
| int tab_id, |
| bool tab_active, |
| const std::wstring& title, |
| const SkBitmap& favicon_bitmap); |
| ~AeroPeekWindow(); |
| |
| // Activates or deactivates this window. |
| // This window uses this information not only for highlighting the selected |
| // tab when Windows shows the thumbnail list, but also for preventing us |
| // from rendering AeroPeek images for deactivated windows so often. |
| void Activate(); |
| void Deactivate(); |
| |
| // Updates the image of this window. |
| // When the AeroPeekManager class calls this function, this window starts |
| // a task which updates its thumbnail image. |
| // NOTE: to prevent sending lots of tasks that update the thumbnail images |
| // and hurt the system performance, we post a task only when |is_loading| is |
| // false for non-active tabs. (On the other hand, we always post an update |
| // task for an active tab as IE8 does.) |
| void Update(bool is_loading); |
| |
| // Destroys this window. |
| // This function removes this window from the thumbnail list and deletes |
| // all the resources attached to this window, i.e. this object is not valid |
| // any longer after calling this function. |
| void Destroy(); |
| |
| // Updates the title of this window. |
| // This function just sends a WM_SETTEXT message to update the window title. |
| void SetTitle(const std::wstring& title); |
| |
| // Updates the icon used for AeroPeek. Unlike SetTitle(), this function just |
| // saves a copy of the given bitmap since it takes time to create a Windows |
| // icon from this bitmap set it as the window icon. We will create a Windows |
| // when Windows sends a WM_GETICON message to retrieve it. |
| void SetFavicon(const SkBitmap& favicon); |
| |
| // Returns the tab ID associated with this window. |
| int tab_id() { return tab_id_; } |
| |
| // Message handlers. |
| BEGIN_MSG_MAP_EX(TabbedThumbnailWindow) |
| MESSAGE_HANDLER_EX(WM_DWMSENDICONICTHUMBNAIL, OnDwmSendIconicThumbnail) |
| MESSAGE_HANDLER_EX(WM_DWMSENDICONICLIVEPREVIEWBITMAP, |
| OnDwmSendIconicLivePreviewBitmap) |
| |
| MSG_WM_ACTIVATE(OnActivate) |
| MSG_WM_CLOSE(OnClose) |
| MSG_WM_CREATE(OnCreate) |
| MSG_WM_GETICON(OnGetIcon) |
| END_MSG_MAP() |
| |
| private: |
| // Updates the thumbnail image of this window. |
| // This function is a wrapper function of CallDwmInvalidateIconicBitmaps() |
| // but it invalidates the thumbnail only when |ready_| is signaled to prevent |
| // us from posting two or more tasks. |
| void UpdateThumbnail(); |
| |
| // Returns the user-perceived content area. |
| gfx::Rect GetContentBounds() const; |
| |
| // Message-handler functions. |
| // Called when a window has been created. |
| LRESULT OnCreate(LPCREATESTRUCT create_struct); |
| |
| // Called when this thumbnail window is activated, i.e. a user clicks this |
| // thumbnail window. |
| void OnActivate(UINT action, BOOL minimized, HWND window); |
| |
| // Called when this thumbnail window is closed, i.e. a user clicks the close |
| // button of this thumbnail window. |
| void OnClose(); |
| |
| // Called when Windows needs a thumbnail image for this thumbnail window. |
| // Windows can send a WM_DWMSENDICONICTHUMBNAIL message anytime when it |
| // needs the thumbnail bitmap for this place-holder window (e.g. when we |
| // register this place-holder window to Windows, etc.) |
| // When this window receives a WM_DWMSENDICONICTHUMBNAIL message, it HAS TO |
| // create a thumbnail bitmap and send it to Windows through a |
| // DwmSendIconicThumbnail() call. (Windows shows a "page-loading" animation |
| // while it waits for a thumbnail bitmap.) |
| LRESULT OnDwmSendIconicThumbnail(UINT message, |
| WPARAM wparam, |
| LPARAM lparam); |
| |
| // Called when Windows needs a preview image for this thumbnail window. |
| // Same as above, Windows can send a WM_DWMSENDICONICLIVEPREVIEWBITMAP |
| // message anytime when it needs a preview bitmap and we have to create and |
| // send the bitmap when it needs it. |
| LRESULT OnDwmSendIconicLivePreviewBitmap(UINT message, |
| WPARAM wparam, |
| LPARAM lparam); |
| |
| // Called when Windows needs an icon for this thumbnail window. |
| // Windows sends a WM_GETICON message with ICON_SMALL when it needs an |
| // AeroPeek icon. we handle WM_GETICON messages by ourselves so we can create |
| // a custom icon from a favicon only when Windows need it. |
| HICON OnGetIcon(UINT index); |
| |
| private: |
| // An application window which owns this tab. |
| // We show this thumbnail image of this window when a user hovers a mouse |
| // cursor onto the taskbar icon of this application window. |
| HWND frame_window_; |
| |
| // An interface which dispatches events received from Window. |
| // This window notifies events received from Windows to TabStrip through |
| // this interface. |
| // We should not directly access TabContents members since Windows may send |
| // AeroPeek events to a tab closed by Chrome. |
| // To prevent such race condition, we get access to TabContents through |
| // AeroPeekManager. |
| AeroPeekWindowDelegate* delegate_; |
| |
| // A tab ID associated with this window. |
| int tab_id_; |
| |
| // A flag that represents whether or not this tab is active. |
| // This flag is used for preventing us from updating the thumbnail images |
| // when this window is not active. |
| bool tab_active_; |
| |
| // An event that represents whether or not we can post a task which updates |
| // the thumbnail image of this window. |
| // We post a task only when this event is signaled. |
| base::WaitableEvent ready_to_update_thumbnail_; |
| |
| // The title of this tab. |
| std::wstring title_; |
| |
| // The favicon for this tab. |
| SkBitmap favicon_bitmap_; |
| base::win::ScopedHICON favicon_; |
| |
| // The icon used by the frame window. |
| // This icon is used when this tab doesn't have a favicon. |
| HICON frame_icon_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AeroPeekWindow); |
| }; |
| |
| AeroPeekWindow::AeroPeekWindow(HWND frame_window, |
| AeroPeekWindowDelegate* delegate, |
| int tab_id, |
| bool tab_active, |
| const std::wstring& title, |
| const SkBitmap& favicon_bitmap) |
| : frame_window_(frame_window), |
| delegate_(delegate), |
| tab_id_(tab_id), |
| tab_active_(tab_active), |
| ready_to_update_thumbnail_(false, true), |
| title_(title), |
| favicon_bitmap_(favicon_bitmap), |
| frame_icon_(NULL) { |
| // Set the class styles and window styles for this thumbnail window. |
| // An AeroPeek window should be a tool window. (Otherwise, |
| // Windows doesn't send WM_DWMSENDICONICTHUMBNAIL messages.) |
| set_initial_class_style(0); |
| set_window_style(WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION); |
| set_window_ex_style(WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE); |
| } |
| |
| AeroPeekWindow::~AeroPeekWindow() { |
| } |
| |
| void AeroPeekWindow::Activate() { |
| tab_active_ = true; |
| |
| // Create a place-holder window and add it to the tab list if it has not been |
| // created yet. (This case happens when we re-attached a detached window.) |
| if (!IsWindow(hwnd())) { |
| Update(false); |
| return; |
| } |
| |
| // Notify Windows to set the thumbnail focus to this window. |
| base::win::ScopedComPtr<ITaskbarList3> taskbar; |
| HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL, |
| CLSCTX_INPROC_SERVER); |
| if (FAILED(result)) { |
| LOG(ERROR) << "failed creating an ITaskbarList3 interface."; |
| return; |
| } |
| |
| result = taskbar->HrInit(); |
| if (FAILED(result)) { |
| LOG(ERROR) << "failed initializing an ITaskbarList3 interface."; |
| return; |
| } |
| |
| result = taskbar->ActivateTab(hwnd()); |
| if (FAILED(result)) { |
| LOG(ERROR) << "failed activating a thumbnail window."; |
| return; |
| } |
| |
| // Update the thumbnail image to the up-to-date one. |
| UpdateThumbnail(); |
| } |
| |
| void AeroPeekWindow::Deactivate() { |
| tab_active_ = false; |
| } |
| |
| void AeroPeekWindow::Update(bool is_loading) { |
| // Create a place-holder window used by AeroPeek if it has not been created |
| // so Windows can send events used by AeroPeek to this window. |
| // Windows automatically sends a WM_DWMSENDICONICTHUMBNAIL message after this |
| // window is registered to Windows. So, we don't have to invalidate the |
| // thumbnail image of this window now. |
| if (!hwnd()) { |
| gfx::Rect bounds; |
| WindowImpl::Init(frame_window_, bounds); |
| return; |
| } |
| |
| // Invalidate the thumbnail image of this window. |
| // When we invalidate the thumbnail image, we HAVE TO handle a succeeding |
| // WM_DWMSENDICONICTHUMBNAIL message and update the thumbnail image with a |
| // DwmSetIconicThumbnail() call. So, we should not call this function when |
| // we don't have enough information to create a thumbnail. |
| if (tab_active_ || !is_loading) |
| UpdateThumbnail(); |
| } |
| |
| void AeroPeekWindow::Destroy() { |
| if (!IsWindow(hwnd())) |
| return; |
| |
| // Remove this window from the tab list of Windows. |
| base::win::ScopedComPtr<ITaskbarList3> taskbar; |
| HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL, |
| CLSCTX_INPROC_SERVER); |
| if (FAILED(result)) |
| return; |
| |
| result = taskbar->HrInit(); |
| if (FAILED(result)) |
| return; |
| |
| result = taskbar->UnregisterTab(hwnd()); |
| |
| // Destroy this window. |
| DestroyWindow(hwnd()); |
| } |
| |
| void AeroPeekWindow::SetTitle(const std::wstring& title) { |
| title_ = title; |
| } |
| |
| void AeroPeekWindow::SetFavicon(const SkBitmap& favicon) { |
| favicon_bitmap_ = favicon; |
| } |
| |
| void AeroPeekWindow::UpdateThumbnail() { |
| // We post a task to actually create a new thumbnail. So, this function may |
| // be called while we are creating a thumbnail. To prevent this window from |
| // posting two or more tasks, we don't invalidate the current thumbnail |
| // when this event is not signaled. |
| if (ready_to_update_thumbnail_.IsSignaled()) |
| CallDwmInvalidateIconicBitmaps(hwnd()); |
| } |
| |
| gfx::Rect AeroPeekWindow::GetContentBounds() const { |
| RECT content_rect; |
| GetClientRect(frame_window_, &content_rect); |
| |
| gfx::Insets content_insets; |
| delegate_->GetContentInsets(&content_insets); |
| |
| gfx::Rect content_bounds(content_rect); |
| content_bounds.Inset(content_insets.left(), |
| content_insets.top(), |
| content_insets.right(), |
| content_insets.bottom()); |
| return content_bounds; |
| } |
| |
| // message handlers |
| |
| void AeroPeekWindow::OnActivate(UINT action, |
| BOOL minimized, |
| HWND window) { |
| // Windows sends a WM_ACTIVATE message not only when a user clicks this |
| // window (i.e. this window gains the thumbnail focus) but also a user clicks |
| // another window (i.e. this window loses the thumbnail focus.) |
| // Return when this window loses the thumbnail focus since we don't have to |
| // do anything for this case. |
| if (action == WA_INACTIVE) |
| return; |
| |
| // Ask Chrome to activate the tab associated with this thumbnail window. |
| // Since TabStripModel calls AeroPeekManager::TabSelectedAt() when it |
| // finishes activating the tab. We will move the tab focus of AeroPeek there. |
| if (delegate_) |
| delegate_->ActivateTab(tab_id_); |
| } |
| |
| LRESULT AeroPeekWindow::OnCreate(LPCREATESTRUCT create_struct) { |
| // Initialize the window title now since WindowImpl::Init() always calls |
| // CreateWindowEx() with its window name NULL. |
| if (!title_.empty()) { |
| SendMessage(hwnd(), WM_SETTEXT, 0, |
| reinterpret_cast<LPARAM>(title_.c_str())); |
| } |
| |
| // Window attributes for DwmSetWindowAttribute(). |
| // These enum values are copied from Windows SDK 7 so we can compile this |
| // file with or without it. |
| // TODO(hbono): Bug 16903: to be deleted when we use Windows SDK 7. |
| enum { |
| DWMWA_NCRENDERING_ENABLED = 1, |
| DWMWA_NCRENDERING_POLICY, |
| DWMWA_TRANSITIONS_FORCEDISABLED, |
| DWMWA_ALLOW_NCPAINT, |
| DWMWA_CAPTION_BUTTON_BOUNDS, |
| DWMWA_NONCLIENT_RTL_LAYOUT, |
| DWMWA_FORCE_ICONIC_REPRESENTATION, |
| DWMWA_FLIP3D_POLICY, |
| DWMWA_EXTENDED_FRAME_BOUNDS, |
| DWMWA_HAS_ICONIC_BITMAP, |
| DWMWA_DISALLOW_PEEK, |
| DWMWA_EXCLUDED_FROM_PEEK, |
| DWMWA_LAST |
| }; |
| |
| // Set DWM attributes to tell Windows that this window can provide the |
| // bitmaps used by AeroPeek. |
| BOOL force_iconic_representation = TRUE; |
| DwmSetWindowAttribute(hwnd(), |
| DWMWA_FORCE_ICONIC_REPRESENTATION, |
| &force_iconic_representation, |
| sizeof(force_iconic_representation)); |
| |
| BOOL has_iconic_bitmap = TRUE; |
| DwmSetWindowAttribute(hwnd(), |
| DWMWA_HAS_ICONIC_BITMAP, |
| &has_iconic_bitmap, |
| sizeof(has_iconic_bitmap)); |
| |
| // Post a task that registers this thumbnail window to Windows because it |
| // may take some time. (For example, when we create an ITaskbarList3 |
| // interface for the first time, Windows loads DLLs and we need to wait for |
| // some time.) |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| new RegisterThumbnailTask(frame_window_, hwnd(), tab_active_)); |
| |
| return 0; |
| } |
| |
| void AeroPeekWindow::OnClose() { |
| // Unregister this window from the tab list of Windows and destroy this |
| // window. |
| // The resources attached to this object will be deleted when TabStrip calls |
| // AeroPeekManager::TabClosingAt(). (Please read the comment in TabClosingAt() |
| // for its details.) |
| Destroy(); |
| |
| // Ask AeroPeekManager to close the tab associated with this thumbnail |
| // window. |
| if (delegate_) |
| delegate_->CloseTab(tab_id_); |
| } |
| |
| LRESULT AeroPeekWindow::OnDwmSendIconicThumbnail(UINT message, |
| WPARAM wparam, |
| LPARAM lparam) { |
| // Update the window title to synchronize the title. |
| SendMessage(hwnd(), WM_SETTEXT, 0, reinterpret_cast<LPARAM>(title_.c_str())); |
| |
| // Create an I/O task since it takes long time to resize these images and |
| // send them to Windows. This task signals |ready_to_update_thumbnail_| in |
| // its destructor to notify us when this task has been finished. (We create an |
| // I/O task even when the given thumbnail is empty to stop the "loading" |
| // animation.) |
| DCHECK(delegate_); |
| |
| SkBitmap thumbnail; |
| delegate_->GetTabThumbnail(tab_id_, &thumbnail); |
| |
| gfx::Size aeropeek_size(HIWORD(lparam), LOWORD(lparam)); |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| new SendThumbnailTask(hwnd(), |
| GetContentBounds(), |
| aeropeek_size, |
| thumbnail, |
| &ready_to_update_thumbnail_)); |
| return 0; |
| } |
| |
| LRESULT AeroPeekWindow::OnDwmSendIconicLivePreviewBitmap(UINT message, |
| WPARAM wparam, |
| LPARAM lparam) { |
| // Same as OnDwmSendIconicThumbnail(), we create an I/O task which creates |
| // a preview image used by AeroPeek and send it to Windows. Unlike |
| // OnDwmSendIconicThumbnail(), we don't have to use events for preventing this |
| // window from sending two or more tasks because Windows doesn't send |
| // WM_DWMSENDICONICLIVEPREVIEWBITMAP messages before we send the preview image |
| // to Windows. |
| DCHECK(delegate_); |
| |
| SkBitmap preview; |
| delegate_->GetTabPreview(tab_id_, &preview); |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| new SendLivePreviewTask(hwnd(), GetContentBounds(), preview)); |
| |
| return 0; |
| } |
| |
| HICON AeroPeekWindow::OnGetIcon(UINT index) { |
| // Return the application icon if this window doesn't have favicons. |
| // We save this application icon to avoid calling LoadIcon() twice or more. |
| if (favicon_bitmap_.isNull()) { |
| if (!frame_icon_) { |
| frame_icon_ = GetAppIcon(); |
| } |
| return frame_icon_; |
| } |
| |
| // Create a Windows icon from SkBitmap and send it to Windows. We set this |
| // icon to the ScopedIcon object to delete it in the destructor. |
| favicon_.Set(IconUtil::CreateHICONFromSkBitmap(favicon_bitmap_)); |
| return favicon_.Get(); |
| } |
| |
| AeroPeekManager::AeroPeekManager(HWND application_window) |
| : application_window_(application_window), |
| border_left_(0), |
| border_top_(0), |
| toolbar_top_(0) { |
| } |
| |
| AeroPeekManager::~AeroPeekManager() { |
| // Delete all AeroPeekWindow objects. |
| for (std::list<AeroPeekWindow*>::iterator i = tab_list_.begin(); |
| i != tab_list_.end(); ++i) { |
| AeroPeekWindow* window = *i; |
| delete window; |
| } |
| } |
| |
| void AeroPeekManager::SetContentInsets(const gfx::Insets& insets) { |
| content_insets_ = insets; |
| } |
| |
| // static |
| bool AeroPeekManager::Enabled() { |
| // We enable our custom AeroPeek only when: |
| // * Chrome is running on Windows 7 and Aero is enabled, |
| // * Chrome is not launched in application mode, and |
| // * Chrome is launched with the "--enable-aero-peek-tabs" option. |
| // TODO(hbono): Bug 37957 <http://crbug.com/37957>: find solutions that avoid |
| // flooding users with tab thumbnails. |
| const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| return base::win::GetVersion() >= base::win::VERSION_WIN7 && |
| views::WidgetWin::IsAeroGlassEnabled() && |
| !command_line->HasSwitch(switches::kApp) && |
| command_line->HasSwitch(switches::kEnableAeroPeekTabs); |
| } |
| |
| void AeroPeekManager::DeleteAeroPeekWindow(int tab_id) { |
| // This function does NOT call AeroPeekWindow::Destroy() before deleting |
| // the AeroPeekWindow instance. |
| for (std::list<AeroPeekWindow*>::iterator i = tab_list_.begin(); |
| i != tab_list_.end(); ++i) { |
| AeroPeekWindow* window = *i; |
| if (window->tab_id() == tab_id) { |
| tab_list_.erase(i); |
| delete window; |
| return; |
| } |
| } |
| } |
| |
| void AeroPeekManager::DeleteAeroPeekWindowForTab(TabContentsWrapper* tab) { |
| // Delete the AeroPeekWindow object associated with this tab and all its |
| // resources. (AeroPeekWindow::Destory() also removes this tab from the tab |
| // list of Windows.) |
| AeroPeekWindow* window = GetAeroPeekWindow(GetTabID(tab->tab_contents())); |
| if (!window) |
| return; |
| |
| window->Destroy(); |
| DeleteAeroPeekWindow(GetTabID(tab->tab_contents())); |
| } |
| |
| AeroPeekWindow* AeroPeekManager::GetAeroPeekWindow(int tab_id) const { |
| size_t size = tab_list_.size(); |
| for (std::list<AeroPeekWindow*>::const_iterator i = tab_list_.begin(); |
| i != tab_list_.end(); ++i) { |
| AeroPeekWindow* window = *i; |
| if (window->tab_id() == tab_id) |
| return window; |
| } |
| return NULL; |
| } |
| |
| void AeroPeekManager::CreateAeroPeekWindowIfNecessary(TabContentsWrapper* tab, |
| bool foreground) { |
| if (GetAeroPeekWindow(GetTabID(tab->tab_contents()))) |
| return; |
| |
| AeroPeekWindow* window = |
| new AeroPeekWindow(application_window_, |
| this, |
| GetTabID(tab->tab_contents()), |
| foreground, |
| tab->tab_contents()->GetTitle(), |
| tab->tab_contents()->GetFavicon()); |
| tab_list_.push_back(window); |
| } |
| |
| TabContents* AeroPeekManager::GetTabContents(int tab_id) const { |
| for (TabContentsIterator iterator; !iterator.done(); ++iterator) { |
| TabContents* target_contents = (*iterator)->tab_contents(); |
| if (target_contents->controller().session_id().id() == tab_id) |
| return target_contents; |
| } |
| return NULL; |
| } |
| |
| int AeroPeekManager::GetTabID(TabContents* contents) const { |
| if (!contents) |
| return -1; |
| return contents->controller().session_id().id(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // AeroPeekManager, TabStripModelObserver implementation: |
| |
| void AeroPeekManager::TabInsertedAt(TabContentsWrapper* contents, |
| int index, |
| bool foreground) { |
| if (!contents) |
| return; |
| |
| CreateAeroPeekWindowIfNecessary(contents, foreground); |
| } |
| |
| void AeroPeekManager::TabDetachedAt(TabContentsWrapper* contents, int index) { |
| if (!contents) |
| return; |
| |
| // Chrome will call TabInsertedAt() when this tab is inserted to another |
| // TabStrip. We will re-create an AeroPeekWindow object for this tab and |
| // re-add it to the tab list there. |
| DeleteAeroPeekWindowForTab(contents); |
| } |
| |
| void AeroPeekManager::TabSelectedAt(TabContentsWrapper* old_contents, |
| TabContentsWrapper* new_contents, |
| int index, |
| bool user_gesture) { |
| if (old_contents == new_contents) |
| return; |
| |
| // Deactivate the old window in the thumbnail list and activate the new one |
| // to synchronize the thumbnail list with TabStrip. |
| if (old_contents) { |
| AeroPeekWindow* old_window = |
| GetAeroPeekWindow(GetTabID(old_contents->tab_contents())); |
| if (old_window) |
| old_window->Deactivate(); |
| } |
| |
| if (new_contents) { |
| AeroPeekWindow* new_window = |
| GetAeroPeekWindow(GetTabID(new_contents->tab_contents())); |
| if (new_window) |
| new_window->Activate(); |
| } |
| } |
| |
| void AeroPeekManager::TabReplacedAt(TabStripModel* tab_strip_model, |
| TabContentsWrapper* old_contents, |
| TabContentsWrapper* new_contents, |
| int index) { |
| DeleteAeroPeekWindowForTab(old_contents); |
| |
| CreateAeroPeekWindowIfNecessary(new_contents, |
| (index == tab_strip_model->active_index())); |
| // We don't need to update the selection as if |new_contents| is selected the |
| // TabStripModel will send TabSelectedAt. |
| } |
| |
| void AeroPeekManager::TabMoved(TabContentsWrapper* contents, |
| int from_index, |
| int to_index, |
| bool pinned_state_changed) { |
| // TODO(hbono): we need to reorder the thumbnail list of Windows here? |
| // (Unfortunately, it is not so trivial to reorder the thumbnail list when |
| // we detach/attach tabs.) |
| } |
| |
| void AeroPeekManager::TabChangedAt(TabContentsWrapper* contents, |
| int index, |
| TabChangeType change_type) { |
| if (!contents) |
| return; |
| |
| // Retrieve the AeroPeekWindow object associated with this tab, update its |
| // title, and post a task that update its thumbnail image if necessary. |
| AeroPeekWindow* window = |
| GetAeroPeekWindow(GetTabID(contents->tab_contents())); |
| if (!window) |
| return; |
| |
| // Update the title, the favicon, and the thumbnail used for AeroPeek. |
| // These function don't actually update the icon and the thumbnail until |
| // Windows needs them (e.g. when a user hovers a taskbar icon) to avoid |
| // hurting the rendering performance. (These functions just save the |
| // information needed for handling update requests from Windows.) |
| window->SetTitle(contents->tab_contents()->GetTitle()); |
| window->SetFavicon(contents->tab_contents()->GetFavicon()); |
| window->Update(contents->tab_contents()->is_loading()); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // AeroPeekManager, AeroPeekWindowDelegate implementation: |
| |
| void AeroPeekManager::ActivateTab(int tab_id) { |
| // Ask TabStrip to activate this tab. |
| // We don't have to update thumbnails now since TabStrip will call |
| // TabSelectedAt() when it actually activates this tab. |
| TabContents* contents = GetTabContents(tab_id); |
| if (contents && contents->delegate()) |
| contents->delegate()->ActivateContents(contents); |
| } |
| |
| void AeroPeekManager::CloseTab(int tab_id) { |
| // Ask TabStrip to close this tab. |
| // TabStrip will call TabClosingAt() when it actually closes this tab. We |
| // will delete the AeroPeekWindow object attached to this tab there. |
| TabContents* contents = GetTabContents(tab_id); |
| if (contents && contents->delegate()) |
| contents->delegate()->CloseContents(contents); |
| } |
| |
| void AeroPeekManager::GetContentInsets(gfx::Insets* insets) { |
| *insets = content_insets_; |
| } |
| |
| bool AeroPeekManager::GetTabThumbnail(int tab_id, SkBitmap* thumbnail) { |
| DCHECK(thumbnail); |
| |
| // Copy the thumbnail image and the favicon of this tab. We will resize the |
| // images and send them to Windows. |
| TabContents* contents = GetTabContents(tab_id); |
| if (!contents) |
| return false; |
| |
| ThumbnailGenerator* generator = g_browser_process->GetThumbnailGenerator(); |
| DCHECK(generator); |
| *thumbnail = generator->GetThumbnailForRenderer(contents->render_view_host()); |
| |
| return true; |
| } |
| |
| bool AeroPeekManager::GetTabPreview(int tab_id, SkBitmap* preview) { |
| DCHECK(preview); |
| |
| // Retrieve the BackingStore associated with the given tab and return its |
| // SkPlatformCanvas. |
| TabContents* contents = GetTabContents(tab_id); |
| if (!contents) |
| return false; |
| |
| RenderViewHost* render_view_host = contents->render_view_host(); |
| if (!render_view_host) |
| return false; |
| |
| BackingStore* backing_store = render_view_host->GetBackingStore(false); |
| if (!backing_store) |
| return false; |
| |
| // Create a copy of this BackingStore image. |
| // This code is just copied from "thumbnail_generator.cc". |
| skia::PlatformCanvas canvas; |
| if (!backing_store->CopyFromBackingStore(gfx::Rect(backing_store->size()), |
| &canvas)) |
| return false; |
| |
| const SkBitmap& bitmap = canvas.getTopPlatformDevice().accessBitmap(false); |
| bitmap.copyTo(preview, SkBitmap::kARGB_8888_Config); |
| return true; |
| } |