1 /* vim: se cin sw=2 ts=2 et : */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is mozilla.org code.
19 * The Initial Developer of the Original Code is
21 * Portions created by the Initial Developer are Copyright (C) 2009
22 * the Initial Developer. All Rights Reserved.
25 * Rob Arnold <tellrob@gmail.com>
26 * Jim Mathies <jmathies@mozilla.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7
44 #include "TaskbarPreview.h"
45 #include <nsITaskbarPreviewController.h>
50 #include <nsIWidget.h>
51 #include <nsIBaseWindow.h>
52 #include <nsIObserverService.h>
53 #include <nsServiceManagerUtils.h>
55 #include "nsUXThemeData.h"
57 #include "nsAppShell.h"
58 #include "TaskbarPreviewButton.h"
60 #include <nsIBaseWindow.h>
61 #include <nsICanvasRenderingContextInternal.h>
62 #include <nsIDOMCanvasRenderingContext2D.h>
63 #include <imgIContainer.h>
64 #include <nsIDocShell.h>
66 // Defined in dwmapi in a header that needs a higher numbered _WINNT #define
67 #define DWM_SIT_DISPLAYFRAME 0x1
74 // Shared by all TaskbarPreviews to avoid the expensive creation process.
75 // Manually refcounted (see gInstCount) by the ctor and dtor of TaskbarPreview.
76 // This is done because static constructors aren't allowed for perf reasons.
77 nsIDOMCanvasRenderingContext2D
* gCtx
= NULL
;
78 // Used in tracking the number of previews. Used in freeing
79 // the static 2d rendering context on shutdown.
80 PRUint32 gInstCount
= 0;
82 /* Helper method to lazily create a canvas rendering context and associate a given
85 * @param shell The docShell used by the canvas context for text settings and other
87 * @param surface The gfxSurface backing the context
88 * @param width The width of the given surface
89 * @param height The height of the given surface
92 GetRenderingContext(nsIDocShell
*shell
, gfxASurface
*surface
,
93 PRUint32 width
, PRUint32 height
) {
95 nsCOMPtr
<nsIDOMCanvasRenderingContext2D
> ctx
= gCtx
;
98 // create the canvas rendering context
99 ctx
= do_CreateInstance("@mozilla.org/content/canvas-rendering-context;1?id=2d", &rv
);
101 NS_WARNING("Could not create nsICanvasRenderingContextInternal for tab previews!");
108 nsCOMPtr
<nsICanvasRenderingContextInternal
> ctxI
= do_QueryInterface(ctx
, &rv
);
112 // Set the surface we'll use to render.
113 return ctxI
->InitializeWithSurface(shell
, surface
, width
, height
);
116 /* Helper method for freeing surface resources associated with the rendering context.
119 ResetRenderingContext() {
124 nsCOMPtr
<nsICanvasRenderingContextInternal
> ctxI
= do_QueryInterface(gCtx
, &rv
);
127 if (NS_FAILED(ctxI
->Reset())) {
135 TaskbarPreview::TaskbarPreview(ITaskbarList4
*aTaskbar
, nsITaskbarPreviewController
*aController
, HWND aHWND
, nsIDocShell
*aShell
)
136 : mTaskbar(aTaskbar
),
137 mController(aController
),
140 mDocShell(do_GetWeakReference(aShell
))
142 // TaskbarPreview may outlive the WinTaskbar that created it
143 ::CoInitialize(NULL
);
147 WindowHook
&hook
= GetWindowHook();
148 hook
.AddMonitor(WM_DESTROY
, MainWindowHook
, this);
151 TaskbarPreview::~TaskbarPreview() {
152 // Avoid dangling pointer
153 if (sActivePreview
== this)
154 sActivePreview
= nsnull
;
156 // Our subclass should have invoked DetachFromNSWindow already.
157 NS_ASSERTION(!mWnd
, "TaskbarPreview::DetachFromNSWindow was not called before destruction");
159 // Make sure to release before potentially uninitializing COM
162 if (--gInstCount
== 0)
169 TaskbarPreview::SetController(nsITaskbarPreviewController
*aController
) {
170 NS_ENSURE_ARG(aController
);
172 mController
= aController
;
177 TaskbarPreview::GetController(nsITaskbarPreviewController
**aController
) {
178 NS_ADDREF(*aController
= mController
);
183 TaskbarPreview::GetTooltip(nsAString
&aTooltip
) {
189 TaskbarPreview::SetTooltip(const nsAString
&aTooltip
) {
191 return CanMakeTaskbarCalls() ? UpdateTooltip() : NS_OK
;
195 TaskbarPreview::SetVisible(PRBool visible
) {
196 if (mVisible
== visible
) return NS_OK
;
199 // If the nsWindow has already been destroyed but the caller is still trying
200 // to use it then just pretend that everything succeeded. The caller doesn't
201 // actually have a way to detect this since it's the same case as when we
202 // CanMakeTaskbarCalls returns false.
206 return visible
? Enable() : Disable();
210 TaskbarPreview::GetVisible(PRBool
*visible
) {
216 TaskbarPreview::SetActive(PRBool active
) {
218 sActivePreview
= this;
219 else if (sActivePreview
== this)
220 sActivePreview
= NULL
;
222 return CanMakeTaskbarCalls() ? ShowActive(active
) : NS_OK
;
226 TaskbarPreview::GetActive(PRBool
*active
) {
227 *active
= sActivePreview
== this;
232 TaskbarPreview::Invalidate() {
234 return NS_ERROR_FAILURE
;
236 // DWM Composition is required for previews
237 if (!nsUXThemeData::CheckForCompositor())
240 HWND previewWindow
= PreviewWindow();
241 return FAILED(nsUXThemeData::dwmInvalidateIconicBitmapsPtr(previewWindow
))
247 TaskbarPreview::UpdateTaskbarProperties() {
248 nsresult rv
= UpdateTooltip();
250 // If we are the active preview and our window is the active window, restore
251 // our active state - otherwise some other non-preview window is now active
252 // and should be displayed as so.
253 if (sActivePreview
== this) {
254 if (mWnd
== ::GetActiveWindow()) {
255 nsresult rvActive
= ShowActive(PR_TRUE
);
256 if (NS_FAILED(rvActive
))
259 sActivePreview
= nsnull
;
266 TaskbarPreview::Enable() {
268 if (CanMakeTaskbarCalls()) {
269 rv
= UpdateTaskbarProperties();
271 WindowHook
&hook
= GetWindowHook();
272 hook
.AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), MainWindowHook
, this);
278 TaskbarPreview::Disable() {
279 WindowHook
&hook
= GetWindowHook();
280 (void) hook
.RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), MainWindowHook
, this);
286 TaskbarPreview::DetachFromNSWindow() {
287 WindowHook
&hook
= GetWindowHook();
288 hook
.RemoveMonitor(WM_DESTROY
, MainWindowHook
, this);
293 TaskbarPreview::WndProc(UINT nMsg
, WPARAM wParam
, LPARAM lParam
) {
295 case WM_DWMSENDICONICTHUMBNAIL
:
297 PRUint32 width
= HIWORD(lParam
);
298 PRUint32 height
= LOWORD(lParam
);
299 float aspectRatio
= width
/float(height
);
302 float preferredAspectRatio
;
303 rv
= mController
->GetThumbnailAspectRatio(&preferredAspectRatio
);
307 PRUint32 thumbnailWidth
= width
;
308 PRUint32 thumbnailHeight
= height
;
310 if (aspectRatio
> preferredAspectRatio
) {
311 thumbnailWidth
= PRUint32(thumbnailHeight
* preferredAspectRatio
);
313 thumbnailHeight
= PRUint32(thumbnailWidth
/ preferredAspectRatio
);
316 DrawBitmap(thumbnailWidth
, thumbnailHeight
, PR_FALSE
);
319 case WM_DWMSENDICONICLIVEPREVIEWBITMAP
:
321 PRUint32 width
, height
;
323 rv
= mController
->GetWidth(&width
);
326 rv
= mController
->GetHeight(&height
);
330 DrawBitmap(width
, height
, PR_TRUE
);
334 return ::DefWindowProcW(PreviewWindow(), nMsg
, wParam
, lParam
);
338 TaskbarPreview::CanMakeTaskbarCalls() {
339 // If the nsWindow has already been destroyed and we know it but our caller
340 // clearly doesn't so we can't make any calls.
343 // Certain functions like SetTabOrder seem to require a visible window. During
344 // window close, the window seems to be hidden before being destroyed.
345 if (!::IsWindowVisible(mWnd
))
348 nsWindow
*window
= nsWindow::GetNSWindowPtr(mWnd
);
349 NS_ASSERTION(window
, "Could not get nsWindow from HWND");
350 return window
->HasTaskbarIconBeenCreated();
356 TaskbarPreview::GetWindowHook() {
357 nsWindow
*window
= nsWindow::GetNSWindowPtr(mWnd
);
358 NS_ASSERTION(window
, "Cannot use taskbar previews in an embedded context!");
360 return window
->GetWindowHook();
364 TaskbarPreview::EnableCustomDrawing(HWND aHWND
, PRBool aEnable
) {
365 nsUXThemeData::dwmSetWindowAttributePtr(
367 DWMWA_FORCE_ICONIC_REPRESENTATION
,
371 nsUXThemeData::dwmSetWindowAttributePtr(
373 DWMWA_HAS_ICONIC_BITMAP
,
380 TaskbarPreview::UpdateTooltip() {
381 NS_ASSERTION(CanMakeTaskbarCalls() && mVisible
, "UpdateTooltip called on invisible tab preview");
383 if (FAILED(mTaskbar
->SetThumbnailTooltip(PreviewWindow(), mTooltip
.get())))
384 return NS_ERROR_FAILURE
;
389 TaskbarPreview::DrawBitmap(PRUint32 width
, PRUint32 height
, PRBool isPreview
) {
391 nsRefPtr
<gfxWindowsSurface
> surface
= new gfxWindowsSurface(gfxIntSize(width
, height
), gfxASurface::ImageFormatARGB32
);
393 nsCOMPtr
<nsIDocShell
> shell
= do_QueryReferent(mDocShell
);
398 rv
= GetRenderingContext(shell
, surface
, width
, height
);
402 PRBool drawFrame
= PR_FALSE
;
404 rv
= mController
->DrawPreview(gCtx
, &drawFrame
);
406 rv
= mController
->DrawThumbnail(gCtx
, width
, height
, &drawFrame
);
411 HDC hDC
= surface
->GetDC();
412 HBITMAP hBitmap
= (HBITMAP
)GetCurrentObject(hDC
, OBJ_BITMAP
);
414 DWORD flags
= drawFrame
? DWM_SIT_DISPLAYFRAME
: 0;
415 POINT pptClient
= { 0, 0 };
417 nsUXThemeData::dwmSetIconicLivePreviewBitmapPtr(PreviewWindow(), hBitmap
, &pptClient
, flags
);
419 nsUXThemeData::dwmSetIconicThumbnailPtr(PreviewWindow(), hBitmap
, flags
);
421 ResetRenderingContext();
426 TaskbarPreview::MainWindowHook(void *aContext
,
427 HWND hWnd
, UINT nMsg
,
428 WPARAM wParam
, LPARAM lParam
,
431 NS_ASSERTION(nMsg
== nsAppShell::GetTaskbarButtonCreatedMessage() ||
433 "Window hook proc called with wrong message");
434 TaskbarPreview
*preview
= reinterpret_cast<TaskbarPreview
*>(aContext
);
435 if (nMsg
== WM_DESTROY
) {
436 // nsWindow is being destroyed
437 // We can't really do anything at this point including removing hooks
438 preview
->mWnd
= NULL
;
440 nsWindow
*window
= nsWindow::GetNSWindowPtr(preview
->mWnd
);
441 NS_ASSERTION(window
, "Cannot use taskbar previews in an embedded context!");
443 window
->SetHasTaskbarIconBeenCreated();
445 if (preview
->mVisible
)
446 preview
->UpdateTaskbarProperties();
452 TaskbarPreview::sActivePreview
= nsnull
;
454 } // namespace widget
455 } // namespace mozilla
457 #endif // MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7