Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(235)

Side by Side Diff: chrome/browser/aeropeek_manager.cc

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

Powered by Google App Engine
This is Rietveld 408576698