diff -r 000000000000 -r 6474c204b198 dom/plugins/base/nsPluginInstanceOwner.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,3195 @@ +/* -*- 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/. */ + +#ifdef MOZ_X11 +#include +#include "gfxXlibSurface.h" +/* X headers suck */ +enum { XKeyPress = KeyPress }; +#include "mozilla/X11Util.h" +using mozilla::DefaultXDisplay; +#endif + +#include "nsPluginInstanceOwner.h" +#include "nsIRunnable.h" +#include "nsContentUtils.h" +#include "nsRect.h" +#include "nsSize.h" +#include "nsDisplayList.h" +#include "ImageLayers.h" +#include "SharedTextureImage.h" +#include "nsObjectFrame.h" +#include "nsIPluginDocument.h" +#include "nsIStringStream.h" +#include "nsNetUtil.h" +#include "mozilla/Preferences.h" +#include "nsILinkHandler.h" +#include "nsIDocShellTreeItem.h" +#include "nsIWebBrowserChrome.h" +#include "nsLayoutUtils.h" +#include "nsIPluginWidget.h" +#include "nsViewManager.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDOMHTMLObjectElement.h" +#include "nsIAppShell.h" +#include "nsIDOMHTMLAppletElement.h" +#include "nsIObjectLoadingContent.h" +#include "nsAttrName.h" +#include "nsIFocusManager.h" +#include "nsFocusManager.h" +#include "nsIDOMDragEvent.h" +#include "nsIScrollableFrame.h" +#include "nsIDocShell.h" +#include "ImageContainer.h" +#include "nsIDOMHTMLCollection.h" +#include "GLContext.h" +#include "GLSharedHandleHelpers.h" +#include "nsIContentInlines.h" +#include "mozilla/MiscEvents.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/TextEvents.h" + +#include "nsContentCID.h" +#include "nsWidgetsCID.h" +static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); +static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + +#ifdef XP_WIN +#include +#include +#endif + +#ifdef XP_MACOSX +#include +#include "nsPluginUtilsOSX.h" +#endif + +#if (MOZ_WIDGET_GTK == 2) +#include +#include +#include +#endif + +#ifdef MOZ_WIDGET_ANDROID +#include "ANPBase.h" +#include "AndroidBridge.h" +#include "nsWindow.h" + +static nsPluginInstanceOwner* sFullScreenInstance = nullptr; + +using namespace mozilla::dom; + +#include +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#endif + +using namespace mozilla; +using namespace mozilla::layers; + +// special class for handeling DOM context menu events because for +// some reason it starves other mouse events if implemented on the +// same class +class nsPluginDOMContextMenuListener : public nsIDOMEventListener +{ +public: + nsPluginDOMContextMenuListener(nsIContent* aContent); + virtual ~nsPluginDOMContextMenuListener(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMEVENTLISTENER + + void Destroy(nsIContent* aContent); + + nsEventStatus ProcessEvent(const WidgetGUIEvent& anEvent) + { + return nsEventStatus_eConsumeNoDefault; + } +}; + +class AsyncPaintWaitEvent : public nsRunnable +{ +public: + AsyncPaintWaitEvent(nsIContent* aContent, bool aFinished) : + mContent(aContent), mFinished(aFinished) + { + } + + NS_IMETHOD Run() + { + nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent, + mFinished ? NS_LITERAL_STRING("MozPaintWaitFinished") : NS_LITERAL_STRING("MozPaintWait"), + true, true); + return NS_OK; + } + +private: + nsCOMPtr mContent; + bool mFinished; +}; + +void +nsPluginInstanceOwner::NotifyPaintWaiter(nsDisplayListBuilder* aBuilder) +{ + // This is notification for reftests about async plugin paint start + if (!mWaitingForPaint && !IsUpToDate() && aBuilder->ShouldSyncDecodeImages()) { + nsCOMPtr event = new AsyncPaintWaitEvent(mContent, false); + // Run this event as soon as it's safe to do so, since listeners need to + // receive it immediately + mWaitingForPaint = nsContentUtils::AddScriptRunner(event); + } +} + +already_AddRefed +nsPluginInstanceOwner::GetImageContainer() +{ + if (!mInstance) + return nullptr; + + nsRefPtr container; + +#if MOZ_WIDGET_ANDROID + // Right now we only draw with Gecko layers on Honeycomb and higher. See Paint() + // for what we do on other versions. + if (AndroidBridge::Bridge()->GetAPIVersion() < 11) + return nullptr; + + LayoutDeviceRect r = GetPluginRect(); + + // NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render + // into, set y-flip flags, etc, so we do this at the beginning. + gfxSize resolution = mObjectFrame->PresContext()->PresShell()->GetCumulativeResolution(); + ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution.width, resolution.height)).Size(); + mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height)); + + container = LayerManager::CreateImageContainer(); + + nsRefPtr img = container->CreateImage(ImageFormat::SHARED_TEXTURE); + + SharedTextureImage::Data data; + data.mSize = gfx::IntSize(r.width, r.height); + data.mHandle = mInstance->CreateSharedHandle(); + data.mShareType = mozilla::gl::SharedTextureShareType::SameProcess; + data.mInverted = mInstance->Inverted(); + + SharedTextureImage* pluginImage = static_cast(img.get()); + pluginImage->SetData(data); + + container->SetCurrentImageInTransaction(img); + + return container.forget(); +#endif + + mInstance->GetImageContainer(getter_AddRefs(container)); + return container.forget(); +} + +void +nsPluginInstanceOwner::SetBackgroundUnknown() +{ + if (mInstance) { + mInstance->SetBackgroundUnknown(); + } +} + +already_AddRefed +nsPluginInstanceOwner::BeginUpdateBackground(const nsIntRect& aRect) +{ + nsIntRect rect = aRect; + nsRefPtr ctx; + if (mInstance && + NS_SUCCEEDED(mInstance->BeginUpdateBackground(&rect, getter_AddRefs(ctx)))) { + return ctx.forget(); + } + return nullptr; +} + +void +nsPluginInstanceOwner::EndUpdateBackground(gfxContext* aContext, + const nsIntRect& aRect) +{ + nsIntRect rect = aRect; + if (mInstance) { + mInstance->EndUpdateBackground(aContext, &rect); + } +} + +bool +nsPluginInstanceOwner::UseAsyncRendering() +{ +#ifdef XP_MACOSX + if (mUseAsyncRendering) { + return true; + } +#endif + + bool isOOP; + bool result = (mInstance && + NS_SUCCEEDED(mInstance->GetIsOOP(&isOOP)) && isOOP +#ifndef XP_MACOSX + && (!mPluginWindow || + mPluginWindow->type == NPWindowTypeDrawable) +#endif + ); + +#ifdef XP_MACOSX + if (result) { + mUseAsyncRendering = true; + } +#endif + + return result; +} + +nsIntSize +nsPluginInstanceOwner::GetCurrentImageSize() +{ + nsIntSize size(0,0); + if (mInstance) { + mInstance->GetImageSize(&size); + } + return size; +} + +nsPluginInstanceOwner::nsPluginInstanceOwner() +{ + // create nsPluginNativeWindow object, it is derived from NPWindow + // struct and allows to manipulate native window procedure + nsCOMPtr pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID); + mPluginHost = static_cast(pluginHostCOM.get()); + if (mPluginHost) + mPluginHost->NewPluginNativeWindow(&mPluginWindow); + else + mPluginWindow = nullptr; + + mObjectFrame = nullptr; + mContent = nullptr; + mWidgetCreationComplete = false; +#ifdef XP_MACOSX + memset(&mCGPluginPortCopy, 0, sizeof(NP_CGContext)); + mInCGPaintLevel = 0; + mSentInitialTopLevelWindowEvent = false; + mColorProfile = nullptr; + mPluginPortChanged = false; +#endif + mContentFocused = false; + mWidgetVisible = true; + mPluginWindowVisible = false; + mPluginDocumentActiveState = true; + mNumCachedAttrs = 0; + mNumCachedParams = 0; + mCachedAttrParamNames = nullptr; + mCachedAttrParamValues = nullptr; + mLastMouseDownButtonType = -1; + +#ifdef XP_MACOSX +#ifndef NP_NO_CARBON + // We don't support Carbon, but it is still the default model for i386 NPAPI. + mEventModel = NPEventModelCarbon; +#else + mEventModel = NPEventModelCocoa; +#endif + mUseAsyncRendering = false; +#endif + + mWaitingForPaint = false; + +#ifdef MOZ_WIDGET_ANDROID + mFullScreen = false; + mJavaView = nullptr; +#endif +} + +nsPluginInstanceOwner::~nsPluginInstanceOwner() +{ + int32_t cnt; + + if (mWaitingForPaint) { + // We don't care when the event is dispatched as long as it's "soon", + // since whoever needs it will be waiting for it. + nsCOMPtr event = new AsyncPaintWaitEvent(mContent, true); + NS_DispatchToMainThread(event); + } + + mObjectFrame = nullptr; + + for (cnt = 0; cnt < (mNumCachedAttrs + 1 + mNumCachedParams); cnt++) { + if (mCachedAttrParamNames && mCachedAttrParamNames[cnt]) { + NS_Free(mCachedAttrParamNames[cnt]); + mCachedAttrParamNames[cnt] = nullptr; + } + + if (mCachedAttrParamValues && mCachedAttrParamValues[cnt]) { + NS_Free(mCachedAttrParamValues[cnt]); + mCachedAttrParamValues[cnt] = nullptr; + } + } + + if (mCachedAttrParamNames) { + NS_Free(mCachedAttrParamNames); + mCachedAttrParamNames = nullptr; + } + + if (mCachedAttrParamValues) { + NS_Free(mCachedAttrParamValues); + mCachedAttrParamValues = nullptr; + } + + PLUG_DeletePluginNativeWindow(mPluginWindow); + mPluginWindow = nullptr; + +#ifdef MOZ_WIDGET_ANDROID + RemovePluginView(); +#endif + + if (mInstance) { + mInstance->SetOwner(nullptr); + } +} + +NS_IMPL_ISUPPORTS(nsPluginInstanceOwner, + nsIPluginInstanceOwner, + nsIDOMEventListener, + nsIPrivacyTransitionObserver, + nsISupportsWeakReference) + +nsresult +nsPluginInstanceOwner::SetInstance(nsNPAPIPluginInstance *aInstance) +{ + NS_ASSERTION(!mInstance || !aInstance, "mInstance should only be set or unset!"); + + // If we're going to null out mInstance after use, be sure to call + // mInstance->SetOwner(nullptr) here, since it now won't be called + // from our destructor. This fixes bug 613376. + if (mInstance && !aInstance) { + mInstance->SetOwner(nullptr); + +#ifdef MOZ_WIDGET_ANDROID + RemovePluginView(); +#endif + } + + mInstance = aInstance; + + nsCOMPtr doc; + GetDocument(getter_AddRefs(doc)); + if (doc) { + nsCOMPtr domWindow = doc->GetWindow(); + if (domWindow) { + nsCOMPtr docShell = domWindow->GetDocShell(); + if (docShell) + docShell->AddWeakPrivacyTransitionObserver(this); + } + } + + return NS_OK; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetWindow(NPWindow *&aWindow) +{ + NS_ASSERTION(mPluginWindow, "the plugin window object being returned is null"); + aWindow = mPluginWindow; + return NS_OK; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetMode(int32_t *aMode) +{ + nsCOMPtr doc; + nsresult rv = GetDocument(getter_AddRefs(doc)); + nsCOMPtr pDoc (do_QueryInterface(doc)); + + if (pDoc) { + *aMode = NP_FULL; + } else { + *aMode = NP_EMBED; + } + + return rv; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetAttributes(uint16_t& n, + const char*const*& names, + const char*const*& values) +{ + nsresult rv = EnsureCachedAttrParamArrays(); + NS_ENSURE_SUCCESS(rv, rv); + + n = mNumCachedAttrs; + names = (const char **)mCachedAttrParamNames; + values = (const char **)mCachedAttrParamValues; + + return rv; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetAttribute(const char* name, const char* *result) +{ + NS_ENSURE_ARG_POINTER(name); + NS_ENSURE_ARG_POINTER(result); + + nsresult rv = EnsureCachedAttrParamArrays(); + NS_ENSURE_SUCCESS(rv, rv); + + *result = nullptr; + + for (int i = 0; i < mNumCachedAttrs; i++) { + if (0 == PL_strcasecmp(mCachedAttrParamNames[i], name)) { + *result = mCachedAttrParamValues[i]; + return NS_OK; + } + } + + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetDOMElement(nsIDOMElement* *result) +{ + return CallQueryInterface(mContent, result); +} + +nsresult nsPluginInstanceOwner::GetInstance(nsNPAPIPluginInstance **aInstance) +{ + NS_ENSURE_ARG_POINTER(aInstance); + + *aInstance = mInstance; + NS_IF_ADDREF(*aInstance); + return NS_OK; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL, + const char *aTarget, + nsIInputStream *aPostStream, + void *aHeadersData, + uint32_t aHeadersDataLen) +{ + NS_ENSURE_TRUE(mContent, NS_ERROR_NULL_POINTER); + + if (mContent->IsEditable()) { + return NS_OK; + } + + nsIDocument *doc = mContent->GetCurrentDoc(); + if (!doc) { + return NS_ERROR_FAILURE; + } + + nsIPresShell *presShell = doc->GetShell(); + if (!presShell) { + return NS_ERROR_FAILURE; + } + + nsPresContext *presContext = presShell->GetPresContext(); + if (!presContext) { + return NS_ERROR_FAILURE; + } + + // the container of the pres context will give us the link handler + nsCOMPtr container = presContext->GetContainerWeak(); + NS_ENSURE_TRUE(container,NS_ERROR_FAILURE); + nsCOMPtr lh = do_QueryInterface(container); + NS_ENSURE_TRUE(lh, NS_ERROR_FAILURE); + + nsAutoString unitarget; + unitarget.AssignASCII(aTarget); // XXX could this be nonascii? + + nsCOMPtr baseURI = GetBaseURI(); + + // Create an absolute URL + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, baseURI); + + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + nsCOMPtr headersDataStream; + if (aPostStream && aHeadersData) { + if (!aHeadersDataLen) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr sis = do_CreateInstance("@mozilla.org/io/string-input-stream;1"); + if (!sis) + return NS_ERROR_OUT_OF_MEMORY; + + rv = sis->SetData((char *)aHeadersData, aHeadersDataLen); + NS_ENSURE_SUCCESS(rv, rv); + headersDataStream = do_QueryInterface(sis); + } + + int32_t blockPopups = + Preferences::GetInt("privacy.popups.disable_from_plugins"); + nsAutoPopupStatePusher popupStatePusher((PopupControlState)blockPopups); + + rv = lh->OnLinkClick(mContent, uri, unitarget.get(), NullString(), + aPostStream, headersDataStream, true); + + return rv; +} + +NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char *aStatusMsg) +{ + nsresult rv = NS_ERROR_FAILURE; + + rv = this->ShowStatus(NS_ConvertUTF8toUTF16(aStatusMsg).get()); + + return rv; +} + +NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char16_t *aStatusMsg) +{ + nsresult rv = NS_ERROR_FAILURE; + + if (!mObjectFrame) { + return rv; + } + nsCOMPtr docShellItem = mObjectFrame->PresContext()->GetDocShell(); + if (NS_FAILED(rv) || !docShellItem) { + return rv; + } + + nsCOMPtr treeOwner; + rv = docShellItem->GetTreeOwner(getter_AddRefs(treeOwner)); + if (NS_FAILED(rv) || !treeOwner) { + return rv; + } + + nsCOMPtr browserChrome(do_GetInterface(treeOwner, &rv)); + if (NS_FAILED(rv) || !browserChrome) { + return rv; + } + rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT, + aStatusMsg); + + return rv; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(nsIDocument* *aDocument) +{ + if (!aDocument) + return NS_ERROR_NULL_POINTER; + + // XXX sXBL/XBL2 issue: current doc or owner doc? + // But keep in mind bug 322414 comment 33 + NS_IF_ADDREF(*aDocument = mContent->OwnerDoc()); + return NS_OK; +} + +NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect *invalidRect) +{ + // If our object frame has gone away, we won't be able to determine + // up-to-date-ness, so just fire off the event. + if (mWaitingForPaint && (!mObjectFrame || IsUpToDate())) { + // We don't care when the event is dispatched as long as it's "soon", + // since whoever needs it will be waiting for it. + nsCOMPtr event = new AsyncPaintWaitEvent(mContent, true); + NS_DispatchToMainThread(event); + mWaitingForPaint = false; + } + + if (!mObjectFrame || !invalidRect || !mWidgetVisible) + return NS_ERROR_FAILURE; + +#if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) + // Each time an asynchronously-drawing plugin sends a new surface to display, + // the image in the ImageContainer is updated and InvalidateRect is called. + // There are different side effects for (sync) Android plugins. + nsRefPtr container; + mInstance->GetImageContainer(getter_AddRefs(container)); +#endif + +#ifndef XP_MACOSX + // Windowed plugins should not be calling NPN_InvalidateRect, but + // Silverlight does and expects it to "work" + if (mWidget) { + mWidget->Invalidate(nsIntRect(invalidRect->left, invalidRect->top, + invalidRect->right - invalidRect->left, + invalidRect->bottom - invalidRect->top)); + return NS_OK; + } +#endif + nsIntRect rect(invalidRect->left, + invalidRect->top, + invalidRect->right - invalidRect->left, + invalidRect->bottom - invalidRect->top); + // invalidRect is in "display pixels". In non-HiDPI modes "display pixels" + // are device pixels. But in HiDPI modes each display pixel corresponds + // to more than one device pixel. + double scaleFactor = 1.0; + GetContentsScaleFactor(&scaleFactor); + rect.ScaleRoundOut(scaleFactor); + mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN, &rect); + return NS_OK; +} + +NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRegion(NPRegion invalidRegion) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsPluginInstanceOwner::RedrawPlugin() +{ + if (mObjectFrame) { + mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN); + } + return NS_OK; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value) +{ + if (!mObjectFrame) { + NS_WARNING("plugin owner has no owner in getting doc's window handle"); + return NS_ERROR_FAILURE; + } + +#if defined(XP_WIN) + void** pvalue = (void**)value; + nsViewManager* vm = mObjectFrame->PresContext()->GetPresShell()->GetViewManager(); + if (!vm) + return NS_ERROR_FAILURE; +#if defined(XP_WIN) + // This property is provided to allow a "windowless" plugin to determine the window it is drawing + // in, so it can translate mouse coordinates it receives directly from the operating system + // to coordinates relative to itself. + + // The original code (outside this #if) returns the document's window, which is OK if the window the "windowless" plugin + // is drawing into has the same origin as the document's window, but this is not the case for "windowless" plugins inside of scrolling DIVs etc + + // To make sure "windowless" plugins always get the right origin for translating mouse coordinates, this code + // determines the window handle of the mozilla window containing the "windowless" plugin. + + // Given that this HWND may not be that of the document's window, there is a slight risk + // of confusing a plugin that is using this HWND for illicit purposes, but since the documentation + // does not suggest this HWND IS that of the document window, rather that of the window + // the plugin is drawn in, this seems like a safe fix. + + // we only attempt to get the nearest window if this really is a "windowless" plugin so as not + // to change any behaviour for the much more common windowed plugins, + // though why this method would even be being called for a windowed plugin escapes me. + if (mPluginWindow && mPluginWindow->type == NPWindowTypeDrawable) { + // it turns out that flash also uses this window for determining focus, and is currently + // unable to show a caret correctly if we return the enclosing window. Therefore for + // now we only return the enclosing window when there is an actual offset which + // would otherwise cause coordinates to be offset incorrectly. (i.e. + // if the enclosing window if offset from the document window) + // + // fixing both the caret and ability to interact issues for a windowless control in a non document aligned windw + // does not seem to be possible without a change to the flash plugin + + nsIWidget* win = mObjectFrame->GetNearestWidget(); + if (win) { + nsView *view = nsView::GetViewFor(win); + NS_ASSERTION(view, "No view for widget"); + nsPoint offset = view->GetOffsetTo(nullptr); + + if (offset.x || offset.y) { + // in the case the two windows are offset from eachother, we do go ahead and return the correct enclosing window + // so that mouse co-ordinates are not messed up. + *pvalue = (void*)win->GetNativeData(NS_NATIVE_WINDOW); + if (*pvalue) + return NS_OK; + } + } + } +#endif + // simply return the topmost document window + nsCOMPtr widget; + vm->GetRootWidget(getter_AddRefs(widget)); + if (widget) { + *pvalue = (void*)widget->GetNativeData(NS_NATIVE_WINDOW); + } else { + NS_ASSERTION(widget, "couldn't get doc's widget in getting doc's window handle"); + } + + return NS_OK; +#elif (defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)) && defined(MOZ_X11) + // X11 window managers want the toplevel window for WM_TRANSIENT_FOR. + nsIWidget* win = mObjectFrame->GetNearestWidget(); + if (!win) + return NS_ERROR_FAILURE; + *static_cast(value) = (long unsigned int)win->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW); + return NS_OK; +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} + +NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel) +{ +#ifdef XP_MACOSX + mEventModel = static_cast(eventModel); + return NS_OK; +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} + +NPError nsPluginInstanceOwner::ShowNativeContextMenu(NPMenu* menu, void* event) +{ + if (!menu || !event) + return NPERR_GENERIC_ERROR; + +#ifdef XP_MACOSX + if (GetEventModel() != NPEventModelCocoa) + return NPERR_INCOMPATIBLE_VERSION_ERROR; + + return NS_NPAPI_ShowCocoaContextMenu(static_cast(menu), mWidget, + static_cast(event)); +#else + return NPERR_INCOMPATIBLE_VERSION_ERROR; +#endif +} + +NPBool nsPluginInstanceOwner::ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, + double *destX, double *destY, NPCoordinateSpace destSpace) +{ +#ifdef XP_MACOSX + if (!mWidget) + return false; + + return NS_NPAPI_ConvertPointCocoa(mWidget->GetNativeData(NS_NATIVE_WIDGET), + sourceX, sourceY, sourceSpace, destX, destY, destSpace); +#else + // we should implement this for all platforms + return false; +#endif +} + +NPError nsPluginInstanceOwner::InitAsyncSurface(NPSize *size, NPImageFormat format, + void *initData, NPAsyncSurface *surface) +{ + return NPERR_INCOMPATIBLE_VERSION_ERROR; +} + +NPError nsPluginInstanceOwner::FinalizeAsyncSurface(NPAsyncSurface *) +{ + return NPERR_INCOMPATIBLE_VERSION_ERROR; +} + +void nsPluginInstanceOwner::SetCurrentAsyncSurface(NPAsyncSurface *, NPRect*) +{ +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetTagType(nsPluginTagType *result) +{ + NS_ENSURE_ARG_POINTER(result); + + *result = nsPluginTagType_Unknown; + + nsIAtom *atom = mContent->Tag(); + + if (atom == nsGkAtoms::applet) + *result = nsPluginTagType_Applet; + else if (atom == nsGkAtoms::embed) + *result = nsPluginTagType_Embed; + else if (atom == nsGkAtoms::object) + *result = nsPluginTagType_Object; + + return NS_OK; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetParameters(uint16_t& n, const char*const*& names, const char*const*& values) +{ + nsresult rv = EnsureCachedAttrParamArrays(); + NS_ENSURE_SUCCESS(rv, rv); + + n = mNumCachedParams; + if (n) { + names = (const char **)(mCachedAttrParamNames + mNumCachedAttrs + 1); + values = (const char **)(mCachedAttrParamValues + mNumCachedAttrs + 1); + } else + names = values = nullptr; + + return rv; +} + +NS_IMETHODIMP nsPluginInstanceOwner::GetParameter(const char* name, const char* *result) +{ + NS_ENSURE_ARG_POINTER(name); + NS_ENSURE_ARG_POINTER(result); + + nsresult rv = EnsureCachedAttrParamArrays(); + NS_ENSURE_SUCCESS(rv, rv); + + *result = nullptr; + + for (int i = mNumCachedAttrs + 1; i < (mNumCachedParams + 1 + mNumCachedAttrs); i++) { + if (0 == PL_strcasecmp(mCachedAttrParamNames[i], name)) { + *result = mCachedAttrParamValues[i]; + return NS_OK; + } + } + + return NS_ERROR_FAILURE; +} + + +// Cache the attributes and/or parameters of our tag into a single set +// of arrays to be compatible with Netscape 4.x. The attributes go first, +// followed by a PARAM/null and then any PARAM tags. Also, hold the +// cached array around for the duration of the life of the instance +// because Netscape 4.x did. See bug 111008. +nsresult nsPluginInstanceOwner::EnsureCachedAttrParamArrays() +{ + if (mCachedAttrParamValues) + return NS_OK; + + NS_PRECONDITION(((mNumCachedAttrs + mNumCachedParams) == 0) && + !mCachedAttrParamNames, + "re-cache of attrs/params not implemented! use the DOM " + "node directy instead"); + + // Convert to a 16-bit count. Subtract 3 in case we add an extra + // "src", "wmode", or "codebase" entry below. + uint32_t cattrs = mContent->GetAttrCount(); + if (cattrs < 0x0000FFFC) { + mNumCachedAttrs = static_cast(cattrs); + } else { + mNumCachedAttrs = 0xFFFC; + } + + // Check if we are java for special codebase handling + const char* mime = nullptr; + bool isJava = NS_SUCCEEDED(mInstance->GetMIMEType(&mime)) && mime && + nsPluginHost::IsJavaMIMEType(mime); + + // now, we need to find all the PARAM tags that are children of us + // however, be careful not to include any PARAMs that don't have us + // as a direct parent. For nested object (or applet) tags, be sure + // to only round up the param tags that coorespond with THIS + // instance. And also, weed out any bogus tags that may get in the + // way, see bug 39609. Then, with any param tag that meet our + // qualification, temporarly cache them in an nsCOMArray until + // we can figure out what size to make our fixed char* array. + nsCOMArray ourParams; + + // Get all dependent PARAM tags, even if they are not direct children. + nsCOMPtr mydomElement = do_QueryInterface(mContent); + NS_ENSURE_TRUE(mydomElement, NS_ERROR_NO_INTERFACE); + + // Making DOM method calls can cause our frame to go away. + nsCOMPtr kungFuDeathGrip(this); + + nsCOMPtr allParams; + NS_NAMED_LITERAL_STRING(xhtml_ns, "http://www.w3.org/1999/xhtml"); + mydomElement->GetElementsByTagNameNS(xhtml_ns, NS_LITERAL_STRING("param"), + getter_AddRefs(allParams)); + if (allParams) { + uint32_t numAllParams; + allParams->GetLength(&numAllParams); + for (uint32_t i = 0; i < numAllParams; i++) { + nsCOMPtr pnode; + allParams->Item(i, getter_AddRefs(pnode)); + nsCOMPtr domelement = do_QueryInterface(pnode); + if (domelement) { + // Ignore params without a name attribute. + nsAutoString name; + domelement->GetAttribute(NS_LITERAL_STRING("name"), name); + if (!name.IsEmpty()) { + // Find the first object or applet parent. + nsCOMPtr parent; + nsCOMPtr domobject; + nsCOMPtr domapplet; + pnode->GetParentNode(getter_AddRefs(parent)); + while (!(domobject || domapplet) && parent) { + domobject = do_QueryInterface(parent); + domapplet = do_QueryInterface(parent); + nsCOMPtr temp; + parent->GetParentNode(getter_AddRefs(temp)); + parent = temp; + } + if (domapplet || domobject) { + if (domapplet) { + parent = do_QueryInterface(domapplet); + } + else { + parent = do_QueryInterface(domobject); + } + nsCOMPtr mydomNode = do_QueryInterface(mydomElement); + if (parent == mydomNode) { + ourParams.AppendObject(domelement); + } + } + } + } + } + } + + // Convert to a 16-bit count. + uint32_t cparams = ourParams.Count(); + if (cparams < 0x0000FFFF) { + mNumCachedParams = static_cast(cparams); + } else { + mNumCachedParams = 0xFFFF; + } + + uint16_t numRealAttrs = mNumCachedAttrs; + + // Some plugins were never written to understand the "data" attribute of the OBJECT tag. + // Real and WMP will not play unless they find a "src" attribute, see bug 152334. + // Nav 4.x would simply replace the "data" with "src". Because some plugins correctly + // look for "data", lets instead copy the "data" attribute and add another entry + // to the bottom of the array if there isn't already a "src" specified. + nsAutoString data; + if (mContent->Tag() == nsGkAtoms::object && + !mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::src) && + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, data) && + !data.IsEmpty()) { + mNumCachedAttrs++; + } + + // "plugins.force.wmode" forces us to send a specific "wmode" parameter, + // used by flash to select a rendering mode. Common values include + // "opaque", "transparent", "windowed", "direct" + nsCString wmodeType; + nsAdoptingCString wmodePref = Preferences::GetCString("plugins.force.wmode"); + if (!wmodePref.IsEmpty()) { + mNumCachedAttrs++; + wmodeType = wmodePref; + } +#if defined(XP_WIN) || defined(XP_LINUX) + // Bug 923745 - Until we support windowed mode plugins in content processes, + // force flash to use a windowless rendering mode. This hack should go away + // when bug 923746 lands. (OS X plugins always use some native widgets, so + // unfortunately this does not help there) + else if (XRE_GetProcessType() == GeckoProcessType_Content) { + mNumCachedAttrs++; + wmodeType.AssignLiteral("transparent"); + } +#endif + + // (Bug 738396) java has quirks in its codebase parsing, pass the + // absolute codebase URI as content sees it. + bool addCodebase = false; + nsAutoCString codebaseStr; + if (isJava) { + nsCOMPtr objlc = do_QueryInterface(mContent); + NS_ENSURE_TRUE(objlc, NS_ERROR_UNEXPECTED); + nsCOMPtr codebaseURI; + nsresult rv = objlc->GetBaseURI(getter_AddRefs(codebaseURI)); + NS_ENSURE_SUCCESS(rv, rv); + codebaseURI->GetSpec(codebaseStr); + if (!mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::codebase)) { + mNumCachedAttrs++; + addCodebase = true; + } + } + + mCachedAttrParamNames = (char**)NS_Alloc(sizeof(char*) * (mNumCachedAttrs + 1 + mNumCachedParams)); + NS_ENSURE_TRUE(mCachedAttrParamNames, NS_ERROR_OUT_OF_MEMORY); + mCachedAttrParamValues = (char**)NS_Alloc(sizeof(char*) * (mNumCachedAttrs + 1 + mNumCachedParams)); + NS_ENSURE_TRUE(mCachedAttrParamValues, NS_ERROR_OUT_OF_MEMORY); + + // Some plugins (eg Flash, see bug 234675.) are actually sensitive to the + // attribute order. So we want to make sure we give the plugin the + // attributes in the order they came in in the source, to be compatible with + // other browsers. Now in HTML, the storage order is the reverse of the + // source order, while in XML and XHTML it's the same as the source order + // (see the AddAttributes functions in the HTML and XML content sinks). + int32_t start, end, increment; + if (mContent->IsHTML() && + mContent->IsInHTMLDocument()) { + // HTML. Walk attributes in reverse order. + start = numRealAttrs - 1; + end = -1; + increment = -1; + } else { + // XHTML or XML. Walk attributes in forward order. + start = 0; + end = numRealAttrs; + increment = 1; + } + + // Set to the next slot to fill in name and value cache arrays. + uint32_t nextAttrParamIndex = 0; + + // Whether or not we force the wmode below while traversing + // the name/value pairs. + bool wmodeSet = false; + + // Add attribute name/value pairs. + for (int32_t index = start; index != end; index += increment) { + const nsAttrName* attrName = mContent->GetAttrNameAt(index); + nsIAtom* atom = attrName->LocalName(); + nsAutoString value; + mContent->GetAttr(attrName->NamespaceID(), atom, value); + nsAutoString name; + atom->ToString(name); + + FixUpURLS(name, value); + + mCachedAttrParamNames [nextAttrParamIndex] = ToNewUTF8String(name); + if (!wmodeType.IsEmpty() && + 0 == PL_strcasecmp(mCachedAttrParamNames[nextAttrParamIndex], "wmode")) { + mCachedAttrParamValues[nextAttrParamIndex] = ToNewUTF8String(NS_ConvertUTF8toUTF16(wmodeType)); + + if (!wmodeSet) { + // We allocated space to add a wmode attr, but we don't need it now. + mNumCachedAttrs--; + wmodeSet = true; + } + } else if (isJava && 0 == PL_strcasecmp(mCachedAttrParamNames[nextAttrParamIndex], "codebase")) { + mCachedAttrParamValues[nextAttrParamIndex] = ToNewUTF8String(NS_ConvertUTF8toUTF16(codebaseStr)); + } else { + mCachedAttrParamValues[nextAttrParamIndex] = ToNewUTF8String(value); + } + nextAttrParamIndex++; + } + + // Potentially add CODEBASE attribute + if (addCodebase) { + mCachedAttrParamNames [nextAttrParamIndex] = ToNewUTF8String(NS_LITERAL_STRING("codebase")); + mCachedAttrParamValues[nextAttrParamIndex] = ToNewUTF8String(NS_ConvertUTF8toUTF16(codebaseStr)); + nextAttrParamIndex++; + } + + // Potentially add WMODE attribute. + if (!wmodeType.IsEmpty() && !wmodeSet) { + mCachedAttrParamNames [nextAttrParamIndex] = ToNewUTF8String(NS_LITERAL_STRING("wmode")); + mCachedAttrParamValues[nextAttrParamIndex] = ToNewUTF8String(NS_ConvertUTF8toUTF16(wmodeType)); + nextAttrParamIndex++; + } + + // Potentially add SRC attribute. + if (!data.IsEmpty()) { + mCachedAttrParamNames [nextAttrParamIndex] = ToNewUTF8String(NS_LITERAL_STRING("SRC")); + mCachedAttrParamValues[nextAttrParamIndex] = ToNewUTF8String(data); + nextAttrParamIndex++; + } + + // Add PARAM and null separator. + mCachedAttrParamNames [nextAttrParamIndex] = ToNewUTF8String(NS_LITERAL_STRING("PARAM")); +#ifdef MOZ_WIDGET_ANDROID + // Flash expects an empty string on android + mCachedAttrParamValues[nextAttrParamIndex] = ToNewUTF8String(NS_LITERAL_STRING("")); +#else + mCachedAttrParamValues[nextAttrParamIndex] = nullptr; +#endif + nextAttrParamIndex++; + + // Add PARAM name/value pairs. + + // We may decrement mNumCachedParams below + uint16_t totalParams = mNumCachedParams; + for (uint16_t i = 0; i < totalParams; i++) { + nsIDOMElement* param = ourParams.ObjectAt(i); + if (!param) { + continue; + } + + nsAutoString name; + nsAutoString value; + param->GetAttribute(NS_LITERAL_STRING("name"), name); // check for empty done above + param->GetAttribute(NS_LITERAL_STRING("value"), value); + + FixUpURLS(name, value); + + /* + * According to the HTML 4.01 spec, at + * http://www.w3.org/TR/html4/types.html#type-cdata + * ''User agents may ignore leading and trailing + * white space in CDATA attribute values (e.g., " + * myval " may be interpreted as "myval"). Authors + * should not declare attribute values with + * leading or trailing white space.'' + * However, do not trim consecutive spaces as in bug 122119 + */ + name.Trim(" \n\r\t\b", true, true, false); + value.Trim(" \n\r\t\b", true, true, false); + if (isJava && name.EqualsIgnoreCase("codebase")) { + // We inserted normalized codebase above, don't include other versions in + // params + mNumCachedParams--; + continue; + } + mCachedAttrParamNames [nextAttrParamIndex] = ToNewUTF8String(name); + mCachedAttrParamValues[nextAttrParamIndex] = ToNewUTF8String(value); + nextAttrParamIndex++; + } + + return NS_OK; +} + +#ifdef XP_MACOSX + +static void InitializeNPCocoaEvent(NPCocoaEvent* event) +{ + memset(event, 0, sizeof(NPCocoaEvent)); +} + +NPDrawingModel nsPluginInstanceOwner::GetDrawingModel() +{ +#ifndef NP_NO_QUICKDRAW + // We don't support the Quickdraw drawing model any more but it's still + // the default model for i386 per NPAPI. + NPDrawingModel drawingModel = NPDrawingModelQuickDraw; +#else + NPDrawingModel drawingModel = NPDrawingModelCoreGraphics; +#endif + + if (!mInstance) + return drawingModel; + + mInstance->GetDrawingModel((int32_t*)&drawingModel); + return drawingModel; +} + +bool nsPluginInstanceOwner::IsRemoteDrawingCoreAnimation() +{ + if (!mInstance) + return false; + + bool coreAnimation; + if (!NS_SUCCEEDED(mInstance->IsRemoteDrawingCoreAnimation(&coreAnimation))) + return false; + + return coreAnimation; +} + +nsresult nsPluginInstanceOwner::ContentsScaleFactorChanged(double aContentsScaleFactor) +{ + if (!mInstance) { + return NS_ERROR_NULL_POINTER; + } + return mInstance->ContentsScaleFactorChanged(aContentsScaleFactor); +} + +NPEventModel nsPluginInstanceOwner::GetEventModel() +{ + return mEventModel; +} + +#define DEFAULT_REFRESH_RATE 20 // 50 FPS + +nsCOMPtr *nsPluginInstanceOwner::sCATimer = nullptr; +nsTArray *nsPluginInstanceOwner::sCARefreshListeners = nullptr; + +void nsPluginInstanceOwner::CARefresh(nsITimer *aTimer, void *aClosure) { + if (!sCARefreshListeners) { + return; + } + for (size_t i = 0; i < sCARefreshListeners->Length(); i++) { + nsPluginInstanceOwner* instanceOwner = (*sCARefreshListeners)[i]; + NPWindow *window; + instanceOwner->GetWindow(window); + if (!window) { + continue; + } + NPRect r; + r.left = 0; + r.top = 0; + r.right = window->width; + r.bottom = window->height; + instanceOwner->InvalidateRect(&r); + } +} + +void nsPluginInstanceOwner::AddToCARefreshTimer() { + if (!mInstance) { + return; + } + + // Flash invokes InvalidateRect for us. + const char* mime = nullptr; + if (NS_SUCCEEDED(mInstance->GetMIMEType(&mime)) && mime) { + if (strcmp(mime, "application/x-shockwave-flash") == 0) { + return; + } + } + + if (!sCARefreshListeners) { + sCARefreshListeners = new nsTArray(); + if (!sCARefreshListeners) { + return; + } + } + + if (sCARefreshListeners->Contains(this)) { + return; + } + + sCARefreshListeners->AppendElement(this); + + if (!sCATimer) { + sCATimer = new nsCOMPtr(); + if (!sCATimer) { + return; + } + } + + if (sCARefreshListeners->Length() == 1) { + *sCATimer = do_CreateInstance("@mozilla.org/timer;1"); + (*sCATimer)->InitWithFuncCallback(CARefresh, nullptr, + DEFAULT_REFRESH_RATE, nsITimer::TYPE_REPEATING_SLACK); + } +} + +void nsPluginInstanceOwner::RemoveFromCARefreshTimer() { + if (!sCARefreshListeners || sCARefreshListeners->Contains(this) == false) { + return; + } + + sCARefreshListeners->RemoveElement(this); + + if (sCARefreshListeners->Length() == 0) { + if (sCATimer) { + (*sCATimer)->Cancel(); + delete sCATimer; + sCATimer = nullptr; + } + delete sCARefreshListeners; + sCARefreshListeners = nullptr; + } +} + +void nsPluginInstanceOwner::RenderCoreAnimation(CGContextRef aCGContext, + int aWidth, int aHeight) +{ + if (aWidth == 0 || aHeight == 0) + return; + + if (!mCARenderer) { + mCARenderer = new nsCARenderer(); + } + + // aWidth and aHeight are in "display pixels". In non-HiDPI modes + // "display pixels" are device pixels. But in HiDPI modes each + // display pixel corresponds to more than one device pixel. + double scaleFactor = 1.0; + GetContentsScaleFactor(&scaleFactor); + + if (!mIOSurface || + (mIOSurface->GetWidth() != (size_t)aWidth || + mIOSurface->GetHeight() != (size_t)aHeight || + mIOSurface->GetContentsScaleFactor() != scaleFactor)) { + mIOSurface = nullptr; + + // If the renderer is backed by an IOSurface, resize it as required. + mIOSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight, scaleFactor); + if (mIOSurface) { + RefPtr attachSurface = MacIOSurface::LookupSurface( + mIOSurface->GetIOSurfaceID(), + scaleFactor); + if (attachSurface) { + mCARenderer->AttachIOSurface(attachSurface); + } else { + NS_ERROR("IOSurface attachment failed"); + mIOSurface = nullptr; + } + } + } + + if (!mColorProfile) { + mColorProfile = CreateSystemColorSpace(); + } + + if (mCARenderer->isInit() == false) { + void *caLayer = nullptr; + nsresult rv = mInstance->GetValueFromPlugin(NPPVpluginCoreAnimationLayer, &caLayer); + if (NS_FAILED(rv) || !caLayer) { + return; + } + + // We don't run Flash in-process so we can unconditionally disallow + // the offliner renderer. + mCARenderer->SetupRenderer(caLayer, aWidth, aHeight, scaleFactor, + DISALLOW_OFFLINE_RENDERER); + + // Setting up the CALayer requires resetting the painting otherwise we + // get garbage for the first few frames. + FixUpPluginWindow(ePluginPaintDisable); + FixUpPluginWindow(ePluginPaintEnable); + } + + CGImageRef caImage = nullptr; + nsresult rt = mCARenderer->Render(aWidth, aHeight, scaleFactor, &caImage); + if (rt == NS_OK && mIOSurface && mColorProfile) { + nsCARenderer::DrawSurfaceToCGContext(aCGContext, mIOSurface, mColorProfile, + 0, 0, aWidth, aHeight); + } else if (rt == NS_OK && caImage != nullptr) { + // Significant speed up by resetting the scaling + ::CGContextSetInterpolationQuality(aCGContext, kCGInterpolationNone ); + ::CGContextTranslateCTM(aCGContext, 0, (double) aHeight * scaleFactor); + ::CGContextScaleCTM(aCGContext, scaleFactor, -scaleFactor); + + ::CGContextDrawImage(aCGContext, CGRectMake(0,0,aWidth,aHeight), caImage); + } else { + NS_NOTREACHED("nsCARenderer::Render failure"); + } +} + +void* nsPluginInstanceOwner::GetPluginPortCopy() +{ + if (GetDrawingModel() == NPDrawingModelCoreGraphics || + GetDrawingModel() == NPDrawingModelCoreAnimation || + GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation) + return &mCGPluginPortCopy; + return nullptr; +} + +// Currently (on OS X in Cocoa widgets) any changes made as a result of +// calling GetPluginPortFromWidget() are immediately reflected in the NPWindow +// structure that has been passed to the plugin via SetWindow(). This is +// because calls to nsChildView::GetNativeData(NS_NATIVE_PLUGIN_PORT_CG) +// always return a pointer to the same internal (private) object, but may +// make changes inside that object. All calls to GetPluginPortFromWidget() made while +// the plugin is active (i.e. excluding those made at our initialization) +// need to take this into account. The easiest way to do so is to replace +// them with calls to SetPluginPortAndDetectChange(). This method keeps track +// of when calls to GetPluginPortFromWidget() result in changes, and sets a flag to make +// sure SetWindow() gets called the next time through FixUpPluginWindow(), so +// that the plugin is notified of these changes. +void* nsPluginInstanceOwner::SetPluginPortAndDetectChange() +{ + if (!mPluginWindow) + return nullptr; + void* pluginPort = GetPluginPortFromWidget(); + if (!pluginPort) + return nullptr; + mPluginWindow->window = pluginPort; + + return mPluginWindow->window; +} + +void nsPluginInstanceOwner::BeginCGPaint() +{ + ++mInCGPaintLevel; +} + +void nsPluginInstanceOwner::EndCGPaint() +{ + --mInCGPaintLevel; + NS_ASSERTION(mInCGPaintLevel >= 0, "Mismatched call to nsPluginInstanceOwner::EndCGPaint()!"); +} + +#endif + +// static +uint32_t +nsPluginInstanceOwner::GetEventloopNestingLevel() +{ + nsCOMPtr appShell = do_GetService(kAppShellCID); + uint32_t currentLevel = 0; + if (appShell) { + appShell->GetEventloopNestingLevel(¤tLevel); +#ifdef XP_MACOSX + // Cocoa widget code doesn't process UI events through the normal + // appshell event loop, so it needs an additional count here. + currentLevel++; +#endif + } + + // No idea how this happens... but Linux doesn't consistently + // process UI events through the appshell event loop. If we get a 0 + // here on any platform we increment the level just in case so that + // we make sure we always tear the plugin down eventually. + if (!currentLevel) { + currentLevel++; + } + + return currentLevel; +} + +#ifdef MOZ_WIDGET_ANDROID + +// Modified version of nsFrame::GetOffsetToCrossDoc that stops when it +// hits an element with a displayport (or runs out of frames). This is +// not really the right thing to do, but it's better than what was here before. +static nsPoint +GetOffsetRootContent(nsIFrame* aFrame) +{ + // offset will hold the final offset + // docOffset holds the currently accumulated offset at the current APD, it + // will be converted and added to offset when the current APD changes. + nsPoint offset(0, 0), docOffset(0, 0); + const nsIFrame* f = aFrame; + int32_t currAPD = aFrame->PresContext()->AppUnitsPerDevPixel(); + int32_t apd = currAPD; + nsRect displayPort; + while (f) { + if (f->GetContent() && nsLayoutUtils::GetDisplayPort(f->GetContent(), &displayPort)) + break; + + docOffset += f->GetPosition(); + nsIFrame* parent = f->GetParent(); + if (parent) { + f = parent; + } else { + nsPoint newOffset(0, 0); + f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset); + int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0; + if (!f || newAPD != currAPD) { + // Convert docOffset to the right APD and add it to offset. + offset += docOffset.ConvertAppUnits(currAPD, apd); + docOffset.x = docOffset.y = 0; + } + currAPD = newAPD; + docOffset += newOffset; + } + } + + offset += docOffset.ConvertAppUnits(currAPD, apd); + + return offset; +} + +LayoutDeviceRect nsPluginInstanceOwner::GetPluginRect() +{ + // Get the offset of the content relative to the page + nsRect bounds = mObjectFrame->GetContentRectRelativeToSelf() + GetOffsetRootContent(mObjectFrame); + LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromAppUnitsToNearest(bounds, mObjectFrame->PresContext()->AppUnitsPerDevPixel()); + return LayoutDeviceRect(rect); +} + +bool nsPluginInstanceOwner::AddPluginView(const LayoutDeviceRect& aRect /* = LayoutDeviceRect(0, 0, 0, 0) */) +{ + if (!mJavaView) { + mJavaView = mInstance->GetJavaSurface(); + + if (!mJavaView) + return false; + + mJavaView = (void*)AndroidBridge::GetJNIEnv()->NewGlobalRef((jobject)mJavaView); + } + + if (AndroidBridge::Bridge()) + AndroidBridge::Bridge()->AddPluginView((jobject)mJavaView, aRect, mFullScreen); + + if (mFullScreen) + sFullScreenInstance = this; + + return true; +} + +void nsPluginInstanceOwner::RemovePluginView() +{ + if (!mInstance || !mJavaView) + return; + + mozilla::widget::android::GeckoAppShell::RemovePluginView((jobject)mJavaView, mFullScreen); + AndroidBridge::GetJNIEnv()->DeleteGlobalRef((jobject)mJavaView); + mJavaView = nullptr; + + if (mFullScreen) + sFullScreenInstance = nullptr; +} + +void nsPluginInstanceOwner::GetVideos(nsTArray& aVideos) +{ + if (!mInstance) + return; + + mInstance->GetVideos(aVideos); +} + +already_AddRefed nsPluginInstanceOwner::GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInfo* aVideoInfo) +{ + nsRefPtr container = LayerManager::CreateImageContainer(); + + nsRefPtr img = container->CreateImage(ImageFormat::SHARED_TEXTURE); + + SharedTextureImage::Data data; + + data.mShareType = gl::SharedTextureShareType::SameProcess; + data.mHandle = gl::CreateSharedHandle(mInstance->GLContext(), + data.mShareType, + aVideoInfo->mSurfaceTexture, + gl::SharedTextureBufferType::SurfaceTexture); + + // The logic below for Honeycomb is just a guess, but seems to work. We don't have a separate + // inverted flag for video. + data.mInverted = AndroidBridge::Bridge()->IsHoneycomb() ? true : mInstance->Inverted(); + data.mSize = gfx::IntSize(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height); + + SharedTextureImage* pluginImage = static_cast(img.get()); + pluginImage->SetData(data); + container->SetCurrentImageInTransaction(img); + + return container.forget(); +} + +void nsPluginInstanceOwner::Invalidate() { + NPRect rect; + rect.left = rect.top = 0; + rect.right = mPluginWindow->width; + rect.bottom = mPluginWindow->height; + InvalidateRect(&rect); +} + +void nsPluginInstanceOwner::RequestFullScreen() { + if (mFullScreen) + return; + + // Remove whatever view we currently have (if any, fullscreen or otherwise) + RemovePluginView(); + + mFullScreen = true; + AddPluginView(); + + mInstance->NotifyFullScreen(mFullScreen); +} + +void nsPluginInstanceOwner::ExitFullScreen() { + if (!mFullScreen) + return; + + RemovePluginView(); + + mFullScreen = false; + + int32_t model = mInstance->GetANPDrawingModel(); + + if (model == kSurface_ANPDrawingModel) { + // We need to do this immediately, otherwise Flash + // sometimes causes a deadlock (bug 762407) + AddPluginView(GetPluginRect()); + } + + mInstance->NotifyFullScreen(mFullScreen); + + // This will cause Paint() to be called, which is where + // we normally add/update views and layers + Invalidate(); +} + +void nsPluginInstanceOwner::ExitFullScreen(jobject view) { + JNIEnv* env = AndroidBridge::GetJNIEnv(); + + if (sFullScreenInstance && sFullScreenInstance->mInstance && + env->IsSameObject(view, (jobject)sFullScreenInstance->mInstance->GetJavaSurface())) { + sFullScreenInstance->ExitFullScreen(); + } +} + +#endif + +nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent) +{ +#ifdef MOZ_WIDGET_ANDROID + if (mInstance) { + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kLifecycle_ANPEventType; + + nsAutoString eventType; + aFocusEvent->GetType(eventType); + if (eventType.EqualsLiteral("focus")) { + event.data.lifecycle.action = kGainFocus_ANPLifecycleAction; + } + else if (eventType.EqualsLiteral("blur")) { + event.data.lifecycle.action = kLoseFocus_ANPLifecycleAction; + } + else { + NS_ASSERTION(false, "nsPluginInstanceOwner::DispatchFocusToPlugin, wierd eventType"); + } + mInstance->HandleEvent(&event, nullptr); + } +#endif + +#ifndef XP_MACOSX + if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) { + // continue only for cases without child window + return aFocusEvent->PreventDefault(); // consume event + } +#endif + + WidgetEvent* theEvent = aFocusEvent->GetInternalNSEvent(); + if (theEvent) { + // we only care about the message in ProcessEvent + WidgetGUIEvent focusEvent(theEvent->mFlags.mIsTrusted, theEvent->message, + nullptr); + nsEventStatus rv = ProcessEvent(focusEvent); + if (nsEventStatus_eConsumeNoDefault == rv) { + aFocusEvent->PreventDefault(); + aFocusEvent->StopPropagation(); + } + } + + return NS_OK; +} + +nsresult nsPluginInstanceOwner::ProcessKeyPress(nsIDOMEvent* aKeyEvent) +{ +#ifdef XP_MACOSX + return DispatchKeyToPlugin(aKeyEvent); +#else + if (SendNativeEvents()) + DispatchKeyToPlugin(aKeyEvent); + + if (mInstance) { + // If this event is going to the plugin, we want to kill it. + // Not actually sending keypress to the plugin, since we didn't before. + aKeyEvent->PreventDefault(); + aKeyEvent->StopPropagation(); + } + return NS_OK; +#endif +} + +nsresult nsPluginInstanceOwner::DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent) +{ +#if !defined(XP_MACOSX) + if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) + return aKeyEvent->PreventDefault(); // consume event + // continue only for cases without child window +#endif + + if (mInstance) { + WidgetKeyboardEvent* keyEvent = + aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent(); + if (keyEvent && keyEvent->eventStructType == NS_KEY_EVENT) { + nsEventStatus rv = ProcessEvent(*keyEvent); + if (nsEventStatus_eConsumeNoDefault == rv) { + aKeyEvent->PreventDefault(); + aKeyEvent->StopPropagation(); + } + } + } + + return NS_OK; +} + +nsresult +nsPluginInstanceOwner::ProcessMouseDown(nsIDOMEvent* aMouseEvent) +{ +#if !defined(XP_MACOSX) + if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) + return aMouseEvent->PreventDefault(); // consume event + // continue only for cases without child window +#endif + + // if the plugin is windowless, we need to set focus ourselves + // otherwise, we might not get key events + if (mObjectFrame && mPluginWindow && + mPluginWindow->type == NPWindowTypeDrawable) { + + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + nsCOMPtr elem = do_QueryInterface(mContent); + fm->SetFocus(elem, 0); + } + } + + WidgetMouseEvent* mouseEvent = + aMouseEvent->GetInternalNSEvent()->AsMouseEvent(); + if (mouseEvent && mouseEvent->eventStructType == NS_MOUSE_EVENT) { + mLastMouseDownButtonType = mouseEvent->button; + nsEventStatus rv = ProcessEvent(*mouseEvent); + if (nsEventStatus_eConsumeNoDefault == rv) { + return aMouseEvent->PreventDefault(); // consume event + } + } + + return NS_OK; +} + +nsresult nsPluginInstanceOwner::DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent, + bool aAllowPropagate) +{ +#if !defined(XP_MACOSX) + if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) + return aMouseEvent->PreventDefault(); // consume event + // continue only for cases without child window +#endif + // don't send mouse events if we are hidden + if (!mWidgetVisible) + return NS_OK; + + WidgetMouseEvent* mouseEvent = + aMouseEvent->GetInternalNSEvent()->AsMouseEvent(); + if (mouseEvent && mouseEvent->eventStructType == NS_MOUSE_EVENT) { + nsEventStatus rv = ProcessEvent(*mouseEvent); + if (nsEventStatus_eConsumeNoDefault == rv) { + aMouseEvent->PreventDefault(); + if (!aAllowPropagate) { + aMouseEvent->StopPropagation(); + } + } + if (mouseEvent->message == NS_MOUSE_BUTTON_UP) { + mLastMouseDownButtonType = -1; + } + } + return NS_OK; +} + +nsresult +nsPluginInstanceOwner::HandleEvent(nsIDOMEvent* aEvent) +{ + NS_ASSERTION(mInstance, "Should have a valid plugin instance or not receive events."); + + nsAutoString eventType; + aEvent->GetType(eventType); + if (eventType.EqualsLiteral("focus")) { + mContentFocused = true; + return DispatchFocusToPlugin(aEvent); + } + if (eventType.EqualsLiteral("blur")) { + mContentFocused = false; + return DispatchFocusToPlugin(aEvent); + } + if (eventType.EqualsLiteral("mousedown")) { + return ProcessMouseDown(aEvent); + } + if (eventType.EqualsLiteral("mouseup")) { + // Don't send a mouse-up event to the plugin if its button type doesn't + // match that of the preceding mouse-down event (if any). This kind of + // mismatch can happen if the previous mouse-down event was sent to a DOM + // element above the plugin, the mouse is still above the plugin, and the + // mouse-down event caused the element to disappear. See bug 627649 and + // bug 909678. + WidgetMouseEvent* mouseEvent = aEvent->GetInternalNSEvent()->AsMouseEvent(); + if (mouseEvent && + static_cast(mouseEvent->button) != mLastMouseDownButtonType) { + aEvent->PreventDefault(); + return NS_OK; + } + return DispatchMouseToPlugin(aEvent); + } + if (eventType.EqualsLiteral("mousemove")) { + return DispatchMouseToPlugin(aEvent, true); + } + if (eventType.EqualsLiteral("click") || + eventType.EqualsLiteral("dblclick") || + eventType.EqualsLiteral("mouseover") || + eventType.EqualsLiteral("mouseout")) { + return DispatchMouseToPlugin(aEvent); + } + if (eventType.EqualsLiteral("keydown") || + eventType.EqualsLiteral("keyup")) { + return DispatchKeyToPlugin(aEvent); + } + if (eventType.EqualsLiteral("keypress")) { + return ProcessKeyPress(aEvent); + } + + nsCOMPtr dragEvent(do_QueryInterface(aEvent)); + if (dragEvent && mInstance) { + WidgetEvent* ievent = aEvent->GetInternalNSEvent(); + if ((ievent && ievent->mFlags.mIsTrusted) && + ievent->message != NS_DRAGDROP_ENTER && ievent->message != NS_DRAGDROP_OVER) { + aEvent->PreventDefault(); + } + + // Let the plugin handle drag events. + aEvent->StopPropagation(); + } + return NS_OK; +} + +#ifdef MOZ_X11 +static unsigned int XInputEventState(const WidgetInputEvent& anEvent) +{ + unsigned int state = 0; + if (anEvent.IsShift()) state |= ShiftMask; + if (anEvent.IsControl()) state |= ControlMask; + if (anEvent.IsAlt()) state |= Mod1Mask; + if (anEvent.IsMeta()) state |= Mod4Mask; + return state; +} +#endif + +nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) +{ + nsEventStatus rv = nsEventStatus_eIgnore; + + if (!mInstance || !mObjectFrame) // if mInstance is null, we shouldn't be here + return nsEventStatus_eIgnore; + +#ifdef XP_MACOSX + if (!mWidget) + return nsEventStatus_eIgnore; + + // we never care about synthesized mouse enter + if (anEvent.message == NS_MOUSE_ENTER_SYNTH) + return nsEventStatus_eIgnore; + + nsCOMPtr pluginWidget = do_QueryInterface(mWidget); + if (!pluginWidget || NS_FAILED(pluginWidget->StartDrawPlugin())) + return nsEventStatus_eIgnore; + + NPEventModel eventModel = GetEventModel(); + + // If we have to synthesize an event we'll use one of these. + NPCocoaEvent synthCocoaEvent; + void* event = anEvent.pluginEvent; + nsPoint pt = + nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) - + mObjectFrame->GetContentRectRelativeToSelf().TopLeft(); + nsPresContext* presContext = mObjectFrame->PresContext(); + // Plugin event coordinates need to be translated from device pixels + // into "display pixels" in HiDPI modes. + double scaleFactor = 1.0; + GetContentsScaleFactor(&scaleFactor); + size_t intScaleFactor = ceil(scaleFactor); + nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x) / intScaleFactor, + presContext->AppUnitsToDevPixels(pt.y) / intScaleFactor); + + if (!event) { + InitializeNPCocoaEvent(&synthCocoaEvent); + switch (anEvent.message) { + case NS_MOUSE_MOVE: + { + // Ignore mouse-moved events that happen as part of a dragging + // operation that started over another frame. See bug 525078. + nsRefPtr frameselection = mObjectFrame->GetFrameSelection(); + if (!frameselection->GetMouseDownState() || + (nsIPresShell::GetCapturingContent() == mObjectFrame->GetContent())) { + synthCocoaEvent.type = NPCocoaEventMouseMoved; + synthCocoaEvent.data.mouse.pluginX = static_cast(ptPx.x); + synthCocoaEvent.data.mouse.pluginY = static_cast(ptPx.y); + event = &synthCocoaEvent; + } + } + break; + case NS_MOUSE_BUTTON_DOWN: + synthCocoaEvent.type = NPCocoaEventMouseDown; + synthCocoaEvent.data.mouse.pluginX = static_cast(ptPx.x); + synthCocoaEvent.data.mouse.pluginY = static_cast(ptPx.y); + event = &synthCocoaEvent; + break; + case NS_MOUSE_BUTTON_UP: + // If we're in a dragging operation that started over another frame, + // convert it into a mouse-entered event (in the Cocoa Event Model). + // See bug 525078. + if (anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton && + (nsIPresShell::GetCapturingContent() != mObjectFrame->GetContent())) { + synthCocoaEvent.type = NPCocoaEventMouseEntered; + synthCocoaEvent.data.mouse.pluginX = static_cast(ptPx.x); + synthCocoaEvent.data.mouse.pluginY = static_cast(ptPx.y); + event = &synthCocoaEvent; + } else { + synthCocoaEvent.type = NPCocoaEventMouseUp; + synthCocoaEvent.data.mouse.pluginX = static_cast(ptPx.x); + synthCocoaEvent.data.mouse.pluginY = static_cast(ptPx.y); + event = &synthCocoaEvent; + } + break; + default: + break; + } + + // If we still don't have an event, bail. + if (!event) { + pluginWidget->EndDrawPlugin(); + return nsEventStatus_eIgnore; + } + } + + int16_t response = kNPEventNotHandled; + void* window = FixUpPluginWindow(ePluginPaintEnable); + if (window || (eventModel == NPEventModelCocoa)) { + mInstance->HandleEvent(event, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + } + + if (eventModel == NPEventModelCocoa && response == kNPEventStartIME) { + pluginWidget->StartComplexTextInputForCurrentEvent(); + } + + if ((response == kNPEventHandled || response == kNPEventStartIME) && + !(anEvent.message == NS_MOUSE_BUTTON_DOWN && + anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton && + !mContentFocused)) { + rv = nsEventStatus_eConsumeNoDefault; + } + + pluginWidget->EndDrawPlugin(); +#endif + +#ifdef XP_WIN + // this code supports windowless plugins + NPEvent *pPluginEvent = (NPEvent*)anEvent.pluginEvent; + // we can get synthetic events from the EventStateManager... these + // have no pluginEvent + NPEvent pluginEvent; + if (anEvent.eventStructType == NS_MOUSE_EVENT) { + if (!pPluginEvent) { + // XXX Should extend this list to synthesize events for more event + // types + pluginEvent.event = 0; + const WidgetMouseEvent* mouseEvent = anEvent.AsMouseEvent(); + switch (anEvent.message) { + case NS_MOUSE_MOVE: + pluginEvent.event = WM_MOUSEMOVE; + break; + case NS_MOUSE_BUTTON_DOWN: { + static const int downMsgs[] = + { WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN }; + static const int dblClickMsgs[] = + { WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK, WM_RBUTTONDBLCLK }; + if (mouseEvent->clickCount == 2) { + pluginEvent.event = dblClickMsgs[mouseEvent->button]; + } else { + pluginEvent.event = downMsgs[mouseEvent->button]; + } + break; + } + case NS_MOUSE_BUTTON_UP: { + static const int upMsgs[] = + { WM_LBUTTONUP, WM_MBUTTONUP, WM_RBUTTONUP }; + pluginEvent.event = upMsgs[mouseEvent->button]; + break; + } + // don't synthesize anything for NS_MOUSE_DOUBLECLICK, since that + // is a synthetic event generated on mouse-up, and Windows WM_*DBLCLK + // messages are sent on mouse-down + default: + break; + } + if (pluginEvent.event) { + pPluginEvent = &pluginEvent; + pluginEvent.wParam = + (::GetKeyState(VK_CONTROL) ? MK_CONTROL : 0) | + (::GetKeyState(VK_SHIFT) ? MK_SHIFT : 0) | + (::GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0) | + (::GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0) | + (::GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0) | + (::GetKeyState(VK_XBUTTON1) ? MK_XBUTTON1 : 0) | + (::GetKeyState(VK_XBUTTON2) ? MK_XBUTTON2 : 0); + } + } + if (pPluginEvent) { + // Make event coordinates relative to our enclosing widget, + // not the widget they were received on. + // See use of NPEvent in widget/windows/nsWindow.cpp + // for why this assert should be safe + NS_ASSERTION(anEvent.message == NS_MOUSE_BUTTON_DOWN || + anEvent.message == NS_MOUSE_BUTTON_UP || + anEvent.message == NS_MOUSE_DOUBLECLICK || + anEvent.message == NS_MOUSE_ENTER_SYNTH || + anEvent.message == NS_MOUSE_EXIT_SYNTH || + anEvent.message == NS_MOUSE_MOVE, + "Incorrect event type for coordinate translation"); + nsPoint pt = + nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) - + mObjectFrame->GetContentRectRelativeToSelf().TopLeft(); + nsPresContext* presContext = mObjectFrame->PresContext(); + nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x), + presContext->AppUnitsToDevPixels(pt.y)); + nsIntPoint widgetPtPx = ptPx + mObjectFrame->GetWindowOriginInPixels(true); + pPluginEvent->lParam = MAKELPARAM(widgetPtPx.x, widgetPtPx.y); + } + } + else if (!pPluginEvent) { + switch (anEvent.message) { + case NS_FOCUS_CONTENT: + pluginEvent.event = WM_SETFOCUS; + pluginEvent.wParam = 0; + pluginEvent.lParam = 0; + pPluginEvent = &pluginEvent; + break; + case NS_BLUR_CONTENT: + pluginEvent.event = WM_KILLFOCUS; + pluginEvent.wParam = 0; + pluginEvent.lParam = 0; + pPluginEvent = &pluginEvent; + break; + } + } + + if (pPluginEvent && !pPluginEvent->event) { + // Don't send null events to plugins. + NS_WARNING("nsObjectFrame ProcessEvent: trying to send null event to plugin."); + return rv; + } + + if (pPluginEvent) { + int16_t response = kNPEventNotHandled; + mInstance->HandleEvent(pPluginEvent, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + if (response == kNPEventHandled) + rv = nsEventStatus_eConsumeNoDefault; + } +#endif + +#ifdef MOZ_X11 + // this code supports windowless plugins + nsIWidget* widget = anEvent.widget; + XEvent pluginEvent = XEvent(); + pluginEvent.type = 0; + + switch(anEvent.eventStructType) + { + case NS_MOUSE_EVENT: + { + switch (anEvent.message) + { + case NS_MOUSE_CLICK: + case NS_MOUSE_DOUBLECLICK: + // Button up/down events sent instead. + return rv; + } + + // Get reference point relative to plugin origin. + const nsPresContext* presContext = mObjectFrame->PresContext(); + nsPoint appPoint = + nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) - + mObjectFrame->GetContentRectRelativeToSelf().TopLeft(); + nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x), + presContext->AppUnitsToDevPixels(appPoint.y)); + const WidgetMouseEvent& mouseEvent = *anEvent.AsMouseEvent(); + // Get reference point relative to screen: + LayoutDeviceIntPoint rootPoint(-1, -1); + if (widget) + rootPoint = anEvent.refPoint + + LayoutDeviceIntPoint::FromUntyped(widget->WidgetToScreenOffset()); +#ifdef MOZ_WIDGET_GTK + Window root = GDK_ROOT_WINDOW(); +#elif defined(MOZ_WIDGET_QT) + Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mozilla::DefaultXDisplay())); +#else + Window root = None; // Could XQueryTree, but this is not important. +#endif + + switch (anEvent.message) + { + case NS_MOUSE_ENTER_SYNTH: + case NS_MOUSE_EXIT_SYNTH: + { + XCrossingEvent& event = pluginEvent.xcrossing; + event.type = anEvent.message == NS_MOUSE_ENTER_SYNTH ? + EnterNotify : LeaveNotify; + event.root = root; + event.time = anEvent.time; + event.x = pluginPoint.x; + event.y = pluginPoint.y; + event.x_root = rootPoint.x; + event.y_root = rootPoint.y; + event.state = XInputEventState(mouseEvent); + // information lost + event.subwindow = None; + event.mode = -1; + event.detail = NotifyDetailNone; + event.same_screen = True; + event.focus = mContentFocused; + } + break; + case NS_MOUSE_MOVE: + { + XMotionEvent& event = pluginEvent.xmotion; + event.type = MotionNotify; + event.root = root; + event.time = anEvent.time; + event.x = pluginPoint.x; + event.y = pluginPoint.y; + event.x_root = rootPoint.x; + event.y_root = rootPoint.y; + event.state = XInputEventState(mouseEvent); + // information lost + event.subwindow = None; + event.is_hint = NotifyNormal; + event.same_screen = True; + } + break; + case NS_MOUSE_BUTTON_DOWN: + case NS_MOUSE_BUTTON_UP: + { + XButtonEvent& event = pluginEvent.xbutton; + event.type = anEvent.message == NS_MOUSE_BUTTON_DOWN ? + ButtonPress : ButtonRelease; + event.root = root; + event.time = anEvent.time; + event.x = pluginPoint.x; + event.y = pluginPoint.y; + event.x_root = rootPoint.x; + event.y_root = rootPoint.y; + event.state = XInputEventState(mouseEvent); + switch (mouseEvent.button) + { + case WidgetMouseEvent::eMiddleButton: + event.button = 2; + break; + case WidgetMouseEvent::eRightButton: + event.button = 3; + break; + default: // WidgetMouseEvent::eLeftButton; + event.button = 1; + break; + } + // information lost: + event.subwindow = None; + event.same_screen = True; + } + break; + } + } + break; + + //XXX case NS_MOUSE_SCROLL_EVENT: not received. + + case NS_KEY_EVENT: + if (anEvent.pluginEvent) + { + XKeyEvent &event = pluginEvent.xkey; +#ifdef MOZ_WIDGET_GTK + event.root = GDK_ROOT_WINDOW(); + event.time = anEvent.time; + const GdkEventKey* gdkEvent = + static_cast(anEvent.pluginEvent); + event.keycode = gdkEvent->hardware_keycode; + event.state = gdkEvent->state; + switch (anEvent.message) + { + case NS_KEY_DOWN: + // Handle NS_KEY_DOWN for modifier key presses + // For non-modifiers we get NS_KEY_PRESS + if (gdkEvent->is_modifier) + event.type = XKeyPress; + break; + case NS_KEY_PRESS: + event.type = XKeyPress; + break; + case NS_KEY_UP: + event.type = KeyRelease; + break; + } +#endif + + // Information that could be obtained from pluginEvent but we may not + // want to promise to provide: + event.subwindow = None; + event.x = 0; + event.y = 0; + event.x_root = -1; + event.y_root = -1; + event.same_screen = False; + } + else + { + // If we need to send synthesized key events, then + // DOMKeyCodeToGdkKeyCode(keyEvent.keyCode) and + // gdk_keymap_get_entries_for_keyval will be useful, but the + // mappings will not be unique. + NS_WARNING("Synthesized key event not sent to plugin"); + } + break; + + default: + switch (anEvent.message) + { + case NS_FOCUS_CONTENT: + case NS_BLUR_CONTENT: + { + XFocusChangeEvent &event = pluginEvent.xfocus; + event.type = + anEvent.message == NS_FOCUS_CONTENT ? FocusIn : FocusOut; + // information lost: + event.mode = -1; + event.detail = NotifyDetailNone; + } + break; + } + } + + if (!pluginEvent.type) { + return rv; + } + + // Fill in (useless) generic event information. + XAnyEvent& event = pluginEvent.xany; + event.display = widget ? + static_cast(widget->GetNativeData(NS_NATIVE_DISPLAY)) : nullptr; + event.window = None; // not a real window + // information lost: + event.serial = 0; + event.send_event = False; + + int16_t response = kNPEventNotHandled; + mInstance->HandleEvent(&pluginEvent, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + if (response == kNPEventHandled) + rv = nsEventStatus_eConsumeNoDefault; +#endif + +#ifdef MOZ_WIDGET_ANDROID + // this code supports windowless plugins + { + // The plugin needs focus to receive keyboard and touch events + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + nsCOMPtr elem = do_QueryInterface(mContent); + fm->SetFocus(elem, 0); + } + } + switch(anEvent.eventStructType) + { + case NS_MOUSE_EVENT: + { + switch (anEvent.message) + { + case NS_MOUSE_CLICK: + case NS_MOUSE_DOUBLECLICK: + // Button up/down events sent instead. + return rv; + } + + // Get reference point relative to plugin origin. + const nsPresContext* presContext = mObjectFrame->PresContext(); + nsPoint appPoint = + nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) - + mObjectFrame->GetContentRectRelativeToSelf().TopLeft(); + nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x), + presContext->AppUnitsToDevPixels(appPoint.y)); + + switch (anEvent.message) + { + case NS_MOUSE_MOVE: + { + // are these going to be touch events? + // pluginPoint.x; + // pluginPoint.y; + } + break; + case NS_MOUSE_BUTTON_DOWN: + { + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kMouse_ANPEventType; + event.data.mouse.action = kDown_ANPMouseAction; + event.data.mouse.x = pluginPoint.x; + event.data.mouse.y = pluginPoint.y; + mInstance->HandleEvent(&event, nullptr, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + } + break; + case NS_MOUSE_BUTTON_UP: + { + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kMouse_ANPEventType; + event.data.mouse.action = kUp_ANPMouseAction; + event.data.mouse.x = pluginPoint.x; + event.data.mouse.y = pluginPoint.y; + mInstance->HandleEvent(&event, nullptr, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + } + break; + } + } + break; + + case NS_KEY_EVENT: + { + const WidgetKeyboardEvent& keyEvent = *anEvent.AsKeyboardEvent(); + LOG("Firing NS_KEY_EVENT %d %d\n", keyEvent.keyCode, keyEvent.charCode); + // pluginEvent is initialized by nsWindow::InitKeyEvent(). + ANPEvent* pluginEvent = reinterpret_cast(keyEvent.pluginEvent); + if (pluginEvent) { + MOZ_ASSERT(pluginEvent->inSize == sizeof(ANPEvent)); + MOZ_ASSERT(pluginEvent->eventType == kKey_ANPEventType); + mInstance->HandleEvent(pluginEvent, nullptr, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); + } + } + break; + + default: + break; + } + rv = nsEventStatus_eConsumeNoDefault; +#endif + + return rv; +} + +nsresult +nsPluginInstanceOwner::Destroy() +{ + SetFrame(nullptr); + +#ifdef XP_MACOSX + RemoveFromCARefreshTimer(); + if (mColorProfile) + ::CGColorSpaceRelease(mColorProfile); +#endif + + // unregister context menu listener + if (mCXMenuListener) { + mCXMenuListener->Destroy(mContent); + mCXMenuListener = nullptr; + } + + mContent->RemoveEventListener(NS_LITERAL_STRING("focus"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("blur"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("click"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("dblclick"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("mouseover"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false); + mContent->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("drop"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("dragdrop"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("drag"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("dragenter"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("dragover"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("dragleave"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("dragexit"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("dragstart"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("draggesture"), this, true); + mContent->RemoveEventListener(NS_LITERAL_STRING("dragend"), this, true); + +#if MOZ_WIDGET_ANDROID + RemovePluginView(); +#endif + + if (mWidget) { + if (mPluginWindow) { + mPluginWindow->SetPluginWidget(nullptr); + } + + nsCOMPtr pluginWidget = do_QueryInterface(mWidget); + if (pluginWidget) { + pluginWidget->SetPluginInstanceOwner(nullptr); + } + mWidget->Destroy(); + } + + return NS_OK; +} + +// Paints are handled differently, so we just simulate an update event. + +#ifdef XP_MACOSX +void nsPluginInstanceOwner::Paint(const gfxRect& aDirtyRect, CGContextRef cgContext) +{ + if (!mInstance || !mObjectFrame) + return; + + gfxRect dirtyRectCopy = aDirtyRect; + double scaleFactor = 1.0; + GetContentsScaleFactor(&scaleFactor); + if (scaleFactor != 1.0) { + ::CGContextScaleCTM(cgContext, scaleFactor, scaleFactor); + // Convert aDirtyRect from device pixels to "display pixels" + // for HiDPI modes + dirtyRectCopy.ScaleRoundOut(1.0 / scaleFactor); + } + + nsCOMPtr pluginWidget = do_QueryInterface(mWidget); + if (pluginWidget && NS_SUCCEEDED(pluginWidget->StartDrawPlugin())) { + DoCocoaEventDrawRect(dirtyRectCopy, cgContext); + pluginWidget->EndDrawPlugin(); + } +} + +void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext) +{ + if (!mInstance || !mObjectFrame) + return; + + // The context given here is only valid during the HandleEvent call. + NPCocoaEvent updateEvent; + InitializeNPCocoaEvent(&updateEvent); + updateEvent.type = NPCocoaEventDrawRect; + updateEvent.data.draw.context = cgContext; + updateEvent.data.draw.x = aDrawRect.X(); + updateEvent.data.draw.y = aDrawRect.Y(); + updateEvent.data.draw.width = aDrawRect.Width(); + updateEvent.data.draw.height = aDrawRect.Height(); + + mInstance->HandleEvent(&updateEvent, nullptr); +} +#endif + +#ifdef XP_WIN +void nsPluginInstanceOwner::Paint(const RECT& aDirty, HDC aDC) +{ + if (!mInstance || !mObjectFrame) + return; + + NPEvent pluginEvent; + pluginEvent.event = WM_PAINT; + pluginEvent.wParam = WPARAM(aDC); + pluginEvent.lParam = LPARAM(&aDirty); + mInstance->HandleEvent(&pluginEvent, nullptr); +} +#endif + +#ifdef MOZ_WIDGET_ANDROID + +void nsPluginInstanceOwner::Paint(gfxContext* aContext, + const gfxRect& aFrameRect, + const gfxRect& aDirtyRect) +{ + if (!mInstance || !mObjectFrame || !mPluginDocumentActiveState || mFullScreen) + return; + + int32_t model = mInstance->GetANPDrawingModel(); + + if (model == kSurface_ANPDrawingModel) { + if (!AddPluginView(GetPluginRect())) { + Invalidate(); + } + return; + } + + if (model != kBitmap_ANPDrawingModel) + return; + +#ifdef ANP_BITMAP_DRAWING_MODEL + static nsRefPtr pluginSurface; + + if (pluginSurface == nullptr || + aFrameRect.width != pluginSurface->Width() || + aFrameRect.height != pluginSurface->Height()) { + + pluginSurface = new gfxImageSurface(gfxIntSize(aFrameRect.width, aFrameRect.height), + gfxImageFormat::ARGB32); + if (!pluginSurface) + return; + } + + // Clears buffer. I think this is needed. + nsRefPtr ctx = new gfxContext(pluginSurface); + ctx->SetOperator(gfxContext::OPERATOR_CLEAR); + ctx->Paint(); + + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = 4; + event.data.draw.model = 1; + + event.data.draw.clip.top = 0; + event.data.draw.clip.left = 0; + event.data.draw.clip.bottom = aFrameRect.width; + event.data.draw.clip.right = aFrameRect.height; + + event.data.draw.data.bitmap.format = kRGBA_8888_ANPBitmapFormat; + event.data.draw.data.bitmap.width = aFrameRect.width; + event.data.draw.data.bitmap.height = aFrameRect.height; + event.data.draw.data.bitmap.baseAddr = pluginSurface->Data(); + event.data.draw.data.bitmap.rowBytes = aFrameRect.width * 4; + + if (!mInstance) + return; + + mInstance->HandleEvent(&event, nullptr); + + aContext->SetOperator(gfxContext::OPERATOR_SOURCE); + aContext->SetSource(pluginSurface, gfxPoint(aFrameRect.x, aFrameRect.y)); + aContext->Clip(aFrameRect); + aContext->Paint(); +#endif +} +#endif + +#if defined(MOZ_X11) +void nsPluginInstanceOwner::Paint(gfxContext* aContext, + const gfxRect& aFrameRect, + const gfxRect& aDirtyRect) +{ + if (!mInstance || !mObjectFrame) + return; + + // to provide crisper and faster drawing. + gfxRect pluginRect = aFrameRect; + if (aContext->UserToDevicePixelSnapped(pluginRect)) { + pluginRect = aContext->DeviceToUser(pluginRect); + } + + // Round out the dirty rect to plugin pixels to ensure the plugin draws + // enough pixels for interpolation to device pixels. + gfxRect dirtyRect = aDirtyRect - pluginRect.TopLeft(); + dirtyRect.RoundOut(); + + // Plugins can only draw an integer number of pixels. + // + // With translation-only transformation matrices, pluginRect is already + // pixel-aligned. + // + // With more complex transformations, modifying the scales in the + // transformation matrix could retain subpixel accuracy and let the plugin + // draw a suitable number of pixels for interpolation to device pixels in + // Renderer::Draw, but such cases are not common enough to warrant the + // effort now. + nsIntSize pluginSize(NS_lround(pluginRect.width), + NS_lround(pluginRect.height)); + + // Determine what the plugin needs to draw. + nsIntRect pluginDirtyRect(int32_t(dirtyRect.x), + int32_t(dirtyRect.y), + int32_t(dirtyRect.width), + int32_t(dirtyRect.height)); + if (!pluginDirtyRect. + IntersectRect(nsIntRect(0, 0, pluginSize.width, pluginSize.height), + pluginDirtyRect)) + return; + + NPWindow* window; + GetWindow(window); + + uint32_t rendererFlags = 0; + if (!mFlash10Quirks) { + rendererFlags |= + Renderer::DRAW_SUPPORTS_CLIP_RECT | + Renderer::DRAW_SUPPORTS_ALTERNATE_VISUAL; + } + + bool transparent; + mInstance->IsTransparent(&transparent); + if (!transparent) + rendererFlags |= Renderer::DRAW_IS_OPAQUE; + + // Renderer::Draw() draws a rectangle with top-left at the aContext origin. + gfxContextAutoSaveRestore autoSR(aContext); + aContext->Translate(pluginRect.TopLeft()); + + Renderer renderer(window, this, pluginSize, pluginDirtyRect); + + Display* dpy = mozilla::DefaultXDisplay(); + Screen* screen = DefaultScreenOfDisplay(dpy); + Visual* visual = DefaultVisualOfScreen(screen); + + renderer.Draw(aContext, nsIntSize(window->width, window->height), + rendererFlags, screen, visual); +} +nsresult +nsPluginInstanceOwner::Renderer::DrawWithXlib(cairo_surface_t* xsurface, + nsIntPoint offset, + nsIntRect *clipRects, + uint32_t numClipRects) +{ + Screen *screen = cairo_xlib_surface_get_screen(xsurface); + Colormap colormap; + Visual* visual; + if (!gfxXlibSurface::GetColormapAndVisual(xsurface, &colormap, &visual)) { + NS_ERROR("Failed to get visual and colormap"); + return NS_ERROR_UNEXPECTED; + } + + nsNPAPIPluginInstance *instance = mInstanceOwner->mInstance; + if (!instance) + return NS_ERROR_FAILURE; + + // See if the plugin must be notified of new window parameters. + bool doupdatewindow = false; + + if (mWindow->x != offset.x || mWindow->y != offset.y) { + mWindow->x = offset.x; + mWindow->y = offset.y; + doupdatewindow = true; + } + + if (nsIntSize(mWindow->width, mWindow->height) != mPluginSize) { + mWindow->width = mPluginSize.width; + mWindow->height = mPluginSize.height; + doupdatewindow = true; + } + + // The clip rect is relative to drawable top-left. + NS_ASSERTION(numClipRects <= 1, "We don't support multiple clip rectangles!"); + nsIntRect clipRect; + if (numClipRects) { + clipRect.x = clipRects[0].x; + clipRect.y = clipRects[0].y; + clipRect.width = clipRects[0].width; + clipRect.height = clipRects[0].height; + // NPRect members are unsigned, but clip rectangles should be contained by + // the surface. + NS_ASSERTION(clipRect.x >= 0 && clipRect.y >= 0, + "Clip rectangle offsets are negative!"); + } + else { + clipRect.x = offset.x; + clipRect.y = offset.y; + clipRect.width = mWindow->width; + clipRect.height = mWindow->height; + // Don't ask the plugin to draw outside the drawable. + // This also ensures that the unsigned clip rectangle offsets won't be -ve. + clipRect.IntersectRect(clipRect, + nsIntRect(0, 0, + cairo_xlib_surface_get_width(xsurface), + cairo_xlib_surface_get_height(xsurface))); + } + + NPRect newClipRect; + newClipRect.left = clipRect.x; + newClipRect.top = clipRect.y; + newClipRect.right = clipRect.XMost(); + newClipRect.bottom = clipRect.YMost(); + if (mWindow->clipRect.left != newClipRect.left || + mWindow->clipRect.top != newClipRect.top || + mWindow->clipRect.right != newClipRect.right || + mWindow->clipRect.bottom != newClipRect.bottom) { + mWindow->clipRect = newClipRect; + doupdatewindow = true; + } + + NPSetWindowCallbackStruct* ws_info = + static_cast(mWindow->ws_info); +#ifdef MOZ_X11 + if (ws_info->visual != visual || ws_info->colormap != colormap) { + ws_info->visual = visual; + ws_info->colormap = colormap; + ws_info->depth = gfxXlibSurface::DepthOfVisual(screen, visual); + doupdatewindow = true; + } +#endif + + { + if (doupdatewindow) + instance->SetWindow(mWindow); + } + + // Translate the dirty rect to drawable coordinates. + nsIntRect dirtyRect = mDirtyRect + offset; + if (mInstanceOwner->mFlash10Quirks) { + // Work around a bug in Flash up to 10.1 d51 at least, where expose event + // top left coordinates within the plugin-rect and not at the drawable + // origin are misinterpreted. (We can move the top left coordinate + // provided it is within the clipRect.) + dirtyRect.SetRect(offset.x, offset.y, + mDirtyRect.XMost(), mDirtyRect.YMost()); + } + // Intersect the dirty rect with the clip rect to ensure that it lies within + // the drawable. + if (!dirtyRect.IntersectRect(dirtyRect, clipRect)) + return NS_OK; + + { + XEvent pluginEvent = XEvent(); + XGraphicsExposeEvent& exposeEvent = pluginEvent.xgraphicsexpose; + // set the drawing info + exposeEvent.type = GraphicsExpose; + exposeEvent.display = DisplayOfScreen(screen); + exposeEvent.drawable = cairo_xlib_surface_get_drawable(xsurface); + exposeEvent.x = dirtyRect.x; + exposeEvent.y = dirtyRect.y; + exposeEvent.width = dirtyRect.width; + exposeEvent.height = dirtyRect.height; + exposeEvent.count = 0; + // information not set: + exposeEvent.serial = 0; + exposeEvent.send_event = False; + exposeEvent.major_code = 0; + exposeEvent.minor_code = 0; + + instance->HandleEvent(&pluginEvent, nullptr); + } + return NS_OK; +} +#endif + +nsresult nsPluginInstanceOwner::Init(nsIContent* aContent) +{ + mLastEventloopNestingLevel = GetEventloopNestingLevel(); + + mContent = aContent; + + // Get a frame, don't reflow. If a reflow was necessary it should have been + // done at a higher level than this (content). + nsIFrame* frame = aContent->GetPrimaryFrame(); + nsIObjectFrame* iObjFrame = do_QueryFrame(frame); + nsObjectFrame* objFrame = static_cast(iObjFrame); + if (objFrame) { + SetFrame(objFrame); + // Some plugins require a specific sequence of shutdown and startup when + // a page is reloaded. Shutdown happens usually when the last instance + // is destroyed. Here we make sure the plugin instance in the old + // document is destroyed before we try to create the new one. + objFrame->PresContext()->EnsureVisible(); + } else { + NS_NOTREACHED("Should not be initializing plugin without a frame"); + return NS_ERROR_FAILURE; + } + + // register context menu listener + mCXMenuListener = new nsPluginDOMContextMenuListener(aContent); + + mContent->AddEventListener(NS_LITERAL_STRING("focus"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("blur"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("mouseup"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("mousedown"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("mousemove"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("click"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("dblclick"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("mouseover"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("mouseout"), this, false, + false); + mContent->AddEventListener(NS_LITERAL_STRING("keypress"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("keydown"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("keyup"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("drop"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("dragdrop"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("drag"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("dragenter"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("dragover"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("dragleave"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("dragexit"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("draggesture"), this, true); + mContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, true); + + return NS_OK; +} + +void* nsPluginInstanceOwner::GetPluginPortFromWidget() +{ +//!!! Port must be released for windowless plugins on Windows, because it is HDC !!! + + void* result = nullptr; + if (mWidget) { +#ifdef XP_WIN + if (mPluginWindow && (mPluginWindow->type == NPWindowTypeDrawable)) + result = mWidget->GetNativeData(NS_NATIVE_GRAPHIC); + else +#endif +#ifdef XP_MACOSX + if (GetDrawingModel() == NPDrawingModelCoreGraphics || + GetDrawingModel() == NPDrawingModelCoreAnimation || + GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation) + result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT_CG); + else +#endif + result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT); + } + return result; +} + +void nsPluginInstanceOwner::ReleasePluginPort(void * pluginPort) +{ +#ifdef XP_WIN + if (mWidget && mPluginWindow && + mPluginWindow->type == NPWindowTypeDrawable) { + mWidget->FreeNativeData((HDC)pluginPort, NS_NATIVE_GRAPHIC); + } +#endif +} + +NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void) +{ + NS_ENSURE_TRUE(mPluginWindow, NS_ERROR_NULL_POINTER); + + nsresult rv = NS_ERROR_FAILURE; + + // Can't call this twice! + if (mWidget) { + NS_WARNING("Trying to create a plugin widget twice!"); + return NS_ERROR_FAILURE; + } + + bool windowless = false; + mInstance->IsWindowless(&windowless); + if (!windowless && !nsIWidget::UsePuppetWidgets()) { + // Try to get a parent widget, on some platforms widget creation will fail without + // a parent. + nsCOMPtr parentWidget; + nsIDocument *doc = nullptr; + if (mContent) { + doc = mContent->OwnerDoc(); + parentWidget = nsContentUtils::WidgetForDocument(doc); + } + + mWidget = do_CreateInstance(kWidgetCID, &rv); + if (NS_FAILED(rv)) { + return rv; + } + + nsWidgetInitData initData; + initData.mWindowType = eWindowType_plugin; + initData.mUnicode = false; + initData.clipChildren = true; + initData.clipSiblings = true; + rv = mWidget->Create(parentWidget.get(), nullptr, nsIntRect(0,0,0,0), + nullptr, &initData); + if (NS_FAILED(rv)) { + mWidget->Destroy(); + mWidget = nullptr; + return rv; + } + + mWidget->EnableDragDrop(true); + mWidget->Show(false); + mWidget->Enable(false); + +#ifdef XP_MACOSX + // Now that we have a widget we want to set the event model before + // any events are processed. + nsCOMPtr pluginWidget = do_QueryInterface(mWidget); + if (!pluginWidget) { + return NS_ERROR_FAILURE; + } + pluginWidget->SetPluginEventModel(GetEventModel()); + pluginWidget->SetPluginDrawingModel(GetDrawingModel()); + + if (GetDrawingModel() == NPDrawingModelCoreAnimation) { + AddToCARefreshTimer(); + } +#endif + } + + if (mObjectFrame) { + // nullptr widget is fine, will result in windowless setup. + mObjectFrame->PrepForDrawing(mWidget); + } + + if (windowless) { + mPluginWindow->type = NPWindowTypeDrawable; + + // this needs to be a HDC according to the spec, but I do + // not see the right way to release it so let's postpone + // passing HDC till paint event when it is really + // needed. Change spec? + mPluginWindow->window = nullptr; +#ifdef MOZ_X11 + // Fill in the display field. + NPSetWindowCallbackStruct* ws_info = + static_cast(mPluginWindow->ws_info); + ws_info->display = DefaultXDisplay(); + + nsAutoCString description; + GetPluginDescription(description); + NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10."); + mFlash10Quirks = StringBeginsWith(description, flash10Head); +#endif + } else if (mWidget) { + // mPluginWindow->type is used in |GetPluginPort| so it must + // be initialized first + mPluginWindow->type = NPWindowTypeWindow; + mPluginWindow->window = GetPluginPortFromWidget(); + // tell the plugin window about the widget + mPluginWindow->SetPluginWidget(mWidget); + + // tell the widget about the current plugin instance owner. + nsCOMPtr pluginWidget = do_QueryInterface(mWidget); + if (pluginWidget) { + pluginWidget->SetPluginInstanceOwner(this); + } + } + + mWidgetCreationComplete = true; + + return NS_OK; +} + +// Mac specific code to fix up the port location and clipping region +#ifdef XP_MACOSX + +void* nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState) +{ + if (!mWidget || !mPluginWindow || !mInstance || !mObjectFrame) + return nullptr; + + nsCOMPtr pluginWidget = do_QueryInterface(mWidget); + if (!pluginWidget) + return nullptr; + + // If we've already set up a CGContext in nsObjectFrame::PaintPlugin(), we + // don't want calls to SetPluginPortAndDetectChange() to step on our work. + if (mInCGPaintLevel < 1) { + SetPluginPortAndDetectChange(); + } + + // We'll need the top-level Cocoa window for the Cocoa event model. + nsIWidget* widget = mObjectFrame->GetNearestWidget(); + if (!widget) + return nullptr; + void *cocoaTopLevelWindow = widget->GetNativeData(NS_NATIVE_WINDOW); + if (!cocoaTopLevelWindow) + return nullptr; + + nsIntPoint pluginOrigin; + nsIntRect widgetClip; + bool widgetVisible; + pluginWidget->GetPluginClipRect(widgetClip, pluginOrigin, widgetVisible); + mWidgetVisible = widgetVisible; + + // printf("GetPluginClipRect returning visible %d\n", widgetVisible); + + // This would be a lot easier if we could use obj-c here, + // but we can't. Since we have only nsIWidget and we can't + // use its native widget (an obj-c object) we have to go + // from the widget's screen coordinates to its window coords + // instead of straight to window coords. + nsIntPoint geckoScreenCoords = mWidget->WidgetToScreenOffset(); + + nsRect windowRect; + NS_NPAPI_CocoaWindowFrame(cocoaTopLevelWindow, windowRect); + + double scaleFactor = 1.0; + GetContentsScaleFactor(&scaleFactor); + int intScaleFactor = ceil(scaleFactor); + + // Convert geckoScreenCoords from device pixels to "display pixels" + // for HiDPI modes. + mPluginWindow->x = geckoScreenCoords.x/intScaleFactor - windowRect.x; + mPluginWindow->y = geckoScreenCoords.y/intScaleFactor - windowRect.y; + + NPRect oldClipRect = mPluginWindow->clipRect; + + // fix up the clipping region + mPluginWindow->clipRect.top = widgetClip.y; + mPluginWindow->clipRect.left = widgetClip.x; + + if (!mWidgetVisible || inPaintState == ePluginPaintDisable) { + mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top; + mPluginWindow->clipRect.right = mPluginWindow->clipRect.left; + } + else if (inPaintState == ePluginPaintEnable) + { + mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top + widgetClip.height; + mPluginWindow->clipRect.right = mPluginWindow->clipRect.left + widgetClip.width; + } + + // if the clip rect changed, call SetWindow() + // (RealPlayer needs this to draw correctly) + if (mPluginWindow->clipRect.left != oldClipRect.left || + mPluginWindow->clipRect.top != oldClipRect.top || + mPluginWindow->clipRect.right != oldClipRect.right || + mPluginWindow->clipRect.bottom != oldClipRect.bottom || + mPluginPortChanged) + { + if (UseAsyncRendering()) { + mInstance->AsyncSetWindow(mPluginWindow); + } + else { + mPluginWindow->CallSetWindow(mInstance); + } + mPluginPortChanged = false; + } + + // After the first NPP_SetWindow call we need to send an initial + // top-level window focus event. + if (!mSentInitialTopLevelWindowEvent) { + // Set this before calling ProcessEvent to avoid endless recursion. + mSentInitialTopLevelWindowEvent = true; + + WidgetPluginEvent pluginEvent(true, NS_PLUGIN_FOCUS_EVENT, nullptr); + NPCocoaEvent cocoaEvent; + InitializeNPCocoaEvent(&cocoaEvent); + cocoaEvent.type = NPCocoaEventWindowFocusChanged; + cocoaEvent.data.focus.hasFocus = NS_NPAPI_CocoaWindowIsMain(cocoaTopLevelWindow); + pluginEvent.pluginEvent = &cocoaEvent; + ProcessEvent(pluginEvent); + } + + return nullptr; +} + +void +nsPluginInstanceOwner::HidePluginWindow() +{ + if (!mPluginWindow || !mInstance) { + return; + } + + mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top; + mPluginWindow->clipRect.right = mPluginWindow->clipRect.left; + mWidgetVisible = false; + if (UseAsyncRendering()) { + mInstance->AsyncSetWindow(mPluginWindow); + } else { + mInstance->SetWindow(mPluginWindow); + } +} + +#else // XP_MACOSX + +void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow) +{ + if (!mPluginWindow) + return; + + // For windowless plugins a non-empty clip rectangle will be + // passed to the plugin during paint, an additional update + // of the the clip rectangle here is not required + if (aSetWindow && !mWidget && mPluginWindowVisible && !UseAsyncRendering()) + return; + + const NPWindow oldWindow = *mPluginWindow; + + bool windowless = (mPluginWindow->type == NPWindowTypeDrawable); + nsIntPoint origin = mObjectFrame->GetWindowOriginInPixels(windowless); + + mPluginWindow->x = origin.x; + mPluginWindow->y = origin.y; + + mPluginWindow->clipRect.left = 0; + mPluginWindow->clipRect.top = 0; + + if (mPluginWindowVisible && mPluginDocumentActiveState) { + mPluginWindow->clipRect.right = mPluginWindow->width; + mPluginWindow->clipRect.bottom = mPluginWindow->height; + } else { + mPluginWindow->clipRect.right = 0; + mPluginWindow->clipRect.bottom = 0; + } + + if (!aSetWindow) + return; + + if (mPluginWindow->x != oldWindow.x || + mPluginWindow->y != oldWindow.y || + mPluginWindow->clipRect.left != oldWindow.clipRect.left || + mPluginWindow->clipRect.top != oldWindow.clipRect.top || + mPluginWindow->clipRect.right != oldWindow.clipRect.right || + mPluginWindow->clipRect.bottom != oldWindow.clipRect.bottom) { + CallSetWindow(); + } +} + +void +nsPluginInstanceOwner::UpdateWindowVisibility(bool aVisible) +{ + mPluginWindowVisible = aVisible; + UpdateWindowPositionAndClipRect(true); +} + +void +nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive) +{ + mPluginDocumentActiveState = aIsActive; + UpdateWindowPositionAndClipRect(true); + +#ifdef MOZ_WIDGET_ANDROID + if (mInstance) { + if (!mPluginDocumentActiveState) + RemovePluginView(); + + mInstance->NotifyOnScreen(mPluginDocumentActiveState); + + // This is, perhaps, incorrect. It is supposed to be sent + // when "the webview has paused or resumed". The side effect + // is that Flash video players pause or resume (if they were + // playing before) based on the value here. I personally think + // we want that on Android when switching to another tab, so + // that's why we call it here. + mInstance->NotifyForeground(mPluginDocumentActiveState); + } +#endif +} +#endif // XP_MACOSX + +NS_IMETHODIMP +nsPluginInstanceOwner::CallSetWindow() +{ + if (mObjectFrame) { + mObjectFrame->CallSetWindow(false); + } else if (mInstance) { + if (UseAsyncRendering()) { + mInstance->AsyncSetWindow(mPluginWindow); + } else { + mInstance->SetWindow(mPluginWindow); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsPluginInstanceOwner::GetContentsScaleFactor(double *result) +{ + NS_ENSURE_ARG_POINTER(result); + double scaleFactor = 1.0; + // On Mac, device pixels need to be translated to (and from) "display pixels" + // for plugins. On other platforms, plugin coordinates are always in device + // pixels. +#if defined(XP_MACOSX) + nsIPresShell* presShell = nsContentUtils::FindPresShellForDocument(mContent->OwnerDoc()); + if (presShell) { + scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/ + presShell->GetPresContext()->DeviceContext()->UnscaledAppUnitsPerDevPixel(); + } +#endif + *result = scaleFactor; + return NS_OK; +} + +void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame) +{ + // Don't do anything if the frame situation hasn't changed. + if (mObjectFrame == aFrame) { + return; + } + + // If we already have a frame that is changing or going away... + if (mObjectFrame) { + // Make sure the old frame isn't holding a reference to us. + mObjectFrame->SetInstanceOwner(nullptr); + } + + // Swap in the new frame (or no frame) + mObjectFrame = aFrame; + + // Set up a new frame + if (mObjectFrame) { + mObjectFrame->SetInstanceOwner(this); + // Can only call PrepForDrawing on an object frame once. Don't do it here unless + // widget creation is complete. Doesn't matter if we actually have a widget. + if (mWidgetCreationComplete) { + mObjectFrame->PrepForDrawing(mWidget); + } + mObjectFrame->FixupWindow(mObjectFrame->GetContentRectRelativeToSelf().Size()); + mObjectFrame->InvalidateFrame(); + + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + const nsIContent* content = aFrame->GetContent(); + if (fm && content) { + mContentFocused = (content == fm->GetFocusedContent()); + } + } +} + +nsObjectFrame* nsPluginInstanceOwner::GetFrame() +{ + return mObjectFrame; +} + +// Little helper function to resolve relative URL in +// |value| for certain inputs of |name| +void nsPluginInstanceOwner::FixUpURLS(const nsString &name, nsAString &value) +{ + if (name.LowerCaseEqualsLiteral("pluginspage")) { + nsCOMPtr baseURI = GetBaseURI(); + nsAutoString newURL; + NS_MakeAbsoluteURI(newURL, value, baseURI); + if (!newURL.IsEmpty()) + value = newURL; + } +} + +NS_IMETHODIMP nsPluginInstanceOwner::PrivateModeChanged(bool aEnabled) +{ + return mInstance ? mInstance->PrivateModeStateChanged(aEnabled) : NS_OK; +} + +already_AddRefed nsPluginInstanceOwner::GetBaseURI() const +{ + if (!mContent) { + return nullptr; + } + return mContent->GetBaseURI(); +} + +// nsPluginDOMContextMenuListener class implementation + +nsPluginDOMContextMenuListener::nsPluginDOMContextMenuListener(nsIContent* aContent) +{ + aContent->AddEventListener(NS_LITERAL_STRING("contextmenu"), this, true); +} + +nsPluginDOMContextMenuListener::~nsPluginDOMContextMenuListener() +{ +} + +NS_IMPL_ISUPPORTS(nsPluginDOMContextMenuListener, + nsIDOMEventListener) + +NS_IMETHODIMP +nsPluginDOMContextMenuListener::HandleEvent(nsIDOMEvent* aEvent) +{ + aEvent->PreventDefault(); // consume event + + return NS_OK; +} + +void nsPluginDOMContextMenuListener::Destroy(nsIContent* aContent) +{ + // Unregister context menu listener + aContent->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), this, true); +}