Bug 1232181 - Add support for capturing plugin windows on Windows. r=aklotz

This commit is contained in:
Jim Mathies 2016-03-01 12:48:26 -06:00
parent 002407fe45
commit 71b61067c1
2 changed files with 252 additions and 0 deletions

View File

@ -60,6 +60,7 @@
#include "nsIWidget.h"
#include "nsPluginNativeWindow.h"
#include "PluginQuirks.h"
#include "nsWindowsHelpers.h"
extern const wchar_t* kFlashFullscreenClass;
#elif defined(MOZ_WIDGET_GTK)
#include "mozilla/dom/ContentChild.h"
@ -76,6 +77,13 @@ using namespace mozilla::plugins;
using namespace mozilla::layers;
using namespace mozilla::gl;
#if defined(XP_WIN)
// Delays associated with attempting an e10s window capture for scrolling.
const int kScrollCaptureDelayMs = 100;
const int kInitScrollCaptureDelayMs = 1000;
const uint32_t kScrollCaptureFillColor = 0xFFa0a0a0; // gray
#endif
void
StreamNotifyParent::ActorDestroy(ActorDestroyReason aWhy)
{
@ -113,6 +121,13 @@ PluginInstanceParent::LookupPluginInstanceByID(uintptr_t aId)
}
#endif
template<>
struct RunnableMethodTraits<PluginInstanceParent>
{
static void RetainCallee(PluginInstanceParent* obj) { }
static void ReleaseCallee(PluginInstanceParent* obj) { }
};
PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
NPP npp,
const nsCString& aMimeType,
@ -139,6 +154,11 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
, mShHeight(0)
, mShColorSpace(nullptr)
#endif
#if defined(XP_WIN)
, mCaptureRefreshTask(nullptr)
, mValidFirstCapture(false)
, mIsScrolling(false)
#endif
{
#if defined(OS_WIN)
if (!sPluginInstanceList) {
@ -163,6 +183,9 @@ PluginInstanceParent::~PluginInstanceParent()
if (mShColorSpace)
::CGColorSpaceRelease(mShColorSpace);
#endif
#if defined(XP_WIN)
CancelScheduledScrollCapture();
#endif
}
bool
@ -440,6 +463,12 @@ PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginWindow(
// non-pointer-sized integer.
*result = mNPNIface->setvalue(mNPP, NPPVpluginWindowBool,
(void*)(intptr_t)windowed);
#if defined(XP_WIN)
if (windowed) {
ScheduleScrollCapture(kScrollCaptureDelayMs);
}
#endif
return true;
}
@ -1187,6 +1216,203 @@ PluginInstanceParent::EndUpdateBackground(const nsIntRect& aRect)
return NS_OK;
}
#if defined(XP_WIN)
//#define CAPTURE_LOG(...) printf_stderr("CAPTURE [%X]: ", this);printf_stderr(__VA_ARGS__);printf_stderr("\n");
#define CAPTURE_LOG(...)
void
PluginInstanceParent::ScheduleScrollCapture(int aTimeout)
{
if (mCaptureRefreshTask) {
return;
}
CAPTURE_LOG("delayed scroll capture requested.");
mCaptureRefreshTask =
NewRunnableMethod(this, &PluginInstanceParent::ScheduledUpdateScrollCaptureCallback);
MessageLoop::current()->PostDelayedTask(FROM_HERE, mCaptureRefreshTask,
kScrollCaptureDelayMs);
}
void
PluginInstanceParent::ScheduledUpdateScrollCaptureCallback()
{
CAPTURE_LOG("taking delayed scrollcapture.");
mCaptureRefreshTask = nullptr;
bool retrigger = false;
UpdateScrollCapture(retrigger);
if (retrigger) {
// reset the async request
ScheduleScrollCapture(kScrollCaptureDelayMs);
}
}
void
PluginInstanceParent::CancelScheduledScrollCapture()
{
CAPTURE_LOG("delayed scroll capture cancelled.");
if (mCaptureRefreshTask) {
mCaptureRefreshTask->Cancel();
mCaptureRefreshTask = nullptr;
}
}
bool
PluginInstanceParent::UpdateScrollCapture(bool& aRequestNewCapture)
{
aRequestNewCapture = false;
if (!::IsWindow(mChildPluginHWND)) {
CAPTURE_LOG("invalid window");
aRequestNewCapture = true;
return false;
}
nsAutoHDC windowDC(::GetDC(mChildPluginHWND));
if (!windowDC) {
CAPTURE_LOG("no windowdc");
aRequestNewCapture = true;
return false;
}
RECT bounds = {0};
::GetWindowRect(mChildPluginHWND, &bounds);
if ((bounds.left == bounds.right && bounds.top == bounds.bottom) ||
(!mWindowSize.width && !mWindowSize.height)) {
CAPTURE_LOG("empty bounds");
// Lots of null window plugins in content, don't capture.
return false;
}
// If we need to init mScrollCapture do so, also reset it if the size of the
// plugin window changes.
if (!mScrollCapture || mScrollCapture->GetSize() != mWindowSize) {
mValidFirstCapture = false;
mScrollCapture =
gfxPlatform::GetPlatform()->CreateOffscreenSurface(mWindowSize,
SurfaceFormat::X8R8G8B8_UINT32);
}
// Check clipping, we don't want to capture windows that are clipped by
// the viewport.
RECT clip = {0};
int rgnType = ::GetWindowRgnBox(mPluginHWND, &clip);
bool clipCorrect = !clip.left && !clip.top &&
clip.right == mWindowSize.width &&
clip.bottom == mWindowSize.height;
bool isVisible = ::IsWindowVisible(mChildPluginHWND);
CAPTURE_LOG("validcap=%d visible=%d region=%d clip=%d:%dx%dx%dx%d",
mValidFirstCapture, isVisible, rgnType, clipCorrect
clip.left, clip.top, clip.right, clip.bottom);
// We have a good capture and can't update so keep using the existing
// capture image. Otherwise fall through so we paint the fill color to
// the layer.
if (mValidFirstCapture && (!isVisible || !clipCorrect)) {
return true;
}
// On Windows we'll need a native bitmap for BitBlt.
RefPtr<gfxWindowsSurface> nativeScrollCapture;
// Copy the plugin window if it's visible and there's no clipping, otherwise
// use a default fill color.
if (isVisible && clipCorrect) {
CAPTURE_LOG("capturing window");
nativeScrollCapture =
new gfxWindowsSurface(mWindowSize, SurfaceFormat::X8R8G8B8_UINT32);
if (!::BitBlt(nativeScrollCapture->GetDC(), 0, 0, mWindowSize.width,
mWindowSize.height, windowDC, 0, 0, SRCCOPY)) {
CAPTURE_LOG("blt failure??");
return false;
}
::GdiFlush();
mValidFirstCapture = true;
}
IntSize targetSize = mScrollCapture->GetSize();
RefPtr<gfx::DrawTarget> dt =
gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mScrollCapture,
targetSize);
if (nativeScrollCapture) {
// Copy the native capture image over to a remotable gfx surface.
RefPtr<gfx::SourceSurface> sourceSurface =
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr,
nativeScrollCapture);
dt->CopySurface(sourceSurface,
IntRect(0, 0, targetSize.width, targetSize.height),
IntPoint());
} else {
CAPTURE_LOG("using fill color");
dt->FillRect(gfx::Rect(0, 0, targetSize.width, targetSize.height),
gfx::ColorPattern(gfx::Color::FromABGR(kScrollCaptureFillColor)),
gfx::DrawOptions(1.f, CompositionOp::OP_SOURCE));
aRequestNewCapture = true;
}
dt->Flush();
// Get a source for mScrollCapture and load it into the image container.
RefPtr<gfx::SourceSurface> cachedSource =
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt,
mScrollCapture);
RefPtr<SourceSurfaceImage> image =
new SourceSurfaceImage(cachedSource->GetSize(), cachedSource);
ImageContainer::NonOwningImage holder(image);
holder.mFrameID = ++mFrameID;
AutoTArray<ImageContainer::NonOwningImage,1> imageList;
imageList.AppendElement(holder);
// inits mImageContainer
ImageContainer *container = GetImageContainer();
container->SetCurrentImages(imageList);
// Invalidate our area in the page so the image gets flushed.
NPRect nprect = {0, 0, targetSize.width, targetSize.height};
RecvNPN_InvalidateRect(nprect);
return true;
}
nsresult
PluginInstanceParent::GetScrollCaptureContainer(ImageContainer** aContainer)
{
if (!aContainer || !::IsWindow(mPluginHWND)) {
return NS_ERROR_FAILURE;
}
if (!mImageContainer) {
ScheduleScrollCapture(kInitScrollCaptureDelayMs);
return NS_ERROR_FAILURE;
}
ImageContainer *container = GetImageContainer();
NS_IF_ADDREF(container);
*aContainer = container;
return NS_OK;
}
nsresult
PluginInstanceParent::UpdateScrollState(bool aIsScrolling)
{
bool scrollStateChanged = (mIsScrolling != aIsScrolling);
mIsScrolling = aIsScrolling;
if (scrollStateChanged && !aIsScrolling) {
// At the end of a dom scroll operation capturing now will attempt to
// capture a window that is still hidden due to the current scroll
// operation. (The browser process will update visibility after layer
// updates get pushed over.) So we delay our attempt for a bit. This
// shouldn't hurt our chances of capturing with APZ scroll since the
// delay is short.
ScheduleScrollCapture(kScrollCaptureDelayMs);
}
return NS_OK;
}
#endif // XP_WIN
PluginAsyncSurrogate*
PluginInstanceParent::GetAsyncSurrogate()
{
@ -1339,6 +1565,9 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
window.type = aWindow->type;
#endif
mWindowSize.width = window.width;
mWindowSize.height = window.height;
#if defined(XP_MACOSX)
double floatScaleFactor = 1.0;
mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &floatScaleFactor);
@ -1383,6 +1612,12 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
}
RecordDrawingModel();
#if defined(XP_WIN)
if (!mCaptureRefreshTask) {
ScheduleScrollCapture(kScrollCaptureDelayMs);
}
#endif
return NPERR_NO_ERROR;
}

View File

@ -342,6 +342,10 @@ public:
nsresult BeginUpdateBackground(const nsIntRect& aRect,
DrawTarget** aDrawTarget);
nsresult EndUpdateBackground(const nsIntRect& aRect);
#if defined(XP_WIN)
nsresult GetScrollCaptureContainer(mozilla::layers::ImageContainer** aContainer);
nsresult UpdateScrollState(bool aIsScrolling);
#endif
void DidComposite();
bool IsUsingDirectDrawing();
@ -401,6 +405,7 @@ private:
bool mIsWhitelistedForShumway;
NPWindowType mWindowType;
int16_t mDrawingModel;
IntSize mWindowSize;
// Since plugins may request different drawing models to find a compatible
// one, we only record the drawing model after a SetWindow call and if the
@ -465,6 +470,18 @@ private:
RefPtr<gfxASurface> mBackground;
RefPtr<ImageContainer> mImageContainer;
#if defined(XP_WIN)
void ScheduleScrollCapture(int aTimeout);
void ScheduledUpdateScrollCaptureCallback();
bool UpdateScrollCapture(bool& aRequestNewCapture);
void CancelScheduledScrollCapture();
RefPtr<gfxASurface> mScrollCapture;
CancelableTask* mCaptureRefreshTask;
bool mValidFirstCapture;
bool mIsScrolling;
#endif
};