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

This commit is contained in:
Jim Mathies 2016-01-12 11:08:34 -06:00
parent 44daa19658
commit 9e48f3f866
3 changed files with 131 additions and 83 deletions

View File

@ -23,12 +23,9 @@
#include "WinUtils.h"
#include "gfxWindowsPlatform.h"
#include <nsIBaseWindow.h>
#include <nsICanvasRenderingContextInternal.h>
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include <imgIContainer.h>
#include <nsIDocShell.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
@ -37,53 +34,8 @@
namespace mozilla {
namespace widget {
namespace {
// Shared by all TaskbarPreviews to avoid the expensive creation process.
// Manually refcounted (see gInstCount) by the ctor and dtor of TaskbarPreview.
// This is done because static constructors aren't allowed for perf reasons.
dom::CanvasRenderingContext2D* gCtx = nullptr;
// Used in tracking the number of previews. Used in freeing
// the static 2d rendering context on shutdown.
uint32_t gInstCount = 0;
/* Helper method to lazily create a canvas rendering context and associate a given
* surface with it.
*
* @param shell The docShell used by the canvas context for text settings and other
* misc things.
* @param surface The gfxSurface backing the context
* @param width The width of the given surface
* @param height The height of the given surface
*/
nsresult
GetRenderingContext(nsIDocShell *shell, gfxASurface *surface,
uint32_t width, uint32_t height) {
if (!gCtx) {
// create the canvas rendering context
Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
gCtx = new mozilla::dom::CanvasRenderingContext2D();
NS_ADDREF(gCtx);
}
// Set the surface we'll use to render.
return gCtx->InitializeWithSurface(shell, surface, width, height);
}
/* Helper method for freeing surface resources associated with the rendering context.
*/
void
ResetRenderingContext() {
if (!gCtx)
return;
if (NS_FAILED(gCtx->Reset())) {
NS_RELEASE(gCtx);
gCtx = nullptr;
}
}
}
///////////////////////////////////////////////////////////////////////////////
// TaskbarPreview
TaskbarPreview::TaskbarPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell)
: mTaskbar(aTaskbar),
@ -95,8 +47,6 @@ TaskbarPreview::TaskbarPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewControl
// TaskbarPreview may outlive the WinTaskbar that created it
::CoInitialize(nullptr);
gInstCount++;
WindowHook &hook = GetWindowHook();
hook.AddMonitor(WM_DESTROY, MainWindowHook, this);
}
@ -112,9 +62,6 @@ TaskbarPreview::~TaskbarPreview() {
// Make sure to release before potentially uninitializing COM
mTaskbar = nullptr;
if (--gInstCount == 0)
NS_IF_RELEASE(gCtx);
::CoUninitialize();
}
@ -357,37 +304,90 @@ TaskbarPreview::UpdateTooltip() {
void
TaskbarPreview::DrawBitmap(uint32_t width, uint32_t height, bool isPreview) {
nsresult rv;
RefPtr<gfxWindowsSurface> surface = new gfxWindowsSurface(gfx::IntSize(width, height), gfx::SurfaceFormat::A8R8G8B8_UINT32);
nsCOMPtr<nsIDocShell> shell = do_QueryReferent(mDocShell);
if (!shell)
nsCOMPtr<nsITaskbarPreviewCallback> callback =
do_CreateInstance("@mozilla.org/widget/taskbar-preview-callback;1", &rv);
MOZ_ASSERT(SUCCEEDED(hr));
if (NS_FAILED(rv)) {
return;
}
rv = GetRenderingContext(shell, surface, width, height);
if (NS_FAILED(rv))
return;
((TaskbarPreviewCallback*)callback.get())->SetPreview(this);
bool drawFrame = false;
if (isPreview)
rv = mController->DrawPreview(gCtx, &drawFrame);
else
rv = mController->DrawThumbnail(gCtx, width, height, &drawFrame);
if (isPreview) {
((TaskbarPreviewCallback*)callback.get())->SetIsPreview();
mController->RequestPreview(callback);
} else {
mController->RequestThumbnail(callback, width, height);
}
}
if (NS_FAILED(rv))
return;
///////////////////////////////////////////////////////////////////////////////
// TaskbarPreviewCallback
HDC hDC = surface->GetDC();
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 = drawFrame ? DWM_SIT_DISPLAYFRAME : 0;
DWORD flags = aDrawBorder ? DWM_SIT_DISPLAYFRAME : 0;
POINT pptClient = { 0, 0 };
if (isPreview)
WinUtils::dwmSetIconicLivePreviewBitmapPtr(PreviewWindow(), hBitmap, &pptClient, flags);
else
WinUtils::dwmSetIconicThumbnailPtr(PreviewWindow(), hBitmap, flags);
ResetRenderingContext();
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 */

View File

@ -13,6 +13,7 @@
#undef LogSeverity // SetupAPI.h #defines this as DWORD
#include <nsITaskbarPreview.h>
#include <nsITaskbarPreviewController.h>
#include <nsAutoPtr.h>
#include <nsString.h>
#include <nsWeakPtr.h>
@ -22,11 +23,15 @@
namespace mozilla {
namespace widget {
class TaskbarPreviewCallback;
class TaskbarPreview : public nsITaskbarPreview
{
public:
TaskbarPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell);
friend class TaskbarPreviewCallback;
NS_DECL_NSITASKBARPREVIEW
protected:
@ -97,6 +102,37 @@ private:
static TaskbarPreview *sActivePreview;
};
/*
* Callback object TaskbarPreview hands to preview controllers when we
* request async thumbnail or live preview images. Controllers invoke
* this interface once they have aquired the requested image.
*/
class TaskbarPreviewCallback : public nsITaskbarPreviewCallback
{
public:
TaskbarPreviewCallback() :
mIsThumbnail(true) {
}
NS_DECL_ISUPPORTS
NS_DECL_NSITASKBARPREVIEWCALLBACK
void SetPreview(TaskbarPreview* aPreview) {
mPreview = aPreview;
}
void SetIsPreview() {
mIsThumbnail = false;
}
protected:
virtual ~TaskbarPreviewCallback() {}
private:
RefPtr<TaskbarPreview> mPreview;
bool mIsThumbnail;
};
} // namespace widget
} // namespace mozilla

View File

@ -164,15 +164,27 @@ DefaultController::GetThumbnailAspectRatio(float *aThumbnailAspectRatio) {
return NS_OK;
}
// deprecated
NS_IMETHODIMP
DefaultController::DrawPreview(nsISupports *ctx, bool *rDrawFrame) {
*rDrawFrame = true;
return NS_ERROR_UNEXPECTED;
}
// deprecated
NS_IMETHODIMP
DefaultController::DrawThumbnail(nsISupports *ctx, uint32_t width, uint32_t height, bool *rDrawFrame) {
*rDrawFrame = false;
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
DefaultController::RequestThumbnail(nsITaskbarPreviewCallback *aCallback, uint32_t width, uint32_t height) {
return NS_OK;
}
NS_IMETHODIMP
DefaultController::DrawThumbnail(nsISupports *ctx, uint32_t width, uint32_t height, bool *rDrawFrame) {
*rDrawFrame = false;
DefaultController::RequestPreview(nsITaskbarPreviewCallback *aCallback) {
return NS_OK;
}