diff options
Diffstat (limited to '')
-rw-r--r-- | layout/generic/nsPluginFrame.cpp | 1681 |
1 files changed, 1681 insertions, 0 deletions
diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp new file mode 100644 index 0000000000..2a9670b2b8 --- /dev/null +++ b/layout/generic/nsPluginFrame.cpp @@ -0,0 +1,1681 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* rendering objects for replaced elements implemented by a plugin */ + +#include "nsPluginFrame.h" + +#include "gfx2DGlue.h" +#include "gfxContext.h" +#include "gfxMatrix.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/PresShell.h" +#ifdef XP_WIN +// This is needed for DoublePassRenderingEvent. +# include "mozilla/plugins/PluginMessageUtils.h" +#endif + +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsPresContext.h" +#include "nsWidgetsCID.h" +#include "nsView.h" +#include "nsViewManager.h" +#include "nsString.h" +#include "nsGkAtoms.h" +#include "nsNPAPIPluginInstance.h" +#include "npapi.h" +#include "nsIObjectLoadingContent.h" +#include "nsContentUtils.h" +#include "nsDisplayList.h" +#include "nsFocusManager.h" +#include "nsLayoutUtils.h" +#include "nsFrameManager.h" +#include "nsIObserverService.h" +#include "GeckoProfiler.h" +#include <algorithm> + +#include "nsIObjectFrame.h" +#include "nsPluginNativeWindow.h" +#include "FrameLayerBuilder.h" + +#include "ImageLayers.h" +#include "nsPluginInstanceOwner.h" + +#ifdef XP_WIN +# include "gfxWindowsNativeDrawing.h" +# include "gfxWindowsSurface.h" +#endif + +#include "Layers.h" +#include "ReadbackLayer.h" +#include "ImageContainer.h" +#include "mozilla/layers/RenderRootStateManager.h" + +// accessibility support +#ifdef ACCESSIBILITY +# include "nsAccessibilityService.h" +#endif + +#include "mozilla/Logging.h" + +#ifdef XP_MACOSX +# include "gfxQuartzNativeDrawing.h" +# include "mozilla/gfx/QuartzSupport.h" +#endif + +#ifdef MOZ_X11 +# include "mozilla/X11Util.h" +#endif + +#ifdef XP_WIN +# include <wtypes.h> +# include <winuser.h> +#endif + +#include "mozilla/dom/BrowserChild.h" + +#ifdef CreateEvent // Thank you MS. +# undef CreateEvent +#endif + +static mozilla::LazyLogModule sPluginFrameLog("nsPluginFrame"); + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::layers; + +class PluginBackgroundSink : public ReadbackSink { + public: + PluginBackgroundSink(nsPluginFrame* aFrame, uint64_t aStartSequenceNumber) + : mLastSequenceNumber(aStartSequenceNumber), mFrame(aFrame) {} + ~PluginBackgroundSink() override { + if (mFrame) { + mFrame->mBackgroundSink = nullptr; + } + } + + void SetUnknown(uint64_t aSequenceNumber) override { + if (!AcceptUpdate(aSequenceNumber)) return; + mFrame->mInstanceOwner->SetBackgroundUnknown(); + } + + already_AddRefed<DrawTarget> BeginUpdate(const nsIntRect& aRect, + uint64_t aSequenceNumber) override { + if (!AcceptUpdate(aSequenceNumber)) return nullptr; + return mFrame->mInstanceOwner->BeginUpdateBackground(aRect); + } + + void EndUpdate(const nsIntRect& aRect) override { + return mFrame->mInstanceOwner->EndUpdateBackground(aRect); + } + + void Destroy() { mFrame = nullptr; } + + protected: + bool AcceptUpdate(uint64_t aSequenceNumber) { + if (aSequenceNumber > mLastSequenceNumber && mFrame && + mFrame->mInstanceOwner) { + mLastSequenceNumber = aSequenceNumber; + return true; + } + return false; + } + + uint64_t mLastSequenceNumber; + nsPluginFrame* mFrame; +}; + +nsPluginFrame::nsPluginFrame(ComputedStyle* aStyle, nsPresContext* aPresContext) + : nsIFrame(aStyle, aPresContext, kClassID), + mInstanceOwner(nullptr), + mOuterView(nullptr), + mInnerView(nullptr), + mBackgroundSink(nullptr), + mReflowCallbackPosted(false) { + MOZ_LOG(sPluginFrameLog, LogLevel::Debug, + ("Created new nsPluginFrame %p\n", this)); +} + +nsPluginFrame::~nsPluginFrame() { + MOZ_LOG(sPluginFrameLog, LogLevel::Debug, + ("nsPluginFrame %p deleted\n", this)); +} + +NS_QUERYFRAME_HEAD(nsPluginFrame) + NS_QUERYFRAME_ENTRY(nsPluginFrame) + NS_QUERYFRAME_ENTRY(nsIObjectFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsIFrame) + +#ifdef ACCESSIBILITY +a11y::AccType nsPluginFrame::AccessibleType() { return a11y::ePluginType; } + +# ifdef XP_WIN +NS_IMETHODIMP nsPluginFrame::GetPluginPort(HWND* aPort) { + *aPort = (HWND)mInstanceOwner->GetPluginPort(); + return NS_OK; +} +# endif +#endif + +void nsPluginFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) { + MOZ_LOG(sPluginFrameLog, LogLevel::Debug, + ("Initializing nsPluginFrame %p for content %p\n", this, aContent)); + + nsIFrame::Init(aContent, aParent, aPrevInFlow); + CreateView(); +} + +void nsPluginFrame::DestroyFrom(nsIFrame* aDestructRoot, + PostDestroyData& aPostDestroyData) { + if (mReflowCallbackPosted) { + PresShell()->CancelReflowCallback(this); + } + + // Ensure our DidComposite observer is gone. + mDidCompositeObserver = nullptr; + + // Tell content owner of the instance to disconnect its frame. + nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(mContent)); + NS_ASSERTION(objContent, "Why not an object loading content?"); + + // The content might not have a reference to the instance owner any longer in + // the case of re-entry during instantiation or teardown, so make sure we're + // dissociated. + if (mInstanceOwner) { + mInstanceOwner->SetFrame(nullptr); + } + objContent->HasNewFrame(nullptr); + + if (mBackgroundSink) { + mBackgroundSink->Destroy(); + } + + nsIFrame::DestroyFrom(aDestructRoot, aPostDestroyData); +} + +/* virtual */ +void nsPluginFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) { + if (HasView()) { + nsView* view = GetView(); + nsViewManager* vm = view->GetViewManager(); + if (vm) { + nsViewVisibility visibility = + IsHidden() ? nsViewVisibility_kHide : nsViewVisibility_kShow; + vm->SetViewVisibility(view, visibility); + } + } + + nsIFrame::DidSetComputedStyle(aOldComputedStyle); +} + +#ifdef DEBUG_FRAME_DUMP +nsresult nsPluginFrame::GetFrameName(nsAString& aResult) const { + return MakeFrameName(u"PluginFrame"_ns, aResult); +} +#endif + +nsresult nsPluginFrame::PrepForDrawing(nsIWidget* aWidget) { + mWidget = aWidget; + + nsView* view = GetView(); + NS_ASSERTION(view, "Object frames must have views"); + if (!view) { + return NS_ERROR_FAILURE; + } + + nsViewManager* viewMan = view->GetViewManager(); + // mark the view as hidden since we don't know the (x,y) until Paint + // XXX is the above comment correct? + viewMan->SetViewVisibility(view, nsViewVisibility_kHide); + + // this is ugly. it was ripped off from didreflow(). MMP + // Position and size view relative to its parent, not relative to our + // parent frame (our parent frame may not have a view). + + nsView* parentWithView; + nsPoint origin; + nsRect r(0, 0, mRect.width, mRect.height); + + GetOffsetFromView(origin, &parentWithView); + viewMan->ResizeView(view, r); + viewMan->MoveViewTo(view, origin.x, origin.y); + + nsPresContext* presContext = PresContext(); + nsRootPresContext* rpc = presContext->GetRootPresContext(); + if (!rpc) { + return NS_ERROR_FAILURE; + } + + if (mWidget) { + // Disallow windowed plugins in popups + nsIFrame* rootFrame = rpc->PresShell()->GetRootFrame(); + nsIWidget* parentWidget = rootFrame->GetNearestWidget(); + if (!parentWidget || + nsLayoutUtils::GetDisplayRootFrame(this) != rootFrame) { + return NS_ERROR_FAILURE; + } + + // We can already have mInnerView if our instance owner went away and then + // came back. So clear the old one before creating a new one. + if (mInnerView) { + if (mInnerView->GetWidget()) { + // The widget listener should have already been cleared by + // SetInstanceOwner (with a null instance owner). + MOZ_RELEASE_ASSERT(mInnerView->GetWidget()->GetWidgetListener() == + nullptr); + } + mInnerView->Destroy(); + mInnerView = nullptr; + } + mInnerView = viewMan->CreateView(GetContentRectRelativeToSelf(), view); + if (!mInnerView) { + NS_ERROR("Could not create inner view"); + return NS_ERROR_OUT_OF_MEMORY; + } + viewMan->InsertChild(view, mInnerView, nullptr, true); + + mWidget->SetParent(parentWidget); + mWidget->Enable(true); + mWidget->Show(true); + + // Set the plugin window to have an empty clip region until we know + // what our true position, size and clip region are. These + // will be reset when nsRootPresContext computes our true + // geometry. The plugin window does need to have a good size here, so + // set the size explicitly to a reasonable guess. + AutoTArray<nsIWidget::Configuration, 1> configurations; + nsIWidget::Configuration* configuration = configurations.AppendElement(); + nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); + configuration->mChild = mWidget; + configuration->mBounds.width = + NSAppUnitsToIntPixels(mRect.width, appUnitsPerDevPixel); + configuration->mBounds.height = + NSAppUnitsToIntPixels(mRect.height, appUnitsPerDevPixel); + parentWidget->ConfigureChildren(configurations); + + mInnerView->AttachWidgetEventHandler(mWidget); + +#ifdef XP_MACOSX + // On Mac, we need to invalidate ourselves since even windowed + // plugins are painted through Thebes and we need to ensure + // the PaintedLayer containing the plugin is updated. + if (parentWidget == GetNearestWidget()) { + InvalidateFrame(); + } +#endif + + RegisterPluginForGeometryUpdates(); + + // Here we set the background color for this widget because some plugins + // will use the child window background color when painting. If it's not + // set, it may default to gray Sometimes, a frame doesn't have a background + // color or is transparent. In this case, walk up the frame tree until we do + // find a frame with a background color + for (nsIFrame* frame = this; frame; frame = frame->GetParent()) { + nscolor bgcolor = + frame->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor); + if (NS_GET_A(bgcolor) > 0) { // make sure we got an actual color + mWidget->SetBackgroundColor(bgcolor); + break; + } + } + } else { + // Changing to windowless mode changes the NPWindow geometry. + FixupWindow(GetContentRectRelativeToSelf().Size()); + RegisterPluginForGeometryUpdates(); + } + + if (!IsHidden()) { + viewMan->SetViewVisibility(view, nsViewVisibility_kShow); + } + +#ifdef ACCESSIBILITY + ; + if (nsAccessibilityService* accService = + PresShell::GetAccessibilityService()) { + accService->RecreateAccessible(PresShell(), mContent); + } +#endif + + return NS_OK; +} + +#define EMBED_DEF_WIDTH 240 +#define EMBED_DEF_HEIGHT 200 + +/* virtual */ +nscoord nsPluginFrame::GetMinISize(gfxContext* aRenderingContext) { + nscoord result = 0; + + if (!IsHidden(false)) { + if (mContent->IsHTMLElement(nsGkAtoms::embed)) { + bool vertical = GetWritingMode().IsVertical(); + result = nsPresContext::CSSPixelsToAppUnits(vertical ? EMBED_DEF_HEIGHT + : EMBED_DEF_WIDTH); + } + } + + DISPLAY_MIN_INLINE_SIZE(this, result); + return result; +} + +/* virtual */ +nscoord nsPluginFrame::GetPrefISize(gfxContext* aRenderingContext) { + return nsPluginFrame::GetMinISize(aRenderingContext); +} + +void nsPluginFrame::GetWidgetConfiguration( + nsTArray<nsIWidget::Configuration>* aConfigurations) { + if (!mWidget) { + return; + } + + if (!mWidget->GetParent()) { + // Plugin widgets should not be toplevel except when they're out of the + // document, in which case the plugin should not be registered for + // geometry updates and this should not be called. But apparently we + // have bugs where mWidget sometimes is toplevel here. Bail out. + NS_ERROR( + "Plugin widgets registered for geometry updates should not be " + "toplevel"); + return; + } + + nsIWidget::Configuration* configuration = aConfigurations->AppendElement(); + configuration->mChild = mWidget; + configuration->mBounds = mNextConfigurationBounds; + configuration->mClipRegion = mNextConfigurationClipRegion.Clone(); +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + if (XRE_IsContentProcess()) { + configuration->mWindowID = + (uintptr_t)mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT); + configuration->mVisible = mWidget->IsVisible(); + } +#endif // defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +} + +void nsPluginFrame::GetDesiredSize(nsPresContext* aPresContext, + const ReflowInput& aReflowInput, + ReflowOutput& aMetrics) { + // By default, we have no area + aMetrics.ClearSize(); + + if (IsHidden(false)) { + return; + } + + aMetrics.Width() = aReflowInput.ComputedWidth(); + aMetrics.Height() = aReflowInput.ComputedHeight(); + + // for EMBED, default to 240x200 for compatibility + if (mContent->IsHTMLElement(nsGkAtoms::embed)) { + if (aMetrics.Width() == NS_UNCONSTRAINEDSIZE) { + aMetrics.Width() = clamped( + nsPresContext::CSSPixelsToAppUnits(EMBED_DEF_WIDTH), + aReflowInput.ComputedMinWidth(), aReflowInput.ComputedMaxWidth()); + } + if (aMetrics.Height() == NS_UNCONSTRAINEDSIZE) { + aMetrics.Height() = clamped( + nsPresContext::CSSPixelsToAppUnits(EMBED_DEF_HEIGHT), + aReflowInput.ComputedMinHeight(), aReflowInput.ComputedMaxHeight()); + } + +#if defined(MOZ_WIDGET_GTK) + // We need to make sure that the size of the object frame does not + // exceed the maximum size of X coordinates. See bug #225357 for + // more information. In theory Gtk2 can handle large coordinates, + // but underlying plugins can't. + aMetrics.Height() = std::min(aPresContext->DevPixelsToAppUnits(INT16_MAX), + aMetrics.Height()); + aMetrics.Width() = std::min(aPresContext->DevPixelsToAppUnits(INT16_MAX), + aMetrics.Width()); +#endif + } + + // At this point, the width has an unconstrained value only if we have + // nothing to go on (no width set, no information from the plugin, nothing). + // Make up a number. + if (aMetrics.Width() == NS_UNCONSTRAINEDSIZE) { + aMetrics.Width() = (aReflowInput.ComputedMinWidth() != NS_UNCONSTRAINEDSIZE) + ? aReflowInput.ComputedMinWidth() + : 0; + } + + // At this point, the height has an unconstrained value only in two cases: + // a) We are in standards mode with percent heights and parent is auto-height + // b) We have no height information at all. + // In either case, we have to make up a number. + if (aMetrics.Height() == NS_UNCONSTRAINEDSIZE) { + aMetrics.Height() = + (aReflowInput.ComputedMinHeight() != NS_UNCONSTRAINEDSIZE) + ? aReflowInput.ComputedMinHeight() + : 0; + } + + // XXXbz don't add in the border and padding, because we screw up our + // plugin's size and positioning if we do... Eventually we _do_ want to + // paint borders, though! At that point, we will need to adjust the desired + // size either here or in Reflow.... Further, we will need to fix Paint() to + // call the superclass in all cases. +} + +void nsPluginFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) { + MarkInReflow(); + DO_GLOBAL_REFLOW_COUNT("nsPluginFrame"); + DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); + MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); + + // Get our desired size + GetDesiredSize(aPresContext, aReflowInput, aMetrics); + aMetrics.SetOverflowAreasToDesiredBounds(); + FinishAndStoreOverflow(&aMetrics); + + // delay plugin instantiation until all children have + // arrived. Otherwise there may be PARAMs or other stuff that the + // plugin needs to see that haven't arrived yet. + if (!GetContent()->IsDoneAddingChildren()) { + return; + } + + // if we are printing or print previewing, bail for now + if (aPresContext->Medium() == nsGkAtoms::print) { + return; + } + + nsRect r(0, 0, aMetrics.Width(), aMetrics.Height()); + r.Deflate(aReflowInput.ComputedPhysicalBorderPadding()); + + if (mInnerView) { + nsViewManager* vm = mInnerView->GetViewManager(); + vm->MoveViewTo(mInnerView, r.x, r.y); + vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), r.Size()), true); + } + + FixupWindow(r.Size()); + if (!mReflowCallbackPosted) { + mReflowCallbackPosted = true; + aPresContext->PresShell()->PostReflowCallback(this); + } + + NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics); +} + +///////////// nsIReflowCallback /////////////// + +bool nsPluginFrame::ReflowFinished() { + mReflowCallbackPosted = false; + CallSetWindow(); + return true; +} + +void nsPluginFrame::ReflowCallbackCanceled() { mReflowCallbackPosted = false; } + +void nsPluginFrame::FixupWindow(const nsSize& aSize) { + nsPresContext* presContext = PresContext(); + + if (!mInstanceOwner) return; + + NPWindow* window; + mInstanceOwner->GetWindow(window); + + NS_ENSURE_TRUE_VOID(window); + + bool windowless = (window->type == NPWindowTypeDrawable); + + nsIntPoint origin = GetWindowOriginInPixels(windowless); + + // window must be in "display pixels" +#if defined(XP_MACOSX) + // window must be in "display pixels" + double scaleFactor = 1.0; + if (NS_FAILED(mInstanceOwner->GetContentsScaleFactor(&scaleFactor))) { + scaleFactor = 1.0; + } + int intScaleFactor = ceil(scaleFactor); + window->x = origin.x / intScaleFactor; + window->y = origin.y / intScaleFactor; + window->width = + presContext->AppUnitsToDevPixels(aSize.width) / intScaleFactor; + window->height = + presContext->AppUnitsToDevPixels(aSize.height) / intScaleFactor; +#else + window->x = origin.x; + window->y = origin.y; + window->width = presContext->AppUnitsToDevPixels(aSize.width); + window->height = presContext->AppUnitsToDevPixels(aSize.height); +#endif + +#ifndef XP_MACOSX + mInstanceOwner->UpdateWindowPositionAndClipRect(false); +#endif + + NotifyPluginReflowObservers(); +} + +nsresult nsPluginFrame::CallSetWindow(bool aCheckIsHidden) { + NPWindow* win = nullptr; + + nsresult rv = NS_ERROR_FAILURE; + RefPtr<nsNPAPIPluginInstance> pi; + if (!mInstanceOwner || !(pi = mInstanceOwner->GetInstance()) || + NS_FAILED(rv = mInstanceOwner->GetWindow(win)) || !win) + return rv; + + nsPluginNativeWindow* window = (nsPluginNativeWindow*)win; + + if (aCheckIsHidden && IsHidden()) return NS_ERROR_FAILURE; + + // Calling either nsPluginInstanceOwner::FixUpPluginWindow() (here, + // on OS X) or SetWindow() (below, on all platforms) can destroy this + // frame. (FixUpPluginWindow() calls SetWindow()). So grab a safe + // reference to mInstanceOwner which we can use below, if needed. + RefPtr<nsPluginInstanceOwner> instanceOwnerRef(mInstanceOwner); + + // refresh the plugin port as well +#ifdef XP_MACOSX + mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintEnable); + // Bail now if our frame has been destroyed. + if (!instanceOwnerRef->GetFrame()) { + return NS_ERROR_FAILURE; + } +#endif + window->window = mInstanceOwner->GetPluginPort(); + + // Adjust plugin dimensions according to pixel snap results + // and reduce amount of SetWindow calls + nsPresContext* presContext = PresContext(); + nsRootPresContext* rootPC = presContext->GetRootPresContext(); + if (!rootPC) return NS_ERROR_FAILURE; + int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); + nsIFrame* rootFrame = rootPC->PresShell()->GetRootFrame(); + nsRect bounds = + GetContentRectRelativeToSelf() + GetOffsetToCrossDoc(rootFrame); + nsIntRect intBounds = bounds.ToNearestPixels(appUnitsPerDevPixel); + + // In e10s, this returns the offset to the top level window, in non-e10s + // it return 0,0. + LayoutDeviceIntPoint intOffset = GetRemoteTabChromeOffset(); + intBounds.x += intOffset.x; + intBounds.y += intOffset.y; + +#if defined(XP_MACOSX) + // window must be in "display pixels" + double scaleFactor = 1.0; + if (NS_FAILED(instanceOwnerRef->GetContentsScaleFactor(&scaleFactor))) { + scaleFactor = 1.0; + } + + size_t intScaleFactor = ceil(scaleFactor); + window->x = intBounds.x / intScaleFactor; + window->y = intBounds.y / intScaleFactor; + window->width = intBounds.width / intScaleFactor; + window->height = intBounds.height / intScaleFactor; +#else + window->x = intBounds.x; + window->y = intBounds.y; + window->width = intBounds.width; + window->height = intBounds.height; +#endif + // BE CAREFUL: By the time we get here the PluginFrame is sometimes destroyed + // and poisoned. If we reference local fields (implicit this deref), + // we will crash. + instanceOwnerRef->ResolutionMayHaveChanged(); + + // This will call pi->SetWindow and take care of window subclassing + // if needed, see bug 132759. Calling SetWindow can destroy this frame + // so check for that before doing anything else with this frame's memory. + if (instanceOwnerRef->UseAsyncRendering()) { + rv = pi->AsyncSetWindow(window); + } else { + rv = window->CallSetWindow(pi); + } + + instanceOwnerRef->ReleasePluginPort(window->window); + + return rv; +} + +void nsPluginFrame::RegisterPluginForGeometryUpdates() { + nsRootPresContext* rpc = PresContext()->GetRootPresContext(); + NS_ASSERTION(rpc, "We should have a root pres context!"); + if (mRootPresContextRegisteredWith == rpc || !rpc) { + // Already registered with current root pres context, + // or null root pres context... + return; + } + if (mRootPresContextRegisteredWith && mRootPresContextRegisteredWith != rpc) { + // Registered to some other root pres context. Unregister, and + // re-register with our current one... + UnregisterPluginForGeometryUpdates(); + } + mRootPresContextRegisteredWith = rpc; + mRootPresContextRegisteredWith->RegisterPluginForGeometryUpdates(mContent); +} + +void nsPluginFrame::UnregisterPluginForGeometryUpdates() { + if (!mRootPresContextRegisteredWith) { + // Not registered... + return; + } + mRootPresContextRegisteredWith->UnregisterPluginForGeometryUpdates(mContent); + mRootPresContextRegisteredWith = nullptr; +} + +void nsPluginFrame::SetInstanceOwner(nsPluginInstanceOwner* aOwner) { + // The ownership model here is historically fuzzy. This should only be called + // by nsPluginInstanceOwner when it is given a new frame, and + // nsObjectLoadingContent should be arbitrating frame-ownership via its + // HasNewFrame callback. + mInstanceOwner = aOwner; + + // Reset the DidCompositeObserver since the owner changed. + mDidCompositeObserver = nullptr; + + if (mInstanceOwner) { + return; + } + + UnregisterPluginForGeometryUpdates(); + if (mWidget && mInnerView) { + mInnerView->DetachWidgetEventHandler(mWidget); + // Make sure the plugin is hidden in case an update of plugin geometry + // hasn't happened since this plugin became hidden. + nsIWidget* parent = mWidget->GetParent(); + if (parent) { + nsTArray<nsIWidget::Configuration> configurations; + nsIWidget::Configuration* configuration = configurations.AppendElement(); + configuration->mChild = mWidget; + parent->ConfigureChildren(configurations); + + mWidget->Show(false); + mWidget->Enable(false); + mWidget->SetParent(nullptr); + } + } +} + +bool nsPluginFrame::IsHidden(bool aCheckVisibilityStyle) const { + if (aCheckVisibilityStyle) { + if (!StyleVisibility()->IsVisibleOrCollapsed()) return true; + } + + // only <embed> tags support the HIDDEN attribute + if (mContent->IsHTMLElement(nsGkAtoms::embed)) { + // Yes, these are really the kooky ways that you could tell 4.x + // not to hide the <embed> once you'd put the 'hidden' attribute + // on the tag... + + // HIDDEN w/ no attributes gets translated as we are hidden for + // compatibility w/ 4.x and IE so we don't create a non-painting + // widget in layout. See bug 188959. + nsAutoString hidden; + if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::hidden, + hidden) && + (hidden.IsEmpty() || (!hidden.LowerCaseEqualsLiteral("false") && + !hidden.LowerCaseEqualsLiteral("no") && + !hidden.LowerCaseEqualsLiteral("off")))) { + return true; + } + } + + return false; +} + +mozilla::LayoutDeviceIntPoint nsPluginFrame::GetRemoteTabChromeOffset() { + LayoutDeviceIntPoint offset; + if (XRE_IsContentProcess()) { + if (nsPIDOMWindowOuter* window = GetContent()->OwnerDoc()->GetWindow()) { + if (nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetInProcessTop()) { + dom::BrowserChild* tc = dom::BrowserChild::GetFrom(topWindow); + if (tc) { + offset += tc->GetChromeOffset(); + } + } + } + } + return offset; +} + +nsIntPoint nsPluginFrame::GetWindowOriginInPixels(bool aWindowless) { + nsView* parentWithView; + nsPoint origin(0, 0); + + GetOffsetFromView(origin, &parentWithView); + + // if it's windowless, let's make sure we have our origin set right + // it may need to be corrected, like after scrolling + if (aWindowless && parentWithView) { + nsPoint offsetToWidget; + parentWithView->GetNearestWidget(&offsetToWidget); + origin += offsetToWidget; + } + origin += GetContentRectRelativeToSelf().TopLeft(); + + nsIntPoint pt(PresContext()->AppUnitsToDevPixels(origin.x), + PresContext()->AppUnitsToDevPixels(origin.y)); + + // If we're in the content process offsetToWidget is tied to the top level + // widget we can access in the child process, which is the tab. We need the + // offset all the way up to the top level native window here. (If this is + // non-e10s this routine will return 0,0.) + if (aWindowless) { + mozilla::LayoutDeviceIntPoint lpt = GetRemoteTabChromeOffset(); + pt += nsIntPoint(lpt.x, lpt.y); + } + + return pt; +} + +void nsPluginFrame::DidReflow(nsPresContext* aPresContext, + const ReflowInput* aReflowInput) { + // Do this check before calling the superclass, as that clears + // NS_FRAME_FIRST_REFLOW + if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { + nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(mContent)); + NS_ASSERTION(objContent, "Why not an object loading content?"); + objContent->HasNewFrame(this); + } + + nsIFrame::DidReflow(aPresContext, aReflowInput); + + if (HasView()) { + nsView* view = GetView(); + nsViewManager* vm = view->GetViewManager(); + if (vm) + vm->SetViewVisibility( + view, IsHidden() ? nsViewVisibility_kHide : nsViewVisibility_kShow); + } +} + +/* static */ +void nsPluginFrame::PaintPrintPlugin(nsIFrame* aFrame, gfxContext* aCtx, + const nsRect& aDirtyRect, nsPoint aPt) { + // Translate the context: + nsPoint pt = aPt + aFrame->GetContentRectRelativeToSelf().TopLeft(); + gfxPoint devPixelPt = nsLayoutUtils::PointToGfxPoint( + pt, aFrame->PresContext()->AppUnitsPerDevPixel()); + + gfxContextMatrixAutoSaveRestore autoSR(aCtx); + aCtx->SetMatrixDouble(aCtx->CurrentMatrixDouble().PreTranslate(devPixelPt)); + + // FIXME - Bug 385435: Doesn't aDirtyRect need translating too? + + static_cast<nsPluginFrame*>(aFrame)->PrintPlugin(*aCtx, aDirtyRect); +} + +/** + * nsDisplayPluginReadback creates an active ReadbackLayer. The ReadbackLayer + * obtains from the compositor the contents of the window underneath + * the ReadbackLayer, which we then use as an opaque buffer for plugins to + * asynchronously draw onto. + */ +class nsDisplayPluginReadback : public nsPaintedDisplayItem { + public: + nsDisplayPluginReadback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) + : nsPaintedDisplayItem(aBuilder, aFrame) { + MOZ_COUNT_CTOR(nsDisplayPluginReadback); + } +#ifdef NS_BUILD_REFCNT_LOGGING + ~nsDisplayPluginReadback() override { + MOZ_COUNT_DTOR(nsDisplayPluginReadback); + } +#endif + + nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; + + NS_DISPLAY_DECL_NAME("PluginReadback", TYPE_PLUGIN_READBACK) + + already_AddRefed<Layer> BuildLayer( + nsDisplayListBuilder* aBuilder, LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) override { + return static_cast<nsPluginFrame*>(mFrame)->BuildLayer( + aBuilder, aManager, this, aContainerParameters); + } + + LayerState GetLayerState( + nsDisplayListBuilder* aBuilder, LayerManager* aManager, + const ContainerLayerParameters& aParameters) override { + return LayerState::LAYER_ACTIVE; + } + + virtual nsDisplayItemGeometry* AllocateGeometry( + nsDisplayListBuilder* aBuilder) override { + return new nsDisplayPluginGeometry(this, aBuilder); + } +}; + +static nsRect GetDisplayItemBounds(nsDisplayListBuilder* aBuilder, + const nsDisplayItem* aItem, + nsIFrame* aFrame) { + // XXX For slightly more accurate region computations we should pixel-snap + // this + return aFrame->GetContentRectRelativeToSelf() + aItem->ToReferenceFrame(); +} + +nsRect nsDisplayPluginReadback::GetBounds(nsDisplayListBuilder* aBuilder, + bool* aSnap) const { + *aSnap = false; + return GetDisplayItemBounds(aBuilder, this, mFrame); +} + +nsRect nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder, + bool* aSnap) const { + *aSnap = true; + return GetDisplayItemBounds(aBuilder, this, mFrame); +} + +void nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { + nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame); + bool snap; + f->PaintPlugin(aBuilder, *aCtx, GetPaintRect(), GetBounds(aBuilder, &snap)); +} + +static nsRect GetClippedBoundsIncludingAllScrollClips( + nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) { + nsRect r = aItem->GetClippedBounds(aBuilder); + for (auto* sc = aItem->GetClipChain(); sc; sc = sc->mParent) { + r = sc->mClip.ApplyNonRoundedIntersection(r); + } + return r; +} + +bool nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion) { + if (aBuilder->IsForPluginGeometry()) { + nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame); + if (!aBuilder->IsInTransform() || f->IsPaintedByGecko()) { + // Since transforms induce reference frames, we don't need to worry + // about this method fluffing out due to non-rectilinear transforms. + nsRect rAncestor = nsLayoutUtils::TransformFrameRectToAncestor( + f, f->GetContentRectRelativeToSelf(), ReferenceFrame()); + nscoord appUnitsPerDevPixel = + ReferenceFrame()->PresContext()->AppUnitsPerDevPixel(); + f->mNextConfigurationBounds = LayoutDeviceIntRect::FromUnknownRect( + rAncestor.ToNearestPixels(appUnitsPerDevPixel)); + + nsRegion visibleRegion; + // Apply all scroll clips when computing the clipped bounds of this item. + // We hide windowed plugins during APZ scrolling, so there never is an + // async transform that we need to take into account when clipping. + visibleRegion.And( + *aVisibleRegion, + GetClippedBoundsIncludingAllScrollClips(this, aBuilder)); + // Make visibleRegion relative to f + visibleRegion.MoveBy(-ToReferenceFrame()); + + f->mNextConfigurationClipRegion.Clear(); + for (auto iter = visibleRegion.RectIter(); !iter.Done(); iter.Next()) { + nsRect rAncestor = nsLayoutUtils::TransformFrameRectToAncestor( + f, iter.Get(), ReferenceFrame()); + LayoutDeviceIntRect rPixels = + LayoutDeviceIntRect::FromUnknownRect( + rAncestor.ToNearestPixels(appUnitsPerDevPixel)) - + f->mNextConfigurationBounds.TopLeft(); + if (!rPixels.IsEmpty()) { + f->mNextConfigurationClipRegion.AppendElement(rPixels); + } + } + } + + if (f->mInnerView) { + // This should produce basically the same rectangle (but not relative + // to the root frame). We only call this here for the side-effect of + // setting mViewToWidgetOffset on the view. + f->mInnerView->CalcWidgetBounds(eWindowType_plugin); + } + } + + return nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion); +} + +nsRegion nsDisplayPlugin::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + bool* aSnap) const { + *aSnap = false; + nsRegion result; + nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame); + if (!aBuilder->IsForPluginGeometry()) { + nsIWidget* widget = f->GetWidget(); + if (widget) { + // Be conservative and treat plugins with widgets as not opaque, + // because that's simple and we might need the content under the widget + // if the widget is unexpectedly clipped away. (As can happen when + // chrome content over a plugin forces us to clip out the plugin for + // security reasons.) + // We shouldn't be repainting the content under plugins much anyway + // since there generally shouldn't be anything to invalidate or paint + // in PaintedLayers there. + return result; + } + } + + if (f->IsOpaque()) { + nsRect bounds = GetBounds(aBuilder, aSnap); + if (aBuilder->IsForPluginGeometry() || + (f->GetPaintedRect(this) + ToReferenceFrame()).Contains(bounds)) { + // We can treat this as opaque + result = bounds; + } + } + + return result; +} + +bool nsDisplayPlugin::CreateWebRenderCommands( + mozilla::wr::DisplayListBuilder& aBuilder, + mozilla::wr::IpcResourceUpdateQueue& aResources, + const StackingContextHelper& aSc, + mozilla::layers::RenderRootStateManager* aManager, + nsDisplayListBuilder* aDisplayListBuilder) { + return static_cast<nsPluginFrame*>(mFrame)->CreateWebRenderCommands( + this, aBuilder, aResources, aSc, aManager, aDisplayListBuilder); +} + +nsresult nsPluginFrame::PluginEventNotifier::Run() { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + obsSvc->NotifyObservers(nullptr, "plugin-changed-event", mEventType.get()); + return NS_OK; +} + +void nsPluginFrame::NotifyPluginReflowObservers() { + nsContentUtils::AddScriptRunner(new PluginEventNotifier(u"reflow"_ns)); +} + +void nsPluginFrame::DidSetWidgetGeometry() { +#if defined(XP_MACOSX) + if (mInstanceOwner && !IsHidden()) { + mInstanceOwner->FixUpPluginWindow( + nsPluginInstanceOwner::ePluginPaintEnable); + } +#else + if (!mWidget && mInstanceOwner) { + // UpdateWindowVisibility will notify the plugin of position changes + // by updating the NPWindow and calling NPP_SetWindow/AsyncSetWindow. + // We treat windowless plugins inside popups as always visible, since + // plugins inside popups don't get valid mNextConfigurationBounds + // set up. + mInstanceOwner->UpdateWindowVisibility( + nsLayoutUtils::IsPopup(nsLayoutUtils::GetDisplayRootFrame(this)) || + !mNextConfigurationBounds.IsEmpty()); + } +#endif +} + +bool nsPluginFrame::IsOpaque() const { +#if defined(XP_MACOSX) + return false; +#else + + if (mInstanceOwner && mInstanceOwner->UseAsyncRendering()) { + return false; + } + return !IsTransparentMode(); +#endif +} + +bool nsPluginFrame::IsTransparentMode() const { +#if defined(XP_MACOSX) + return false; +#else + if (!mInstanceOwner) return false; + + NPWindow* window = nullptr; + mInstanceOwner->GetWindow(window); + if (!window) { + return false; + } + + if (window->type != NPWindowTypeDrawable) return false; + + RefPtr<nsNPAPIPluginInstance> pi = mInstanceOwner->GetInstance(); + if (!pi) { + return false; + } + + bool transparent = false; + pi->IsTransparent(&transparent); + return transparent; +#endif +} + +void nsPluginFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) { + // XXX why are we painting collapsed object frames? + if (!IsVisibleOrCollapsedForPainting()) return; + + DisplayBorderBackgroundOutline(aBuilder, aLists); + + nsPresContext::nsPresContextType type = PresContext()->Type(); + + // If we are painting in Print Preview do nothing.... + if (type == nsPresContext::eContext_PrintPreview) return; + + DO_GLOBAL_REFLOW_COUNT_DSP("nsPluginFrame"); + +#ifndef XP_MACOSX + if (mWidget && aBuilder->IsInTransform()) { + // Windowed plugins should not be rendered inside a transform. + return; + } +#endif + + if (aBuilder->IsForPainting() && mInstanceOwner) { + // Update plugin frame for both content scaling and full zoom changes. + mInstanceOwner->ResolutionMayHaveChanged(); +#ifdef XP_MACOSX + mInstanceOwner->WindowFocusMayHaveChanged(); +#endif + if (mInstanceOwner->UseAsyncRendering()) { + NPWindow* window = nullptr; + mInstanceOwner->GetWindow(window); + bool isVisible = window && window->width > 0 && window->height > 0; + if (isVisible && aBuilder->ShouldSyncDecodeImages()) { +#ifndef XP_MACOSX + mInstanceOwner->UpdateWindowVisibility(true); +#endif + } + + mInstanceOwner->NotifyPaintWaiter(aBuilder); + } + } + + DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox clip( + aBuilder, this); + + // determine if we are printing + if (type == nsPresContext::eContext_Print) { + aLists.Content()->AppendNewToTop<nsDisplayGeneric>( + aBuilder, this, PaintPrintPlugin, "PrintPlugin", + DisplayItemType::TYPE_PRINT_PLUGIN); + } else { + LayerState state = GetLayerState(aBuilder, nullptr); + if (state == LayerState::LAYER_INACTIVE && + nsDisplayItem::ForceActiveLayers()) { + state = LayerState::LAYER_ACTIVE; + } + if (aBuilder->IsPaintingToWindow() && state == LayerState::LAYER_ACTIVE && + IsTransparentMode()) { + aLists.Content()->AppendNewToTop<nsDisplayPluginReadback>(aBuilder, this); + } + + aLists.Content()->AppendNewToTop<nsDisplayPlugin>(aBuilder, this); + } +} + +void nsPluginFrame::PrintPlugin(gfxContext& aRenderingContext, + const nsRect& aDirtyRect) { + nsCOMPtr<nsIObjectLoadingContent> obj(do_QueryInterface(mContent)); + if (!obj) return; + + nsIFrame* frame = nullptr; + obj->GetPrintFrame(&frame); + if (!frame) return; + + nsPresContext* presContext = PresContext(); + // make sure this is REALLY an nsIObjectFrame + // we may need to go through the children to get it + nsIObjectFrame* objectFrame = do_QueryFrame(frame); + if (!objectFrame) objectFrame = GetNextObjectFrame(presContext, frame); + if (!objectFrame) return; + + // finally we can get our plugin instance + RefPtr<nsNPAPIPluginInstance> pi = objectFrame->GetPluginInstance(); + if (!pi) { + return; + } + + // now we need to setup the correct location for printing + NPWindow window; + window.window = nullptr; + + // prepare embedded mode printing struct + NPPrint npprint; + npprint.mode = NP_EMBED; + + // we need to find out if we are windowless or not + bool windowless = false; + pi->IsWindowless(&windowless); + window.type = windowless ? NPWindowTypeDrawable : NPWindowTypeWindow; + + window.clipRect.bottom = 0; + window.clipRect.top = 0; + window.clipRect.left = 0; + window.clipRect.right = 0; + +// platform specific printing code +#if defined(XP_UNIX) || defined(XP_MACOSX) + // Doesn't work in a thebes world, or on OS X. + (void)window; + (void)npprint; +#elif defined(XP_WIN) + + /* On Windows, we use the win32 printing surface to print. This, in + * turn, uses the Cairo paginated surface, which in turn uses the + * meta surface to record all operations and then play them back. + * This doesn't work too well for plugins, because if plugins render + * directly into the DC, the meta surface won't have any knowledge + * of them, and so at the end when it actually does the replay step, + * it'll fill the background with white and draw over whatever was + * rendered before. + * + * So, to avoid this, we use PushGroup, which creates a new windows + * surface, the plugin renders to that, and then we use normal + * cairo methods to composite that in such that it's recorded using the + * meta surface. + */ + + // we'll already be translated into the right spot by gfxWindowsNativeDrawing + nsSize contentSize = GetContentRectRelativeToSelf().Size(); + window.x = 0; + window.y = 0; + window.width = presContext->AppUnitsToDevPixels(contentSize.width); + window.height = presContext->AppUnitsToDevPixels(contentSize.height); + + aRenderingContext.Save(); + + // Make sure plugins don't do any damage outside of where they're supposed to + aRenderingContext.NewPath(); + gfxRect r(window.x, window.y, window.width, window.height); + aRenderingContext.Rectangle(r); + aRenderingContext.Clip(); + + gfxWindowsNativeDrawing nativeDraw(&aRenderingContext, r); + do { + HDC dc = nativeDraw.BeginNativeDrawing(); + if (!dc) return; + + // XXX don't we need to call nativeDraw.TransformToNativeRect here? + npprint.print.embedPrint.platformPrint = dc; + npprint.print.embedPrint.window = window; + // send off print info to plugin + pi->Print(&npprint); + + nativeDraw.EndNativeDrawing(); + } while (nativeDraw.ShouldRenderAgain()); + nativeDraw.PaintToContext(); + + aRenderingContext.Restore(); +#endif + + // XXX Nav 4.x always sent a SetWindow call after print. Should we do the + // same? + // XXX Calling DidReflow here makes no sense!!! + frame->DidReflow(presContext, nullptr); // DidReflow will take care of it +} + +nsRect nsPluginFrame::GetPaintedRect(const nsDisplayPlugin* aItem) const { + if (!mInstanceOwner) return nsRect(); + nsRect r = GetContentRectRelativeToSelf(); + if (!mInstanceOwner->UseAsyncRendering()) return r; + + nsIntSize size = mInstanceOwner->GetCurrentImageSize(); + nsPresContext* pc = PresContext(); + r.IntersectRect(r, nsRect(0, 0, pc->DevPixelsToAppUnits(size.width), + pc->DevPixelsToAppUnits(size.height))); + return r; +} + +LayerState nsPluginFrame::GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager) { + if (!mInstanceOwner) return LayerState::LAYER_NONE; + + if (mInstanceOwner->NeedsScrollImageLayer()) { + return LayerState::LAYER_ACTIVE; + } + + if (!mInstanceOwner->UseAsyncRendering()) { + return LayerState::LAYER_NONE; + } + + return LayerState::LAYER_ACTIVE_FORCE; +} + +class PluginFrameDidCompositeObserver final : public DidCompositeObserver { + public: + PluginFrameDidCompositeObserver(nsPluginInstanceOwner* aOwner, + LayerManager* aLayerManager) + : mInstanceOwner(aOwner), mLayerManager(aLayerManager) {} + ~PluginFrameDidCompositeObserver() { + mLayerManager->RemoveDidCompositeObserver(this); + } + void DidComposite() override { mInstanceOwner->DidComposite(); } + bool IsValid(LayerManager* aLayerManager) { + return aLayerManager == mLayerManager; + } + + private: + nsPluginInstanceOwner* mInstanceOwner; + RefPtr<LayerManager> mLayerManager; +}; + +bool nsPluginFrame::GetBounds(nsDisplayItem* aItem, IntSize& aSize, + gfxRect& aRect) { + if (!mInstanceOwner) return false; + + NPWindow* window = nullptr; + mInstanceOwner->GetWindow(window); + if (!window) return false; + + if (window->width <= 0 || window->height <= 0) return false; + +#if defined(XP_MACOSX) + // window is in "display pixels", but size needs to be in device pixels + // window must be in "display pixels" + double scaleFactor = 1.0; + if (NS_FAILED(mInstanceOwner->GetContentsScaleFactor(&scaleFactor))) { + scaleFactor = 1.0; + } + + size_t intScaleFactor = ceil(scaleFactor); +#else + size_t intScaleFactor = 1; +#endif + + aSize = + IntSize(window->width * intScaleFactor, window->height * intScaleFactor); + + nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame(); + aRect = + nsLayoutUtils::RectToGfxRect(area, PresContext()->AppUnitsPerDevPixel()); + // to provide crisper and faster drawing. + aRect.Round(); + + return true; +} + +bool nsPluginFrame::CreateWebRenderCommands( + nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder, + mozilla::wr::IpcResourceUpdateQueue& aResources, + const StackingContextHelper& aSc, + mozilla::layers::RenderRootStateManager* aManager, + nsDisplayListBuilder* aDisplayListBuilder) { + IntSize size; + gfxRect r; + if (!GetBounds(aItem, size, r)) { + return true; + } + + RefPtr<ImageContainer> container; + // Image for Windowed plugins that support window capturing for scroll + // operations or async windowless rendering. + container = mInstanceOwner->GetImageContainer(); + if (!container) { + // This can occur if our instance is gone or if the current plugin + // configuration does not require a backing image layer. + return true; + } + +#ifdef XP_MACOSX + if (!mInstanceOwner->UseAsyncRendering()) { + mInstanceOwner->DoCocoaEventDrawRect(r, nullptr); + } +#endif + + RefPtr<LayerManager> lm = aDisplayListBuilder->GetWidgetLayerManager(); + if (!mDidCompositeObserver || !mDidCompositeObserver->IsValid(lm)) { + mDidCompositeObserver = + MakeUnique<PluginFrameDidCompositeObserver>(mInstanceOwner, lm); + } + lm->AddDidCompositeObserver(mDidCompositeObserver.get()); + + // If the image container is empty, we don't want to fallback. Any other + // failure will be due to resource constraints and fallback is unlikely to + // help us. Hence we can ignore the return value from PushImage. + LayoutDeviceRect dest(r.x, r.y, size.width, size.height); + aManager->CommandBuilder().PushImage(aItem, container, aBuilder, aResources, + aSc, dest, dest); + return true; +} + +already_AddRefed<Layer> nsPluginFrame::BuildLayer( + nsDisplayListBuilder* aBuilder, LayerManager* aManager, + nsDisplayItem* aItem, + const ContainerLayerParameters& aContainerParameters) { + IntSize size; + gfxRect r; + if (!GetBounds(aItem, size, r)) { + return nullptr; + } + + RefPtr<Layer> layer = + (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem)); + + if (aItem->GetType() == DisplayItemType::TYPE_PLUGIN) { + RefPtr<ImageContainer> container; + // Image for Windowed plugins that support window capturing for scroll + // operations or async windowless rendering. + container = mInstanceOwner->GetImageContainer(); + if (!container) { + // This can occur if our instance is gone or if the current plugin + // configuration does not require a backing image layer. + return nullptr; + } + + if (!layer) { + mInstanceOwner->NotifyPaintWaiter(aBuilder); + // Initialize ImageLayer + layer = aManager->CreateImageLayer(); + if (!layer) return nullptr; + } + + NS_ASSERTION(layer->GetType() == Layer::TYPE_IMAGE, "Bad layer type"); + ImageLayer* imglayer = static_cast<ImageLayer*>(layer.get()); +#ifdef XP_MACOSX + if (!mInstanceOwner->UseAsyncRendering()) { + mInstanceOwner->DoCocoaEventDrawRect(r, nullptr); + } +#endif + + imglayer->SetScaleToSize(size, ScaleMode::STRETCH); + imglayer->SetContainer(container); + SamplingFilter samplingFilter = + nsLayoutUtils::GetSamplingFilterForFrame(this); +#ifdef MOZ_GFX_OPTIMIZE_MOBILE + if (!aManager->IsCompositingCheap()) { + // Pixman just horrible with bilinear filter scaling + samplingFilter = SamplingFilter::POINT; + } +#endif + imglayer->SetSamplingFilter(samplingFilter); + + layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0); + + if (aBuilder->IsPaintingToWindow() && aBuilder->GetWidgetLayerManager() && + (aBuilder->GetWidgetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT || + aBuilder->GetWidgetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_WR) && + mInstanceOwner->UseAsyncRendering()) { + RefPtr<LayerManager> lm = aBuilder->GetWidgetLayerManager(); + if (!mDidCompositeObserver || !mDidCompositeObserver->IsValid(lm)) { + mDidCompositeObserver = + MakeUnique<PluginFrameDidCompositeObserver>(mInstanceOwner, lm); + } + lm->AddDidCompositeObserver(mDidCompositeObserver.get()); + } + } else { + NS_ASSERTION(aItem->GetType() == DisplayItemType::TYPE_PLUGIN_READBACK, + "Unknown item type"); + MOZ_ASSERT(!IsOpaque(), "Opaque plugins don't use backgrounds"); + + if (!layer) { + layer = aManager->CreateReadbackLayer(); + if (!layer) return nullptr; + } + NS_ASSERTION(layer->GetType() == Layer::TYPE_READBACK, "Bad layer type"); + + ReadbackLayer* readback = static_cast<ReadbackLayer*>(layer.get()); + if (readback->GetSize() != size) { + // This will destroy any old background sink and notify us that the + // background is now unknown + readback->SetSink(nullptr); + readback->SetSize(size); + + if (mBackgroundSink) { + // Maybe we still have a background sink associated with another + // readback layer that wasn't recycled for some reason? Unhook it + // now so that if this frame goes away, it doesn't have a dangling + // reference to us. + mBackgroundSink->Destroy(); + } + mBackgroundSink = + new PluginBackgroundSink(this, readback->AllocateSequenceNumber()); + readback->SetSink(mBackgroundSink); + // The layer has taken ownership of our sink. When either the sink dies + // or the frame dies, the connection from the surviving object is nulled + // out. + } + } + + // Set a transform on the layer to draw the plugin in the right place + gfxPoint p = r.TopLeft() + aContainerParameters.mOffset; + Matrix transform = Matrix::Translation(p.x, p.y); + + layer->SetBaseTransform(Matrix4x4::From2D(transform)); + return layer.forget(); +} + +void nsPluginFrame::PaintPlugin(nsDisplayListBuilder* aBuilder, + gfxContext& aRenderingContext, + const nsRect& aDirtyRect, + const nsRect& aPluginRect) { +#if defined(DEBUG) + // On Desktop, we should have built a layer as we no longer support in-process + // plugins or synchronous painting. We can only get here for windowed plugins + // (which draw themselves), or via some error/unload state. + if (mInstanceOwner) { + NPWindow* window = nullptr; + mInstanceOwner->GetWindow(window); + MOZ_ASSERT(!window || window->type == NPWindowTypeWindow); + } +#endif +} + +nsresult nsPluginFrame::HandleEvent(nsPresContext* aPresContext, + WidgetGUIEvent* anEvent, + nsEventStatus* anEventStatus) { + NS_ENSURE_ARG_POINTER(anEvent); + NS_ENSURE_ARG_POINTER(anEventStatus); + nsresult rv = NS_OK; + + if (!mInstanceOwner) return NS_ERROR_NULL_POINTER; + + mInstanceOwner->ConsiderNewEventloopNestingLevel(); + + if (anEvent->mMessage == ePluginActivate) { + if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) { + RefPtr<dom::Element> elem = GetContent()->AsElement(); + return fm->SetFocus(elem, 0); + } + } else if (anEvent->mMessage == ePluginFocus) { + if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) { + RefPtr<dom::Element> elem = GetContent()->AsElement(); + return fm->FocusPlugin(elem); + } + } + +#ifdef XP_WIN + rv = nsIFrame::HandleEvent(aPresContext, anEvent, anEventStatus); + return rv; +#endif + +#ifdef XP_MACOSX + // we want to process some native mouse events in the cocoa event model + if ((anEvent->mMessage == eMouseEnterIntoWidget || + anEvent->mMessage == eWheel) && + mInstanceOwner->GetEventModel() == NPEventModelCocoa) { + *anEventStatus = mInstanceOwner->ProcessEvent(*anEvent); + // Due to plugin code reentering Gecko, this frame may be dead at this + // point. + return rv; + } + + // These two calls to PresShell::SetCapturingContent() (on mouse-down + // and mouse-up) are needed to make the routing of mouse events while + // dragging conform to standard OS X practice, and to the Cocoa NPAPI spec. + // See bug 525078 and bug 909678. + if (anEvent->mMessage == eMouseDown) { + PresShell::SetCapturingContent(GetContent(), + CaptureFlags::IgnoreAllowedState); + } +#endif + + rv = nsIFrame::HandleEvent(aPresContext, anEvent, anEventStatus); + + // We need to be careful from this point because the call to + // nsIFrame::HandleEvent() might have killed us. + +#ifdef XP_MACOSX + if (anEvent->mMessage == eMouseUp) { + PresShell::ReleaseCapturingContent(); + } +#endif + + return rv; +} + +void nsPluginFrame::HandleWheelEventAsDefaultAction( + WidgetWheelEvent* aWheelEvent) { + MOZ_ASSERT(WantsToHandleWheelEventAsDefaultAction()); + MOZ_ASSERT(!aWheelEvent->DefaultPrevented()); + + if (NS_WARN_IF(!mInstanceOwner) || + NS_WARN_IF(aWheelEvent->mMessage != eWheel)) { + return; + } + + // If the wheel event has native message, it should may be handled by + // HandleEvent() in the future. In such case, we should do nothing here. + if (NS_WARN_IF(!!aWheelEvent->mPluginEvent)) { + return; + } + + mInstanceOwner->ProcessEvent(*aWheelEvent); + // We need to assume that the event is always consumed/handled by the + // plugin. There is no way to know if it's actually consumed/handled. + aWheelEvent->mViewPortIsOverscrolled = false; + aWheelEvent->mOverflowDeltaX = 0; + aWheelEvent->mOverflowDeltaY = 0; + // Consume the event explicitly. + aWheelEvent->PreventDefault(); +} + +bool nsPluginFrame::WantsToHandleWheelEventAsDefaultAction() const { +#ifdef XP_WIN + if (!mInstanceOwner) { + return false; + } + NPWindow* window = nullptr; + mInstanceOwner->GetWindow(window); + // On Windows, only when the plugin is windowless, we need to send wheel + // events as default action. + return window->type == NPWindowTypeDrawable; +#else + return false; +#endif +} + +nsNPAPIPluginInstance* nsPluginFrame::GetPluginInstance() { + if (!mInstanceOwner) { + return nullptr; + } + + return mInstanceOwner->GetInstance(); +} + +Maybe<nsIFrame::Cursor> nsPluginFrame::GetCursor(const nsPoint& aPoint) { + if (!mInstanceOwner) { + return Nothing(); + } + + RefPtr<nsNPAPIPluginInstance> inst = mInstanceOwner->GetInstance(); + if (!inst) { + return Nothing(); + } + + bool useDOMCursor = + static_cast<nsNPAPIPluginInstance*>(inst.get())->UsesDOMForCursor(); + if (!useDOMCursor) { + return Nothing(); + } + + return nsIFrame::GetCursor(aPoint); +} + +void nsPluginFrame::SetIsDocumentActive(bool aIsActive) { + if (mInstanceOwner) { + mInstanceOwner->UpdateDocumentActiveState(aIsActive); + } +} + +// static +nsIObjectFrame* nsPluginFrame::GetNextObjectFrame(nsPresContext* aPresContext, + nsIFrame* aRoot) { + for (nsIFrame* child : aRoot->PrincipalChildList()) { + nsIObjectFrame* outFrame = do_QueryFrame(child); + if (outFrame) { + if (outFrame->GetPluginInstance()) { // make sure we have a REAL plugin + return outFrame; + } + } + + outFrame = GetNextObjectFrame(aPresContext, child); + if (outFrame) return outFrame; + } + + return nullptr; +} + +/*static*/ +void nsPluginFrame::BeginSwapDocShells(nsISupports* aSupports) { + MOZ_ASSERT(aSupports, "null parameter"); + nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports)); + if (!content) { + return; + } + + // This function is called from a document content enumerator so we need + // to filter out the nsPluginFrames and ignore the rest. + nsIObjectFrame* obj = do_QueryFrame(content->GetPrimaryFrame()); + if (!obj) return; + + nsPluginFrame* objectFrame = static_cast<nsPluginFrame*>(obj); + NS_ASSERTION(!objectFrame->mWidget || objectFrame->mWidget->GetParent(), + "Plugin windows must not be toplevel"); + objectFrame->UnregisterPluginForGeometryUpdates(); +} + +/*static*/ +void nsPluginFrame::EndSwapDocShells(nsISupports* aSupports) { + MOZ_ASSERT(aSupports, "null parameter"); + nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports)); + if (!content) { + return; + } + + // This function is called from a document content enumerator so we need + // to filter out the nsPluginFrames and ignore the rest. + nsIObjectFrame* obj = do_QueryFrame(content->GetPrimaryFrame()); + if (!obj) return; + + nsPluginFrame* objectFrame = static_cast<nsPluginFrame*>(obj); + nsRootPresContext* rootPC = objectFrame->PresContext()->GetRootPresContext(); + NS_ASSERTION(rootPC, "unable to register the plugin frame"); + nsIWidget* widget = objectFrame->mWidget; + if (widget) { + // Reparent the widget. + nsIWidget* parent = rootPC->PresShell()->GetRootFrame()->GetNearestWidget(); + widget->SetParent(parent); + AutoWeakFrame weakFrame(objectFrame); + objectFrame->CallSetWindow(); + if (!weakFrame.IsAlive()) { + return; + } + } + + if (objectFrame->mInstanceOwner) { + objectFrame->RegisterPluginForGeometryUpdates(); + } +} + +nsIFrame* NS_NewObjectFrame(PresShell* aPresShell, ComputedStyle* aStyle) { + return new (aPresShell) nsPluginFrame(aStyle, aPresShell->GetPresContext()); +} + +bool nsPluginFrame::IsPaintedByGecko() const { +#ifdef XP_MACOSX + return true; +#else + return !mWidget; +#endif +} + +NS_IMPL_FRAMEARENA_HELPERS(nsPluginFrame) |