--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -55,32 +55,40 @@
#include <windowsx.h>
#include "gfxWindowsPlatform.h"
#include "mozilla/plugins/PluginSurfaceParent.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#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"
#include <gdk/gdk.h>
#elif defined(XP_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
#endif // defined(XP_MACOSX)
// This is the pref used to determine whether to use Shumway on a Flash object
// (when Shumway is enabled).
static const char kShumwayWhitelistPref[] = "shumway.swf.whitelist";
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)
{
// Implement me! Bug 1005162
}
bool
StreamNotifyParent::RecvRedirectNotifyResponse(const bool& allow)
@@ -108,16 +116,23 @@ PluginInstanceParent::LookupPluginInstan
return sPluginInstanceList->Get((void*)aId);
}
return nullptr;
}
}
}
#endif
+template<>
+struct RunnableMethodTraits<PluginInstanceParent>
+{
+ static void RetainCallee(PluginInstanceParent* obj) { }
+ static void ReleaseCallee(PluginInstanceParent* obj) { }
+};
+
PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
NPP npp,
const nsCString& aMimeType,
const NPNetscapeFuncs* npniface)
: mParent(parent)
, mSurrogate(PluginAsyncSurrogate::Cast(npp))
, mUseSurrogate(true)
, mNPP(npp)
@@ -134,16 +149,21 @@ PluginInstanceParent::PluginInstancePare
, mPluginWndProc(nullptr)
, mNestedEventState(false)
#endif // defined(XP_WIN)
#if defined(XP_MACOSX)
, mShWidth(0)
, mShHeight(0)
, mShColorSpace(nullptr)
#endif
+#if defined(XP_WIN)
+ , mCaptureRefreshTask(nullptr)
+ , mValidFirstCapture(false)
+ , mIsScrolling(false)
+#endif
{
#if defined(OS_WIN)
if (!sPluginInstanceList) {
sPluginInstanceList = new nsClassHashtable<nsVoidPtrHashKey, PluginInstanceParent>();
}
#endif
}
@@ -158,16 +178,19 @@ PluginInstanceParent::~PluginInstancePar
#endif
#if defined(MOZ_WIDGET_COCOA)
if (mShWidth != 0 && mShHeight != 0) {
DeallocShmem(mShSurface);
}
if (mShColorSpace)
::CGColorSpaceRelease(mShColorSpace);
#endif
+#if defined(XP_WIN)
+ CancelScheduledScrollCapture();
+#endif
}
bool
PluginInstanceParent::InitMetadata(const nsACString& aMimeType,
const nsACString& aSrcAttribute)
{
if (aSrcAttribute.IsEmpty()) {
return false;
@@ -435,16 +458,22 @@ bool
PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginWindow(
const bool& windowed, NPError* result)
{
// Yes, we are passing a boolean as a void*. We have to cast to intptr_t
// first to avoid gcc warnings about casting to a pointer from a
// non-pointer-sized integer.
*result = mNPNIface->setvalue(mNPP, NPPVpluginWindowBool,
(void*)(intptr_t)windowed);
+
+#if defined(XP_WIN)
+ if (windowed) {
+ ScheduleScrollCapture(kScrollCaptureDelayMs);
+ }
+#endif
return true;
}
bool
PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginTransparent(
const bool& transparent, NPError* result)
{
*result = mNPNIface->setvalue(mNPP, NPPVpluginTransparentBool,
@@ -1182,16 +1211,218 @@ PluginInstanceParent::EndUpdateBackgroun
XSync(DefaultXDisplay(), False);
#endif
Unused << SendUpdateBackground(BackgroundDescriptor(), 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));
+ dt->Flush();
+ aRequestNewCapture = true;
+ }
+
+ // 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 (!::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)
+{
+#if defined(XP_WIN)
+ 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;
+#else
+ NS_NOTREACHED("Not implemented");
+ return NS_ERROR_FAILURE;
+#endif
+}
+#endif // XP_WIN
+
PluginAsyncSurrogate*
PluginInstanceParent::GetAsyncSurrogate()
{
return mSurrogate;
}
bool
PluginInstanceParent::CreateBackground(const nsIntSize& aSize)
@@ -1334,16 +1565,19 @@ PluginInstanceParent::NPP_SetWindow(cons
window.x = aWindow->x;
window.y = aWindow->y;
window.width = aWindow->width;
window.height = aWindow->height;
window.clipRect = aWindow->clipRect; // MacOS specific
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);
int scaleFactor = ceil(floatScaleFactor);
window.contentsScaleFactor = floatScaleFactor;
if (mShWidth != window.width * scaleFactor || mShHeight != window.height * scaleFactor) {
if (mDrawingModel == NPDrawingModelCoreAnimation ||
@@ -1378,16 +1612,22 @@ PluginInstanceParent::NPP_SetWindow(cons
window.colormap = ws_info->colormap;
#endif
if (!CallNPP_SetWindow(window)) {
return NPERR_GENERIC_ERROR;
}
RecordDrawingModel();
+
+#if defined(XP_WIN)
+ if (!mCaptureRefreshTask) {
+ ScheduleScrollCapture(kScrollCaptureDelayMs);
+ }
+#endif
return NPERR_NO_ERROR;
}
NPError
PluginInstanceParent::NPP_GetValue(NPPVariable aVariable,
void* _retval)
{
switch (aVariable) {
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -337,16 +337,20 @@ public:
#ifdef XP_MACOSX
nsresult IsRemoteDrawingCoreAnimation(bool *aDrawing);
nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
#endif
nsresult SetBackgroundUnknown();
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();
virtual PluginAsyncSurrogate* GetAsyncSurrogate() override;
virtual PluginInstanceParent* GetInstance() override { return this; }
@@ -396,16 +400,17 @@ private:
RefPtr<PluginAsyncSurrogate> mSurrogate;
bool mUseSurrogate;
NPP mNPP;
const NPNetscapeFuncs* mNPNIface;
nsCString mSrcAttribute;
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
// drawing model has changed.
int mLastRecordedDrawingModel;
nsDataHashtable<nsPtrHashKey<NPObject>, PluginScriptableObjectParent*> mScriptableObjects;
@@ -460,15 +465,27 @@ private:
// the browser, but a "read-only" reference is sent to the plugin.
//
// We have explicitly chosen not to provide any guarantees about
// the consistency of the pixels in |mBackground|. A plugin may
// be able to observe partial updates to the background.
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
};
} // namespace plugins
} // namespace mozilla
#endif // ifndef dom_plugins_PluginInstanceParent_h