/* -*- 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 #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 # include #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 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 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 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* 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 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 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 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 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 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 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 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(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 BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override { return static_cast(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(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(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(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(mFrame)->CreateWebRenderCommands( this, aBuilder, aResources, aSc, aManager, aDisplayListBuilder); } nsresult nsPluginFrame::PluginEventNotifier::Run() { nsCOMPtr 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 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( 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(aBuilder, this); } aLists.Content()->AppendNewToTop(aBuilder, this); } } void nsPluginFrame::PrintPlugin(gfxContext& aRenderingContext, const nsRect& aDirtyRect) { nsCOMPtr 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 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 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 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 lm = aDisplayListBuilder->GetWidgetLayerManager(); if (!mDidCompositeObserver || !mDidCompositeObserver->IsValid(lm)) { mDidCompositeObserver = MakeUnique(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 nsPluginFrame::BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, nsDisplayItem* aItem, const ContainerLayerParameters& aContainerParameters) { IntSize size; gfxRect r; if (!GetBounds(aItem, size, r)) { return nullptr; } RefPtr layer = (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem)); if (aItem->GetType() == DisplayItemType::TYPE_PLUGIN) { RefPtr 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(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 lm = aBuilder->GetWidgetLayerManager(); if (!mDidCompositeObserver || !mDidCompositeObserver->IsValid(lm)) { mDidCompositeObserver = MakeUnique(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(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 fm = nsFocusManager::GetFocusManager()) { RefPtr elem = GetContent()->AsElement(); return fm->SetFocus(elem, 0); } } else if (anEvent->mMessage == ePluginFocus) { if (RefPtr fm = nsFocusManager::GetFocusManager()) { RefPtr 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 nsPluginFrame::GetCursor(const nsPoint& aPoint) { if (!mInstanceOwner) { return Nothing(); } RefPtr inst = mInstanceOwner->GetInstance(); if (!inst) { return Nothing(); } bool useDOMCursor = static_cast(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 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(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 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(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)