diff -r 000000000000 -r 6474c204b198 layout/generic/nsObjectFrame.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/generic/nsObjectFrame.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,2063 @@ +/* -*- 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 "nsObjectFrame.h" + +#include "gfx2DGlue.h" +#include "mozilla/BasicEvents.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 + +#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" + +// 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 "prlog.h" + +#ifdef XP_MACOSX +#include "gfxQuartzNativeDrawing.h" +#include "nsPluginUtilsOSX.h" +#include "mozilla/gfx/QuartzSupport.h" +#endif + +#ifdef MOZ_X11 +#include "mozilla/X11Util.h" +using mozilla::DefaultXDisplay; +#endif + +#ifdef XP_WIN +#include +#include +#endif + +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#include "GLContext.h" +#endif + +#ifdef CreateEvent // Thank you MS. +#undef CreateEvent +#endif + +#ifdef PR_LOGGING +static PRLogModuleInfo * +GetObjectFrameLog() +{ + static PRLogModuleInfo *sLog; + if (!sLog) + sLog = PR_NewLogModule("nsObjectFrame"); + return sLog; +} +#endif /* PR_LOGGING */ + +#if defined(XP_MACOSX) && !defined(__LP64__) + +// The header files QuickdrawAPI.h and QDOffscreen.h are missing on OS X 10.7 +// and up (though the QuickDraw APIs defined in them are still present) -- so +// we need to supply the relevant parts of their contents here. It's likely +// that Apple will eventually remove the APIs themselves (probably in OS X +// 10.8), so we need to make them weak imports, and test for their presence +// before using them. +extern "C" { + #if !defined(__QUICKDRAWAPI__) + extern void SetRect( + Rect * r, + short left, + short top, + short right, + short bottom) + __attribute__((weak_import)); + #endif /* __QUICKDRAWAPI__ */ + + #if !defined(__QDOFFSCREEN__) + extern QDErr NewGWorldFromPtr( + GWorldPtr * offscreenGWorld, + UInt32 PixelFormat, + const Rect * boundsRect, + CTabHandle cTable, /* can be nullptr */ + GDHandle aGDevice, /* can be nullptr */ + GWorldFlags flags, + Ptr newBuffer, + SInt32 rowBytes) + __attribute__((weak_import)); + extern void DisposeGWorld(GWorldPtr offscreenGWorld) + __attribute__((weak_import)); + #endif /* __QDOFFSCREEN__ */ +} + +#endif /* #if defined(XP_MACOSX) && !defined(__LP64__) */ + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::layers; + +class PluginBackgroundSink : public ReadbackSink { +public: + PluginBackgroundSink(nsObjectFrame* aFrame, uint64_t aStartSequenceNumber) + : mLastSequenceNumber(aStartSequenceNumber), mFrame(aFrame) {} + ~PluginBackgroundSink() + { + if (mFrame) { + mFrame->mBackgroundSink = nullptr; + } + } + + virtual void SetUnknown(uint64_t aSequenceNumber) + { + if (!AcceptUpdate(aSequenceNumber)) + return; + mFrame->mInstanceOwner->SetBackgroundUnknown(); + } + + virtual already_AddRefed + BeginUpdate(const nsIntRect& aRect, uint64_t aSequenceNumber) + { + if (!AcceptUpdate(aSequenceNumber)) + return nullptr; + return mFrame->mInstanceOwner->BeginUpdateBackground(aRect); + } + + virtual void EndUpdate(gfxContext* aContext, const nsIntRect& aRect) + { + return mFrame->mInstanceOwner->EndUpdateBackground(aContext, 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; + nsObjectFrame* mFrame; +}; + +nsObjectFrame::nsObjectFrame(nsStyleContext* aContext) + : nsObjectFrameSuper(aContext) + , mReflowCallbackPosted(false) +{ + PR_LOG(GetObjectFrameLog(), PR_LOG_DEBUG, + ("Created new nsObjectFrame %p\n", this)); +} + +nsObjectFrame::~nsObjectFrame() +{ + PR_LOG(GetObjectFrameLog(), PR_LOG_DEBUG, + ("nsObjectFrame %p deleted\n", this)); +} + +NS_QUERYFRAME_HEAD(nsObjectFrame) + NS_QUERYFRAME_ENTRY(nsObjectFrame) + NS_QUERYFRAME_ENTRY(nsIObjectFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsObjectFrameSuper) + +#ifdef ACCESSIBILITY +a11y::AccType +nsObjectFrame::AccessibleType() +{ + return a11y::ePluginType; +} + +#ifdef XP_WIN +NS_IMETHODIMP nsObjectFrame::GetPluginPort(HWND *aPort) +{ + *aPort = (HWND) mInstanceOwner->GetPluginPortFromWidget(); + return NS_OK; +} +#endif +#endif + +void +nsObjectFrame::Init(nsIContent* aContent, + nsIFrame* aParent, + nsIFrame* aPrevInFlow) +{ + PR_LOG(GetObjectFrameLog(), PR_LOG_DEBUG, + ("Initializing nsObjectFrame %p for content %p\n", this, aContent)); + + nsObjectFrameSuper::Init(aContent, aParent, aPrevInFlow); +} + +void +nsObjectFrame::DestroyFrom(nsIFrame* aDestructRoot) +{ + if (mReflowCallbackPosted) { + PresContext()->PresShell()->CancelReflowCallback(this); + } + + // Tell content owner of the instance to disconnect its frame. + nsCOMPtr objContent(do_QueryInterface(mContent)); + NS_ASSERTION(objContent, "Why not an object loading content?"); + + // The content might not have a reference to the instance owner any longer in + // the case of re-entry during instantiation or teardown, so make sure we're + // dissociated. + if (mInstanceOwner) { + mInstanceOwner->SetFrame(nullptr); + } + objContent->HasNewFrame(nullptr); + + if (mBackgroundSink) { + mBackgroundSink->Destroy(); + } + + nsObjectFrameSuper::DestroyFrom(aDestructRoot); +} + +/* virtual */ void +nsObjectFrame::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); + } + } + + nsObjectFrameSuper::DidSetStyleContext(aOldStyleContext); +} + +nsIAtom* +nsObjectFrame::GetType() const +{ + return nsGkAtoms::objectFrame; +} + +#ifdef DEBUG_FRAME_DUMP +nsresult +nsObjectFrame::GetFrameName(nsAString& aResult) const +{ + return MakeFrameName(NS_LITERAL_STRING("ObjectFrame"), aResult); +} +#endif + +nsresult +nsObjectFrame::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->Show(true); + mWidget->Enable(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. + nsAutoTArray 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); + + nsRefPtr dx = viewMan->GetDeviceContext(); + 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 Thebes layer 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()); + +#ifndef XP_MACOSX + RegisterPluginForGeometryUpdates(); +#endif + } + + 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 +nsObjectFrame::GetMinWidth(nsRenderingContext *aRenderingContext) +{ + nscoord result = 0; + + if (!IsHidden(false)) { + nsIAtom *atom = mContent->Tag(); + if (atom == nsGkAtoms::applet || atom == nsGkAtoms::embed) { + result = nsPresContext::CSSPixelsToAppUnits(EMBED_DEF_WIDTH); + } + } + + DISPLAY_MIN_WIDTH(this, result); + return result; +} + +/* virtual */ nscoord +nsObjectFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) +{ + return nsObjectFrame::GetMinWidth(aRenderingContext); +} + +void +nsObjectFrame::GetDesiredSize(nsPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aMetrics) +{ + // By default, we have no area + aMetrics.Width() = 0; + aMetrics.Height() = 0; + + if (IsHidden(false)) { + return; + } + + aMetrics.Width() = aReflowState.ComputedWidth(); + aMetrics.Height() = aReflowState.ComputedHeight(); + + // for EMBED and APPLET, default to 240x200 for compatibility + nsIAtom *atom = mContent->Tag(); + if (atom == nsGkAtoms::applet || atom == nsGkAtoms::embed) { + if (aMetrics.Width() == NS_UNCONSTRAINEDSIZE) { + aMetrics.Width() = clamped(nsPresContext::CSSPixelsToAppUnits(EMBED_DEF_WIDTH), + aReflowState.ComputedMinWidth(), + aReflowState.ComputedMaxWidth()); + } + if (aMetrics.Height() == NS_UNCONSTRAINEDSIZE) { + aMetrics.Height() = clamped(nsPresContext::CSSPixelsToAppUnits(EMBED_DEF_HEIGHT), + aReflowState.ComputedMinHeight(), + aReflowState.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() = + (aReflowState.ComputedMinWidth() != NS_UNCONSTRAINEDSIZE) ? + aReflowState.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() = + (aReflowState.ComputedMinHeight() != NS_UNCONSTRAINEDSIZE) ? + aReflowState.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. +} + +nsresult +nsObjectFrame::Reflow(nsPresContext* aPresContext, + nsHTMLReflowMetrics& aMetrics, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + DO_GLOBAL_REFLOW_COUNT("nsObjectFrame"); + DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); + + // Get our desired size + GetDesiredSize(aPresContext, aReflowState, 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 NS_OK; + } + + // if we are printing or print previewing, bail for now + if (aPresContext->Medium() == nsGkAtoms::print) { + aStatus = NS_FRAME_COMPLETE; + return NS_OK; + } + + nsRect r(0, 0, aMetrics.Width(), aMetrics.Height()); + r.Deflate(aReflowState.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, aReflowState, aMetrics); + return NS_OK; +} + +///////////// nsIReflowCallback /////////////// + +bool +nsObjectFrame::ReflowFinished() +{ + mReflowCallbackPosted = false; + CallSetWindow(); + return true; +} + +void +nsObjectFrame::ReflowCallbackCanceled() +{ + mReflowCallbackPosted = false; +} + +void +nsObjectFrame::FixupWindow(const nsSize& aSize) +{ + nsPresContext* presContext = PresContext(); + + if (!mInstanceOwner) + return; + + NPWindow *window; + mInstanceOwner->GetWindow(window); + + NS_ENSURE_TRUE_VOID(window); + +#ifdef XP_MACOSX + nsWeakFrame weakFrame(this); + mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintDisable); + if (!weakFrame.IsAlive()) { + return; + } +#endif + + bool windowless = (window->type == NPWindowTypeDrawable); + + nsIntPoint origin = GetWindowOriginInPixels(windowless); + + // 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; + + // on the Mac we need to set the clipRect to { 0, 0, 0, 0 } for now. This will keep + // us from drawing on screen until the widget is properly positioned, which will not + // happen until we have finished the reflow process. +#ifdef XP_MACOSX + window->clipRect.top = 0; + window->clipRect.left = 0; + window->clipRect.bottom = 0; + window->clipRect.right = 0; +#else + mInstanceOwner->UpdateWindowPositionAndClipRect(false); +#endif + + NotifyPluginReflowObservers(); +} + +nsresult +nsObjectFrame::CallSetWindow(bool aCheckIsHidden) +{ + NPWindow *win = nullptr; + + nsresult rv = NS_ERROR_FAILURE; + nsRefPtr 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; +#ifdef XP_MACOSX + nsWeakFrame weakFrame(this); + mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintDisable); + if (!weakFrame.IsAlive()) { + return NS_ERROR_NOT_AVAILABLE; + } +#endif + + if (aCheckIsHidden && IsHidden()) + return NS_ERROR_FAILURE; + + // refresh the plugin port as well + window->window = mInstanceOwner->GetPluginPortFromWidget(); + + // 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); + + // window must be in "display pixels" + double scaleFactor = 1.0; + if (NS_FAILED(mInstanceOwner->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; + + // Calling SetWindow might destroy this frame. We need to use the instance + // owner to clean up so hold a ref. + nsRefPtr instanceOwnerRef(mInstanceOwner); + + // 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 (mInstanceOwner->UseAsyncRendering()) { + rv = pi->AsyncSetWindow(window); + } + else { + rv = window->CallSetWindow(pi); + } + + instanceOwnerRef->ReleasePluginPort(window->window); + + return rv; +} + +void +nsObjectFrame::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 +nsObjectFrame::UnregisterPluginForGeometryUpdates() +{ + if (!mRootPresContextRegisteredWith) { + // Not registered... + return; + } + mRootPresContextRegisteredWith->UnregisterPluginForGeometryUpdates(mContent); + mRootPresContextRegisteredWith = nullptr; +} + +void +nsObjectFrame::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; + if (mInstanceOwner) { + return; + } + UnregisterPluginForGeometryUpdates(); + if (mWidget && mInnerView) { + mInnerView->DetachWidgetEventHandler(mWidget); + // Make sure the plugin is hidden in case an update of plugin geometry + // hasn't happened since this plugin became hidden. + nsIWidget* parent = mWidget->GetParent(); + if (parent) { + nsTArray configurations; + nsIWidget::Configuration* configuration = configurations.AppendElement(); + configuration->mChild = mWidget; + parent->ConfigureChildren(configurations); + + mWidget->Show(false); + mWidget->Enable(false); + mWidget->SetParent(nullptr); + } + } +} + +bool +nsObjectFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse) +{ + if (aTabIndex) + *aTabIndex = -1; + return nsObjectFrameSuper::IsFocusable(aTabIndex, aWithMouse); +} + +bool +nsObjectFrame::IsHidden(bool aCheckVisibilityStyle) const +{ + if (aCheckVisibilityStyle) { + if (!StyleVisibility()->IsVisibleOrCollapsed()) + return true; + } + + // only tags support the HIDDEN attribute + if (mContent->Tag() == nsGkAtoms::embed) { + // Yes, these are really the kooky ways that you could tell 4.x + // not to hide the once you'd put the 'hidden' attribute + // on the tag... + + // HIDDEN w/ no attributes gets translated as we are hidden for + // compatibility w/ 4.x and IE so we don't create a non-painting + // widget in layout. See bug 188959. + nsAutoString hidden; + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::hidden, hidden) && + (hidden.IsEmpty() || + (!hidden.LowerCaseEqualsLiteral("false") && + !hidden.LowerCaseEqualsLiteral("no") && + !hidden.LowerCaseEqualsLiteral("off")))) { + return true; + } + } + + return false; +} + +nsIntPoint nsObjectFrame::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(); + + return nsIntPoint(PresContext()->AppUnitsToDevPixels(origin.x), + PresContext()->AppUnitsToDevPixels(origin.y)); +} + +nsresult +nsObjectFrame::DidReflow(nsPresContext* aPresContext, + const nsHTMLReflowState* aReflowState, + 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 objContent(do_QueryInterface(mContent)); + NS_ASSERTION(objContent, "Why not an object loading content?"); + objContent->HasNewFrame(this); + } + + nsresult rv = nsObjectFrameSuper::DidReflow(aPresContext, aReflowState, 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 rv; + + if (HasView()) { + nsView* view = GetView(); + nsViewManager* vm = view->GetViewManager(); + if (vm) + vm->SetViewVisibility(view, IsHidden() ? nsViewVisibility_kHide : nsViewVisibility_kShow); + } + + return rv; +} + +/* static */ void +nsObjectFrame::PaintPrintPlugin(nsIFrame* aFrame, nsRenderingContext* aCtx, + const nsRect& aDirtyRect, nsPoint aPt) +{ + nsPoint pt = aPt + aFrame->GetContentRectRelativeToSelf().TopLeft(); + nsRenderingContext::AutoPushTranslation translate(aCtx, pt); + // FIXME - Bug 385435: Doesn't aDirtyRect need translating too? + static_cast(aFrame)->PrintPlugin(*aCtx, aDirtyRect); +} + +class nsDisplayPluginReadback : public nsDisplayItem { +public: + nsDisplayPluginReadback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) + : nsDisplayItem(aBuilder, aFrame) + { + MOZ_COUNT_CTOR(nsDisplayPluginReadback); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayPluginReadback() { + MOZ_COUNT_DTOR(nsDisplayPluginReadback); + } +#endif + + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, + bool* aSnap) MOZ_OVERRIDE; + virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion) MOZ_OVERRIDE; + + NS_DISPLAY_DECL_NAME("PluginReadback", TYPE_PLUGIN_READBACK) + + virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE + { + return static_cast(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters); + } + + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) MOZ_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); +} + +bool +nsDisplayPluginReadback::ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion) +{ + if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion)) + return false; + + nsRect expand; + bool snap; + expand.IntersectRect(aAllowVisibleRegionExpansion, GetBounds(aBuilder, &snap)); + // *Add* our bounds to the visible region so that stuff underneath us is + // likely to be made visible, so we can use it for a background! This is + // a bit crazy since we normally only subtract from the visible region. + aVisibleRegion->Or(*aVisibleRegion, expand); + return true; +} + +#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) MOZ_OVERRIDE; + virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion) MOZ_OVERRIDE; + + NS_DISPLAY_DECL_NAME("PluginVideo", TYPE_PLUGIN_VIDEO) + + virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE + { + return static_cast(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters); + } + + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) MOZ_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); +} + +bool +nsDisplayPluginVideo::ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion) +{ + return nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion); +} + +#endif + +nsRect +nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) +{ + *aSnap = true; + return GetDisplayItemBounds(aBuilder, this, mFrame); +} + +void +nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + nsObjectFrame* f = static_cast(mFrame); + bool snap; + f->PaintPlugin(aBuilder, *aCtx, mVisibleRect, GetBounds(aBuilder, &snap)); +} + +bool +nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion) +{ + if (aBuilder->IsForPluginGeometry()) { + nsObjectFrame* f = static_cast(mFrame); + if (!aBuilder->IsInTransform() || f->IsPaintedByGecko()) { + // Since transforms induce reference frames, we don't need to worry + // about this method fluffing out due to non-rectilinear transforms. + nsRect rAncestor = nsLayoutUtils::TransformFrameRectToAncestor(f, + f->GetContentRectRelativeToSelf(), ReferenceFrame()); + nscoord appUnitsPerDevPixel = + ReferenceFrame()->PresContext()->AppUnitsPerDevPixel(); + f->mNextConfigurationBounds = rAncestor.ToNearestPixels(appUnitsPerDevPixel); + + nsRegion visibleRegion; + visibleRegion.And(*aVisibleRegion, GetClippedBounds(aBuilder)); + // Make visibleRegion relative to f + visibleRegion.MoveBy(-ToReferenceFrame()); + + f->mNextConfigurationClipRegion.Clear(); + nsRegionRectIterator iter(visibleRegion); + for (const nsRect* r = iter.Next(); r; r = iter.Next()) { + nsRect rAncestor = + nsLayoutUtils::TransformFrameRectToAncestor(f, *r, ReferenceFrame()); + nsIntRect rPixels = 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, + aAllowVisibleRegionExpansion); +} + +nsRegion +nsDisplayPlugin::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + bool* aSnap) +{ + *aSnap = false; + nsRegion result; + nsObjectFrame* f = static_cast(mFrame); + if (!aBuilder->IsForPluginGeometry()) { + nsIWidget* widget = f->GetWidget(); + if (widget) { + // Be conservative and treat plugins with widgets as not opaque, + // because that's simple and we might need the content under the widget + // if the widget is unexpectedly clipped away. (As can happen when + // chrome content over a plugin forces us to clip out the plugin for + // security reasons.) + // We shouldn't be repainting the content under plugins much anyway + // since there generally shouldn't be anything to invalidate or paint + // in ThebesLayers 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 +nsObjectFrame::PluginEventNotifier::Run() { + nsCOMPtr obsSvc = + mozilla::services::GetObserverService(); + obsSvc->NotifyObservers(nullptr, "plugin-changed-event", mEventType.get()); + return NS_OK; +} + +void +nsObjectFrame::NotifyPluginReflowObservers() +{ + nsContentUtils::AddScriptRunner(new PluginEventNotifier(NS_LITERAL_STRING("reflow"))); +} + +void +nsObjectFrame::DidSetWidgetGeometry() +{ +#if defined(XP_MACOSX) + if (mInstanceOwner) { + 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 +nsObjectFrame::IsOpaque() const +{ +#if defined(XP_MACOSX) + // ??? + return false; +#elif defined(MOZ_WIDGET_ANDROID) + // We don't know, so just assume transparent + return false; +#else + return !IsTransparentMode(); +#endif +} + +bool +nsObjectFrame::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; + nsRefPtr pi; + rv = mInstanceOwner->GetInstance(getter_AddRefs(pi)); + if (NS_FAILED(rv) || !pi) + return false; + + bool transparent = false; + pi->IsTransparent(&transparent); + return transparent; +#endif +} + +void +nsObjectFrame::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("nsObjectFrame"); + +#ifndef XP_MACOSX + if (mWidget && aBuilder->IsInTransform()) { + // Windowed plugins should not be rendered inside a transform. + return; + } +#endif + + if (aBuilder->IsForPainting() && mInstanceOwner && 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, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT); + + // 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 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 +nsObjectFrame::PrintPlugin(nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + nsCOMPtr obj(do_QueryInterface(mContent)); + if (!obj) + return; + + nsIFrame* frame = nullptr; + obj->GetPrintFrame(&frame); + if (!frame) + return; + + nsPresContext* presContext = PresContext(); + // make sure this is REALLY an nsIObjectFrame + // we may need to go through the children to get it + nsIObjectFrame* objectFrame = do_QueryFrame(frame); + if (!objectFrame) + objectFrame = GetNextObjectFrame(presContext,frame); + if (!objectFrame) + return; + + // finally we can get our plugin instance + nsRefPtr 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_MACOSX) && !defined(__LP64__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // Don't use this code if any of the QuickDraw APIs it currently requires + // are missing (as they probably will be on OS X 10.8 and up). + if (!&::SetRect || !&::NewGWorldFromPtr || !&::DisposeGWorld) { + NS_WARNING("Cannot print plugin -- required QuickDraw APIs are missing!"); + return; + } + + 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(); + if (!ctx) + return; + gfxContextAutoSaveRestore save(ctx); + + ctx->NewPath(); + + gfxRect rect(window.x, window.y, window.width, window.height); + + ctx->Rectangle(rect); + ctx->Clip(); + + gfxQuartzNativeDrawing nativeDraw(ctx, rect); + CGContextRef cgContext = nativeDraw.BeginNativeDrawing(); + if (!cgContext) { + nativeDraw.EndNativeDrawing(); + return; + } + + window.clipRect.right = window.width; + window.clipRect.bottom = window.height; + window.type = NPWindowTypeDrawable; + + ::Rect gwBounds; + ::SetRect(&gwBounds, 0, 0, window.width, window.height); + + nsTArray buffer(window.width * window.height * 4); + CGColorSpaceRef cspace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + if (!cspace) { + nativeDraw.EndNativeDrawing(); + return; + } + CGContextRef cgBuffer = + ::CGBitmapContextCreate(buffer.Elements(), + window.width, window.height, 8, window.width * 4, + cspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedFirst); + ::CGColorSpaceRelease(cspace); + if (!cgBuffer) { + nativeDraw.EndNativeDrawing(); + return; + } + GWorldPtr gWorld; + if (::NewGWorldFromPtr(&gWorld, k32ARGBPixelFormat, &gwBounds, + nullptr, nullptr, 0, + buffer.Elements(), window.width * 4) != noErr) { + ::CGContextRelease(cgBuffer); + nativeDraw.EndNativeDrawing(); + return; + } + + window.clipRect.right = window.width; + window.clipRect.bottom = window.height; + window.type = NPWindowTypeDrawable; + // Setting nsPluginPrint/NPPrint.print.embedPrint.window.window to + // &GWorldPtr and nsPluginPrint/NPPrint.print.embedPrint.platformPrint to + // GWorldPtr isn't any kind of standard (it's not documented anywhere). + // But that's what WebKit does. And it's what the Flash plugin (apparently + // the only NPAPI plugin on OS X to support printing) seems to expect. So + // we do the same. The Flash plugin uses the CoreGraphics drawing mode. + // But a GWorldPtr should be usable in either CoreGraphics or QuickDraw + // drawing mode. See bug 191046. + window.window = &gWorld; + npprint.print.embedPrint.platformPrint = gWorld; + npprint.print.embedPrint.window = window; + pi->Print(&npprint); + + ::CGContextTranslateCTM(cgContext, 0.0f, float(window.height)); + ::CGContextScaleCTM(cgContext, 1.0f, -1.0f); + CGImageRef image = ::CGBitmapContextCreateImage(cgBuffer); + if (!image) { + ::CGContextRestoreGState(cgContext); + ::CGContextRelease(cgBuffer); + ::DisposeGWorld(gWorld); + nativeDraw.EndNativeDrawing(); + return; + } + ::CGContextDrawImage(cgContext, + ::CGRectMake(0, 0, window.width, window.height), + image); + ::CGImageRelease(image); + ::CGContextRelease(cgBuffer); + + ::DisposeGWorld(gWorld); + + nativeDraw.EndNativeDrawing(); +#pragma clang diagnostic warning "-Wdeprecated-declarations" +#elif defined(XP_UNIX) + + /* XXX this just flat-out doesn't work in a thebes world -- + * RenderEPS is a no-op. So don't bother to do any work here. + */ + (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 +nsObjectFrame::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 +nsObjectFrame::GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager) +{ + if (!mInstanceOwner) + return LAYER_NONE; + +#ifdef MOZ_WIDGET_ANDROID + // We always want a layer on Honeycomb and later + if (AndroidBridge::Bridge()->GetAPIVersion() >= 11) + return LAYER_ACTIVE; +#endif + + if (!mInstanceOwner->UseAsyncRendering()) { + return LAYER_NONE; + } + + return LAYER_ACTIVE; +} + +already_AddRefed +nsObjectFrame::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; + + // window is in "display pixels", but size needs to be in device pixels + double scaleFactor = 1.0; + if (NS_FAILED(mInstanceOwner->GetContentsScaleFactor(&scaleFactor))) { + scaleFactor = 1.0; + } + int intScaleFactor = ceil(scaleFactor); + 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(); + nsRefPtr layer = + (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem)); + + if (aItem->GetType() == nsDisplayItem::TYPE_PLUGIN) { + // Create image + nsRefPtr container = mInstanceOwner->GetImageContainer(); + if (!container) { + // This can occur if our instance is gone. + return nullptr; + } + + if (!layer) { + mInstanceOwner->NotifyPaintWaiter(aBuilder); + // Initialize ImageLayer + layer = aManager->CreateImageLayer(); + if (!layer) + return nullptr; + } + + NS_ASSERTION(layer->GetType() == Layer::TYPE_IMAGE, "Bad layer type"); + ImageLayer* imglayer = static_cast(layer.get()); +#ifdef XP_MACOSX + if (!mInstanceOwner->UseAsyncRendering()) { + mInstanceOwner->DoCocoaEventDrawRect(r, nullptr); + } +#endif + + imglayer->SetScaleToSize(size, ScaleMode::STRETCH); + imglayer->SetContainer(container); + GraphicsFilter filter = + nsLayoutUtils::GetGraphicsFilterForFrame(this); +#ifdef MOZ_GFX_OPTIMIZE_MOBILE + if (!aManager->IsCompositingCheap()) { + // Pixman just horrible with bilinear filter scaling + filter = GraphicsFilter::FILTER_NEAREST; + } +#endif + imglayer->SetFilter(filter); + + layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0); +#ifdef MOZ_WIDGET_ANDROID + } else if (aItem->GetType() == nsDisplayItem::TYPE_PLUGIN_VIDEO) { + nsDisplayPluginVideo* videoItem = reinterpret_cast(aItem); + nsNPAPIPluginInstance::VideoInfo* videoInfo = videoItem->VideoInfo(); + + nsRefPtr container = mInstanceOwner->GetImageContainerForVideo(videoInfo); + if (!container) + return nullptr; + + if (!layer) { + // Initialize ImageLayer + layer = aManager->CreateImageLayer(); + if (!layer) + return nullptr; + } + + ImageLayer* imglayer = static_cast(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"); + NS_ABORT_IF_FALSE(!IsOpaque(), "Opaque plugins don't use backgrounds"); + + if (!layer) { + layer = aManager->CreateReadbackLayer(); + if (!layer) + return nullptr; + } + NS_ASSERTION(layer->GetType() == Layer::TYPE_READBACK, "Bad layer type"); + + ReadbackLayer* readback = static_cast(layer.get()); + if (readback->GetSize() != ThebesIntSize(size)) { + // This will destroy any old background sink and notify us that the + // background is now unknown + readback->SetSink(nullptr); + readback->SetSize(ThebesIntSize(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 + Matrix transform; + gfxPoint p = r.TopLeft() + aContainerParameters.mOffset; + transform.Translate(p.x, p.y); + + layer->SetBaseTransform(Matrix4x4::From2D(transform)); + layer->SetVisibleRegion(ThebesIntRect(IntRect(IntPoint(0, 0), size))); + return layer.forget(); +} + +void +nsObjectFrame::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; + } +#endif + + // Screen painting code +#if defined(XP_MACOSX) + // delegate all painting to the plugin instance. + if (mInstanceOwner) { + if (mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreGraphics || + mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreAnimation || + mInstanceOwner->GetDrawingModel() == + NPDrawingModelInvalidatingCoreAnimation) { + int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); + // Clip to the content area where the plugin should be drawn. If + // we don't do this, the plugin can draw outside its bounds. + nsIntRect contentPixels = aPluginRect.ToNearestPixels(appUnitsPerDevPixel); + nsIntRect dirtyPixels = aDirtyRect.ToOutsidePixels(appUnitsPerDevPixel); + nsIntRect clipPixels; + clipPixels.IntersectRect(contentPixels, dirtyPixels); + + // Don't invoke the drawing code if the clip is empty. + if (clipPixels.IsEmpty()) + return; + + gfxRect nativeClipRect(clipPixels.x, clipPixels.y, + clipPixels.width, clipPixels.height); + gfxContext* ctx = aRenderingContext.ThebesContext(); + + gfxContextAutoSaveRestore save(ctx); + ctx->NewPath(); + ctx->Rectangle(nativeClipRect); + ctx->Clip(); + gfxPoint offset(contentPixels.x, contentPixels.y); + ctx->Translate(offset); + + gfxQuartzNativeDrawing nativeDrawing(ctx, nativeClipRect - offset); + + CGContextRef cgContext = nativeDrawing.BeginNativeDrawing(); + if (!cgContext) { + NS_WARNING("null CGContextRef during PaintPlugin"); + return; + } + + nsRefPtr inst; + GetPluginInstance(getter_AddRefs(inst)); + if (!inst) { + NS_WARNING("null plugin instance during PaintPlugin"); + nativeDrawing.EndNativeDrawing(); + return; + } + NPWindow* window; + mInstanceOwner->GetWindow(window); + if (!window) { + NS_WARNING("null plugin window during PaintPlugin"); + nativeDrawing.EndNativeDrawing(); + return; + } + NP_CGContext* cgPluginPortCopy = + static_cast(mInstanceOwner->GetPluginPortCopy()); + if (!cgPluginPortCopy) { + NS_WARNING("null plugin port copy during PaintPlugin"); + nativeDrawing.EndNativeDrawing(); + return; + } + + mInstanceOwner->BeginCGPaint(); + if (mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreAnimation || + mInstanceOwner->GetDrawingModel() == + NPDrawingModelInvalidatingCoreAnimation) { + // CoreAnimation is updated, render the layer and perform a readback. + mInstanceOwner->RenderCoreAnimation(cgContext, window->width, window->height); + } else { + mInstanceOwner->Paint(nativeClipRect - offset, cgContext); + } + mInstanceOwner->EndCGPaint(); + + nativeDrawing.EndNativeDrawing(); + } else { + // FIXME - Bug 385435: Doesn't aDirtyRect need translating too? + nsRenderingContext::AutoPushTranslation + translate(&aRenderingContext, aPluginRect.TopLeft()); + + // this rect is used only in the CoreGraphics drawing model + gfxRect tmpRect(0, 0, 0, 0); + mInstanceOwner->Paint(tmpRect, nullptr); + } + } +#elif defined(MOZ_X11) + if (mInstanceOwner) { + NPWindow *window; + mInstanceOwner->GetWindow(window); + if (window->type == NPWindowTypeDrawable) { + gfxRect frameGfxRect = + PresContext()->AppUnitsToGfxUnits(aPluginRect); + gfxRect dirtyGfxRect = + PresContext()->AppUnitsToGfxUnits(aDirtyRect); + gfxContext* ctx = aRenderingContext.ThebesContext(); + + mInstanceOwner->Paint(ctx, frameGfxRect, dirtyGfxRect); + } + } +#elif defined(XP_WIN) + nsRefPtr inst; + GetPluginInstance(getter_AddRefs(inst)); + if (inst) { + gfxRect frameGfxRect = + PresContext()->AppUnitsToGfxUnits(aPluginRect); + gfxRect dirtyGfxRect = + PresContext()->AppUnitsToGfxUnits(aDirtyRect); + gfxContext *ctx = aRenderingContext.ThebesContext(); + gfxMatrix currentMatrix = ctx->CurrentMatrix(); + + if (ctx->UserToDevicePixelSnapped(frameGfxRect, false)) { + dirtyGfxRect = ctx->UserToDevice(dirtyGfxRect); + ctx->IdentityMatrix(); + } + dirtyGfxRect.RoundOut(); + + // Look if it's windowless + NPWindow *window; + mInstanceOwner->GetWindow(window); + + if (window->type == NPWindowTypeDrawable) { + // the offset of the DC + nsPoint origin; + + gfxWindowsNativeDrawing nativeDraw(ctx, frameGfxRect); + if (nativeDraw.IsDoublePass()) { + // OOP plugin specific: let the shim know before we paint if we are doing a + // double pass render. If this plugin isn't oop, the register window message + // will be ignored. + NPEvent pluginEvent; + pluginEvent.event = plugins::DoublePassRenderingEvent(); + pluginEvent.wParam = 0; + pluginEvent.lParam = 0; + if (pluginEvent.event) + inst->HandleEvent(&pluginEvent, nullptr); + } + do { + HDC hdc = nativeDraw.BeginNativeDrawing(); + if (!hdc) + return; + + RECT dest; + nativeDraw.TransformToNativeRect(frameGfxRect, dest); + RECT dirty; + nativeDraw.TransformToNativeRect(dirtyGfxRect, dirty); + + window->window = hdc; + window->x = dest.left; + window->y = dest.top; + window->clipRect.left = 0; + window->clipRect.top = 0; + // if we're painting, we're visible. + window->clipRect.right = window->width; + window->clipRect.bottom = window->height; + + // Windowless plugins on windows need a special event to update their location, + // see bug 135737. + // + // bug 271442: note, the rectangle we send is now purely the bounds of the plugin + // relative to the window it is contained in, which is useful for the plugin to + // correctly translate mouse coordinates. + // + // this does not mesh with the comments for bug 135737 which imply that the rectangle + // must be clipped in some way to prevent the plugin attempting to paint over areas + // it shouldn't. + // + // since the two uses of the rectangle are mutually exclusive in some cases, and + // since I don't see any incorrect painting (at least with Flash and ViewPoint - + // the originator of bug 135737), it seems that windowless plugins are not relying + // on information here for clipping their drawing, and we can safely use this message + // to tell the plugin exactly where it is in all cases. + + nsIntPoint origin = GetWindowOriginInPixels(true); + nsIntRect winlessRect = nsIntRect(origin, nsIntSize(window->width, window->height)); + + if (!mWindowlessRect.IsEqualEdges(winlessRect)) { + mWindowlessRect = winlessRect; + + WINDOWPOS winpos; + memset(&winpos, 0, sizeof(winpos)); + winpos.x = mWindowlessRect.x; + winpos.y = mWindowlessRect.y; + winpos.cx = mWindowlessRect.width; + winpos.cy = mWindowlessRect.height; + + // finally, update the plugin by sending it a WM_WINDOWPOSCHANGED event + NPEvent pluginEvent; + pluginEvent.event = WM_WINDOWPOSCHANGED; + pluginEvent.wParam = 0; + pluginEvent.lParam = (LPARAM)&winpos; + inst->HandleEvent(&pluginEvent, nullptr); + } + + inst->SetWindow(window); + + mInstanceOwner->Paint(dirty, hdc); + nativeDraw.EndNativeDrawing(); + } while (nativeDraw.ShouldRenderAgain()); + nativeDraw.PaintToContext(); + } + + ctx->SetMatrix(currentMatrix); + } +#endif +} + +nsresult +nsObjectFrame::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->message == NS_PLUGIN_ACTIVATE) { + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + nsCOMPtr elem = do_QueryInterface(GetContent()); + if (fm && elem) + return fm->SetFocus(elem, 0); + } + else if (anEvent->message == NS_PLUGIN_FOCUS) { + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) + return fm->FocusPlugin(GetContent()); + } + +#ifdef XP_MACOSX + if (anEvent->message == NS_PLUGIN_RESOLUTION_CHANGED) { + double scaleFactor = 1.0; + mInstanceOwner->GetContentsScaleFactor(&scaleFactor); + mInstanceOwner->ContentsScaleFactorChanged(scaleFactor); + return NS_OK; + } +#endif + + 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 = nsObjectFrameSuper::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->message == NS_MOUSE_ENTER || + anEvent->message == NS_WHEEL_WHEEL) && + 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->message == NS_MOUSE_BUTTON_DOWN) { + nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED); + } +#endif + + rv = nsObjectFrameSuper::HandleEvent(aPresContext, anEvent, anEventStatus); + + // We need to be careful from this point because the call to + // nsObjectFrameSuper::HandleEvent() might have killed us. + +#ifdef XP_MACOSX + if (anEvent->message == NS_MOUSE_BUTTON_UP) { + nsIPresShell::SetCapturingContent(nullptr, 0); + } +#endif + + return rv; +} + +nsresult +nsObjectFrame::GetPluginInstance(nsNPAPIPluginInstance** aPluginInstance) +{ + *aPluginInstance = nullptr; + + if (!mInstanceOwner) { + return NS_OK; + } + + return mInstanceOwner->GetInstance(aPluginInstance); +} + +nsresult +nsObjectFrame::GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor) +{ + if (!mInstanceOwner) { + return NS_ERROR_FAILURE; + } + + nsRefPtr inst; + mInstanceOwner->GetInstance(getter_AddRefs(inst)); + if (!inst) { + return NS_ERROR_FAILURE; + } + + bool useDOMCursor = static_cast(inst.get())->UsesDOMForCursor(); + if (!useDOMCursor) { + return NS_ERROR_FAILURE; + } + + return nsObjectFrameSuper::GetCursor(aPoint, aCursor); +} + +void +nsObjectFrame::SetIsDocumentActive(bool aIsActive) +{ +#ifndef XP_MACOSX + if (mInstanceOwner) { + mInstanceOwner->UpdateDocumentActiveState(aIsActive); + } +#endif +} + +// static +nsIObjectFrame * +nsObjectFrame::GetNextObjectFrame(nsPresContext* aPresContext, nsIFrame* aRoot) +{ + nsIFrame* child = aRoot->GetFirstPrincipalChild(); + + while (child) { + nsIObjectFrame* outFrame = do_QueryFrame(child); + if (outFrame) { + nsRefPtr 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; + child = child->GetNextSibling(); + } + + return nullptr; +} + +/*static*/ void +nsObjectFrame::BeginSwapDocShells(nsIContent* aContent, void*) +{ + NS_PRECONDITION(aContent, ""); + + // This function is called from a document content enumerator so we need + // to filter out the nsObjectFrames and ignore the rest. + nsIObjectFrame* obj = do_QueryFrame(aContent->GetPrimaryFrame()); + if (!obj) + return; + + nsObjectFrame* objectFrame = static_cast(obj); + NS_ASSERTION(!objectFrame->mWidget || objectFrame->mWidget->GetParent(), + "Plugin windows must not be toplevel"); + objectFrame->UnregisterPluginForGeometryUpdates(); +} + +/*static*/ void +nsObjectFrame::EndSwapDocShells(nsIContent* aContent, void*) +{ + NS_PRECONDITION(aContent, ""); + + // This function is called from a document content enumerator so we need + // to filter out the nsObjectFrames and ignore the rest. + nsIObjectFrame* obj = do_QueryFrame(aContent->GetPrimaryFrame()); + if (!obj) + return; + + nsObjectFrame* objectFrame = static_cast(obj); + nsRootPresContext* rootPC = objectFrame->PresContext()->GetRootPresContext(); + NS_ASSERTION(rootPC, "unable to register the plugin frame"); + nsIWidget* widget = objectFrame->mWidget; + if (widget) { + // Reparent the widget. + nsIWidget* parent = + rootPC->PresShell()->GetRootFrame()->GetNearestWidget(); + widget->SetParent(parent); + nsWeakFrame weakFrame(objectFrame); + objectFrame->CallSetWindow(); + if (!weakFrame.IsAlive()) { + return; + } + } + +#ifdef XP_MACOSX + if (objectFrame->mWidget) { + objectFrame->RegisterPluginForGeometryUpdates(); + } +#else + objectFrame->RegisterPluginForGeometryUpdates(); +#endif +} + +nsIFrame* +NS_NewObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) +{ + return new (aPresShell) nsObjectFrame(aContext); +} + +bool +nsObjectFrame::IsPaintedByGecko() const +{ +#ifdef XP_MACOSX + return true; +#else + return !mWidget; +#endif +} + +NS_IMPL_FRAMEARENA_HELPERS(nsObjectFrame)