diff --git a/gfx/tests/gtest/MockWidget.h b/gfx/tests/gtest/MockWidget.h index 6ac100448ec3c..ae9a529a4cffc 100644 --- a/gfx/tests/gtest/MockWidget.h +++ b/gfx/tests/gtest/MockWidget.h @@ -67,6 +67,10 @@ class MockWidget : public nsBaseWidget { virtual void Enable(bool aState) override {} virtual bool IsEnabled() const override { return true; } virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override {} + virtual nsresult ConfigureChildren( + const nsTArray& aConfigurations) override { + return NS_OK; + } virtual void Invalidate(const LayoutDeviceIntRect& aRect) override {} virtual nsresult SetTitle(const nsAString& title) override { return NS_OK; } virtual LayoutDeviceIntPoint WidgetToScreenOffset() override { diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index a49c7e2723924..8d6442df3283c 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -228,6 +228,26 @@ void PuppetWidget::Resize(double aWidth, double aHeight, bool aRepaint) { } } +nsresult PuppetWidget::ConfigureChildren( + const nsTArray& aConfigurations) { + for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { + const Configuration& configuration = aConfigurations[i]; + PuppetWidget* w = static_cast(configuration.mChild.get()); + NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); + w->SetWindowClipRegion(configuration.mClipRegion, true); + LayoutDeviceIntRect bounds = w->GetBounds(); + if (bounds.Size() != configuration.mBounds.Size()) { + w->Resize(configuration.mBounds.X(), configuration.mBounds.Y(), + configuration.mBounds.Width(), configuration.mBounds.Height(), + true); + } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) { + w->Move(configuration.mBounds.X(), configuration.mBounds.Y()); + } + w->SetWindowClipRegion(configuration.mClipRegion, false); + } + return NS_OK; +} + void PuppetWidget::SetFocus(Raise aRaise, CallerType aCallerType) { if (aRaise == Raise::Yes && mBrowserChild) { mBrowserChild->SendRequestFocus(true, aCallerType); diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index 9339747bda845..090dff32d23eb 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -117,6 +117,9 @@ class PuppetWidget : public nsBaseWidget, virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; + virtual nsresult ConfigureChildren( + const nsTArray& aConfigurations) override; + virtual void Invalidate(const LayoutDeviceIntRect& aRect) override; // PuppetWidgets don't have native data, as they're purely nonnative. diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 0e6c95209f646..b68abbed4ea27 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1819,6 +1819,17 @@ void nsWindow::Destroy() { #endif } +nsresult nsWindow::ConfigureChildren( + const nsTArray& config) { + for (uint32_t i = 0; i < config.Length(); ++i) { + nsWindow* childWin = (nsWindow*)config[i].mChild.get(); + childWin->Resize(config[i].mBounds.x, config[i].mBounds.y, + config[i].mBounds.width, config[i].mBounds.height, false); + } + + return NS_OK; +} + mozilla::widget::EventDispatcher* nsWindow::GetEventDispatcher() const { if (mAndroidView) { return mAndroidView->mEventDispatcher; diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index 5c7f4d9621bab..be1049a35c1a2 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -139,6 +139,8 @@ class nsWindow final : public nsBaseWidget { const LayoutDeviceIntRect& aRect, nsWidgetInitData* aInitData) override; virtual void Destroy() override; + virtual nsresult ConfigureChildren( + const nsTArray&) override; virtual void SetParent(nsIWidget* aNewParent) override; virtual nsIWidget* GetParent(void) override; virtual float GetDPI() override; diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 3c5244992438b..4da65f68b601a 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -344,6 +344,7 @@ class nsChildView final : public nsBaseWidget { void EnsureContentLayerForMainThreadPainting(); virtual void* GetNativeData(uint32_t aDataType) override; + virtual nsresult ConfigureChildren(const nsTArray& aConfigurations) override; virtual LayoutDeviceIntPoint WidgetToScreenOffset() override; virtual bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) override { return false; } diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index b0c72fd3b3c1c..bbcf694f9f882 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -1250,6 +1250,10 @@ static void blinkRgn(RgnHandle rgn) { #pragma mark - +nsresult nsChildView::ConfigureChildren(const nsTArray& aConfigurations) { + return NS_OK; +} + // Invokes callback and ProcessEvent methods on Event Listener object nsresult nsChildView::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus) { RefPtr kungFuDeathGrip(this); diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index b5c3a4576ac6f..6506ae55dcc31 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -295,6 +295,7 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { virtual nsresult SetTitle(const nsAString& aTitle) override; virtual void Invalidate(const LayoutDeviceIntRect& aRect) override; + virtual nsresult ConfigureChildren(const nsTArray& aConfigurations) override; virtual WindowRenderer* GetWindowRenderer() override; virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override; virtual void CaptureRollupEvents(nsIRollupListener* aListener, bool aDoCapture) override; diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 49f5f85b7b913..fd91a5c1a7144 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -399,6 +399,7 @@ static unsigned int WindowMaskForBorderStyle(nsBorderStyle aBorderStyle) { switch (mWindowType) { case eWindowType_invisible: case eWindowType_child: + case eWindowType_plugin: break; case eWindowType_popup: if (aBorderStyle != eBorderStyle_default && mBorderStyle & eBorderStyle_title) { @@ -1035,6 +1036,13 @@ static unsigned int WindowMaskForBorderStyle(nsBorderStyle aBorderStyle) { return (mWindowType == eWindowType_popup) && mWasShown && ([[NSScreen screens] count] > 1); } +nsresult nsCocoaWindow::ConfigureChildren(const nsTArray& aConfigurations) { + if (mPopupContentView) { + mPopupContentView->ConfigureChildren(aConfigurations); + } + return NS_OK; +} + WindowRenderer* nsCocoaWindow::GetWindowRenderer() { if (mPopupContentView) { return mPopupContentView->GetWindowRenderer(); diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index d24c57d5dcb59..9349f30c48cf0 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -5367,9 +5367,16 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, SetWindowMouseTransparent(aInitData->mMouseTransparent); } } break; + + case eWindowType_plugin: + case eWindowType_plugin_ipc_chrome: + case eWindowType_plugin_ipc_content: + MOZ_ASSERT_UNREACHABLE("Unexpected eWindowType_plugin*"); + return NS_ERROR_FAILURE; default: MOZ_ASSERT_UNREACHABLE("Unexpected eWindowType"); return NS_ERROR_FAILURE; + break; } // label the drawing window with this object so we can find our way home @@ -6175,6 +6182,77 @@ bool nsWindow::DoDrawTilebarCorners() { !mIsTiled; } +nsresult nsWindow::ConfigureChildren( + const nsTArray& aConfigurations) { + // If this is a remotely updated widget we receive clipping, position, and + // size information from a source other than our owner. Don't let our parent + // update this information. + if (mWindowType == eWindowType_plugin_ipc_chrome) { + return NS_OK; + } + + for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { + const Configuration& configuration = aConfigurations[i]; + auto* w = static_cast(configuration.mChild.get()); + NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); + w->SetWindowClipRegion(configuration.mClipRegion, true); + if (w->mBounds.Size() != configuration.mBounds.Size()) { + w->Resize(configuration.mBounds.x, configuration.mBounds.y, + configuration.mBounds.width, configuration.mBounds.height, + true); + } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) { + w->Move(configuration.mBounds.x, configuration.mBounds.y); + } + w->SetWindowClipRegion(configuration.mClipRegion, false); + } + return NS_OK; +} + +nsresult nsWindow::SetWindowClipRegion( + const nsTArray& aRects, bool aIntersectWithExisting) { + const nsTArray* newRects = &aRects; + + AutoTArray intersectRects; + if (aIntersectWithExisting) { + AutoTArray existingRects; + GetWindowClipRegion(&existingRects); + + LayoutDeviceIntRegion existingRegion = RegionFromArray(existingRects); + LayoutDeviceIntRegion newRegion = RegionFromArray(aRects); + LayoutDeviceIntRegion intersectRegion; + intersectRegion.And(newRegion, existingRegion); + + // If mClipRects is null we haven't set a clip rect yet, so we + // need to set the clip even if it is equal. + if (mClipRects && intersectRegion.IsEqual(existingRegion)) { + return NS_OK; + } + + if (!intersectRegion.IsEqual(newRegion)) { + ArrayFromRegion(intersectRegion, intersectRects); + newRects = &intersectRects; + } + } + + if (IsWindowClipRegionEqual(*newRects)) return NS_OK; + + StoreWindowClipRegion(*newRects); + + if (!mGdkWindow) return NS_OK; + + cairo_region_t* region = cairo_region_create(); + for (uint32_t i = 0; i < newRects->Length(); ++i) { + const LayoutDeviceIntRect& r = newRects->ElementAt(i); + cairo_rectangle_int_t rect = {r.x, r.y, r.width, r.height}; + cairo_region_union_rectangle(region, &rect); + } + + gdk_window_shape_combine_region(mGdkWindow, region, 0, 0); + cairo_region_destroy(region); + + return NS_OK; +} + void nsWindow::ResizeTransparencyBitmap() { if (!mTransparencyBitmap) return; diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index da321a7be649c..ddc2f00cda09c 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -168,6 +168,9 @@ class nsWindow final : public nsBaseWidget { virtual void CaptureRollupEvents(nsIRollupListener* aListener, bool aDoCapture) override; [[nodiscard]] virtual nsresult GetAttention(int32_t aCycleCount) override; + virtual nsresult SetWindowClipRegion( + const nsTArray& aRects, + bool aIntersectWithExisting) override; virtual bool HasPendingInputEvent() override; virtual bool PrepareForFullscreenTransition(nsISupports** aData) override; @@ -289,6 +292,8 @@ class nsWindow final : public nsBaseWidget { virtual void SetTransparencyMode(nsTransparencyMode aMode) override; virtual nsTransparencyMode GetTransparencyMode() override; virtual void SetWindowMouseTransparent(bool aIsTransparent) override; + virtual nsresult ConfigureChildren( + const nsTArray& aConfigurations) override; nsresult UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride); diff --git a/widget/headless/HeadlessWidget.h b/widget/headless/HeadlessWidget.h index 438456368c36f..1035eb4117342 100644 --- a/widget/headless/HeadlessWidget.h +++ b/widget/headless/HeadlessWidget.h @@ -75,6 +75,12 @@ class HeadlessWidget : public nsBaseWidget { virtual void Enable(bool aState) override; virtual bool IsEnabled() const override; virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; + virtual nsresult ConfigureChildren( + const nsTArray& aConfigurations) override { + MOZ_ASSERT_UNREACHABLE( + "Headless widgets do not support configuring children."); + return NS_ERROR_FAILURE; + } virtual void Invalidate(const LayoutDeviceIntRect& aRect) override { // TODO: see if we need to do anything here. } diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index ffa49f704647b..c6c6ad9b6c3d8 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -97,6 +97,10 @@ static int32_t gNumWidgets; # include "nsCocoaFeatures.h" #endif +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +static nsRefPtrHashtable* sPluginWidgetList; +#endif + nsIRollupListener* nsBaseWidget::gRollupListener = nullptr; using namespace mozilla::dom; @@ -150,6 +154,7 @@ nsBaseWidget::nsBaseWidget() mBorderStyle(eBorderStyle_none), mBounds(0, 0, 0, 0), mOriginalBounds(nullptr), + mClipRectCount(0), mSizeMode(nsSizeMode_Normal), mIsTiled(false), mPopupLevel(ePopupLevelTop), @@ -170,6 +175,11 @@ nsBaseWidget::nsBaseWidget() debug_RegisterPrefCallbacks(); #endif +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + if (!sPluginWidgetList) { + sPluginWidgetList = new nsRefPtrHashtable(); + } +#endif mShutdownObserver = new WidgetShutdownObserver(this); } @@ -320,6 +330,12 @@ void nsBaseWidget::Shutdown() { DestroyCompositor(); FreeLocalesChangedObserver(); FreeShutdownObserver(); +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + if (sPluginWidgetList) { + delete sPluginWidgetList; + sPluginWidgetList = nullptr; + } +#endif } void nsBaseWidget::QuitIME() { @@ -532,6 +548,11 @@ void nsBaseWidget::Destroy() { if (parent) { parent->RemoveChild(this); } + +#if defined(XP_WIN) + // Allow our scroll capture container to be cleaned up, if we have one. + mScrollCaptureContainer = nullptr; +#endif } //------------------------------------------------------------------------- @@ -729,6 +750,72 @@ nsTransparencyMode nsBaseWidget::GetTransparencyMode() { return eTransparencyOpaque; } +bool nsBaseWidget::IsWindowClipRegionEqual( + const nsTArray& aRects) { + return mClipRects && mClipRectCount == aRects.Length() && + memcmp(mClipRects.get(), aRects.Elements(), + sizeof(LayoutDeviceIntRect) * mClipRectCount) == 0; +} + +void nsBaseWidget::StoreWindowClipRegion( + const nsTArray& aRects) { + mClipRectCount = aRects.Length(); + mClipRects = MakeUnique(mClipRectCount); + if (mClipRects) { + memcpy(mClipRects.get(), aRects.Elements(), + sizeof(LayoutDeviceIntRect) * mClipRectCount); + } +} + +void nsBaseWidget::GetWindowClipRegion(nsTArray* aRects) { + if (mClipRects) { + aRects->AppendElements(mClipRects.get(), mClipRectCount); + } else { + aRects->AppendElement( + LayoutDeviceIntRect(0, 0, mBounds.Width(), mBounds.Height())); + } +} + +const LayoutDeviceIntRegion nsBaseWidget::RegionFromArray( + const nsTArray& aRects) { + LayoutDeviceIntRegion region; + for (uint32_t i = 0; i < aRects.Length(); ++i) { + region.Or(region, aRects[i]); + } + return region; +} + +void nsBaseWidget::ArrayFromRegion(const LayoutDeviceIntRegion& aRegion, + nsTArray& aRects) { + for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { + aRects.AppendElement(iter.Get()); + } +} + +nsresult nsBaseWidget::SetWindowClipRegion( + const nsTArray& aRects, bool aIntersectWithExisting) { + if (!aIntersectWithExisting) { + StoreWindowClipRegion(aRects); + } else { + // get current rects + nsTArray currentRects; + GetWindowClipRegion(¤tRects); + // create region from them + LayoutDeviceIntRegion currentRegion = RegionFromArray(currentRects); + // create region from new rects + LayoutDeviceIntRegion newRegion = RegionFromArray(aRects); + // intersect regions + LayoutDeviceIntRegion intersection; + intersection.And(currentRegion, newRegion); + // create int rect array from intersection + nsTArray rects; + ArrayFromRegion(intersection, rects); + // store + StoreWindowClipRegion(rects); + } + return NS_OK; +} + /* virtual */ void nsBaseWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage, uint16_t aDuration, @@ -2049,6 +2136,42 @@ void nsBaseWidget::NotifyLiveResizeStopped() { } } +void nsBaseWidget::RegisterPluginWindowForRemoteUpdates() { +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) + MOZ_ASSERT_UNREACHABLE( + "nsBaseWidget::RegisterPluginWindowForRemoteUpdates " + "not implemented!"); + return; +#else + MOZ_ASSERT(NS_IsMainThread()); + void* id = GetNativeData(NS_NATIVE_PLUGIN_ID); + if (!id) { + NS_WARNING("This is not a valid native widget!"); + return; + } + MOZ_ASSERT(sPluginWidgetList); + sPluginWidgetList->InsertOrUpdate(id, RefPtr{this}); +#endif +} + +void nsBaseWidget::UnregisterPluginWindowForRemoteUpdates() { +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) + MOZ_ASSERT_UNREACHABLE( + "nsBaseWidget::UnregisterPluginWindowForRemoteUpdates " + "not implemented!"); + return; +#else + MOZ_ASSERT(NS_IsMainThread()); + void* id = GetNativeData(NS_NATIVE_PLUGIN_ID); + if (!id) { + NS_WARNING("This is not a valid native widget!"); + return; + } + MOZ_ASSERT(sPluginWidgetList); + sPluginWidgetList->Remove(id); +#endif +} + nsresult nsBaseWidget::AsyncEnableDragDrop(bool aEnable) { RefPtr kungFuDeathGrip = this; return NS_DispatchToCurrentThreadQueue( @@ -2058,6 +2181,118 @@ nsresult nsBaseWidget::AsyncEnableDragDrop(bool aEnable) { kAsyncDragDropTimeout, EventQueuePriority::Idle); } +// static +nsIWidget* nsIWidget::LookupRegisteredPluginWindow(uintptr_t aWindowID) { +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) + MOZ_ASSERT_UNREACHABLE( + "nsBaseWidget::LookupRegisteredPluginWindow " + "not implemented!"); + return nullptr; +#else + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(sPluginWidgetList); + return sPluginWidgetList->GetWeak((void*)aWindowID); +#endif +} + +// static +void nsIWidget::UpdateRegisteredPluginWindowVisibility( + uintptr_t aOwnerWidget, nsTArray& aPluginIds) { +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) + MOZ_ASSERT_UNREACHABLE( + "nsBaseWidget::UpdateRegisteredPluginWindowVisibility" + " not implemented!"); + return; +#else + 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 (const auto& entry : *sPluginWidgetList) { + const void* windowId = entry.GetKey(); + nsIWidget* widget = entry.GetWeak(); + + MOZ_ASSERT(windowId); + MOZ_ASSERT(widget); + + if (!widget->Destroyed()) { + if ((uintptr_t)widget->GetParent() == aOwnerWidget) { + widget->Show(aPluginIds.Contains((uintptr_t)windowId)); + } + } + } +#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 (const auto& entry : *sPluginWidgetList) { + DebugOnly windowId = entry.GetKey(); + nsIWidget* widget = entry.GetWeak(); + + MOZ_ASSERT(windowId); + MOZ_ASSERT(widget); + + if (!widget->Destroyed() && widget->IsVisible()) { + if ((uintptr_t)widget->GetParent() == aOwnerWidget) { + widget->UpdateScrollCapture(); + } + } + } +} + +uint64_t nsBaseWidget::CreateScrollCaptureContainer() { + mScrollCaptureContainer = + MakeAndAddRef(ImageContainer::ASYNCHRONOUS); + if (!mScrollCaptureContainer) { + NS_WARNING("Failed to create ImageContainer for widget image capture."); + return ImageContainer::sInvalidAsyncContainerId; + } + + return mScrollCaptureContainer->GetAsyncContainerHandle().Value(); +} + +void nsBaseWidget::UpdateScrollCapture() { + // Don't capture if no container or no size. + if (!mScrollCaptureContainer || mBounds.IsEmpty()) { + 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 snapshot = CreateScrollSnapshot(); + if (!snapshot) { + return; + } + + ImageContainer::NonOwningImage holder(new SourceSurfaceImage(snapshot)); + + AutoTArray 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::ToDeviceColor(kScrollCaptureFillColor)), + gfx::DrawOptions(1.f, gfx::CompositionOp::OP_SOURCE)); + aSnapshotDrawTarget->Flush(); +} +#endif + const IMENotificationRequests& nsIWidget::IMENotificationRequestsRef() { TextEventDispatcher* dispatcher = GetTextEventDispatcher(); return dispatcher->IMENotificationRequestsRef(); diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index 6427129a97b34..b34dc851489ca 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -31,6 +31,13 @@ #include +#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 gfxContext; @@ -194,6 +201,7 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { } void SetTransparencyMode(nsTransparencyMode aMode) override; nsTransparencyMode GetTransparencyMode() override; + void GetWindowClipRegion(nsTArray* aRects) override; void SetWindowShadowStyle(mozilla::StyleWindowShadow aStyle) override {} void SetShowsToolbarButton(bool aShow) override {} void SetSupportsNativeFullscreen(bool aSupportsNativeFullscreen) override {} @@ -234,6 +242,8 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { void SetModal(bool aModal) override {} uint32_t GetMaxTouchPoints() const override; void SetWindowClass(const nsAString& xulWinType) override {} + nsresult SetWindowClipRegion(const nsTArray& aRects, + bool aIntersectWithExisting) override; // Return whether this widget interprets parameters to Move and Resize APIs // as "desktop pixels" rather than "device pixels", and therefore // applies its GetDefaultScale() value to them before using them as mBounds @@ -328,6 +338,10 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { void NotifySizeMoveDone(); void NotifyWindowMoved(int32_t aX, int32_t aY); + // Register plugin windows for remote updates from the compositor + void RegisterPluginWindowForRemoteUpdates() override; + void UnregisterPluginWindowForRemoteUpdates() override; + void SetNativeData(uint32_t aDataType, uintptr_t aVal) override {} // Should be called by derived implementations to notify on system color and @@ -402,6 +416,10 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { void QuitIME(); +#if defined(XP_WIN) + uint64_t CreateScrollCaptureContainer() override; +#endif + // These functions should be called at the start and end of a "live" widget // resize (i.e. when the window contents are repainting during the resize, // such as when the user drags a window border). It will suppress the @@ -466,6 +484,11 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { mozilla::WidgetInputEvent* aEvent, const mozilla::layers::APZEventResult& aApzResult); + const LayoutDeviceIntRegion RegionFromArray( + const nsTArray& aRects); + void ArrayFromRegion(const LayoutDeviceIntRegion& aRegion, + nsTArray& aRects); + nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode, uint32_t aModifierFlags, @@ -554,6 +577,13 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { void* GetPseudoIMEContext(); protected: + // Utility to check if an array of clip rects is equal to our + // internally stored clip rect array mClipRects. + bool IsWindowClipRegionEqual(const nsTArray& aRects); + + // Stores the clip rectangles in aRects into mClipRects. + void StoreWindowClipRegion(const nsTArray& aRects); + virtual already_AddRefed AllocateChildPopupWidget() { return nsIWidget::CreateChildWindow(); } @@ -639,6 +669,27 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { void DispatchPanGestureInput(mozilla::PanGestureInput& aInput); void DispatchPinchGestureInput(mozilla::PinchGestureInput& aInput); +#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 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 mScrollCaptureContainer; +#endif + protected: // Returns whether compositing should use an external surface size. virtual bool UseExternalCompositingSurface() const { return false; } @@ -681,6 +732,9 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { nsBorderStyle mBorderStyle; LayoutDeviceIntRect mBounds; LayoutDeviceIntRect* mOriginalBounds; + // When this pointer is null, the widget is not clipped + mozilla::UniquePtr mClipRects; + uint32_t mClipRectCount; nsSizeMode mSizeMode; bool mIsTiled; nsPopupLevel mPopupLevel; diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 880f6efcefd4f..11077bc94ad4d 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -127,10 +127,13 @@ typedef void* nsNativeWidget; #define NS_NATIVE_REGION 5 #define NS_NATIVE_OFFSETX 6 #define NS_NATIVE_OFFSETY 7 +#define NS_NATIVE_PLUGIN_PORT 8 #define NS_NATIVE_SCREEN 9 // The toplevel GtkWidget containing this nsIWidget: #define NS_NATIVE_SHELLWIDGET 10 #define NS_NATIVE_OPENGL_CONTEXT 12 +// See RegisterPluginWindowForRemoteUpdates +#define NS_NATIVE_PLUGIN_ID 13 // This is available only with GetNativeData() in parent process. Anybody // shouldn't access this pointer as a valid pointer since the result may be // special value like NS_ONLY_ONE_NATIVE_IME_CONTEXT. So, the result is just @@ -139,6 +142,10 @@ typedef void* nsNativeWidget; // XP code should use nsIWidget::GetNativeIMEContext() instead of using this. #define NS_RAW_NATIVE_IME_CONTEXT 14 #define NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID 15 +#ifdef XP_MACOSX +# define NS_NATIVE_PLUGIN_PORT_QD 100 +# define NS_NATIVE_PLUGIN_PORT_CG 101 +#endif #ifdef XP_WIN # define NS_NATIVE_TSF_THREAD_MGR 100 # define NS_NATIVE_TSF_CATEGORY_MGR 101 @@ -146,6 +153,8 @@ typedef void* nsNativeWidget; # define NS_NATIVE_ICOREWINDOW 103 // winrt specific #endif #if defined(MOZ_WIDGET_GTK) +// set/get nsPluginNativeWindowGtk, e10s specific +# define NS_NATIVE_PLUGIN_OBJECT_PTR 104 # define NS_NATIVE_EGL_WINDOW 106 #endif #ifdef MOZ_WIDGET_ANDROID @@ -1016,6 +1025,15 @@ class nsIWidget : public nsISupports { */ nsWindowType WindowType() { return mWindowType; } + /** + * Determines if this widget is one of the three types of plugin widgets. + */ + bool IsPlugin() { + return mWindowType == eWindowType_plugin || + mWindowType == eWindowType_plugin_ipc_chrome || + mWindowType == eWindowType_plugin_ipc_content; + } + /** * Set the transparency mode of the top-level window containing this widget. * So, e.g., if you call this on the widget for an IFRAME, the top level @@ -1041,6 +1059,94 @@ class nsIWidget : public nsISupports { */ virtual nsTransparencyMode GetTransparencyMode() = 0; + /** + * This represents a command to set the bounds and clip region of + * a child widget. + */ + struct Configuration { + nsCOMPtr mChild; + uintptr_t mWindowID; // e10s specific, the unique plugin port id + bool mVisible; // e10s specific, widget visibility + LayoutDeviceIntRect mBounds; + CopyableTArray mClipRegion; + }; + + /** + * Sets the clip region of each mChild (which must actually be a child + * of this widget) to the union of the pixel rects given in + * mClipRegion, all relative to the top-left of the child + * widget. Clip regions are not implemented on all platforms and only + * need to actually work for children that are plugins. + * + * Also sets the bounds of each child to mBounds. + * + * This will invalidate areas of the children that have changed, but + * does not need to invalidate any part of this widget. + * + * Children should be moved in the order given; the array is + * sorted so to minimize unnecessary invalidation if children are + * moved in that order. + */ + virtual nsresult ConfigureChildren( + const nsTArray& aConfigurations) = 0; + virtual nsresult SetWindowClipRegion( + const nsTArray& aRects, + bool aIntersectWithExisting) = 0; + + /** + * Appends to aRects the rectangles constituting this widget's clip + * region. If this widget is not clipped, appends a single rectangle + * (0, 0, bounds.width, bounds.height). + */ + virtual void GetWindowClipRegion(nsTArray* aRects) = 0; + + /** + * Register or unregister native plugin widgets which receive Configuration + * data from the content process via the compositor. + * + * Lookups are used by the main thread via the compositor to lookup widgets + * based on a unique window id. On Windows and Linux this is the + * NS_NATIVE_PLUGIN_PORT (hwnd/XID). This tracking maintains a reference to + * widgets held. Consumers are responsible for removing widgets from this + * list. + */ + virtual void RegisterPluginWindowForRemoteUpdates() = 0; + virtual void UnregisterPluginWindowForRemoteUpdates() = 0; + static nsIWidget* LookupRegisteredPluginWindow(uintptr_t aWindowID); + + /** + * Iterates across the list of registered plugin widgets and updates thier + * visibility based on which plugins are included in the 'visible' list. + * + * The compositor knows little about tabs, but it does know which plugin + * widgets are currently included in the visible layer tree. It calls this + * helper to hide widgets it knows nothing about. + */ + static void UpdateRegisteredPluginWindowVisibility( + uintptr_t aOwnerWidget, nsTArray& 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. * diff --git a/widget/nsWidgetInitData.h b/widget/nsWidgetInitData.h index 8054200243684..80cbeb59bdcee 100644 --- a/widget/nsWidgetInitData.h +++ b/widget/nsWidgetInitData.h @@ -21,6 +21,11 @@ enum nsWindowType { eWindowType_child, // child windows (contained inside a window on the // desktop (has no border)) eWindowType_invisible, // windows that are invisible or offscreen + eWindowType_plugin, // plugin window + eWindowType_plugin_ipc_chrome, // chrome side native widget for plugins + // (e10s) + eWindowType_plugin_ipc_content, // content side puppet widget for plugins + // (e10s) }; /** diff --git a/widget/tests/gtest/MockWinWidget.h b/widget/tests/gtest/MockWinWidget.h index 52bbd12e4537f..8dc94ece6f3c1 100644 --- a/widget/tests/gtest/MockWinWidget.h +++ b/widget/tests/gtest/MockWinWidget.h @@ -53,6 +53,10 @@ class MockWinWidget : public nsBaseWidget { virtual void Enable(bool aState) override {} virtual bool IsEnabled() const override { return true; } virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override {} + virtual nsresult ConfigureChildren( + const nsTArray& aConfigurations) override { + return NS_OK; + } virtual void Invalidate(const LayoutDeviceIntRect& aRect) override {} virtual nsresult SetTitle(const nsAString& title) override { return NS_OK; } virtual LayoutDeviceIntPoint WidgetToScreenOffset() override { diff --git a/widget/uikit/nsWindow.h b/widget/uikit/nsWindow.h index fa9cd721a4d9f..d1c3a04cfe9d8 100644 --- a/widget/uikit/nsWindow.h +++ b/widget/uikit/nsWindow.h @@ -63,6 +63,7 @@ class nsWindow final : public nsBaseWidget { virtual nsresult SetTitle(const nsAString& aTitle) override { return NS_OK; } virtual void Invalidate(const LayoutDeviceIntRect& aRect) override; + virtual nsresult ConfigureChildren(const nsTArray& aConfigurations) override; virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override; void WillPaintWindow(); diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm index 1e4a988861e70..fd992c9ad1396 100644 --- a/widget/uikit/nsWindow.mm +++ b/widget/uikit/nsWindow.mm @@ -491,6 +491,16 @@ - (void)drawRect:(CGRect)aRect inContext:(CGContextRef)aContext { return NS_OK; } +nsresult nsWindow::ConfigureChildren(const nsTArray& config) { + for (uint32_t i = 0; i < config.Length(); ++i) { + nsWindow* childWin = (nsWindow*)config[i].mChild.get(); + childWin->Resize(config[i].mBounds.x, config[i].mBounds.y, config[i].mBounds.width, + config[i].mBounds.height, false); + } + + return NS_OK; +} + void nsWindow::Show(bool aState) { if (aState != mVisible) { mNativeView.hidden = aState ? NO : YES; @@ -701,6 +711,10 @@ - (void)drawRect:(CGRect)aRect inContext:(CGContextRef)aContext { retVal = 0; break; + case NS_NATIVE_PLUGIN_PORT: + // not implemented + break; + case NS_RAW_NATIVE_IME_CONTEXT: retVal = GetPseudoIMEContext(); if (retVal) { diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index 09270622c24a3..31f3955d4ad80 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -441,6 +441,21 @@ void MouseScrollHandler::ProcessNativeMouseWheelMessage(nsWindowBase* aWidget, ::SetForegroundWindow(destWindow->GetWindowHandle()); } + // If the found window is our plugin window, it means that the message + // has been handled by the plugin but not consumed. We should handle the + // message on its parent window. However, note that the DOM event may + // cause accessing the plugin. Therefore, we should unlock the plugin + // process by using PostMessage(). + if (destWindow->IsPlugin()) { + destWindow = destWindow->GetParentWindowBase(false); + if (!destWindow) { + MOZ_LOG( + gMouseScrollLog, LogLevel::Info, + ("MouseScroll::ProcessNativeMouseWheelMessage: " + "Our window which is a parent of a plugin window is not found")); + return; + } + } MOZ_LOG(gMouseScrollLog, LogLevel::Info, ("MouseScroll::ProcessNativeMouseWheelMessage: Succeeded, " "Posting internal message to an nsWindow (%p)...", @@ -466,6 +481,33 @@ void MouseScrollHandler::ProcessNativeMouseWheelMessage(nsWindowBase* aWidget, return; } + // If we're a plugin window (MozillaWindowClass) and cursor in this window, + // the message shouldn't go to plugin's wndproc again. So, we should handle + // it on parent window. However, note that the DOM event may cause accessing + // the plugin. Therefore, we should unlock the plugin process by using + // PostMessage(). + if (aWidget->IsPlugin() && aWidget->GetWindowHandle() == pluginWnd) { + nsWindowBase* destWindow = aWidget->GetParentWindowBase(false); + if (!destWindow) { + MOZ_LOG(gMouseScrollLog, LogLevel::Info, + ("MouseScroll::ProcessNativeMouseWheelMessage: Our normal window " + "which " + "is a parent of this plugin window is not found")); + return; + } + MOZ_LOG( + gMouseScrollLog, LogLevel::Info, + ("MouseScroll::ProcessNativeMouseWheelMessage: Succeeded, " + "Posting internal message to an nsWindow (%p) which is parent of this " + "plugin window...", + destWindow)); + mIsWaitingInternalMessage = true; + UINT internalMessage = WinUtils::GetInternalMessage(aMessage); + ::PostMessage(destWindow->GetWindowHandle(), internalMessage, aWParam, + aLParam); + return; + } + // If the window is a part of plugin, we should post the message to it. MOZ_LOG( gMouseScrollLog, LogLevel::Info, @@ -1592,6 +1634,11 @@ void MouseScrollHandler::SynthesizingEvent::NativeMessageReceived( if (aWidget && aWidget->GetWindowHandle() == mWnd) { return; } + // If the target window is not ours and received window is our plugin + // window, it comes from child window of the plugin. + if (aWidget && aWidget->IsPlugin() && !WinUtils::GetNSWindowBasePtr(mWnd)) { + return; + } // Otherwise, the message may not be sent by us. } diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index cf5d3638fbf88..6d4a122cb9765 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -942,6 +942,14 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, } else { className = GetWindowClass(); } + // Plugins are created in the disabled state so that they can't + // steal focus away from our main window. This is especially + // important if the plugin has loaded in a background tab. + if (aInitData->mWindowType == eWindowType_plugin || + aInitData->mWindowType == eWindowType_plugin_ipc_chrome || + aInitData->mWindowType == eWindowType_plugin_ipc_content) { + style |= WS_DISABLED; + } if (aInitData->mWindowType == eWindowType_toplevel && !aParent && !sFirstTopLevelWindowCreated) { @@ -1028,7 +1036,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } - if (mWindowType != eWindowType_invisible && + if (!IsPlugin() && mWindowType != eWindowType_invisible && MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) { // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977) // @@ -1241,6 +1249,13 @@ DWORD nsWindow::WindowStyle() { DWORD style; switch (mWindowType) { + case eWindowType_plugin: + case eWindowType_plugin_ipc_chrome: + case eWindowType_plugin_ipc_content: + case eWindowType_child: + style = WS_OVERLAPPED; + break; + case eWindowType_dialog: style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | DS_3DLOOK | DS_MODALFRAME | WS_CLIPCHILDREN; @@ -1327,6 +1342,12 @@ DWORD nsWindow::WindowStyle() { // Return nsWindow extended styles DWORD nsWindow::WindowExStyle() { switch (mWindowType) { + case eWindowType_plugin: + case eWindowType_plugin_ipc_chrome: + case eWindowType_plugin_ipc_content: + case eWindowType_child: + return 0; + case eWindowType_dialog: return WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME; @@ -1526,6 +1547,103 @@ nsTArray nsWindow::EnumAllWindows() { return windows; } +static already_AddRefed 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 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); + } + + HDC windowDC = ::GetDC(mWnd); + if (!windowDC) { + return GetFallbackScrollSnapshot(clip); + } + auto releaseDC = MakeScopeExit([&] { ::ReleaseDC(mWnd, windowDC); }); + + 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 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 dt = Factory::CreateDrawTargetForCairoSurface( + mCurrentSnapshot->surface->CairoSurface(), + mCurrentSnapshot->surface->GetSize(), &format); + + DefaultFillScrollCapture(dt); + } + + return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface); +} + /************************************************************** * * SECTION: nsIWidget::Show @@ -1957,6 +2075,15 @@ void nsWindow::Move(double aX, double aY) { ClearThemeRegion(); UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE; + // Workaround SetWindowPos bug with D3D9. If our window has a clip + // region, some drivers or OSes may incorrectly copy into the clipped-out + // area. + if (IsPlugin() && !mWindowRenderer && mClipRects && + (mClipRectCount != 1 || + !mClipRects[0].IsEqualInterior( + LayoutDeviceIntRect(0, 0, mBounds.Width(), mBounds.Height())))) { + flags |= SWP_NOCOPYBITS; + } double oldScale = mDefaultScale; mResizeState = IN_SIZEMOVE; VERIFY(::SetWindowPos(mWnd, nullptr, x, y, 0, 0, flags)); @@ -3207,9 +3334,22 @@ void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) { // all values must be set to -1 to get a full sheet of glass. MARGINS margins = {-1, -1, -1, -1}; if (!aOpaqueRegion.IsEmpty()) { + LayoutDeviceIntRect pluginBounds; + for (nsIWidget* child = GetFirstChild(); child; + child = child->GetNextSibling()) { + if (child->IsPlugin()) { + // Collect the bounds of all plugins for GetLargestRectangle. + LayoutDeviceIntRect childBounds = child->GetBounds(); + pluginBounds.UnionRect(pluginBounds, childBounds); + } + } + LayoutDeviceIntRect clientBounds = GetClientBounds(); - // Find the largest rectangle and use that to calculate the inset. - LayoutDeviceIntRect largest = aOpaqueRegion.GetLargestRectangle(); + + // Find the largest rectangle and use that to calculate the inset. Our top + // priority is to include the bounds of all plugins. + LayoutDeviceIntRect largest = + aOpaqueRegion.GetLargestRectangle(pluginBounds); margins.cxLeftWidth = largest.X(); margins.cxRightWidth = clientBounds.Width() - largest.XMost(); margins.cyBottomHeight = clientBounds.Height() - largest.YMost(); @@ -3633,6 +3773,8 @@ void* nsWindow::GetNativeData(uint32_t aDataType) { mIsRTL ? WS_EX_LAYOUTRTL : 0, GetWindowClass(), L"", WS_CHILD, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, mWnd, nullptr, nsToolkit::mDllInstance, nullptr); + case NS_NATIVE_PLUGIN_ID: + case NS_NATIVE_PLUGIN_PORT: case NS_NATIVE_WIDGET: case NS_NATIVE_WINDOW: case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID: @@ -3669,6 +3811,7 @@ void nsWindow::FreeNativeData(void* data, uint32_t aDataType) { case NS_NATIVE_GRAPHIC: case NS_NATIVE_WIDGET: case NS_NATIVE_WINDOW: + case NS_NATIVE_PLUGIN_PORT: break; default: break; @@ -6051,7 +6194,7 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, break; case WM_GESTURENOTIFY: { - if (mWindowType != eWindowType_invisible) { + if (mWindowType != eWindowType_invisible && !IsPlugin()) { // A GestureNotify event is dispatched to decide which single-finger // panning direction should be active (including none) and if pan // feedback should be displayed. Java and plugin windows can make their @@ -7175,6 +7318,52 @@ bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam) { return true; // Handled } +nsresult nsWindow::ConfigureChildren( + const nsTArray& aConfigurations) { + // If this is a remotely updated widget we receive clipping, position, and + // size information from a source other than our owner. Don't let our parent + // update this information. + if (mWindowType == eWindowType_plugin_ipc_chrome) { + return NS_OK; + } + + // XXXroc we could use BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos + // here, if that helps in some situations. So far I haven't seen a + // need. + for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { + const Configuration& configuration = aConfigurations[i]; + nsWindow* w = static_cast(configuration.mChild.get()); + NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); + nsresult rv = w->SetWindowClipRegion(configuration.mClipRegion, true); + NS_ENSURE_SUCCESS(rv, rv); + LayoutDeviceIntRect bounds = w->GetBounds(); + if (bounds.Size() != configuration.mBounds.Size()) { + w->Resize(configuration.mBounds.X(), configuration.mBounds.Y(), + configuration.mBounds.Width(), configuration.mBounds.Height(), + true); + } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) { + w->Move(configuration.mBounds.X(), configuration.mBounds.Y()); + + if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() || + GetWindowRenderer()->GetBackendType() != + LayersBackend::LAYERS_BASIC) { + // XXX - Workaround for Bug 587508. This will invalidate the part of the + // plugin window that might be touched by moving content somehow. The + // underlying problem should be found and fixed! + LayoutDeviceIntRegion r; + r.Sub(bounds, configuration.mBounds); + r.MoveBy(-bounds.X(), -bounds.Y()); + LayoutDeviceIntRect toInvalidate = r.GetBounds(); + + WinUtils::InvalidatePluginAsWorkaround(w, toInvalidate); + } + } + rv = w->SetWindowClipRegion(configuration.mClipRegion, false); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + static HRGN CreateHRGNFromArray(const nsTArray& aRects) { int32_t size = sizeof(RGNDATAHEADER) + sizeof(RECT) * aRects.Length(); AutoTArray buf; @@ -7195,6 +7384,47 @@ static HRGN CreateHRGNFromArray(const nsTArray& aRects) { return ::ExtCreateRegion(nullptr, buf.Length(), data); } +nsresult nsWindow::SetWindowClipRegion( + const nsTArray& aRects, bool aIntersectWithExisting) { + if (IsWindowClipRegionEqual(aRects)) { + return NS_OK; + } + + nsBaseWidget::SetWindowClipRegion(aRects, aIntersectWithExisting); + + HRGN dest = CreateHRGNFromArray(aRects); + if (!dest) return NS_ERROR_OUT_OF_MEMORY; + + if (aIntersectWithExisting) { + HRGN current = ::CreateRectRgn(0, 0, 0, 0); + if (current) { + if (::GetWindowRgn(mWnd, current) != 0 /*ERROR*/) { + ::CombineRgn(dest, dest, current, RGN_AND); + } + ::DeleteObject(current); + } + } + + // If a plugin is not visible, especially if it is in a background tab, + // it should not be able to steal keyboard focus. This code checks whether + // the region that the plugin is being clipped to is NULLREGION. If it is, + // the plugin window gets disabled. + if (IsPlugin()) { + if (NULLREGION == ::CombineRgn(dest, dest, dest, RGN_OR)) { + ::ShowWindow(mWnd, SW_HIDE); + ::EnableWindow(mWnd, FALSE); + } else { + ::EnableWindow(mWnd, TRUE); + ::ShowWindow(mWnd, SW_SHOW); + } + } + if (!::SetWindowRgn(mWnd, dest, TRUE)) { + ::DeleteObject(dest); + return NS_ERROR_FAILURE; + } + return NS_OK; +} + // WM_DESTROY event handler void nsWindow::OnDestroy() { mOnDestroyCalled = true; @@ -7676,7 +7906,12 @@ LRESULT CALLBACK nsWindow::MozSpecialMouseProc(int code, WPARAM wParam, case WM_MOUSEHWHEEL: { MOUSEHOOKSTRUCT* ms = (MOUSEHOOKSTRUCT*)lParam; nsIWidget* mozWin = WinUtils::GetNSWindowPtr(ms->hwnd); - if (!mozWin) { + if (mozWin) { + // If this window is windowed plugin window, the mouse events are not + // sent to us. + if (static_cast(mozWin)->IsPlugin()) + ScheduleHookTimer(ms->hwnd, (UINT)wParam); + } else { ScheduleHookTimer(ms->hwnd, (UINT)wParam); } break; diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index be08549520eff..776fa1c4a8c7b 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -182,6 +182,8 @@ class nsWindow final : public nsWindowBase { virtual LayoutDeviceIntPoint GetClientOffset() override; void SetBackgroundColor(const nscolor& aColor) override; virtual void SetCursor(const Cursor&) override; + virtual nsresult ConfigureChildren( + const nsTArray& aConfigurations) override; virtual bool PrepareForFullscreenTransition(nsISupports** aData) override; virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage, uint16_t aDuration, @@ -548,6 +550,9 @@ class nsWindow final : public nsWindowBase { void StopFlashing(); static HWND WindowAtMouse(); static bool IsTopLevelMouseExit(HWND aWnd); + virtual nsresult SetWindowClipRegion( + const nsTArray& aRects, + bool aIntersectWithExisting) override; LayoutDeviceIntRegion GetRegionToPaint(bool aForceFullRepaint, PAINTSTRUCT ps, HDC aDC); nsIWidgetListener* GetPaintListener(); @@ -557,6 +562,24 @@ class nsWindow final : public nsWindowBase { mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResourceUpdates) override; + already_AddRefed CreateScrollSnapshot() override; + + struct ScrollSnapshot { + RefPtr surface; + bool surfaceHasSnapshot = false; + RECT clip; + }; + + ScrollSnapshot* EnsureSnapshotSurface(ScrollSnapshot& aSnapshotData, + const mozilla::gfx::IntSize& aSize); + + ScrollSnapshot mFullSnapshot; + ScrollSnapshot mPartialSnapshot; + ScrollSnapshot* mCurrentSnapshot = nullptr; + + already_AddRefed GetFallbackScrollSnapshot( + const RECT& aRequiredClip); + void CreateCompositor() override; void DestroyCompositor() override; void RequestFxrOutput();