diff options
Diffstat (limited to 'layout/generic/nsPluginFrame.cpp')
-rw-r--r-- | layout/generic/nsPluginFrame.cpp | 1887 |
1 files changed, 1887 insertions, 0 deletions
diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp new file mode 100644 index 000000000..34ed12d44 --- /dev/null +++ b/layout/generic/nsPluginFrame.cpp @@ -0,0 +1,1887 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:set ts=2 sts=2 sw=2 et cin: +/* 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 "gfxMatrix.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/MouseEvents.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 "nsIPresShell.h" +#include "nsWidgetsCID.h" +#include "nsView.h" +#include "nsViewManager.h" +#include "nsString.h" +#include "nsGkAtoms.h" +#include "nsIPluginInstanceOwner.h" +#include "nsNPAPIPluginInstance.h" +#include "nsIDOMElement.h" +#include "nsRenderingContext.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 "DisplayItemScrollClip.h" +#include "Layers.h" +#include "ReadbackLayer.h" +#include "ImageContainer.h" + +// accessibility support +#ifdef ACCESSIBILITY +#include "nsAccessibilityService.h" +#endif + +#ifdef MOZ_LOGGING +#define FORCE_PR_LOG 1 /* Allow logging in the release build */ +#endif /* MOZ_LOGGING */ +#include "mozilla/Logging.h" + +#ifdef XP_MACOSX +#include "gfxQuartzNativeDrawing.h" +#include "mozilla/gfx/QuartzSupport.h" +#endif + +#ifdef MOZ_X11 +#include "mozilla/X11Util.h" +using mozilla::DefaultXDisplay; +#endif + +#ifdef XP_WIN +#include <wtypes.h> +#include <winuser.h> +#endif + +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#include "GLContext.h" +#endif + +#include "mozilla/dom/TabChild.h" +#include "ClientLayerManager.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(nsStyleContext* aContext) + : nsFrame(aContext) + , mInstanceOwner(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(nsFrame) + +#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)); + + nsFrame::Init(aContent, aParent, aPrevInFlow); +} + +void +nsPluginFrame::DestroyFrom(nsIFrame* aDestructRoot) +{ + if (mReflowCallbackPosted) { + PresContext()->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(); + } + + nsFrame::DestroyFrom(aDestructRoot); +} + +/* virtual */ void +nsPluginFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + if (HasView()) { + nsView* view = GetView(); + nsViewManager* vm = view->GetViewManager(); + if (vm) { + nsViewVisibility visibility = + IsHidden() ? nsViewVisibility_kHide : nsViewVisibility_kShow; + vm->SetViewVisibility(view, visibility); + } + } + + nsFrame::DidSetStyleContext(aOldStyleContext); +} + +nsIAtom* +nsPluginFrame::GetType() const +{ + return nsGkAtoms::objectFrame; +} + +#ifdef DEBUG_FRAME_DUMP +nsresult +nsPluginFrame::GetFrameName(nsAString& aResult) const +{ + return MakeFrameName(NS_LITERAL_STRING("PluginFrame"), 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()->FrameManager()->GetRootFrame(); + nsIWidget* parentWidget = rootFrame->GetNearestWidget(); + if (!parentWidget || nsLayoutUtils::GetDisplayRootFrame(this) != rootFrame) { + return NS_ERROR_FAILURE; + } + + 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(eCSSProperty_background_color); + 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 + nsAccessibilityService* accService = nsIPresShell::AccService(); + if (accService) { + accService->RecreateAccessible(PresContext()->PresShell(), mContent); + } +#endif + + return NS_OK; +} + +#define EMBED_DEF_WIDTH 240 +#define EMBED_DEF_HEIGHT 200 + +/* virtual */ nscoord +nsPluginFrame::GetMinISize(nsRenderingContext *aRenderingContext) +{ + nscoord result = 0; + + if (!IsHidden(false)) { + if (mContent->IsAnyOfHTMLElements(nsGkAtoms::applet, + nsGkAtoms::embed)) { + bool vertical = GetWritingMode().IsVertical(); + result = nsPresContext::CSSPixelsToAppUnits( + vertical ? EMBED_DEF_HEIGHT : EMBED_DEF_WIDTH); + } + } + + DISPLAY_MIN_WIDTH(this, result); + return result; +} + +/* virtual */ nscoord +nsPluginFrame::GetPrefISize(nsRenderingContext *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; +#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 and APPLET, default to 240x200 for compatibility + if (mContent->IsAnyOfHTMLElements(nsGkAtoms::applet, + 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); + + // 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()) { + aStatus = NS_FRAME_COMPLETE; + return; + } + + // if we are printing or print previewing, bail for now + if (aPresContext->Medium() == nsGkAtoms::print) { + aStatus = NS_FRAME_COMPLETE; + 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); + } + + aStatus = NS_FRAME_COMPLETE; + + 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 || + NS_FAILED(rv = mInstanceOwner->GetInstance(getter_AddRefs(pi))) || + !pi || + 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()->FrameManager()->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::IsFocusable(int32_t *aTabIndex, bool aWithMouse) +{ + if (aTabIndex) + *aTabIndex = -1; + return nsFrame::IsFocusable(aTabIndex, aWithMouse); +} + +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->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->GetTop()) { + dom::TabChild* tc = dom::TabChild::GetFrom(topWindow); + if (tc) { + offset += tc->GetChromeDisplacement(); + } + } + } + } + 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, + nsDidReflowStatus aStatus) +{ + // Do this check before calling the superclass, as that clears + // NS_FRAME_FIRST_REFLOW + if (aStatus == nsDidReflowStatus::FINISHED && + (GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(mContent)); + NS_ASSERTION(objContent, "Why not an object loading content?"); + objContent->HasNewFrame(this); + } + + nsFrame::DidReflow(aPresContext, aReflowInput, aStatus); + + // The view is created hidden; once we have reflowed it and it has been + // positioned then we show it. + if (aStatus != nsDidReflowStatus::FINISHED) + return; + + 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, nsRenderingContext* aCtx, + const nsRect& aDirtyRect, nsPoint aPt) +{ + gfxContext* ctx = aCtx->ThebesContext(); + + // Translate the context: + nsPoint pt = aPt + aFrame->GetContentRectRelativeToSelf().TopLeft(); + gfxPoint devPixelPt = + nsLayoutUtils::PointToGfxPoint(pt, aFrame->PresContext()->AppUnitsPerDevPixel()); + + gfxContextMatrixAutoSaveRestore autoSR(ctx); + ctx->SetMatrix(ctx->CurrentMatrix().Translate(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 nsDisplayItem { +public: + nsDisplayPluginReadback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) + : nsDisplayItem(aBuilder, aFrame) + { + MOZ_COUNT_CTOR(nsDisplayPluginReadback); + } +#ifdef NS_BUILD_REFCNT_LOGGING + ~nsDisplayPluginReadback() override { + MOZ_COUNT_DTOR(nsDisplayPluginReadback); + } +#endif + + nsRect GetBounds(nsDisplayListBuilder* aBuilder, + bool* aSnap) 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 LAYER_ACTIVE; + } +}; + +static nsRect +GetDisplayItemBounds(nsDisplayListBuilder* aBuilder, 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) +{ + *aSnap = false; + return GetDisplayItemBounds(aBuilder, this, mFrame); +} + +#ifdef MOZ_WIDGET_ANDROID + +class nsDisplayPluginVideo : public nsDisplayItem { +public: + nsDisplayPluginVideo(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsNPAPIPluginInstance::VideoInfo* aVideoInfo) + : nsDisplayItem(aBuilder, aFrame), mVideoInfo(aVideoInfo) + { + MOZ_COUNT_CTOR(nsDisplayPluginVideo); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayPluginVideo() { + MOZ_COUNT_DTOR(nsDisplayPluginVideo); + } +#endif + + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, + bool* aSnap) override; + + NS_DISPLAY_DECL_NAME("PluginVideo", TYPE_PLUGIN_VIDEO) + + virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) override + { + return static_cast<nsPluginFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters); + } + + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) override + { + return LAYER_ACTIVE; + } + + nsNPAPIPluginInstance::VideoInfo* VideoInfo() { return mVideoInfo; } + +private: + nsNPAPIPluginInstance::VideoInfo* mVideoInfo; +}; + +nsRect +nsDisplayPluginVideo::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) +{ + *aSnap = false; + return GetDisplayItemBounds(aBuilder, this, mFrame); +} + +#endif + +nsRect +nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) +{ + *aSnap = true; + return GetDisplayItemBounds(aBuilder, this, mFrame); +} + +void +nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame); + bool snap; + f->PaintPlugin(aBuilder, *aCtx, mVisibleRect, GetBounds(aBuilder, &snap)); +} + +static nsRect +GetClippedBoundsIncludingAllScrollClips(nsDisplayItem* aItem, + nsDisplayListBuilder* aBuilder) +{ + nsRect r = aItem->GetClippedBounds(aBuilder); + for (auto* sc = aItem->ScrollClip(); sc; sc = sc->mParent) { + if (sc->mClip) { + 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) +{ + *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; +} + +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(NS_LITERAL_STRING("reflow"))); +} + +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; +#elif defined(MOZ_WIDGET_ANDROID) + // We don't know, so just assume transparent + 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; + + nsresult rv; + RefPtr<nsNPAPIPluginInstance> pi; + rv = mInstanceOwner->GetInstance(getter_AddRefs(pi)); + if (NS_FAILED(rv) || !pi) + return false; + + bool transparent = false; + pi->IsTransparent(&transparent); + return transparent; +#endif +} + +void +nsPluginFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + // XXX why are we painting collapsed object frames? + if (!IsVisibleOrCollapsedForPainting(aBuilder)) + 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(new (aBuilder) + nsDisplayGeneric(aBuilder, this, PaintPrintPlugin, "PrintPlugin", + nsDisplayItem::TYPE_PRINT_PLUGIN)); + } else { + LayerState state = GetLayerState(aBuilder, nullptr); + if (state == LAYER_INACTIVE && + nsDisplayItem::ForceActiveLayers()) { + state = LAYER_ACTIVE; + } + // We don't need this on Android, and it just confuses things +#if !MOZ_WIDGET_ANDROID + if (aBuilder->IsPaintingToWindow() && + state == LAYER_ACTIVE && + IsTransparentMode()) { + aLists.Content()->AppendNewToTop(new (aBuilder) + nsDisplayPluginReadback(aBuilder, this)); + } +#endif + +#if MOZ_WIDGET_ANDROID + if (aBuilder->IsPaintingToWindow() && + state == LAYER_ACTIVE) { + + nsTArray<nsNPAPIPluginInstance::VideoInfo*> videos; + mInstanceOwner->GetVideos(videos); + + for (uint32_t i = 0; i < videos.Length(); i++) { + aLists.Content()->AppendNewToTop(new (aBuilder) + nsDisplayPluginVideo(aBuilder, this, videos[i])); + } + } +#endif + + aLists.Content()->AppendNewToTop(new (aBuilder) + nsDisplayPlugin(aBuilder, this)); + } +} + +void +nsPluginFrame::PrintPlugin(nsRenderingContext& 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; + if (NS_FAILED(objectFrame->GetPluginInstance(getter_AddRefs(pi))) || !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); + + gfxContext *ctx = aRenderingContext.ThebesContext(); + + ctx->Save(); + + /* Make sure plugins don't do any damage outside of where they're supposed to */ + ctx->NewPath(); + gfxRect r(window.x, window.y, window.width, window.height); + ctx->Rectangle(r); + ctx->Clip(); + + gfxWindowsNativeDrawing nativeDraw(ctx, 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(); + + ctx->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!!! + nsDidReflowStatus status = nsDidReflowStatus::FINISHED; // should we use a special status? + frame->DidReflow(presContext, + nullptr, status); // DidReflow will take care of it +} + +nsRect +nsPluginFrame::GetPaintedRect(nsDisplayPlugin* aItem) +{ + 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 LAYER_NONE; + +#ifdef MOZ_WIDGET_ANDROID + // We always want a layer on Honeycomb and later + return LAYER_ACTIVE; +#else + if (mInstanceOwner->NeedsScrollImageLayer()) { + return LAYER_ACTIVE; + } + + if (!mInstanceOwner->UseAsyncRendering()) { + return LAYER_NONE; + } + + return LAYER_ACTIVE_FORCE; +#endif +} + +class PluginFrameDidCompositeObserver final : public ClientLayerManager:: + DidCompositeObserver +{ +public: + PluginFrameDidCompositeObserver(nsPluginInstanceOwner* aOwner, ClientLayerManager* aLayerManager) + : mInstanceOwner(aOwner), + mLayerManager(aLayerManager) + { + } + ~PluginFrameDidCompositeObserver() { + mLayerManager->RemoveDidCompositeObserver(this); + } + void DidComposite() override { + mInstanceOwner->DidComposite(); + } + bool IsValid(ClientLayerManager* aLayerManager) { + return aLayerManager == mLayerManager; + } + +private: + nsPluginInstanceOwner* mInstanceOwner; + RefPtr<ClientLayerManager> mLayerManager; +}; + +already_AddRefed<Layer> +nsPluginFrame::BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + nsDisplayItem* aItem, + const ContainerLayerParameters& aContainerParameters) +{ + if (!mInstanceOwner) + return nullptr; + + NPWindow* window = nullptr; + mInstanceOwner->GetWindow(window); + if (!window) + return nullptr; + + if (window->width <= 0 || window->height <= 0) + return nullptr; + +#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 + + IntSize size(window->width * intScaleFactor, window->height * intScaleFactor); + + nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame(); + gfxRect r = nsLayoutUtils::RectToGfxRect(area, PresContext()->AppUnitsPerDevPixel()); + // to provide crisper and faster drawing. + r.Round(); + RefPtr<Layer> layer = + (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem)); + + if (aItem->GetType() == nsDisplayItem::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()->AsClientLayerManager() && + mInstanceOwner->UseAsyncRendering()) + { + RefPtr<ClientLayerManager> lm = aBuilder->GetWidgetLayerManager()->AsClientLayerManager(); + if (!mDidCompositeObserver || !mDidCompositeObserver->IsValid(lm)) { + mDidCompositeObserver = MakeUnique<PluginFrameDidCompositeObserver>(mInstanceOwner, lm); + } + lm->AddDidCompositeObserver(mDidCompositeObserver.get()); + } +#ifdef MOZ_WIDGET_ANDROID + } else if (aItem->GetType() == nsDisplayItem::TYPE_PLUGIN_VIDEO) { + nsDisplayPluginVideo* videoItem = reinterpret_cast<nsDisplayPluginVideo*>(aItem); + nsNPAPIPluginInstance::VideoInfo* videoInfo = videoItem->VideoInfo(); + + RefPtr<ImageContainer> container = mInstanceOwner->GetImageContainerForVideo(videoInfo); + if (!container) + return nullptr; + + if (!layer) { + // Initialize ImageLayer + layer = aManager->CreateImageLayer(); + if (!layer) + return nullptr; + } + + ImageLayer* imglayer = static_cast<ImageLayer*>(layer.get()); + imglayer->SetContainer(container); + + layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0); + + // Set the offset and size according to the video dimensions + r.MoveBy(videoInfo->mDimensions.TopLeft()); + size.width = videoInfo->mDimensions.width; + size.height = videoInfo->mDimensions.height; +#endif + } else { + NS_ASSERTION(aItem->GetType() == nsDisplayItem::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, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, const nsRect& aPluginRect) +{ +#if defined(MOZ_WIDGET_ANDROID) + if (mInstanceOwner) { + gfxRect frameGfxRect = + PresContext()->AppUnitsToGfxUnits(aPluginRect); + gfxRect dirtyGfxRect = + PresContext()->AppUnitsToGfxUnits(aDirtyRect); + + gfxContext* ctx = aRenderingContext.ThebesContext(); + + mInstanceOwner->Paint(ctx, frameGfxRect, dirtyGfxRect); + return; + } +#else +# 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 +#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) { + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(GetContent()); + if (fm && elem) + return fm->SetFocus(elem, 0); + } + else if (anEvent->mMessage == ePluginFocus) { + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + nsCOMPtr<nsIContent> content = GetContent(); + return fm->FocusPlugin(content); + } + } + + if (mInstanceOwner->SendNativeEvents() && + anEvent->IsNativeEventDelivererForPlugin()) { + *anEventStatus = mInstanceOwner->ProcessEvent(*anEvent); + // Due to plugin code reentering Gecko, this frame may be dead at this + // point. + return rv; + } + +#ifdef XP_WIN + rv = nsFrame::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 nsIPresShell::SetCapturingContext() (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) { + nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED); + } +#endif + + rv = nsFrame::HandleEvent(aPresContext, anEvent, anEventStatus); + + // We need to be careful from this point because the call to + // nsFrame::HandleEvent() might have killed us. + +#ifdef XP_MACOSX + if (anEvent->mMessage == eMouseUp) { + nsIPresShell::SetCapturingContent(nullptr, 0); + } +#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 +} + +nsresult +nsPluginFrame::GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance) +{ + *aPluginInstance = nullptr; + + if (!mInstanceOwner) { + return NS_OK; + } + + return mInstanceOwner->GetInstance(aPluginInstance); +} + +nsresult +nsPluginFrame::GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor) +{ + if (!mInstanceOwner) { + return NS_ERROR_FAILURE; + } + + RefPtr<nsNPAPIPluginInstance> inst; + mInstanceOwner->GetInstance(getter_AddRefs(inst)); + if (!inst) { + return NS_ERROR_FAILURE; + } + + bool useDOMCursor = static_cast<nsNPAPIPluginInstance*>(inst.get())->UsesDOMForCursor(); + if (!useDOMCursor) { + return NS_ERROR_FAILURE; + } + + return nsFrame::GetCursor(aPoint, aCursor); +} + +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) { + RefPtr<nsNPAPIPluginInstance> pi; + outFrame->GetPluginInstance(getter_AddRefs(pi)); // make sure we have a REAL plugin + if (pi) + return outFrame; + } + + outFrame = GetNextObjectFrame(aPresContext, child); + if (outFrame) + return outFrame; + } + + return nullptr; +} + +/*static*/ void +nsPluginFrame::BeginSwapDocShells(nsISupports* aSupports, void*) +{ + NS_PRECONDITION(aSupports, ""); + 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, void*) +{ + NS_PRECONDITION(aSupports, ""); + 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); + nsWeakFrame weakFrame(objectFrame); + objectFrame->CallSetWindow(); + if (!weakFrame.IsAlive()) { + return; + } + } + + if (objectFrame->mInstanceOwner) { + objectFrame->RegisterPluginForGeometryUpdates(); + } +} + +nsIFrame* +NS_NewObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) +{ + return new (aPresShell) nsPluginFrame(aContext); +} + +bool +nsPluginFrame::IsPaintedByGecko() const +{ +#ifdef XP_MACOSX + return true; +#else + return !mWidget; +#endif +} + +NS_IMPL_FRAMEARENA_HELPERS(nsPluginFrame) |