From 71b61067c169418f98bcba97c091bc1187969aa0 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Tue, 1 Mar 2016 12:48:26 -0600 Subject: [PATCH] Bug 1232181 - Add support for capturing plugin windows on Windows. r=aklotz --- dom/plugins/ipc/PluginInstanceParent.cpp | 235 +++++++++++++++++++++++ dom/plugins/ipc/PluginInstanceParent.h | 17 ++ 2 files changed, 252 insertions(+) diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 34e6547abcfe..6f7bee2bf35a 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -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 +{ + 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 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 dt = + gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mScrollCapture, + targetSize); + + if (nativeScrollCapture) { + // Copy the native capture image over to a remotable gfx surface. + RefPtr 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 cachedSource = + gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, + mScrollCapture); + RefPtr image = + new SourceSurfaceImage(cachedSource->GetSize(), cachedSource); + + ImageContainer::NonOwningImage holder(image); + holder.mFrameID = ++mFrameID; + + AutoTArray 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; } diff --git a/dom/plugins/ipc/PluginInstanceParent.h b/dom/plugins/ipc/PluginInstanceParent.h index 751cc72574d2..8d3a2ab29a79 100644 --- a/dom/plugins/ipc/PluginInstanceParent.h +++ b/dom/plugins/ipc/PluginInstanceParent.h @@ -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 mBackground; RefPtr mImageContainer; + +#if defined(XP_WIN) + void ScheduleScrollCapture(int aTimeout); + void ScheduledUpdateScrollCaptureCallback(); + bool UpdateScrollCapture(bool& aRequestNewCapture); + void CancelScheduledScrollCapture(); + + RefPtr mScrollCapture; + CancelableTask* mCaptureRefreshTask; + bool mValidFirstCapture; + bool mIsScrolling; +#endif };