Files
palemoon27/widget/windows/TaskbarPreview.cpp
T
roytam1 34a56e5a1d import changes from `dev' branch of rmottola/Arctic-Fox:
- import from mozilla: Bug 1208969 TSFTextStore shouldn't grant document lock after starting to destroy the context r=emk (5766724701d5) (387b995959)
- import from mozilla: Bug 1096804 - Add a new asynchronous callback interface for Windows taskbar thumbnail and preview image requests. r=roc (8afe4f072efd) (2b5114dbf3)
- Bug 890156 - patch 0.1 - Switch from 'display pixels' to 'desktop pixels' terminology in widget code. r=kats (a5bd32715a)
- Bug 890156 - patch 0.3 - Create a version of nsIWidget::Create that takes Desktop pixels, because that's what we actually need to pass in some cases. r=mstange (ca85c82533)
- Bug 890156 - patch 1 - Add nsIWidget::GetDesktopToDeviceScale() method. r=emk (40a608fccd)
- Bug 1189622 part 1 - Ensure the in-fullscreen-change flag is set before calling the second phase. r=smaug (52b8f80f37)
- Bug 1189622 part 2 - Allow widget's MakeFullScreen to fail, and call FinishFullscreenChange directly if that happens. r=smaug (b9a89868b3)
- Bug 1189622 part 3 - Return failure if fullscreen support is not available for the X11 desktop. r=karlt (8845780b0f)
- Bug 890156 - patch 0.0 - Declare a DesktopPixel type in Units.h, to be used for the coordinate system used by the host system to manage the desktop space. r=kats (230fcc152e)
- Bug 890156 - patch 2 - Expose DevicePixelsPerDesktopPixel through nsIBaseWindow and its implementations. r=emk (255b157d4f)
- Bug 1096804 - Update taskbar thumbnail and preview handler code to support an asynchronous response that receives a canvas from front end js. Deprecate old sync apis. r=roc (c9bdb6a318)
- Bug 1096804 - Fix debug build error, remove use of non-existent HRESULT and assert. r=buildfix (bfc82b5eb5)
- Bug 890156 - patch 3 - Remove gfxWindowsPlatform::GetDPIScale and replace it with methods in WinUtils, ready for per-monitor DPI support. r=emk (e72d779b32)
- Bug 1234422 TSFTextStore should retry to notify TSF/TIP of layout change if synchronous calls of OnLayoutChange() don't cause retrieving layout information r=m_kato (ea7dd1ccd3)
- Bug 1222190 - Implement GetTimeAsyncForPossibleBackwardsSkew for windows to fix backwards skew on hibernation. r=birtles (4cb42d1cda)
- Bug 506815 - Replace MouseTrailer with TrackMouseEvent. r=jimm (454cd15037)
- Bug 1232669 - Fixed typo MOOUSEMOVE in nsWindow r=chutten (cf70097af6)
- Bug 1235821 - Don't track mouseleave when capturing mouse. r=jimm (5a7bbf1d62)
- Bug 1222444 - remove unused variable in nsWindow::GetMessageTimeStamp r=roc (d45f63dd0e)
- Bug 1217565 - Fix assertion in nsWindow::IPCWindowProcHandler to account for suppressed neutering; r=jimm (d27f977c9c)
- Bug 1234838 - Update the windows backwards time skew fix code to use a PostMessage rather than a gecko NS_DispatchToMainThread. r=karlt (74c3d7b3b9)
- Bug 1191583 - Add telemetry probes for touch usage. r=jimm p=ally (7e00f1bb9e)
- Bug 1165303 - avoid hiding and reshowing the window in response to our own settext messages because it makes Windows 10 unhappy, r=jimm (4190d342a8)
- Bug 890156 - patch 4 - Update widget/windows code for per-monitor DPI support. r=emk (76193d54eb)
- put back some XP vs Vista stuff (fba145ee7a)
- Bug 1205643 - "Wrong scrollbar border prevents scrolling to cursor when mouse is on the screen edge". r=karlt (206ad88949)
- Bug 890156 - patch 5 - Make Windows native-theme code handle per-monitor DPI scaling when necessary. r=emk (dad68a3767)
- Bug 890156 - patch 6 - Update window placement code to work with desktop pixels, for per-monitor DPI support on Windows. r=emk (1e9adc0a84)
- Bug 890156 - patch 7 - Declare that we support Windows per-monitor DPI via the app manifest. r=emk (98856d7a17)
- Bug 890156 - patch 8 - Explicitly set line-height in the About dialog, to avoid discrepancies in spacing across screens with different DPI. r=emk (ce8430ea77)
- Bug 1239589 - Change nsBaseWidget::mBounds to a LayoutDeviceIntRect. r=kats. (69ddcf93b0)
- Bug 1239864 (part 1) - Add new, nicer rect-iterators for nsRegion and nsIntRegion. r=roc. (0a7af16ebb)
- Bug 1239864 (part 2) - Use the new rect iterators in nsRegion.cpp. r=roc. (c7413ab413)
- Bug 1239864 (part 3) - Use the new rect iterators in docshell/ and dom/. r=baku. (34d65b2989)
- Bug 1236266 - Use release assert in IPC serialization for regions (r=jmuizelaar) (418442b5fe)
- Bug 1219352 - Rearrange fields in FrameMetrics. r=botond (60fab5aec3)
- Bug 1219352 - Pack the FrameMetrics booleans. r=botond (8b29ce6ca4)
- Bug 1193055 - Only fire TransformBegin/TransformEnd notifications for actual wheel animations. r=botond (20cf23f07f)
- Bug 1193055 - Update windowed plugin visibility in response to apz scroll in the chrome process vs. relying on a full round trip through content. Reduces lag in windowed plugin updates. r=kats (5387bc8841)
- Bug 1193055 - Follow up, remove extraneous methods noted in review comments. r=followup (3a989b6f68)
- Bug 1226826 - Add a CheckerboardEvent class to record stuff when checkerboarding happens. r=botond (a82ac77b4b)
- Bug 1239385 - Use correct coordinate system when computing the amount to scroll during a pinch r=botond (c0e8bec574)
- Bug 1219352 - Update a couple of bool-setters to take a bool argument. r=botond (9efebb15fd)
- Bug 1226826 - Record velocity at point of displayport request in checkerboard event. r=botond (8e496ccc94)
- Bug 1194851 - Add helper functions HasDisplayPort() and HasCriticalDisplayPort(). r=kats (a0da8cd2a1)
- Bug 1229039 - Miscellaneous logging fixes/updates. r=botond (c055e26989)
- Bug 1228597 - Clean up code paths that (un)apply a pres shell resolution. r=tn,botond (683f43d0a8)
- Bug 1232852 (part 9) - Simplify ReleasePointerCaptureCaller. r=tn. (67b0f86278)
- Bug 1229752 - When C++ APZ scroll offset diverges from the Gecko scroll offset, it is not being applied properly to the event position r=botond,tnikkel (f63f0405a9)
- Bug 1226826 - Record paint times in the checkerboard event. r=botond (47f92a1d8d)
- Bug 1239864 (part 4) - Use the new rect iterators in gfx/. r=nical. (4189b5929d)
- Bug 1239864 (part 5) - Use the new rect iterators in gfx/. r=nical. (92b4c72e5f)
- Bug 1232484 - Remove 'using namespace gfx' and missing includes that were papered over by unified builds but can cause breakage. r=BenWa (40f70a6ca1)
- Bug 1239864 (part 6) - Use the new rect iterators in gfx/. r=nical. (f6ececa928)
- Bug 1239864 (part 7) - Use the new rect iterators in gfx/. r=nical. (4bcdc8fbc1)
- Bug 1231888 - Remove AutoCairoPixmanBugWorkaround. r=jrmuizel. (64ad72f140)
- Bug 1238438 (part 1) - Remove dead function gfxUtils::PathFromRegion(). r=nical. (84c95c7f35)
- Bug 1238438 (part 2) - Inline both ClipToRegionInternal() functions.  r=nical. (14276bddb8)
- Bug 1238438 (part 3) - Inline both PathFromRegionInternal() functions. r=nical. (84f9636b9c)
- Bug 1239864 (part 8) - Use the new rect iterators in gfx/. r=nical. (6250fcd573)
2023-08-15 17:39:55 +08:00

427 lines
12 KiB
C++

/* vim: se cin sw=2 ts=2 et : */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TaskbarPreview.h"
#include <nsITaskbarPreviewController.h>
#include <windows.h>
#include <nsError.h>
#include <nsCOMPtr.h>
#include <nsIWidget.h>
#include <nsIBaseWindow.h>
#include <nsIObserverService.h>
#include <nsServiceManagerUtils.h>
#include "nsUXThemeData.h"
#include "nsWindow.h"
#include "nsAppShell.h"
#include "TaskbarPreviewButton.h"
#include "WinUtils.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/Telemetry.h"
// Defined in dwmapi in a header that needs a higher numbered _WINNT #define
#define DWM_SIT_DISPLAYFRAME 0x1
namespace mozilla {
namespace widget {
///////////////////////////////////////////////////////////////////////////////
// TaskbarPreview
TaskbarPreview::TaskbarPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell)
: mTaskbar(aTaskbar),
mController(aController),
mWnd(aHWND),
mVisible(false),
mDocShell(do_GetWeakReference(aShell))
{
// TaskbarPreview may outlive the WinTaskbar that created it
::CoInitialize(nullptr);
WindowHook &hook = GetWindowHook();
hook.AddMonitor(WM_DESTROY, MainWindowHook, this);
}
TaskbarPreview::~TaskbarPreview() {
// Avoid dangling pointer
if (sActivePreview == this)
sActivePreview = nullptr;
// Our subclass should have invoked DetachFromNSWindow already.
NS_ASSERTION(!mWnd, "TaskbarPreview::DetachFromNSWindow was not called before destruction");
// Make sure to release before potentially uninitializing COM
mTaskbar = nullptr;
::CoUninitialize();
}
NS_IMETHODIMP
TaskbarPreview::SetController(nsITaskbarPreviewController *aController) {
NS_ENSURE_ARG(aController);
mController = aController;
return NS_OK;
}
NS_IMETHODIMP
TaskbarPreview::GetController(nsITaskbarPreviewController **aController) {
NS_ADDREF(*aController = mController);
return NS_OK;
}
NS_IMETHODIMP
TaskbarPreview::GetTooltip(nsAString &aTooltip) {
aTooltip = mTooltip;
return NS_OK;
}
NS_IMETHODIMP
TaskbarPreview::SetTooltip(const nsAString &aTooltip) {
mTooltip = aTooltip;
return CanMakeTaskbarCalls() ? UpdateTooltip() : NS_OK;
}
NS_IMETHODIMP
TaskbarPreview::SetVisible(bool visible) {
if (mVisible == visible) return NS_OK;
mVisible = visible;
// If the nsWindow has already been destroyed but the caller is still trying
// to use it then just pretend that everything succeeded. The caller doesn't
// actually have a way to detect this since it's the same case as when we
// CanMakeTaskbarCalls returns false.
if (!mWnd)
return NS_OK;
return visible ? Enable() : Disable();
}
NS_IMETHODIMP
TaskbarPreview::GetVisible(bool *visible) {
*visible = mVisible;
return NS_OK;
}
NS_IMETHODIMP
TaskbarPreview::SetActive(bool active) {
if (active)
sActivePreview = this;
else if (sActivePreview == this)
sActivePreview = nullptr;
return CanMakeTaskbarCalls() ? ShowActive(active) : NS_OK;
}
NS_IMETHODIMP
TaskbarPreview::GetActive(bool *active) {
*active = sActivePreview == this;
return NS_OK;
}
NS_IMETHODIMP
TaskbarPreview::Invalidate() {
if (!mVisible)
return NS_ERROR_FAILURE;
// DWM Composition is required for previews
if (!nsUXThemeData::CheckForCompositor())
return NS_OK;
HWND previewWindow = PreviewWindow();
return FAILED(WinUtils::dwmInvalidateIconicBitmapsPtr(previewWindow))
? NS_ERROR_FAILURE
: NS_OK;
}
nsresult
TaskbarPreview::UpdateTaskbarProperties() {
nsresult rv = UpdateTooltip();
// If we are the active preview and our window is the active window, restore
// our active state - otherwise some other non-preview window is now active
// and should be displayed as so.
if (sActivePreview == this) {
if (mWnd == ::GetActiveWindow()) {
nsresult rvActive = ShowActive(true);
if (NS_FAILED(rvActive))
rv = rvActive;
} else {
sActivePreview = nullptr;
}
}
return rv;
}
nsresult
TaskbarPreview::Enable() {
nsresult rv = NS_OK;
if (CanMakeTaskbarCalls()) {
rv = UpdateTaskbarProperties();
} else {
WindowHook &hook = GetWindowHook();
hook.AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), MainWindowHook, this);
}
return rv;
}
nsresult
TaskbarPreview::Disable() {
WindowHook &hook = GetWindowHook();
(void) hook.RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), MainWindowHook, this);
return NS_OK;
}
bool
TaskbarPreview::IsWindowAvailable() const {
if (mWnd) {
nsWindow* win = WinUtils::GetNSWindowPtr(mWnd);
if(win && !win->Destroyed()) {
return true;
}
}
return false;
}
void
TaskbarPreview::DetachFromNSWindow() {
WindowHook &hook = GetWindowHook();
hook.RemoveMonitor(WM_DESTROY, MainWindowHook, this);
mWnd = nullptr;
}
LRESULT
TaskbarPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
switch (nMsg) {
case WM_DWMSENDICONICTHUMBNAIL:
{
uint32_t width = HIWORD(lParam);
uint32_t height = LOWORD(lParam);
float aspectRatio = width/float(height);
nsresult rv;
float preferredAspectRatio;
rv = mController->GetThumbnailAspectRatio(&preferredAspectRatio);
if (NS_FAILED(rv))
break;
uint32_t thumbnailWidth = width;
uint32_t thumbnailHeight = height;
if (aspectRatio > preferredAspectRatio) {
thumbnailWidth = uint32_t(thumbnailHeight * preferredAspectRatio);
} else {
thumbnailHeight = uint32_t(thumbnailWidth / preferredAspectRatio);
}
DrawBitmap(thumbnailWidth, thumbnailHeight, false);
}
break;
case WM_DWMSENDICONICLIVEPREVIEWBITMAP:
{
uint32_t width, height;
nsresult rv;
rv = mController->GetWidth(&width);
if (NS_FAILED(rv))
break;
rv = mController->GetHeight(&height);
if (NS_FAILED(rv))
break;
double scale = nsIWidget::DefaultScaleOverride();
if (scale <= 0.0) {
scale = WinUtils::LogToPhysFactor(PreviewWindow());
}
DrawBitmap(NSToIntRound(scale * width), NSToIntRound(scale * height), true);
}
break;
}
return ::DefWindowProcW(PreviewWindow(), nMsg, wParam, lParam);
}
bool
TaskbarPreview::CanMakeTaskbarCalls() {
// If the nsWindow has already been destroyed and we know it but our caller
// clearly doesn't so we can't make any calls.
if (!mWnd)
return false;
// Certain functions like SetTabOrder seem to require a visible window. During
// window close, the window seems to be hidden before being destroyed.
if (!::IsWindowVisible(mWnd))
return false;
if (mVisible) {
nsWindow *window = WinUtils::GetNSWindowPtr(mWnd);
NS_ASSERTION(window, "Could not get nsWindow from HWND");
return window->HasTaskbarIconBeenCreated();
}
return false;
}
WindowHook&
TaskbarPreview::GetWindowHook() {
nsWindow *window = WinUtils::GetNSWindowPtr(mWnd);
NS_ASSERTION(window, "Cannot use taskbar previews in an embedded context!");
return window->GetWindowHook();
}
void
TaskbarPreview::EnableCustomDrawing(HWND aHWND, bool aEnable) {
BOOL enabled = aEnable;
WinUtils::dwmSetWindowAttributePtr(
aHWND,
DWMWA_FORCE_ICONIC_REPRESENTATION,
&enabled,
sizeof(enabled));
WinUtils::dwmSetWindowAttributePtr(
aHWND,
DWMWA_HAS_ICONIC_BITMAP,
&enabled,
sizeof(enabled));
}
nsresult
TaskbarPreview::UpdateTooltip() {
NS_ASSERTION(CanMakeTaskbarCalls() && mVisible, "UpdateTooltip called on invisible tab preview");
if (FAILED(mTaskbar->SetThumbnailTooltip(PreviewWindow(), mTooltip.get())))
return NS_ERROR_FAILURE;
return NS_OK;
}
void
TaskbarPreview::DrawBitmap(uint32_t width, uint32_t height, bool isPreview) {
nsresult rv;
nsCOMPtr<nsITaskbarPreviewCallback> callback =
do_CreateInstance("@mozilla.org/widget/taskbar-preview-callback;1", &rv);
if (NS_FAILED(rv)) {
return;
}
((TaskbarPreviewCallback*)callback.get())->SetPreview(this);
if (isPreview) {
((TaskbarPreviewCallback*)callback.get())->SetIsPreview();
mController->RequestPreview(callback);
} else {
mController->RequestThumbnail(callback, width, height);
}
}
///////////////////////////////////////////////////////////////////////////////
// TaskbarPreviewCallback
NS_IMPL_ISUPPORTS(TaskbarPreviewCallback, nsITaskbarPreviewCallback)
/* void done (in nsISupports aCanvas, in boolean aDrawBorder); */
NS_IMETHODIMP
TaskbarPreviewCallback::Done(nsISupports *aCanvas, bool aDrawBorder) {
// We create and destroy TaskbarTabPreviews from front end code in response
// to TabOpen and TabClose events. Each TaskbarTabPreview creates and owns a
// proxy HWND which it hands to Windows as a tab identifier. When a tab
// closes, TaskbarTabPreview Disable() method is called by front end, which
// destroys the proxy window and clears mProxyWindow which is the HWND
// returned from PreviewWindow(). So, since this is async, we should check to
// be sure the tab is still alive before doing all this gfx work and making
// dwm calls. To accomplish this we check the result of PreviewWindow().
if (!aCanvas || !mPreview || !mPreview->PreviewWindow() ||
!mPreview->IsWindowAvailable()) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMHTMLCanvasElement> domcanvas(do_QueryInterface(aCanvas));
dom::HTMLCanvasElement * canvas = ((dom::HTMLCanvasElement*)domcanvas.get());
if (!canvas) {
return NS_ERROR_FAILURE;
}
RefPtr<gfx::SourceSurface> source = canvas->GetSurfaceSnapshot();
if (!source) {
return NS_ERROR_FAILURE;
}
RefPtr<gfxWindowsSurface> target = new gfxWindowsSurface(source->GetSize(),
gfx::SurfaceFormat::A8R8G8B8_UINT32);
if (!target) {
return NS_ERROR_FAILURE;
}
RefPtr<gfx::DataSourceSurface> srcSurface = source->GetDataSurface();
RefPtr<gfxImageSurface> imageSurface = target->GetAsImageSurface();
if (!srcSurface || !imageSurface) {
return NS_ERROR_FAILURE;
}
gfx::DataSourceSurface::MappedSurface sourceMap;
srcSurface->Map(gfx::DataSourceSurface::READ, &sourceMap);
mozilla::gfx::CopySurfaceDataToPackedArray(sourceMap.mData,
imageSurface->Data(),
srcSurface->GetSize(),
sourceMap.mStride,
BytesPerPixel(srcSurface->GetFormat()));
srcSurface->Unmap();
HDC hDC = target->GetDC();
HBITMAP hBitmap = (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP);
DWORD flags = aDrawBorder ? DWM_SIT_DISPLAYFRAME : 0;
POINT pptClient = { 0, 0 };
HRESULT hr;
if (!mIsThumbnail) {
hr = WinUtils::dwmSetIconicLivePreviewBitmapPtr(mPreview->PreviewWindow(),
hBitmap, &pptClient, flags);
} else {
hr = WinUtils::dwmSetIconicThumbnailPtr(mPreview->PreviewWindow(),
hBitmap, flags);
}
MOZ_ASSERT(SUCCEEDED(hr));
return NS_OK;
}
/* static */
bool
TaskbarPreview::MainWindowHook(void *aContext,
HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam,
LRESULT *aResult)
{
NS_ASSERTION(nMsg == nsAppShell::GetTaskbarButtonCreatedMessage() ||
nMsg == WM_DESTROY,
"Window hook proc called with wrong message");
NS_ASSERTION(aContext, "Null context in MainWindowHook");
if (!aContext)
return false;
TaskbarPreview *preview = reinterpret_cast<TaskbarPreview*>(aContext);
if (nMsg == WM_DESTROY) {
// nsWindow is being destroyed
// We can't really do anything at this point including removing hooks
return false;
} else {
nsWindow *window = WinUtils::GetNSWindowPtr(preview->mWnd);
if (window) {
window->SetHasTaskbarIconBeenCreated();
if (preview->mVisible)
preview->UpdateTaskbarProperties();
}
}
return false;
}
TaskbarPreview *
TaskbarPreview::sActivePreview = nullptr;
} // namespace widget
} // namespace mozilla