diff options
Diffstat (limited to 'dom/plugins/base/nsNPAPIPluginInstance.cpp')
-rw-r--r-- | dom/plugins/base/nsNPAPIPluginInstance.cpp | 1203 |
1 files changed, 1203 insertions, 0 deletions
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp new file mode 100644 index 0000000000..a1462f66f5 --- /dev/null +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -0,0 +1,1203 @@ +/* -*- 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" + +#include "mozilla/Logging.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 "mozilla/dom/Document.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" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLObjectElementBinding.h" +#include "AudioChannelService.h" +#include "GeckoProfiler.h" + +using namespace mozilla; +using namespace mozilla::dom; + +using namespace mozilla; +using namespace mozilla::plugins::parent; +using namespace mozilla::layers; + +NS_IMPL_ISUPPORTS(nsNPAPIPluginInstance, nsIAudioChannelAgentCallback) + +nsNPAPIPluginInstance::nsNPAPIPluginInstance() + : mDrawingModel(kDefaultDrawingModel), + mRunning(NOT_STARTED), + mWindowless(false), + mTransparent(false), + mCached(false), + mUsesDOMForCursor(false), + mInPluginInitCall(false), + mPlugin(nullptr), + mMIMEType(nullptr), + mOwner(nullptr) +#ifdef XP_MACOSX + , + mCurrentPluginEvent(nullptr) +#endif + , + mCachedParamLength(0), + mCachedParamNames(nullptr), + mCachedParamValues(nullptr) { + mNPP.pdata = nullptr; + mNPP.ndata = this; + + PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance ctor: this=%p\n", this)); +} + +nsNPAPIPluginInstance::~nsNPAPIPluginInstance() { + PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance dtor: this=%p\n", this)); + + if (mMIMEType) { + free(mMIMEType); + mMIMEType = nullptr; + } + + if (!mCachedParamValues || !mCachedParamNames) { + return; + } + MOZ_ASSERT(mCachedParamValues && mCachedParamNames); + + for (uint32_t i = 0; i < mCachedParamLength; i++) { + if (mCachedParamNames[i]) { + free(mCachedParamNames[i]); + mCachedParamNames[i] = nullptr; + } + if (mCachedParamValues[i]) { + free(mCachedParamValues[i]); + mCachedParamValues[i] = nullptr; + } + } + + free(mCachedParamNames); + mCachedParamNames = nullptr; + + free(mCachedParamValues); + mCachedParamValues = nullptr; +} + +uint32_t nsNPAPIPluginInstance::gInUnsafePluginCalls = 0; + +void nsNPAPIPluginInstance::Destroy() { + Stop(); + mPlugin = nullptr; + mAudioChannelAgent = nullptr; +} + +TimeStamp nsNPAPIPluginInstance::StopTime() { return mStopTime; } + +nsresult nsNPAPIPluginInstance::Initialize(nsNPAPIPlugin* aPlugin, + nsPluginInstanceOwner* aOwner, + const nsACString& aMIMEType) { + AUTO_PROFILER_LABEL("nsNPAPIPlugin::Initialize", OTHER); + 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.IsEmpty()) { + mMIMEType = ToNewCString(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) { + PopupBlocker::PopPopupControlState(PopupBlocker::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; + } + + mRunning = DESTROYING; + mStopTime = TimeStamp::Now(); + + // clean up open streams + while (mStreamListeners.Length() > 0) { + RefPtr<nsNPAPIPluginStreamListener> 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; + + nsJSNPRuntime::OnPluginDestroy(&mNPP); + + if (error != NPERR_NO_ERROR) + return NS_ERROR_FAILURE; + else + return NS_OK; +} + +already_AddRefed<nsPIDOMWindowOuter> nsNPAPIPluginInstance::GetDOMWindow() { + if (!mOwner) return nullptr; + + RefPtr<nsPluginInstanceOwner> kungFuDeathGrip(mOwner); + + nsCOMPtr<Document> doc; + kungFuDeathGrip->GetDocument(getter_AddRefs(doc)); + if (!doc) return nullptr; + + RefPtr<nsPIDOMWindowOuter> window = doc->GetWindow(); + + return window.forget(); +} + +nsresult nsNPAPIPluginInstance::GetTagType(nsPluginTagType* result) { + if (!mOwner) { + return NS_ERROR_FAILURE; + } + + return mOwner->GetTagType(result); +} + +nsTArray<nsNPAPIPluginStreamListener*>* +nsNPAPIPluginInstance::StreamListeners() { + return &mStreamListeners; +} + +nsTArray<nsPluginStreamListenerPeer*>* +nsNPAPIPluginInstance::FileCachedStreamListeners() { + return &mFileCachedStreamListeners; +} + +nsresult nsNPAPIPluginInstance::Start() { + if (mRunning == RUNNING) { + return NS_OK; + } + + if (!mOwner) { + MOZ_ASSERT(false, "Should not be calling Start() on unowned plugin."); + return NS_ERROR_FAILURE; + } + + PluginDestructionGuard guard(this); + + nsTArray<MozPluginParameter> attributes; + nsTArray<MozPluginParameter> params; + + nsPluginTagType tagtype; + nsresult rv = GetTagType(&tagtype); + if (NS_SUCCEEDED(rv)) { + mOwner->GetAttributes(attributes); + mOwner->GetParameters(params); + } else { + MOZ_ASSERT(false, "Failed to get tag type."); + } + + mCachedParamLength = attributes.Length() + 1 + params.Length(); + + // We add an extra entry "PARAM" as a separator between the attribute + // and param values, but we don't count it if there are no <param> entries. + // Legacy behavior quirk. + uint32_t quirkParamLength = + params.Length() ? mCachedParamLength : attributes.Length(); + + mCachedParamNames = (char**)moz_xmalloc(sizeof(char*) * mCachedParamLength); + mCachedParamValues = (char**)moz_xmalloc(sizeof(char*) * mCachedParamLength); + + for (uint32_t i = 0; i < attributes.Length(); i++) { + mCachedParamNames[i] = ToNewUTF8String(attributes[i].mName); + mCachedParamValues[i] = ToNewUTF8String(attributes[i].mValue); + } + + mCachedParamNames[attributes.Length()] = ToNewUTF8String(u"PARAM"_ns); + mCachedParamValues[attributes.Length()] = nullptr; + + for (uint32_t i = 0, pos = attributes.Length() + 1; i < params.Length(); + i++) { + mCachedParamNames[pos] = ToNewUTF8String(params[i].mName); + mCachedParamValues[pos] = ToNewUTF8String(params[i].mValue); + pos++; + } + + const char* mimetype; + NPError error = NPERR_GENERIC_ERROR; + + GetMIMEType(&mimetype); + + bool oldVal = mInPluginInitCall; + mInPluginInitCall = true; + + // Need this on the stack before calling NPP_New otherwise some callbacks that + // the plugin may make could fail (NPN_HasProperty, for example). + NPPAutoPusher autopush(&mNPP); + + if (!mPlugin) return NS_ERROR_FAILURE; + + PluginLibrary* library = mPlugin->GetLibrary(); + 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, quirkParamLength, + mCachedParamNames, mCachedParamValues, nullptr, &error); + mInPluginInitCall = oldVal; + + NPP_PLUGIN_LOG( + PLUGIN_LOG_NORMAL, + ("NPP New called: this=%p, npp=%p, mime=%s, argc=%d, return=%d\n", this, + &mNPP, mimetype, quirkParamLength, error)); + + if (NS_FAILED(newResult) || error != NPERR_NO_ERROR) { + mRunning = DESTROYED; + nsJSNPRuntime::OnPluginDestroy(&mNPP); + return NS_ERROR_FAILURE; + } + + return newResult; +} + +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 + 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); + + NPError error; + NS_TRY_SAFE_CALL_RETURN( + error, (*pluginFunctions->setwindow)(&mNPP, (NPWindow*)window), this, + NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); + // 'error' is only used if this is a logging-enabled build. + // That is somewhat complex to check, so we just use "unused" + // to suppress any compiler warnings in build configurations + // where the logging is a no-op. + mozilla::Unused << error; + + 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, error)); + } + return NS_OK; +} + +nsresult nsNPAPIPluginInstance::NewStreamListener( + const char* aURL, void* notifyData, + nsNPAPIPluginStreamListener** listener) { + RefPtr<nsNPAPIPluginStreamListener> 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; + + AUTO_PROFILER_LABEL("nsNPAPIPluginInstance::HandleEvent", OTHER); + + 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) { +#ifdef XP_MACOSX + mCurrentPluginEvent = event; +#endif +#if defined(XP_WIN) + NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), + this, aSafeToReenterGecko); +#else + 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; +#ifdef XP_MACOSX + mCurrentPluginEvent = nullptr; +#endif + } + + 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=%p, 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; + 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 + +nsresult nsNPAPIPluginInstance::GetDrawingModel(int32_t* aModel) { + *aModel = (int32_t)mDrawingModel; + return NS_OK; +} + +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) { +#if defined(XP_MACOSX) || defined(XP_WIN) + 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::CSSZoomFactorChanged(float aCSSZoomFactor) { + if (RUNNING != mRunning) return NS_OK; + + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance informing plugin of " + "CSS Zoom Factor 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; + double value = static_cast<double>(aCSSZoomFactor); + NS_TRY_SAFE_CALL_RETURN( + error, (*pluginFunctions->setvalue)(&mNPP, NPNVCSSZoomFactor, &value), + this, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); + return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE; +} + +nsresult nsNPAPIPluginInstance::GetJSObject(JSContext* cx, + JSObject** outObject) { + 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) { +#if defined(XP_MACOSX) + // All OS X plugins are windowless. + *isWindowless = true; +#else + *isWindowless = mWindowless; +#endif + return NS_OK; +} + +class MOZ_STACK_CLASS AutoPluginLibraryCall { + public: + explicit AutoPluginLibraryCall(nsNPAPIPluginInstance* aThis) + : mThis(aThis), mGuard(aThis), mLibrary(nullptr) { + nsNPAPIPlugin* plugin = mThis->GetPlugin(); + if (plugin) mLibrary = plugin->GetLibrary(); + } + explicit 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); +} + +#if defined(XP_WIN) +nsresult nsNPAPIPluginInstance::GetScrollCaptureContainer( + ImageContainer** aContainer) { + *aContainer = nullptr; + + if (RUNNING != mRunning) return NS_OK; + + AutoPluginLibraryCall library(this); + return !library ? NS_ERROR_FAILURE + : library->GetScrollCaptureContainer(&mNPP, aContainer); +} +#endif + +nsresult nsNPAPIPluginInstance::HandledWindowedPluginKeyEvent( + const NativeEventData& aKeyEventData, bool aIsConsumed) { + if (NS_WARN_IF(!mPlugin)) { + return NS_ERROR_FAILURE; + } + + PluginLibrary* library = mPlugin->GetLibrary(); + if (NS_WARN_IF(!library)) { + return NS_ERROR_FAILURE; + } + return library->HandledWindowedPluginKeyEvent(&mNPP, aKeyEventData, + aIsConsumed); +} + +void nsNPAPIPluginInstance::DidComposite() { + if (RUNNING != mRunning) return; + + AutoPluginLibraryCall library(this); + library->DidComposite(&mNPP); +} + +nsresult nsNPAPIPluginInstance::NotifyPainted(void) { + MOZ_ASSERT_UNREACHABLE("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, DrawTarget** aDrawTarget) { + if (RUNNING != mRunning) return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) return NS_ERROR_FAILURE; + + return library->BeginUpdateBackground(&mNPP, *aRect, aDrawTarget); +} + +nsresult nsNPAPIPluginInstance::EndUpdateBackground(nsIntRect* aRect) { + if (RUNNING != mRunning) return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) return NS_ERROR_FAILURE; + + return library->EndUpdateBackground(&mNPP, *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(MakeStringSpan(value), aValue); + + // NPPVformValue allocates with NPN_MemAlloc(), which uses + // nsMemory. + free(value); + + return NS_OK; +} + +nsresult nsNPAPIPluginInstance::PushPopupsEnabledState(bool aEnabled) { + nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow(); + if (!window) return NS_ERROR_FAILURE; + + PopupBlocker::PopupControlState oldState = + PopupBlocker::PushPopupControlState( + aEnabled ? PopupBlocker::openAllowed : PopupBlocker::openAbused, + true); + + // XXX(Bug 1631371) Check if this should use a fallible operation as it + // pretended earlier. + mPopupStates.AppendElement(oldState); + + return NS_OK; +} + +nsresult nsNPAPIPluginInstance::PopPopupsEnabledState() { + if (mPopupStates.IsEmpty()) { + // Nothing to pop. + return NS_OK; + } + + nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow(); + if (!window) return NS_ERROR_FAILURE; + + PopupBlocker::PopPopupControlState(mPopupStates.PopLastElement()); + + 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<NPBool>(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<Document> doc; + mOwner->GetDocument(getter_AddRefs(doc)); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + + nsCOMPtr<nsPIDOMWindowOuter> domwindow = doc->GetWindow(); + NS_ENSURE_TRUE(domwindow, NS_ERROR_FAILURE); + + nsCOMPtr<nsIDocShell> docShell = domwindow->GetDocShell(); + nsCOMPtr<nsILoadContext> 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)); + + // 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; + const short timerType = (repeat ? (short)nsITimer::TYPE_REPEATING_SLACK + : (short)nsITimer::TYPE_ONE_SHOT); + rv = NS_NewTimerWithFuncCallback( + getter_AddRefs(newTimer->timer), PluginTimerCallback, newTimer, interval, + timerType, "nsNPAPIPluginInstance::ScheduleTimer"); + if (NS_FAILED(rv)) { + delete newTimer; + return 0; + } + + // 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; +} + +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(Element** 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; +} + +nsPluginInstanceOwner* nsNPAPIPluginInstance::GetOwner() { return mOwner; } + +void nsNPAPIPluginInstance::SetOwner(nsPluginInstanceOwner* aOwner) { + mOwner = aOwner; +} + +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); + } +} + +double nsNPAPIPluginInstance::GetContentsScaleFactor() { + double scaleFactor = 1.0; + if (mOwner) { + mOwner->GetContentsScaleFactor(&scaleFactor); + } + return scaleFactor; +} + +float nsNPAPIPluginInstance::GetCSSZoomFactor() { + float zoomFactor = 1.0; + if (mOwner) { + mOwner->GetCSSZoomFactor(&zoomFactor); + } + return zoomFactor; +} + +nsresult nsNPAPIPluginInstance::GetRunID(uint32_t* aRunID) { + if (NS_WARN_IF(!aRunID)) { + return NS_ERROR_INVALID_POINTER; + } + + if (NS_WARN_IF(!mPlugin)) { + return NS_ERROR_FAILURE; + } + + PluginLibrary* library = mPlugin->GetLibrary(); + if (!library) { + return NS_ERROR_FAILURE; + } + + return library->GetRunID(aRunID); +} + +nsresult nsNPAPIPluginInstance::CreateAudioChannelAgentIfNeeded() { + if (mAudioChannelAgent) { + return NS_OK; + } + + mAudioChannelAgent = new AudioChannelAgent(); + + nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow(); + if (NS_WARN_IF(!window)) { + return NS_ERROR_FAILURE; + } + + nsresult rv = mAudioChannelAgent->Init(window->GetCurrentInnerWindow(), this); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; +} + +void nsNPAPIPluginInstance::NotifyStartedPlaying() { + nsresult rv = CreateAudioChannelAgentIfNeeded(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + MOZ_ASSERT(mAudioChannelAgent); + rv = mAudioChannelAgent->NotifyStartedPlaying( + mIsMuted ? AudioChannelService::AudibleState::eNotAudible + : AudioChannelService::AudibleState::eAudible); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + mAudioChannelAgent->PullInitialUpdate(); +} + +void nsNPAPIPluginInstance::NotifyStoppedPlaying() { + MOZ_ASSERT(mAudioChannelAgent); + nsresult rv = mAudioChannelAgent->NotifyStoppedPlaying(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } +} + +NS_IMETHODIMP +nsNPAPIPluginInstance::WindowVolumeChanged(float aVolume, bool aMuted) { + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("nsNPAPIPluginInstance, WindowVolumeChanged, " + "this = %p, aVolume = %f, aMuted = %s\n", + this, aVolume, aMuted ? "true" : "false")); + // We just support mute/unmute + if (mWindowMuted != aMuted) { + mWindowMuted = aMuted; + return UpdateMutedIfNeeded(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsNPAPIPluginInstance::WindowSuspendChanged(nsSuspendedTypes aSuspend) { + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("nsNPAPIPluginInstance, WindowSuspendChanged, " + "this = %p, aSuspend = %s\n", + this, SuspendTypeToStr(aSuspend))); + const bool isSuspended = aSuspend != nsISuspendedTypes::NONE_SUSPENDED; + if (mWindowSuspended != isSuspended) { + mWindowSuspended = isSuspended; + // It doesn't support suspending, so we just do something like mute/unmute. + return UpdateMutedIfNeeded(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsNPAPIPluginInstance::WindowAudioCaptureChanged(bool aCapture) { + return NS_OK; +} + +void nsNPAPIPluginInstance::NotifyAudibleStateChanged() const { + // This happens when global window destroyed, we would notify agent's callback + // to mute its volume, but the nsNSNPAPI had released the agent before that. + if (!mAudioChannelAgent) { + return; + } + AudioChannelService::AudibleState audibleState = + mIsMuted ? AudioChannelService::AudibleState::eNotAudible + : AudioChannelService::AudibleState::eAudible; + // Because we don't really support suspending nsNPAPI, so all audible changes + // come from changing its volume. + mAudioChannelAgent->NotifyStartedAudible( + audibleState, AudioChannelService::AudibleChangedReasons::eVolumeChanged); +} + +nsresult nsNPAPIPluginInstance::UpdateMutedIfNeeded() { + const bool shouldMute = mWindowSuspended || mWindowMuted; + if (mIsMuted == shouldMute) { + return NS_OK; + } + + mIsMuted = shouldMute; + NotifyAudibleStateChanged(); + nsresult rv = SetMuted(mIsMuted); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetMuted failed"); + return rv; +} + +nsresult nsNPAPIPluginInstance::SetMuted(bool aIsMuted) { + if (RUNNING != mRunning) return NS_OK; + + PLUGIN_LOG( + PLUGIN_LOG_NORMAL, + ("nsNPAPIPluginInstance informing plugin of mute 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<NPBool>(aIsMuted); + NS_TRY_SAFE_CALL_RETURN( + error, (*pluginFunctions->setvalue)(&mNPP, NPNVmuteAudioBool, &value), + this, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO); + return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE; +} |