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