Bug 1252877 Part 2: On Windows capture an image for windowed plugins to be displayed during APZ scroll. r=jimm, r=mattwoodrow

MozReview-Commit-ID: ElE0GD3tLah
This commit is contained in:
Bob Owen 2016-07-18 09:54:02 +01:00
parent b2b638175c
commit 25fe73cb68
16 changed files with 393 additions and 14 deletions

View File

@ -47,6 +47,16 @@ parent:
* on the chrome side. This is only currently used on Windows.
*/
sync SetNativeChildWindow(uintptr_t childWindow);
child:
/**
* Used to set the ID of a scroll capture container from the parent process,
* so that we can create a proxy container in the layer tree.
* @param aScrollCaptureId async container ID of the parent container
* @param aPluginInstanceId plugin ID on which to set the scroll capture ID
*/
async SetScrollCaptureId(uint64_t aScrollCaptureId,
uintptr_t aPluginInstanceId);
};
}

View File

@ -1372,23 +1372,28 @@ PluginInstanceParent::UpdateScrollCapture(bool& aRequestNewCapture)
return true;
}
nsresult
PluginInstanceParent::SetScrollCaptureId(uint64_t aScrollCaptureId)
{
if (aScrollCaptureId == ImageContainer::sInvalidAsyncContainerId) {
return NS_ERROR_FAILURE;
}
mImageContainer = new ImageContainer(aScrollCaptureId);
return NS_OK;
}
nsresult
PluginInstanceParent::GetScrollCaptureContainer(ImageContainer** aContainer)
{
if (!aContainer || !::IsWindow(mPluginHWND)) {
return NS_ERROR_FAILURE;
}
if (!aContainer || !mImageContainer) {
return NS_ERROR_FAILURE;
}
if (!mImageContainer) {
ScheduleScrollCapture(kInitScrollCaptureDelayMs);
return NS_ERROR_FAILURE;
}
RefPtr<ImageContainer> container = GetImageContainer();
container.forget(aContainer);
ImageContainer *container = GetImageContainer();
NS_IF_ADDREF(container);
*aContainer = container;
return NS_OK;
return NS_OK;
}
nsresult

View File

@ -331,6 +331,7 @@ public:
DrawTarget** aDrawTarget);
nsresult EndUpdateBackground(const nsIntRect& aRect);
#if defined(XP_WIN)
nsresult SetScrollCaptureId(uint64_t aScrollCaptureId);
nsresult GetScrollCaptureContainer(mozilla::layers::ImageContainer** aContainer);
nsresult UpdateScrollState(bool aIsScrolling);
#endif

View File

@ -12,6 +12,10 @@
#include "mozilla/DebugOnly.h"
#include "nsDebug.h"
#if defined(XP_WIN)
#include "mozilla/plugins/PluginInstanceParent.h"
#endif
#define PWLOG(...)
//#define PWLOG(...) printf_stderr(__VA_ARGS__)
@ -31,6 +35,25 @@ PluginWidgetChild::~PluginWidgetChild()
MOZ_COUNT_DTOR(PluginWidgetChild);
}
bool
PluginWidgetChild::RecvSetScrollCaptureId(const uint64_t& aScrollCaptureId,
const uintptr_t& aPluginInstanceId)
{
#if defined(XP_WIN)
PluginInstanceParent* instance =
PluginInstanceParent::LookupPluginInstanceByID(aPluginInstanceId);
if (instance) {
NS_WARN_IF(NS_FAILED(instance->SetScrollCaptureId(aScrollCaptureId)));
}
return true;
#else
MOZ_ASSERT_UNREACHABLE(
"PluginWidgetChild::RecvSetScrollCaptureId calls not expected.");
return false;
#endif
}
// Called by the proxy widget when it is destroyed by layout. Only gets
// called once.
void

View File

@ -19,6 +19,9 @@ public:
PluginWidgetChild();
virtual ~PluginWidgetChild();
bool RecvSetScrollCaptureId(const uint64_t& aScrollCaptureId,
const uintptr_t& aPluginInstanceId) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
void SetWidget(mozilla::widget::PluginWidgetProxy* aWidget) {

View File

@ -143,6 +143,11 @@ PluginWidgetParent::RecvCreate(nsresult* aResult)
mozilla::dom::kPluginWidgetContentParentProperty,
GetTabParent()->Manager()->AsContentParent());
NS_ASSERTION(winres, "SetPropW call failure");
uint64_t scrollCaptureId = mWidget->CreateScrollCaptureContainer();
uintptr_t pluginId =
reinterpret_cast<uintptr_t>(mWidget->GetNativeData(NS_NATIVE_PLUGIN_ID));
Unused << SendSetScrollCaptureId(scrollCaptureId, pluginId);
#endif
// This is a special call we make to nsBaseWidget to register this

View File

@ -28,6 +28,7 @@
#include "mozilla/unused.h"
#include "mozilla/DebugOnly.h"
#if defined(XP_WIN)
#include "mozilla/layers/ImageBridgeChild.h"
#include "WinUtils.h"
#endif
#include "mozilla/widget/CompositorWidget.h"
@ -425,6 +426,35 @@ CompositorBridgeChild::RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint
#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
}
#if defined(XP_WIN)
static void
ScheduleSendAllPluginsCaptured(CompositorBridgeChild* aThis, MessageLoop* aLoop)
{
aLoop->PostTask(NewNonOwningRunnableMethod(
aThis, &CompositorBridgeChild::SendAllPluginsCaptured));
}
#endif
bool
CompositorBridgeChild::RecvCaptureAllPlugins(const uintptr_t& aParentWidget)
{
#if defined(XP_WIN)
MOZ_ASSERT(NS_IsMainThread());
nsIWidget::CaptureRegisteredPlugins(aParentWidget);
// Bounce the call to SendAllPluginsCaptured off the ImageBridgeChild loop,
// to make sure that the image updates on that thread have been processed.
ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
NewRunnableFunction(&ScheduleSendAllPluginsCaptured, this,
MessageLoop::current()));
return true;
#else
MOZ_ASSERT_UNREACHABLE(
"CompositorBridgeChild::RecvCaptureAllPlugins calls unexpected.");
return false;
#endif
}
bool
CompositorBridgeChild::RecvHideAllPlugins(const uintptr_t& aParentWidget)
{
@ -436,9 +466,7 @@ CompositorBridgeChild::RecvHideAllPlugins(const uintptr_t& aParentWidget)
MOZ_ASSERT(NS_IsMainThread());
nsTArray<uintptr_t> list;
nsIWidget::UpdateRegisteredPluginWindowVisibility(aParentWidget, list);
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
SendRemotePluginsReady();
#endif
return true;
#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
}

View File

@ -109,6 +109,9 @@ public:
const LayoutDeviceIntRegion& aVisibleRegion,
nsTArray<PluginWindowData>&& aPlugins) override;
virtual bool
RecvCaptureAllPlugins(const uintptr_t& aParentWidget) override;
virtual bool
RecvHideAllPlugins(const uintptr_t& aParentWidget) override;

View File

@ -1973,6 +1973,8 @@ public:
return true;
}
virtual bool RecvAllPluginsCaptured() override { return true; }
virtual bool RecvGetTileSize(int32_t* aWidth, int32_t* aHeight) override
{
*aWidth = gfxPlatform::GetPlatform()->GetTileWidth();
@ -2638,11 +2640,30 @@ CompositorBridgeParent::HideAllPluginWindows()
mDeferPluginWindows = true;
mPluginWindowsHidden = true;
#if defined(XP_WIN)
// We will get an async reply that this has happened and then send hide.
Unused << SendCaptureAllPlugins(parentWidget);
#else
Unused << SendHideAllPlugins(parentWidget);
ScheduleComposition();
#endif
}
#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
bool
CompositorBridgeParent::RecvAllPluginsCaptured()
{
#if defined(XP_WIN)
ForceComposeToTarget(nullptr);
Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey());
return true;
#else
MOZ_ASSERT_UNREACHABLE(
"CompositorBridgeParent::RecvAllPluginsCaptured calls unexpected.");
return false;
#endif
}
void
CrossProcessCompositorBridgeParent::DidComposite(
uint64_t aId,

View File

@ -269,6 +269,8 @@ public:
const ScrollableLayerGuid& aGuid,
const CSSIntRegion& aRegion);
virtual bool RecvAllPluginsCaptured() override;
virtual void ActorDestroy(ActorDestroyReason why) override;
virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,

View File

@ -91,6 +91,13 @@ child:
LayoutDeviceIntRegion aVisibleRegion,
PluginWindowData[] aPlugins);
/**
* Captures an image for all visible child plugins of a given widget for use
* during scrolling.
* @param aParentWidget parent of widgets to be captured
*/
async CaptureAllPlugins(uintptr_t aParentWidget);
/**
* Hides all registered plugin widgets associated with a particular chrome
* widget.
@ -203,6 +210,11 @@ parent:
ScrollableLayerGuid guid,
CSSIntRegion region);
/**
* Sent when the child has finished CaptureAllPlugins.
*/
async AllPluginsCaptured();
async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t id, uint64_t aSerial);
child:

View File

@ -522,6 +522,11 @@ NS_METHOD nsBaseWidget::Destroy()
parent->RemoveChild(this);
}
#if defined(XP_WIN)
// Allow our scroll capture container to be cleaned up, if we have one.
mScrollCaptureContainer = nullptr;
#endif
return NS_OK;
}
@ -2144,6 +2149,81 @@ nsIWidget::UpdateRegisteredPluginWindowVisibility(uintptr_t aOwnerWidget,
#endif
}
#if defined(XP_WIN)
// static
void
nsIWidget::CaptureRegisteredPlugins(uintptr_t aOwnerWidget)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sPluginWidgetList);
// Our visible list is associated with a compositor which is associated with
// a specific top level window. We use the parent widget during iteration
// to skip the plugin widgets owned by other top level windows.
for (auto iter = sPluginWidgetList->Iter(); !iter.Done(); iter.Next()) {
const void* windowId = iter.Key();
nsIWidget* widget = iter.UserData();
MOZ_ASSERT(windowId);
MOZ_ASSERT(widget);
if (!widget->Destroyed() && widget->IsVisible()) {
if ((uintptr_t)widget->GetParent() == aOwnerWidget) {
widget->UpdateScrollCapture();
}
}
}
}
uint64_t
nsBaseWidget::CreateScrollCaptureContainer()
{
mScrollCaptureContainer =
LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
if (!mScrollCaptureContainer) {
NS_WARNING("Failed to create ImageContainer for widget image capture.");
return ImageContainer::sInvalidAsyncContainerId;
}
return mScrollCaptureContainer->GetAsyncContainerID();
}
void
nsBaseWidget::UpdateScrollCapture()
{
// Don't capture if no container or no size.
if (!mScrollCaptureContainer || mBounds.width <= 0 || mBounds.height <= 0) {
return;
}
// If the derived class cannot take a snapshot, for example due to clipping,
// then it is responsible for creating a fallback. If null is returned, this
// means that we want to keep the existing snapshot.
RefPtr<gfx::SourceSurface> snapshot = CreateScrollSnapshot();
if (!snapshot) {
return;
}
ImageContainer::NonOwningImage holder(new SourceSurfaceImage(snapshot));
AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
imageList.AppendElement(holder);
mScrollCaptureContainer->SetCurrentImages(imageList);
}
void
nsBaseWidget::DefaultFillScrollCapture(DrawTarget* aSnapshotDrawTarget)
{
gfx::IntSize dtSize = aSnapshotDrawTarget->GetSize();
aSnapshotDrawTarget->FillRect(
gfx::Rect(0, 0, dtSize.width, dtSize.height),
gfx::ColorPattern(gfx::Color::FromABGR(kScrollCaptureFillColor)),
gfx::DrawOptions(1.f, gfx::CompositionOp::OP_SOURCE));
aSnapshotDrawTarget->Flush();
}
#endif
NS_IMETHODIMP_(nsIWidget::NativeIMEContext)
nsIWidget::GetNativeIMEContext()
{

View File

@ -22,6 +22,14 @@
#include "nsPIDOMWindow.h"
#include "nsWeakReference.h"
#include <algorithm>
#if defined(XP_WIN)
// Scroll capture constants
const uint32_t kScrollCaptureFillColor = 0xFFa0a0a0; // gray
const mozilla::gfx::SurfaceFormat kScrollCaptureFormat =
mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32;
#endif
class nsIContent;
class nsAutoRollup;
class gfxContext;
@ -36,6 +44,7 @@ class Accessible;
namespace gfx {
class DrawTarget;
class SourceSurface;
} // namespace gfx
namespace layers {
@ -46,6 +55,7 @@ class APZCTreeManager;
class GeckoContentController;
class APZEventState;
class CompositorSession;
class ImageContainer;
struct ScrollableLayerGuid;
} // namespace layers
@ -104,6 +114,7 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference
protected:
typedef base::Thread Thread;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::SourceSurface SourceSurface;
typedef mozilla::layers::BasicLayerManager BasicLayerManager;
typedef mozilla::layers::BufferMode BufferMode;
typedef mozilla::layers::CompositorBridgeChild CompositorBridgeChild;
@ -118,6 +129,7 @@ protected:
typedef mozilla::ScreenRotation ScreenRotation;
typedef mozilla::widget::CompositorWidgetDelegate CompositorWidgetDelegate;
typedef mozilla::layers::CompositorSession CompositorSession;
typedef mozilla::layers::ImageContainer ImageContainer;
virtual ~nsBaseWidget();
@ -354,6 +366,10 @@ public:
void Shutdown();
#if defined(XP_WIN)
uint64_t CreateScrollCaptureContainer() override;
#endif
protected:
// These are methods for CompositorWidgetWrapper, and should only be
// accessed from that class. Derived widgets can choose which methods to
@ -542,6 +558,29 @@ protected:
bool UseAPZ();
#if defined(XP_WIN)
void UpdateScrollCapture() override;
/**
* To be overridden by derived classes to return a snapshot that can be used
* during scrolling. Returning null means we won't update the container.
* @return an already AddRefed SourceSurface containing the snapshot
*/
virtual already_AddRefed<SourceSurface> CreateScrollSnapshot()
{
return nullptr;
};
/**
* Used by derived classes to create a fallback scroll image.
* @param aSnapshotDrawTarget DrawTarget to fill with fallback image.
*/
void DefaultFillScrollCapture(DrawTarget* aSnapshotDrawTarget);
RefPtr<ImageContainer> mScrollCaptureContainer;
#endif
protected:
// Returns whether compositing should use an external surface size.
virtual bool UseExternalCompositingSurface() const {

View File

@ -1085,6 +1085,28 @@ class nsIWidget : public nsISupports
static void UpdateRegisteredPluginWindowVisibility(uintptr_t aOwnerWidget,
nsTArray<uintptr_t>& aPluginIds);
#if defined(XP_WIN)
/**
* Iterates over the list of registered plugins and for any that are owned
* by aOwnerWidget and visible it takes a snapshot.
*
* @param aOwnerWidget only captures visible widgets owned by this
*/
static void CaptureRegisteredPlugins(uintptr_t aOwnerWidget);
/**
* Take a scroll capture for this widget if possible.
*/
virtual void UpdateScrollCapture() = 0;
/**
* Creates an async ImageContainer to hold scroll capture images that can be
* used if the plugin is hidden during scroll.
* @return the async container ID of the created ImageContainer.
*/
virtual uint64_t CreateScrollCaptureContainer() = 0;
#endif
/**
* Set the shadow style of the window.
*

View File

@ -188,6 +188,11 @@
#include "InkCollector.h"
// ERROR from wingdi.h (below) gets undefined by some code.
// #define ERROR 0
// #define RGN_ERROR ERROR
#define ERROR 0
#if !defined(SM_CONVERTIBLESLATEMODE)
#define SM_CONVERTIBLESLATEMODE 0x2003
#endif
@ -1181,6 +1186,107 @@ nsWindow::EnumAllWindows(WindowEnumCallback aCallback)
(LPARAM)aCallback);
}
static already_AddRefed<SourceSurface>
CreateSourceSurfaceForGfxSurface(gfxASurface* aSurface)
{
MOZ_ASSERT(aSurface);
return Factory::CreateSourceSurfaceForCairoSurface(
aSurface->CairoSurface(), aSurface->GetSize(),
aSurface->GetSurfaceFormat());
}
nsWindow::ScrollSnapshot*
nsWindow::EnsureSnapshotSurface(ScrollSnapshot& aSnapshotData,
const mozilla::gfx::IntSize& aSize)
{
// If the surface doesn't exist or is the wrong size then create new one.
if (!aSnapshotData.surface || aSnapshotData.surface->GetSize() != aSize) {
aSnapshotData.surface = new gfxWindowsSurface(aSize, kScrollCaptureFormat);
aSnapshotData.surfaceHasSnapshot = false;
}
return &aSnapshotData;
}
already_AddRefed<SourceSurface>
nsWindow::CreateScrollSnapshot()
{
RECT clip = { 0 };
int rgnType = ::GetWindowRgnBox(mWnd, &clip);
if (rgnType == RGN_ERROR) {
// We failed to get the clip assume that we need a full fallback.
clip.left = 0;
clip.top = 0;
clip.right = mBounds.width;
clip.bottom = mBounds.height;
return GetFallbackScrollSnapshot(clip);
}
// Check that the window is in a position to snapshot. We don't check for
// clipped width as that doesn't currently matter for APZ scrolling.
if (clip.top || clip.bottom != mBounds.height) {
return GetFallbackScrollSnapshot(clip);
}
nsAutoHDC windowDC(::GetDC(mWnd));
if (!windowDC) {
return GetFallbackScrollSnapshot(clip);
}
gfx::IntSize snapshotSize(mBounds.width, mBounds.height);
ScrollSnapshot* snapshot;
if (clip.left || clip.right != mBounds.width) {
// Can't do a full snapshot, so use the partial snapshot.
snapshot = EnsureSnapshotSurface(mPartialSnapshot, snapshotSize);
} else {
snapshot = EnsureSnapshotSurface(mFullSnapshot, snapshotSize);
}
// Note that we know that the clip is full height.
if (!::BitBlt(snapshot->surface->GetDC(), clip.left, 0, clip.right - clip.left,
clip.bottom, windowDC, clip.left, 0, SRCCOPY)) {
return GetFallbackScrollSnapshot(clip);
}
::GdiFlush();
snapshot->surface->Flush();
snapshot->surfaceHasSnapshot = true;
snapshot->clip = clip;
mCurrentSnapshot = snapshot;
return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface);
}
already_AddRefed<SourceSurface>
nsWindow::GetFallbackScrollSnapshot(const RECT& aRequiredClip)
{
gfx::IntSize snapshotSize(mBounds.width, mBounds.height);
// If the current snapshot is the correct size and covers the required clip,
// just keep that by returning null.
// Note: we know the clip is always full height.
if (mCurrentSnapshot &&
mCurrentSnapshot->surface->GetSize() == snapshotSize &&
mCurrentSnapshot->clip.left <= aRequiredClip.left &&
mCurrentSnapshot->clip.right >= aRequiredClip.right) {
return nullptr;
}
// Otherwise we'll use the full snapshot, making sure it is big enough first.
mCurrentSnapshot = EnsureSnapshotSurface(mFullSnapshot, snapshotSize);
// If there is no snapshot, create a default.
if (!mCurrentSnapshot->surfaceHasSnapshot) {
gfx::SurfaceFormat format = mCurrentSnapshot->surface->GetSurfaceFormat();
RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForCairoSurface(
mCurrentSnapshot->surface->CairoSurface(),
mCurrentSnapshot->surface->GetSize(), &format);
DefaultFillScrollCapture(dt);
}
return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface);
}
/**************************************************************
*
* SECTION: nsIWidget::Show

View File

@ -487,6 +487,25 @@ protected:
void ClearCachedResources();
nsIWidgetListener* GetPaintListener();
already_AddRefed<SourceSurface> CreateScrollSnapshot() override;
struct ScrollSnapshot
{
RefPtr<gfxWindowsSurface> surface;
bool surfaceHasSnapshot = false;
RECT clip;
};
ScrollSnapshot* EnsureSnapshotSurface(ScrollSnapshot& aSnapshotData,
const mozilla::gfx::IntSize& aSize);
ScrollSnapshot mFullSnapshot;
ScrollSnapshot mPartialSnapshot;
ScrollSnapshot* mCurrentSnapshot = nullptr;
already_AddRefed<SourceSurface>
GetFallbackScrollSnapshot(const RECT& aRequiredClip);
protected:
nsCOMPtr<nsIWidget> mParent;
nsIntSize mLastSize;