diff -r 000000000000 -r 6474c204b198 dom/plugins/base/nsNPAPIPluginInstance.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1839 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "mozilla/DebugOnly.h" + +#ifdef MOZ_WIDGET_ANDROID +// For ScreenOrientation.h and Hal.h +#include "base/basictypes.h" +#endif + +#include "prlog.h" +#include "prmem.h" +#include "nscore.h" +#include "prenv.h" + +#include "nsNPAPIPluginInstance.h" +#include "nsNPAPIPlugin.h" +#include "nsNPAPIPluginStreamListener.h" +#include "nsPluginHost.h" +#include "nsPluginLogging.h" +#include "nsContentUtils.h" +#include "nsPluginInstanceOwner.h" + +#include "nsThreadUtils.h" +#include "nsIDOMElement.h" +#include "nsIDocument.h" +#include "nsIDocShell.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScriptContext.h" +#include "nsDirectoryServiceDefs.h" +#include "nsJSNPRuntime.h" +#include "nsPluginStreamListenerPeer.h" +#include "nsSize.h" +#include "nsNetCID.h" +#include "nsIContent.h" +#include "nsVersionComparator.h" +#include "mozilla/Preferences.h" +#include "mozilla/unused.h" +#include "nsILoadContext.h" + +using namespace mozilla; + +#ifdef MOZ_WIDGET_ANDROID +#include "ANPBase.h" +#include +#include "android_npapi.h" +#include "mozilla/Mutex.h" +#include "mozilla/CondVar.h" +#include "AndroidBridge.h" +#include "mozilla/dom/ScreenOrientation.h" +#include "mozilla/Hal.h" +#include "GLContextProvider.h" +#include "GLContext.h" +#include "TexturePoolOGL.h" +#include "GLSharedHandleHelpers.h" + +using namespace mozilla::gl; + +typedef nsNPAPIPluginInstance::VideoInfo VideoInfo; + +class PluginEventRunnable : public nsRunnable +{ +public: + PluginEventRunnable(nsNPAPIPluginInstance* instance, ANPEvent* event) + : mInstance(instance), mEvent(*event), mCanceled(false) {} + + virtual nsresult Run() { + if (mCanceled) + return NS_OK; + + mInstance->HandleEvent(&mEvent, nullptr); + mInstance->PopPostedEvent(this); + return NS_OK; + } + + void Cancel() { mCanceled = true; } +private: + nsNPAPIPluginInstance* mInstance; + ANPEvent mEvent; + bool mCanceled; +}; + +static nsRefPtr sPluginContext = nullptr; + +static bool EnsureGLContext() +{ + if (!sPluginContext) { + gfxIntSize dummySize(16, 16); + sPluginContext = GLContextProvider::CreateOffscreen(dummySize, + GLContext::SurfaceCaps::Any()); + } + + return sPluginContext != nullptr; +} + +class SharedPluginTexture MOZ_FINAL { +public: + NS_INLINE_DECL_REFCOUNTING(SharedPluginTexture) + + SharedPluginTexture() : mLock("SharedPluginTexture.mLock") + { + } + + nsNPAPIPluginInstance::TextureInfo Lock() + { + if (!EnsureGLContext()) { + mTextureInfo.mTexture = 0; + return mTextureInfo; + } + + if (!mTextureInfo.mTexture && sPluginContext->MakeCurrent()) { + sPluginContext->fGenTextures(1, &mTextureInfo.mTexture); + } + + mLock.Lock(); + return mTextureInfo; + } + + void Release(nsNPAPIPluginInstance::TextureInfo& aTextureInfo) + { + mTextureInfo = aTextureInfo; + mLock.Unlock(); + } + + SharedTextureHandle CreateSharedHandle() + { + MutexAutoLock lock(mLock); + + if (!EnsureGLContext()) + return 0; + + if (mTextureInfo.mWidth == 0 || mTextureInfo.mHeight == 0) + return 0; + + SharedTextureHandle handle = + gl::CreateSharedHandle(sPluginContext, + gl::SharedTextureShareType::SameProcess, + (void*)mTextureInfo.mTexture, + gl::SharedTextureBufferType::TextureID); + + // We want forget about this now, so delete the texture. Assigning it to zero + // ensures that we create a new one in Lock() + sPluginContext->fDeleteTextures(1, &mTextureInfo.mTexture); + mTextureInfo.mTexture = 0; + + return handle; + } + +private: + // Private destructor, to discourage deletion outside of Release(): + ~SharedPluginTexture() + { + } + + nsNPAPIPluginInstance::TextureInfo mTextureInfo; + + Mutex mLock; +}; + +static std::map sPluginNPPMap; + +#endif + +using namespace mozilla; +using namespace mozilla::plugins::parent; +using namespace mozilla::layers; + +static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID); + +NS_IMPL_ISUPPORTS0(nsNPAPIPluginInstance) + +nsNPAPIPluginInstance::nsNPAPIPluginInstance() + : + mDrawingModel(kDefaultDrawingModel), +#ifdef MOZ_WIDGET_ANDROID + mANPDrawingModel(0), + mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary), + mWakeLocked(false), + mFullScreen(false), + mInverted(false), +#endif + mRunning(NOT_STARTED), + mWindowless(false), + mTransparent(false), + mCached(false), + mUsesDOMForCursor(false), + mInPluginInitCall(false), + mPlugin(nullptr), + mMIMEType(nullptr), + mOwner(nullptr), + mCurrentPluginEvent(nullptr) +#ifdef MOZ_WIDGET_ANDROID + , mOnScreen(true) +#endif + , mHaveJavaC2PJSObjectQuirk(false) +{ + mNPP.pdata = nullptr; + mNPP.ndata = this; + + PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance ctor: this=%p\n",this)); + +#ifdef MOZ_WIDGET_ANDROID + sPluginNPPMap[&mNPP] = this; +#endif +} + +nsNPAPIPluginInstance::~nsNPAPIPluginInstance() +{ + PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance dtor: this=%p\n",this)); + +#ifdef MOZ_WIDGET_ANDROID + sPluginNPPMap.erase(&mNPP); +#endif + + if (mMIMEType) { + PR_Free((void *)mMIMEType); + mMIMEType = nullptr; + } +} + +uint32_t nsNPAPIPluginInstance::gInUnsafePluginCalls = 0; + +void +nsNPAPIPluginInstance::Destroy() +{ + Stop(); + mPlugin = nullptr; + +#if MOZ_WIDGET_ANDROID + if (mContentSurface) + mContentSurface->SetFrameAvailableCallback(nullptr); + + mContentTexture = nullptr; + mContentSurface = nullptr; + + std::map::iterator it; + for (it = mVideos.begin(); it != mVideos.end(); it++) { + it->second->mSurfaceTexture->SetFrameAvailableCallback(nullptr); + delete it->second; + } + mVideos.clear(); + SetWakeLock(false); +#endif +} + +TimeStamp +nsNPAPIPluginInstance::StopTime() +{ + return mStopTime; +} + +nsresult nsNPAPIPluginInstance::Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const char* aMIMEType) +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::Initialize this=%p\n",this)); + + NS_ENSURE_ARG_POINTER(aPlugin); + NS_ENSURE_ARG_POINTER(aOwner); + + mPlugin = aPlugin; + mOwner = aOwner; + + if (aMIMEType) { + mMIMEType = (char*)PR_Malloc(strlen(aMIMEType) + 1); + if (mMIMEType) { + PL_strcpy(mMIMEType, aMIMEType); + } + } + + return Start(); +} + +nsresult nsNPAPIPluginInstance::Stop() +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::Stop this=%p\n",this)); + + // Make sure the plugin didn't leave popups enabled. + if (mPopupStates.Length() > 0) { + nsCOMPtr window = GetDOMWindow(); + + if (window) { + window->PopPopupControlState(openAbused); + } + } + + if (RUNNING != mRunning) { + return NS_OK; + } + + // clean up all outstanding timers + for (uint32_t i = mTimers.Length(); i > 0; i--) + UnscheduleTimer(mTimers[i - 1]->id); + + // If there's code from this plugin instance on the stack, delay the + // destroy. + if (PluginDestructionGuard::DelayDestroy(this)) { + return NS_OK; + } + + // Make sure we lock while we're writing to mRunning after we've + // started as other threads might be checking that inside a lock. + { + AsyncCallbackAutoLock lock; + mRunning = DESTROYING; + mStopTime = TimeStamp::Now(); + } + + OnPluginDestroy(&mNPP); + + // clean up open streams + while (mStreamListeners.Length() > 0) { + nsRefPtr currentListener(mStreamListeners[0]); + currentListener->CleanUpStream(NPRES_USER_BREAK); + mStreamListeners.RemoveElement(currentListener); + } + + if (!mPlugin || !mPlugin->GetLibrary()) + return NS_ERROR_FAILURE; + + NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs(); + + NPError error = NPERR_GENERIC_ERROR; + if (pluginFunctions->destroy) { + NPSavedData *sdata = 0; + + NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroy)(&mNPP, &sdata), this, + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); + + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("NPP Destroy called: this=%p, npp=%p, return=%d\n", this, &mNPP, error)); + } + mRunning = DESTROYED; + +#if MOZ_WIDGET_ANDROID + for (uint32_t i = 0; i < mPostedEvents.Length(); i++) { + mPostedEvents[i]->Cancel(); + } + + mPostedEvents.Clear(); +#endif + + nsJSNPRuntime::OnPluginDestroy(&mNPP); + + if (error != NPERR_NO_ERROR) + return NS_ERROR_FAILURE; + else + return NS_OK; +} + +already_AddRefed +nsNPAPIPluginInstance::GetDOMWindow() +{ + if (!mOwner) + return nullptr; + + nsRefPtr deathGrip(mOwner); + + nsCOMPtr doc; + mOwner->GetDocument(getter_AddRefs(doc)); + if (!doc) + return nullptr; + + nsRefPtr window = doc->GetWindow(); + + return window.forget(); +} + +nsresult +nsNPAPIPluginInstance::GetTagType(nsPluginTagType *result) +{ + if (!mOwner) { + return NS_ERROR_FAILURE; + } + + return mOwner->GetTagType(result); +} + +nsresult +nsNPAPIPluginInstance::GetAttributes(uint16_t& n, const char*const*& names, + const char*const*& values) +{ + if (!mOwner) { + return NS_ERROR_FAILURE; + } + + return mOwner->GetAttributes(n, names, values); +} + +nsresult +nsNPAPIPluginInstance::GetParameters(uint16_t& n, const char*const*& names, + const char*const*& values) +{ + if (!mOwner) { + return NS_ERROR_FAILURE; + } + + return mOwner->GetParameters(n, names, values); +} + +nsresult +nsNPAPIPluginInstance::GetMode(int32_t *result) +{ + if (mOwner) + return mOwner->GetMode(result); + else + return NS_ERROR_FAILURE; +} + +nsTArray* +nsNPAPIPluginInstance::StreamListeners() +{ + return &mStreamListeners; +} + +nsTArray* +nsNPAPIPluginInstance::FileCachedStreamListeners() +{ + return &mFileCachedStreamListeners; +} + +nsresult +nsNPAPIPluginInstance::Start() +{ + if (mRunning == RUNNING) { + return NS_OK; + } + + PluginDestructionGuard guard(this); + + uint16_t count = 0; + const char* const* names = nullptr; + const char* const* values = nullptr; + nsPluginTagType tagtype; + nsresult rv = GetTagType(&tagtype); + if (NS_SUCCEEDED(rv)) { + // Note: If we failed to get the tag type, we may be a full page plugin, so no arguments + rv = GetAttributes(count, names, values); + NS_ENSURE_SUCCESS(rv, rv); + + // nsPluginTagType_Object or Applet may also have PARAM tags + // Note: The arrays handed back by GetParameters() are + // crafted specially to be directly behind the arrays from GetAttributes() + // with a null entry as a separator. This is for 4.x backwards compatibility! + // see bug 111008 for details + if (tagtype != nsPluginTagType_Embed) { + uint16_t pcount = 0; + const char* const* pnames = nullptr; + const char* const* pvalues = nullptr; + if (NS_SUCCEEDED(GetParameters(pcount, pnames, pvalues))) { + // Android expects an empty string as the separator instead of null +#ifdef MOZ_WIDGET_ANDROID + NS_ASSERTION(PL_strcmp(values[count], "") == 0, "attribute/parameter array not setup correctly for Android NPAPI plugins"); +#else + NS_ASSERTION(!values[count], "attribute/parameter array not setup correctly for NPAPI plugins"); +#endif + if (pcount) + count += ++pcount; // if it's all setup correctly, then all we need is to + // change the count (attrs + PARAM/blank + params) + } + } + } + + int32_t mode; + const char* mimetype; + NPError error = NPERR_GENERIC_ERROR; + + GetMode(&mode); + GetMIMEType(&mimetype); + + CheckJavaC2PJSObjectQuirk(count, names, values); + + // Some older versions of Flash have a bug in them + // that causes the stack to become currupt if we + // pass swliveconnect=1 in the NPP_NewProc arrays. + // See bug 149336 (UNIX), bug 186287 (Mac) + // + // The code below disables the attribute unless + // the environment variable: + // MOZILLA_PLUGIN_DISABLE_FLASH_SWLIVECONNECT_HACK + // is set. + // + // It is okay to disable this attribute because + // back in 4.x, scripting required liveconnect to + // start Java which was slow. Scripting no longer + // requires starting Java and is quick plus controled + // from the browser, so Flash now ignores this attribute. + // + // This code can not be put at the time of creating + // the array because we may need to examine the + // stream header to determine we want Flash. + + static const char flashMimeType[] = "application/x-shockwave-flash"; + static const char blockedParam[] = "swliveconnect"; + if (count && !PL_strcasecmp(mimetype, flashMimeType)) { + static int cachedDisableHack = 0; + if (!cachedDisableHack) { + if (PR_GetEnv("MOZILLA_PLUGIN_DISABLE_FLASH_SWLIVECONNECT_HACK")) + cachedDisableHack = -1; + else + cachedDisableHack = 1; + } + if (cachedDisableHack > 0) { + for (uint16_t i=0; iGetLibrary(); + if (!library) + return NS_ERROR_FAILURE; + + // Mark this instance as running before calling NPP_New because the plugin may + // call other NPAPI functions, like NPN_GetURLNotify, that assume this is set + // before returning. If the plugin returns failure, we'll clear it out below. + mRunning = RUNNING; + + nsresult newResult = library->NPP_New((char*)mimetype, &mNPP, (uint16_t)mode, + count, (char**)names, (char**)values, + nullptr, &error); + mInPluginInitCall = oldVal; + + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("NPP New called: this=%p, npp=%p, mime=%s, mode=%d, argc=%d, return=%d\n", + this, &mNPP, mimetype, mode, count, error)); + + if (NS_FAILED(newResult) || error != NPERR_NO_ERROR) { + mRunning = DESTROYED; + nsJSNPRuntime::OnPluginDestroy(&mNPP); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult nsNPAPIPluginInstance::SetWindow(NPWindow* window) +{ + // NPAPI plugins don't want a SetWindow(nullptr). + if (!window || RUNNING != mRunning) + return NS_OK; + +#if MOZ_WIDGET_GTK + // bug 108347, flash plugin on linux doesn't like window->width <= + // 0, but Java needs wants this call. + if (!nsPluginHost::IsJavaMIMEType(mMIMEType) && window->type == NPWindowTypeWindow && + (window->width <= 0 || window->height <= 0)) { + return NS_OK; + } +#endif + + if (!mPlugin || !mPlugin->GetLibrary()) + return NS_ERROR_FAILURE; + + NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs(); + + if (pluginFunctions->setwindow) { + PluginDestructionGuard guard(this); + + // XXX Turns out that NPPluginWindow and NPWindow are structurally + // identical (on purpose!), so there's no need to make a copy. + + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetWindow (about to call it) this=%p\n",this)); + + bool oldVal = mInPluginInitCall; + mInPluginInitCall = true; + + NPPAutoPusher nppPusher(&mNPP); + + DebugOnly error; + NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setwindow)(&mNPP, (NPWindow*)window), this, + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); + + mInPluginInitCall = oldVal; + + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("NPP SetWindow called: this=%p, [x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d], return=%d\n", + this, window->x, window->y, window->width, window->height, + window->clipRect.top, window->clipRect.bottom, window->clipRect.left, window->clipRect.right, (NPError)error)); + } + return NS_OK; +} + +nsresult +nsNPAPIPluginInstance::NewStreamFromPlugin(const char* type, const char* target, + nsIOutputStream* *result) +{ + nsPluginStreamToFile* stream = new nsPluginStreamToFile(target, mOwner); + if (!stream) + return NS_ERROR_OUT_OF_MEMORY; + + return stream->QueryInterface(kIOutputStreamIID, (void**)result); +} + +nsresult +nsNPAPIPluginInstance::NewStreamListener(const char* aURL, void* notifyData, + nsNPAPIPluginStreamListener** listener) +{ + nsRefPtr sl = new nsNPAPIPluginStreamListener(this, notifyData, aURL); + + mStreamListeners.AppendElement(sl); + + sl.forget(listener); + + return NS_OK; +} + +nsresult nsNPAPIPluginInstance::Print(NPPrint* platformPrint) +{ + NS_ENSURE_TRUE(platformPrint, NS_ERROR_NULL_POINTER); + + PluginDestructionGuard guard(this); + + if (!mPlugin || !mPlugin->GetLibrary()) + return NS_ERROR_FAILURE; + + NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs(); + + NPPrint* thePrint = (NPPrint *)platformPrint; + + // to be compatible with the older SDK versions and to match what + // NPAPI and other browsers do, overwrite |window.type| field with one + // more copy of |platformPrint|. See bug 113264 + uint16_t sdkmajorversion = (pluginFunctions->version & 0xff00)>>8; + uint16_t sdkminorversion = pluginFunctions->version & 0x00ff; + if ((sdkmajorversion == 0) && (sdkminorversion < 11)) { + // Let's copy platformPrint bytes over to where it was supposed to be + // in older versions -- four bytes towards the beginning of the struct + // but we should be careful about possible misalignments + if (sizeof(NPWindowType) >= sizeof(void *)) { + void* source = thePrint->print.embedPrint.platformPrint; + void** destination = (void **)&(thePrint->print.embedPrint.window.type); + *destination = source; + } else { + NS_ERROR("Incompatible OS for assignment"); + } + } + + if (pluginFunctions->print) + NS_TRY_SAFE_CALL_VOID((*pluginFunctions->print)(&mNPP, thePrint), this, + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); + + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("NPP PrintProc called: this=%p, pDC=%p, [x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d]\n", + this, + platformPrint->print.embedPrint.platformPrint, + platformPrint->print.embedPrint.window.x, + platformPrint->print.embedPrint.window.y, + platformPrint->print.embedPrint.window.width, + platformPrint->print.embedPrint.window.height, + platformPrint->print.embedPrint.window.clipRect.top, + platformPrint->print.embedPrint.window.clipRect.bottom, + platformPrint->print.embedPrint.window.clipRect.left, + platformPrint->print.embedPrint.window.clipRect.right)); + + return NS_OK; +} + +nsresult nsNPAPIPluginInstance::HandleEvent(void* event, int16_t* result, + NSPluginCallReentry aSafeToReenterGecko) +{ + if (RUNNING != mRunning) + return NS_OK; + + if (!event) + return NS_ERROR_FAILURE; + + PluginDestructionGuard guard(this); + + if (!mPlugin || !mPlugin->GetLibrary()) + return NS_ERROR_FAILURE; + + NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs(); + + int16_t tmpResult = kNPEventNotHandled; + + if (pluginFunctions->event) { + mCurrentPluginEvent = event; +#if defined(XP_WIN) + NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), this, + aSafeToReenterGecko); +#else + MAIN_THREAD_JNI_REF_GUARD; + tmpResult = (*pluginFunctions->event)(&mNPP, event); +#endif + NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPP HandleEvent called: this=%p, npp=%p, event=%p, return=%d\n", + this, &mNPP, event, tmpResult)); + + if (result) + *result = tmpResult; + mCurrentPluginEvent = nullptr; + } + + return NS_OK; +} + +nsresult nsNPAPIPluginInstance::GetValueFromPlugin(NPPVariable variable, void* value) +{ + if (!mPlugin || !mPlugin->GetLibrary()) + return NS_ERROR_FAILURE; + + NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs(); + + nsresult rv = NS_ERROR_FAILURE; + + if (pluginFunctions->getvalue && RUNNING == mRunning) { + PluginDestructionGuard guard(this); + + NPError pluginError = NPERR_GENERIC_ERROR; + NS_TRY_SAFE_CALL_RETURN(pluginError, (*pluginFunctions->getvalue)(&mNPP, variable, value), this, + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); + NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("NPP GetValue called: this=%p, npp=%p, var=%d, value=%d, return=%d\n", + this, &mNPP, variable, value, pluginError)); + + if (pluginError == NPERR_NO_ERROR) { + rv = NS_OK; + } + } + + return rv; +} + +nsNPAPIPlugin* nsNPAPIPluginInstance::GetPlugin() +{ + return mPlugin; +} + +nsresult nsNPAPIPluginInstance::GetNPP(NPP* aNPP) +{ + if (aNPP) + *aNPP = &mNPP; + else + return NS_ERROR_NULL_POINTER; + + return NS_OK; +} + +NPError nsNPAPIPluginInstance::SetWindowless(bool aWindowless) +{ + mWindowless = aWindowless; + + if (mMIMEType) { + // bug 558434 - Prior to 3.6.4, we assumed windowless was transparent. + // Silverlight apparently relied on this quirk, so we default to + // transparent unless they specify otherwise after setting the windowless + // property. (Last tested version: sl 4.0). + // Changes to this code should be matched with changes in + // PluginInstanceChild::InitQuirksMode. + NS_NAMED_LITERAL_CSTRING(silverlight, "application/x-silverlight"); + if (!PL_strncasecmp(mMIMEType, silverlight.get(), silverlight.Length())) { + mTransparent = true; + } + } + + return NPERR_NO_ERROR; +} + +NPError nsNPAPIPluginInstance::SetTransparent(bool aTransparent) +{ + mTransparent = aTransparent; + return NPERR_NO_ERROR; +} + +NPError nsNPAPIPluginInstance::SetUsesDOMForCursor(bool aUsesDOMForCursor) +{ + mUsesDOMForCursor = aUsesDOMForCursor; + return NPERR_NO_ERROR; +} + +bool +nsNPAPIPluginInstance::UsesDOMForCursor() +{ + return mUsesDOMForCursor; +} + +void nsNPAPIPluginInstance::SetDrawingModel(NPDrawingModel aModel) +{ + mDrawingModel = aModel; +} + +void nsNPAPIPluginInstance::RedrawPlugin() +{ + mOwner->RedrawPlugin(); +} + +#if defined(XP_MACOSX) +void nsNPAPIPluginInstance::SetEventModel(NPEventModel aModel) +{ + // the event model needs to be set for the object frame immediately + if (!mOwner) { + NS_WARNING("Trying to set event model without a plugin instance owner!"); + return; + } + + mOwner->SetEventModel(aModel); +} +#endif + +#if defined(MOZ_WIDGET_ANDROID) + +static void SendLifecycleEvent(nsNPAPIPluginInstance* aInstance, uint32_t aAction) +{ + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kLifecycle_ANPEventType; + event.data.lifecycle.action = aAction; + aInstance->HandleEvent(&event, nullptr); +} + +void nsNPAPIPluginInstance::NotifyForeground(bool aForeground) +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetForeground this=%p\n foreground=%d",this, aForeground)); + if (RUNNING != mRunning) + return; + + SendLifecycleEvent(this, aForeground ? kResume_ANPLifecycleAction : kPause_ANPLifecycleAction); +} + +void nsNPAPIPluginInstance::NotifyOnScreen(bool aOnScreen) +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetOnScreen this=%p\n onScreen=%d",this, aOnScreen)); + if (RUNNING != mRunning || mOnScreen == aOnScreen) + return; + + mOnScreen = aOnScreen; + SendLifecycleEvent(this, aOnScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction); +} + +void nsNPAPIPluginInstance::MemoryPressure() +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::MemoryPressure this=%p\n",this)); + if (RUNNING != mRunning) + return; + + SendLifecycleEvent(this, kFreeMemory_ANPLifecycleAction); +} + +void nsNPAPIPluginInstance::NotifyFullScreen(bool aFullScreen) +{ + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::NotifyFullScreen this=%p\n",this)); + + if (RUNNING != mRunning || mFullScreen == aFullScreen) + return; + + mFullScreen = aFullScreen; + SendLifecycleEvent(this, mFullScreen ? kEnterFullScreen_ANPLifecycleAction : kExitFullScreen_ANPLifecycleAction); + + if (mFullScreen && mFullScreenOrientation != dom::eScreenOrientation_None) { + mozilla::widget::android::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation); + } +} + +void nsNPAPIPluginInstance::NotifySize(nsIntSize size) +{ + if (kOpenGL_ANPDrawingModel != GetANPDrawingModel() || + size == mCurrentSize) + return; + + mCurrentSize = size; + + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kDraw_ANPEventType; + event.data.draw.model = kOpenGL_ANPDrawingModel; + event.data.draw.data.surfaceSize.width = size.width; + event.data.draw.data.surfaceSize.height = size.height; + + HandleEvent(&event, nullptr); +} + +void nsNPAPIPluginInstance::SetANPDrawingModel(uint32_t aModel) +{ + mANPDrawingModel = aModel; +} + +void* nsNPAPIPluginInstance::GetJavaSurface() +{ + void* surface = nullptr; + nsresult rv = GetValueFromPlugin(kJavaSurface_ANPGetValue, &surface); + if (NS_FAILED(rv)) + return nullptr; + + return surface; +} + +void nsNPAPIPluginInstance::PostEvent(void* event) +{ + PluginEventRunnable *r = new PluginEventRunnable(this, (ANPEvent*)event); + mPostedEvents.AppendElement(nsRefPtr(r)); + + NS_DispatchToMainThread(r); +} + +void nsNPAPIPluginInstance::SetFullScreenOrientation(uint32_t orientation) +{ + if (mFullScreenOrientation == orientation) + return; + + uint32_t oldOrientation = mFullScreenOrientation; + mFullScreenOrientation = orientation; + + if (mFullScreen) { + // We're already fullscreen so immediately apply the orientation change + + if (mFullScreenOrientation != dom::eScreenOrientation_None) { + mozilla::widget::android::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation); + } else if (oldOrientation != dom::eScreenOrientation_None) { + // We applied an orientation when we entered fullscreen, but + // we don't want it anymore + mozilla::widget::android::GeckoAppShell::UnlockScreenOrientation(); + } + } +} + +void nsNPAPIPluginInstance::PopPostedEvent(PluginEventRunnable* r) +{ + mPostedEvents.RemoveElement(r); +} + +void nsNPAPIPluginInstance::SetWakeLock(bool aLocked) +{ + if (aLocked == mWakeLocked) + return; + + mWakeLocked = aLocked; + hal::ModifyWakeLock(NS_LITERAL_STRING("nsNPAPIPluginInstance"), + mWakeLocked ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE, + hal::WAKE_LOCK_NO_CHANGE); +} + +void nsNPAPIPluginInstance::EnsureSharedTexture() +{ + if (!mContentTexture) + mContentTexture = new SharedPluginTexture(); +} + +GLContext* nsNPAPIPluginInstance::GLContext() +{ + if (!EnsureGLContext()) + return nullptr; + + return sPluginContext; +} + +nsNPAPIPluginInstance::TextureInfo nsNPAPIPluginInstance::LockContentTexture() +{ + EnsureSharedTexture(); + return mContentTexture->Lock(); +} + +void nsNPAPIPluginInstance::ReleaseContentTexture(nsNPAPIPluginInstance::TextureInfo& aTextureInfo) +{ + EnsureSharedTexture(); + mContentTexture->Release(aTextureInfo); +} + +nsSurfaceTexture* nsNPAPIPluginInstance::CreateSurfaceTexture() +{ + if (!EnsureGLContext()) + return nullptr; + + GLuint texture = TexturePoolOGL::AcquireTexture(); + if (!texture) + return nullptr; + + nsSurfaceTexture* surface = nsSurfaceTexture::Create(texture); + if (!surface) + return nullptr; + + nsCOMPtr frameCallback = NS_NewRunnableMethod(this, &nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable); + surface->SetFrameAvailableCallback(frameCallback); + return surface; +} + +void nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable() +{ + if (mRunning == RUNNING && mOwner) + AndroidBridge::Bridge()->ScheduleComposite(); +} + +void* nsNPAPIPluginInstance::AcquireContentWindow() +{ + if (!mContentSurface) { + mContentSurface = CreateSurfaceTexture(); + + if (!mContentSurface) + return nullptr; + } + + return mContentSurface->GetNativeWindow(); +} + +SharedTextureHandle nsNPAPIPluginInstance::CreateSharedHandle() +{ + if (mContentTexture) { + return mContentTexture->CreateSharedHandle(); + } else if (mContentSurface) { + EnsureGLContext(); + return gl::CreateSharedHandle(sPluginContext, + gl::SharedTextureShareType::SameProcess, + mContentSurface, + gl::SharedTextureBufferType::SurfaceTexture); + } else return 0; +} + +void* nsNPAPIPluginInstance::AcquireVideoWindow() +{ + nsSurfaceTexture* surface = CreateSurfaceTexture(); + if (!surface) + return nullptr; + + VideoInfo* info = new VideoInfo(surface); + + void* window = info->mSurfaceTexture->GetNativeWindow(); + mVideos.insert(std::pair(window, info)); + + return window; +} + +void nsNPAPIPluginInstance::ReleaseVideoWindow(void* window) +{ + std::map::iterator it = mVideos.find(window); + if (it == mVideos.end()) + return; + + delete it->second; + mVideos.erase(window); +} + +void nsNPAPIPluginInstance::SetVideoDimensions(void* window, gfxRect aDimensions) +{ + std::map::iterator it; + + it = mVideos.find(window); + if (it == mVideos.end()) + return; + + it->second->mDimensions = aDimensions; +} + +void nsNPAPIPluginInstance::GetVideos(nsTArray& aVideos) +{ + std::map::iterator it; + for (it = mVideos.begin(); it != mVideos.end(); it++) + aVideos.AppendElement(it->second); +} + +void nsNPAPIPluginInstance::SetInverted(bool aInverted) +{ + if (aInverted == mInverted) + return; + + mInverted = aInverted; +} + +nsNPAPIPluginInstance* nsNPAPIPluginInstance::GetFromNPP(NPP npp) +{ + std::map::iterator it; + + it = sPluginNPPMap.find(npp); + if (it == sPluginNPPMap.end()) + return nullptr; + + return it->second; +} + +#endif + +nsresult nsNPAPIPluginInstance::GetDrawingModel(int32_t* aModel) +{ +#if defined(XP_MACOSX) + *aModel = (int32_t)mDrawingModel; + return NS_OK; +#else + return NS_ERROR_FAILURE; +#endif +} + +nsresult nsNPAPIPluginInstance::IsRemoteDrawingCoreAnimation(bool* aDrawing) +{ +#ifdef XP_MACOSX + if (!mPlugin) + return NS_ERROR_FAILURE; + + PluginLibrary* library = mPlugin->GetLibrary(); + if (!library) + return NS_ERROR_FAILURE; + + return library->IsRemoteDrawingCoreAnimation(&mNPP, aDrawing); +#else + return NS_ERROR_FAILURE; +#endif +} + +nsresult nsNPAPIPluginInstance::ContentsScaleFactorChanged(double aContentsScaleFactor) +{ +#ifdef XP_MACOSX + if (!mPlugin) + return NS_ERROR_FAILURE; + + PluginLibrary* library = mPlugin->GetLibrary(); + if (!library) + return NS_ERROR_FAILURE; + + // We only need to call this if the plugin is running OOP. + if (!library->IsOOP()) + return NS_OK; + + return library->ContentsScaleFactorChanged(&mNPP, aContentsScaleFactor); +#else + return NS_ERROR_FAILURE; +#endif +} + +nsresult +nsNPAPIPluginInstance::GetJSObject(JSContext *cx, JSObject** outObject) +{ + if (mHaveJavaC2PJSObjectQuirk) { + return NS_ERROR_FAILURE; + } + + NPObject *npobj = nullptr; + nsresult rv = GetValueFromPlugin(NPPVpluginScriptableNPObject, &npobj); + if (NS_FAILED(rv) || !npobj) + return NS_ERROR_FAILURE; + + *outObject = nsNPObjWrapper::GetNewOrUsed(&mNPP, cx, npobj); + + _releaseobject(npobj); + + return NS_OK; +} + +void +nsNPAPIPluginInstance::SetCached(bool aCache) +{ + mCached = aCache; +} + +bool +nsNPAPIPluginInstance::ShouldCache() +{ + return mCached; +} + +nsresult +nsNPAPIPluginInstance::IsWindowless(bool* isWindowless) +{ +#ifdef MOZ_WIDGET_ANDROID + // On android, pre-honeycomb, all plugins are treated as windowless. + *isWindowless = true; +#else + *isWindowless = mWindowless; +#endif + return NS_OK; +} + +class MOZ_STACK_CLASS AutoPluginLibraryCall +{ +public: + AutoPluginLibraryCall(nsNPAPIPluginInstance* aThis) + : mThis(aThis), mGuard(aThis), mLibrary(nullptr) + { + nsNPAPIPlugin* plugin = mThis->GetPlugin(); + if (plugin) + mLibrary = plugin->GetLibrary(); + } + operator bool() { return !!mLibrary; } + PluginLibrary* operator->() { return mLibrary; } + +private: + nsNPAPIPluginInstance* mThis; + PluginDestructionGuard mGuard; + PluginLibrary* mLibrary; +}; + +nsresult +nsNPAPIPluginInstance::AsyncSetWindow(NPWindow* window) +{ + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) + return NS_ERROR_FAILURE; + + return library->AsyncSetWindow(&mNPP, window); +} + +nsresult +nsNPAPIPluginInstance::GetImageContainer(ImageContainer**aContainer) +{ + *aContainer = nullptr; + + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + return !library ? NS_ERROR_FAILURE : library->GetImageContainer(&mNPP, aContainer); +} + +nsresult +nsNPAPIPluginInstance::GetImageSize(nsIntSize* aSize) +{ + *aSize = nsIntSize(0, 0); + + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + return !library ? NS_ERROR_FAILURE : library->GetImageSize(&mNPP, aSize); +} + +nsresult +nsNPAPIPluginInstance::NotifyPainted(void) +{ + NS_NOTREACHED("Dead code, shouldn't be called."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +nsNPAPIPluginInstance::GetIsOOP(bool* aIsAsync) +{ + AutoPluginLibraryCall library(this); + if (!library) + return NS_ERROR_FAILURE; + + *aIsAsync = library->IsOOP(); + return NS_OK; +} + +nsresult +nsNPAPIPluginInstance::SetBackgroundUnknown() +{ + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) + return NS_ERROR_FAILURE; + + return library->SetBackgroundUnknown(&mNPP); +} + +nsresult +nsNPAPIPluginInstance::BeginUpdateBackground(nsIntRect* aRect, + gfxContext** aContext) +{ + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) + return NS_ERROR_FAILURE; + + return library->BeginUpdateBackground(&mNPP, *aRect, aContext); +} + +nsresult +nsNPAPIPluginInstance::EndUpdateBackground(gfxContext* aContext, + nsIntRect* aRect) +{ + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) + return NS_ERROR_FAILURE; + + return library->EndUpdateBackground(&mNPP, aContext, *aRect); +} + +nsresult +nsNPAPIPluginInstance::IsTransparent(bool* isTransparent) +{ + *isTransparent = mTransparent; + return NS_OK; +} + +nsresult +nsNPAPIPluginInstance::GetFormValue(nsAString& aValue) +{ + aValue.Truncate(); + + char *value = nullptr; + nsresult rv = GetValueFromPlugin(NPPVformValue, &value); + if (NS_FAILED(rv) || !value) + return NS_ERROR_FAILURE; + + CopyUTF8toUTF16(value, aValue); + + // NPPVformValue allocates with NPN_MemAlloc(), which uses + // nsMemory. + nsMemory::Free(value); + + return NS_OK; +} + +nsresult +nsNPAPIPluginInstance::PushPopupsEnabledState(bool aEnabled) +{ + nsCOMPtr window = GetDOMWindow(); + if (!window) + return NS_ERROR_FAILURE; + + PopupControlState oldState = + window->PushPopupControlState(aEnabled ? openAllowed : openAbused, + true); + + if (!mPopupStates.AppendElement(oldState)) { + // Appending to our state stack failed, pop what we just pushed. + window->PopPopupControlState(oldState); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +nsNPAPIPluginInstance::PopPopupsEnabledState() +{ + int32_t last = mPopupStates.Length() - 1; + + if (last < 0) { + // Nothing to pop. + return NS_OK; + } + + nsCOMPtr window = GetDOMWindow(); + if (!window) + return NS_ERROR_FAILURE; + + PopupControlState &oldState = mPopupStates[last]; + + window->PopPopupControlState(oldState); + + mPopupStates.RemoveElementAt(last); + + return NS_OK; +} + +nsresult +nsNPAPIPluginInstance::GetPluginAPIVersion(uint16_t* version) +{ + NS_ENSURE_ARG_POINTER(version); + + if (!mPlugin) + return NS_ERROR_FAILURE; + + if (!mPlugin->GetLibrary()) + return NS_ERROR_FAILURE; + + NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs(); + + *version = pluginFunctions->version; + + return NS_OK; +} + +nsresult +nsNPAPIPluginInstance::PrivateModeStateChanged(bool enabled) +{ + if (RUNNING != mRunning) + return NS_OK; + + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance informing plugin of private mode state change this=%p\n",this)); + + if (!mPlugin || !mPlugin->GetLibrary()) + return NS_ERROR_FAILURE; + + NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs(); + + if (!pluginFunctions->setvalue) + return NS_ERROR_FAILURE; + + PluginDestructionGuard guard(this); + + NPError error; + NPBool value = static_cast(enabled); + NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value), this, + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); + return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE; +} + +nsresult +nsNPAPIPluginInstance::IsPrivateBrowsing(bool* aEnabled) +{ + if (!mOwner) + return NS_ERROR_FAILURE; + + nsCOMPtr doc; + mOwner->GetDocument(getter_AddRefs(doc)); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + + nsCOMPtr domwindow = doc->GetWindow(); + NS_ENSURE_TRUE(domwindow, NS_ERROR_FAILURE); + + nsCOMPtr docShell = domwindow->GetDocShell(); + nsCOMPtr loadContext = do_QueryInterface(docShell); + *aEnabled = (loadContext && loadContext->UsePrivateBrowsing()); + return NS_OK; +} + +static void +PluginTimerCallback(nsITimer *aTimer, void *aClosure) +{ + nsNPAPITimer* t = (nsNPAPITimer*)aClosure; + NPP npp = t->npp; + uint32_t id = t->id; + + PLUGIN_LOG(PLUGIN_LOG_NOISY, ("nsNPAPIPluginInstance running plugin timer callback this=%p\n", npp->ndata)); + + MAIN_THREAD_JNI_REF_GUARD; + // Some plugins (Flash on Android) calls unscheduletimer + // from this callback. + t->inCallback = true; + (*(t->callback))(npp, id); + t->inCallback = false; + + // Make sure we still have an instance and the timer is still alive + // after the callback. + nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata; + if (!inst || !inst->TimerWithID(id, nullptr)) + return; + + // use UnscheduleTimer to clean up if this is a one-shot timer + uint32_t timerType; + t->timer->GetType(&timerType); + if (t->needUnschedule || timerType == nsITimer::TYPE_ONE_SHOT) + inst->UnscheduleTimer(id); +} + +nsNPAPITimer* +nsNPAPIPluginInstance::TimerWithID(uint32_t id, uint32_t* index) +{ + uint32_t len = mTimers.Length(); + for (uint32_t i = 0; i < len; i++) { + if (mTimers[i]->id == id) { + if (index) + *index = i; + return mTimers[i]; + } + } + return nullptr; +} + +uint32_t +nsNPAPIPluginInstance::ScheduleTimer(uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)) +{ + if (RUNNING != mRunning) + return 0; + + nsNPAPITimer *newTimer = new nsNPAPITimer(); + + newTimer->inCallback = newTimer->needUnschedule = false; + newTimer->npp = &mNPP; + + // generate ID that is unique to this instance + uint32_t uniqueID = mTimers.Length(); + while ((uniqueID == 0) || TimerWithID(uniqueID, nullptr)) + uniqueID++; + newTimer->id = uniqueID; + + // create new xpcom timer, scheduled correctly + nsresult rv; + nsCOMPtr xpcomTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + delete newTimer; + return 0; + } + const short timerType = (repeat ? (short)nsITimer::TYPE_REPEATING_SLACK : (short)nsITimer::TYPE_ONE_SHOT); + xpcomTimer->InitWithFuncCallback(PluginTimerCallback, newTimer, interval, timerType); + newTimer->timer = xpcomTimer; + + // save callback function + newTimer->callback = timerFunc; + + // add timer to timers array + mTimers.AppendElement(newTimer); + + return newTimer->id; +} + +void +nsNPAPIPluginInstance::UnscheduleTimer(uint32_t timerID) +{ + // find the timer struct by ID + uint32_t index; + nsNPAPITimer* t = TimerWithID(timerID, &index); + if (!t) + return; + + if (t->inCallback) { + t->needUnschedule = true; + return; + } + + // cancel the timer + t->timer->Cancel(); + + // remove timer struct from array + mTimers.RemoveElementAt(index); + + // delete timer + delete t; +} + +// Show the context menu at the location for the current event. +// This can only be called from within an NPP_SendEvent call. +NPError +nsNPAPIPluginInstance::PopUpContextMenu(NPMenu* menu) +{ + if (mOwner && mCurrentPluginEvent) + return mOwner->ShowNativeContextMenu(menu, mCurrentPluginEvent); + + return NPERR_GENERIC_ERROR; +} + +NPBool +nsNPAPIPluginInstance::ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, + double *destX, double *destY, NPCoordinateSpace destSpace) +{ + if (mOwner) + return mOwner->ConvertPoint(sourceX, sourceY, sourceSpace, destX, destY, destSpace); + + return false; +} + +nsresult +nsNPAPIPluginInstance::GetDOMElement(nsIDOMElement* *result) +{ + if (!mOwner) { + *result = nullptr; + return NS_ERROR_FAILURE; + } + + return mOwner->GetDOMElement(result); +} + +nsresult +nsNPAPIPluginInstance::InvalidateRect(NPRect *invalidRect) +{ + if (RUNNING != mRunning) + return NS_OK; + + if (!mOwner) + return NS_ERROR_FAILURE; + + return mOwner->InvalidateRect(invalidRect); +} + +nsresult +nsNPAPIPluginInstance::InvalidateRegion(NPRegion invalidRegion) +{ + if (RUNNING != mRunning) + return NS_OK; + + if (!mOwner) + return NS_ERROR_FAILURE; + + return mOwner->InvalidateRegion(invalidRegion); +} + +nsresult +nsNPAPIPluginInstance::GetMIMEType(const char* *result) +{ + if (!mMIMEType) + *result = ""; + else + *result = mMIMEType; + + return NS_OK; +} + +nsresult +nsNPAPIPluginInstance::GetJSContext(JSContext* *outContext) +{ + if (!mOwner) + return NS_ERROR_FAILURE; + + nsRefPtr deathGrip(mOwner); + + *outContext = nullptr; + nsCOMPtr document; + + nsresult rv = mOwner->GetDocument(getter_AddRefs(document)); + + if (NS_SUCCEEDED(rv) && document) { + nsCOMPtr global = + do_QueryInterface(document->GetWindow()); + + if (global) { + nsIScriptContext *context = global->GetContext(); + + if (context) { + *outContext = context->GetNativeContext(); + } + } + } + + return rv; +} + +nsPluginInstanceOwner* +nsNPAPIPluginInstance::GetOwner() +{ + return mOwner; +} + +void +nsNPAPIPluginInstance::SetOwner(nsPluginInstanceOwner *aOwner) +{ + mOwner = aOwner; +} + +nsresult +nsNPAPIPluginInstance::ShowStatus(const char* message) +{ + if (mOwner) + return mOwner->ShowStatus(message); + + return NS_ERROR_FAILURE; +} + +nsresult +nsNPAPIPluginInstance::AsyncSetWindow(NPWindow& window) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +void +nsNPAPIPluginInstance::URLRedirectResponse(void* notifyData, NPBool allow) +{ + if (!notifyData) { + return; + } + + uint32_t listenerCount = mStreamListeners.Length(); + for (uint32_t i = 0; i < listenerCount; i++) { + nsNPAPIPluginStreamListener* currentListener = mStreamListeners[i]; + if (currentListener->GetNotifyData() == notifyData) { + currentListener->URLRedirectResponse(allow); + } + } +} + +NPError +nsNPAPIPluginInstance::InitAsyncSurface(NPSize *size, NPImageFormat format, + void *initData, NPAsyncSurface *surface) +{ + if (mOwner) + return mOwner->InitAsyncSurface(size, format, initData, surface); + + return NPERR_GENERIC_ERROR; +} + +NPError +nsNPAPIPluginInstance::FinalizeAsyncSurface(NPAsyncSurface *surface) +{ + if (mOwner) + return mOwner->FinalizeAsyncSurface(surface); + + return NPERR_GENERIC_ERROR; +} + +void +nsNPAPIPluginInstance::SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed) +{ + if (mOwner) + mOwner->SetCurrentAsyncSurface(surface, changed); +} + +class CarbonEventModelFailureEvent : public nsRunnable { +public: + nsCOMPtr mContent; + + CarbonEventModelFailureEvent(nsIContent* aContent) + : mContent(aContent) + {} + + ~CarbonEventModelFailureEvent() {} + + NS_IMETHOD Run(); +}; + +NS_IMETHODIMP +CarbonEventModelFailureEvent::Run() +{ + nsString type = NS_LITERAL_STRING("npapi-carbon-event-model-failure"); + nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent, + type, true, true); + return NS_OK; +} + +void +nsNPAPIPluginInstance::CarbonNPAPIFailure() +{ + nsCOMPtr element; + GetDOMElement(getter_AddRefs(element)); + if (!element) { + return; + } + + nsCOMPtr content(do_QueryInterface(element)); + if (!content) { + return; + } + + nsCOMPtr e = new CarbonEventModelFailureEvent(content); + nsresult rv = NS_DispatchToCurrentThread(e); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to dispatch CarbonEventModelFailureEvent."); + } +} + +static bool +GetJavaVersionFromMimetype(nsPluginTag* pluginTag, nsCString& version) +{ + for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); ++i) { + nsCString type = pluginTag->mMimeTypes[i]; + nsAutoCString jpi("application/x-java-applet;jpi-version="); + + int32_t idx = type.Find(jpi, false, 0, -1); + if (idx != 0) { + continue; + } + + type.Cut(0, jpi.Length()); + if (type.IsEmpty()) { + continue; + } + + type.ReplaceChar('_', '.'); + version = type; + return true; + } + + return false; +} + +void +nsNPAPIPluginInstance::CheckJavaC2PJSObjectQuirk(uint16_t paramCount, + const char* const* paramNames, + const char* const* paramValues) +{ + if (!mMIMEType || !mPlugin) { + return; + } + + nsPluginTagType tagtype; + nsresult rv = GetTagType(&tagtype); + if (NS_FAILED(rv) || + (tagtype != nsPluginTagType_Applet)) { + return; + } + + nsRefPtr pluginHost = nsPluginHost::GetInst(); + if (!pluginHost) { + return; + } + + nsPluginTag* pluginTag = pluginHost->TagForPlugin(mPlugin); + if (!pluginTag || + !pluginTag->mIsJavaPlugin) { + return; + } + + // check the params for "code" being present and non-empty + bool haveCodeParam = false; + bool isCodeParamEmpty = true; + + for (uint16_t i = paramCount; i > 0; --i) { + if (PL_strcasecmp(paramNames[i - 1], "code") == 0) { + haveCodeParam = true; + if (strlen(paramValues[i - 1]) > 0) { + isCodeParamEmpty = false; + } + break; + } + } + + // Due to the Java version being specified inconsistently across platforms + // check the version via the mimetype for choosing specific Java versions + nsCString javaVersion; + if (!GetJavaVersionFromMimetype(pluginTag, javaVersion)) { + return; + } + + mozilla::Version version = javaVersion.get(); + + if (version >= "1.7.0.4") { + return; + } + + if (!haveCodeParam && version >= "1.6.0.34" && version < "1.7") { + return; + } + + if (haveCodeParam && !isCodeParamEmpty) { + return; + } + + mHaveJavaC2PJSObjectQuirk = true; +} + +double +nsNPAPIPluginInstance::GetContentsScaleFactor() +{ + double scaleFactor = 1.0; + if (mOwner) { + mOwner->GetContentsScaleFactor(&scaleFactor); + } + return scaleFactor; +}