summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--base/base.gyp1
-rw-r--r--base/scoped_handle_win.h10
-rw-r--r--base/scoped_native_library.h39
-rw-r--r--base/scoped_native_library_unittest.cc30
-rw-r--r--chrome/browser/aeropeek_manager.cc1223
-rw-r--r--chrome/browser/aeropeek_manager.h182
-rw-r--r--chrome/browser/views/frame/browser_view.cc24
-rw-r--r--chrome/browser/views/frame/browser_view.h4
-rwxr-xr-xchrome/chrome_browser.gypi3
10 files changed, 1516 insertions, 1 deletions
diff --git a/AUTHORS b/AUTHORS
index 1d82bb6..b6cd114 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -64,6 +64,7 @@ Alexander Sulfrian <alexander@sulfrian.net>
Philippe Beaudoin <philippe.beaudoin@gmail.com>
Mark Hahnenberg <mhahnenb@gmail.com>
Alex Gartrell <alexgartrell@gmail.com>
+Leith Bade <leith@leithalweapon.geek.nz>
James Choi <jchoi42@pha.jhu.edu>
Paul Kehrer <paul.l.kehrer@gmail.com>
Chamal De Silva <chamal.desilva@gmail.com>
diff --git a/base/base.gyp b/base/base.gyp
index f7be725..2eec8e1 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -99,6 +99,7 @@
'ref_counted_unittest.cc',
'scoped_bstr_win_unittest.cc',
'scoped_comptr_win_unittest.cc',
+ 'scoped_native_library_unittest.cc',
'scoped_ptr_unittest.cc',
'scoped_temp_dir_unittest.cc',
'scoped_variant_win_unittest.cc',
diff --git a/base/scoped_handle_win.h b/base/scoped_handle_win.h
index 1e60024..ea37ba2 100644
--- a/base/scoped_handle_win.h
+++ b/base/scoped_handle_win.h
@@ -186,11 +186,19 @@ class ScopedGDIObject {
DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject);
};
+// An explicit specialization for HICON because we have to call DestroyIcon()
+// instead of DeleteObject() for HICON.
+template<>
+void ScopedGDIObject<HICON>::Close() {
+ if (object_)
+ DestroyIcon(object_);
+}
+
// Typedefs for some common use cases.
typedef ScopedGDIObject<HBITMAP> ScopedBitmap;
typedef ScopedGDIObject<HRGN> ScopedRegion;
typedef ScopedGDIObject<HFONT> ScopedHFONT;
-
+typedef ScopedGDIObject<HICON> ScopedHICON;
// Like ScopedHandle except for HGLOBAL.
template<class T>
diff --git a/base/scoped_native_library.h b/base/scoped_native_library.h
new file mode 100644
index 0000000..e2609f4
--- /dev/null
+++ b/base/scoped_native_library.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 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.
+
+#ifndef BASE_SCOPED_NATIVE_LIBRARY_H_
+#define BASE_SCOPED_NATIVE_LIBRARY_H_
+
+#include "base/file_path.h"
+#include "base/native_library.h"
+
+namespace base {
+
+// A class which encapsulates a base::NativeLibrary object available only in a
+// scope.
+// This class automatically unloads the loaded library in its destructor.
+class ScopedNativeLibrary {
+ public:
+ explicit ScopedNativeLibrary(const FilePath& library_path) {
+ library_ = base::LoadNativeLibrary(library_path);
+ }
+
+ ~ScopedNativeLibrary() {
+ if (library_)
+ base::UnloadNativeLibrary(library_);
+ }
+
+ void* GetFunctionPointer(const char* function_name) {
+ if (!library_)
+ return NULL;
+ return base::GetFunctionPointerFromNativeLibrary(library_, function_name);
+ }
+
+ private:
+ base::NativeLibrary library_;
+};
+
+} // namespace base
+
+#endif // BASE_SCOPED_NATIVE_LIBRARY_H_
diff --git a/base/scoped_native_library_unittest.cc b/base/scoped_native_library_unittest.cc
new file mode 100644
index 0000000..178e20d
--- /dev/null
+++ b/base/scoped_native_library_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2010 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 "base/scoped_native_library.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Tests whether or not a function pointer retrieved via ScopedNativeLibrary
+// is available only in a scope.
+TEST(ScopedNativeLibrary, Basic) {
+#if defined(OS_WIN)
+ // Get the pointer to DirectDrawCreate() from "ddraw.dll" and verify it
+ // is valid only in this scope.
+ // FreeLibrary() doesn't actually unload a DLL until its reference count
+ // becomes zero, i.e. this function pointer is still valid if the DLL used
+ // in this test is also used by another part of this executable.
+ // So, this test uses "ddraw.dll", which is not used by Chrome at all but
+ // installed on all versions of Windows.
+ FARPROC test_function;
+ {
+ FilePath path(base::GetNativeLibraryName(L"ddraw"));
+ base::ScopedNativeLibrary library(path);
+ test_function = reinterpret_cast<FARPROC>(
+ library.GetFunctionPointer("DirectDrawCreate"));
+ EXPECT_EQ(0, IsBadCodePtr(test_function));
+ }
+ EXPECT_NE(0, IsBadCodePtr(test_function));
+#endif
+}
diff --git a/chrome/browser/aeropeek_manager.cc b/chrome/browser/aeropeek_manager.cc
new file mode 100644
index 0000000..ae1dec6
--- /dev/null
+++ b/chrome/browser/aeropeek_manager.cc
@@ -0,0 +1,1223 @@
+// Copyright (c) 2010 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/gfx/gdi_util.h"
+#include "app/gfx/icon_util.h"
+#include "app/gfx/insets.h"
+#include "app/win_util.h"
+#include "app/win/window_impl.h"
+#include "base/command_line.h"
+#include "base/scoped_comptr_win.h"
+#include "base/scoped_handle_win.h"
+#include "base/scoped_native_library.h"
+#include "base/win_util.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/renderer_host/backing_store.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents_view.h"
+#include "chrome/browser/tab_contents/thumbnail_generator.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "skia/ext/image_operations.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkBitmap.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.
+ win_util::SetAppIdForWindow(chrome::kBrowserAppID, 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?
+ ScopedComPtr<ITaskbarList3> taskbar;
+ HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
+ CLSCTX_INPROC_SERVER);
+ if (FAILED(result)) {
+ LOG(INFO) << "failed creating a TaskbarList object: " << result;
+ return;
+ }
+
+ result = taskbar->HrInit();
+ if (FAILED(result)) {
+ LOG(INFO) << "failed initializing a TaskbarList obejct: " << result;
+ return;
+ }
+
+ result = taskbar->RegisterTab(window_, frame_window_);
+ if (FAILED(result)) {
+ LOG(INFO) << "failed registering a thumbnail window: " << result;
+ return;
+ }
+
+ result = taskbar->SetTabOrder(window_, NULL);
+ if (FAILED(result)) {
+ LOG(INFO) << "failed adding a thumbnail window: " << result;
+ return;
+ }
+
+ if (active_) {
+ result = taskbar->SetTabActive(window_, frame_window_, 0);
+ if (FAILED(result))
+ LOG(INFO) << "failed activating a thumbnail window: " << result;
+ }
+ }
+
+ 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.
+ 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;
+ 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.
+ 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;
+ 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 app::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_;
+ 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.
+ 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(NULL, 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.
+ 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.)
+ ChromeThread::PostTask(ChromeThread::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));
+ ChromeThread::PostTask(ChromeThread::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);
+
+ ChromeThread::PostTask(ChromeThread::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_ = LoadIcon(GetModuleHandle(chrome::kBrowserResourcesDll),
+ MAKEINTRESOURCE(IDR_MAINFRAME));
+ }
+ 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) {
+}
+
+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
+ // * Chrome is not lauched in the application mode.
+ return win_util::GetWinVersion() >= win_util::WINVERSION_WIN7 &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(switches::kApp);
+}
+
+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;
+ }
+ }
+}
+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;
+}
+
+TabContents* AeroPeekManager::GetTabContents(int tab_id) const {
+ for (TabContentsIterator iterator; !iterator.done(); ++iterator) {
+ TabContents* target_contents = *iterator;
+ 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(TabContents* contents,
+ int index,
+ bool foreground) {
+ // If there are not any AeroPeekWindow objects associated with the given
+ // tab, Create a new AeroPeekWindow object and add it to the list.
+ if (GetAeroPeekWindow(GetTabID(contents)))
+ return;
+
+ AeroPeekWindow* window = new AeroPeekWindow(application_window_,
+ this,
+ GetTabID(contents),
+ foreground,
+ contents->GetTitle(),
+ contents->GetFavIcon());
+ if (!window)
+ return;
+
+ tab_list_.push_back(window);
+}
+
+void AeroPeekManager::TabClosingAt(TabContents* contents, int index) {
+ // 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(contents));
+ if (!window)
+ return;
+
+ window->Destroy();
+ DeleteAeroPeekWindow(GetTabID(contents));
+}
+
+void AeroPeekManager::TabDetachedAt(TabContents* contents, int index) {
+ // Same as TabClosingAt(), we remove this tab from the tab list and delete
+ // its AeroPeekWindow.
+ // 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.
+ TabClosingAt(contents, index);
+}
+
+void AeroPeekManager::TabSelectedAt(TabContents* old_contents,
+ TabContents* new_contents,
+ int index,
+ bool user_gesture) {
+ // Deactivate the old window in the thumbnail list and activate the new one
+ // to synchronize the thumbnail list with TabStrip.
+ AeroPeekWindow* old_window = GetAeroPeekWindow(GetTabID(old_contents));
+ if (old_window)
+ old_window->Deactivate();
+
+ AeroPeekWindow* new_window = GetAeroPeekWindow(GetTabID(new_contents));
+ if (new_window)
+ new_window->Activate();
+}
+
+void AeroPeekManager::TabMoved(TabContents* 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(TabContents* contents,
+ int index,
+ TabChangeType change_type) {
+ // 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));
+ 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->GetTitle());
+ window->SetFavIcon(contents->GetFavIcon());
+ window->Update(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(gfx::Point(0, 0),
+ backing_store->size()),
+ &canvas))
+ return false;
+
+ const SkBitmap& bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
+ bitmap.copyTo(preview, SkBitmap::kARGB_8888_Config);
+ return true;
+}
diff --git a/chrome/browser/aeropeek_manager.h b/chrome/browser/aeropeek_manager.h
new file mode 100644
index 0000000..c68e2ea
--- /dev/null
+++ b/chrome/browser/aeropeek_manager.h
@@ -0,0 +1,182 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_AEROPEEK_MANAGER_H_
+#define CHROME_BROWSER_AEROPEEK_MANAGER_H_
+
+#include <list>
+
+#include "app/gfx/insets.h"
+#include "base/waitable_event.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+
+namespace gfx {
+class Size;
+}
+class TabContents;
+class AeroPeekWindow;
+class SkBitmap;
+
+// A class which defines interfaces called from AeroPeekWindow.
+// This class is used for dispatching an event received by a thumbnail window
+// and for retrieving information from Chrome.
+// An AeroPeek window receives the following events:
+// * A user clicks an AeroPeek thumbnail.
+// We need to select a tab associated with this window.
+// * A user closes an AeroPeek thumbnail.
+// We need to close a tab associated with this window.
+// * A user clicks a toolbar button in an AeroPeek window.
+// We may need to dispatch this button event to a tab associated with this
+// thumbnail window.
+// <http://msdn.microsoft.com/en-us/library/dd378460(VS.85).aspx#thumbbars>.
+// Also, it needs the following information of the browser:
+// * The bitmap of a tab associated with this window.
+// This bitmap is used for creating thumbnail and preview images.
+// * The rectangle of a browser frame.
+// This rectangle is used for pasting the above bitmap to the right position
+// and for marking the tab-content area as opaque.
+// We assume these functions are called only from a UI thread (i.e.
+// Chrome_BrowserMain).
+class AeroPeekWindowDelegate {
+ public:
+ virtual void ActivateTab(int tab_id) = 0;
+ virtual void CloseTab(int tab_id) = 0;
+ virtual void GetContentInsets(gfx::Insets* insets) = 0;
+ virtual bool GetTabThumbnail(int tab_id, SkBitmap* thumbnail) = 0;
+ virtual bool GetTabPreview(int tab_id, SkBitmap* preview) = 0;
+};
+
+// A class that implements AeroPeek of Windows 7:
+// <http://msdn.microsoft.com/en-us/library/dd378460(VS.85).aspx#thumbnails>.
+// Windows 7 can dispay a thumbnail image of each tab to its taskbar so that
+// a user can preview the contents of a tab (AeroPeek), choose a tab, close
+// a tab, etc.
+// This class implements the TabStripModelObserver interface to receive the
+// following events sent from TabStripModel and dispatch them to Windows:
+// * A tab is added.
+// This class adds a thumbnail window for this tab to the thumbnail list
+// of Windows.
+// * A tab is being closed.
+// This class deletes the thumbnail window associated with this tab from the
+// thumbnail list of Windows.
+// * A tab has been updated.
+// This class updates the image of the thumbnail window associated with this
+// tab.
+// Also, this class receives events sent from Windows via thumbnail windows to
+// TabStripModel:
+// * A thumbnail window is closed.
+// Ask TabStrip to close the tab associated with this thumbnail window.
+// * A thumbnail window is selected.
+// Ask TabStrip to activate the tab associated with this thumbnail window.
+//
+// The simplest usage of this class is:
+// 1. Create an instance of TabThumbnailManager.
+// 2. Add this instance to the observer list of a TabStrip object.
+//
+// scoped_ptr<TabThumbnailManager> manager;
+// manager.reset(new TabThumbnailManager(
+// frame_->GetWindow()->GetNativeWindow(),
+// border_left,
+// border_top,
+// toolbar_top));
+// g_browser->tabstrip_model()->AddObserver(manager);
+//
+// 3. Remove this instance from the observer list of the TabStrip object when
+// we don't need it.
+//
+// g_browser->tabstrip_model()->RemoveObserver(manager);
+//
+class AeroPeekManager : public TabStripModelObserver,
+ public AeroPeekWindowDelegate {
+ public:
+ explicit AeroPeekManager(HWND application_window);
+ ~AeroPeekManager();
+
+ // Sets the margins of the "user-perceived content area".
+ // (See comments of |content_insets_|).
+ void SetContentInsets(const gfx::Insets& insets);
+
+ // Returns whether or not we should enable Tab Thumbnailing and Aero Peek
+ // of Windows 7.
+ static bool Enabled();
+
+ // Overridden from TabStripModelObserver:
+ virtual void TabInsertedAt(TabContents* contents,
+ int index,
+ bool foreground);
+ virtual void TabClosingAt(TabContents* contents, int index);
+ virtual void TabDetachedAt(TabContents* contents, int index);
+ virtual void TabSelectedAt(TabContents* old_contents,
+ TabContents* new_contents,
+ int index,
+ bool user_gesture);
+ virtual void TabMoved(TabContents* contents,
+ int from_index,
+ int to_index,
+ bool pinned_state_changed);
+ virtual void TabChangedAt(TabContents* contents,
+ int index,
+ TabChangeType change_type);
+
+ // Overriden from TabThumbnailWindowDelegate:
+ virtual void CloseTab(int tab_id);
+ virtual void ActivateTab(int tab_id);
+ virtual void GetContentInsets(gfx::Insets* insets);
+ virtual bool GetTabThumbnail(int tab_id, SkBitmap* thumbnail);
+ virtual bool GetTabPreview(int tab_id, SkBitmap* preview);
+
+ private:
+ // Deletes the TabThumbnailWindow object associated with the specified
+ // Tab ID.
+ void DeleteAeroPeekWindow(int tab_id);
+
+ // Retrieves the AeroPeekWindow object associated with the specified
+ // Tab ID.
+ AeroPeekWindow* GetAeroPeekWindow(int tab_id) const;
+
+ // Returns a rectangle that fits into the destination rectangle and keeps
+ // the pixel-aspect ratio of the source one.
+ // (This function currently uses the longer-fit algorithm as IE8 does.)
+ void GetOutputBitmapSize(const gfx::Size& destination,
+ const gfx::Size& source,
+ gfx::Size* output) const;
+
+ // Returns the TabContents object associated with the specified Tab ID only
+ // if it is alive.
+ // Since Windows cannot send AeroPeek events directly to Chrome windows, we
+ // use a place-holder window to receive AeroPeek events. So, when Windows
+ // sends an AeroPeek event, the corresponding tab (and TabContents) may have
+ // been deleted by Chrome. To prevent us from accessing deleted TabContents,
+ // we need to check if the tab is still alive.
+ TabContents* GetTabContents(int tab_id) const;
+
+ // Returns the tab ID from the specified TabContents.
+ int GetTabID(TabContents* contents) const;
+
+ private:
+ // The parent window of the place-holder windows used by AeroPeek.
+ // In the case of Chrome, this window becomes a browser frame.
+ HWND application_window_;
+
+ // The list of the place-holder windows used by AeroPeek.
+ std::list<AeroPeekWindow*> tab_list_;
+
+ // The left and top borders of the frame window.
+ // When we create a preview bitmap, we use these values for preventing from
+ // over-writing the area of the browser frame.
+ int border_left_;
+ int border_top_;
+
+ // The top position of the toolbar.
+ // This value is used for setting the alpha values of the frame area so a
+ // preview image can use transparent colors only in the frame area.
+ int toolbar_top_;
+
+ // The margins of the "user-perceived content area".
+ // This value is used for pasting a tab image onto this "user-perceived
+ // content area" when creating a preview image.
+ gfx::Insets content_insets_;
+};
+
+#endif // CHROME_BROWSER_AEROPEEK_MANAGER_H_
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index 5ceb0a8..87264f4 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -81,6 +81,7 @@
#if defined(OS_WIN)
#include "app/win_util.h"
+#include "chrome/browser/aeropeek_manager.h"
#include "chrome/browser/jumplist.h"
#elif defined(OS_LINUX)
#include "chrome/browser/views/accelerator_table_gtk.h"
@@ -426,6 +427,10 @@ BrowserView::~BrowserView() {
browser_->tabstrip_model()->RemoveObserver(this);
#if defined(OS_WIN)
+ // Remove this observer.
+ if (aeropeek_manager_.get())
+ browser_->tabstrip_model()->RemoveObserver(aeropeek_manager_.get());
+
// Stop hung plugin monitoring.
ticker_.Stop();
ticker_.UnregisterTickHandler(&hung_window_detector_);
@@ -1576,6 +1581,18 @@ void BrowserView::Layout() {
if (GetLayoutManager()) {
GetLayoutManager()->Layout(this);
SchedulePaint();
+#if defined(OS_WIN)
+ // Send the margins of the "user-perceived content area" of this
+ // browser window so AeroPeekManager can render a background-tab image in
+ // the area.
+ if (aeropeek_manager_.get()) {
+ gfx::Insets insets(GetFindBarBoundingBox().y() + 1,
+ GetTabStripBounds().x(),
+ GetTabStripBounds().x(),
+ GetTabStripBounds().x());
+ aeropeek_manager_->SetContentInsets(insets);
+ }
+#endif
}
}
@@ -1704,6 +1721,13 @@ void BrowserView::Init() {
jumplist_.reset(new JumpList);
jumplist_->AddObserver(browser_->profile());
}
+
+ if (AeroPeekManager::Enabled()) {
+ gfx::Rect bounds(frame_->GetBoundsForTabStrip(tabstrip()));
+ aeropeek_manager_.reset(new AeroPeekManager(
+ frame_->GetWindow()->GetNativeWindow()));
+ browser_->tabstrip_model()->AddObserver(aeropeek_manager_.get());
+ }
#endif
browser_extender_.reset(new BrowserExtender());
diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h
index 6498f31..1cdca86a 100644
--- a/chrome/browser/views/frame/browser_view.h
+++ b/chrome/browser/views/frame/browser_view.h
@@ -47,6 +47,7 @@ class FullscreenExitBubble;
class HtmlDialogUIDelegate;
class InfoBarContainer;
#if defined(OS_WIN)
+class AeroPeekManager;
class JumpList;
#endif
class LocationBarView;
@@ -554,6 +555,9 @@ class BrowserView : public BrowserBubbleHost,
// The custom JumpList for Windows 7.
scoped_ptr<JumpList> jumplist_;
+
+ // The custom AeroPeek manager for Windows 7.
+ scoped_ptr<AeroPeekManager> aeropeek_manager_;
#endif
// The timer used to update frames for the Loading Animation.
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index ab32af3..fb75ba1 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -48,6 +48,8 @@
# mocks.
'browser/accessibility_events.h',
'browser/accessibility_events.cc',
+ 'browser/aeropeek_manager.cc',
+ 'browser/aeropeek_manager.h',
'browser/alternate_nav_url_fetcher.cc',
'browser/alternate_nav_url_fetcher.h',
'browser/app_controller_cppsafe_mac.h',
@@ -2898,6 +2900,7 @@
],
# Exclude files that should be excluded for all non-Windows platforms.
'sources!': [
+ 'browser/aeropeek_manager.cc',
'browser/autocomplete/autocomplete_accessibility.cc',
'browser/bookmarks/bookmark_menu_controller.cc',
'browser/bookmarks/bookmark_menu_controller.h',