summaryrefslogtreecommitdiffstats
path: root/dom/plugins/base
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/base')
-rw-r--r--dom/plugins/base/PluginPRLibrary.cpp347
-rw-r--r--dom/plugins/base/PluginPRLibrary.h158
-rw-r--r--dom/plugins/base/android/ANPAudio.cpp390
-rw-r--r--dom/plugins/base/android/ANPBase.h36
-rw-r--r--dom/plugins/base/android/ANPEvent.cpp31
-rw-r--r--dom/plugins/base/android/ANPKeyCodes.h152
-rw-r--r--dom/plugins/base/android/ANPLog.cpp26
-rw-r--r--dom/plugins/base/android/ANPNativeWindow.cpp39
-rw-r--r--dom/plugins/base/android/ANPSurface.cpp266
-rw-r--r--dom/plugins/base/android/ANPSystem.cpp88
-rw-r--r--dom/plugins/base/android/ANPVideo.cpp55
-rw-r--r--dom/plugins/base/android/ANPWindow.cpp152
-rw-r--r--dom/plugins/base/android/android_npapi.h1027
-rw-r--r--dom/plugins/base/android/moz.build35
-rw-r--r--dom/plugins/base/moz.build117
-rw-r--r--dom/plugins/base/npapi.h921
-rw-r--r--dom/plugins/base/npfunctions.h281
-rw-r--r--dom/plugins/base/npruntime.h393
-rw-r--r--dom/plugins/base/nptypes.h88
-rw-r--r--dom/plugins/base/nsIHTTPHeaderListener.idl29
-rw-r--r--dom/plugins/base/nsIPluginDocument.idl16
-rw-r--r--dom/plugins/base/nsIPluginHost.idl172
-rw-r--r--dom/plugins/base/nsIPluginInputStream.idl20
-rw-r--r--dom/plugins/base/nsIPluginInstanceOwner.idl122
-rw-r--r--dom/plugins/base/nsIPluginTag.idl76
-rw-r--r--dom/plugins/base/nsJSNPRuntime.cpp2328
-rw-r--r--dom/plugins/base/nsJSNPRuntime.h108
-rw-r--r--dom/plugins/base/nsNPAPIPlugin.cpp2819
-rw-r--r--dom/plugins/base/nsNPAPIPlugin.h414
-rw-r--r--dom/plugins/base/nsNPAPIPluginInstance.cpp1861
-rw-r--r--dom/plugins/base/nsNPAPIPluginInstance.h464
-rw-r--r--dom/plugins/base/nsNPAPIPluginStreamListener.cpp959
-rw-r--r--dom/plugins/base/nsNPAPIPluginStreamListener.h149
-rw-r--r--dom/plugins/base/nsPluginDirServiceProvider.cpp448
-rw-r--r--dom/plugins/base/nsPluginDirServiceProvider.h46
-rw-r--r--dom/plugins/base/nsPluginHost.cpp4239
-rw-r--r--dom/plugins/base/nsPluginHost.h465
-rw-r--r--dom/plugins/base/nsPluginInstanceOwner.cpp3894
-rw-r--r--dom/plugins/base/nsPluginInstanceOwner.h420
-rw-r--r--dom/plugins/base/nsPluginLogging.h90
-rw-r--r--dom/plugins/base/nsPluginManifestLineReader.h102
-rw-r--r--dom/plugins/base/nsPluginModule.cpp29
-rw-r--r--dom/plugins/base/nsPluginNativeWindow.cpp53
-rw-r--r--dom/plugins/base/nsPluginNativeWindow.h80
-rw-r--r--dom/plugins/base/nsPluginNativeWindowGtk.cpp356
-rw-r--r--dom/plugins/base/nsPluginNativeWindowGtk.h51
-rw-r--r--dom/plugins/base/nsPluginNativeWindowWin.cpp748
-rw-r--r--dom/plugins/base/nsPluginStreamListenerPeer.cpp1424
-rw-r--r--dom/plugins/base/nsPluginStreamListenerPeer.h185
-rw-r--r--dom/plugins/base/nsPluginTags.cpp1045
-rw-r--r--dom/plugins/base/nsPluginTags.h236
-rw-r--r--dom/plugins/base/nsPluginsCID.h13
-rw-r--r--dom/plugins/base/nsPluginsDir.h76
-rw-r--r--dom/plugins/base/nsPluginsDirDarwin.cpp591
-rw-r--r--dom/plugins/base/nsPluginsDirUnix.cpp421
-rw-r--r--dom/plugins/base/nsPluginsDirUtils.h89
-rw-r--r--dom/plugins/base/nsPluginsDirWin.cpp433
-rw-r--r--dom/plugins/base/nspluginroot.idl31
58 files changed, 29704 insertions, 0 deletions
diff --git a/dom/plugins/base/PluginPRLibrary.cpp b/dom/plugins/base/PluginPRLibrary.cpp
new file mode 100644
index 000000000..a7ad76ea4
--- /dev/null
+++ b/dom/plugins/base/PluginPRLibrary.cpp
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ * 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/PluginPRLibrary.h"
+#include "nsNPAPIPluginInstance.h"
+
+// Some plugins on Windows, notably Quake Live, implement NP_Initialize using
+// cdecl instead of the documented stdcall. In order to work around this,
+// we force the caller to use a frame pointer.
+#if defined(XP_WIN) && defined(_M_IX86)
+#include <malloc.h>
+
+// gNotOptimized exists so that the compiler will not optimize the alloca
+// below.
+static int gNotOptimized;
+#define CALLING_CONVENTION_HACK void* foo = _alloca(gNotOptimized);
+#else
+#define CALLING_CONVENTION_HACK
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "android_npapi.h"
+#include <android/log.h>
+#undef ALOG
+#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoJavaEnv", ## args)
+#endif
+
+using namespace mozilla::layers;
+
+namespace mozilla {
+#ifdef MOZ_WIDGET_ANDROID
+nsresult
+PluginPRLibrary::NP_Initialize(NPNetscapeFuncs* bFuncs,
+ NPPluginFuncs* pFuncs, NPError* error)
+{
+ JNIEnv* env = jni::GetEnvForThread();
+
+ mozilla::AutoLocalJNIFrame jniFrame(env);
+
+ if (mNP_Initialize) {
+ *error = mNP_Initialize(bFuncs, pFuncs, env);
+ } else {
+ NP_InitializeFunc pfNP_Initialize = (NP_InitializeFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
+ if (!pfNP_Initialize)
+ return NS_ERROR_FAILURE;
+ *error = pfNP_Initialize(bFuncs, pFuncs, env);
+ }
+
+ // Save pointers to functions that get called through PluginLibrary itself.
+ mNPP_New = pFuncs->newp;
+ mNPP_ClearSiteData = pFuncs->clearsitedata;
+ mNPP_GetSitesWithData = pFuncs->getsiteswithdata;
+ return NS_OK;
+}
+#elif defined(MOZ_WIDGET_GONK)
+nsresult
+PluginPRLibrary::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
+{
+ return NS_OK;
+}
+#elif defined(XP_UNIX) && !defined(XP_MACOSX)
+nsresult
+PluginPRLibrary::NP_Initialize(NPNetscapeFuncs* bFuncs,
+ NPPluginFuncs* pFuncs, NPError* error)
+{
+ if (mNP_Initialize) {
+ *error = mNP_Initialize(bFuncs, pFuncs);
+ } else {
+ NP_InitializeFunc pfNP_Initialize = (NP_InitializeFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
+ if (!pfNP_Initialize)
+ return NS_ERROR_FAILURE;
+ *error = pfNP_Initialize(bFuncs, pFuncs);
+ }
+
+
+ // Save pointers to functions that get called through PluginLibrary itself.
+ mNPP_New = pFuncs->newp;
+ mNPP_ClearSiteData = pFuncs->clearsitedata;
+ mNPP_GetSitesWithData = pFuncs->getsiteswithdata;
+ return NS_OK;
+}
+#else
+nsresult
+PluginPRLibrary::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
+{
+ CALLING_CONVENTION_HACK
+
+ if (mNP_Initialize) {
+ *error = mNP_Initialize(bFuncs);
+ } else {
+ NP_InitializeFunc pfNP_Initialize = (NP_InitializeFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
+ if (!pfNP_Initialize)
+ return NS_ERROR_FAILURE;
+ *error = pfNP_Initialize(bFuncs);
+ }
+
+ return NS_OK;
+}
+#endif
+
+nsresult
+PluginPRLibrary::NP_Shutdown(NPError* error)
+{
+ CALLING_CONVENTION_HACK
+
+ if (mNP_Shutdown) {
+ *error = mNP_Shutdown();
+ } else {
+ NP_ShutdownFunc pfNP_Shutdown = (NP_ShutdownFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
+ if (!pfNP_Shutdown)
+ return NS_ERROR_FAILURE;
+ *error = pfNP_Shutdown();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+PluginPRLibrary::NP_GetMIMEDescription(const char** mimeDesc)
+{
+ CALLING_CONVENTION_HACK
+
+ if (mNP_GetMIMEDescription) {
+ *mimeDesc = mNP_GetMIMEDescription();
+ }
+ else {
+ NP_GetMIMEDescriptionFunc pfNP_GetMIMEDescription =
+ (NP_GetMIMEDescriptionFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_GetMIMEDescription");
+ if (!pfNP_GetMIMEDescription) {
+ *mimeDesc = "";
+ return NS_ERROR_FAILURE;
+ }
+ *mimeDesc = pfNP_GetMIMEDescription();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+PluginPRLibrary::NP_GetValue(void *future, NPPVariable aVariable,
+ void *aValue, NPError* error)
+{
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ if (mNP_GetValue) {
+ *error = mNP_GetValue(future, aVariable, aValue);
+ } else {
+ NP_GetValueFunc pfNP_GetValue = (NP_GetValueFunc)PR_FindFunctionSymbol(mLibrary, "NP_GetValue");
+ if (!pfNP_GetValue)
+ return NS_ERROR_FAILURE;
+ *error = pfNP_GetValue(future, aVariable, aValue);
+ }
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+nsresult
+PluginPRLibrary::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
+{
+ CALLING_CONVENTION_HACK
+
+ if (mNP_GetEntryPoints) {
+ *error = mNP_GetEntryPoints(pFuncs);
+ } else {
+ NP_GetEntryPointsFunc pfNP_GetEntryPoints = (NP_GetEntryPointsFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_GetEntryPoints");
+ if (!pfNP_GetEntryPoints)
+ return NS_ERROR_FAILURE;
+ *error = pfNP_GetEntryPoints(pFuncs);
+ }
+
+ // Save pointers to functions that get called through PluginLibrary itself.
+ mNPP_New = pFuncs->newp;
+ mNPP_ClearSiteData = pFuncs->clearsitedata;
+ mNPP_GetSitesWithData = pFuncs->getsiteswithdata;
+ return NS_OK;
+}
+#endif
+
+nsresult
+PluginPRLibrary::NPP_New(NPMIMEType pluginType, NPP instance,
+ uint16_t mode, int16_t argc, char* argn[],
+ char* argv[], NPSavedData* saved,
+ NPError* error)
+{
+ if (!mNPP_New)
+ return NS_ERROR_FAILURE;
+
+ MAIN_THREAD_JNI_REF_GUARD;
+ *error = mNPP_New(pluginType, instance, mode, argc, argn, argv, saved);
+ return NS_OK;
+}
+
+nsresult
+PluginPRLibrary::NPP_ClearSiteData(const char* site, uint64_t flags,
+ uint64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback)
+{
+ if (!mNPP_ClearSiteData) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ MAIN_THREAD_JNI_REF_GUARD;
+ NPError result = mNPP_ClearSiteData(site, flags, maxAge);
+
+ nsresult rv;
+ switch (result) {
+ case NPERR_NO_ERROR:
+ rv = NS_OK;
+ break;
+ case NPERR_TIME_RANGE_NOT_SUPPORTED:
+ rv = NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
+ break;
+ case NPERR_MALFORMED_SITE:
+ rv = NS_ERROR_INVALID_ARG;
+ break;
+ default:
+ rv = NS_ERROR_FAILURE;
+ }
+ callback->Callback(rv);
+ return NS_OK;
+}
+
+nsresult
+PluginPRLibrary::NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback)
+{
+ if (!mNPP_GetSitesWithData) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ MAIN_THREAD_JNI_REF_GUARD;
+ char** sites = mNPP_GetSitesWithData();
+ if (!sites) {
+ return NS_OK;
+ }
+ InfallibleTArray<nsCString> result;
+ char** iterator = sites;
+ while (*iterator) {
+ result.AppendElement(*iterator);
+ free(*iterator);
+ ++iterator;
+ }
+ callback->SitesWithData(result);
+ free(sites);
+
+ return NS_OK;
+}
+
+nsresult
+PluginPRLibrary::AsyncSetWindow(NPP instance, NPWindow* window)
+{
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+PluginPRLibrary::GetImageContainer(NPP instance, ImageContainer** aContainer)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+#if defined(XP_MACOSX)
+nsresult
+PluginPRLibrary::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
+{
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+ *aDrawing = false;
+ return NS_OK;
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+nsresult
+PluginPRLibrary::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor)
+{
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+ return NS_OK;
+}
+#endif
+
+nsresult
+PluginPRLibrary::GetImageSize(NPP instance, nsIntSize* aSize)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+PluginPRLibrary::SetBackgroundUnknown(NPP instance)
+{
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+ NS_ERROR("Unexpected use of async APIs for in-process plugin.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+PluginPRLibrary::BeginUpdateBackground(NPP instance, const nsIntRect&,
+ DrawTarget** aDrawTarget)
+{
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+ NS_ERROR("Unexpected use of async APIs for in-process plugin.");
+ *aDrawTarget = nullptr;
+ return NS_OK;
+}
+
+nsresult
+PluginPRLibrary::EndUpdateBackground(NPP instance, const nsIntRect&)
+{
+ NS_RUNTIMEABORT("This should never be called");
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+#if defined(XP_WIN)
+nsresult
+PluginPRLibrary::GetScrollCaptureContainer(NPP aInstance, ImageContainer** aContainer)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+#endif
+
+nsresult
+PluginPRLibrary::HandledWindowedPluginKeyEvent(
+ NPP aInstance,
+ const NativeEventData& aNativeKeyData,
+ bool aIsConsumed)
+{
+ nsNPAPIPluginInstance* instance = (nsNPAPIPluginInstance*)aInstance->ndata;
+ if (NS_WARN_IF(!instance)) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/dom/plugins/base/PluginPRLibrary.h b/dom/plugins/base/PluginPRLibrary.h
new file mode 100644
index 000000000..602ad8cf8
--- /dev/null
+++ b/dom/plugins/base/PluginPRLibrary.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * 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/. */
+
+#ifndef PluginPRLibrary_h
+#define PluginPRLibrary_h 1
+
+#include "mozilla/PluginLibrary.h"
+#include "nsNPAPIPlugin.h"
+#include "npfunctions.h"
+
+namespace mozilla {
+
+class PluginPRLibrary : public PluginLibrary
+{
+public:
+ PluginPRLibrary(const char* aFilePath, PRLibrary* aLibrary) :
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ mNP_Initialize(nullptr),
+#else
+ mNP_Initialize(nullptr),
+#endif
+ mNP_Shutdown(nullptr),
+ mNP_GetMIMEDescription(nullptr),
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ mNP_GetValue(nullptr),
+#endif
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ mNP_GetEntryPoints(nullptr),
+#endif
+ mNPP_New(nullptr),
+ mNPP_ClearSiteData(nullptr),
+ mNPP_GetSitesWithData(nullptr),
+ mLibrary(aLibrary),
+ mFilePath(aFilePath)
+ {
+ NS_ASSERTION(mLibrary, "need non-null lib");
+ // addref here??
+ }
+
+ virtual ~PluginPRLibrary()
+ {
+ // unref here??
+ }
+
+ virtual void SetPlugin(nsNPAPIPlugin*) override { }
+
+ virtual bool HasRequiredFunctions() override {
+ mNP_Initialize = (NP_InitializeFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
+ if (!mNP_Initialize)
+ return false;
+
+ mNP_Shutdown = (NP_ShutdownFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
+ if (!mNP_Shutdown)
+ return false;
+
+ mNP_GetMIMEDescription = (NP_GetMIMEDescriptionFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_GetMIMEDescription");
+#ifndef XP_MACOSX
+ if (!mNP_GetMIMEDescription)
+ return false;
+#endif
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ mNP_GetValue = (NP_GetValueFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_GetValue");
+ if (!mNP_GetValue)
+ return false;
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ mNP_GetEntryPoints = (NP_GetEntryPointsFunc)
+ PR_FindFunctionSymbol(mLibrary, "NP_GetEntryPoints");
+ if (!mNP_GetEntryPoints)
+ return false;
+#endif
+ return true;
+ }
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* aNetscapeFuncs,
+ NPPluginFuncs* aFuncs, NPError* aError) override;
+#else
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* aNetscapeFuncs,
+ NPError* aError) override;
+#endif
+
+ virtual nsresult NP_Shutdown(NPError* aError) override;
+ virtual nsresult NP_GetMIMEDescription(const char** aMimeDesc) override;
+
+ virtual nsresult NP_GetValue(void* aFuture, NPPVariable aVariable,
+ void* aValue, NPError* aError) override;
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ virtual nsresult NP_GetEntryPoints(NPPluginFuncs* aFuncs, NPError* aError) override;
+#endif
+
+ virtual nsresult NPP_New(NPMIMEType aPluginType, NPP aInstance,
+ uint16_t aMode, int16_t aArgc, char* aArgn[],
+ char* aArgv[], NPSavedData* aSaved,
+ NPError* aError) override;
+
+ virtual nsresult NPP_ClearSiteData(const char* aSite, uint64_t aFlags,
+ uint64_t aMaxAge, nsCOMPtr<nsIClearSiteDataCallback> callback) override;
+ virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) override;
+
+ virtual nsresult AsyncSetWindow(NPP aInstance, NPWindow* aWindow) override;
+ virtual nsresult GetImageContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
+ virtual nsresult GetImageSize(NPP aInstance, nsIntSize* aSize) override;
+ virtual bool IsOOP() override { return false; }
+#if defined(XP_MACOSX)
+ virtual nsresult IsRemoteDrawingCoreAnimation(NPP aInstance, bool* aDrawing) override;
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ virtual nsresult ContentsScaleFactorChanged(NPP aInstance, double aContentsScaleFactor) override;
+#endif
+ virtual nsresult SetBackgroundUnknown(NPP instance) override;
+ virtual nsresult BeginUpdateBackground(NPP instance, const nsIntRect&,
+ DrawTarget** aDrawTarget) override;
+ virtual nsresult EndUpdateBackground(NPP instance,
+ const nsIntRect&) override;
+ virtual void DidComposite(NPP aInstance) override { }
+ virtual void GetLibraryPath(nsACString& aPath) { aPath.Assign(mFilePath); }
+ virtual nsresult GetRunID(uint32_t* aRunID) override { return NS_ERROR_NOT_IMPLEMENTED; }
+ virtual void SetHasLocalInstance() override { }
+#if defined(XP_WIN)
+ virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
+#endif
+ virtual nsresult HandledWindowedPluginKeyEvent(
+ NPP aInstance,
+ const mozilla::NativeEventData& aNativeKeyData,
+ bool aIsCOnsumed) override;
+
+private:
+ NP_InitializeFunc mNP_Initialize;
+ NP_ShutdownFunc mNP_Shutdown;
+ NP_GetMIMEDescriptionFunc mNP_GetMIMEDescription;
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ NP_GetValueFunc mNP_GetValue;
+#endif
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ NP_GetEntryPointsFunc mNP_GetEntryPoints;
+#endif
+ NPP_NewProcPtr mNPP_New;
+ NPP_ClearSiteDataPtr mNPP_ClearSiteData;
+ NPP_GetSitesWithDataPtr mNPP_GetSitesWithData;
+ PRLibrary* mLibrary;
+ nsCString mFilePath;
+};
+
+
+} // namespace mozilla
+
+#endif // ifndef PluginPRLibrary_h
diff --git a/dom/plugins/base/android/ANPAudio.cpp b/dom/plugins/base/android/ANPAudio.cpp
new file mode 100644
index 000000000..bc47e8999
--- /dev/null
+++ b/dom/plugins/base/android/ANPAudio.cpp
@@ -0,0 +1,390 @@
+/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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 "base/basictypes.h"
+#include "AndroidBridge.h"
+
+#include <android/log.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "assert.h"
+#include "ANPBase.h"
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Mutex.h"
+
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPluginsAudio" , ## args)
+#define ASSIGN(obj, name) (obj)->name = anp_audio_##name
+
+/* android.media.AudioTrack */
+struct AudioTrack {
+ jclass at_class;
+ jmethodID constructor;
+ jmethodID flush;
+ jmethodID pause;
+ jmethodID play;
+ jmethodID setvol;
+ jmethodID stop;
+ jmethodID write;
+ jmethodID getpos;
+ jmethodID getstate;
+ jmethodID release;
+};
+
+enum AudioTrackMode {
+ MODE_STATIC = 0,
+ MODE_STREAM = 1
+};
+
+/* android.media.AudioManager */
+enum AudioManagerStream {
+ STREAM_VOICE_CALL = 0,
+ STREAM_SYSTEM = 1,
+ STREAM_RING = 2,
+ STREAM_MUSIC = 3,
+ STREAM_ALARM = 4,
+ STREAM_NOTIFICATION = 5,
+ STREAM_DTMF = 8
+};
+
+/* android.media.AudioFormat */
+enum AudioFormatChannel {
+ CHANNEL_OUT_MONO = 4,
+ CHANNEL_OUT_STEREO = 12
+};
+
+enum AudioFormatEncoding {
+ ENCODING_PCM_16BIT = 2,
+ ENCODING_PCM_8BIT = 3
+};
+
+enum AudioFormatState {
+ STATE_UNINITIALIZED = 0,
+ STATE_INITIALIZED = 1,
+ STATE_NO_STATIC_DATA = 2
+};
+
+static struct AudioTrack at;
+
+static jclass
+init_jni_bindings(JNIEnv *jenv) {
+ jclass jc =
+ (jclass)jenv->NewGlobalRef(jenv->FindClass("android/media/AudioTrack"));
+
+ at.constructor = jenv->GetMethodID(jc, "<init>", "(IIIIII)V");
+ at.flush = jenv->GetMethodID(jc, "flush", "()V");
+ at.pause = jenv->GetMethodID(jc, "pause", "()V");
+ at.play = jenv->GetMethodID(jc, "play", "()V");
+ at.setvol = jenv->GetMethodID(jc, "setStereoVolume", "(FF)I");
+ at.stop = jenv->GetMethodID(jc, "stop", "()V");
+ at.write = jenv->GetMethodID(jc, "write", "([BII)I");
+ at.getpos = jenv->GetMethodID(jc, "getPlaybackHeadPosition", "()I");
+ at.getstate = jenv->GetMethodID(jc, "getState", "()I");
+ at.release = jenv->GetMethodID(jc, "release", "()V");
+
+ return jc;
+}
+
+struct ANPAudioTrack {
+ jobject output_unit;
+ jclass at_class;
+
+ unsigned int rate;
+ unsigned int channels;
+ unsigned int bufferSize;
+ unsigned int isStopped;
+ unsigned int keepGoing;
+
+ mozilla::Mutex lock;
+
+ void* user;
+ ANPAudioCallbackProc proc;
+ ANPSampleFormat format;
+
+ ANPAudioTrack() : lock("ANPAudioTrack") { }
+};
+
+class AudioRunnable : public mozilla::Runnable
+{
+public:
+ NS_DECL_NSIRUNNABLE
+
+ AudioRunnable(ANPAudioTrack* aAudioTrack) {
+ mTrack = aAudioTrack;
+ }
+
+ ANPAudioTrack* mTrack;
+};
+
+NS_IMETHODIMP
+AudioRunnable::Run()
+{
+ PR_SetCurrentThreadName("Android Audio");
+
+ JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
+
+ mozilla::AutoLocalJNIFrame autoFrame(jenv, 2);
+
+ jbyteArray bytearray = jenv->NewByteArray(mTrack->bufferSize);
+ if (!bytearray) {
+ LOG("AudioRunnable:: Run. Could not create bytearray");
+ return NS_ERROR_FAILURE;
+ }
+
+ jbyte *byte = jenv->GetByteArrayElements(bytearray, nullptr);
+ if (!byte) {
+ LOG("AudioRunnable:: Run. Could not create bytearray");
+ return NS_ERROR_FAILURE;
+ }
+
+ ANPAudioBuffer buffer;
+ buffer.channelCount = mTrack->channels;
+ buffer.format = mTrack->format;
+ buffer.bufferData = (void*) byte;
+
+ while (true)
+ {
+ // reset the buffer size
+ buffer.size = mTrack->bufferSize;
+
+ {
+ mozilla::MutexAutoLock lock(mTrack->lock);
+
+ if (!mTrack->keepGoing)
+ break;
+
+ // Get data from the plugin
+ mTrack->proc(kMoreData_ANPAudioEvent, mTrack->user, &buffer);
+ }
+
+ if (buffer.size == 0) {
+ LOG("%p - kMoreData_ANPAudioEvent", mTrack);
+ continue;
+ }
+
+ size_t wroteSoFar = 0;
+ jint retval;
+ do {
+ retval = jenv->CallIntMethod(mTrack->output_unit,
+ at.write,
+ bytearray,
+ wroteSoFar,
+ buffer.size - wroteSoFar);
+ if (retval < 0) {
+ LOG("%p - Write failed %d", mTrack, retval);
+ break;
+ }
+
+ wroteSoFar += retval;
+
+ } while(wroteSoFar < buffer.size);
+ }
+
+ jenv->CallVoidMethod(mTrack->output_unit, at.release);
+
+ jenv->DeleteGlobalRef(mTrack->output_unit);
+ jenv->DeleteGlobalRef(mTrack->at_class);
+
+ delete mTrack;
+
+ jenv->ReleaseByteArrayElements(bytearray, byte, 0);
+
+ return NS_OK;
+}
+
+ANPAudioTrack*
+anp_audio_newTrack(uint32_t sampleRate, // sampling rate in Hz
+ ANPSampleFormat format,
+ int channelCount, // MONO=1, STEREO=2
+ ANPAudioCallbackProc proc,
+ void* user)
+{
+ ANPAudioTrack *s = new ANPAudioTrack();
+ if (s == nullptr) {
+ return nullptr;
+ }
+
+ JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
+
+ s->at_class = init_jni_bindings(jenv);
+ s->rate = sampleRate;
+ s->channels = channelCount;
+ s->bufferSize = s->rate * s->channels;
+ s->isStopped = true;
+ s->keepGoing = false;
+ s->user = user;
+ s->proc = proc;
+ s->format = format;
+
+ int jformat;
+ switch (format) {
+ case kPCM16Bit_ANPSampleFormat:
+ jformat = ENCODING_PCM_16BIT;
+ break;
+ case kPCM8Bit_ANPSampleFormat:
+ jformat = ENCODING_PCM_8BIT;
+ break;
+ default:
+ LOG("Unknown audio format. defaulting to 16bit.");
+ jformat = ENCODING_PCM_16BIT;
+ break;
+ }
+
+ int jChannels;
+ switch (channelCount) {
+ case 1:
+ jChannels = CHANNEL_OUT_MONO;
+ break;
+ case 2:
+ jChannels = CHANNEL_OUT_STEREO;
+ break;
+ default:
+ LOG("Unknown channel count. defaulting to mono.");
+ jChannels = CHANNEL_OUT_MONO;
+ break;
+ }
+
+ mozilla::AutoLocalJNIFrame autoFrame(jenv);
+
+ jobject obj = jenv->NewObject(s->at_class,
+ at.constructor,
+ STREAM_MUSIC,
+ s->rate,
+ jChannels,
+ jformat,
+ s->bufferSize,
+ MODE_STREAM);
+
+ if (autoFrame.CheckForException() || obj == nullptr) {
+ jenv->DeleteGlobalRef(s->at_class);
+ delete s;
+ return nullptr;
+ }
+
+ jint state = jenv->CallIntMethod(obj, at.getstate);
+
+ if (autoFrame.CheckForException() || state == STATE_UNINITIALIZED) {
+ jenv->DeleteGlobalRef(s->at_class);
+ delete s;
+ return nullptr;
+ }
+
+ s->output_unit = jenv->NewGlobalRef(obj);
+ return s;
+}
+
+void
+anp_audio_deleteTrack(ANPAudioTrack* s)
+{
+ if (s == nullptr) {
+ return;
+ }
+
+ mozilla::MutexAutoLock lock(s->lock);
+ s->keepGoing = false;
+
+ // deallocation happens in the AudioThread. There is a
+ // potential leak if anp_audio_start is never called, but
+ // we do not see that from flash.
+}
+
+void
+anp_audio_start(ANPAudioTrack* s)
+{
+ if (s == nullptr || s->output_unit == nullptr) {
+ return;
+ }
+
+ if (s->keepGoing) {
+ // we are already playing. Ignore.
+ return;
+ }
+
+ JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
+
+ mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
+ jenv->CallVoidMethod(s->output_unit, at.play);
+
+ if (autoFrame.CheckForException()) {
+ jenv->DeleteGlobalRef(s->at_class);
+ delete s;
+ return;
+ }
+
+ s->isStopped = false;
+ s->keepGoing = true;
+
+ // AudioRunnable now owns the ANPAudioTrack
+ RefPtr<AudioRunnable> runnable = new AudioRunnable(s);
+
+ nsCOMPtr<nsIThread> thread;
+ NS_NewThread(getter_AddRefs(thread), runnable);
+}
+
+void
+anp_audio_pause(ANPAudioTrack* s)
+{
+ if (s == nullptr || s->output_unit == nullptr) {
+ return;
+ }
+
+ JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
+
+ mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
+ jenv->CallVoidMethod(s->output_unit, at.pause);
+}
+
+void
+anp_audio_stop(ANPAudioTrack* s)
+{
+ if (s == nullptr || s->output_unit == nullptr) {
+ return;
+ }
+
+ s->isStopped = true;
+ JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
+
+ mozilla::AutoLocalJNIFrame autoFrame(jenv, 0);
+ jenv->CallVoidMethod(s->output_unit, at.stop);
+}
+
+bool
+anp_audio_isStopped(ANPAudioTrack* s)
+{
+ return s->isStopped;
+}
+
+uint32_t
+anp_audio_trackLatency(ANPAudioTrack* s) {
+ // Hardcode an estimate of the system's audio latency. Flash hardcodes
+ // similar latency estimates for pre-Honeycomb devices that do not support
+ // ANPAudioTrackInterfaceV1's trackLatency(). The Android stock browser
+ // calls android::AudioTrack::latency(), an internal Android API that is
+ // not available in the public NDK:
+ // https://github.com/android/platform_external_webkit/commit/49bf866973cb3b2a6c74c0eab864e9562e4cbab1
+ return 100; // milliseconds
+}
+
+void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) {
+ _assert(i->inSize == sizeof(*i));
+ ASSIGN(i, newTrack);
+ ASSIGN(i, deleteTrack);
+ ASSIGN(i, start);
+ ASSIGN(i, pause);
+ ASSIGN(i, stop);
+ ASSIGN(i, isStopped);
+}
+
+void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) {
+ _assert(i->inSize == sizeof(*i));
+ ASSIGN(i, newTrack);
+ ASSIGN(i, deleteTrack);
+ ASSIGN(i, start);
+ ASSIGN(i, pause);
+ ASSIGN(i, stop);
+ ASSIGN(i, isStopped);
+ ASSIGN(i, trackLatency);
+}
diff --git a/dom/plugins/base/android/ANPBase.h b/dom/plugins/base/android/ANPBase.h
new file mode 100644
index 000000000..f9712dc79
--- /dev/null
+++ b/dom/plugins/base/android/ANPBase.h
@@ -0,0 +1,36 @@
+/* -*- Mode: IDL; 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 <stdlib.h>
+#include "android_npapi.h"
+#include "nsISupportsImpl.h"
+
+#define NOT_IMPLEMENTED_FATAL() do { \
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoPlugins", \
+ "%s not implemented %s, %d", \
+ __PRETTY_FUNCTION__, __FILE__, __LINE__); \
+ abort(); \
+ } while(0)
+
+#define NOT_IMPLEMENTED() \
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoPlugins", \
+ "!!!!!!!!!!!!!! %s not implemented %s, %d", \
+ __PRETTY_FUNCTION__, __FILE__, __LINE__); \
+
+void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i);
+void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1* i);
+void InitCanvasInterface(ANPCanvasInterfaceV0 *i);
+void InitEventInterface(ANPEventInterfaceV0 *i);
+void InitLogInterface(ANPLogInterfaceV0 *i);
+void InitPaintInterface(ANPPaintInterfaceV0 *i);
+void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i);
+void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i);
+void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i);
+void InitTypeFaceInterface(ANPTypefaceInterfaceV0 *i);
+void InitWindowInterface(ANPWindowInterfaceV0 *i);
+void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i);
+void InitVideoInterfaceV1(ANPVideoInterfaceV1 *i);
+void InitOpenGLInterface(ANPOpenGLInterfaceV0 *i);
+void InitNativeWindowInterface(ANPNativeWindowInterfaceV0 *i);
diff --git a/dom/plugins/base/android/ANPEvent.cpp b/dom/plugins/base/android/ANPEvent.cpp
new file mode 100644
index 000000000..cac8c94f0
--- /dev/null
+++ b/dom/plugins/base/android/ANPEvent.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: IDL; 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 "assert.h"
+#include "ANPBase.h"
+#include <android/log.h>
+#include "nsThreadUtils.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsNPAPIPlugin.h"
+
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name) (obj)->name = anp_event_##name
+
+void
+anp_event_postEvent(NPP instance, const ANPEvent* event)
+{
+ LOG("%s", __PRETTY_FUNCTION__);
+
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+ pinst->PostEvent((void*) event);
+
+ LOG("returning from %s", __PRETTY_FUNCTION__);
+}
+
+
+void InitEventInterface(ANPEventInterfaceV0 *i) {
+ _assert(i->inSize == sizeof(*i));
+ ASSIGN(i, postEvent);
+}
diff --git a/dom/plugins/base/android/ANPKeyCodes.h b/dom/plugins/base/android/ANPKeyCodes.h
new file mode 100644
index 000000000..edfe2b95c
--- /dev/null
+++ b/dom/plugins/base/android/ANPKeyCodes.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANPKeyCodes_DEFINED
+#define ANPKeyCodes_DEFINED
+
+/* List the key codes that are set to a plugin in the ANPKeyEvent.
+
+ These exactly match the values in android/view/KeyEvent.java and the
+ corresponding .h file android/keycodes.h.
+*/
+enum ANPKeyCodes {
+ kUnknown_ANPKeyCode = 0,
+
+ kSoftLeft_ANPKeyCode = 1,
+ kSoftRight_ANPKeyCode = 2,
+ kHome_ANPKeyCode = 3,
+ kBack_ANPKeyCode = 4,
+ kCall_ANPKeyCode = 5,
+ kEndCall_ANPKeyCode = 6,
+ k0_ANPKeyCode = 7,
+ k1_ANPKeyCode = 8,
+ k2_ANPKeyCode = 9,
+ k3_ANPKeyCode = 10,
+ k4_ANPKeyCode = 11,
+ k5_ANPKeyCode = 12,
+ k6_ANPKeyCode = 13,
+ k7_ANPKeyCode = 14,
+ k8_ANPKeyCode = 15,
+ k9_ANPKeyCode = 16,
+ kStar_ANPKeyCode = 17,
+ kPound_ANPKeyCode = 18,
+ kDpadUp_ANPKeyCode = 19,
+ kDpadDown_ANPKeyCode = 20,
+ kDpadLeft_ANPKeyCode = 21,
+ kDpadRight_ANPKeyCode = 22,
+ kDpadCenter_ANPKeyCode = 23,
+ kVolumeUp_ANPKeyCode = 24,
+ kVolumeDown_ANPKeyCode = 25,
+ kPower_ANPKeyCode = 26,
+ kCamera_ANPKeyCode = 27,
+ kClear_ANPKeyCode = 28,
+ kA_ANPKeyCode = 29,
+ kB_ANPKeyCode = 30,
+ kC_ANPKeyCode = 31,
+ kD_ANPKeyCode = 32,
+ kE_ANPKeyCode = 33,
+ kF_ANPKeyCode = 34,
+ kG_ANPKeyCode = 35,
+ kH_ANPKeyCode = 36,
+ kI_ANPKeyCode = 37,
+ kJ_ANPKeyCode = 38,
+ kK_ANPKeyCode = 39,
+ kL_ANPKeyCode = 40,
+ kM_ANPKeyCode = 41,
+ kN_ANPKeyCode = 42,
+ kO_ANPKeyCode = 43,
+ kP_ANPKeyCode = 44,
+ kQ_ANPKeyCode = 45,
+ kR_ANPKeyCode = 46,
+ kS_ANPKeyCode = 47,
+ kT_ANPKeyCode = 48,
+ kU_ANPKeyCode = 49,
+ kV_ANPKeyCode = 50,
+ kW_ANPKeyCode = 51,
+ kX_ANPKeyCode = 52,
+ kY_ANPKeyCode = 53,
+ kZ_ANPKeyCode = 54,
+ kComma_ANPKeyCode = 55,
+ kPeriod_ANPKeyCode = 56,
+ kAltLeft_ANPKeyCode = 57,
+ kAltRight_ANPKeyCode = 58,
+ kShiftLeft_ANPKeyCode = 59,
+ kShiftRight_ANPKeyCode = 60,
+ kTab_ANPKeyCode = 61,
+ kSpace_ANPKeyCode = 62,
+ kSym_ANPKeyCode = 63,
+ kExplorer_ANPKeyCode = 64,
+ kEnvelope_ANPKeyCode = 65,
+ kNewline_ANPKeyCode = 66,
+ kDel_ANPKeyCode = 67,
+ kGrave_ANPKeyCode = 68,
+ kMinus_ANPKeyCode = 69,
+ kEquals_ANPKeyCode = 70,
+ kLeftBracket_ANPKeyCode = 71,
+ kRightBracket_ANPKeyCode = 72,
+ kBackslash_ANPKeyCode = 73,
+ kSemicolon_ANPKeyCode = 74,
+ kApostrophe_ANPKeyCode = 75,
+ kSlash_ANPKeyCode = 76,
+ kAt_ANPKeyCode = 77,
+ kNum_ANPKeyCode = 78,
+ kHeadSetHook_ANPKeyCode = 79,
+ kFocus_ANPKeyCode = 80,
+ kPlus_ANPKeyCode = 81,
+ kMenu_ANPKeyCode = 82,
+ kNotification_ANPKeyCode = 83,
+ kSearch_ANPKeyCode = 84,
+ kMediaPlayPause_ANPKeyCode = 85,
+ kMediaStop_ANPKeyCode = 86,
+ kMediaNext_ANPKeyCode = 87,
+ kMediaPrevious_ANPKeyCode = 88,
+ kMediaRewind_ANPKeyCode = 89,
+ kMediaFastForward_ANPKeyCode = 90,
+ kMute_ANPKeyCode = 91,
+ kPageUp_ANPKeyCode = 92,
+ kPageDown_ANPKeyCode = 93,
+ kPictsymbols_ANPKeyCode = 94,
+ kSwitchCharset_ANPKeyCode = 95,
+ kButtonA_ANPKeyCode = 96,
+ kButtonB_ANPKeyCode = 97,
+ kButtonC_ANPKeyCode = 98,
+ kButtonX_ANPKeyCode = 99,
+ kButtonY_ANPKeyCode = 100,
+ kButtonZ_ANPKeyCode = 101,
+ kButtonL1_ANPKeyCode = 102,
+ kButtonR1_ANPKeyCode = 103,
+ kButtonL2_ANPKeyCode = 104,
+ kButtonR2_ANPKeyCode = 105,
+ kButtonThumbL_ANPKeyCode = 106,
+ kButtonThumbR_ANPKeyCode = 107,
+ kButtonStart_ANPKeyCode = 108,
+ kButtonSelect_ANPKeyCode = 109,
+ kButtonMode_ANPKeyCode = 110,
+
+ // NOTE: If you add a new keycode here you must also add it to several other files.
+ // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
+};
+
+#endif
diff --git a/dom/plugins/base/android/ANPLog.cpp b/dom/plugins/base/android/ANPLog.cpp
new file mode 100644
index 000000000..7ce13107b
--- /dev/null
+++ b/dom/plugins/base/android/ANPLog.cpp
@@ -0,0 +1,26 @@
+/* -*- Mode: IDL; 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 "assert.h"
+#include "ANPBase.h"
+#include <android/log.h>
+
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name) (obj)->name = anp_log_##name
+
+void
+anp_log_log(ANPLogType type, const char format[], ...) {
+
+ va_list argp;
+ va_start(argp,format);
+ __android_log_vprint(type == kError_ANPLogType ? ANDROID_LOG_ERROR : type == kWarning_ANPLogType ?
+ ANDROID_LOG_WARN : ANDROID_LOG_INFO, "GeckoPluginLog", format, argp);
+ va_end(argp);
+}
+
+void InitLogInterface(ANPLogInterfaceV0 *i) {
+ _assert(i->inSize == sizeof(*i));
+ ASSIGN(i, log);
+}
diff --git a/dom/plugins/base/android/ANPNativeWindow.cpp b/dom/plugins/base/android/ANPNativeWindow.cpp
new file mode 100644
index 000000000..88b43bd4a
--- /dev/null
+++ b/dom/plugins/base/android/ANPNativeWindow.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: IDL; 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/. */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include <android/log.h>
+#include "ANPBase.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsNPAPIPluginInstance.h"
+#include "gfxRect.h"
+
+using namespace mozilla;
+
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name) (obj)->name = anp_native_window_##name
+
+static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+ return pinst->AcquireContentWindow();
+}
+
+static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) {
+ // NativeWindow is TopLeft if uninverted.
+ gl::OriginPos newOriginPos = gl::OriginPos::TopLeft;
+ if (isContentInverted)
+ newOriginPos = gl::OriginPos::BottomLeft;
+
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+ pinst->SetOriginPos(newOriginPos);
+ pinst->RedrawPlugin();
+}
+
+
+void InitNativeWindowInterface(ANPNativeWindowInterfaceV0* i) {
+ ASSIGN(i, acquireNativeWindow);
+ ASSIGN(i, invertPluginContent);
+}
diff --git a/dom/plugins/base/android/ANPSurface.cpp b/dom/plugins/base/android/ANPSurface.cpp
new file mode 100644
index 000000000..b6a699f28
--- /dev/null
+++ b/dom/plugins/base/android/ANPSurface.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode: IDL; 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 <dlfcn.h>
+#include <android/log.h>
+#include "ANPBase.h"
+
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name) (obj)->name = anp_surface_##name
+
+#define CLEAR_EXCEPTION(env) if (env->ExceptionOccurred()) env->ExceptionClear();
+
+#define ANDROID_REGION_SIZE 512
+
+enum {
+ PIXEL_FORMAT_RGBA_8888 = 1,
+ PIXEL_FORMAT_RGB_565 = 4,
+};
+
+struct SurfaceInfo {
+ uint32_t w;
+ uint32_t h;
+ uint32_t s;
+ uint32_t usage;
+ uint32_t format;
+ unsigned char* bits;
+ uint32_t reserved[2];
+};
+
+typedef struct ARect {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} ARect;
+
+
+// used to cache JNI method and field IDs for Surface Objects
+static struct ANPSurfaceInterfaceJavaGlue {
+ bool initialized;
+ jmethodID getSurfaceHolder;
+ jmethodID getSurface;
+ jfieldID surfacePointer;
+} gSurfaceJavaGlue;
+
+static struct ANPSurfaceFunctions {
+ bool initialized;
+
+ int (* lock)(void*, SurfaceInfo*, void*);
+ int (* unlockAndPost)(void*);
+
+ void* (* regionConstructor)(void*);
+ void (* setRegion)(void*, ARect const&);
+} gSurfaceFunctions;
+
+
+static inline void* getSurface(JNIEnv* env, jobject view) {
+ if (!env || !view) {
+ return nullptr;
+ }
+
+ if (!gSurfaceJavaGlue.initialized) {
+
+ jclass surfaceViewClass = env->FindClass("android/view/SurfaceView");
+ gSurfaceJavaGlue.getSurfaceHolder = env->GetMethodID(surfaceViewClass, "getHolder", "()Landroid/view/SurfaceHolder;");
+
+ jclass surfaceHolderClass = env->FindClass("android/view/SurfaceHolder");
+ gSurfaceJavaGlue.getSurface = env->GetMethodID(surfaceHolderClass, "getSurface", "()Landroid/view/Surface;");
+
+ jclass surfaceClass = env->FindClass("android/view/Surface");
+ gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
+ "mSurfacePointer", "I");
+
+ if (!gSurfaceJavaGlue.surfacePointer) {
+ CLEAR_EXCEPTION(env);
+
+ // It was something else in 2.2.
+ gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
+ "mSurface", "I");
+
+ if (!gSurfaceJavaGlue.surfacePointer) {
+ CLEAR_EXCEPTION(env);
+
+ // And something else in 2.3+
+ gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
+ "mNativeSurface", "I");
+
+ CLEAR_EXCEPTION(env);
+ }
+ }
+
+ if (!gSurfaceJavaGlue.surfacePointer) {
+ LOG("Failed to acquire surface pointer");
+ return nullptr;
+ }
+
+ env->DeleteLocalRef(surfaceClass);
+ env->DeleteLocalRef(surfaceViewClass);
+ env->DeleteLocalRef(surfaceHolderClass);
+
+ gSurfaceJavaGlue.initialized = (gSurfaceJavaGlue.surfacePointer != nullptr);
+ }
+
+ jobject holder = env->CallObjectMethod(view, gSurfaceJavaGlue.getSurfaceHolder);
+ jobject surface = env->CallObjectMethod(holder, gSurfaceJavaGlue.getSurface);
+ jint surfacePointer = env->GetIntField(surface, gSurfaceJavaGlue.surfacePointer);
+
+ env->DeleteLocalRef(holder);
+ env->DeleteLocalRef(surface);
+
+ return (void*)surfacePointer;
+}
+
+static ANPBitmapFormat convertPixelFormat(int32_t format) {
+ switch (format) {
+ case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_ANPBitmapFormat;
+ case PIXEL_FORMAT_RGB_565: return kRGB_565_ANPBitmapFormat;
+ default: return kUnknown_ANPBitmapFormat;
+ }
+}
+
+static int bytesPerPixel(int32_t format) {
+ switch (format) {
+ case PIXEL_FORMAT_RGBA_8888: return 4;
+ case PIXEL_FORMAT_RGB_565: return 2;
+ default: return -1;
+ }
+}
+
+static bool init() {
+ if (gSurfaceFunctions.initialized)
+ return true;
+
+ void* handle = dlopen("libsurfaceflinger_client.so", RTLD_LAZY);
+
+ if (!handle) {
+ LOG("Failed to open libsurfaceflinger_client.so");
+ return false;
+ }
+
+ gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb");
+ gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv");
+
+
+ if (!gSurfaceFunctions.lock) {
+ // Stuff changed in 3.0/4.0
+ handle = dlopen("libgui.so", RTLD_LAZY);
+ gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE");
+ gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv");
+ }
+
+ handle = dlopen("libui.so", RTLD_LAZY);
+ if (!handle) {
+ LOG("Failed to open libui.so");
+ return false;
+ }
+
+ gSurfaceFunctions.regionConstructor = (void* (*)(void*))dlsym(handle, "_ZN7android6RegionC1Ev");
+ gSurfaceFunctions.setRegion = (void (*)(void*, ARect const&))dlsym(handle, "_ZN7android6Region3setERKNS_4RectE");
+
+ gSurfaceFunctions.initialized = (gSurfaceFunctions.lock && gSurfaceFunctions.unlockAndPost &&
+ gSurfaceFunctions.regionConstructor && gSurfaceFunctions.setRegion);
+ LOG("Initialized? %d\n", gSurfaceFunctions.initialized);
+ return gSurfaceFunctions.initialized;
+}
+
+// FIXME: All of this should be changed to use the equivalent things in AndroidBridge, bug 758612
+static bool anp_surface_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) {
+ if (!bitmap || !surfaceView) {
+ return false;
+ }
+
+ void* surface = getSurface(env, surfaceView);
+
+ if (!bitmap || !surface) {
+ return false;
+ }
+
+ if (!init()) {
+ return false;
+ }
+
+ void* region = nullptr;
+ if (dirtyRect) {
+ region = malloc(ANDROID_REGION_SIZE);
+ gSurfaceFunctions.regionConstructor(region);
+
+ ARect rect;
+ rect.left = dirtyRect->left;
+ rect.top = dirtyRect->top;
+ rect.right = dirtyRect->right;
+ rect.bottom = dirtyRect->bottom;
+
+ gSurfaceFunctions.setRegion(region, rect);
+ }
+
+ SurfaceInfo info;
+ int err = gSurfaceFunctions.lock(surface, &info, region);
+ if (err < 0) {
+ LOG("Failed to lock surface");
+ return false;
+ }
+
+ // the surface may have expanded the dirty region so we must to pass that
+ // information back to the plugin.
+ if (dirtyRect) {
+ ARect* dirtyBounds = (ARect*)region; // The bounds are the first member, so this should work!
+
+ dirtyRect->left = dirtyBounds->left;
+ dirtyRect->right = dirtyBounds->right;
+ dirtyRect->top = dirtyBounds->top;
+ dirtyRect->bottom = dirtyBounds->bottom;
+ }
+
+ if (region)
+ free(region);
+
+ int bpr = info.s * bytesPerPixel(info.format);
+
+ bitmap->format = convertPixelFormat(info.format);
+ bitmap->width = info.w;
+ bitmap->height = info.h;
+ bitmap->rowBytes = bpr;
+
+ if (info.w > 0 && info.h > 0) {
+ bitmap->baseAddr = info.bits;
+ } else {
+ bitmap->baseAddr = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+static void anp_surface_unlock(JNIEnv* env, jobject surfaceView) {
+ if (!surfaceView) {
+ return;
+ }
+
+ if (!init()) {
+ return;
+ }
+
+ void* surface = getSurface(env, surfaceView);
+
+ if (!surface) {
+ return;
+ }
+
+ gSurfaceFunctions.unlockAndPost(surface);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void InitSurfaceInterface(ANPSurfaceInterfaceV0* i) {
+ ASSIGN(i, lock);
+ ASSIGN(i, unlock);
+
+ // setup the java glue struct
+ gSurfaceJavaGlue.initialized = false;
+
+ // setup the function struct
+ gSurfaceFunctions.initialized = false;
+}
diff --git a/dom/plugins/base/android/ANPSystem.cpp b/dom/plugins/base/android/ANPSystem.cpp
new file mode 100644
index 000000000..d5b2a7710
--- /dev/null
+++ b/dom/plugins/base/android/ANPSystem.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: IDL; 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 "base/basictypes.h"
+
+#include "ANPBase.h"
+#include "GeneratedJNIWrappers.h"
+#include "PluginPRLibrary.h"
+#include "assert.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsNPAPIPlugin.h"
+
+#include <android/log.h>
+
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name) (obj)->name = anp_system_##name
+
+const char*
+anp_system_getApplicationDataDirectory(NPP instance)
+{
+ static const char *dir = nullptr;
+ static const char *privateDir = nullptr;
+
+ bool isPrivate = false;
+
+ if (!dir) {
+ dir = getenv("ANDROID_PLUGIN_DATADIR");
+ }
+
+ if (!privateDir) {
+ privateDir = getenv("ANDROID_PLUGIN_DATADIR_PRIVATE");
+ }
+
+ if (!instance) {
+ return dir;
+ }
+
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+ if (pinst && NS_SUCCEEDED(pinst->IsPrivateBrowsing(&isPrivate)) && isPrivate) {
+ return privateDir;
+ }
+
+ return dir;
+}
+
+const char*
+anp_system_getApplicationDataDirectory()
+{
+ return anp_system_getApplicationDataDirectory(nullptr);
+}
+
+jclass anp_system_loadJavaClass(NPP instance, const char* className)
+{
+ LOG("%s", __PRETTY_FUNCTION__);
+
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+ mozilla::PluginPRLibrary* lib = static_cast<mozilla::PluginPRLibrary*>(pinst->GetPlugin()->GetLibrary());
+
+ nsCString libName;
+ lib->GetLibraryPath(libName);
+
+ return mozilla::java::GeckoAppShell::LoadPluginClass(className, libName).Forget();
+}
+
+void anp_system_setPowerState(NPP instance, ANPPowerState powerState)
+{
+ nsNPAPIPluginInstance* pinst = nsNPAPIPluginInstance::GetFromNPP(instance);
+
+ if (pinst) {
+ pinst->SetWakeLock(powerState == kScreenOn_ANPPowerState);
+ }
+}
+
+void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i) {
+ _assert(i->inSize == sizeof(*i));
+ ASSIGN(i, getApplicationDataDirectory);
+ ASSIGN(i, loadJavaClass);
+ ASSIGN(i, setPowerState);
+}
+
+void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i) {
+ _assert(i->inSize == sizeof(*i));
+ ASSIGN(i, getApplicationDataDirectory);
+ ASSIGN(i, loadJavaClass);
+ ASSIGN(i, setPowerState);
+}
diff --git a/dom/plugins/base/android/ANPVideo.cpp b/dom/plugins/base/android/ANPVideo.cpp
new file mode 100644
index 000000000..185ab1194
--- /dev/null
+++ b/dom/plugins/base/android/ANPVideo.cpp
@@ -0,0 +1,55 @@
+/* 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 <android/log.h>
+#include "ANPBase.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsNPAPIPluginInstance.h"
+#include "gfxRect.h"
+
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name) (obj)->name = anp_video_##name
+
+using namespace mozilla;
+
+typedef nsNPAPIPluginInstance::VideoInfo VideoInfo;
+
+static ANPNativeWindow anp_video_acquireNativeWindow(NPP instance) {
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+ return pinst->AcquireVideoWindow();
+}
+
+static void anp_video_setWindowDimensions(NPP instance, const ANPNativeWindow window,
+ const ANPRectF* dimensions) {
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+ gfxRect rect(dimensions->left, dimensions->top,
+ dimensions->right - dimensions->left,
+ dimensions->bottom - dimensions->top);
+
+ pinst->SetVideoDimensions(window, rect);
+ pinst->RedrawPlugin();
+}
+
+static void anp_video_releaseNativeWindow(NPP instance, ANPNativeWindow window) {
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+ pinst->ReleaseVideoWindow(window);
+ pinst->RedrawPlugin();
+}
+
+static void anp_video_setFramerateCallback(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc callback) {
+ // Bug 722682
+ NOT_IMPLEMENTED();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void InitVideoInterfaceV1(ANPVideoInterfaceV1* i) {
+ ASSIGN(i, acquireNativeWindow);
+ ASSIGN(i, setWindowDimensions);
+ ASSIGN(i, releaseNativeWindow);
+ ASSIGN(i, setFramerateCallback);
+}
diff --git a/dom/plugins/base/android/ANPWindow.cpp b/dom/plugins/base/android/ANPWindow.cpp
new file mode 100644
index 000000000..e9003aff5
--- /dev/null
+++ b/dom/plugins/base/android/ANPWindow.cpp
@@ -0,0 +1,152 @@
+/* -*- 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 "base/basictypes.h"
+#include "assert.h"
+#include "ANPBase.h"
+#include <android/log.h>
+#include "nsNPAPIPluginInstance.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsWindow.h"
+#include "mozilla/dom/ScreenOrientation.h"
+
+#undef LOG
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name) (obj)->name = anp_window_##name
+
+using namespace mozilla;
+using namespace mozilla::widget;
+using namespace mozilla::dom;
+
+void
+anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count)
+{
+ NOT_IMPLEMENTED();
+}
+
+void
+anp_window_clearVisibleRects(NPP instance)
+{
+ NOT_IMPLEMENTED();
+}
+
+void
+anp_window_showKeyboard(NPP instance, bool value)
+{
+ InputContext context;
+ context.mIMEState.mEnabled = value ? IMEState::PLUGIN : IMEState::DISABLED;
+ context.mIMEState.mOpen = value ? IMEState::OPEN : IMEState::CLOSED;
+ context.mActionHint.Assign(EmptyString());
+
+ InputContextAction action;
+ action.mCause = InputContextAction::CAUSE_UNKNOWN;
+ action.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
+
+ nsWindow* window = nsWindow::TopWindow();
+ if (!window) {
+ LOG("Couldn't get top window?");
+ return;
+ }
+
+ window->SetInputContext(context, action);
+}
+
+void
+anp_window_requestFullScreen(NPP instance)
+{
+ nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+ RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
+ if (!owner) {
+ return;
+ }
+
+ owner->RequestFullScreen();
+}
+
+void
+anp_window_exitFullScreen(NPP instance)
+{
+ nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+ RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
+ if (!owner) {
+ return;
+ }
+
+ owner->ExitFullScreen();
+}
+
+void
+anp_window_requestCenterFitZoom(NPP instance)
+{
+ NOT_IMPLEMENTED();
+}
+
+ANPRectI
+anp_window_visibleRect(NPP instance)
+{
+ ANPRectI rect = { 0, 0, 0, 0 };
+
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+ nsIntSize currentSize = pinst->CurrentSize();
+ rect.left = rect.top = 0;
+ rect.right = currentSize.width;
+ rect.bottom = currentSize.height;
+
+ return rect;
+}
+
+void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation)
+{
+ short newOrientation;
+
+ // Convert to the ActivityInfo equivalent
+ switch (orientation) {
+ case kFixedLandscape_ANPScreenOrientation:
+ newOrientation = eScreenOrientation_LandscapePrimary;
+ break;
+ case kFixedPortrait_ANPScreenOrientation:
+ newOrientation = eScreenOrientation_PortraitPrimary;
+ break;
+ case kLandscape_ANPScreenOrientation:
+ newOrientation = eScreenOrientation_LandscapePrimary |
+ eScreenOrientation_LandscapeSecondary;
+ break;
+ case kPortrait_ANPScreenOrientation:
+ newOrientation = eScreenOrientation_PortraitPrimary |
+ eScreenOrientation_PortraitSecondary;
+ break;
+ default:
+ newOrientation = eScreenOrientation_None;
+ break;
+ }
+
+ nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+ pinst->SetFullScreenOrientation(newOrientation);
+}
+
+void InitWindowInterface(ANPWindowInterfaceV0 *i) {
+ _assert(i->inSize == sizeof(*i));
+ ASSIGN(i, setVisibleRects);
+ ASSIGN(i, clearVisibleRects);
+ ASSIGN(i, showKeyboard);
+ ASSIGN(i, requestFullScreen);
+ ASSIGN(i, exitFullScreen);
+ ASSIGN(i, requestCenterFitZoom);
+}
+
+void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i) {
+ _assert(i->inSize == sizeof(*i));
+ ASSIGN(i, setVisibleRects);
+ ASSIGN(i, clearVisibleRects);
+ ASSIGN(i, showKeyboard);
+ ASSIGN(i, requestFullScreen);
+ ASSIGN(i, exitFullScreen);
+ ASSIGN(i, requestCenterFitZoom);
+ ASSIGN(i, visibleRect);
+ ASSIGN(i, requestFullScreenOrientation);
+}
diff --git a/dom/plugins/base/android/android_npapi.h b/dom/plugins/base/android/android_npapi.h
new file mode 100644
index 000000000..16463d799
--- /dev/null
+++ b/dom/plugins/base/android/android_npapi.h
@@ -0,0 +1,1027 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Defines the android-specific types and functions as part of npapi
+
+ In particular, defines the window and event types that are passed to
+ NPN_GetValue, NPP_SetWindow and NPP_HandleEvent.
+
+ To minimize what native libraries the plugin links against, some
+ functionality is provided via function-ptrs (e.g. time, sound)
+ */
+
+#ifndef android_npapi_H
+#define android_npapi_H
+
+#include <stdint.h>
+#include <jni.h>
+#include "npapi.h"
+#include "GLDefs.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// General types
+
+enum ANPBitmapFormats {
+ kUnknown_ANPBitmapFormat = 0,
+ kRGBA_8888_ANPBitmapFormat = 1,
+ kRGB_565_ANPBitmapFormat = 2
+};
+typedef int32_t ANPBitmapFormat;
+
+struct ANPPixelPacking {
+ uint8_t AShift;
+ uint8_t ABits;
+ uint8_t RShift;
+ uint8_t RBits;
+ uint8_t GShift;
+ uint8_t GBits;
+ uint8_t BShift;
+ uint8_t BBits;
+};
+
+struct ANPBitmap {
+ void* baseAddr;
+ ANPBitmapFormat format;
+ int32_t width;
+ int32_t height;
+ int32_t rowBytes;
+};
+
+struct ANPRectF {
+ float left;
+ float top;
+ float right;
+ float bottom;
+};
+
+struct ANPRectI {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+};
+
+struct ANPCanvas;
+struct ANPMatrix;
+struct ANPPaint;
+struct ANPPath;
+struct ANPTypeface;
+
+///////////////////////////////////////////////////////////////////////////////
+// NPN_GetValue
+
+/** queries for a specific ANPInterface.
+
+ Maybe called with NULL for the NPP instance
+
+ NPN_GetValue(inst, interface_enum, ANPInterface*)
+ */
+#define kLogInterfaceV0_ANPGetValue ((NPNVariable)1000)
+#define kAudioTrackInterfaceV0_ANPGetValue ((NPNVariable)1001)
+#define kCanvasInterfaceV0_ANPGetValue ((NPNVariable)1002)
+#define kMatrixInterfaceV0_ANPGetValue ((NPNVariable)1003)
+#define kPaintInterfaceV0_ANPGetValue ((NPNVariable)1004)
+#define kPathInterfaceV0_ANPGetValue ((NPNVariable)1005)
+#define kTypefaceInterfaceV0_ANPGetValue ((NPNVariable)1006)
+#define kWindowInterfaceV0_ANPGetValue ((NPNVariable)1007)
+#define kBitmapInterfaceV0_ANPGetValue ((NPNVariable)1008)
+#define kSurfaceInterfaceV0_ANPGetValue ((NPNVariable)1009)
+#define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010)
+#define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011)
+
+#define kAudioTrackInterfaceV1_ANPGetValue ((NPNVariable)1012)
+#define kOpenGLInterfaceV0_ANPGetValue ((NPNVariable)1013)
+#define kWindowInterfaceV1_ANPGetValue ((NPNVariable)1014)
+#define kVideoInterfaceV0_ANPGetValue ((NPNVariable)1015)
+#define kSystemInterfaceV1_ANPGetValue ((NPNVariable)1016)
+#define kSystemInterfaceV2_ANPGetValue ((NPNVariable)1017)
+#define kWindowInterfaceV2_ANPGetValue ((NPNVariable)1018)
+#define kNativeWindowInterfaceV0_ANPGetValue ((NPNVariable)1019)
+#define kVideoInterfaceV1_ANPGetValue ((NPNVariable)1020)
+
+/** queries for the drawing models supported on this device.
+
+ NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits)
+ */
+#define kSupportedDrawingModel_ANPGetValue ((NPNVariable)2000)
+
+/** queries for the context (android.content.Context) of the plugin. If no
+ instance is specified the application's context is returned. If the instance
+ is given then the context returned is identical to the context used to
+ create the webview in which that instance resides.
+
+ NOTE: Holding onto a non-application context after your instance has been
+ destroyed will cause a memory leak. Refer to the android documentation to
+ determine what context is best suited for your particular scenario.
+
+ NPN_GetValue(inst, kJavaContext_ANPGetValue, jobject context)
+ */
+#define kJavaContext_ANPGetValue ((NPNVariable)2001)
+
+///////////////////////////////////////////////////////////////////////////////
+// NPN_SetValue
+
+/** Request to set the drawing model. SetValue will return false if the drawing
+ model is not supported or has insufficient information for configuration.
+
+ NPN_SetValue(inst, kRequestDrawingModel_ANPSetValue, (void*)foo_ANPDrawingModel)
+ */
+#define kRequestDrawingModel_ANPSetValue ((NPPVariable)1000)
+
+/** These are used as bitfields in ANPSupportedDrawingModels_EnumValue,
+ and as-is in ANPRequestDrawingModel_EnumValue. The drawing model determines
+ how to interpret the ANPDrawingContext provided in the Draw event and how
+ to interpret the NPWindow->window field.
+ */
+enum ANPDrawingModels {
+ /** Draw into a bitmap from the browser thread in response to a Draw event.
+ NPWindow->window is reserved (ignore)
+ */
+ kBitmap_ANPDrawingModel = 1 << 0,
+ /** Draw into a surface (e.g. raster, openGL, etc.) using the Java surface
+ interface. When this model is used the browser will invoke the Java
+ class specified in the plugin's apk manifest. From that class the browser
+ will invoke the appropriate method to return an an instance of a android
+ Java View. The instance is then embedded in the html. The plugin can then
+ manipulate the view as it would any normal Java View in android.
+
+ Unlike the bitmap model, a surface model is opaque so no html content
+ behind the plugin will be visible. Unless the plugin needs to be
+ transparent the surface model should be chosen over the bitmap model as
+ it will have better performance.
+
+ Further, a plugin can manipulate some surfaces in native code using the
+ ANPSurfaceInterface. This interface can be used to manipulate Java
+ objects that extend Surface.class by allowing them to access the
+ surface's underlying bitmap in native code. For instance, if a raster
+ surface is used the plugin can lock, draw directly into the bitmap, and
+ unlock the surface in native code without making JNI calls to the Java
+ surface object.
+ */
+ kSurface_ANPDrawingModel = 1 << 1,
+ kOpenGL_ANPDrawingModel = 1 << 2,
+};
+typedef int32_t ANPDrawingModel;
+
+/** Request to receive/disable events. If the pointer is NULL then all flags will
+ be disabled. Otherwise, the event type will be enabled iff its corresponding
+ bit in the EventFlags bit field is set.
+
+ NPN_SetValue(inst, ANPAcceptEvents, (void*)EventFlags)
+ */
+#define kAcceptEvents_ANPSetValue ((NPPVariable)1001)
+
+/** The EventFlags are a set of bits used to determine which types of events the
+ plugin wishes to receive. For example, if the value is 0x03 then both key
+ and touch events will be provided to the plugin.
+ */
+enum ANPEventFlag {
+ kKey_ANPEventFlag = 0x01,
+ kTouch_ANPEventFlag = 0x02,
+};
+typedef uint32_t ANPEventFlags;
+
+///////////////////////////////////////////////////////////////////////////////
+// NPP_GetValue
+
+/** Requests that the plugin return a java surface to be displayed. This will
+ only be used if the plugin has choosen the kSurface_ANPDrawingModel.
+
+ NPP_GetValue(inst, kJavaSurface_ANPGetValue, jobject surface)
+ */
+#define kJavaSurface_ANPGetValue ((NPPVariable)2000)
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ANDROID INTERFACE DEFINITIONS
+
+/** Interfaces provide additional functionality to the plugin via function ptrs.
+ Once an interface is retrieved, it is valid for the lifetime of the plugin
+ (just like browserfuncs).
+
+ All ANPInterfaces begin with an inSize field, which must be set by the
+ caller (plugin) with the number of bytes allocated for the interface.
+ e.g. SomeInterface si; si.inSize = sizeof(si); browser->getvalue(..., &si);
+ */
+struct ANPInterface {
+ uint32_t inSize; // size (in bytes) of this struct
+};
+
+enum ANPLogTypes {
+ kError_ANPLogType = 0, // error
+ kWarning_ANPLogType = 1, // warning
+ kDebug_ANPLogType = 2 // debug only (informational)
+};
+typedef int32_t ANPLogType;
+
+struct ANPLogInterfaceV0 : ANPInterface {
+ /** dumps printf messages to the log file
+ e.g. interface->log(instance, kWarning_ANPLogType, "value is %d", value);
+ */
+ void (*log)(ANPLogType, const char format[], ...);
+};
+
+/** ANPColor is always defined to have the same packing on all platforms, and
+ it is always unpremultiplied.
+
+ This is in contrast to 32bit format(s) in bitmaps, which are premultiplied,
+ and their packing may vary depending on the platform, hence the need for
+ ANPBitmapInterface::getPixelPacking()
+ */
+typedef uint32_t ANPColor;
+#define ANPColor_ASHIFT 24
+#define ANPColor_RSHIFT 16
+#define ANPColor_GSHIFT 8
+#define ANPColor_BSHIFT 0
+#define ANP_MAKE_COLOR(a, r, g, b) \
+ (((a) << ANPColor_ASHIFT) | \
+ ((r) << ANPColor_RSHIFT) | \
+ ((g) << ANPColor_GSHIFT) | \
+ ((b) << ANPColor_BSHIFT))
+
+enum ANPPaintFlag {
+ kAntiAlias_ANPPaintFlag = 1 << 0,
+ kFilterBitmap_ANPPaintFlag = 1 << 1,
+ kDither_ANPPaintFlag = 1 << 2,
+ kUnderlineText_ANPPaintFlag = 1 << 3,
+ kStrikeThruText_ANPPaintFlag = 1 << 4,
+ kFakeBoldText_ANPPaintFlag = 1 << 5,
+};
+typedef uint32_t ANPPaintFlags;
+
+enum ANPPaintStyles {
+ kFill_ANPPaintStyle = 0,
+ kStroke_ANPPaintStyle = 1,
+ kFillAndStroke_ANPPaintStyle = 2
+};
+typedef int32_t ANPPaintStyle;
+
+enum ANPPaintCaps {
+ kButt_ANPPaintCap = 0,
+ kRound_ANPPaintCap = 1,
+ kSquare_ANPPaintCap = 2
+};
+typedef int32_t ANPPaintCap;
+
+enum ANPPaintJoins {
+ kMiter_ANPPaintJoin = 0,
+ kRound_ANPPaintJoin = 1,
+ kBevel_ANPPaintJoin = 2
+};
+typedef int32_t ANPPaintJoin;
+
+enum ANPPaintAligns {
+ kLeft_ANPPaintAlign = 0,
+ kCenter_ANPPaintAlign = 1,
+ kRight_ANPPaintAlign = 2
+};
+typedef int32_t ANPPaintAlign;
+
+enum ANPTextEncodings {
+ kUTF8_ANPTextEncoding = 0,
+ kUTF16_ANPTextEncoding = 1,
+};
+typedef int32_t ANPTextEncoding;
+
+enum ANPTypefaceStyles {
+ kBold_ANPTypefaceStyle = 1 << 0,
+ kItalic_ANPTypefaceStyle = 1 << 1
+};
+typedef uint32_t ANPTypefaceStyle;
+
+typedef uint32_t ANPFontTableTag;
+
+struct ANPFontMetrics {
+ /** The greatest distance above the baseline for any glyph (will be <= 0) */
+ float fTop;
+ /** The recommended distance above the baseline (will be <= 0) */
+ float fAscent;
+ /** The recommended distance below the baseline (will be >= 0) */
+ float fDescent;
+ /** The greatest distance below the baseline for any glyph (will be >= 0) */
+ float fBottom;
+ /** The recommended distance to add between lines of text (will be >= 0) */
+ float fLeading;
+};
+
+struct ANPTypefaceInterfaceV0 : ANPInterface {
+ /** Return a new reference to the typeface that most closely matches the
+ requested name and style. Pass null as the name to return
+ the default font for the requested style. Will never return null
+
+ The 5 generic font names "serif", "sans-serif", "monospace", "cursive",
+ "fantasy" are recognized, and will be mapped to their logical font
+ automatically by this call.
+
+ @param name May be NULL. The name of the font family.
+ @param style The style (normal, bold, italic) of the typeface.
+ @return reference to the closest-matching typeface. Caller must call
+ unref() when they are done with the typeface.
+ */
+ ANPTypeface* (*createFromName)(const char name[], ANPTypefaceStyle);
+
+ /** Return a new reference to the typeface that most closely matches the
+ requested typeface and specified Style. Use this call if you want to
+ pick a new style from the same family of the existing typeface.
+ If family is NULL, this selects from the default font's family.
+
+ @param family May be NULL. The name of the existing type face.
+ @param s The style (normal, bold, italic) of the type face.
+ @return reference to the closest-matching typeface. Call must call
+ unref() when they are done.
+ */
+ ANPTypeface* (*createFromTypeface)(const ANPTypeface* family,
+ ANPTypefaceStyle);
+
+ /** Return the owner count of the typeface. A newly created typeface has an
+ owner count of 1. When the owner count is reaches 0, the typeface is
+ deleted.
+ */
+ int32_t (*getRefCount)(const ANPTypeface*);
+
+ /** Increment the owner count on the typeface
+ */
+ void (*ref)(ANPTypeface*);
+
+ /** Decrement the owner count on the typeface. When the count goes to 0,
+ the typeface is deleted.
+ */
+ void (*unref)(ANPTypeface*);
+
+ /** Return the style bits for the specified typeface
+ */
+ ANPTypefaceStyle (*getStyle)(const ANPTypeface*);
+
+ /** Some fonts are stored in files. If that is true for the fontID, then
+ this returns the byte length of the full file path. If path is not null,
+ then the full path is copied into path (allocated by the caller), up to
+ length bytes. If index is not null, then it is set to the truetype
+ collection index for this font, or 0 if the font is not in a collection.
+
+ Note: getFontPath does not assume that path is a null-terminated string,
+ so when it succeeds, it only copies the bytes of the file name and
+ nothing else (i.e. it copies exactly the number of bytes returned by the
+ function. If the caller wants to treat path[] as a C string, it must be
+ sure that it is allocated at least 1 byte larger than the returned size,
+ and it must copy in the terminating 0.
+
+ If the fontID does not correspond to a file, then the function returns
+ 0, and the path and index parameters are ignored.
+
+ @param fontID The font whose file name is being queried
+ @param path Either NULL, or storage for receiving up to length bytes
+ of the font's file name. Allocated by the caller.
+ @param length The maximum space allocated in path (by the caller).
+ Ignored if path is NULL.
+ @param index Either NULL, or receives the TTC index for this font.
+ If the font is not a TTC, then will be set to 0.
+ @return The byte length of th font's file name, or 0 if the font is not
+ baked by a file.
+ */
+ int32_t (*getFontPath)(const ANPTypeface*, char path[], int32_t length,
+ int32_t* index);
+
+ /** Return a UTF8 encoded path name for the font directory, or NULL if not
+ supported. If returned, this string address will be valid for the life
+ of the plugin instance. It will always end with a '/' character.
+ */
+ const char* (*getFontDirectoryPath)();
+};
+
+struct ANPPaintInterfaceV0 : ANPInterface {
+ /** Return a new paint object, which holds all of the color and style
+ attributes that affect how things (geometry, text, bitmaps) are drawn
+ in a ANPCanvas.
+
+ The paint that is returned is not tied to any particular plugin
+ instance, but it must only be accessed from one thread at a time.
+ */
+ ANPPaint* (*newPaint)();
+ void (*deletePaint)(ANPPaint*);
+
+ ANPPaintFlags (*getFlags)(const ANPPaint*);
+ void (*setFlags)(ANPPaint*, ANPPaintFlags);
+
+ ANPColor (*getColor)(const ANPPaint*);
+ void (*setColor)(ANPPaint*, ANPColor);
+
+ ANPPaintStyle (*getStyle)(const ANPPaint*);
+ void (*setStyle)(ANPPaint*, ANPPaintStyle);
+
+ float (*getStrokeWidth)(const ANPPaint*);
+ float (*getStrokeMiter)(const ANPPaint*);
+ ANPPaintCap (*getStrokeCap)(const ANPPaint*);
+ ANPPaintJoin (*getStrokeJoin)(const ANPPaint*);
+ void (*setStrokeWidth)(ANPPaint*, float);
+ void (*setStrokeMiter)(ANPPaint*, float);
+ void (*setStrokeCap)(ANPPaint*, ANPPaintCap);
+ void (*setStrokeJoin)(ANPPaint*, ANPPaintJoin);
+
+ ANPTextEncoding (*getTextEncoding)(const ANPPaint*);
+ ANPPaintAlign (*getTextAlign)(const ANPPaint*);
+ float (*getTextSize)(const ANPPaint*);
+ float (*getTextScaleX)(const ANPPaint*);
+ float (*getTextSkewX)(const ANPPaint*);
+ void (*setTextEncoding)(ANPPaint*, ANPTextEncoding);
+ void (*setTextAlign)(ANPPaint*, ANPPaintAlign);
+ void (*setTextSize)(ANPPaint*, float);
+ void (*setTextScaleX)(ANPPaint*, float);
+ void (*setTextSkewX)(ANPPaint*, float);
+
+ /** Return the typeface ine paint, or null if there is none. This does not
+ modify the owner count of the returned typeface.
+ */
+ ANPTypeface* (*getTypeface)(const ANPPaint*);
+
+ /** Set the paint's typeface. If the paint already had a non-null typeface,
+ its owner count is decremented. If the new typeface is non-null, its
+ owner count is incremented.
+ */
+ void (*setTypeface)(ANPPaint*, ANPTypeface*);
+
+ /** Return the width of the text. If bounds is not null, return the bounds
+ of the text in that rectangle.
+ */
+ float (*measureText)(ANPPaint*, const void* text, uint32_t byteLength,
+ ANPRectF* bounds);
+
+ /** Return the number of unichars specifed by the text.
+ If widths is not null, returns the array of advance widths for each
+ unichar.
+ If bounds is not null, returns the array of bounds for each unichar.
+ */
+ int (*getTextWidths)(ANPPaint*, const void* text, uint32_t byteLength,
+ float widths[], ANPRectF bounds[]);
+
+ /** Return in metrics the spacing values for text, respecting the paint's
+ typeface and pointsize, and return the spacing between lines
+ (descent - ascent + leading). If metrics is NULL, it will be ignored.
+ */
+ float (*getFontMetrics)(ANPPaint*, ANPFontMetrics* metrics);
+};
+
+struct ANPCanvasInterfaceV0 : ANPInterface {
+ /** Return a canvas that will draw into the specified bitmap. Note: the
+ canvas copies the fields of the bitmap, so it need not persist after
+ this call, but the canvas DOES point to the same pixel memory that the
+ bitmap did, so the canvas should not be used after that pixel memory
+ goes out of scope. In the case of creating a canvas to draw into the
+ pixels provided by kDraw_ANPEventType, those pixels are only while
+ handling that event.
+
+ The canvas that is returned is not tied to any particular plugin
+ instance, but it must only be accessed from one thread at a time.
+ */
+ ANPCanvas* (*newCanvas)(const ANPBitmap*);
+ void (*deleteCanvas)(ANPCanvas*);
+
+ void (*save)(ANPCanvas*);
+ void (*restore)(ANPCanvas*);
+ void (*translate)(ANPCanvas*, float tx, float ty);
+ void (*scale)(ANPCanvas*, float sx, float sy);
+ void (*rotate)(ANPCanvas*, float degrees);
+ void (*skew)(ANPCanvas*, float kx, float ky);
+ void (*concat)(ANPCanvas*, const ANPMatrix*);
+ void (*clipRect)(ANPCanvas*, const ANPRectF*);
+ void (*clipPath)(ANPCanvas*, const ANPPath*);
+
+ /** Return the current matrix on the canvas
+ */
+ void (*getTotalMatrix)(ANPCanvas*, ANPMatrix*);
+ /** Return the current clip bounds in local coordinates, expanding it to
+ account for antialiasing edge effects if aa is true. If the
+ current clip is empty, return false and ignore the bounds argument.
+ */
+ bool (*getLocalClipBounds)(ANPCanvas*, ANPRectF* bounds, bool aa);
+ /** Return the current clip bounds in device coordinates in bounds. If the
+ current clip is empty, return false and ignore the bounds argument.
+ */
+ bool (*getDeviceClipBounds)(ANPCanvas*, ANPRectI* bounds);
+
+ void (*drawColor)(ANPCanvas*, ANPColor);
+ void (*drawPaint)(ANPCanvas*, const ANPPaint*);
+ void (*drawLine)(ANPCanvas*, float x0, float y0, float x1, float y1,
+ const ANPPaint*);
+ void (*drawRect)(ANPCanvas*, const ANPRectF*, const ANPPaint*);
+ void (*drawOval)(ANPCanvas*, const ANPRectF*, const ANPPaint*);
+ void (*drawPath)(ANPCanvas*, const ANPPath*, const ANPPaint*);
+ void (*drawText)(ANPCanvas*, const void* text, uint32_t byteLength,
+ float x, float y, const ANPPaint*);
+ void (*drawPosText)(ANPCanvas*, const void* text, uint32_t byteLength,
+ const float xy[], const ANPPaint*);
+ void (*drawBitmap)(ANPCanvas*, const ANPBitmap*, float x, float y,
+ const ANPPaint*);
+ void (*drawBitmapRect)(ANPCanvas*, const ANPBitmap*,
+ const ANPRectI* src, const ANPRectF* dst,
+ const ANPPaint*);
+};
+
+struct ANPWindowInterfaceV0 : ANPInterface {
+ /** Registers a set of rectangles that the plugin would like to keep on
+ screen. The rectangles are listed in order of priority with the highest
+ priority rectangle in location rects[0]. The browser will attempt to keep
+ as many of the rectangles on screen as possible and will scroll them into
+ view in response to the invocation of this method and other various events.
+ The count specifies how many rectangles are in the array. If the count is
+ zero it signals the browser that any existing rectangles should be cleared
+ and no rectangles will be tracked.
+ */
+ void (*setVisibleRects)(NPP instance, const ANPRectI rects[], int32_t count);
+ /** Clears any rectangles that are being tracked as a result of a call to
+ setVisibleRects. This call is equivalent to setVisibleRect(inst, NULL, 0).
+ */
+ void (*clearVisibleRects)(NPP instance);
+ /** Given a boolean value of true the device will be requested to provide
+ a keyboard. A value of false will result in a request to hide the
+ keyboard. Further, the on-screen keyboard will not be displayed if a
+ physical keyboard is active.
+ */
+ void (*showKeyboard)(NPP instance, bool value);
+ /** Called when a plugin wishes to enter into full screen mode. The plugin's
+ Java class (defined in the plugin's apk manifest) will be called
+ asynchronously to provide a View object to be displayed full screen.
+ */
+ void (*requestFullScreen)(NPP instance);
+ /** Called when a plugin wishes to exit from full screen mode. As a result,
+ the plugin's full screen view will be discarded by the view system.
+ */
+ void (*exitFullScreen)(NPP instance);
+ /** Called when a plugin wishes to be zoomed and centered in the current view.
+ */
+ void (*requestCenterFitZoom)(NPP instance);
+};
+
+enum ANPScreenOrientations {
+ /** No preference specified: let the system decide the best orientation.
+ */
+ kDefault_ANPScreenOrientation = 0,
+ /** Would like to have the screen in a landscape orientation, but it will
+ not allow for 180 degree rotations.
+ */
+ kFixedLandscape_ANPScreenOrientation = 1,
+ /** Would like to have the screen in a portrait orientation, but it will
+ not allow for 180 degree rotations.
+ */
+ kFixedPortrait_ANPScreenOrientation = 2,
+ /** Would like to have the screen in landscape orientation, but can use the
+ sensor to change which direction the screen is facing.
+ */
+ kLandscape_ANPScreenOrientation = 3,
+ /** Would like to have the screen in portrait orientation, but can use the
+ sensor to change which direction the screen is facing.
+ */
+ kPortrait_ANPScreenOrientation = 4
+};
+
+typedef int32_t ANPScreenOrientation;
+
+struct ANPWindowInterfaceV2 : ANPWindowInterfaceV0 {
+ /** Returns a rectangle representing the visible area of the plugin on
+ screen. The coordinates are relative to the size of the plugin in the
+ document and therefore will never be negative or exceed the plugin's size.
+ */
+ ANPRectI (*visibleRect)(NPP instance);
+
+ /** Called when the plugin wants to specify a particular screen orientation
+ when entering into full screen mode. The orientation must be set prior
+ to entering into full screen. After entering full screen any subsequent
+ changes will be updated the next time the plugin goes full screen.
+ */
+ void (*requestFullScreenOrientation)(NPP instance, ANPScreenOrientation orientation);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum ANPSampleFormats {
+ kUnknown_ANPSamleFormat = 0,
+ kPCM16Bit_ANPSampleFormat = 1,
+ kPCM8Bit_ANPSampleFormat = 2
+};
+typedef int32_t ANPSampleFormat;
+
+/** The audio buffer is passed to the callback proc to request more samples.
+ It is owned by the system, and the callback may read it, but should not
+ maintain a pointer to it outside of the scope of the callback proc.
+ */
+struct ANPAudioBuffer {
+ // RO - repeat what was specified in newTrack()
+ int32_t channelCount;
+ // RO - repeat what was specified in newTrack()
+ ANPSampleFormat format;
+ /** This buffer is owned by the caller. Inside the callback proc, up to
+ "size" bytes of sample data should be written into this buffer. The
+ address is only valid for the scope of a single invocation of the
+ callback proc.
+ */
+ void* bufferData;
+ /** On input, specifies the maximum number of bytes that can be written
+ to "bufferData". On output, specifies the actual number of bytes that
+ the callback proc wrote into "bufferData".
+ */
+ uint32_t size;
+};
+
+enum ANPAudioEvents {
+ /** This event is passed to the callback proc when the audio-track needs
+ more sample data written to the provided buffer parameter.
+ */
+ kMoreData_ANPAudioEvent = 0,
+ /** This event is passed to the callback proc if the audio system runs out
+ of sample data. In this event, no buffer parameter will be specified
+ (i.e. NULL will be passed to the 3rd parameter).
+ */
+ kUnderRun_ANPAudioEvent = 1
+};
+typedef int32_t ANPAudioEvent;
+
+/** Called to feed sample data to the track. This will be called in a separate
+ thread. However, you may call trackStop() from the callback (but you
+ cannot delete the track).
+
+ For example, when you have written the last chunk of sample data, you can
+ immediately call trackStop(). This will take effect after the current
+ buffer has been played.
+
+ The "user" parameter is the same value that was passed to newTrack()
+ */
+typedef void (*ANPAudioCallbackProc)(ANPAudioEvent event, void* user,
+ ANPAudioBuffer* buffer);
+
+struct ANPAudioTrack; // abstract type for audio tracks
+
+struct ANPAudioTrackInterfaceV0 : ANPInterface {
+ /** Create a new audio track, or NULL on failure. The track is initially in
+ the stopped state and therefore ANPAudioCallbackProc will not be called
+ until the track is started.
+ */
+ ANPAudioTrack* (*newTrack)(uint32_t sampleRate, // sampling rate in Hz
+ ANPSampleFormat,
+ int channelCount, // MONO=1, STEREO=2
+ ANPAudioCallbackProc,
+ void* user);
+ /** Deletes a track that was created using newTrack. The track can be
+ deleted in any state and it waits for the ANPAudioCallbackProc thread
+ to exit before returning.
+ */
+ void (*deleteTrack)(ANPAudioTrack*);
+
+ void (*start)(ANPAudioTrack*);
+ void (*pause)(ANPAudioTrack*);
+ void (*stop)(ANPAudioTrack*);
+ /** Returns true if the track is not playing (e.g. pause or stop was called,
+ or start was never called.
+ */
+ bool (*isStopped)(ANPAudioTrack*);
+};
+
+struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 {
+ /** Returns the track's latency in milliseconds. */
+ uint32_t (*trackLatency)(ANPAudioTrack*);
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent
+
+enum ANPEventTypes {
+ kNull_ANPEventType = 0,
+ kKey_ANPEventType = 1,
+ /** Mouse events are triggered by either clicking with the navigational pad
+ or by tapping the touchscreen (if the kDown_ANPTouchAction is handled by
+ the plugin then no mouse event is generated). The kKey_ANPEventFlag has
+ to be set to true in order to receive these events.
+ */
+ kMouse_ANPEventType = 2,
+ /** Touch events are generated when the user touches on the screen. The
+ kTouch_ANPEventFlag has to be set to true in order to receive these
+ events.
+ */
+ kTouch_ANPEventType = 3,
+ /** Only triggered by a plugin using the kBitmap_ANPDrawingModel. This event
+ signals that the plugin needs to redraw itself into the provided bitmap.
+ */
+ kDraw_ANPEventType = 4,
+ kLifecycle_ANPEventType = 5,
+
+ /** This event type is completely defined by the plugin.
+ When creating an event, the caller must always set the first
+ two fields, the remaining data is optional.
+ ANPEvent evt;
+ evt.inSize = sizeof(ANPEvent);
+ evt.eventType = kCustom_ANPEventType
+ // other data slots are optional
+ evt.other[] = ...;
+ To post a copy of the event, call
+ eventInterface->postEvent(myNPPInstance, &evt);
+ That call makes a copy of the event struct, and post that on the event
+ queue for the plugin.
+ */
+ kCustom_ANPEventType = 6,
+};
+typedef int32_t ANPEventType;
+
+enum ANPKeyActions {
+ kDown_ANPKeyAction = 0,
+ kUp_ANPKeyAction = 1,
+};
+typedef int32_t ANPKeyAction;
+
+#include "ANPKeyCodes.h"
+typedef int32_t ANPKeyCode;
+
+enum ANPKeyModifiers {
+ kAlt_ANPKeyModifier = 1 << 0,
+ kShift_ANPKeyModifier = 1 << 1,
+};
+// bit-field containing some number of ANPKeyModifier bits
+typedef uint32_t ANPKeyModifier;
+
+enum ANPMouseActions {
+ kDown_ANPMouseAction = 0,
+ kUp_ANPMouseAction = 1,
+};
+typedef int32_t ANPMouseAction;
+
+enum ANPTouchActions {
+ /** This occurs when the user first touches on the screen. As such, this
+ action will always occur prior to any of the other touch actions. If
+ the plugin chooses to not handle this action then no other events
+ related to that particular touch gesture will be generated.
+ */
+ kDown_ANPTouchAction = 0,
+ kUp_ANPTouchAction = 1,
+ kMove_ANPTouchAction = 2,
+ kCancel_ANPTouchAction = 3,
+ // The web view will ignore the return value from the following actions
+ kLongPress_ANPTouchAction = 4,
+ kDoubleTap_ANPTouchAction = 5,
+};
+typedef int32_t ANPTouchAction;
+
+enum ANPLifecycleActions {
+ /** The web view containing this plugin has been paused. See documentation
+ on the android activity lifecycle for more information.
+ */
+ kPause_ANPLifecycleAction = 0,
+ /** The web view containing this plugin has been resumed. See documentation
+ on the android activity lifecycle for more information.
+ */
+ kResume_ANPLifecycleAction = 1,
+ /** The plugin has focus and is now the recipient of input events (e.g. key,
+ touch, etc.)
+ */
+ kGainFocus_ANPLifecycleAction = 2,
+ /** The plugin has lost focus and will not receive any input events until it
+ regains focus. This event is always preceded by a GainFocus action.
+ */
+ kLoseFocus_ANPLifecycleAction = 3,
+ /** The browser is running low on available memory and is requesting that
+ the plugin free any unused/inactive resources to prevent a performance
+ degradation.
+ */
+ kFreeMemory_ANPLifecycleAction = 4,
+ /** The page has finished loading. This happens when the page's top level
+ frame reports that it has completed loading.
+ */
+ kOnLoad_ANPLifecycleAction = 5,
+ /** The browser is honoring the plugin's request to go full screen. Upon
+ returning from this event the browser will resize the plugin's java
+ surface to full-screen coordinates.
+ */
+ kEnterFullScreen_ANPLifecycleAction = 6,
+ /** The browser has exited from full screen mode. Immediately prior to
+ sending this event the browser has resized the plugin's java surface to
+ its original coordinates.
+ */
+ kExitFullScreen_ANPLifecycleAction = 7,
+ /** The plugin is visible to the user on the screen. This event will always
+ occur after a kOffScreen_ANPLifecycleAction event.
+ */
+ kOnScreen_ANPLifecycleAction = 8,
+ /** The plugin is no longer visible to the user on the screen. This event
+ will always occur prior to an kOnScreen_ANPLifecycleAction event.
+ */
+ kOffScreen_ANPLifecycleAction = 9,
+};
+typedef uint32_t ANPLifecycleAction;
+
+/* This is what is passed to NPP_HandleEvent() */
+struct ANPEvent {
+ uint32_t inSize; // size of this struct in bytes
+ ANPEventType eventType;
+ // use based on the value in eventType
+ union {
+ struct {
+ ANPKeyAction action;
+ ANPKeyCode nativeCode;
+ int32_t virtualCode; // windows virtual key code
+ ANPKeyModifier modifiers;
+ int32_t repeatCount; // 0 for initial down (or up)
+ int32_t unichar; // 0 if there is no value
+ } key;
+ struct {
+ ANPMouseAction action;
+ int32_t x; // relative to your "window" (0...width)
+ int32_t y; // relative to your "window" (0...height)
+ } mouse;
+ struct {
+ ANPTouchAction action;
+ ANPKeyModifier modifiers;
+ int32_t x; // relative to your "window" (0...width)
+ int32_t y; // relative to your "window" (0...height)
+ } touch;
+ struct {
+ ANPLifecycleAction action;
+ } lifecycle;
+ struct {
+ ANPDrawingModel model;
+ // relative to (0,0) in top-left of your plugin
+ ANPRectI clip;
+ // use based on the value in model
+ union {
+ ANPBitmap bitmap;
+ struct {
+ int32_t width;
+ int32_t height;
+ } surfaceSize;
+ } data;
+ } draw;
+ } data;
+};
+
+
+struct ANPEventInterfaceV0 : ANPInterface {
+ /** Post a copy of the specified event to the plugin. The event will be
+ delivered to the plugin in its main thread (the thread that receives
+ other ANPEvents). If, after posting before delivery, the NPP instance
+ is torn down, the event will be discarded.
+ */
+ void (*postEvent)(NPP inst, const ANPEvent* event);
+};
+
+struct ANPSurfaceInterfaceV0 : ANPInterface {
+ /** Locks the surface from manipulation by other threads and provides a bitmap
+ to be written to. The dirtyRect param specifies which portion of the
+ bitmap will be written to. If the dirtyRect is NULL then the entire
+ surface will be considered dirty. If the lock was successful the function
+ will return true and the bitmap will be set to point to a valid bitmap.
+ If not the function will return false and the bitmap will be set to NULL.
+ */
+ bool (*lock)(JNIEnv* env, jobject surface, ANPBitmap* bitmap, ANPRectI* dirtyRect);
+ /** Given a locked surface handle (i.e. result of a successful call to lock)
+ the surface is unlocked and the contents of the bitmap, specifically
+ those inside the dirtyRect are written to the screen.
+ */
+ void (*unlock)(JNIEnv* env, jobject surface);
+};
+
+/**
+ * TODO should we not use EGL and GL data types for ABI safety?
+ */
+struct ANPTextureInfo {
+ GLuint textureId;
+ uint32_t width;
+ uint32_t height;
+ GLenum internalFormat;
+};
+
+typedef void* ANPEGLContext;
+
+struct ANPOpenGLInterfaceV0 : ANPInterface {
+ ANPEGLContext (*acquireContext)(NPP instance);
+
+ ANPTextureInfo (*lockTexture)(NPP instance);
+
+ void (*releaseTexture)(NPP instance, const ANPTextureInfo*);
+
+ /**
+ * Invert the contents of the plugin on the y-axis.
+ * default is to not be inverted (i.e. use OpenGL coordinates)
+ */
+ void (*invertPluginContent)(NPP instance, bool isContentInverted);
+};
+
+enum ANPPowerStates {
+ kDefault_ANPPowerState = 0,
+ kScreenOn_ANPPowerState = 1
+};
+typedef int32_t ANPPowerState;
+
+struct ANPSystemInterfaceV1 : ANPInterface {
+ /** Return the path name for the current Application's plugin data directory,
+ or NULL if not supported
+ */
+ const char* (*getApplicationDataDirectory)();
+
+ /** A helper function to load java classes from the plugin's apk. The
+ function looks for a class given the fully qualified and null terminated
+ string representing the className. For example,
+
+ const char* className = "com.android.mypackage.MyClass";
+
+ If the class cannot be found or there is a problem loading the class
+ NULL will be returned.
+ */
+ jclass (*loadJavaClass)(NPP instance, const char* className);
+
+ void (*setPowerState)(NPP instance, ANPPowerState powerState);
+};
+
+struct ANPSystemInterfaceV2 : ANPInterface {
+ /** Return the path name for the current Application's plugin data directory,
+ or NULL if not supported. This directory will change depending on whether
+ or not the plugin is found within an incognito tab.
+ */
+ const char* (*getApplicationDataDirectory)(NPP instance);
+
+ // redeclaration of existing features
+ jclass (*loadJavaClass)(NPP instance, const char* className);
+ void (*setPowerState)(NPP instance, ANPPowerState powerState);
+};
+
+typedef void* ANPNativeWindow;
+
+/** Called to notify the plugin that a video frame has been composited by the
+* browser for display. This will be called in a separate thread and as such
+* you cannot call releaseNativeWindow from the callback.
+*
+* The timestamp is in nanoseconds, and is monotonically increasing.
+*/
+typedef void (*ANPVideoFrameCallbackProc)(ANPNativeWindow* window, int64_t timestamp);
+
+struct ANPVideoInterfaceV1 : ANPInterface {
+
+ /**
+ * Constructs a new native window to be used for rendering video content.
+ *
+ * Subsequent calls will produce new windows, but may also return NULL after
+ * n attempts if the browser has reached it's limit. Further, if the browser
+ * is unable to acquire the window quickly it may also return NULL in order
+ * to not prevent the plugin from executing. A subsequent call will then
+ * return the window if it is avaiable.
+ *
+ * NOTE: The hardware may fail if you try to decode more than the allowable
+ * number of videos supported on that device.
+ */
+ ANPNativeWindow (*acquireNativeWindow)(NPP instance);
+
+ /**
+ * Sets the rectangle that specifies where the video content is to be drawn.
+ * The dimensions are in document space. Further, if the rect is NULL the
+ * browser will not attempt to draw the window, therefore do not set the
+ * dimensions until you queue the first buffer in the window.
+ */
+ void (*setWindowDimensions)(NPP instance, const ANPNativeWindow window, const ANPRectF* dimensions);
+
+ /**
+ */
+ void (*releaseNativeWindow)(NPP instance, ANPNativeWindow window);
+
+ /** Set a callback to be notified when an ANPNativeWindow is composited by
+ * the browser.
+ */
+ void (*setFramerateCallback)(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc);
+};
+
+struct ANPNativeWindowInterfaceV0 : ANPInterface {
+ /**
+ * Constructs a new native window to be used for rendering plugin content.
+ *
+ * Subsequent calls will return the original constructed window. Further, if
+ * the browser is unable to acquire the window quickly it may return NULL in
+ * order to not block the plugin indefinitely. A subsequent call will then
+ * return the window if it is available.
+ */
+ ANPNativeWindow (*acquireNativeWindow)(NPP instance);
+
+ /**
+ * Invert the contents of the plugin on the y-axis.
+ * default is to not be inverted (e.g. use OpenGL coordinates)
+ */
+ void (*invertPluginContent)(NPP instance, bool isContentInverted);
+};
+
+
+#endif
diff --git a/dom/plugins/base/android/moz.build b/dom/plugins/base/android/moz.build
new file mode 100644
index 000000000..d95d20d5b
--- /dev/null
+++ b/dom/plugins/base/android/moz.build
@@ -0,0 +1,35 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS += [
+ 'android_npapi.h',
+ 'ANPKeyCodes.h',
+]
+
+SOURCES += [
+ 'ANPAudio.cpp',
+ 'ANPEvent.cpp',
+ 'ANPLog.cpp',
+ 'ANPNativeWindow.cpp',
+ 'ANPSurface.cpp',
+ 'ANPSystem.cpp',
+ 'ANPVideo.cpp',
+ 'ANPWindow.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+ '/dom/plugins/base',
+ '/gfx/gl',
+ '/widget',
+ '/widget/android',
+]
+
+DEFINES['MOZ_APP_NAME'] = '"%s"' % CONFIG['MOZ_APP_NAME']
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
diff --git a/dom/plugins/base/moz.build b/dom/plugins/base/moz.build
new file mode 100644
index 000000000..f43f75f79
--- /dev/null
+++ b/dom/plugins/base/moz.build
@@ -0,0 +1,117 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ DIRS += ['android']
+
+XPIDL_SOURCES += [
+ 'nsIHTTPHeaderListener.idl',
+ 'nsIPluginDocument.idl',
+ 'nsIPluginHost.idl',
+ 'nsIPluginInputStream.idl',
+ 'nsIPluginInstanceOwner.idl',
+ 'nsIPluginTag.idl',
+ 'nspluginroot.idl',
+]
+
+XPIDL_MODULE = 'plugin'
+
+EXPORTS += [
+ 'npapi.h',
+ 'npfunctions.h',
+ 'npruntime.h',
+ 'nptypes.h',
+ 'nsJSNPRuntime.h',
+ 'nsNPAPIPluginInstance.h',
+ 'nsPluginDirServiceProvider.h',
+ 'nsPluginHost.h',
+ 'nsPluginInstanceOwner.h',
+ 'nsPluginLogging.h',
+ 'nsPluginNativeWindow.h',
+ 'nsPluginNativeWindowGtk.h',
+ 'nsPluginsCID.h',
+ 'nsPluginsDir.h',
+ 'nsPluginTags.h',
+]
+
+EXPORTS.mozilla += [
+ 'PluginPRLibrary.h',
+]
+
+UNIFIED_SOURCES += [
+ 'nsJSNPRuntime.cpp',
+ 'nsNPAPIPluginInstance.cpp',
+ 'nsNPAPIPluginStreamListener.cpp',
+ 'nsPluginInstanceOwner.cpp',
+ 'nsPluginModule.cpp',
+ 'nsPluginStreamListenerPeer.cpp',
+ 'nsPluginTags.cpp',
+ 'PluginPRLibrary.cpp',
+]
+
+SOURCES += [
+ 'nsNPAPIPlugin.cpp', # Conflict with X11 headers
+ 'nsPluginHost.cpp', # Conflict with NS_NPAPIPLUGIN_CALLBACK
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ UNIFIED_SOURCES += [
+ 'nsPluginDirServiceProvider.cpp',
+ 'nsPluginNativeWindowWin.cpp',
+ 'nsPluginsDirWin.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'nsPluginNativeWindow.cpp',
+ 'nsPluginsDirDarwin.cpp',
+ ]
+else:
+ UNIFIED_SOURCES += [
+ 'nsPluginsDirUnix.cpp',
+ ]
+ if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ # This file cannot be built in unified mode because of name clashes in X11 headers.
+ SOURCES += [
+ 'nsPluginNativeWindowGtk.cpp',
+ ]
+ else:
+ UNIFIED_SOURCES += [
+ 'nsPluginNativeWindow.cpp',
+ ]
+
+LOCAL_INCLUDES += [
+ '/dom/base',
+ '/dom/plugins/ipc',
+ '/layout/generic',
+ '/layout/xul',
+ '/netwerk/base',
+ '/widget',
+ '/widget/android',
+ '/widget/cocoa',
+ '/xpcom/base',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ LOCAL_INCLUDES += [
+ '/dom/plugins/base/android',
+ ]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ LOCAL_INCLUDES += [
+ '/xpcom/base',
+ ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+DEFINES['SK_BUILD_FOR_ANDROID_NDK'] = True
+
+FINAL_LIBRARY = 'xul'
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+CXXFLAGS += CONFIG['TK_CFLAGS']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/dom/plugins/base/npapi.h b/dom/plugins/base/npapi.h
new file mode 100644
index 000000000..12ac635c7
--- /dev/null
+++ b/dom/plugins/base/npapi.h
@@ -0,0 +1,921 @@
+/* -*- Mode: C; tab-width: 4; 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/. */
+
+#ifndef npapi_h_
+#define npapi_h_
+
+#include "nptypes.h"
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+#include <windef.h>
+#ifndef XP_WIN
+#define XP_WIN 1
+#endif
+#endif
+
+#if defined(__SYMBIAN32__)
+#ifndef XP_SYMBIAN
+#define XP_SYMBIAN 1
+#undef XP_WIN
+#endif
+#endif
+
+#if defined(__APPLE_CC__) && !defined(XP_UNIX)
+#ifndef XP_MACOSX
+#define XP_MACOSX 1
+#endif
+#endif
+
+#if defined(XP_MACOSX) && defined(__LP64__)
+#define NP_NO_QUICKDRAW
+#define NP_NO_CARBON
+#endif
+
+#if defined(XP_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#include <OpenGL/OpenGL.h>
+#ifndef NP_NO_CARBON
+#include <Carbon/Carbon.h>
+#endif
+#endif
+
+#if defined(XP_UNIX)
+#include <stdio.h>
+#if defined(MOZ_X11)
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "X11UndefineNone.h"
+#endif
+#endif
+
+#if defined(XP_SYMBIAN)
+#include <QEvent>
+#include <QRegion>
+#endif
+
+/*----------------------------------------------------------------------*/
+/* Plugin Version Constants */
+/*----------------------------------------------------------------------*/
+
+#define NP_VERSION_MAJOR 0
+#define NP_VERSION_MINOR 29
+
+
+/* The OS/2 version of Netscape uses RC_DATA to define the
+ mime types, file extensions, etc that are required.
+ Use a vertical bar to separate types, end types with \0.
+ FileVersion and ProductVersion are 32bit ints, all other
+ entries are strings that MUST be terminated with a \0.
+
+AN EXAMPLE:
+
+RCDATA NP_INFO_ProductVersion { 1,0,0,1,}
+
+RCDATA NP_INFO_MIMEType { "video/x-video|",
+ "video/x-flick\0" }
+RCDATA NP_INFO_FileExtents { "avi|",
+ "flc\0" }
+RCDATA NP_INFO_FileOpenName{ "MMOS2 video player(*.avi)|",
+ "MMOS2 Flc/Fli player(*.flc)\0" }
+
+RCDATA NP_INFO_FileVersion { 1,0,0,1 }
+RCDATA NP_INFO_CompanyName { "Netscape Communications\0" }
+RCDATA NP_INFO_FileDescription { "NPAVI32 Extension DLL\0"
+RCDATA NP_INFO_InternalName { "NPAVI32\0" )
+RCDATA NP_INFO_LegalCopyright { "Copyright Netscape Communications \251 1996\0"
+RCDATA NP_INFO_OriginalFilename { "NVAPI32.DLL" }
+RCDATA NP_INFO_ProductName { "NPAVI32 Dynamic Link Library\0" }
+*/
+/* RC_DATA types for version info - required */
+#define NP_INFO_ProductVersion 1
+#define NP_INFO_MIMEType 2
+#define NP_INFO_FileOpenName 3
+#define NP_INFO_FileExtents 4
+/* RC_DATA types for version info - used if found */
+#define NP_INFO_FileDescription 5
+#define NP_INFO_ProductName 6
+/* RC_DATA types for version info - optional */
+#define NP_INFO_CompanyName 7
+#define NP_INFO_FileVersion 8
+#define NP_INFO_InternalName 9
+#define NP_INFO_LegalCopyright 10
+#define NP_INFO_OriginalFilename 11
+
+#ifndef RC_INVOKED
+
+/*----------------------------------------------------------------------*/
+/* Definition of Basic Types */
+/*----------------------------------------------------------------------*/
+
+typedef unsigned char NPBool;
+typedef int16_t NPError;
+typedef int16_t NPReason;
+typedef char* NPMIMEType;
+
+/*----------------------------------------------------------------------*/
+/* Structures and definitions */
+/*----------------------------------------------------------------------*/
+
+#if !defined(__LP64__)
+#if defined(XP_MACOSX)
+#pragma options align=mac68k
+#endif
+#endif /* __LP64__ */
+
+/*
+ * NPP is a plug-in's opaque instance handle
+ */
+typedef struct _NPP
+{
+ void* pdata; /* plug-in private data */
+ void* ndata; /* netscape private data */
+} NPP_t;
+
+typedef NPP_t* NPP;
+
+typedef struct _NPStream
+{
+ void* pdata; /* plug-in private data */
+ void* ndata; /* netscape private data */
+ const char* url;
+ uint32_t end;
+ uint32_t lastmodified;
+ void* notifyData;
+ const char* headers; /* Response headers from host.
+ * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS.
+ * Used for HTTP only; nullptr for non-HTTP.
+ * Available from NPP_NewStream onwards.
+ * Plugin should copy this data before storing it.
+ * Includes HTTP status line and all headers,
+ * preferably verbatim as received from server,
+ * headers formatted as in HTTP ("Header: Value"),
+ * and newlines (\n, NOT \r\n) separating lines.
+ * Terminated by \n\0 (NOT \n\n\0). */
+} NPStream;
+
+typedef struct _NPByteRange
+{
+ int32_t offset; /* negative offset means from the end */
+ uint32_t length;
+ struct _NPByteRange* next;
+} NPByteRange;
+
+typedef struct _NPSavedData
+{
+ int32_t len;
+ void* buf;
+} NPSavedData;
+
+typedef struct _NPRect
+{
+ uint16_t top;
+ uint16_t left;
+ uint16_t bottom;
+ uint16_t right;
+} NPRect;
+
+typedef struct _NPSize
+{
+ int32_t width;
+ int32_t height;
+} NPSize;
+
+typedef enum {
+ NPFocusNext = 0,
+ NPFocusPrevious = 1
+} NPFocusDirection;
+
+/* These formats describe the format in the memory byte-order. This means if
+ * a 32-bit value of a pixel is viewed on a little-endian system the layout will
+ * be 0xAARRGGBB. The Alpha channel will be stored in the most significant
+ * bits. */
+typedef enum {
+ /* 32-bit per pixel 8-bit per channel - premultiplied alpha */
+ NPImageFormatBGRA32 = 0x1,
+ /* 32-bit per pixel 8-bit per channel - 1 unused channel */
+ NPImageFormatBGRX32 = 0x2
+} NPImageFormat;
+
+typedef struct _NPAsyncSurface
+{
+ uint32_t version;
+ NPSize size;
+ NPImageFormat format;
+ union {
+ struct {
+ uint32_t stride;
+ void *data;
+ } bitmap;
+#if defined(XP_WIN)
+ HANDLE sharedHandle;
+#endif
+ };
+} NPAsyncSurface;
+
+/* Return values for NPP_HandleEvent */
+#define kNPEventNotHandled 0
+#define kNPEventHandled 1
+/* Exact meaning must be spec'd in event model. */
+#define kNPEventStartIME 2
+
+#if defined(XP_UNIX)
+/*
+ * Unix specific structures and definitions
+ */
+
+/*
+ * Callback Structures.
+ *
+ * These are used to pass additional platform specific information.
+ */
+enum {
+ NP_SETWINDOW = 1,
+ NP_PRINT
+};
+
+typedef struct
+{
+ int32_t type;
+} NPAnyCallbackStruct;
+
+typedef struct
+{
+ int32_t type;
+#if defined(MOZ_X11)
+ Display* display;
+ Visual* visual;
+ Colormap colormap;
+ unsigned int depth;
+#endif
+} NPSetWindowCallbackStruct;
+
+typedef struct
+{
+ int32_t type;
+ FILE* fp;
+} NPPrintCallbackStruct;
+
+#endif /* XP_UNIX */
+
+#if defined(XP_WIN)
+/*
+ * Windows specific structures and definitions
+ */
+
+/*
+ * Information about the default audio device. These values share meaning with
+ * the parameters to the Windows API IMMNotificationClient object.
+ * This is the value of the NPNVaudioDeviceChangeDetails variable.
+ */
+typedef struct _NPAudioDeviceChangeDetails
+{
+ int32_t flow;
+ int32_t role;
+ const wchar_t* defaultDevice; // this pointer is only valid during the call
+ // to NPPSetValue.
+} NPAudioDeviceChangeDetails;
+
+#endif /* XP_WIN */
+
+typedef enum {
+ NPDrawingModelDUMMY
+#if defined(XP_MACOSX)
+#ifndef NP_NO_QUICKDRAW
+ , NPDrawingModelQuickDraw = 0
+#endif
+ , NPDrawingModelCoreGraphics = 1
+ , NPDrawingModelOpenGL = 2
+ , NPDrawingModelCoreAnimation = 3
+ , NPDrawingModelInvalidatingCoreAnimation = 4
+#endif
+#if defined(XP_WIN)
+ , NPDrawingModelSyncWin = 5
+#endif
+#if defined(MOZ_X11)
+ , NPDrawingModelSyncX = 6
+#endif
+ , NPDrawingModelAsyncBitmapSurface = 7
+#if defined(XP_WIN)
+ , NPDrawingModelAsyncWindowsDXGISurface = 8
+#endif
+} NPDrawingModel;
+
+#ifdef XP_MACOSX
+typedef enum {
+#ifndef NP_NO_CARBON
+ NPEventModelCarbon = 0,
+#endif
+ NPEventModelCocoa = 1
+} NPEventModel;
+#endif
+
+/*
+ * The following masks are applied on certain platforms to NPNV and
+ * NPPV selectors that pass around pointers to COM interfaces. Newer
+ * compilers on some platforms may generate vtables that are not
+ * compatible with older compilers. To prevent older plugins from
+ * not understanding a new browser's ABI, these masks change the
+ * values of those selectors on those platforms. To remain backwards
+ * compatible with different versions of the browser, plugins can
+ * use these masks to dynamically determine and use the correct C++
+ * ABI that the browser is expecting. This does not apply to Windows
+ * as Microsoft's COM ABI will likely not change.
+ */
+
+#define NP_ABI_GCC3_MASK 0x10000000
+/*
+ * gcc 3.x generated vtables on UNIX and OSX are incompatible with
+ * previous compilers.
+ */
+#if (defined(XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3))
+#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK
+#else
+#define _NP_ABI_MIXIN_FOR_GCC3 0
+#endif
+
+#if defined(XP_MACOSX)
+#define NP_ABI_MACHO_MASK 0x01000000
+#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK
+#else
+#define _NP_ABI_MIXIN_FOR_MACHO 0
+#endif
+
+#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO)
+
+/*
+ * List of variable names for which NPP_GetValue shall be implemented
+ */
+typedef enum {
+ NPPVpluginNameString = 1,
+ NPPVpluginDescriptionString,
+ NPPVpluginWindowBool,
+ NPPVpluginTransparentBool,
+ NPPVjavaClass,
+ NPPVpluginWindowSize,
+ NPPVpluginTimerInterval,
+ NPPVpluginScriptableInstance = (10 | NP_ABI_MASK),
+ NPPVpluginScriptableIID = 11,
+ NPPVjavascriptPushCallerBool = 12,
+ NPPVpluginKeepLibraryInMemory = 13,
+ NPPVpluginNeedsXEmbed = 14,
+
+ /* Get the NPObject for scripting the plugin. Introduced in NPAPI minor version 14.
+ */
+ NPPVpluginScriptableNPObject = 15,
+
+ /* Get the plugin value (as \0-terminated UTF-8 string data) for
+ * form submission if the plugin is part of a form. Use
+ * NPN_MemAlloc() to allocate memory for the string data. Introduced
+ * in NPAPI minor version 15.
+ */
+ NPPVformValue = 16,
+
+ NPPVpluginUrlRequestsDisplayedBool = 17,
+
+ /* Checks if the plugin is interested in receiving the http body of
+ * all http requests (including failed ones, http status != 200).
+ */
+ NPPVpluginWantsAllNetworkStreams = 18,
+
+ /* Browsers can retrieve a native ATK accessibility plug ID via this variable. */
+ NPPVpluginNativeAccessibleAtkPlugId = 19,
+
+ /* Checks to see if the plug-in would like the browser to load the "src" attribute. */
+ NPPVpluginCancelSrcStream = 20,
+
+ NPPVsupportsAdvancedKeyHandling = 21,
+
+ NPPVpluginUsesDOMForCursorBool = 22,
+
+ /* Used for negotiating drawing models */
+ NPPVpluginDrawingModel = 1000
+#if defined(XP_MACOSX)
+ /* Used for negotiating event models */
+ , NPPVpluginEventModel = 1001
+ /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */
+ , NPPVpluginCoreAnimationLayer = 1003
+#endif
+ /* Notification that the plugin just started or stopped playing audio */
+ , NPPVpluginIsPlayingAudio = 4000
+#if defined(XP_WIN)
+ /* Notification that the plugin requests notification when the default audio device has changed */
+ , NPPVpluginRequiresAudioDeviceChanges = 4001
+#endif
+
+} NPPVariable;
+
+/*
+ * List of variable names for which NPN_GetValue should be implemented.
+ */
+typedef enum {
+ NPNVxDisplay = 1,
+ NPNVxtAppContext,
+ NPNVnetscapeWindow,
+ NPNVjavascriptEnabledBool,
+ NPNVasdEnabledBool,
+ NPNVisOfflineBool,
+
+ NPNVserviceManager = (10 | NP_ABI_MASK),
+ NPNVDOMElement = (11 | NP_ABI_MASK),
+ NPNVDOMWindow = (12 | NP_ABI_MASK),
+ NPNVToolkit = (13 | NP_ABI_MASK),
+ NPNVSupportsXEmbedBool = 14,
+
+ /* Get the NPObject wrapper for the browser window. */
+ NPNVWindowNPObject = 15,
+
+ /* Get the NPObject wrapper for the plugins DOM element. */
+ NPNVPluginElementNPObject = 16,
+
+ NPNVSupportsWindowless = 17,
+
+ NPNVprivateModeBool = 18,
+
+ NPNVsupportsAdvancedKeyHandling = 21,
+
+ NPNVdocumentOrigin = 22,
+
+ NPNVCSSZoomFactor = 23,
+
+ NPNVpluginDrawingModel = 1000 /* Get the current drawing model (NPDrawingModel) */
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ , NPNVcontentsScaleFactor = 1001
+#endif
+#if defined(XP_MACOSX)
+#ifndef NP_NO_QUICKDRAW
+ , NPNVsupportsQuickDrawBool = 2000
+#endif
+ , NPNVsupportsCoreGraphicsBool = 2001
+ , NPNVsupportsOpenGLBool = 2002
+ , NPNVsupportsCoreAnimationBool = 2003
+ , NPNVsupportsInvalidatingCoreAnimationBool = 2004
+#endif
+ , NPNVsupportsAsyncBitmapSurfaceBool = 2007
+#if defined(XP_WIN)
+ , NPNVsupportsAsyncWindowsDXGISurfaceBool = 2008
+ , NPNVpreferredDXGIAdapter = 2009
+#endif
+#if defined(XP_MACOSX)
+#ifndef NP_NO_CARBON
+ , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */
+#endif
+ , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */
+ , NPNVsupportsUpdatedCocoaTextInputBool = 3002 /* TRUE if the browser supports the updated
+ Cocoa text input specification. */
+#endif
+ , NPNVmuteAudioBool = 4000 /* Request that the browser wants to mute or unmute the plugin */
+#if defined(XP_WIN)
+ , NPNVaudioDeviceChangeDetails = 4001 /* Provides information about the new default audio device */
+#endif
+#if defined(XP_MACOSX)
+ , NPNVsupportsCompositingCoreAnimationPluginsBool = 74656 /* TRUE if the browser supports
+ CA model compositing */
+#endif
+} NPNVariable;
+
+typedef enum {
+ NPNURLVCookie = 501,
+ NPNURLVProxy
+} NPNURLVariable;
+
+/*
+ * The type of Toolkit the widgets use
+ */
+typedef enum {
+ NPNVGtk12 = 1,
+ NPNVGtk2
+} NPNToolkitType;
+
+/*
+ * The type of a NPWindow - it specifies the type of the data structure
+ * returned in the window field.
+ */
+typedef enum {
+ NPWindowTypeWindow = 1,
+ NPWindowTypeDrawable
+} NPWindowType;
+
+typedef struct _NPWindow
+{
+ void* window; /* Platform specific window handle */
+ /* OS/2: x - Position of bottom left corner */
+ /* OS/2: y - relative to visible netscape window */
+ int32_t x; /* Position of top left corner relative */
+ int32_t y; /* to a netscape page. */
+ uint32_t width; /* Maximum window size */
+ uint32_t height;
+ NPRect clipRect; /* Clipping rectangle in port coordinates */
+#if (defined(XP_UNIX) || defined(XP_SYMBIAN)) && !defined(XP_MACOSX)
+ void * ws_info; /* Platform-dependent additional data */
+#endif /* XP_UNIX */
+ NPWindowType type; /* Is this a window or a drawable? */
+} NPWindow;
+
+typedef struct _NPImageExpose
+{
+ char* data; /* image pointer */
+ int32_t stride; /* Stride of data image pointer */
+ int32_t depth; /* Depth of image pointer */
+ int32_t x; /* Expose x */
+ int32_t y; /* Expose y */
+ uint32_t width; /* Expose width */
+ uint32_t height; /* Expose height */
+ NPSize dataSize; /* Data buffer size */
+ float translateX; /* translate X matrix value */
+ float translateY; /* translate Y matrix value */
+ float scaleX; /* scale X matrix value */
+ float scaleY; /* scale Y matrix value */
+} NPImageExpose;
+
+typedef struct _NPFullPrint
+{
+ NPBool pluginPrinted;/* Set TRUE if plugin handled fullscreen printing */
+ NPBool printOne; /* TRUE if plugin should print one copy to default
+ printer */
+ void* platformPrint; /* Platform-specific printing info */
+} NPFullPrint;
+
+typedef struct _NPEmbedPrint
+{
+ NPWindow window;
+ void* platformPrint; /* Platform-specific printing info */
+} NPEmbedPrint;
+
+typedef struct _NPPrint
+{
+ uint16_t mode; /* NP_FULL or NP_EMBED */
+ union
+ {
+ NPFullPrint fullPrint; /* if mode is NP_FULL */
+ NPEmbedPrint embedPrint; /* if mode is NP_EMBED */
+ } print;
+} NPPrint;
+
+#if defined(XP_MACOSX)
+#ifndef NP_NO_CARBON
+typedef EventRecord NPEvent;
+#endif
+#elif defined(XP_SYMBIAN)
+typedef QEvent NPEvent;
+#elif defined(XP_WIN)
+typedef struct _NPEvent
+{
+ uint16_t event;
+ uintptr_t wParam;
+ uintptr_t lParam;
+} NPEvent;
+#elif defined(XP_UNIX) && defined(MOZ_X11)
+typedef XEvent NPEvent;
+#else
+typedef void* NPEvent;
+#endif
+
+#if defined(XP_MACOSX)
+typedef void* NPRegion;
+#ifndef NP_NO_QUICKDRAW
+typedef RgnHandle NPQDRegion;
+#endif
+typedef CGPathRef NPCGRegion;
+#elif defined(XP_WIN)
+typedef HRGN NPRegion;
+#elif defined(XP_UNIX) && defined(MOZ_X11)
+typedef Region NPRegion;
+#elif defined(XP_SYMBIAN)
+typedef QRegion* NPRegion;
+#else
+typedef void *NPRegion;
+#endif
+
+typedef struct _NPNSString NPNSString;
+typedef struct _NPNSWindow NPNSWindow;
+typedef struct _NPNSMenu NPNSMenu;
+
+#if defined(XP_MACOSX)
+typedef NPNSMenu NPMenu;
+#else
+typedef void *NPMenu;
+#endif
+
+typedef enum {
+ NPCoordinateSpacePlugin = 1,
+ NPCoordinateSpaceWindow,
+ NPCoordinateSpaceFlippedWindow,
+ NPCoordinateSpaceScreen,
+ NPCoordinateSpaceFlippedScreen
+} NPCoordinateSpace;
+
+#if defined(XP_MACOSX)
+
+#ifndef NP_NO_QUICKDRAW
+typedef struct NP_Port
+{
+ CGrafPtr port;
+ int32_t portx; /* position inside the topmost window */
+ int32_t porty;
+} NP_Port;
+#endif /* NP_NO_QUICKDRAW */
+
+/*
+ * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics
+ * as its drawing model.
+ */
+
+typedef struct NP_CGContext
+{
+ CGContextRef context;
+ void *window; /* A WindowRef under the Carbon event model. */
+} NP_CGContext;
+
+/*
+ * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its
+ * drawing model.
+ */
+
+typedef struct NP_GLContext
+{
+ CGLContextObj context;
+#ifdef NP_NO_CARBON
+ NPNSWindow *window;
+#else
+ void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */
+#endif
+} NP_GLContext;
+
+typedef enum {
+ NPCocoaEventDrawRect = 1,
+ NPCocoaEventMouseDown,
+ NPCocoaEventMouseUp,
+ NPCocoaEventMouseMoved,
+ NPCocoaEventMouseEntered,
+ NPCocoaEventMouseExited,
+ NPCocoaEventMouseDragged,
+ NPCocoaEventKeyDown,
+ NPCocoaEventKeyUp,
+ NPCocoaEventFlagsChanged,
+ NPCocoaEventFocusChanged,
+ NPCocoaEventWindowFocusChanged,
+ NPCocoaEventScrollWheel,
+ NPCocoaEventTextInput
+} NPCocoaEventType;
+
+typedef struct _NPCocoaEvent {
+ NPCocoaEventType type;
+ uint32_t version;
+ union {
+ struct {
+ uint32_t modifierFlags;
+ double pluginX;
+ double pluginY;
+ int32_t buttonNumber;
+ int32_t clickCount;
+ double deltaX;
+ double deltaY;
+ double deltaZ;
+ } mouse;
+ struct {
+ uint32_t modifierFlags;
+ NPNSString *characters;
+ NPNSString *charactersIgnoringModifiers;
+ NPBool isARepeat;
+ uint16_t keyCode;
+ } key;
+ struct {
+ CGContextRef context;
+ double x;
+ double y;
+ double width;
+ double height;
+ } draw;
+ struct {
+ NPBool hasFocus;
+ } focus;
+ struct {
+ NPNSString *text;
+ } text;
+ } data;
+} NPCocoaEvent;
+
+#ifndef NP_NO_CARBON
+/* Non-standard event types that can be passed to HandleEvent */
+enum NPEventType {
+ NPEventType_GetFocusEvent = (osEvt + 16),
+ NPEventType_LoseFocusEvent,
+ NPEventType_AdjustCursorEvent,
+ NPEventType_MenuCommandEvent,
+ NPEventType_ClippingChangedEvent,
+ NPEventType_ScrollingBeginsEvent = 1000,
+ NPEventType_ScrollingEndsEvent
+};
+#endif /* NP_NO_CARBON */
+
+#endif /* XP_MACOSX */
+
+/*
+ * Values for mode passed to NPP_New:
+ */
+#define NP_EMBED 1
+#define NP_FULL 2
+
+/*
+ * Values for stream type passed to NPP_NewStream:
+ */
+#define NP_NORMAL 1
+#define NP_SEEK 2
+#define NP_ASFILE 3
+#define NP_ASFILEONLY 4
+
+#define NP_MAXREADY (((unsigned)(~0)<<1)>>1)
+
+/*
+ * Flags for NPP_ClearSiteData.
+ */
+#define NP_CLEAR_ALL 0
+#define NP_CLEAR_CACHE (1 << 0)
+
+#if !defined(__LP64__)
+#if defined(XP_MACOSX)
+#pragma options align=reset
+#endif
+#endif /* __LP64__ */
+
+/*----------------------------------------------------------------------*/
+/* Error and Reason Code definitions */
+/*----------------------------------------------------------------------*/
+
+/*
+ * Values of type NPError:
+ */
+#define NPERR_BASE 0
+#define NPERR_NO_ERROR (NPERR_BASE + 0)
+#define NPERR_GENERIC_ERROR (NPERR_BASE + 1)
+#define NPERR_INVALID_INSTANCE_ERROR (NPERR_BASE + 2)
+#define NPERR_INVALID_FUNCTABLE_ERROR (NPERR_BASE + 3)
+#define NPERR_MODULE_LOAD_FAILED_ERROR (NPERR_BASE + 4)
+#define NPERR_OUT_OF_MEMORY_ERROR (NPERR_BASE + 5)
+#define NPERR_INVALID_PLUGIN_ERROR (NPERR_BASE + 6)
+#define NPERR_INVALID_PLUGIN_DIR_ERROR (NPERR_BASE + 7)
+#define NPERR_INCOMPATIBLE_VERSION_ERROR (NPERR_BASE + 8)
+#define NPERR_INVALID_PARAM (NPERR_BASE + 9)
+#define NPERR_INVALID_URL (NPERR_BASE + 10)
+#define NPERR_FILE_NOT_FOUND (NPERR_BASE + 11)
+#define NPERR_NO_DATA (NPERR_BASE + 12)
+#define NPERR_STREAM_NOT_SEEKABLE (NPERR_BASE + 13)
+#define NPERR_TIME_RANGE_NOT_SUPPORTED (NPERR_BASE + 14)
+#define NPERR_MALFORMED_SITE (NPERR_BASE + 15)
+
+/*
+ * Values of type NPReason:
+ */
+#define NPRES_BASE 0
+#define NPRES_DONE (NPRES_BASE + 0)
+#define NPRES_NETWORK_ERR (NPRES_BASE + 1)
+#define NPRES_USER_BREAK (NPRES_BASE + 2)
+
+/*
+ * Don't use these obsolete error codes any more.
+ */
+#define NP_NOERR NP_NOERR_is_obsolete_use_NPERR_NO_ERROR
+#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR
+#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK
+
+/*
+ * Version feature information
+ */
+#define NPVERS_HAS_STREAMOUTPUT 8
+#define NPVERS_HAS_NOTIFICATION 9
+#define NPVERS_HAS_LIVECONNECT 9
+#define NPVERS_68K_HAS_LIVECONNECT 11
+#define NPVERS_HAS_WINDOWLESS 11
+#define NPVERS_HAS_XPCONNECT_SCRIPTING 13
+#define NPVERS_HAS_NPRUNTIME_SCRIPTING 14
+#define NPVERS_HAS_FORM_VALUES 15
+#define NPVERS_HAS_POPUPS_ENABLED_STATE 16
+#define NPVERS_HAS_RESPONSE_HEADERS 17
+#define NPVERS_HAS_NPOBJECT_ENUM 18
+#define NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL 19
+#define NPVERS_HAS_ALL_NETWORK_STREAMS 20
+#define NPVERS_HAS_URL_AND_AUTH_INFO 21
+#define NPVERS_HAS_PRIVATE_MODE 22
+#define NPVERS_MACOSX_HAS_COCOA_EVENTS 23
+#define NPVERS_HAS_ADVANCED_KEY_HANDLING 25
+#define NPVERS_HAS_URL_REDIRECT_HANDLING 26
+#define NPVERS_HAS_CLEAR_SITE_DATA 27
+
+/*----------------------------------------------------------------------*/
+/* Function Prototypes */
+/*----------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* NPP_* functions are provided by the plugin and called by the navigator. */
+
+#if defined(XP_UNIX)
+const char* NPP_GetMIMEDescription(void);
+#endif
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance,
+ uint16_t mode, int16_t argc, char* argn[],
+ char* argv[], NPSavedData* saved);
+NPError NPP_Destroy(NPP instance, NPSavedData** save);
+NPError NPP_SetWindow(NPP instance, NPWindow* window);
+NPError NPP_NewStream(NPP instance, NPMIMEType type,
+ NPStream* stream, NPBool seekable,
+ uint16_t* stype);
+NPError NPP_DestroyStream(NPP instance, NPStream* stream,
+ NPReason reason);
+int32_t NPP_WriteReady(NPP instance, NPStream* stream);
+int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset,
+ int32_t len, void* buffer);
+void NPP_StreamAsFile(NPP instance, NPStream* stream,
+ const char* fname);
+void NPP_Print(NPP instance, NPPrint* platformPrint);
+int16_t NPP_HandleEvent(NPP instance, void* event);
+void NPP_URLNotify(NPP instance, const char* url,
+ NPReason reason, void* notifyData);
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value);
+NPBool NPP_GotFocus(NPP instance, NPFocusDirection direction);
+void NPP_LostFocus(NPP instance);
+void NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status, void* notifyData);
+NPError NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge);
+char** NPP_GetSitesWithData(void);
+void NPP_DidComposite(NPP instance);
+
+/* NPN_* functions are provided by the navigator and called by the plugin. */
+void NPN_Version(int* plugin_major, int* plugin_minor,
+ int* netscape_major, int* netscape_minor);
+NPError NPN_GetURLNotify(NPP instance, const char* url,
+ const char* target, void* notifyData);
+NPError NPN_GetURL(NPP instance, const char* url,
+ const char* target);
+NPError NPN_PostURLNotify(NPP instance, const char* url,
+ const char* target, uint32_t len,
+ const char* buf, NPBool file,
+ void* notifyData);
+NPError NPN_PostURL(NPP instance, const char* url,
+ const char* target, uint32_t len,
+ const char* buf, NPBool file);
+NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList);
+NPError NPN_NewStream(NPP instance, NPMIMEType type,
+ const char* target, NPStream** stream);
+int32_t NPN_Write(NPP instance, NPStream* stream, int32_t len,
+ void* buffer);
+NPError NPN_DestroyStream(NPP instance, NPStream* stream,
+ NPReason reason);
+void NPN_Status(NPP instance, const char* message);
+const char* NPN_UserAgent(NPP instance);
+void* NPN_MemAlloc(uint32_t size);
+void NPN_MemFree(void* ptr);
+uint32_t NPN_MemFlush(uint32_t size);
+void NPN_ReloadPlugins(NPBool reloadPages);
+NPError NPN_GetValue(NPP instance, NPNVariable variable,
+ void *value);
+NPError NPN_SetValue(NPP instance, NPPVariable variable,
+ void *value);
+void NPN_InvalidateRect(NPP instance, NPRect *invalidRect);
+void NPN_InvalidateRegion(NPP instance,
+ NPRegion invalidRegion);
+void NPN_ForceRedraw(NPP instance);
+void NPN_PushPopupsEnabledState(NPP instance, NPBool enabled);
+void NPN_PopPopupsEnabledState(NPP instance);
+void NPN_PluginThreadAsyncCall(NPP instance,
+ void (*func) (void *),
+ void *userData);
+NPError NPN_GetValueForURL(NPP instance, NPNURLVariable variable,
+ const char *url, char **value,
+ uint32_t *len);
+NPError NPN_SetValueForURL(NPP instance, NPNURLVariable variable,
+ const char *url, const char *value,
+ uint32_t len);
+NPError NPN_GetAuthenticationInfo(NPP instance,
+ const char *protocol,
+ const char *host, int32_t port,
+ const char *scheme,
+ const char *realm,
+ char **username, uint32_t *ulen,
+ char **password,
+ uint32_t *plen);
+uint32_t NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID));
+void NPN_UnscheduleTimer(NPP instance, uint32_t timerID);
+NPError NPN_PopUpContextMenu(NPP instance, NPMenu* menu);
+NPBool NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
+NPBool NPN_HandleEvent(NPP instance, void *event, NPBool handled);
+NPBool NPN_UnfocusInstance(NPP instance, NPFocusDirection direction);
+void NPN_URLRedirectResponse(NPP instance, void* notifyData, NPBool allow);
+NPError NPN_InitAsyncSurface(NPP instance, NPSize *size,
+ NPImageFormat format, void *initData,
+ NPAsyncSurface *surface);
+NPError NPN_FinalizeAsyncSurface(NPP instance, NPAsyncSurface *surface);
+void NPN_SetCurrentAsyncSurface(NPP instance, NPAsyncSurface *surface, NPRect *changed);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* RC_INVOKED */
+
+#endif /* npapi_h_ */
diff --git a/dom/plugins/base/npfunctions.h b/dom/plugins/base/npfunctions.h
new file mode 100644
index 000000000..4c9d66ea0
--- /dev/null
+++ b/dom/plugins/base/npfunctions.h
@@ -0,0 +1,281 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef npfunctions_h_
+#define npfunctions_h_
+
+#include "npapi.h"
+#include "npruntime.h"
+
+#ifdef MOZ_WIDGET_ANDROID
+#include <jni.h>
+#endif
+
+typedef NPError (* NPP_NewProcPtr)(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved);
+typedef NPError (* NPP_DestroyProcPtr)(NPP instance, NPSavedData** save);
+typedef NPError (* NPP_SetWindowProcPtr)(NPP instance, NPWindow* window);
+typedef NPError (* NPP_NewStreamProcPtr)(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype);
+typedef NPError (* NPP_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason);
+typedef int32_t (* NPP_WriteReadyProcPtr)(NPP instance, NPStream* stream);
+typedef int32_t (* NPP_WriteProcPtr)(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer);
+typedef void (* NPP_StreamAsFileProcPtr)(NPP instance, NPStream* stream, const char* fname);
+typedef void (* NPP_PrintProcPtr)(NPP instance, NPPrint* platformPrint);
+typedef int16_t (* NPP_HandleEventProcPtr)(NPP instance, void* event);
+typedef void (* NPP_URLNotifyProcPtr)(NPP instance, const char* url, NPReason reason, void* notifyData);
+/* Any NPObjects returned to the browser via NPP_GetValue should be retained
+ by the plugin on the way out. The browser is responsible for releasing. */
+typedef NPError (* NPP_GetValueProcPtr)(NPP instance, NPPVariable variable, void *ret_value);
+typedef NPError (* NPP_SetValueProcPtr)(NPP instance, NPNVariable variable, void *value);
+typedef NPBool (* NPP_GotFocusPtr)(NPP instance, NPFocusDirection direction);
+typedef void (* NPP_LostFocusPtr)(NPP instance);
+typedef void (* NPP_URLRedirectNotifyPtr)(NPP instance, const char* url, int32_t status, void* notifyData);
+typedef NPError (* NPP_ClearSiteDataPtr)(const char* site, uint64_t flags, uint64_t maxAge);
+typedef char** (* NPP_GetSitesWithDataPtr)(void);
+typedef void (* NPP_DidCompositePtr)(NPP instance);
+
+typedef NPError (*NPN_GetValueProcPtr)(NPP instance, NPNVariable variable, void *ret_value);
+typedef NPError (*NPN_SetValueProcPtr)(NPP instance, NPPVariable variable, void *value);
+typedef NPError (*NPN_GetURLNotifyProcPtr)(NPP instance, const char* url, const char* window, void* notifyData);
+typedef NPError (*NPN_PostURLNotifyProcPtr)(NPP instance, const char* url, const char* window, uint32_t len, const char* buf, NPBool file, void* notifyData);
+typedef NPError (*NPN_GetURLProcPtr)(NPP instance, const char* url, const char* window);
+typedef NPError (*NPN_PostURLProcPtr)(NPP instance, const char* url, const char* window, uint32_t len, const char* buf, NPBool file);
+typedef NPError (*NPN_RequestReadProcPtr)(NPStream* stream, NPByteRange* rangeList);
+typedef NPError (*NPN_NewStreamProcPtr)(NPP instance, NPMIMEType type, const char* window, NPStream** stream);
+typedef int32_t (*NPN_WriteProcPtr)(NPP instance, NPStream* stream, int32_t len, void* buffer);
+typedef NPError (*NPN_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason);
+typedef void (*NPN_StatusProcPtr)(NPP instance, const char* message);
+/* Browser manages the lifetime of the buffer returned by NPN_UserAgent, don't
+ depend on it sticking around and don't free it. */
+typedef const char* (*NPN_UserAgentProcPtr)(NPP instance);
+typedef void* (*NPN_MemAllocProcPtr)(uint32_t size);
+typedef void (*NPN_MemFreeProcPtr)(void* ptr);
+typedef uint32_t (*NPN_MemFlushProcPtr)(uint32_t size);
+typedef void (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages);
+typedef void* (*NPN_GetJavaEnvProcPtr)(void);
+typedef void* (*NPN_GetJavaPeerProcPtr)(NPP instance);
+typedef void (*NPN_InvalidateRectProcPtr)(NPP instance, NPRect *rect);
+typedef void (*NPN_InvalidateRegionProcPtr)(NPP instance, NPRegion region);
+typedef void (*NPN_ForceRedrawProcPtr)(NPP instance);
+typedef NPIdentifier (*NPN_GetStringIdentifierProcPtr)(const NPUTF8* name);
+typedef void (*NPN_GetStringIdentifiersProcPtr)(const NPUTF8** names, int32_t nameCount, NPIdentifier* identifiers);
+typedef NPIdentifier (*NPN_GetIntIdentifierProcPtr)(int32_t intid);
+typedef bool (*NPN_IdentifierIsStringProcPtr)(NPIdentifier identifier);
+typedef NPUTF8* (*NPN_UTF8FromIdentifierProcPtr)(NPIdentifier identifier);
+typedef int32_t (*NPN_IntFromIdentifierProcPtr)(NPIdentifier identifier);
+typedef NPObject* (*NPN_CreateObjectProcPtr)(NPP npp, NPClass *aClass);
+typedef NPObject* (*NPN_RetainObjectProcPtr)(NPObject *obj);
+typedef void (*NPN_ReleaseObjectProcPtr)(NPObject *obj);
+typedef bool (*NPN_InvokeProcPtr)(NPP npp, NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result);
+typedef bool (*NPN_InvokeDefaultProcPtr)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+typedef bool (*NPN_EvaluateProcPtr)(NPP npp, NPObject *obj, NPString *script, NPVariant *result);
+typedef bool (*NPN_GetPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName, NPVariant *result);
+typedef bool (*NPN_SetPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName, const NPVariant *value);
+typedef bool (*NPN_RemovePropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName);
+typedef bool (*NPN_HasPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName);
+typedef bool (*NPN_HasMethodProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName);
+typedef void (*NPN_ReleaseVariantValueProcPtr)(NPVariant *variant);
+typedef void (*NPN_SetExceptionProcPtr)(NPObject *obj, const NPUTF8 *message);
+typedef void (*NPN_PushPopupsEnabledStateProcPtr)(NPP npp, NPBool enabled);
+typedef void (*NPN_PopPopupsEnabledStateProcPtr)(NPP npp);
+typedef bool (*NPN_EnumerateProcPtr)(NPP npp, NPObject *obj, NPIdentifier **identifier, uint32_t *count);
+typedef void (*NPN_PluginThreadAsyncCallProcPtr)(NPP instance, void (*func)(void *), void *userData);
+typedef bool (*NPN_ConstructProcPtr)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+typedef NPError (*NPN_GetValueForURLPtr)(NPP npp, NPNURLVariable variable, const char *url, char **value, uint32_t *len);
+typedef NPError (*NPN_SetValueForURLPtr)(NPP npp, NPNURLVariable variable, const char *url, const char *value, uint32_t len);
+typedef NPError (*NPN_GetAuthenticationInfoPtr)(NPP npp, const char *protocol, const char *host, int32_t port, const char *scheme, const char *realm, char **username, uint32_t *ulen, char **password, uint32_t *plen);
+typedef uint32_t (*NPN_ScheduleTimerPtr)(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID));
+typedef void (*NPN_UnscheduleTimerPtr)(NPP instance, uint32_t timerID);
+typedef NPError (*NPN_PopUpContextMenuPtr)(NPP instance, NPMenu* menu);
+typedef NPBool (*NPN_ConvertPointPtr)(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
+typedef NPBool (*NPN_HandleEventPtr)(NPP instance, void *event, NPBool handled);
+typedef NPBool (*NPN_UnfocusInstancePtr)(NPP instance, NPFocusDirection direction);
+typedef void (*NPN_URLRedirectResponsePtr)(NPP instance, void* notifyData, NPBool allow);
+typedef NPError (*NPN_InitAsyncSurfacePtr)(NPP instance, NPSize *size, NPImageFormat format, void *initData, NPAsyncSurface *surface);
+typedef NPError (*NPN_FinalizeAsyncSurfacePtr)(NPP instance, NPAsyncSurface *surface);
+typedef void (*NPN_SetCurrentAsyncSurfacePtr)(NPP instance, NPAsyncSurface *surface, NPRect *changed);
+
+typedef void (*NPN_DummyPtr)(void);
+
+typedef struct _NPPluginFuncs {
+ uint16_t size;
+ uint16_t version;
+ NPP_NewProcPtr newp;
+ NPP_DestroyProcPtr destroy;
+ NPP_SetWindowProcPtr setwindow;
+ NPP_NewStreamProcPtr newstream;
+ NPP_DestroyStreamProcPtr destroystream;
+ NPP_StreamAsFileProcPtr asfile;
+ NPP_WriteReadyProcPtr writeready;
+ NPP_WriteProcPtr write;
+ NPP_PrintProcPtr print;
+ NPP_HandleEventProcPtr event;
+ NPP_URLNotifyProcPtr urlnotify;
+ void* javaClass;
+ NPP_GetValueProcPtr getvalue;
+ NPP_SetValueProcPtr setvalue;
+ NPP_GotFocusPtr gotfocus;
+ NPP_LostFocusPtr lostfocus;
+ NPP_URLRedirectNotifyPtr urlredirectnotify;
+ NPP_ClearSiteDataPtr clearsitedata;
+ NPP_GetSitesWithDataPtr getsiteswithdata;
+ NPP_DidCompositePtr didComposite;
+} NPPluginFuncs;
+
+typedef struct _NPNetscapeFuncs {
+ uint16_t size;
+ uint16_t version;
+ NPN_GetURLProcPtr geturl;
+ NPN_PostURLProcPtr posturl;
+ NPN_RequestReadProcPtr requestread;
+ NPN_NewStreamProcPtr newstream;
+ NPN_WriteProcPtr write;
+ NPN_DestroyStreamProcPtr destroystream;
+ NPN_StatusProcPtr status;
+ NPN_UserAgentProcPtr uagent;
+ NPN_MemAllocProcPtr memalloc;
+ NPN_MemFreeProcPtr memfree;
+ NPN_MemFlushProcPtr memflush;
+ NPN_ReloadPluginsProcPtr reloadplugins;
+ NPN_GetJavaEnvProcPtr getJavaEnv;
+ NPN_GetJavaPeerProcPtr getJavaPeer;
+ NPN_GetURLNotifyProcPtr geturlnotify;
+ NPN_PostURLNotifyProcPtr posturlnotify;
+ NPN_GetValueProcPtr getvalue;
+ NPN_SetValueProcPtr setvalue;
+ NPN_InvalidateRectProcPtr invalidaterect;
+ NPN_InvalidateRegionProcPtr invalidateregion;
+ NPN_ForceRedrawProcPtr forceredraw;
+ NPN_GetStringIdentifierProcPtr getstringidentifier;
+ NPN_GetStringIdentifiersProcPtr getstringidentifiers;
+ NPN_GetIntIdentifierProcPtr getintidentifier;
+ NPN_IdentifierIsStringProcPtr identifierisstring;
+ NPN_UTF8FromIdentifierProcPtr utf8fromidentifier;
+ NPN_IntFromIdentifierProcPtr intfromidentifier;
+ NPN_CreateObjectProcPtr createobject;
+ NPN_RetainObjectProcPtr retainobject;
+ NPN_ReleaseObjectProcPtr releaseobject;
+ NPN_InvokeProcPtr invoke;
+ NPN_InvokeDefaultProcPtr invokeDefault;
+ NPN_EvaluateProcPtr evaluate;
+ NPN_GetPropertyProcPtr getproperty;
+ NPN_SetPropertyProcPtr setproperty;
+ NPN_RemovePropertyProcPtr removeproperty;
+ NPN_HasPropertyProcPtr hasproperty;
+ NPN_HasMethodProcPtr hasmethod;
+ NPN_ReleaseVariantValueProcPtr releasevariantvalue;
+ NPN_SetExceptionProcPtr setexception;
+ NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate;
+ NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate;
+ NPN_EnumerateProcPtr enumerate;
+ NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall;
+ NPN_ConstructProcPtr construct;
+ NPN_GetValueForURLPtr getvalueforurl;
+ NPN_SetValueForURLPtr setvalueforurl;
+ NPN_GetAuthenticationInfoPtr getauthenticationinfo;
+ NPN_ScheduleTimerPtr scheduletimer;
+ NPN_UnscheduleTimerPtr unscheduletimer;
+ NPN_PopUpContextMenuPtr popupcontextmenu;
+ NPN_ConvertPointPtr convertpoint;
+ NPN_HandleEventPtr handleevent;
+ NPN_UnfocusInstancePtr unfocusinstance;
+ NPN_URLRedirectResponsePtr urlredirectresponse;
+ NPN_InitAsyncSurfacePtr initasyncsurface;
+ NPN_FinalizeAsyncSurfacePtr finalizeasyncsurface;
+ NPN_SetCurrentAsyncSurfacePtr setcurrentasyncsurface;
+} NPNetscapeFuncs;
+
+#ifdef XP_MACOSX
+/*
+ * Mac OS X version(s) of NP_GetMIMEDescription(const char *)
+ * These can be called to retreive MIME information from the plugin dynamically
+ *
+ * Note: For compatibility with Quicktime, BPSupportedMIMEtypes is another way
+ * to get mime info from the plugin only on OSX and may not be supported
+ * in furture version -- use NP_GetMIMEDescription instead
+ */
+enum
+{
+ kBPSupportedMIMETypesStructVers_1 = 1
+};
+typedef struct _BPSupportedMIMETypes
+{
+ SInt32 structVersion; /* struct version */
+ Handle typeStrings; /* STR# formated handle, allocated by plug-in */
+ Handle infoStrings; /* STR# formated handle, allocated by plug-in */
+} BPSupportedMIMETypes;
+OSErr BP_GetSupportedMIMETypes(BPSupportedMIMETypes *mimeInfo, UInt32 flags);
+#define NP_GETMIMEDESCRIPTION_NAME "NP_GetMIMEDescription"
+typedef const char* (*NP_GetMIMEDescriptionProcPtr)(void);
+typedef OSErr (*BP_GetSupportedMIMETypesProcPtr)(BPSupportedMIMETypes*, UInt32);
+#endif
+
+#if defined(_WIN32)
+#define OSCALL WINAPI
+#else
+#define OSCALL
+#endif
+
+#if defined(XP_UNIX)
+/* GCC 3.3 and later support the visibility attribute. */
+#if defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))
+#define NP_VISIBILITY_DEFAULT __attribute__((visibility("default")))
+#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+#define NP_VISIBILITY_DEFAULT __global
+#else
+#define NP_VISIBILITY_DEFAULT
+#endif
+#define NP_EXPORT(__type) NP_VISIBILITY_DEFAULT __type
+#endif
+
+#if defined(_WIN32)
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* plugin meta member functions */
+typedef NPError (OSCALL *NP_GetEntryPointsFunc)(NPPluginFuncs*);
+NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs);
+typedef NPError (OSCALL *NP_InitializeFunc)(NPNetscapeFuncs*);
+NPError OSCALL NP_Initialize(NPNetscapeFuncs* bFuncs);
+typedef NPError (OSCALL *NP_ShutdownFunc)(void);
+NPError OSCALL NP_Shutdown(void);
+typedef const char* (*NP_GetMIMEDescriptionFunc)(void);
+const char* NP_GetMIMEDescription(void);
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+#ifdef XP_UNIX
+#ifdef __cplusplus
+extern "C" {
+#endif
+typedef char* (*NP_GetPluginVersionFunc)(void);
+NP_EXPORT(char*) NP_GetPluginVersion(void);
+typedef const char* (*NP_GetMIMEDescriptionFunc)(void);
+NP_EXPORT(const char*) NP_GetMIMEDescription(void);
+#ifdef XP_MACOSX
+typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*);
+NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs);
+typedef NPError (*NP_GetEntryPointsFunc)(NPPluginFuncs*);
+NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs);
+#else
+#ifdef MOZ_WIDGET_ANDROID
+typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*, NPPluginFuncs*, JNIEnv* pEnv);
+NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, JNIEnv* pEnv);
+#else
+typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*, NPPluginFuncs*);
+NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs);
+#endif
+#endif
+typedef NPError (*NP_ShutdownFunc)(void);
+NP_EXPORT(NPError) NP_Shutdown(void);
+typedef NPError (*NP_GetValueFunc)(void *, NPPVariable, void *);
+NP_EXPORT(NPError) NP_GetValue(void *future, NPPVariable aVariable, void *aValue);
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+#endif /* npfunctions_h_ */
diff --git a/dom/plugins/base/npruntime.h b/dom/plugins/base/npruntime.h
new file mode 100644
index 000000000..4af7d3c34
--- /dev/null
+++ b/dom/plugins/base/npruntime.h
@@ -0,0 +1,393 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (c) 2004, Apple Computer, Inc. and The Mozilla Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla
+ * Foundation ("Mozilla") nor the names of their contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, MOZILLA OR
+ * THEIR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _NP_RUNTIME_H_
+#define _NP_RUNTIME_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "nptypes.h"
+
+/*
+ This API is used to facilitate binding code written in C to script
+ objects. The API in this header does not assume the presence of a
+ user agent. That is, it can be used to bind C code to scripting
+ environments outside of the context of a user agent.
+
+ However, the normal use of the this API is in the context of a
+ scripting environment running in a browser or other user agent.
+ In particular it is used to support the extended Netscape
+ script-ability API for plugins (NP-SAP). NP-SAP is an extension
+ of the Netscape plugin API. As such we have adopted the use of
+ the "NP" prefix for this API.
+
+ The following NP{N|P}Variables were added to the Netscape plugin
+ API (in npapi.h):
+
+ NPNVWindowNPObject
+ NPNVPluginElementNPObject
+ NPPVpluginScriptableNPObject
+
+ These variables are exposed through NPN_GetValue() and
+ NPP_GetValue() (respectively) and are used to establish the
+ initial binding between the user agent and native code. The DOM
+ objects in the user agent can be examined and manipulated using
+ the NPN_ functions that operate on NPObjects described in this
+ header.
+
+ To the extent possible the assumptions about the scripting
+ language used by the scripting environment have been minimized.
+*/
+
+#define NP_BEGIN_MACRO do {
+#define NP_END_MACRO } while (0)
+
+/*
+ Objects (non-primitive data) passed between 'C' and script is
+ always wrapped in an NPObject. The 'interface' of an NPObject is
+ described by an NPClass.
+*/
+typedef struct NPObject NPObject;
+typedef struct NPClass NPClass;
+
+typedef char NPUTF8;
+typedef struct _NPString {
+ const NPUTF8 *UTF8Characters;
+ uint32_t UTF8Length;
+} NPString;
+
+typedef enum {
+ NPVariantType_Void,
+ NPVariantType_Null,
+ NPVariantType_Bool,
+ NPVariantType_Int32,
+ NPVariantType_Double,
+ NPVariantType_String,
+ NPVariantType_Object
+} NPVariantType;
+
+typedef struct _NPVariant {
+ NPVariantType type;
+ union {
+ bool boolValue;
+ int32_t intValue;
+ double doubleValue;
+ NPString stringValue;
+ NPObject *objectValue;
+ } value;
+} NPVariant;
+
+/*
+ NPN_ReleaseVariantValue is called on all 'out' parameters
+ references. Specifically it is to be called on variants that own
+ their value, as is the case with all non-const NPVariant*
+ arguments after a successful call to any methods (except this one)
+ in this API.
+
+ After calling NPN_ReleaseVariantValue, the type of the variant
+ will be NPVariantType_Void.
+*/
+void NPN_ReleaseVariantValue(NPVariant *variant);
+
+#define NPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void)
+#define NPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null)
+#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool)
+#define NPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32)
+#define NPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double)
+#define NPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String)
+#define NPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object)
+
+#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue)
+#define NPVARIANT_TO_INT32(_v) ((_v).value.intValue)
+#define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue)
+#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue)
+#define NPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue)
+
+#define VOID_TO_NPVARIANT(_v) \
+NP_BEGIN_MACRO \
+ (_v).type = NPVariantType_Void; \
+ (_v).value.objectValue = NULL; \
+NP_END_MACRO
+
+#define NULL_TO_NPVARIANT(_v) \
+NP_BEGIN_MACRO \
+ (_v).type = NPVariantType_Null; \
+ (_v).value.objectValue = NULL; \
+NP_END_MACRO
+
+#define BOOLEAN_TO_NPVARIANT(_val, _v) \
+NP_BEGIN_MACRO \
+ (_v).type = NPVariantType_Bool; \
+ (_v).value.boolValue = !!(_val); \
+NP_END_MACRO
+
+#define INT32_TO_NPVARIANT(_val, _v) \
+NP_BEGIN_MACRO \
+ (_v).type = NPVariantType_Int32; \
+ (_v).value.intValue = _val; \
+NP_END_MACRO
+
+#define DOUBLE_TO_NPVARIANT(_val, _v) \
+NP_BEGIN_MACRO \
+ (_v).type = NPVariantType_Double; \
+ (_v).value.doubleValue = _val; \
+NP_END_MACRO
+
+#define STRINGZ_TO_NPVARIANT(_val, _v) \
+NP_BEGIN_MACRO \
+ (_v).type = NPVariantType_String; \
+ NPString str = { _val, (uint32_t)(strlen(_val)) }; \
+ (_v).value.stringValue = str; \
+NP_END_MACRO
+
+#define STRINGN_TO_NPVARIANT(_val, _len, _v) \
+NP_BEGIN_MACRO \
+ (_v).type = NPVariantType_String; \
+ NPString str = { _val, (uint32_t)(_len) }; \
+ (_v).value.stringValue = str; \
+NP_END_MACRO
+
+#define OBJECT_TO_NPVARIANT(_val, _v) \
+NP_BEGIN_MACRO \
+ (_v).type = NPVariantType_Object; \
+ (_v).value.objectValue = _val; \
+NP_END_MACRO
+
+
+/*
+ Type mappings (JavaScript types have been used for illustration
+ purposes):
+
+ JavaScript to C (NPVariant with type:)
+ undefined NPVariantType_Void
+ null NPVariantType_Null
+ Boolean NPVariantType_Bool
+ Number NPVariantType_Double or NPVariantType_Int32
+ String NPVariantType_String
+ Object NPVariantType_Object
+
+ C (NPVariant with type:) to JavaScript
+ NPVariantType_Void undefined
+ NPVariantType_Null null
+ NPVariantType_Bool Boolean
+ NPVariantType_Int32 Number
+ NPVariantType_Double Number
+ NPVariantType_String String
+ NPVariantType_Object Object
+*/
+
+typedef void *NPIdentifier;
+
+/*
+ NPObjects have methods and properties. Methods and properties are
+ identified with NPIdentifiers. These identifiers may be reflected
+ in script. NPIdentifiers can be either strings or integers, IOW,
+ methods and properties can be identified by either strings or
+ integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be
+ compared using ==. In case of any errors, the requested
+ NPIdentifier(s) will be NULL. NPIdentifier lifetime is controlled
+ by the browser. Plugins do not need to worry about memory management
+ with regards to NPIdentifiers.
+*/
+NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name);
+void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount,
+ NPIdentifier *identifiers);
+NPIdentifier NPN_GetIntIdentifier(int32_t intid);
+bool NPN_IdentifierIsString(NPIdentifier identifier);
+
+/*
+ The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed.
+*/
+NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier);
+
+/*
+ Get the integer represented by identifier. If identifier is not an
+ integer identifier, the behaviour is undefined.
+*/
+int32_t NPN_IntFromIdentifier(NPIdentifier identifier);
+
+/*
+ NPObject behavior is implemented using the following set of
+ callback functions.
+
+ The NPVariant *result argument of these functions (where
+ applicable) should be released using NPN_ReleaseVariantValue().
+*/
+typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass);
+typedef void (*NPDeallocateFunctionPtr)(NPObject *npobj);
+typedef void (*NPInvalidateFunctionPtr)(NPObject *npobj);
+typedef bool (*NPHasMethodFunctionPtr)(NPObject *npobj, NPIdentifier name);
+typedef bool (*NPInvokeFunctionPtr)(NPObject *npobj, NPIdentifier name,
+ const NPVariant *args, uint32_t argCount,
+ NPVariant *result);
+typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj,
+ const NPVariant *args,
+ uint32_t argCount,
+ NPVariant *result);
+typedef bool (*NPHasPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name);
+typedef bool (*NPGetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name,
+ NPVariant *result);
+typedef bool (*NPSetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name,
+ const NPVariant *value);
+typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj,
+ NPIdentifier name);
+typedef bool (*NPEnumerationFunctionPtr)(NPObject *npobj, NPIdentifier **value,
+ uint32_t *count);
+typedef bool (*NPConstructFunctionPtr)(NPObject *npobj,
+ const NPVariant *args,
+ uint32_t argCount,
+ NPVariant *result);
+
+/*
+ NPObjects returned by create, retain, invoke, and getProperty pass
+ a reference count to the caller. That is, the callee adds a
+ reference count which passes to the caller. It is the caller's
+ responsibility to release the returned object.
+
+ NPInvokeFunctionPtr function may return 0 to indicate a void
+ result.
+
+ NPInvalidateFunctionPtr is called by the scripting environment
+ when the native code is shutdown. Any attempt to message a
+ NPObject instance after the invalidate callback has been
+ called will result in undefined behavior, even if the native code
+ is still retaining those NPObject instances. (The runtime
+ will typically return immediately, with 0 or NULL, from an
+ attempt to dispatch to a NPObject, but this behavior should not
+ be depended upon.)
+
+ The NPEnumerationFunctionPtr function may pass an array of
+ NPIdentifiers back to the caller. The callee allocs the memory of
+ the array using NPN_MemAlloc(), and it's the caller's responsibility
+ to release it using NPN_MemFree().
+*/
+struct NPClass
+{
+ uint32_t structVersion;
+ NPAllocateFunctionPtr allocate;
+ NPDeallocateFunctionPtr deallocate;
+ NPInvalidateFunctionPtr invalidate;
+ NPHasMethodFunctionPtr hasMethod;
+ NPInvokeFunctionPtr invoke;
+ NPInvokeDefaultFunctionPtr invokeDefault;
+ NPHasPropertyFunctionPtr hasProperty;
+ NPGetPropertyFunctionPtr getProperty;
+ NPSetPropertyFunctionPtr setProperty;
+ NPRemovePropertyFunctionPtr removeProperty;
+ NPEnumerationFunctionPtr enumerate;
+ NPConstructFunctionPtr construct;
+};
+
+#define NP_CLASS_STRUCT_VERSION 3
+
+#define NP_CLASS_STRUCT_VERSION_ENUM 2
+#define NP_CLASS_STRUCT_VERSION_CTOR 3
+
+#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass) \
+ ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM)
+
+#define NP_CLASS_STRUCT_VERSION_HAS_CTOR(npclass) \
+ ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR)
+
+struct NPObject {
+ NPClass *_class;
+ uint32_t referenceCount;
+ /*
+ * Additional space may be allocated here by types of NPObjects
+ */
+};
+
+/*
+ If the class has an allocate function, NPN_CreateObject invokes
+ that function, otherwise a NPObject is allocated and
+ returned. This method will initialize the referenceCount member of
+ the NPObject to 1.
+*/
+NPObject *NPN_CreateObject(NPP npp, NPClass *aClass);
+
+/*
+ Increment the NPObject's reference count.
+*/
+NPObject *NPN_RetainObject(NPObject *npobj);
+
+/*
+ Decremented the NPObject's reference count. If the reference
+ count goes to zero, the class's destroy function is invoke if
+ specified, otherwise the object is freed directly.
+*/
+void NPN_ReleaseObject(NPObject *npobj);
+
+/*
+ Functions to access script objects represented by NPObject.
+
+ Calls to script objects are synchronous. If a function returns a
+ value, it will be supplied via the result NPVariant
+ argument. Successful calls will return true, false will be
+ returned in case of an error.
+
+ Calls made from plugin code to script must be made from the thread
+ on which the plugin was initialized.
+*/
+
+bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName,
+ const NPVariant *args, uint32_t argCount, NPVariant *result);
+bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script,
+ NPVariant *result);
+bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName,
+ NPVariant *result);
+bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName,
+ const NPVariant *value);
+bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName);
+bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName);
+bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName);
+bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier,
+ uint32_t *count);
+bool NPN_Construct(NPP npp, NPObject *npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+
+/*
+ NPN_SetException may be called to trigger a script exception upon
+ return from entry points into NPObjects. Typical usage:
+
+ NPN_SetException (npobj, message);
+*/
+void NPN_SetException(NPObject *npobj, const NPUTF8 *message);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/dom/plugins/base/nptypes.h b/dom/plugins/base/nptypes.h
new file mode 100644
index 000000000..12a5fb78e
--- /dev/null
+++ b/dom/plugins/base/nptypes.h
@@ -0,0 +1,88 @@
+/* -*- 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/. */
+
+#ifndef nptypes_h_
+#define nptypes_h_
+
+/*
+ * Header file for ensuring that C99 types ([u]int32_t, [u]int64_t and bool) and
+ * true/false macros are available.
+ */
+
+#if defined(WIN32)
+ /*
+ * Win32 and OS/2 don't know C99, so define [u]int_16/32/64 here. The bool
+ * is predefined tho, both in C and C++.
+ */
+ typedef short int16_t;
+ typedef unsigned short uint16_t;
+ typedef int int32_t;
+ typedef unsigned int uint32_t;
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX)
+ /*
+ * AIX and SunOS ship a inttypes.h header that defines [u]int32_t,
+ * but not bool for C.
+ */
+ #include <inttypes.h>
+
+ #ifndef __cplusplus
+ typedef int bool;
+ #define true 1
+ #define false 0
+ #endif
+#elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD)
+ /*
+ * BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and
+ * u_int32_t.
+ */
+ #include <sys/types.h>
+
+ /*
+ * BSD/OS ships no header that defines uint32_t, nor bool (for C)
+ */
+ #if defined(bsdi)
+ typedef u_int32_t uint32_t;
+ typedef u_int64_t uint64_t;
+
+ #if !defined(__cplusplus)
+ typedef int bool;
+ #define true 1
+ #define false 0
+ #endif
+ #else
+ /*
+ * FreeBSD and OpenBSD define uint32_t and bool.
+ */
+ #include <inttypes.h>
+ #include <stdbool.h>
+ #endif
+#elif defined(BEOS)
+ #include <inttypes.h>
+#else
+ /*
+ * For those that ship a standard C99 stdint.h header file, include
+ * it. Can't do the same for stdbool.h tho, since some systems ship
+ * with a stdbool.h file that doesn't compile!
+ */
+ #include <stdint.h>
+
+ #ifndef __cplusplus
+ #if !defined(__GNUC__) || (__GNUC__ > 2 || __GNUC_MINOR__ > 95)
+ #include <stdbool.h>
+ #else
+ /*
+ * GCC 2.91 can't deal with a typedef for bool, but a #define
+ * works.
+ */
+ #define bool int
+ #define true 1
+ #define false 0
+ #endif
+ #endif
+#endif
+
+#endif /* nptypes_h_ */
diff --git a/dom/plugins/base/nsIHTTPHeaderListener.idl b/dom/plugins/base/nsIHTTPHeaderListener.idl
new file mode 100644
index 000000000..7be881641
--- /dev/null
+++ b/dom/plugins/base/nsIHTTPHeaderListener.idl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsISupports.idl"
+
+/**
+ * The nsIHTTPHeaderListener interface allows plugin authors to
+ * access HTTP Response headers after issuing an
+ * nsIPluginHost::{GetURL,PostURL}() call. <P>
+ */
+
+[scriptable, uuid(ea51e0b8-871c-4b85-92da-6f400394c5ec)]
+interface nsIHTTPHeaderListener : nsISupports
+{
+ /**
+ * Called for each HTTP Response header.
+ * NOTE: You must copy the values of the params.
+ */
+ void newResponseHeader(in string headerName, in string headerValue);
+
+ /**
+ * Called once for the HTTP Response status line.
+ * Value does NOT include a terminating newline.
+ * NOTE: You must copy this value.
+ */
+ void statusLine(in string line);
+};
diff --git a/dom/plugins/base/nsIPluginDocument.idl b/dom/plugins/base/nsIPluginDocument.idl
new file mode 100644
index 000000000..f91aac621
--- /dev/null
+++ b/dom/plugins/base/nsIPluginDocument.idl
@@ -0,0 +1,16 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsIStreamListener.idl"
+
+[uuid(a93a0f0f-24f0-4206-a21b-56a43dcbdd88)]
+interface nsIPluginDocument : nsISupports
+{
+ /**
+ * Causes the plugin to print in full-page mode
+ */
+ void print();
+};
diff --git a/dom/plugins/base/nsIPluginHost.idl b/dom/plugins/base/nsIPluginHost.idl
new file mode 100644
index 000000000..d1124aa25
--- /dev/null
+++ b/dom/plugins/base/nsIPluginHost.idl
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nspluginroot.idl"
+#include "nsISupports.idl"
+#include "nsIPluginTag.idl"
+
+%{C++
+#define MOZ_PLUGIN_HOST_CONTRACTID \
+ "@mozilla.org/plugin/host;1"
+%}
+
+[scriptable, function, uuid(9c311778-7c2c-4ad8-b439-b8a2786a20dd)]
+interface nsIClearSiteDataCallback : nsISupports
+{
+ /**
+ * callback with the result from a call to clearSiteData
+ */
+ void callback(in nsresult rv);
+};
+
+[scriptable, uuid(f938f5ba-7093-42cd-a559-af8039d99204)]
+interface nsIPluginHost : nsISupports
+{
+ /**
+ * Causes the plugins directory to be searched again for new plugin
+ * libraries.
+ */
+ void reloadPlugins();
+
+ void getPluginTags([optional] out unsigned long aPluginCount,
+ [retval, array, size_is(aPluginCount)] out nsIPluginTag aResults);
+
+ /*
+ * Flags for use with clearSiteData.
+ *
+ * FLAG_CLEAR_ALL: clear all data associated with a site.
+ * FLAG_CLEAR_CACHE: clear cached data that can be retrieved again without
+ * loss of functionality. To be used out of concern for
+ * space and not necessarily privacy.
+ */
+ const uint32_t FLAG_CLEAR_ALL = 0;
+ const uint32_t FLAG_CLEAR_CACHE = 1;
+
+ /*
+ * For use with Get*ForType functions
+ */
+ const uint32_t EXCLUDE_NONE = 0;
+ const uint32_t EXCLUDE_DISABLED = 1 << 0;
+ const uint32_t EXCLUDE_FAKE = 1 << 1;
+
+ /*
+ * Clear site data for a given plugin.
+ *
+ * @param plugin: the plugin to clear data for, such as one returned by
+ * nsIPluginHost.getPluginTags.
+ * @param domain: the domain to clear data for. If this argument is null,
+ * clear data for all domains. Otherwise, it must be a domain
+ * only (not a complete URI or IRI). The base domain for the
+ * given site will be determined; any data for the base domain
+ * or its subdomains will be cleared.
+ * @param flags: a flag value defined above.
+ * @param maxAge: the maximum age in seconds of data to clear, inclusive. If
+ * maxAge is 0, no data is cleared; if it is -1, all data is
+ * cleared.
+ *
+ * @throws NS_ERROR_INVALID_ARG if the domain argument is malformed.
+ * @throws NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED if maxAge is a value other
+ * than -1 and the plugin does not support clearing by timerange in
+ * general or for that particular site and/or flag combination.
+ */
+ void clearSiteData(in nsIPluginTag plugin, in AUTF8String domain,
+ in uint64_t flags, in int64_t maxAge,
+ in nsIClearSiteDataCallback callback);
+
+ /*
+ * Determine if a plugin has stored data for a given site.
+ *
+ * @param plugin: the plugin to query, such as one returned by
+ * nsIPluginHost.getPluginTags.
+ * @param domain: the domain to test. If this argument is null, test if data
+ * is stored for any site. The base domain for the given domain
+ * will be determined; if any data for the base domain or its
+ * subdomains is found, return true.
+ */
+ boolean siteHasData(in nsIPluginTag plugin, in AUTF8String domain);
+
+ /**
+ * Get the "permission string" for the plugin. This is a string that can be
+ * passed to the permission manager to see whether the plugin is allowed to
+ * run, for example. This will typically be based on the plugin's "nice name"
+ * and its blocklist state.
+ *
+ * @mimeType The MIME type we're interested in.
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ */
+ ACString getPermissionStringForType(in AUTF8String mimeType,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Get the "permission string" for the plugin. This is a string that can be
+ * passed to the permission manager to see whether the plugin is allowed to
+ * run, for example. This will typically be based on the plugin's "nice name"
+ * and its blocklist state.
+ *
+ * @tag The tage we're interested in
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ */
+ ACString getPermissionStringForTag(in nsIPluginTag tag,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Get the nsIPluginTag for this MIME type. This method works with both
+ * enabled and disabled/blocklisted plugins, but an enabled plugin will
+ * always be returned if available.
+ *
+ * A fake plugin tag, if one exists and is available, will be returned in
+ * preference to NPAPI plugin tags unless excluded by the excludeFlags.
+ *
+ * @mimeType The MIME type we're interested in.
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ *
+ * @throws NS_ERROR_NOT_AVAILABLE if no plugin is available for this MIME
+ * type.
+ */
+ nsIPluginTag getPluginTagForType(in AUTF8String mimeType,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Get the nsIPluginTag enabled state for this MIME type. See
+ * nsIPluginTag.enabledState.
+ *
+ * @mimeType The MIME type we're interested in.
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ */
+ unsigned long getStateForType(in AUTF8String mimeType,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Get the blocklist state for a MIME type. See nsIPluginTag.blocklistState.
+ *
+ * @mimeType The MIME type we're interested in.
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ */
+ uint32_t getBlocklistStateForType(in AUTF8String aMimeType,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Create a fake plugin tag, register it, and return it. The argument is a
+ * FakePluginTagInit dictionary. See documentation in
+ * FakePluginTagInit.webidl for what it should look like. Will throw
+ * NS_ERROR_UNEXPECTED if there is already a fake plugin registered with the
+ * given handler URI.
+ */
+ [implicit_jscontext]
+ nsIFakePluginTag registerFakePlugin(in jsval initDictionary);
+
+ /**
+ * Get a reference to an existing fake plugin tag for the given MIME type, if
+ * any. Can return null.
+ */
+ nsIFakePluginTag getFakePlugin(in AUTF8String mimeType);
+
+ /**
+ * Unregister a fake plugin. The argument can be the .handlerURI.spec of an
+ * existing nsIFakePluginTag, or just a known handler URI string that was
+ * passed in the FakePluginTagInit when registering.
+ */
+ void unregisterFakePlugin(in AUTF8String handlerURI);
+};
diff --git a/dom/plugins/base/nsIPluginInputStream.idl b/dom/plugins/base/nsIPluginInputStream.idl
new file mode 100644
index 000000000..886838441
--- /dev/null
+++ b/dom/plugins/base/nsIPluginInputStream.idl
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIInputStream.idl"
+#include "nspluginroot.idl"
+
+/**
+ * The nsIPluginInputStream interface ...
+ */
+[uuid(af160530-542a-11d2-8164-006008119d7a)]
+interface nsIPluginInputStream : nsIInputStream {
+ /**
+ * Corresponds to NPStream's lastmodified field.)
+ */
+ void getLastModified(out unsigned long aResult);
+
+ void requestRead(out NPByteRange aRangeList);
+};
diff --git a/dom/plugins/base/nsIPluginInstanceOwner.idl b/dom/plugins/base/nsIPluginInstanceOwner.idl
new file mode 100644
index 000000000..87e51599c
--- /dev/null
+++ b/dom/plugins/base/nsIPluginInstanceOwner.idl
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsISupports.idl"
+#include "nspluginroot.idl"
+#include "nsIInputStream.idl"
+
+interface nsIDocument;
+
+%{C++
+#include "npapi.h"
+#include "mozilla/EventForwards.h"
+class nsNPAPIPluginInstance;
+
+enum nsPluginTagType {
+ nsPluginTagType_Unknown,
+ nsPluginTagType_Embed,
+ nsPluginTagType_Object,
+ nsPluginTagType_Applet
+};
+%}
+
+[ptr] native nsNPAPIPluginInstancePtr(nsNPAPIPluginInstance);
+
+// Do not make this interface scriptable, because the virtual functions in C++
+// blocks will make script call the wrong functions.
+[uuid(7d65452e-c167-4cba-a0e3-ddc61bdde8c3)]
+interface nsIPluginInstanceOwner : nsISupports
+{
+ /**
+ * Let the owner know what its instance is
+ */
+ void setInstance(in nsNPAPIPluginInstancePtr aInstance);
+
+ /**
+ * Get the instance associated with this owner.
+ */
+ nsNPAPIPluginInstancePtr getInstance();
+
+ /**
+ * Get a handle to the window structure of the owner.
+ * This pointer cannot be made persistent by the caller.
+ */
+ void getWindow(in NPWindowStarRef aWindow);
+
+ /**
+ * Get the display mode for the plugin instance.
+ */
+ readonly attribute int32_t mode;
+
+ /**
+ * Create a place for the plugin to live in the owner's
+ * environment. this may or may not create a window
+ * depending on the windowless state of the plugin instance.
+ */
+ void createWidget();
+
+%{C++
+ /**
+ * Called when there is a valid target so that the proper
+ * frame can be updated with new content. will not be called
+ * with nullptr aTarget.
+ */
+ NS_IMETHOD
+ GetURL(const char *aURL, const char *aTarget,
+ nsIInputStream *aPostStream,
+ void *aHeadersData, uint32_t aHeadersDataLen,
+ bool aDoCheckLoadURIChecks) = 0;
+%}
+
+ /**
+ * Get the associated document.
+ */
+ readonly attribute nsIDocument document;
+
+ /**
+ * Invalidate the rectangle
+ */
+ void invalidateRect(in NPRectPtr aRect);
+
+ /**
+ * Invalidate the region
+ */
+ void invalidateRegion(in NPRegion aRegion);
+
+ /**
+ * Have the plugin recomposited.
+ */
+ void redrawPlugin();
+
+ /**
+ * Get NetscapeWindow, corresponds to NPNVnetscapeWindow
+ */
+ void getNetscapeWindow(in voidPtr aValue);
+
+ /**
+ * Convert between plugin, window, and screen coordinate spaces.
+ */
+%{C++
+ virtual NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace) = 0;
+ virtual NPError InitAsyncSurface(NPSize *size, NPImageFormat format,
+ void *initData, NPAsyncSurface *surface) = 0;
+ virtual NPError FinalizeAsyncSurface(NPAsyncSurface *surface) = 0;
+ virtual void SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed) = 0;
+%}
+
+ void setEventModel(in int32_t eventModel);
+
+ /**
+ * Call NPP_SetWindow on the plugin.
+ */
+ void callSetWindow();
+
+ /**
+ * Get the contents scale factor for the screen the plugin is
+ * drawn on.
+ */
+ double getContentsScaleFactor();
+};
diff --git a/dom/plugins/base/nsIPluginTag.idl b/dom/plugins/base/nsIPluginTag.idl
new file mode 100644
index 000000000..1c2d47734
--- /dev/null
+++ b/dom/plugins/base/nsIPluginTag.idl
@@ -0,0 +1,76 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+interface nsIURI;
+
+[scriptable, uuid(5daa99d5-265a-4397-b429-c943803e2619)]
+interface nsIPluginTag : nsISupports
+{
+ // enabledState is stored as one of the following as an integer in prefs,
+ // so if new states are added, they must not renumber the existing states.
+ const unsigned long STATE_DISABLED = 0;
+ const unsigned long STATE_CLICKTOPLAY = 1;
+ const unsigned long STATE_ENABLED = 2;
+
+ readonly attribute AUTF8String description;
+ readonly attribute AUTF8String filename;
+ readonly attribute AUTF8String fullpath;
+ readonly attribute AUTF8String version;
+ readonly attribute AUTF8String name;
+
+ // The 'nice' name of this plugin, e.g. 'flash' 'java'
+ readonly attribute AUTF8String niceName;
+
+ /**
+ * true only if this plugin is "hardblocked" and cannot be enabled.
+ */
+ // FIXME-jsplugins QI to fakePluginTag possible
+ // FIXME-jsplugins implement missing + tests (whatever that means)
+ readonly attribute boolean blocklisted;
+
+ /**
+ * true if the state is non-default and locked, false otherwise.
+ */
+ readonly attribute boolean isEnabledStateLocked;
+
+ // If this plugin is capable of being used (not disabled, blocklisted, etc)
+ readonly attribute boolean active;
+
+ // Get a specific nsIBlocklistService::STATE_*
+ readonly attribute unsigned long blocklistState;
+
+ readonly attribute boolean disabled;
+ readonly attribute boolean clicktoplay;
+ readonly attribute boolean loaded;
+ // See the STATE_* values above.
+ attribute unsigned long enabledState;
+
+ readonly attribute PRTime lastModifiedTime;
+
+ void getMimeTypes([optional] out unsigned long aCount,
+ [retval, array, size_is(aCount)] out wstring aResults);
+ void getMimeDescriptions([optional] out unsigned long aCount,
+ [retval, array, size_is(aCount)]
+ out wstring aResults);
+ void getExtensions([optional] out unsigned long aCount,
+ [retval, array, size_is(aCount)]
+ out wstring aResults);
+};
+
+/**
+ * An interface representing a "fake" plugin: one implemented in JavaScript, not
+ * as a NPAPI plug-in. See nsIPluginHost.registerFakePlugin and the
+ * documentation for the FakePluginTagInit dictionary.
+ */
+[scriptable, uuid(6d22c968-226d-4156-b230-da6ad6bbf6e8)]
+interface nsIFakePluginTag : nsIPluginTag
+{
+ // The URI that should be loaded into the tag (as a frame) to handle the
+ // plugin. Note that the original data/src value for the plugin is not loaded
+ // and will need to be requested by the handler via XHR or similar if desired.
+ readonly attribute nsIURI handlerURI;
+};
diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp
new file mode 100644
index 000000000..05e0ec4ba
--- /dev/null
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -0,0 +1,2328 @@
+/* -*- 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 "base/basictypes.h"
+
+#include "jsfriendapi.h"
+#include "jswrapper.h"
+
+#include "nsAutoPtr.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsJSNPRuntime.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsIGlobalObject.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsDOMJSUtils.h"
+#include "nsJSUtils.h"
+#include "nsIDocument.h"
+#include "nsIXPConnect.h"
+#include "xpcpublic.h"
+#include "nsIDOMElement.h"
+#include "prmem.h"
+#include "nsIContent.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsWrapperCacheInlines.h"
+#include "js/GCHashTable.h"
+#include "js/TracingAPI.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/plugins/PluginAsyncSurrogate.h"
+
+using mozilla::plugins::AsyncNPObject;
+using mozilla::plugins::PluginAsyncSurrogate;
+
+#define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class"
+
+using namespace mozilla::plugins::parent;
+using namespace mozilla;
+
+#include "mozilla/plugins/PluginScriptableObjectParent.h"
+using mozilla::plugins::PluginScriptableObjectParent;
+using mozilla::plugins::ParentNPObject;
+
+struct JSObjWrapperHasher
+{
+ typedef nsJSObjWrapperKey Key;
+ typedef Key Lookup;
+
+ static uint32_t hash(const Lookup &l) {
+ return js::MovableCellHasher<JS::Heap<JSObject*>>::hash(l.mJSObj) ^
+ HashGeneric(l.mNpp);
+ }
+
+ static bool match(const Key& k, const Lookup &l) {
+ return js::MovableCellHasher<JS::Heap<JSObject*>>::match(k.mJSObj, l.mJSObj) &&
+ k.mNpp == l.mNpp;
+ }
+};
+
+namespace JS {
+template <>
+struct GCPolicy<nsJSObjWrapper*> {
+ static void trace(JSTracer* trc, nsJSObjWrapper** wrapper, const char* name) {
+ MOZ_ASSERT(wrapper);
+ MOZ_ASSERT(*wrapper);
+ (*wrapper)->trace(trc);
+ }
+};
+} // namespace JS
+
+class NPObjWrapperHashEntry : public PLDHashEntryHdr
+{
+public:
+ NPObject *mNPObj; // Must be the first member for the PLDHash stubs to work
+ JS::TenuredHeap<JSObject*> mJSObj;
+ NPP mNpp;
+};
+
+// Hash of JSObject wrappers that wraps JSObjects as NPObjects. There
+// will be one wrapper per JSObject per plugin instance, i.e. if two
+// plugins access the JSObject x, two wrappers for x will be
+// created. This is needed to be able to properly drop the wrappers
+// when a plugin is torn down in case there's a leak in the plugin (we
+// don't want to leak the world just because a plugin leaks an
+// NPObject).
+typedef JS::GCHashMap<nsJSObjWrapperKey,
+ nsJSObjWrapper*,
+ JSObjWrapperHasher,
+ js::SystemAllocPolicy> JSObjWrapperTable;
+static JSObjWrapperTable sJSObjWrappers;
+
+// Whether it's safe to iterate sJSObjWrappers. Set to true when sJSObjWrappers
+// has been initialized and is not currently being enumerated.
+static bool sJSObjWrappersAccessible = false;
+
+// Hash of NPObject wrappers that wrap NPObjects as JSObjects.
+static PLDHashTable* sNPObjWrappers;
+
+// Global wrapper count. This includes JSObject wrappers *and*
+// NPObject wrappers. When this count goes to zero, there are no more
+// wrappers and we can kill off hash tables etc.
+static int32_t sWrapperCount;
+
+static bool sCallbackIsRegistered = false;
+
+static nsTArray<NPObject*>* sDelayedReleases;
+
+namespace {
+
+inline void
+CastNPObject(NPObject *aObj, PluginScriptableObjectParent*& aActor,
+ PluginAsyncSurrogate*& aSurrogate)
+{
+ aActor = nullptr;
+ aSurrogate = nullptr;
+ if (aObj->_class == PluginScriptableObjectParent::GetClass()) {
+ aActor = static_cast<ParentNPObject*>(aObj)->parent;
+ } else if (aObj->_class == PluginAsyncSurrogate::GetClass()) {
+ aSurrogate = static_cast<AsyncNPObject*>(aObj)->mSurrogate;
+ }
+}
+
+inline bool
+NPObjectIsOutOfProcessProxy(NPObject *obj)
+{
+ return obj->_class == PluginScriptableObjectParent::GetClass() ||
+ obj->_class == PluginAsyncSurrogate::GetClass();
+}
+
+} // namespace
+
+// Helper class that suppresses any JS exceptions that were thrown while
+// the plugin executed JS, if the nsJSObjWrapper has a destroy pending.
+// Note that this class is the product (vestige?) of a long evolution in how
+// error reporting worked, and hence the mIsDestroyPending check, and hence this
+// class in general, may or may not actually be necessary.
+
+class MOZ_STACK_CLASS AutoJSExceptionSuppressor
+{
+public:
+ AutoJSExceptionSuppressor(dom::AutoEntryScript& aes, nsJSObjWrapper* aWrapper)
+ : mAes(aes)
+ , mIsDestroyPending(aWrapper->mDestroyPending)
+ {
+ }
+
+ ~AutoJSExceptionSuppressor()
+ {
+ if (mIsDestroyPending) {
+ mAes.ClearException();
+ }
+ }
+
+protected:
+ dom::AutoEntryScript& mAes;
+ bool mIsDestroyPending;
+};
+
+
+NPClass nsJSObjWrapper::sJSObjWrapperNPClass =
+ {
+ NP_CLASS_STRUCT_VERSION,
+ nsJSObjWrapper::NP_Allocate,
+ nsJSObjWrapper::NP_Deallocate,
+ nsJSObjWrapper::NP_Invalidate,
+ nsJSObjWrapper::NP_HasMethod,
+ nsJSObjWrapper::NP_Invoke,
+ nsJSObjWrapper::NP_InvokeDefault,
+ nsJSObjWrapper::NP_HasProperty,
+ nsJSObjWrapper::NP_GetProperty,
+ nsJSObjWrapper::NP_SetProperty,
+ nsJSObjWrapper::NP_RemoveProperty,
+ nsJSObjWrapper::NP_Enumerate,
+ nsJSObjWrapper::NP_Construct
+ };
+
+static bool
+NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::Handle<JS::Value> v);
+
+static bool
+NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::ObjectOpResult &result);
+
+static bool
+NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result);
+
+static bool
+NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
+
+static bool
+NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj, JS::AutoIdVector &properties,
+ bool enumerableOnly);
+
+static bool
+NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ bool *resolvedp);
+
+static void
+NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj);
+
+static void
+NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old);
+
+static bool
+NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp);
+
+static bool
+NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp);
+
+static bool
+NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
+
+static bool
+CreateNPObjectMember(NPP npp, JSContext *cx,
+ JS::Handle<JSObject*> obj, NPObject* npobj,
+ JS::Handle<jsid> id, NPVariant* getPropertyResult,
+ JS::MutableHandle<JS::Value> vp);
+
+const static js::ClassOps sNPObjectJSWrapperClassOps = {
+ NPObjWrapper_AddProperty,
+ NPObjWrapper_DelProperty,
+ NPObjWrapper_GetProperty,
+ NPObjWrapper_SetProperty,
+ nullptr,
+ NPObjWrapper_Resolve,
+ nullptr, /* mayResolve */
+ NPObjWrapper_Finalize,
+ NPObjWrapper_Call,
+ nullptr, /* hasInstance */
+ NPObjWrapper_Construct,
+ nullptr, /* trace */
+};
+
+const static js::ClassExtension sNPObjectJSWrapperClassExtension = {
+ nullptr, /* weakmapKeyDelegateOp */
+ NPObjWrapper_ObjectMoved
+};
+
+const static js::ObjectOps sNPObjectJSWrapperObjectOps = {
+ nullptr, // lookupProperty
+ nullptr, // defineProperty
+ nullptr, // hasProperty
+ nullptr, // getProperty
+ nullptr, // setProperty
+ nullptr, // getOwnPropertyDescriptor
+ nullptr, // deleteProperty
+ nullptr, nullptr, // watch/unwatch
+ nullptr, // getElements
+ NPObjWrapper_Enumerate,
+ nullptr,
+};
+
+const static js::Class sNPObjectJSWrapperClass = {
+ NPRUNTIME_JSCLASS_NAME,
+ JSCLASS_HAS_PRIVATE |
+ JSCLASS_FOREGROUND_FINALIZE,
+ &sNPObjectJSWrapperClassOps,
+ JS_NULL_CLASS_SPEC,
+ &sNPObjectJSWrapperClassExtension,
+ &sNPObjectJSWrapperObjectOps
+};
+
+typedef struct NPObjectMemberPrivate {
+ JS::Heap<JSObject *> npobjWrapper;
+ JS::Heap<JS::Value> fieldValue;
+ JS::Heap<jsid> methodName;
+ NPP npp;
+} NPObjectMemberPrivate;
+
+static bool
+NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
+ JS::MutableHandleValue vp);
+
+static void
+NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
+
+static bool
+NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
+
+static void
+NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
+
+static bool
+NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
+
+static const JSClassOps sNPObjectMemberClassOps = {
+ nullptr, nullptr, NPObjectMember_GetProperty, nullptr,
+ nullptr, nullptr, nullptr,
+ NPObjectMember_Finalize, NPObjectMember_Call,
+ nullptr, nullptr, NPObjectMember_Trace
+};
+
+static const JSClass sNPObjectMemberClass = {
+ "NPObject Ambiguous Member class",
+ JSCLASS_HAS_PRIVATE |
+ JSCLASS_FOREGROUND_FINALIZE,
+ &sNPObjectMemberClassOps
+};
+
+static void
+OnWrapperDestroyed();
+
+static void
+TraceJSObjWrappers(JSTracer *trc, void *data)
+{
+ if (sJSObjWrappers.initialized()) {
+ sJSObjWrappers.trace(trc);
+ }
+}
+
+static void
+DelayedReleaseGCCallback(JSGCStatus status)
+{
+ if (JSGC_END == status) {
+ // Take ownership of sDelayedReleases and null it out now. The
+ // _releaseobject call below can reenter GC and double-free these objects.
+ nsAutoPtr<nsTArray<NPObject*> > delayedReleases(sDelayedReleases);
+ sDelayedReleases = nullptr;
+
+ if (delayedReleases) {
+ for (uint32_t i = 0; i < delayedReleases->Length(); ++i) {
+ NPObject* obj = (*delayedReleases)[i];
+ if (obj)
+ _releaseobject(obj);
+ OnWrapperDestroyed();
+ }
+ }
+ }
+}
+
+static bool
+RegisterGCCallbacks()
+{
+ if (sCallbackIsRegistered) {
+ return true;
+ }
+
+ // Register a callback to trace wrapped JSObjects.
+ JSContext* cx = dom::danger::GetJSContext();
+ if (!JS_AddExtraGCRootsTracer(cx, TraceJSObjWrappers, nullptr)) {
+ return false;
+ }
+
+ // Register our GC callback to perform delayed destruction of finalized
+ // NPObjects.
+ xpc::AddGCCallback(DelayedReleaseGCCallback);
+
+ sCallbackIsRegistered = true;
+
+ return true;
+}
+
+static void
+UnregisterGCCallbacks()
+{
+ MOZ_ASSERT(sCallbackIsRegistered);
+
+ // Remove tracing callback.
+ JSContext* cx = dom::danger::GetJSContext();
+ JS_RemoveExtraGCRootsTracer(cx, TraceJSObjWrappers, nullptr);
+
+ // Remove delayed destruction callback.
+ if (sCallbackIsRegistered) {
+ xpc::RemoveGCCallback(DelayedReleaseGCCallback);
+ sCallbackIsRegistered = false;
+ }
+}
+
+static bool
+CreateJSObjWrapperTable()
+{
+ MOZ_ASSERT(!sJSObjWrappersAccessible);
+ MOZ_ASSERT(!sJSObjWrappers.initialized());
+
+ if (!RegisterGCCallbacks()) {
+ return false;
+ }
+
+ if (!sJSObjWrappers.init(16)) {
+ NS_ERROR("Error initializing PLDHashTable sJSObjWrappers!");
+ return false;
+ }
+
+ sJSObjWrappersAccessible = true;
+ return true;
+}
+
+static void
+DestroyJSObjWrapperTable()
+{
+ MOZ_ASSERT(sJSObjWrappersAccessible);
+ MOZ_ASSERT(sJSObjWrappers.initialized());
+ MOZ_ASSERT(sJSObjWrappers.count() == 0);
+
+ // No more wrappers, and our hash was initialized. Finish the
+ // hash to prevent leaking it.
+ sJSObjWrappers.finish();
+ sJSObjWrappersAccessible = false;
+}
+
+static bool
+CreateNPObjWrapperTable()
+{
+ MOZ_ASSERT(!sNPObjWrappers);
+
+ if (!RegisterGCCallbacks()) {
+ return false;
+ }
+
+ sNPObjWrappers =
+ new PLDHashTable(PLDHashTable::StubOps(), sizeof(NPObjWrapperHashEntry));
+ return true;
+}
+
+static void
+DestroyNPObjWrapperTable()
+{
+ MOZ_ASSERT(sNPObjWrappers->EntryCount() == 0);
+
+ delete sNPObjWrappers;
+ sNPObjWrappers = nullptr;
+}
+
+static void
+OnWrapperCreated()
+{
+ ++sWrapperCount;
+}
+
+static void
+OnWrapperDestroyed()
+{
+ NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
+
+ if (--sWrapperCount == 0) {
+ if (sJSObjWrappersAccessible) {
+ DestroyJSObjWrapperTable();
+ }
+
+ if (sNPObjWrappers) {
+ // No more wrappers, and our hash was initialized. Finish the
+ // hash to prevent leaking it.
+ DestroyNPObjWrapperTable();
+ }
+
+ UnregisterGCCallbacks();
+ }
+}
+
+namespace mozilla {
+namespace plugins {
+namespace parent {
+
+static nsIGlobalObject*
+GetGlobalObject(NPP npp)
+{
+ NS_ENSURE_TRUE(npp, nullptr);
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
+ NS_ENSURE_TRUE(inst, nullptr);
+
+ RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
+ NS_ENSURE_TRUE(owner, nullptr);
+
+ nsCOMPtr<nsIDocument> doc;
+ owner->GetDocument(getter_AddRefs(doc));
+ NS_ENSURE_TRUE(doc, nullptr);
+
+ return doc->GetScopeObject();
+}
+
+} // namespace parent
+} // namespace plugins
+} // namespace mozilla
+
+static NPP
+LookupNPP(NPObject *npobj);
+
+
+static JS::Value
+NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant)
+{
+ switch (variant->type) {
+ case NPVariantType_Void :
+ return JS::UndefinedValue();
+ case NPVariantType_Null :
+ return JS::NullValue();
+ case NPVariantType_Bool :
+ return JS::BooleanValue(NPVARIANT_TO_BOOLEAN(*variant));
+ case NPVariantType_Int32 :
+ {
+ // Don't use INT_TO_JSVAL directly to prevent bugs when dealing
+ // with ints larger than what fits in a integer JS::Value.
+ return ::JS_NumberValue(NPVARIANT_TO_INT32(*variant));
+ }
+ case NPVariantType_Double :
+ {
+ return ::JS_NumberValue(NPVARIANT_TO_DOUBLE(*variant));
+ }
+ case NPVariantType_String :
+ {
+ const NPString *s = &NPVARIANT_TO_STRING(*variant);
+ NS_ConvertUTF8toUTF16 utf16String(s->UTF8Characters, s->UTF8Length);
+
+ JSString *str =
+ ::JS_NewUCStringCopyN(cx, utf16String.get(), utf16String.Length());
+
+ if (str) {
+ return JS::StringValue(str);
+ }
+
+ break;
+ }
+ case NPVariantType_Object:
+ {
+ if (npp) {
+ JSObject *obj =
+ nsNPObjWrapper::GetNewOrUsed(npp, cx, NPVARIANT_TO_OBJECT(*variant));
+
+ if (obj) {
+ return JS::ObjectValue(*obj);
+ }
+ }
+
+ NS_ERROR("Error wrapping NPObject!");
+
+ break;
+ }
+ default:
+ NS_ERROR("Unknown NPVariant type!");
+ }
+
+ NS_ERROR("Unable to convert NPVariant to jsval!");
+
+ return JS::UndefinedValue();
+}
+
+bool
+JSValToNPVariant(NPP npp, JSContext *cx, const JS::Value& val, NPVariant *variant)
+{
+ NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
+
+ if (val.isPrimitive()) {
+ if (val.isUndefined()) {
+ VOID_TO_NPVARIANT(*variant);
+ } else if (val.isNull()) {
+ NULL_TO_NPVARIANT(*variant);
+ } else if (val.isBoolean()) {
+ BOOLEAN_TO_NPVARIANT(val.toBoolean(), *variant);
+ } else if (val.isInt32()) {
+ INT32_TO_NPVARIANT(val.toInt32(), *variant);
+ } else if (val.isDouble()) {
+ double d = val.toDouble();
+ int i;
+ if (JS_DoubleIsInt32(d, &i)) {
+ INT32_TO_NPVARIANT(i, *variant);
+ } else {
+ DOUBLE_TO_NPVARIANT(d, *variant);
+ }
+ } else if (val.isString()) {
+ JSString *jsstr = val.toString();
+
+ nsAutoJSString str;
+ if (!str.init(cx, jsstr)) {
+ return false;
+ }
+
+ uint32_t len;
+ char *p = ToNewUTF8String(str, &len);
+
+ if (!p) {
+ return false;
+ }
+
+ STRINGN_TO_NPVARIANT(p, len, *variant);
+ } else {
+ NS_ERROR("Unknown primitive type!");
+
+ return false;
+ }
+
+ return true;
+ }
+
+ // The reflected plugin object may be in another compartment if the plugin
+ // element has since been adopted into a new document. We don't bother
+ // transplanting the plugin objects, and just do a unwrap with security
+ // checks if we encounter one of them as an argument. If the unwrap fails,
+ // we run with the original wrapped object, since sometimes there are
+ // legitimate cases where a security wrapper ends up here (for example,
+ // Location objects, which are _always_ behind security wrappers).
+ JS::Rooted<JSObject*> obj(cx, val.toObjectOrNull());
+ obj = js::CheckedUnwrap(obj);
+ if (!obj) {
+ obj = val.toObjectOrNull();
+ }
+
+ NPObject* npobj = nsJSObjWrapper::GetNewOrUsed(npp, obj);
+ if (!npobj) {
+ return false;
+ }
+
+ // Pass over ownership of npobj to *variant
+ OBJECT_TO_NPVARIANT(npobj, *variant);
+
+ return true;
+}
+
+static void
+ThrowJSExceptionASCII(JSContext *cx, const char *message)
+{
+ const char *ex = PeekException();
+
+ if (ex) {
+ nsAutoString ucex;
+
+ if (message) {
+ AppendASCIItoUTF16(message, ucex);
+
+ AppendASCIItoUTF16(" [plugin exception: ", ucex);
+ }
+
+ AppendUTF8toUTF16(ex, ucex);
+
+ if (message) {
+ AppendASCIItoUTF16("].", ucex);
+ }
+
+ JSString *str = ::JS_NewUCStringCopyN(cx, ucex.get(), ucex.Length());
+
+ if (str) {
+ JS::Rooted<JS::Value> exn(cx, JS::StringValue(str));
+ ::JS_SetPendingException(cx, exn);
+ }
+
+ PopException();
+ } else {
+ ::JS_ReportErrorASCII(cx, "%s", message);
+ }
+}
+
+static bool
+ReportExceptionIfPending(JSContext *cx)
+{
+ const char *ex = PeekException();
+
+ if (!ex) {
+ return true;
+ }
+
+ ThrowJSExceptionASCII(cx, nullptr);
+
+ return false;
+}
+
+nsJSObjWrapper::nsJSObjWrapper(NPP npp)
+ : mJSObj(nullptr), mNpp(npp), mDestroyPending(false)
+{
+ MOZ_COUNT_CTOR(nsJSObjWrapper);
+ OnWrapperCreated();
+}
+
+nsJSObjWrapper::~nsJSObjWrapper()
+{
+ MOZ_COUNT_DTOR(nsJSObjWrapper);
+
+ // Invalidate first, since it relies on sJSObjWrappers.
+ NP_Invalidate(this);
+
+ OnWrapperDestroyed();
+}
+
+// static
+NPObject *
+nsJSObjWrapper::NP_Allocate(NPP npp, NPClass *aClass)
+{
+ NS_ASSERTION(aClass == &sJSObjWrapperNPClass,
+ "Huh, wrong class passed to NP_Allocate()!!!");
+
+ return new nsJSObjWrapper(npp);
+}
+
+// static
+void
+nsJSObjWrapper::NP_Deallocate(NPObject *npobj)
+{
+ // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate().
+ delete (nsJSObjWrapper *)npobj;
+}
+
+// static
+void
+nsJSObjWrapper::NP_Invalidate(NPObject *npobj)
+{
+ nsJSObjWrapper *jsnpobj = (nsJSObjWrapper *)npobj;
+
+ if (jsnpobj && jsnpobj->mJSObj) {
+
+ if (sJSObjWrappersAccessible) {
+ // Remove the wrapper from the hash
+ nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
+ JSObjWrapperTable::Ptr ptr = sJSObjWrappers.lookup(key);
+ MOZ_ASSERT(ptr.found());
+ sJSObjWrappers.remove(ptr);
+ }
+
+ // Forget our reference to the JSObject.
+ jsnpobj->mJSObj = nullptr;
+ }
+}
+
+static bool
+GetProperty(JSContext *cx, JSObject *objArg, NPIdentifier npid, JS::MutableHandle<JS::Value> rval)
+{
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<JSObject *> obj(cx, objArg);
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ return ::JS_GetPropertyById(cx, obj, id, rval);
+}
+
+// static
+bool
+nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id)
+{
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI HasMethod");
+ JSContext *cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx,
+ "Null npobj in nsJSObjWrapper::NP_HasMethod!");
+
+ return false;
+ }
+
+ nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+
+ JSAutoCompartment ac(cx, npjsobj->mJSObj);
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+
+ JS::Rooted<JS::Value> v(cx);
+ bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v);
+
+ return ok && !v.isPrimitive() &&
+ ::JS_ObjectIsFunction(cx, v.toObjectOrNull());
+}
+
+static bool
+doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args,
+ uint32_t argCount, bool ctorCall, NPVariant *result)
+{
+ NPP npp = NPPStack::Peek();
+
+ nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ // We're about to run script via JS_CallFunctionValue, so we need an
+ // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
+ dom::AutoEntryScript aes(globalObject, "NPAPI doInvoke");
+ JSContext *cx = aes.cx();
+
+ if (!npobj || !result) {
+ ThrowJSExceptionASCII(cx, "Null npobj, or result in doInvoke!");
+
+ return false;
+ }
+
+ // Initialize *result
+ VOID_TO_NPVARIANT(*result);
+
+ nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+
+ JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
+ JSAutoCompartment ac(cx, jsobj);
+ JS::Rooted<JS::Value> fv(cx);
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+
+ if (method != NPIdentifier_VOID) {
+ if (!GetProperty(cx, jsobj, method, &fv) ||
+ ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
+ return false;
+ }
+ } else {
+ fv.setObject(*jsobj);
+ }
+
+ // Convert args
+ JS::AutoValueVector jsargs(cx);
+ if (!jsargs.reserve(argCount)) {
+ ::JS_ReportOutOfMemory(cx);
+ return false;
+ }
+ for (uint32_t i = 0; i < argCount; ++i) {
+ jsargs.infallibleAppend(NPVariantToJSVal(npp, cx, args + i));
+ }
+
+ JS::Rooted<JS::Value> v(cx);
+ bool ok = false;
+
+ if (ctorCall) {
+ JSObject *newObj =
+ ::JS_New(cx, jsobj, jsargs);
+
+ if (newObj) {
+ v.setObject(*newObj);
+ ok = true;
+ }
+ } else {
+ ok = ::JS_CallFunctionValue(cx, jsobj, fv, jsargs, &v);
+ }
+
+ if (ok)
+ ok = JSValToNPVariant(npp, cx, v, result);
+
+ return ok;
+}
+
+// static
+bool
+nsJSObjWrapper::NP_Invoke(NPObject *npobj, NPIdentifier method,
+ const NPVariant *args, uint32_t argCount,
+ NPVariant *result)
+{
+ if (method == NPIdentifier_VOID) {
+ return false;
+ }
+
+ return doInvoke(npobj, method, args, argCount, false, result);
+}
+
+// static
+bool
+nsJSObjWrapper::NP_InvokeDefault(NPObject *npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result)
+{
+ return doInvoke(npobj, NPIdentifier_VOID, args, argCount, false,
+ result);
+}
+
+// static
+bool
+nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier npid)
+{
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI HasProperty");
+ JSContext *cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx,
+ "Null npobj in nsJSObjWrapper::NP_HasProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+ bool found, ok = false;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
+ JSAutoCompartment ac(cx, jsobj);
+
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ ok = ::JS_HasPropertyById(cx, jsobj, id, &found);
+ return ok && found;
+}
+
+// static
+bool
+nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier id,
+ NPVariant *result)
+{
+ NPP npp = NPPStack::Peek();
+
+ nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ // We're about to run script via JS_CallFunctionValue, so we need an
+ // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
+ dom::AutoEntryScript aes(globalObject, "NPAPI get");
+ JSContext *cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx,
+ "Null npobj in nsJSObjWrapper::NP_GetProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JSAutoCompartment ac(cx, npjsobj->mJSObj);
+
+ JS::Rooted<JS::Value> v(cx);
+ return (GetProperty(cx, npjsobj->mJSObj, id, &v) &&
+ JSValToNPVariant(npp, cx, v, result));
+}
+
+// static
+bool
+nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier npid,
+ const NPVariant *value)
+{
+ NPP npp = NPPStack::Peek();
+
+ nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ // We're about to run script via JS_CallFunctionValue, so we need an
+ // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
+ dom::AutoEntryScript aes(globalObject, "NPAPI set");
+ JSContext *cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx,
+ "Null npobj in nsJSObjWrapper::NP_SetProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+ bool ok = false;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj);
+ JSAutoCompartment ac(cx, jsObj);
+
+ JS::Rooted<JS::Value> v(cx, NPVariantToJSVal(npp, cx, value));
+
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ ok = ::JS_SetPropertyById(cx, jsObj, id, v);
+
+ return ok;
+}
+
+// static
+bool
+nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier npid)
+{
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI RemoveProperty");
+ JSContext *cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx,
+ "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::ObjectOpResult result;
+ JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj);
+ JSAutoCompartment ac(cx, obj);
+
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ if (!::JS_DeletePropertyById(cx, obj, id, result))
+ return false;
+
+ if (result) {
+ // FIXME: See bug 425823, we shouldn't need to do this, and once
+ // that bug is fixed we can remove this code.
+ bool hasProp;
+ if (!::JS_HasPropertyById(cx, obj, id, &hasProp))
+ return false;
+ if (!hasProp)
+ return true;
+
+ // The property might have been deleted, but it got
+ // re-resolved, so no, it's not really deleted.
+ result.failCantDelete();
+ }
+
+ return result.reportError(cx, obj, id);
+}
+
+//static
+bool
+nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray,
+ uint32_t *count)
+{
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI Enumerate");
+ JSContext *cx = aes.cx();
+
+ *idarray = 0;
+ *count = 0;
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx,
+ "Null npobj in nsJSObjWrapper::NP_Enumerate!");
+
+ return false;
+ }
+
+ nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
+ JSAutoCompartment ac(cx, jsobj);
+
+ JS::Rooted<JS::IdVector> ida(cx, JS::IdVector(cx));
+ if (!JS_Enumerate(cx, jsobj, &ida)) {
+ return false;
+ }
+
+ *count = ida.length();
+ *idarray = (NPIdentifier *)PR_Malloc(*count * sizeof(NPIdentifier));
+ if (!*idarray) {
+ ThrowJSExceptionASCII(cx, "Memory allocation failed for NPIdentifier!");
+ return false;
+ }
+
+ for (uint32_t i = 0; i < *count; i++) {
+ JS::Rooted<JS::Value> v(cx);
+ if (!JS_IdToValue(cx, ida[i], &v)) {
+ PR_Free(*idarray);
+ return false;
+ }
+
+ NPIdentifier id;
+ if (v.isString()) {
+ JS::Rooted<JSString*> str(cx, v.toString());
+ str = JS_AtomizeAndPinJSString(cx, str);
+ if (!str) {
+ PR_Free(*idarray);
+ return false;
+ }
+ id = StringToNPIdentifier(cx, str);
+ } else {
+ NS_ASSERTION(v.isInt32(),
+ "The element in ida must be either string or int!\n");
+ id = IntToNPIdentifier(v.toInt32());
+ }
+
+ (*idarray)[i] = id;
+ }
+
+ return true;
+}
+
+//static
+bool
+nsJSObjWrapper::NP_Construct(NPObject *npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result)
+{
+ return doInvoke(npobj, NPIdentifier_VOID, args, argCount, true, result);
+}
+
+// Look up or create an NPObject that wraps the JSObject obj.
+
+// static
+NPObject *
+nsJSObjWrapper::GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj)
+{
+ if (!npp) {
+ NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
+
+ return nullptr;
+ }
+
+ // If we're running out-of-process and initializing asynchronously, and if
+ // the plugin has been asked to destroy itself during initialization,
+ // don't return any new NPObjects.
+ nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(npp->ndata);
+ if (inst->GetPlugin()->GetLibrary()->IsOOP()) {
+ PluginAsyncSurrogate* surrogate = PluginAsyncSurrogate::Cast(npp);
+ if (surrogate && surrogate->IsDestroyPending()) {
+ return nullptr;
+ }
+ }
+
+ // No need to enter the right compartment here as we only get the
+ // class and private from the JSObject, neither of which cares about
+ // compartments.
+
+ if (nsNPObjWrapper::IsWrapper(obj)) {
+ // obj is one of our own, its private data is the NPObject we're
+ // looking for.
+
+ NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
+
+ // If the private is null, that means that the object has already been torn
+ // down, possible because the owning plugin was destroyed (there can be
+ // multiple plugins, so the fact that it was destroyed does not prevent one
+ // of its dead JS objects from being passed to another plugin). There's not
+ // much use in wrapping such a dead object, so we just return null, causing
+ // us to throw.
+ if (!npobj)
+ return nullptr;
+
+ if (LookupNPP(npobj) == npp)
+ return _retainobject(npobj);
+ }
+
+ if (!sJSObjWrappers.initialized()) {
+ // No hash yet (or any more), initialize it.
+ if (!CreateJSObjWrapperTable())
+ return nullptr;
+ }
+ MOZ_ASSERT(sJSObjWrappersAccessible);
+
+ JSObjWrapperTable::Ptr p = sJSObjWrappers.lookupForAdd(nsJSObjWrapperKey(obj, npp));
+ if (p) {
+ MOZ_ASSERT(p->value());
+ // Found a live nsJSObjWrapper, return it.
+
+ return _retainobject(p->value());
+ }
+
+ // No existing nsJSObjWrapper, create one.
+
+ nsJSObjWrapper *wrapper =
+ (nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass);
+
+ if (!wrapper) {
+ // Out of memory, entry not yet added to table.
+ return nullptr;
+ }
+
+ wrapper->mJSObj = obj;
+
+ // Insert the new wrapper into the hashtable, rooting the JSObject. Its
+ // lifetime is now tied to that of the NPObject.
+ if (!sJSObjWrappers.putNew(nsJSObjWrapperKey(obj, npp), wrapper)) {
+ // Out of memory, free the wrapper we created.
+ _releaseobject(wrapper);
+ return nullptr;
+ }
+
+ return wrapper;
+}
+
+// Climb the prototype chain, unwrapping as necessary until we find an NP object
+// wrapper.
+//
+// Because this function unwraps, its return value must be wrapped for the cx
+// compartment for callers that plan to hold onto the result or do anything
+// substantial with it.
+static JSObject *
+GetNPObjectWrapper(JSContext *cx, JS::Handle<JSObject*> aObj, bool wrapResult = true)
+{
+ JS::Rooted<JSObject*> obj(cx, aObj);
+ while (obj && (obj = js::CheckedUnwrap(obj))) {
+ if (nsNPObjWrapper::IsWrapper(obj)) {
+ if (wrapResult && !JS_WrapObject(cx, &obj)) {
+ return nullptr;
+ }
+ return obj;
+ }
+
+ JSAutoCompartment ac(cx, obj);
+ if (!::JS_GetPrototype(cx, obj, &obj)) {
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+static NPObject *
+GetNPObject(JSContext *cx, JS::Handle<JSObject*> aObj)
+{
+ JS::Rooted<JSObject*> obj(cx, aObj);
+ obj = GetNPObjectWrapper(cx, obj, /* wrapResult = */ false);
+ if (!obj) {
+ return nullptr;
+ }
+
+ return (NPObject *)::JS_GetPrivate(obj);
+}
+
+
+// Does not actually add a property because this is always followed by a
+// SetProperty call.
+static bool
+NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::Handle<JS::Value> v)
+{
+ NPObject *npobj = GetNPObject(cx, obj);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->hasMethod) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ if (NPObjectIsOutOfProcessProxy(npobj)) {
+ return true;
+ }
+
+ PluginDestructionGuard pdg(LookupNPP(npobj));
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+ bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ if (hasProperty)
+ return true;
+
+ // We must permit methods here since JS_DefineUCFunction() will add
+ // the function as a property
+ bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ if (!hasMethod) {
+ ThrowJSExceptionASCII(cx, "Trying to add unsupported property on NPObject!");
+
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::ObjectOpResult &result)
+{
+ NPObject *npobj = GetNPObject(cx, obj);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->removeProperty) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ PluginDestructionGuard pdg(LookupNPP(npobj));
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ if (!NPObjectIsOutOfProcessProxy(npobj)) {
+ bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ if (!hasProperty)
+ return result.succeed();
+ }
+
+ // This removeProperty hook may throw an exception and return false; or just
+ // return false without an exception pending, which behaves like `delete
+ // obj.prop` returning false: in strict mode it becomes a TypeError. Legacy
+ // code---nothing else that uses the JSAPI works this way anymore.
+ bool succeeded = npobj->_class->removeProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+ return succeeded ? result.succeed() : result.failCantDelete();
+}
+
+static bool
+NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result)
+{
+ NPObject *npobj = GetNPObject(cx, obj);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->setProperty) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ // Find out what plugin (NPP) is the owner of the object we're
+ // manipulating, and make it own any JSObject wrappers created here.
+ NPP npp = LookupNPP(npobj);
+
+ if (!npp) {
+ ThrowJSExceptionASCII(cx, "No NPP found for NPObject!");
+
+ return false;
+ }
+
+ PluginDestructionGuard pdg(npp);
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ if (!NPObjectIsOutOfProcessProxy(npobj)) {
+ bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ if (!hasProperty) {
+ ThrowJSExceptionASCII(cx, "Trying to set unsupported property on NPObject!");
+
+ return false;
+ }
+ }
+
+ NPVariant npv;
+ if (!JSValToNPVariant(npp, cx, vp, &npv)) {
+ ThrowJSExceptionASCII(cx, "Error converting jsval to NPVariant!");
+
+ return false;
+ }
+
+ bool ok = npobj->_class->setProperty(npobj, identifier, &npv);
+ _releasevariantvalue(&npv); // Release the variant
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ if (!ok) {
+ ThrowJSExceptionASCII(cx, "Error setting property on NPObject!");
+
+ return false;
+ }
+
+ return result.succeed();
+}
+
+static bool
+NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
+{
+ NPObject *npobj = GetNPObject(cx, obj);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->hasMethod || !npobj->_class->getProperty) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ if (JSID_IS_SYMBOL(id)) {
+ JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id));
+ if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) {
+ JS::RootedObject obj(cx, JS_GetFunctionObject(
+ JS_NewFunction(
+ cx, NPObjWrapper_toPrimitive, 1, 0,
+ "Symbol.toPrimitive")));
+ if (!obj)
+ return false;
+ vp.setObject(*obj);
+ return true;
+ }
+
+ if (JS::GetSymbolCode(sym) == JS::SymbolCode::toStringTag) {
+ JS::RootedString tag(cx, JS_NewStringCopyZ(cx, NPRUNTIME_JSCLASS_NAME));
+ if (!tag) {
+ return false;
+ }
+
+ vp.setString(tag);
+ return true;
+ }
+
+ vp.setUndefined();
+ return true;
+ }
+
+ // Find out what plugin (NPP) is the owner of the object we're
+ // manipulating, and make it own any JSObject wrappers created here.
+ NPP npp = LookupNPP(npobj);
+ if (!npp) {
+ ThrowJSExceptionASCII(cx, "No NPP found for NPObject!");
+
+ return false;
+ }
+
+ PluginDestructionGuard pdg(npp);
+
+ bool hasProperty, hasMethod;
+
+ NPVariant npv;
+ VOID_TO_NPVARIANT(npv);
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ if (NPObjectIsOutOfProcessProxy(npobj)) {
+ PluginScriptableObjectParent* actor = nullptr;
+ PluginAsyncSurrogate* surrogate = nullptr;
+ CastNPObject(npobj, actor, surrogate);
+
+ // actor and surrogate may be null if the plugin crashed.
+ if (!actor && !surrogate)
+ return false;
+
+ bool success = false;
+ if (surrogate) {
+ success = surrogate->GetPropertyHelper(npobj, identifier, &hasProperty,
+ &hasMethod, &npv);
+ } else if (actor) {
+ success = actor->GetPropertyHelper(identifier, &hasProperty, &hasMethod,
+ &npv);
+ }
+
+ if (!ReportExceptionIfPending(cx)) {
+ if (success)
+ _releasevariantvalue(&npv);
+ return false;
+ }
+
+ if (success) {
+ // We return NPObject Member class here to support ambiguous members.
+ if (hasProperty && hasMethod)
+ return CreateNPObjectMember(npp, cx, obj, npobj, id, &npv, vp);
+
+ if (hasProperty) {
+ vp.set(NPVariantToJSVal(npp, cx, &npv));
+ _releasevariantvalue(&npv);
+
+ if (!ReportExceptionIfPending(cx))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ hasMethod = npobj->_class->hasMethod(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ // We return NPObject Member class here to support ambiguous members.
+ if (hasProperty && hasMethod)
+ return CreateNPObjectMember(npp, cx, obj, npobj, id, nullptr, vp);
+
+ if (hasProperty) {
+ if (npobj->_class->getProperty(npobj, identifier, &npv))
+ vp.set(NPVariantToJSVal(npp, cx, &npv));
+
+ _releasevariantvalue(&npv);
+
+ if (!ReportExceptionIfPending(cx))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+CallNPMethodInternal(JSContext *cx, JS::Handle<JSObject*> obj, unsigned argc,
+ JS::Value *argv, JS::Value *rval, bool ctorCall)
+{
+ NPObject *npobj = GetNPObject(cx, obj);
+
+ if (!npobj || !npobj->_class) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ // Find out what plugin (NPP) is the owner of the object we're
+ // manipulating, and make it own any JSObject wrappers created here.
+ NPP npp = LookupNPP(npobj);
+
+ if (!npp) {
+ ThrowJSExceptionASCII(cx, "Error finding NPP for NPObject!");
+
+ return false;
+ }
+
+ PluginDestructionGuard pdg(npp);
+
+ NPVariant npargs_buf[8];
+ NPVariant *npargs = npargs_buf;
+
+ if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
+ // Our stack buffer isn't large enough to hold all arguments,
+ // malloc a buffer.
+ npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
+
+ if (!npargs) {
+ ThrowJSExceptionASCII(cx, "Out of memory!");
+
+ return false;
+ }
+ }
+
+ // Convert arguments
+ uint32_t i;
+ for (i = 0; i < argc; ++i) {
+ if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) {
+ ThrowJSExceptionASCII(cx, "Error converting jsvals to NPVariants!");
+
+ if (npargs != npargs_buf) {
+ PR_Free(npargs);
+ }
+
+ return false;
+ }
+ }
+
+ NPVariant v;
+ VOID_TO_NPVARIANT(v);
+
+ JSObject *funobj = argv[-2].toObjectOrNull();
+ bool ok;
+ const char *msg = "Error calling method on NPObject!";
+
+ if (ctorCall) {
+ // construct a new NPObject based on the NPClass in npobj. Fail if
+ // no construct method is available.
+
+ if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) &&
+ npobj->_class->construct) {
+ ok = npobj->_class->construct(npobj, npargs, argc, &v);
+ } else {
+ ok = false;
+
+ msg = "Attempt to construct object from class with no constructor.";
+ }
+ } else if (funobj != obj) {
+ // A obj.function() style call is made, get the method name from
+ // the function object.
+
+ if (npobj->_class->invoke) {
+ JSFunction *fun = ::JS_GetObjectFunction(funobj);
+ JS::Rooted<JSString*> funId(cx, ::JS_GetFunctionId(fun));
+ JSString *name = ::JS_AtomizeAndPinJSString(cx, funId);
+ NPIdentifier id = StringToNPIdentifier(cx, name);
+
+ ok = npobj->_class->invoke(npobj, id, npargs, argc, &v);
+ } else {
+ ok = false;
+
+ msg = "Attempt to call a method on object with no invoke method.";
+ }
+ } else {
+ if (npobj->_class->invokeDefault) {
+ // obj is a callable object that is being called, no method name
+ // available then. Invoke the default method.
+
+ ok = npobj->_class->invokeDefault(npobj, npargs, argc, &v);
+ } else {
+ ok = false;
+
+ msg = "Attempt to call a default method on object with no "
+ "invokeDefault method.";
+ }
+ }
+
+ // Release arguments.
+ for (i = 0; i < argc; ++i) {
+ _releasevariantvalue(npargs + i);
+ }
+
+ if (npargs != npargs_buf) {
+ PR_Free(npargs);
+ }
+
+ if (!ok) {
+ // ReportExceptionIfPending returns a return value, which is true
+ // if no exception was thrown. In that case, throw our own.
+ if (ReportExceptionIfPending(cx))
+ ThrowJSExceptionASCII(cx, msg);
+
+ return false;
+ }
+
+ *rval = NPVariantToJSVal(npp, cx, &v);
+
+ // *rval now owns the value, release our reference.
+ _releasevariantvalue(&v);
+
+ return ReportExceptionIfPending(cx);
+}
+
+static bool
+CallNPMethod(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
+ if (!obj)
+ return false;
+
+ return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
+}
+
+static bool
+NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj,
+ JS::AutoIdVector &properties, bool enumerableOnly)
+{
+ NPObject *npobj = GetNPObject(cx, obj);
+ if (!npobj || !npobj->_class) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+ return false;
+ }
+
+ PluginDestructionGuard pdg(LookupNPP(npobj));
+
+ if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
+ !npobj->_class->enumerate) {
+ return true;
+ }
+
+ NPIdentifier *identifiers;
+ uint32_t length;
+ if (!npobj->_class->enumerate(npobj, &identifiers, &length)) {
+ if (ReportExceptionIfPending(cx)) {
+ // ReportExceptionIfPending returns a return value, which is true
+ // if no exception was thrown. In that case, throw our own.
+ ThrowJSExceptionASCII(cx, "Error enumerating properties on scriptable "
+ "plugin object");
+ }
+ return false;
+ }
+
+ if (!properties.reserve(length))
+ return false;
+
+ JS::Rooted<jsid> id(cx);
+ for (uint32_t i = 0; i < length; i++) {
+ id = NPIdentifierToJSId(identifiers[i]);
+ properties.infallibleAppend(id);
+ }
+
+ PR_Free(identifiers);
+ return true;
+}
+
+static bool
+NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ bool *resolvedp)
+{
+ if (JSID_IS_SYMBOL(id))
+ return true;
+
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::JS);
+
+ NPObject *npobj = GetNPObject(cx, obj);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->hasMethod) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ PluginDestructionGuard pdg(LookupNPP(npobj));
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ if (hasProperty) {
+ NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
+ "id must be either string or int!\n");
+ if (!::JS_DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
+ JSPROP_ENUMERATE | JSPROP_SHARED)) {
+ return false;
+ }
+
+ *resolvedp = true;
+
+ return true;
+ }
+
+ bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
+ if (!ReportExceptionIfPending(cx))
+ return false;
+
+ if (hasMethod) {
+ NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
+ "id must be either string or int!\n");
+
+ JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallNPMethod, 0,
+ JSPROP_ENUMERATE);
+
+ *resolvedp = true;
+
+ return fnc != nullptr;
+ }
+
+ // no property or method
+ return true;
+}
+
+static void
+NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj)
+{
+ NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
+ if (npobj) {
+ if (sNPObjWrappers) {
+ sNPObjWrappers->Remove(npobj);
+ }
+ }
+
+ if (!sDelayedReleases)
+ sDelayedReleases = new nsTArray<NPObject*>;
+ sDelayedReleases->AppendElement(npobj);
+}
+
+static void
+NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old)
+{
+ // The wrapper JSObject has been moved, so we need to update the entry in the
+ // sNPObjWrappers hash table, if present.
+
+ if (!sNPObjWrappers) {
+ return;
+ }
+
+ NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
+ if (!npobj) {
+ return;
+ }
+
+ // Calling PLDHashTable::Search() will not result in GC.
+ JS::AutoSuppressGCAnalysis nogc;
+
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
+ MOZ_ASSERT(entry && entry->mJSObj);
+ MOZ_ASSERT(entry->mJSObj.unbarrieredGetPtr() == old);
+ entry->mJSObj = obj;
+}
+
+static bool
+NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> obj(cx, &args.callee());
+ return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
+}
+
+static bool
+NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> obj(cx, &args.callee());
+ return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, true);
+}
+
+static bool
+NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ // Plugins do not simply use the default OrdinaryToPrimitive behavior,
+ // because that behavior involves calling toString or valueOf on objects
+ // which weren't designed to accommodate this. Usually this wouldn't be a
+ // problem, because the absence of either property, or the presence of either
+ // property with a value that isn't callable, will cause that property to
+ // simply be ignored. But there is a problem in one specific case: Java,
+ // specifically java.lang.Integer. The Integer class has static valueOf
+ // methods, none of which are nullary, so the JS-reflected method will behave
+ // poorly when called with no arguments. We work around this problem by
+ // giving plugins a [Symbol.toPrimitive]() method which uses only toString
+ // and not valueOf.
+
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedValue thisv(cx, args.thisv());
+ if (thisv.isPrimitive())
+ return true;
+
+ JS::RootedObject obj(cx, &thisv.toObject());
+ JS::RootedValue v(cx);
+ if (!JS_GetProperty(cx, obj, "toString", &v))
+ return false;
+ if (v.isObject() && JS::IsCallable(&v.toObject())) {
+ if (!JS_CallFunctionValue(cx, obj, v, JS::HandleValueArray::empty(), args.rval()))
+ return false;
+ if (args.rval().isPrimitive())
+ return true;
+ }
+
+ JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
+ JSMSG_CANT_CONVERT_TO,
+ JS_GetClass(obj)->name, "primitive type");
+ return false;
+}
+
+bool
+nsNPObjWrapper::IsWrapper(JSObject *obj)
+{
+ return js::GetObjectClass(obj) == &sNPObjectJSWrapperClass;
+}
+
+// An NPObject is going away, make sure we null out the JS object's
+// private data in case this is an NPObject that came from a plugin
+// and it's destroyed prematurely.
+
+// static
+void
+nsNPObjWrapper::OnDestroy(NPObject *npobj)
+{
+ if (!npobj) {
+ return;
+ }
+
+ if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
+ // npobj is one of our own, no private data to clean up here.
+
+ return;
+ }
+
+ if (!sNPObjWrappers) {
+ // No hash yet (or any more), no used wrappers available.
+
+ return;
+ }
+
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
+
+ if (entry && entry->mJSObj) {
+ // Found a live NPObject wrapper, null out its JSObjects' private
+ // data.
+
+ ::JS_SetPrivate(entry->mJSObj, nullptr);
+
+ // Remove the npobj from the hash now that it went away.
+ sNPObjWrappers->RawRemove(entry);
+
+ // The finalize hook will call OnWrapperDestroyed().
+ }
+}
+
+// Look up or create a JSObject that wraps the NPObject npobj.
+
+// static
+JSObject *
+nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj)
+{
+ if (!npobj) {
+ NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!");
+
+ return nullptr;
+ }
+
+ if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
+ // npobj is one of our own, return its existing JSObject.
+
+ JS::Rooted<JSObject*> obj(cx, ((nsJSObjWrapper *)npobj)->mJSObj);
+ if (!JS_WrapObject(cx, &obj)) {
+ return nullptr;
+ }
+ return obj;
+ }
+
+ if (!npp) {
+ NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
+
+ return nullptr;
+ }
+
+ if (!sNPObjWrappers) {
+ // No hash yet (or any more), initialize it.
+ if (!CreateNPObjWrapperTable()) {
+ return nullptr;
+ }
+ }
+
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Add(npobj, fallible));
+
+ if (!entry) {
+ // Out of memory
+ JS_ReportOutOfMemory(cx);
+
+ return nullptr;
+ }
+
+ if (entry->mJSObj) {
+ // Found a live NPObject wrapper. It may not be in the same compartment
+ // as cx, so we need to wrap it before returning it.
+ JS::Rooted<JSObject*> obj(cx, entry->mJSObj);
+ if (!JS_WrapObject(cx, &obj)) {
+ return nullptr;
+ }
+ return obj;
+ }
+
+ entry->mNPObj = npobj;
+ entry->mNpp = npp;
+
+ uint32_t generation = sNPObjWrappers->Generation();
+
+ // No existing JSObject, create one.
+
+ JS::Rooted<JSObject*> obj(cx, ::JS_NewObject(cx, js::Jsvalify(&sNPObjectJSWrapperClass)));
+
+ if (generation != sNPObjWrappers->Generation()) {
+ // Reload entry if the JS_NewObject call caused a GC and reallocated
+ // the table (see bug 445229). This is guaranteed to succeed.
+
+ entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
+ NS_ASSERTION(entry, "Hashtable didn't find what we just added?");
+ }
+
+ if (!obj) {
+ // OOM? Remove the stale entry from the hash.
+
+ sNPObjWrappers->RawRemove(entry);
+
+ return nullptr;
+ }
+
+ OnWrapperCreated();
+
+ entry->mJSObj = obj;
+
+ ::JS_SetPrivate(obj, npobj);
+
+ // The new JSObject now holds on to npobj
+ _retainobject(npobj);
+
+ return obj;
+}
+
+// static
+void
+nsJSNPRuntime::OnPluginDestroy(NPP npp)
+{
+ if (sJSObjWrappersAccessible) {
+
+ // Prevent modification of sJSObjWrappers table if we go reentrant.
+ sJSObjWrappersAccessible = false;
+
+ for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) {
+ nsJSObjWrapper *npobj = e.front().value();
+ MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
+ if (npobj->mNpp == npp) {
+ if (npobj->_class && npobj->_class->invalidate) {
+ npobj->_class->invalidate(npobj);
+ }
+
+ _releaseobject(npobj);
+
+ e.removeFront();
+ }
+ }
+
+ sJSObjWrappersAccessible = true;
+ }
+
+ if (sNPObjWrappers) {
+ for (auto i = sNPObjWrappers->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<NPObjWrapperHashEntry*>(i.Get());
+
+ if (entry->mNpp == npp) {
+ // HACK: temporarily hide the table we're enumerating so that
+ // invalidate() and deallocate() don't touch it.
+ PLDHashTable *tmp = sNPObjWrappers;
+ sNPObjWrappers = nullptr;
+
+ NPObject *npobj = entry->mNPObj;
+
+ if (npobj->_class && npobj->_class->invalidate) {
+ npobj->_class->invalidate(npobj);
+ }
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ {
+ int32_t refCnt = npobj->referenceCount;
+ while (refCnt) {
+ --refCnt;
+ NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
+ }
+ }
+#endif
+
+ // Force deallocation of plugin objects since the plugin they came
+ // from is being torn down.
+ if (npobj->_class && npobj->_class->deallocate) {
+ npobj->_class->deallocate(npobj);
+ } else {
+ PR_Free(npobj);
+ }
+
+ ::JS_SetPrivate(entry->mJSObj, nullptr);
+
+ sNPObjWrappers = tmp;
+
+ if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) {
+ OnWrapperDestroyed();
+ }
+
+ i.Remove();
+ }
+ }
+ }
+}
+
+// static
+void
+nsJSNPRuntime::OnPluginDestroyPending(NPP npp)
+{
+ if (sJSObjWrappersAccessible) {
+ // Prevent modification of sJSObjWrappers table if we go reentrant.
+ sJSObjWrappersAccessible = false;
+ for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) {
+ nsJSObjWrapper *npobj = e.front().value();
+ MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
+ if (npobj->mNpp == npp) {
+ npobj->mDestroyPending = true;
+ }
+ }
+ sJSObjWrappersAccessible = true;
+ }
+}
+
+// Find the NPP for a NPObject.
+static NPP
+LookupNPP(NPObject *npobj)
+{
+ if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
+ nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
+ return o->mNpp;
+ }
+
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Add(npobj, fallible));
+
+ if (!entry) {
+ return nullptr;
+ }
+
+ NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
+
+ return entry->mNpp;
+}
+
+static bool
+CreateNPObjectMember(NPP npp, JSContext *cx,
+ JS::Handle<JSObject*> aObj, NPObject* npobj,
+ JS::Handle<jsid> id, NPVariant* getPropertyResult,
+ JS::MutableHandle<JS::Value> vp)
+{
+ if (!npobj || !npobj->_class || !npobj->_class->getProperty ||
+ !npobj->_class->invoke) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject");
+
+ return false;
+ }
+
+ NPObjectMemberPrivate *memberPrivate =
+ (NPObjectMemberPrivate *)PR_Malloc(sizeof(NPObjectMemberPrivate));
+ if (!memberPrivate)
+ return false;
+
+ // Make sure to clear all members in case something fails here
+ // during initialization.
+ memset(memberPrivate, 0, sizeof(NPObjectMemberPrivate));
+
+ JS::Rooted<JSObject*> obj(cx, aObj);
+
+ JSObject *memobj = ::JS_NewObject(cx, &sNPObjectMemberClass);
+ if (!memobj) {
+ PR_Free(memberPrivate);
+ return false;
+ }
+
+ vp.setObject(*memobj);
+
+ ::JS_SetPrivate(memobj, (void *)memberPrivate);
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ JS::Rooted<JS::Value> fieldValue(cx);
+ NPVariant npv;
+
+ if (getPropertyResult) {
+ // Plugin has already handed us the value we want here.
+ npv = *getPropertyResult;
+ }
+ else {
+ VOID_TO_NPVARIANT(npv);
+
+ NPBool hasProperty = npobj->_class->getProperty(npobj, identifier,
+ &npv);
+ if (!ReportExceptionIfPending(cx) || !hasProperty) {
+ return false;
+ }
+ }
+
+ fieldValue = NPVariantToJSVal(npp, cx, &npv);
+
+ // npobjWrapper is the JSObject through which we make sure we don't
+ // outlive the underlying NPObject, so make sure it points to the
+ // real JSObject wrapper for the NPObject.
+ obj = GetNPObjectWrapper(cx, obj);
+
+ memberPrivate->npobjWrapper = obj;
+
+ memberPrivate->fieldValue = fieldValue;
+ memberPrivate->methodName = id;
+ memberPrivate->npp = npp;
+
+ return true;
+}
+
+static bool
+NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
+ JS::MutableHandleValue vp)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ if (JSID_IS_SYMBOL(id)) {
+ JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id));
+ if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) {
+ JS::RootedObject obj(cx, JS_GetFunctionObject(
+ JS_NewFunction(
+ cx, NPObjectMember_toPrimitive, 1, 0,
+ "Symbol.toPrimitive")));
+ if (!obj)
+ return false;
+ vp.setObject(*obj);
+ return true;
+ }
+ }
+
+ return true;
+}
+
+static void
+NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj)
+{
+ NPObjectMemberPrivate *memberPrivate;
+
+ memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
+ if (!memberPrivate)
+ return;
+
+ PR_Free(memberPrivate);
+}
+
+static bool
+NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> memobj(cx, &args.callee());
+ NS_ENSURE_TRUE(memobj, false);
+
+ NPObjectMemberPrivate *memberPrivate =
+ (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
+ &sNPObjectMemberClass,
+ &args);
+ if (!memberPrivate || !memberPrivate->npobjWrapper)
+ return false;
+
+ JS::Rooted<JSObject*> objWrapper(cx, memberPrivate->npobjWrapper);
+ NPObject *npobj = GetNPObject(cx, objWrapper);
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx, "Call on invalid member object");
+
+ return false;
+ }
+
+ NPVariant npargs_buf[8];
+ NPVariant *npargs = npargs_buf;
+
+ if (args.length() > (sizeof(npargs_buf) / sizeof(NPVariant))) {
+ // Our stack buffer isn't large enough to hold all arguments,
+ // malloc a buffer.
+ npargs = (NPVariant *)PR_Malloc(args.length() * sizeof(NPVariant));
+
+ if (!npargs) {
+ ThrowJSExceptionASCII(cx, "Out of memory!");
+
+ return false;
+ }
+ }
+
+ // Convert arguments
+ for (uint32_t i = 0; i < args.length(); ++i) {
+ if (!JSValToNPVariant(memberPrivate->npp, cx, args[i], npargs + i)) {
+ ThrowJSExceptionASCII(cx, "Error converting jsvals to NPVariants!");
+
+ if (npargs != npargs_buf) {
+ PR_Free(npargs);
+ }
+
+ return false;
+ }
+ }
+
+
+ NPVariant npv;
+ bool ok = npobj->_class->invoke(npobj,
+ JSIdToNPIdentifier(memberPrivate->methodName),
+ npargs, args.length(), &npv);
+
+ // Release arguments.
+ for (uint32_t i = 0; i < args.length(); ++i) {
+ _releasevariantvalue(npargs + i);
+ }
+
+ if (npargs != npargs_buf) {
+ PR_Free(npargs);
+ }
+
+ if (!ok) {
+ // ReportExceptionIfPending returns a return value, which is true
+ // if no exception was thrown. In that case, throw our own.
+ if (ReportExceptionIfPending(cx))
+ ThrowJSExceptionASCII(cx, "Error calling method on NPObject!");
+
+ return false;
+ }
+
+ args.rval().set(NPVariantToJSVal(memberPrivate->npp, cx, &npv));
+
+ // *vp now owns the value, release our reference.
+ _releasevariantvalue(&npv);
+
+ return ReportExceptionIfPending(cx);
+}
+
+static void
+NPObjectMember_Trace(JSTracer *trc, JSObject *obj)
+{
+ NPObjectMemberPrivate *memberPrivate =
+ (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
+ if (!memberPrivate)
+ return;
+
+ // Our NPIdentifier is not always interned, so we must trace it.
+ JS::TraceEdge(trc, &memberPrivate->methodName, "NPObjectMemberPrivate.methodName");
+
+ JS::TraceEdge(trc, &memberPrivate->fieldValue, "NPObject Member => fieldValue");
+
+ // There's no strong reference from our private data to the
+ // NPObject, so make sure to mark the NPObject wrapper to keep the
+ // NPObject alive as long as this NPObjectMember is alive.
+ JS::TraceEdge(trc, &memberPrivate->npobjWrapper,
+ "NPObject Member => npobjWrapper");
+}
+
+static bool
+NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedValue thisv(cx, args.thisv());
+ if (thisv.isPrimitive()) {
+ args.rval().set(thisv);
+ return true;
+ }
+
+ JS::RootedObject obj(cx, &thisv.toObject());
+ NPObjectMemberPrivate *memberPrivate =
+ (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
+ &sNPObjectMemberClass,
+ &args);
+ if (!memberPrivate)
+ return false;
+
+ JSType hint;
+ if (!JS::GetFirstArgumentAsTypeHint(cx, args, &hint))
+ return false;
+
+ args.rval().set(memberPrivate->fieldValue);
+ if (args.rval().isObject()) {
+ JS::Rooted<JSObject*> objVal(cx, &args.rval().toObject());
+ return JS::ToPrimitive(cx, objVal, hint, args.rval());
+ }
+ return true;
+}
+
+// static
+bool
+nsJSObjWrapper::HasOwnProperty(NPObject *npobj, NPIdentifier npid)
+{
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI HasOwnProperty");
+ JSContext *cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx,
+ "Null npobj in nsJSObjWrapper::NP_HasOwnProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
+ bool found, ok = false;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
+ JSAutoCompartment ac(cx, jsobj);
+
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ ok = ::JS_AlreadyHasOwnPropertyById(cx, jsobj, id, &found);
+ return ok && found;
+}
diff --git a/dom/plugins/base/nsJSNPRuntime.h b/dom/plugins/base/nsJSNPRuntime.h
new file mode 100644
index 000000000..871988e06
--- /dev/null
+++ b/dom/plugins/base/nsJSNPRuntime.h
@@ -0,0 +1,108 @@
+/* -*- 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/. */
+
+#ifndef nsJSNPRuntime_h_
+#define nsJSNPRuntime_h_
+
+#include "nscore.h"
+#include "npapi.h"
+#include "npruntime.h"
+#include "PLDHashTable.h"
+#include "js/RootingAPI.h"
+
+class nsJSNPRuntime
+{
+public:
+ static void OnPluginDestroy(NPP npp);
+ static void OnPluginDestroyPending(NPP npp);
+};
+
+class nsJSObjWrapperKey
+{
+public:
+ nsJSObjWrapperKey(JSObject *obj, NPP npp)
+ : mJSObj(obj), mNpp(npp)
+ {
+ }
+
+ bool operator==(const nsJSObjWrapperKey& other) const {
+ return mJSObj == other.mJSObj && mNpp == other.mNpp;
+ }
+ bool operator!=(const nsJSObjWrapperKey& other) const {
+ return !(*this == other);
+ }
+
+ void trace(JSTracer* trc) {
+ JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapperKey");
+ }
+
+ nsJSObjWrapperKey(const nsJSObjWrapperKey& other)
+ : mJSObj(other.mJSObj),
+ mNpp(other.mNpp)
+ {}
+ void operator=(const nsJSObjWrapperKey& other) {
+ mJSObj = other.mJSObj;
+ mNpp = other.mNpp;
+ }
+
+ JS::Heap<JSObject*> mJSObj;
+ NPP mNpp;
+};
+
+class nsJSObjWrapper : public NPObject
+{
+public:
+ JS::Heap<JSObject *> mJSObj;
+ const NPP mNpp;
+ bool mDestroyPending;
+
+ static NPObject* GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj);
+ static bool HasOwnProperty(NPObject* npobj, NPIdentifier npid);
+
+ void trace(JSTracer* trc) {
+ JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapper");
+ }
+
+protected:
+ explicit nsJSObjWrapper(NPP npp);
+ ~nsJSObjWrapper();
+
+ static NPObject * NP_Allocate(NPP npp, NPClass *aClass);
+ static void NP_Deallocate(NPObject *obj);
+ static void NP_Invalidate(NPObject *obj);
+ static bool NP_HasMethod(NPObject *, NPIdentifier identifier);
+ static bool NP_Invoke(NPObject *obj, NPIdentifier method,
+ const NPVariant *args, uint32_t argCount,
+ NPVariant *result);
+ static bool NP_InvokeDefault(NPObject *obj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+ static bool NP_HasProperty(NPObject * obj, NPIdentifier property);
+ static bool NP_GetProperty(NPObject *obj, NPIdentifier property,
+ NPVariant *result);
+ static bool NP_SetProperty(NPObject *obj, NPIdentifier property,
+ const NPVariant *value);
+ static bool NP_RemoveProperty(NPObject *obj, NPIdentifier property);
+ static bool NP_Enumerate(NPObject *npobj, NPIdentifier **identifier,
+ uint32_t *count);
+ static bool NP_Construct(NPObject *obj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+
+public:
+ static NPClass sJSObjWrapperNPClass;
+};
+
+class nsNPObjWrapper
+{
+public:
+ static bool IsWrapper(JSObject *obj);
+ static void OnDestroy(NPObject *npobj);
+ static JSObject *GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj);
+};
+
+bool
+JSValToNPVariant(NPP npp, JSContext *cx, const JS::Value& val, NPVariant *variant);
+
+
+#endif // nsJSNPRuntime_h_
diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp
new file mode 100644
index 000000000..1bea269cd
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -0,0 +1,2819 @@
+/* -*- 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 "base/basictypes.h"
+
+/* This must occur *after* layers/PLayerTransaction.h to avoid typedefs conflicts. */
+#include "mozilla/ArrayUtils.h"
+
+#include "pratom.h"
+#include "prmem.h"
+#include "prenv.h"
+#include "prclist.h"
+
+#include "jsfriendapi.h"
+
+#include "nsPluginHost.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsPluginStreamListenerPeer.h"
+#include "nsIServiceManager.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsPluginInstanceOwner.h"
+
+#include "nsPluginsDir.h"
+#include "nsPluginLogging.h"
+
+#include "nsIDOMElement.h"
+#include "nsPIDOMWindow.h"
+#include "nsGlobalWindow.h"
+#include "nsIDocument.h"
+#include "nsIContent.h"
+#include "nsIIDNService.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsDOMJSUtils.h"
+#include "nsIPrincipal.h"
+#include "nsWildCard.h"
+#include "nsContentUtils.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsIXULRuntime.h"
+#include "nsIXPConnect.h"
+
+#include "nsIObserverService.h"
+#include <prinrval.h>
+
+#ifdef MOZ_WIDGET_COCOA
+#include <Carbon/Carbon.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <OpenGL/OpenGL.h>
+#include "nsCocoaFeatures.h"
+#include "PluginUtilsOSX.h"
+#endif
+
+// needed for nppdf plugin
+#if (MOZ_WIDGET_GTK)
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#if (MOZ_WIDGET_GTK == 2)
+#include "gtk2xtbin.h"
+#endif
+#endif
+
+#include "nsJSUtils.h"
+#include "nsJSNPRuntime.h"
+#include "nsIHttpAuthManager.h"
+#include "nsICookieService.h"
+#include "nsILoadContext.h"
+#include "nsIDocShell.h"
+
+#include "nsNetUtil.h"
+#include "nsNetCID.h"
+
+#include "mozilla/Mutex.h"
+#include "mozilla/PluginLibrary.h"
+using mozilla::PluginLibrary;
+
+#include "mozilla/PluginPRLibrary.h"
+using mozilla::PluginPRLibrary;
+
+#include "mozilla/plugins/PluginModuleParent.h"
+using mozilla::plugins::PluginModuleChromeParent;
+using mozilla::plugins::PluginModuleContentParent;
+
+#ifdef MOZ_X11
+#include "mozilla/X11Util.h"
+#endif
+
+#ifdef XP_WIN
+#include <windows.h>
+#include "mozilla/WindowsVersion.h"
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/Compatibility.h"
+#endif
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include <android/log.h>
+#include "android_npapi.h"
+#include "ANPBase.h"
+#include "GeneratedJNIWrappers.h"
+#undef LOG
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#endif
+
+#include "nsIAudioChannelAgent.h"
+#include "AudioChannelService.h"
+
+using namespace mozilla;
+using namespace mozilla::plugins::parent;
+
+// We should make this const...
+static NPNetscapeFuncs sBrowserFuncs = {
+ sizeof(sBrowserFuncs),
+ (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR,
+ _geturl,
+ _posturl,
+ _requestread,
+ _newstream,
+ _write,
+ _destroystream,
+ _status,
+ _useragent,
+ _memalloc,
+ _memfree,
+ _memflush,
+ _reloadplugins,
+ _getJavaEnv,
+ _getJavaPeer,
+ _geturlnotify,
+ _posturlnotify,
+ _getvalue,
+ _setvalue,
+ _invalidaterect,
+ _invalidateregion,
+ _forceredraw,
+ _getstringidentifier,
+ _getstringidentifiers,
+ _getintidentifier,
+ _identifierisstring,
+ _utf8fromidentifier,
+ _intfromidentifier,
+ _createobject,
+ _retainobject,
+ _releaseobject,
+ _invoke,
+ _invokeDefault,
+ _evaluate,
+ _getproperty,
+ _setproperty,
+ _removeproperty,
+ _hasproperty,
+ _hasmethod,
+ _releasevariantvalue,
+ _setexception,
+ _pushpopupsenabledstate,
+ _poppopupsenabledstate,
+ _enumerate,
+ _pluginthreadasynccall,
+ _construct,
+ _getvalueforurl,
+ _setvalueforurl,
+ _getauthenticationinfo,
+ _scheduletimer,
+ _unscheduletimer,
+ _popupcontextmenu,
+ _convertpoint,
+ nullptr, // handleevent, unimplemented
+ nullptr, // unfocusinstance, unimplemented
+ _urlredirectresponse,
+ _initasyncsurface,
+ _finalizeasyncsurface,
+ _setcurrentasyncsurface
+};
+
+static Mutex *sPluginThreadAsyncCallLock = nullptr;
+static PRCList sPendingAsyncCalls = PR_INIT_STATIC_CLIST(&sPendingAsyncCalls);
+
+// POST/GET stream type
+enum eNPPStreamTypeInternal {
+ eNPPStreamTypeInternal_Get,
+ eNPPStreamTypeInternal_Post
+};
+
+void NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState)
+{
+ nsNPAPIPluginInstance::BeginPluginCall(aReentryState);
+}
+
+void NS_NotifyPluginCall(NSPluginCallReentry aReentryState)
+{
+ nsNPAPIPluginInstance::EndPluginCall(aReentryState);
+}
+
+static void CheckClassInitialized()
+{
+ static bool initialized = false;
+
+ if (initialized)
+ return;
+
+ if (!sPluginThreadAsyncCallLock)
+ sPluginThreadAsyncCallLock = new Mutex("nsNPAPIPlugin.sPluginThreadAsyncCallLock");
+
+ initialized = true;
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,("NPN callbacks initialized\n"));
+}
+
+nsNPAPIPlugin::nsNPAPIPlugin()
+{
+ memset((void*)&mPluginFuncs, 0, sizeof(mPluginFuncs));
+ mPluginFuncs.size = sizeof(mPluginFuncs);
+ mPluginFuncs.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+
+ mLibrary = nullptr;
+}
+
+nsNPAPIPlugin::~nsNPAPIPlugin()
+{
+ delete mLibrary;
+ mLibrary = nullptr;
+}
+
+void
+nsNPAPIPlugin::PluginCrashed(const nsAString& pluginDumpID,
+ const nsAString& browserDumpID)
+{
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ host->PluginCrashed(this, pluginDumpID, browserDumpID);
+}
+
+bool
+nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag)
+{
+#ifdef MOZ_WIDGET_ANDROID
+ return false;
+#else
+ return true;
+#endif
+}
+
+inline PluginLibrary*
+GetNewPluginLibrary(nsPluginTag *aPluginTag)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ if (!aPluginTag) {
+ return nullptr;
+ }
+
+ if (XRE_IsContentProcess()) {
+ return PluginModuleContentParent::LoadModule(aPluginTag->mId, aPluginTag);
+ }
+
+ if (nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
+ return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId, aPluginTag);
+ }
+ return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary);
+}
+
+// Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin (not instance).
+nsresult
+nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+ *aResult = nullptr;
+
+ if (!aPluginTag) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CheckClassInitialized();
+
+ RefPtr<nsNPAPIPlugin> plugin = new nsNPAPIPlugin();
+
+ PluginLibrary* pluginLib = GetNewPluginLibrary(aPluginTag);
+ if (!pluginLib) {
+ return NS_ERROR_FAILURE;
+ }
+
+#if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
+ if (!pluginLib->HasRequiredFunctions()) {
+ NS_WARNING("Not all necessary functions exposed by plugin, it will not load.");
+ delete pluginLib;
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+ plugin->mLibrary = pluginLib;
+ pluginLib->SetPlugin(plugin);
+
+// Exchange NPAPI entry points.
+#if defined(XP_WIN)
+ // NP_GetEntryPoints must be called before NP_Initialize on Windows.
+ NPError pluginCallError;
+ nsresult rv = pluginLib->NP_GetEntryPoints(&plugin->mPluginFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // NP_Initialize must be called after NP_GetEntryPoints on Windows.
+ rv = pluginLib->NP_Initialize(&sBrowserFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+#elif defined(XP_MACOSX)
+ // NP_Initialize must be called before NP_GetEntryPoints on Mac OS X.
+ // We need to match WebKit's behavior.
+ NPError pluginCallError;
+ nsresult rv = pluginLib->NP_Initialize(&sBrowserFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = pluginLib->NP_GetEntryPoints(&plugin->mPluginFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+#elif defined(MOZ_WIDGET_GONK)
+#else
+ NPError pluginCallError;
+ nsresult rv = pluginLib->NP_Initialize(&sBrowserFuncs, &plugin->mPluginFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+ plugin.forget(aResult);
+ return NS_OK;
+}
+
+PluginLibrary*
+nsNPAPIPlugin::GetLibrary()
+{
+ return mLibrary;
+}
+
+NPPluginFuncs*
+nsNPAPIPlugin::PluginFuncs()
+{
+ return &mPluginFuncs;
+}
+
+nsresult
+nsNPAPIPlugin::Shutdown()
+{
+ NPP_PLUGIN_LOG(PLUGIN_LOG_BASIC,
+ ("NPP Shutdown to be called: this=%p\n", this));
+
+ NPError shutdownError;
+ mLibrary->NP_Shutdown(&shutdownError);
+
+ return NS_OK;
+}
+
+nsresult
+nsNPAPIPlugin::RetainStream(NPStream *pstream, nsISupports **aRetainedPeer)
+{
+ if (!aRetainedPeer)
+ return NS_ERROR_NULL_POINTER;
+
+ *aRetainedPeer = nullptr;
+
+ if (!pstream || !pstream->ndata)
+ return NS_ERROR_NULL_POINTER;
+
+ nsNPAPIStreamWrapper* streamWrapper = static_cast<nsNPAPIStreamWrapper*>(pstream->ndata);
+ nsNPAPIPluginStreamListener* listener = streamWrapper->GetStreamListener();
+ if (!listener) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsIStreamListener* streamListener = listener->GetStreamListenerPeer();
+ if (!streamListener) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *aRetainedPeer = streamListener;
+ NS_ADDREF(*aRetainedPeer);
+ return NS_OK;
+}
+
+// Create a new NPP GET or POST (given in the type argument) url
+// stream that may have a notify callback
+NPError
+MakeNewNPAPIStreamInternal(NPP npp, const char *relativeURL, const char *target,
+ eNPPStreamTypeInternal type,
+ bool bDoNotify = false,
+ void *notifyData = nullptr, uint32_t len = 0,
+ const char *buf = nullptr, NPBool file = false)
+{
+ if (!npp)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ PluginDestructionGuard guard(npp);
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *) npp->ndata;
+ if (!inst || !inst->IsRunning())
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ nsCOMPtr<nsIPluginHost> pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
+ nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+ if (!pluginHost) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ RefPtr<nsNPAPIPluginStreamListener> listener;
+ // Set aCallNotify here to false. If pluginHost->GetURL or PostURL fail,
+ // the listener's destructor will do the notification while we are about to
+ // return a failure code.
+ // Call SetCallNotify(true) below after we are sure we cannot return a failure
+ // code.
+ if (!target) {
+ inst->NewStreamListener(relativeURL, notifyData,
+ getter_AddRefs(listener));
+ if (listener) {
+ listener->SetCallNotify(false);
+ }
+ }
+
+ switch (type) {
+ case eNPPStreamTypeInternal_Get:
+ {
+ if (NS_FAILED(pluginHost->GetURL(inst, relativeURL, target, listener,
+ nullptr, nullptr, false)))
+ return NPERR_GENERIC_ERROR;
+ break;
+ }
+ case eNPPStreamTypeInternal_Post:
+ {
+ if (NS_FAILED(pluginHost->PostURL(inst, relativeURL, len, buf, file,
+ target, listener, nullptr, nullptr,
+ false, 0, nullptr)))
+ return NPERR_GENERIC_ERROR;
+ break;
+ }
+ default:
+ NS_ERROR("how'd I get here");
+ }
+
+ if (listener) {
+ // SetCallNotify(bDoNotify) here, see comment above.
+ listener->SetCallNotify(bDoNotify);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+#if defined(MOZ_MEMORY_WINDOWS)
+extern "C" size_t malloc_usable_size(const void *ptr);
+#endif
+
+namespace {
+
+static char *gNPPException;
+
+class nsPluginThreadRunnable : public Runnable,
+ public PRCList
+{
+public:
+ nsPluginThreadRunnable(NPP instance, PluginThreadCallback func,
+ void *userData);
+ virtual ~nsPluginThreadRunnable();
+
+ NS_IMETHOD Run();
+
+ bool IsForInstance(NPP instance)
+ {
+ return (mInstance == instance);
+ }
+
+ void Invalidate()
+ {
+ mFunc = nullptr;
+ }
+
+ bool IsValid()
+ {
+ return (mFunc != nullptr);
+ }
+
+private:
+ NPP mInstance;
+ PluginThreadCallback mFunc;
+ void *mUserData;
+};
+
+static nsIDocument *
+GetDocumentFromNPP(NPP npp)
+{
+ NS_ENSURE_TRUE(npp, nullptr);
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
+ NS_ENSURE_TRUE(inst, nullptr);
+
+ PluginDestructionGuard guard(inst);
+
+ RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
+ NS_ENSURE_TRUE(owner, nullptr);
+
+ nsCOMPtr<nsIDocument> doc;
+ owner->GetDocument(getter_AddRefs(doc));
+
+ return doc;
+}
+
+static already_AddRefed<nsIChannel>
+GetChannelFromNPP(NPP npp)
+{
+ nsCOMPtr<nsIDocument> doc = GetDocumentFromNPP(npp);
+ if (!doc)
+ return nullptr;
+ nsCOMPtr<nsPIDOMWindowOuter> domwindow = doc->GetWindow();
+ nsCOMPtr<nsIChannel> channel;
+ if (domwindow) {
+ nsCOMPtr<nsIDocShell> docShell = domwindow->GetDocShell();
+ if (docShell) {
+ docShell->GetCurrentDocumentChannel(getter_AddRefs(channel));
+ }
+ }
+ return channel.forget();
+}
+
+static NPIdentifier
+doGetIdentifier(JSContext *cx, const NPUTF8* name)
+{
+ NS_ConvertUTF8toUTF16 utf16name(name);
+
+ JSString *str = ::JS_AtomizeAndPinUCStringN(cx, utf16name.get(), utf16name.Length());
+
+ if (!str)
+ return nullptr;
+
+ return StringToNPIdentifier(cx, str);
+}
+
+#if defined(MOZ_MEMORY_WINDOWS)
+BOOL
+InHeap(HANDLE hHeap, LPVOID lpMem)
+{
+ BOOL success = FALSE;
+ PROCESS_HEAP_ENTRY he;
+ he.lpData = nullptr;
+ while (HeapWalk(hHeap, &he) != 0) {
+ if (he.lpData == lpMem) {
+ success = TRUE;
+ break;
+ }
+ }
+ HeapUnlock(hHeap);
+ return success;
+}
+#endif
+
+} /* anonymous namespace */
+
+NPPExceptionAutoHolder::NPPExceptionAutoHolder()
+ : mOldException(gNPPException)
+{
+ gNPPException = nullptr;
+}
+
+NPPExceptionAutoHolder::~NPPExceptionAutoHolder()
+{
+ NS_ASSERTION(!gNPPException, "NPP exception not properly cleared!");
+
+ gNPPException = mOldException;
+}
+
+nsPluginThreadRunnable::nsPluginThreadRunnable(NPP instance,
+ PluginThreadCallback func,
+ void *userData)
+ : mInstance(instance), mFunc(func), mUserData(userData)
+{
+ if (!sPluginThreadAsyncCallLock) {
+ // Failed to create lock, not much we can do here then...
+ mFunc = nullptr;
+
+ return;
+ }
+
+ PR_INIT_CLIST(this);
+
+ {
+ MutexAutoLock lock(*sPluginThreadAsyncCallLock);
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+ if (!inst || !inst->IsRunning()) {
+ // The plugin was stopped, ignore this async call.
+ mFunc = nullptr;
+
+ return;
+ }
+
+ PR_APPEND_LINK(this, &sPendingAsyncCalls);
+ }
+}
+
+nsPluginThreadRunnable::~nsPluginThreadRunnable()
+{
+ if (!sPluginThreadAsyncCallLock) {
+ return;
+ }
+
+ {
+ MutexAutoLock lock(*sPluginThreadAsyncCallLock);
+
+ PR_REMOVE_LINK(this);
+ }
+}
+
+NS_IMETHODIMP
+nsPluginThreadRunnable::Run()
+{
+ if (mFunc) {
+ PluginDestructionGuard guard(mInstance);
+
+ NS_TRY_SAFE_CALL_VOID(mFunc(mUserData), nullptr,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+ }
+
+ return NS_OK;
+}
+
+void
+OnPluginDestroy(NPP instance)
+{
+ if (!sPluginThreadAsyncCallLock) {
+ return;
+ }
+
+ {
+ MutexAutoLock lock(*sPluginThreadAsyncCallLock);
+
+ if (PR_CLIST_IS_EMPTY(&sPendingAsyncCalls)) {
+ return;
+ }
+
+ nsPluginThreadRunnable *r =
+ (nsPluginThreadRunnable *)PR_LIST_HEAD(&sPendingAsyncCalls);
+
+ do {
+ if (r->IsForInstance(instance)) {
+ r->Invalidate();
+ }
+
+ r = (nsPluginThreadRunnable *)PR_NEXT_LINK(r);
+ } while (r != &sPendingAsyncCalls);
+ }
+}
+
+void
+OnShutdown()
+{
+ NS_ASSERTION(PR_CLIST_IS_EMPTY(&sPendingAsyncCalls),
+ "Pending async plugin call list not cleaned up!");
+
+ if (sPluginThreadAsyncCallLock) {
+ delete sPluginThreadAsyncCallLock;
+
+ sPluginThreadAsyncCallLock = nullptr;
+ }
+}
+
+AsyncCallbackAutoLock::AsyncCallbackAutoLock()
+{
+ if (sPluginThreadAsyncCallLock) {
+ sPluginThreadAsyncCallLock->Lock();
+ }
+}
+
+AsyncCallbackAutoLock::~AsyncCallbackAutoLock()
+{
+ if (sPluginThreadAsyncCallLock) {
+ sPluginThreadAsyncCallLock->Unlock();
+ }
+}
+
+NPP NPPStack::sCurrentNPP = nullptr;
+
+const char *
+PeekException()
+{
+ return gNPPException;
+}
+
+void
+PopException()
+{
+ NS_ASSERTION(gNPPException, "Uh, no NPP exception to pop!");
+
+ if (gNPPException) {
+ free(gNPPException);
+
+ gNPPException = nullptr;
+ }
+}
+
+//
+// Static callbacks that get routed back through the new C++ API
+//
+
+namespace mozilla {
+namespace plugins {
+namespace parent {
+
+NPError
+_geturl(NPP npp, const char* relativeURL, const char* target)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_geturl called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_GetURL: npp=%p, target=%s, url=%s\n", (void *)npp, target,
+ relativeURL));
+
+ PluginDestructionGuard guard(npp);
+
+ // Block Adobe Acrobat from loading URLs that are not http:, https:,
+ // or ftp: URLs if the given target is null.
+ if (!target && relativeURL &&
+ (strncmp(relativeURL, "http:", 5) != 0) &&
+ (strncmp(relativeURL, "https:", 6) != 0) &&
+ (strncmp(relativeURL, "ftp:", 4) != 0)) {
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *) npp->ndata;
+
+ const char *name = nullptr;
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ host->GetPluginName(inst, &name);
+
+ if (name && strstr(name, "Adobe") && strstr(name, "Acrobat")) {
+ return NPERR_NO_ERROR;
+ }
+ }
+
+ return MakeNewNPAPIStreamInternal(npp, relativeURL, target,
+ eNPPStreamTypeInternal_Get);
+}
+
+NPError
+_geturlnotify(NPP npp, const char* relativeURL, const char* target,
+ void* notifyData)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_geturlnotify called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_GetURLNotify: npp=%p, target=%s, notify=%p, url=%s\n", (void*)npp,
+ target, notifyData, relativeURL));
+
+ PluginDestructionGuard guard(npp);
+
+ return MakeNewNPAPIStreamInternal(npp, relativeURL, target,
+ eNPPStreamTypeInternal_Get, true,
+ notifyData);
+}
+
+NPError
+_posturlnotify(NPP npp, const char *relativeURL, const char *target,
+ uint32_t len, const char *buf, NPBool file, void *notifyData)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_posturlnotify called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ if (!buf)
+ return NPERR_INVALID_PARAM;
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_PostURLNotify: npp=%p, target=%s, len=%d, file=%d, "
+ "notify=%p, url=%s, buf=%s\n",
+ (void*)npp, target, len, file, notifyData, relativeURL,
+ buf));
+
+ PluginDestructionGuard guard(npp);
+
+ return MakeNewNPAPIStreamInternal(npp, relativeURL, target,
+ eNPPStreamTypeInternal_Post, true,
+ notifyData, len, buf, file);
+}
+
+NPError
+_posturl(NPP npp, const char *relativeURL, const char *target,
+ uint32_t len, const char *buf, NPBool file)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_posturl called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_PostURL: npp=%p, target=%s, file=%d, len=%d, url=%s, "
+ "buf=%s\n",
+ (void*)npp, target, file, len, relativeURL, buf));
+
+ PluginDestructionGuard guard(npp);
+
+ return MakeNewNPAPIStreamInternal(npp, relativeURL, target,
+ eNPPStreamTypeInternal_Post, false, nullptr,
+ len, buf, file);
+}
+
+NPError
+_newstream(NPP npp, NPMIMEType type, const char* target, NPStream* *result)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_newstream called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_NewStream: npp=%p, type=%s, target=%s\n", (void*)npp,
+ (const char *)type, target));
+
+ NPError err = NPERR_INVALID_INSTANCE_ERROR;
+ if (npp && npp->ndata) {
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata;
+
+ PluginDestructionGuard guard(inst);
+
+ nsCOMPtr<nsIOutputStream> stream;
+ if (NS_SUCCEEDED(inst->NewStreamFromPlugin((const char*) type, target,
+ getter_AddRefs(stream)))) {
+ nsNPAPIStreamWrapper* wrapper = new nsNPAPIStreamWrapper(stream, nullptr);
+ if (wrapper) {
+ (*result) = &wrapper->mNPStream;
+ err = NPERR_NO_ERROR;
+ } else {
+ err = NPERR_OUT_OF_MEMORY_ERROR;
+ }
+ } else {
+ err = NPERR_GENERIC_ERROR;
+ }
+ }
+ return err;
+}
+
+int32_t
+_write(NPP npp, NPStream *pstream, int32_t len, void *buffer)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_write called from the wrong thread\n"));
+ return 0;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_Write: npp=%p, url=%s, len=%d, buffer=%s\n", (void*)npp,
+ pstream->url, len, (char*)buffer));
+
+ // negative return indicates failure to the plugin
+ if (!npp)
+ return -1;
+
+ PluginDestructionGuard guard(npp);
+
+ nsNPAPIStreamWrapper* wrapper = static_cast<nsNPAPIStreamWrapper*>(pstream->ndata);
+ if (!wrapper) {
+ return -1;
+ }
+
+ nsIOutputStream* stream = wrapper->GetOutputStream();
+ if (!stream) {
+ return -1;
+ }
+
+ uint32_t count = 0;
+ nsresult rv = stream->Write((char *)buffer, len, &count);
+
+ if (NS_FAILED(rv)) {
+ return -1;
+ }
+
+ return (int32_t)count;
+}
+
+NPError
+_destroystream(NPP npp, NPStream *pstream, NPError reason)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_destroystream called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_DestroyStream: npp=%p, url=%s, reason=%d\n", (void*)npp,
+ pstream->url, (int)reason));
+
+ if (!npp)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ PluginDestructionGuard guard(npp);
+
+ nsNPAPIStreamWrapper *streamWrapper = static_cast<nsNPAPIStreamWrapper*>(pstream->ndata);
+ if (!streamWrapper) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ nsNPAPIPluginStreamListener *listener = streamWrapper->GetStreamListener();
+ if (listener) {
+ // This type of stream is going from the browser to the plugin. It's either the
+ // initial src/data stream or another stream resulting from NPN_GetURL* or
+ // NPN_PostURL*.
+ //
+ // Calling OnStopBinding on the listener may cause it to be deleted due to the
+ // releasing of its last references.
+ listener->OnStopBinding(nullptr, NS_BINDING_ABORTED);
+ } else {
+ // This type of stream (NPStream) was created via NPN_NewStream. The plugin holds
+ // the reference until it is to be deleted here. Deleting the wrapper will
+ // release the wrapped nsIOutputStream.
+ //
+ // The NPStream the plugin references should always be a sub-object of its own
+ // 'ndata', which is our nsNPAPIStramWrapper. See bug 548441.
+ NS_ASSERTION((char*)streamWrapper <= (char*)pstream &&
+ ((char*)pstream) + sizeof(*pstream)
+ <= ((char*)streamWrapper) + sizeof(*streamWrapper),
+ "pstream is not a subobject of wrapper");
+ delete streamWrapper;
+ }
+
+ // 'listener' and/or 'streamWrapper' may be invalid (deleted) at this point. Don't
+ // touch them again!
+
+ return NPERR_NO_ERROR;
+}
+
+void
+_status(NPP npp, const char *message)
+{
+ // NPN_Status is no longer supported.
+}
+
+void
+_memfree (void *ptr)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_memfree called from the wrong thread\n"));
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPN_MemFree: ptr=%p\n", ptr));
+
+ if (ptr)
+ free(ptr);
+}
+
+uint32_t
+_memflush(uint32_t size)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_memflush called from the wrong thread\n"));
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPN_MemFlush: size=%d\n", size));
+
+ nsMemory::HeapMinimize(true);
+ return 0;
+}
+
+void
+_reloadplugins(NPBool reloadPages)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_reloadplugins called from the wrong thread\n"));
+ return;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_ReloadPlugins: reloadPages=%d\n", reloadPages));
+
+ nsCOMPtr<nsIPluginHost> pluginHost(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
+ if (!pluginHost)
+ return;
+
+ pluginHost->ReloadPlugins();
+}
+
+void
+_invalidaterect(NPP npp, NPRect *invalidRect)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_invalidaterect called from the wrong thread\n"));
+ return;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_InvalidateRect: npp=%p, top=%d, left=%d, bottom=%d, "
+ "right=%d\n", (void *)npp, invalidRect->top,
+ invalidRect->left, invalidRect->bottom, invalidRect->right));
+
+ if (!npp || !npp->ndata) {
+ NS_WARNING("_invalidaterect: npp or npp->ndata == 0");
+ return;
+ }
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata;
+
+ PluginDestructionGuard guard(inst);
+
+ inst->InvalidateRect((NPRect *)invalidRect);
+}
+
+void
+_invalidateregion(NPP npp, NPRegion invalidRegion)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_invalidateregion called from the wrong thread\n"));
+ return;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_InvalidateRegion: npp=%p, region=%p\n", (void*)npp,
+ (void*)invalidRegion));
+
+ if (!npp || !npp->ndata) {
+ NS_WARNING("_invalidateregion: npp or npp->ndata == 0");
+ return;
+ }
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata;
+
+ PluginDestructionGuard guard(inst);
+
+ inst->InvalidateRegion((NPRegion)invalidRegion);
+}
+
+void
+_forceredraw(NPP npp)
+{
+}
+
+NPObject*
+_getwindowobject(NPP npp)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getwindowobject called from the wrong thread\n"));
+ return nullptr;
+ }
+
+ // The window want to return here is the outer window, *not* the inner (since
+ // we don't know what the plugin will do with it).
+ nsIDocument* doc = GetDocumentFromNPP(npp);
+ NS_ENSURE_TRUE(doc, nullptr);
+ nsCOMPtr<nsPIDOMWindowOuter> outer = doc->GetWindow();
+ NS_ENSURE_TRUE(outer, nullptr);
+
+ JS::Rooted<JSObject*> global(dom::RootingCx(),
+ nsGlobalWindow::Cast(outer)->GetGlobalJSObject());
+ return nsJSObjWrapper::GetNewOrUsed(npp, global);
+}
+
+NPObject*
+_getpluginelement(NPP npp)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getpluginelement called from the wrong thread\n"));
+ return nullptr;
+ }
+
+ nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(npp->ndata);
+ if (!inst)
+ return nullptr;
+
+ nsCOMPtr<nsIDOMElement> element;
+ inst->GetDOMElement(getter_AddRefs(element));
+
+ if (!element)
+ return nullptr;
+
+ nsIDocument *doc = GetDocumentFromNPP(npp);
+ if (NS_WARN_IF(!doc)) {
+ return nullptr;
+ }
+
+ dom::AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(doc->GetInnerWindow()))) {
+ return nullptr;
+ }
+ JSContext* cx = jsapi.cx();
+
+ nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
+ NS_ENSURE_TRUE(xpc, nullptr);
+
+ JS::RootedObject obj(cx);
+ xpc->WrapNative(cx, ::JS::CurrentGlobalOrNull(cx), element,
+ NS_GET_IID(nsIDOMElement), obj.address());
+ NS_ENSURE_TRUE(obj, nullptr);
+
+ return nsJSObjWrapper::GetNewOrUsed(npp, obj);
+}
+
+NPIdentifier
+_getstringidentifier(const NPUTF8* name)
+{
+ if (!name) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("NPN_getstringidentifier: passed null name"));
+ return nullptr;
+ }
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getstringidentifier called from the wrong thread\n"));
+ }
+
+ AutoSafeJSContext cx;
+ return doGetIdentifier(cx, name);
+}
+
+void
+_getstringidentifiers(const NPUTF8** names, int32_t nameCount,
+ NPIdentifier *identifiers)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getstringidentifiers called from the wrong thread\n"));
+ }
+
+ AutoSafeJSContext cx;
+
+ for (int32_t i = 0; i < nameCount; ++i) {
+ if (names[i]) {
+ identifiers[i] = doGetIdentifier(cx, names[i]);
+ } else {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("NPN_getstringidentifiers: passed null name"));
+ identifiers[i] = nullptr;
+ }
+ }
+}
+
+NPIdentifier
+_getintidentifier(int32_t intid)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getstringidentifier called from the wrong thread\n"));
+ }
+ return IntToNPIdentifier(intid);
+}
+
+NPUTF8*
+_utf8fromidentifier(NPIdentifier id)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_utf8fromidentifier called from the wrong thread\n"));
+ }
+ if (!id)
+ return nullptr;
+
+ if (!NPIdentifierIsString(id)) {
+ return nullptr;
+ }
+
+ JSString *str = NPIdentifierToString(id);
+ nsAutoString autoStr;
+ AssignJSFlatString(autoStr, JS_ASSERT_STRING_IS_FLAT(str));
+
+ return ToNewUTF8String(autoStr);
+}
+
+int32_t
+_intfromidentifier(NPIdentifier id)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_intfromidentifier called from the wrong thread\n"));
+ }
+
+ if (!NPIdentifierIsInt(id)) {
+ return INT32_MIN;
+ }
+
+ return NPIdentifierToInt(id);
+}
+
+bool
+_identifierisstring(NPIdentifier id)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_identifierisstring called from the wrong thread\n"));
+ }
+
+ return NPIdentifierIsString(id);
+}
+
+NPObject*
+_createobject(NPP npp, NPClass* aClass)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_createobject called from the wrong thread\n"));
+ return nullptr;
+ }
+ if (!npp) {
+ NS_ERROR("Null npp passed to _createobject()!");
+
+ return nullptr;
+ }
+
+ PluginDestructionGuard guard(npp);
+
+ if (!aClass) {
+ NS_ERROR("Null class passed to _createobject()!");
+
+ return nullptr;
+ }
+
+ NPPAutoPusher nppPusher(npp);
+
+ NPObject *npobj;
+
+ if (aClass->allocate) {
+ npobj = aClass->allocate(npp, aClass);
+ } else {
+ npobj = (NPObject *)PR_Malloc(sizeof(NPObject));
+ }
+
+ if (npobj) {
+ npobj->_class = aClass;
+ npobj->referenceCount = 1;
+ NS_LOG_ADDREF(npobj, 1, "BrowserNPObject", sizeof(NPObject));
+ }
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("Created NPObject %p, NPClass %p\n", npobj, aClass));
+
+ return npobj;
+}
+
+NPObject*
+_retainobject(NPObject* npobj)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_retainobject called from the wrong thread\n"));
+ }
+ if (npobj) {
+#ifdef NS_BUILD_REFCNT_LOGGING
+ int32_t refCnt =
+#endif
+ PR_ATOMIC_INCREMENT((int32_t*)&npobj->referenceCount);
+ NS_LOG_ADDREF(npobj, refCnt, "BrowserNPObject", sizeof(NPObject));
+ }
+
+ return npobj;
+}
+
+void
+_releaseobject(NPObject* npobj)
+{
+ // If nothing is passed, just return, even if we're on the wrong thread.
+ if (!npobj) {
+ return;
+ }
+
+ // THIS IS A KNOWN LEAK. SEE BUG 1221448.
+ // If releaseobject is called off the main thread and we have a valid pointer,
+ // we at least know it was created on the main thread (see _createobject
+ // implementation). However, forwarding the deletion back to the main thread
+ // without careful checking could cause bad memory management races. So, for
+ // now, we leak by warning and then just returning early. But it should fix
+ // java 7 crashes.
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_releaseobject called from the wrong thread\n"));
+ return;
+ }
+
+ int32_t refCnt = PR_ATOMIC_DECREMENT((int32_t*)&npobj->referenceCount);
+ NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
+
+ if (refCnt == 0) {
+ nsNPObjWrapper::OnDestroy(npobj);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("Deleting NPObject %p, refcount hit 0\n", npobj));
+
+ if (npobj->_class && npobj->_class->deallocate) {
+ npobj->_class->deallocate(npobj);
+ } else {
+ PR_Free(npobj);
+ }
+ }
+}
+
+bool
+_invoke(NPP npp, NPObject* npobj, NPIdentifier method, const NPVariant *args,
+ uint32_t argCount, NPVariant *result)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_invoke called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->invoke)
+ return false;
+
+ PluginDestructionGuard guard(npp);
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_Invoke(npp %p, npobj %p, method %p, args %d\n", npp,
+ npobj, method, argCount));
+
+ return npobj->_class->invoke(npobj, method, args, argCount, result);
+}
+
+bool
+_invokeDefault(NPP npp, NPObject* npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_invokedefault called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->invokeDefault)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_InvokeDefault(npp %p, npobj %p, args %d\n", npp,
+ npobj, argCount));
+
+ return npobj->_class->invokeDefault(npobj, args, argCount, result);
+}
+
+bool
+_evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_evaluate called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp)
+ return false;
+
+ NPPAutoPusher nppPusher(npp);
+
+ nsIDocument *doc = GetDocumentFromNPP(npp);
+ NS_ENSURE_TRUE(doc, false);
+
+ nsGlobalWindow* win = nsGlobalWindow::Cast(doc->GetInnerWindow());
+ if (NS_WARN_IF(!win || !win->FastGetGlobalJSObject())) {
+ return false;
+ }
+
+ nsAutoMicroTask mt;
+ dom::AutoEntryScript aes(win, "NPAPI NPN_evaluate");
+ JSContext* cx = aes.cx();
+
+ JS::Rooted<JSObject*> obj(cx, nsNPObjWrapper::GetNewOrUsed(npp, cx, npobj));
+
+ if (!obj) {
+ return false;
+ }
+
+ obj = js::ToWindowIfWindowProxy(obj);
+ MOZ_ASSERT(obj, "ToWindowIfWindowProxy should never return null");
+
+ if (result) {
+ // Initialize the out param to void
+ VOID_TO_NPVARIANT(*result);
+ }
+
+ if (!script || !script->UTF8Length || !script->UTF8Characters) {
+ // Nothing to evaluate.
+
+ return true;
+ }
+
+ NS_ConvertUTF8toUTF16 utf16script(script->UTF8Characters,
+ script->UTF8Length);
+
+ nsIPrincipal *principal = doc->NodePrincipal();
+
+ nsAutoCString specStr;
+ const char *spec;
+
+ nsCOMPtr<nsIURI> uri;
+ principal->GetURI(getter_AddRefs(uri));
+
+ if (uri) {
+ uri->GetSpec(specStr);
+ spec = specStr.get();
+ } else {
+ // No URI in a principal means it's the system principal. If the
+ // document URI is a chrome:// URI, pass that in as the URI of the
+ // script, else pass in null for the filename as there's no way to
+ // know where this document really came from. Passing in null here
+ // also means that the script gets treated by XPConnect as if it
+ // needs additional protection, which is what we want for unknown
+ // chrome code anyways.
+
+ uri = doc->GetDocumentURI();
+ bool isChrome = false;
+
+ if (uri && NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome) {
+ uri->GetSpec(specStr);
+ spec = specStr.get();
+ } else {
+ spec = nullptr;
+ }
+ }
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_Evaluate(npp %p, npobj %p, script <<<%s>>>) called\n",
+ npp, npobj, script->UTF8Characters));
+
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(spec, 0)
+ .setVersion(JSVERSION_DEFAULT);
+ JS::Rooted<JS::Value> rval(cx);
+ nsJSUtils::EvaluateOptions evalOptions(cx);
+ if (obj != js::GetGlobalForObjectCrossCompartment(obj) &&
+ !evalOptions.scopeChain.append(obj)) {
+ return false;
+ }
+ obj = js::GetGlobalForObjectCrossCompartment(obj);
+ nsresult rv = nsJSUtils::EvaluateString(cx, utf16script, obj, options,
+ evalOptions, &rval);
+
+ return NS_SUCCEEDED(rv) &&
+ (!result || JSValToNPVariant(npp, cx, rval, result));
+}
+
+bool
+_getproperty(NPP npp, NPObject* npobj, NPIdentifier property,
+ NPVariant *result)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getproperty called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->getProperty)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_GetProperty(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, property));
+
+ if (!npobj->_class->getProperty(npobj, property, result))
+ return false;
+
+ // If a Java plugin tries to get the document.URL or document.documentURI
+ // property from us, don't pass back a value that Java won't be able to
+ // understand -- one that will make the URL(String) constructor throw a
+ // MalformedURL exception. Passing such a value causes Java Plugin2 to
+ // crash (to throw a RuntimeException in Plugin2Manager.getDocumentBase()).
+ // Also don't pass back a value that Java is likely to mishandle.
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*) npp->ndata;
+ if (!inst)
+ return false;
+ nsNPAPIPlugin* plugin = inst->GetPlugin();
+ if (!plugin)
+ return false;
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ nsPluginTag* pluginTag = host->TagForPlugin(plugin);
+ if (!pluginTag->mIsJavaPlugin)
+ return true;
+
+ if (!NPVARIANT_IS_STRING(*result))
+ return true;
+
+ NPUTF8* propertyName = _utf8fromidentifier(property);
+ if (!propertyName)
+ return true;
+ bool notURL =
+ (PL_strcasecmp(propertyName, "URL") &&
+ PL_strcasecmp(propertyName, "documentURI"));
+ _memfree(propertyName);
+ if (notURL)
+ return true;
+
+ NPObject* window_obj = _getwindowobject(npp);
+ if (!window_obj)
+ return true;
+
+ NPVariant doc_v;
+ NPObject* document_obj = nullptr;
+ NPIdentifier doc_id = _getstringidentifier("document");
+ bool ok = npobj->_class->getProperty(window_obj, doc_id, &doc_v);
+ _releaseobject(window_obj);
+ if (ok) {
+ if (NPVARIANT_IS_OBJECT(doc_v)) {
+ document_obj = NPVARIANT_TO_OBJECT(doc_v);
+ } else {
+ _releasevariantvalue(&doc_v);
+ return true;
+ }
+ } else {
+ return true;
+ }
+ _releaseobject(document_obj);
+ if (document_obj != npobj)
+ return true;
+
+ NPString urlnp = NPVARIANT_TO_STRING(*result);
+ nsXPIDLCString url;
+ url.Assign(urlnp.UTF8Characters, urlnp.UTF8Length);
+
+ bool javaCompatible = false;
+ if (NS_FAILED(NS_CheckIsJavaCompatibleURLString(url, &javaCompatible)))
+ javaCompatible = false;
+ if (javaCompatible)
+ return true;
+
+ // If Java won't be able to interpret the original value of document.URL or
+ // document.documentURI, or is likely to mishandle it, pass back something
+ // that Java will understand but won't be able to use to access the network,
+ // and for which same-origin checks will always fail.
+
+ if (inst->mFakeURL.IsVoid()) {
+ // Abort (do an error return) if NS_MakeRandomInvalidURLString() fails.
+ if (NS_FAILED(NS_MakeRandomInvalidURLString(inst->mFakeURL))) {
+ _releasevariantvalue(result);
+ return false;
+ }
+ }
+
+ _releasevariantvalue(result);
+ char* fakeurl = (char *) _memalloc(inst->mFakeURL.Length() + 1);
+ strcpy(fakeurl, inst->mFakeURL);
+ STRINGZ_TO_NPVARIANT(fakeurl, *result);
+
+ return true;
+}
+
+bool
+_setproperty(NPP npp, NPObject* npobj, NPIdentifier property,
+ const NPVariant *value)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_setproperty called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->setProperty)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_SetProperty(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, property));
+
+ return npobj->_class->setProperty(npobj, property, value);
+}
+
+bool
+_removeproperty(NPP npp, NPObject* npobj, NPIdentifier property)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_removeproperty called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->removeProperty)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_RemoveProperty(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, property));
+
+ return npobj->_class->removeProperty(npobj, property);
+}
+
+bool
+_hasproperty(NPP npp, NPObject* npobj, NPIdentifier propertyName)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_hasproperty called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->hasProperty)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_HasProperty(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, propertyName));
+
+ return npobj->_class->hasProperty(npobj, propertyName);
+}
+
+bool
+_hasmethod(NPP npp, NPObject* npobj, NPIdentifier methodName)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_hasmethod called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->hasMethod)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_HasMethod(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, methodName));
+
+ return npobj->_class->hasMethod(npobj, methodName);
+}
+
+bool
+_enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier,
+ uint32_t *count)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_enumerate called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class)
+ return false;
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_Enumerate(npp %p, npobj %p) called\n", npp, npobj));
+
+ if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
+ !npobj->_class->enumerate) {
+ *identifier = 0;
+ *count = 0;
+ return true;
+ }
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ return npobj->_class->enumerate(npobj, identifier, count);
+}
+
+bool
+_construct(NPP npp, NPObject* npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_construct called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class ||
+ !NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) ||
+ !npobj->_class->construct) {
+ return false;
+ }
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ return npobj->_class->construct(npobj, args, argCount, result);
+}
+
+void
+_releasevariantvalue(NPVariant* variant)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_releasevariantvalue called from the wrong thread\n"));
+ }
+ switch (variant->type) {
+ case NPVariantType_Void :
+ case NPVariantType_Null :
+ case NPVariantType_Bool :
+ case NPVariantType_Int32 :
+ case NPVariantType_Double :
+ break;
+ case NPVariantType_String :
+ {
+ const NPString *s = &NPVARIANT_TO_STRING(*variant);
+
+ if (s->UTF8Characters) {
+#if defined(MOZ_MEMORY_WINDOWS)
+ if (malloc_usable_size((void *)s->UTF8Characters) != 0) {
+ PR_Free((void *)s->UTF8Characters);
+ } else {
+ void *p = (void *)s->UTF8Characters;
+ DWORD nheaps = 0;
+ AutoTArray<HANDLE, 50> heaps;
+ nheaps = GetProcessHeaps(0, heaps.Elements());
+ heaps.AppendElements(nheaps);
+ GetProcessHeaps(nheaps, heaps.Elements());
+ for (DWORD i = 0; i < nheaps; i++) {
+ if (InHeap(heaps[i], p)) {
+ HeapFree(heaps[i], 0, p);
+ break;
+ }
+ }
+ }
+#else
+ free((void *)s->UTF8Characters);
+#endif
+ }
+ break;
+ }
+ case NPVariantType_Object:
+ {
+ NPObject *npobj = NPVARIANT_TO_OBJECT(*variant);
+
+ if (npobj)
+ _releaseobject(npobj);
+
+ break;
+ }
+ default:
+ NS_ERROR("Unknown NPVariant type!");
+ }
+
+ VOID_TO_NPVARIANT(*variant);
+}
+
+void
+_setexception(NPObject* npobj, const NPUTF8 *message)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_setexception called from the wrong thread\n"));
+ return;
+ }
+
+ if (!message) return;
+
+ if (gNPPException) {
+ // If a plugin throws multiple exceptions, we'll only report the
+ // last one for now.
+ free(gNPPException);
+ }
+
+ gNPPException = strdup(message);
+}
+
+NPError
+_getvalue(NPP npp, NPNVariable variable, void *result)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getvalue called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_GetValue: npp=%p, var=%d\n",
+ (void*)npp, (int)variable));
+
+ nsresult res;
+
+ PluginDestructionGuard guard(npp);
+
+ // Cast NPNVariable enum to int to avoid warnings about including switch
+ // cases for android_npapi.h's non-standard ANPInterface values.
+ switch (static_cast<int>(variable)) {
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ case NPNVxDisplay : {
+#if defined(MOZ_X11)
+ if (npp) {
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *) npp->ndata;
+ bool windowless = false;
+ inst->IsWindowless(&windowless);
+ // The documentation on the types for many variables in NP(N|P)_GetValue
+ // is vague. Often boolean values are NPBool (1 byte), but
+ // https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins
+ // treats NPPVpluginNeedsXEmbed as PRBool (int), and
+ // on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|.
+ // thus we can't use NPBool for needsXEmbed, or the three bytes above
+ // it on the stack would get clobbered. so protect with the larger bool.
+ int needsXEmbed = 0;
+ if (!windowless) {
+ res = inst->GetValueFromPlugin(NPPVpluginNeedsXEmbed, &needsXEmbed);
+ // If the call returned an error code make sure we still use our default value.
+ if (NS_FAILED(res)) {
+ needsXEmbed = 0;
+ }
+ }
+ if (windowless || needsXEmbed) {
+ (*(Display **)result) = mozilla::DefaultXDisplay();
+ return NPERR_NO_ERROR;
+ }
+ }
+#if (MOZ_WIDGET_GTK == 2)
+ // adobe nppdf calls XtGetApplicationNameAndClass(display,
+ // &instance, &class) we have to init Xt toolkit before get
+ // XtDisplay just call gtk_xtbin_new(w,0) once
+ static GtkWidget *gtkXtBinHolder = 0;
+ if (!gtkXtBinHolder) {
+ gtkXtBinHolder = gtk_xtbin_new(gdk_get_default_root_window(),0);
+ // it crashes on destroy, let it leak
+ // gtk_widget_destroy(gtkXtBinHolder);
+ }
+ (*(Display **)result) = GTK_XTBIN(gtkXtBinHolder)->xtdisplay;
+ return NPERR_NO_ERROR;
+#endif
+#endif
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case NPNVxtAppContext:
+ return NPERR_GENERIC_ERROR;
+#endif
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ case NPNVnetscapeWindow: {
+ if (!npp || !npp->ndata)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *) npp->ndata;
+
+ RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
+ NS_ENSURE_TRUE(owner, NPERR_NO_ERROR);
+
+ if (NS_SUCCEEDED(owner->GetNetscapeWindow(result))) {
+ return NPERR_NO_ERROR;
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+#endif
+
+ case NPNVjavascriptEnabledBool: {
+ *(NPBool*)result = false;
+ bool js = false;
+ res = Preferences::GetBool("javascript.enabled", &js);
+ if (NS_SUCCEEDED(res)) {
+ *(NPBool*)result = js;
+ }
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVasdEnabledBool:
+ *(NPBool*)result = false;
+ return NPERR_NO_ERROR;
+
+ case NPNVisOfflineBool: {
+ bool offline = false;
+ nsCOMPtr<nsIIOService> ioservice =
+ do_GetService(NS_IOSERVICE_CONTRACTID, &res);
+ if (NS_SUCCEEDED(res))
+ res = ioservice->GetOffline(&offline);
+ if (NS_FAILED(res))
+ return NPERR_GENERIC_ERROR;
+
+ *(NPBool*)result = offline;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVToolkit: {
+#ifdef MOZ_WIDGET_GTK
+ *((NPNToolkitType*)result) = NPNVGtk2;
+#endif
+
+ if (*(NPNToolkitType*)result)
+ return NPERR_NO_ERROR;
+
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case NPNVSupportsXEmbedBool: {
+#ifdef MOZ_WIDGET_GTK
+ *(NPBool*)result = true;
+#else
+ *(NPBool*)result = false;
+#endif
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVWindowNPObject: {
+ *(NPObject **)result = _getwindowobject(npp);
+
+ return *(NPObject **)result ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+ }
+
+ case NPNVPluginElementNPObject: {
+ *(NPObject **)result = _getpluginelement(npp);
+
+ return *(NPObject **)result ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+ }
+
+ case NPNVSupportsWindowless: {
+#if defined(XP_WIN) || defined(XP_MACOSX) || \
+ (defined(MOZ_X11) && defined(MOZ_WIDGET_GTK))
+ *(NPBool*)result = true;
+#else
+ *(NPBool*)result = false;
+#endif
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVprivateModeBool: {
+ bool privacy;
+ nsNPAPIPluginInstance *inst = static_cast<nsNPAPIPluginInstance*>(npp->ndata);
+ if (!inst)
+ return NPERR_GENERIC_ERROR;
+
+ nsresult rv = inst->IsPrivateBrowsing(&privacy);
+ if (NS_FAILED(rv))
+ return NPERR_GENERIC_ERROR;
+ *(NPBool*)result = (NPBool)privacy;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVdocumentOrigin: {
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
+ if (!inst) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ nsCOMPtr<nsIDOMElement> element;
+ inst->GetDOMElement(getter_AddRefs(element));
+ if (!element) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ nsCOMPtr<nsIContent> content(do_QueryInterface(element));
+ if (!content) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ nsIPrincipal* principal = content->NodePrincipal();
+
+ nsAutoString utf16Origin;
+ res = nsContentUtils::GetUTFOrigin(principal, utf16Origin);
+ if (NS_FAILED(res)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ nsCOMPtr<nsIIDNService> idnService = do_GetService(NS_IDNSERVICE_CONTRACTID);
+ if (!idnService) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // This is a bit messy: we convert to UTF-8 here, but then
+ // nsIDNService::Normalize will convert back to UTF-16 for processing,
+ // and back to UTF-8 again to return the result.
+ // Alternative: perhaps we should add a NormalizeUTF16 version of the API,
+ // and just convert to UTF-8 for the final return (resulting in one
+ // encoding form conversion instead of three).
+ NS_ConvertUTF16toUTF8 utf8Origin(utf16Origin);
+ nsAutoCString normalizedUTF8Origin;
+ res = idnService->Normalize(utf8Origin, normalizedUTF8Origin);
+ if (NS_FAILED(res)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ *(char**)result = ToNewCString(normalizedUTF8Origin);
+ return *(char**)result ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+ }
+
+#ifdef XP_MACOSX
+ case NPNVpluginDrawingModel: {
+ if (npp) {
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata;
+ if (inst) {
+ NPDrawingModel drawingModel;
+ inst->GetDrawingModel((int32_t*)&drawingModel);
+ *(NPDrawingModel*)result = drawingModel;
+ return NPERR_NO_ERROR;
+ }
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+
+#ifndef NP_NO_QUICKDRAW
+ case NPNVsupportsQuickDrawBool: {
+ *(NPBool*)result = false;
+
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ case NPNVsupportsCoreGraphicsBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCoreAnimationBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsInvalidatingCoreAnimationBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCompositingCoreAnimationPluginsBool: {
+ *(NPBool*)result = PR_TRUE;
+
+ return NPERR_NO_ERROR;
+ }
+
+#ifndef NP_NO_CARBON
+ case NPNVsupportsCarbonBool: {
+ *(NPBool*)result = false;
+
+ return NPERR_NO_ERROR;
+ }
+#endif
+ case NPNVsupportsCocoaBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsUpdatedCocoaTextInputBool: {
+ *(NPBool*)result = true;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ case NPNVcontentsScaleFactor: {
+ nsNPAPIPluginInstance *inst =
+ (nsNPAPIPluginInstance *) (npp ? npp->ndata : nullptr);
+ double scaleFactor = inst ? inst->GetContentsScaleFactor() : 1.0;
+ *(double*)result = scaleFactor;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ case NPNVCSSZoomFactor: {
+ nsNPAPIPluginInstance *inst =
+ (nsNPAPIPluginInstance *) (npp ? npp->ndata : nullptr);
+ double scaleFactor = inst ? inst->GetCSSZoomFactor() : 1.0;
+ *(double*)result = scaleFactor;
+ return NPERR_NO_ERROR;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ case kLogInterfaceV0_ANPGetValue: {
+ LOG("get log interface");
+ ANPLogInterfaceV0 *i = (ANPLogInterfaceV0 *) result;
+ InitLogInterface(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kBitmapInterfaceV0_ANPGetValue: {
+ LOG("get bitmap interface");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case kMatrixInterfaceV0_ANPGetValue: {
+ LOG("get matrix interface");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case kPathInterfaceV0_ANPGetValue: {
+ LOG("get path interface");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case kTypefaceInterfaceV0_ANPGetValue: {
+ LOG("get typeface interface");
+ ANPTypefaceInterfaceV0 *i = (ANPTypefaceInterfaceV0 *) result;
+ InitTypeFaceInterface(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kPaintInterfaceV0_ANPGetValue: {
+ LOG("get paint interface");
+ ANPPaintInterfaceV0 *i = (ANPPaintInterfaceV0 *) result;
+ InitPaintInterface(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kCanvasInterfaceV0_ANPGetValue: {
+ LOG("get canvas interface");
+ ANPCanvasInterfaceV0 *i = (ANPCanvasInterfaceV0 *) result;
+ InitCanvasInterface(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kWindowInterfaceV0_ANPGetValue: {
+ LOG("get window interface");
+ ANPWindowInterfaceV0 *i = (ANPWindowInterfaceV0 *) result;
+ InitWindowInterface(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kAudioTrackInterfaceV0_ANPGetValue: {
+ LOG("get audio interface");
+ ANPAudioTrackInterfaceV0 *i = (ANPAudioTrackInterfaceV0 *) result;
+ InitAudioTrackInterfaceV0(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kEventInterfaceV0_ANPGetValue: {
+ LOG("get event interface");
+ ANPEventInterfaceV0 *i = (ANPEventInterfaceV0 *) result;
+ InitEventInterface(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kSystemInterfaceV0_ANPGetValue: {
+ LOG("get system interface");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case kSurfaceInterfaceV0_ANPGetValue: {
+ LOG("get surface interface");
+ ANPSurfaceInterfaceV0 *i = (ANPSurfaceInterfaceV0 *) result;
+ InitSurfaceInterface(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kSupportedDrawingModel_ANPGetValue: {
+ LOG("get supported drawing model");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case kJavaContext_ANPGetValue: {
+ LOG("get java context");
+ auto ret = java::GeckoAppShell::GetContext();
+ if (!ret)
+ return NPERR_GENERIC_ERROR;
+
+ *static_cast<jobject*>(result) = ret.Forget();
+ return NPERR_NO_ERROR;
+ }
+
+ case kAudioTrackInterfaceV1_ANPGetValue: {
+ LOG("get audio interface v1");
+ ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result;
+ InitAudioTrackInterfaceV1(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kNativeWindowInterfaceV0_ANPGetValue: {
+ LOG("get native window interface v0");
+ ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result;
+ InitNativeWindowInterface(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kOpenGLInterfaceV0_ANPGetValue: {
+ LOG("get openGL interface");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case kWindowInterfaceV1_ANPGetValue: {
+ LOG("get Window interface V1");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case kWindowInterfaceV2_ANPGetValue: {
+ LOG("get Window interface V2");
+ ANPWindowInterfaceV2 *i = (ANPWindowInterfaceV2 *) result;
+ InitWindowInterfaceV2(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kVideoInterfaceV0_ANPGetValue: {
+ LOG("get video interface V0");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case kVideoInterfaceV1_ANPGetValue: {
+ LOG("get video interface V1");
+ ANPVideoInterfaceV1 *i = (ANPVideoInterfaceV1*) result;
+ InitVideoInterfaceV1(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kSystemInterfaceV1_ANPGetValue: {
+ LOG("get system interface v1");
+ ANPSystemInterfaceV1* i = reinterpret_cast<ANPSystemInterfaceV1*>(result);
+ InitSystemInterfaceV1(i);
+ return NPERR_NO_ERROR;
+ }
+
+ case kSystemInterfaceV2_ANPGetValue: {
+ LOG("get system interface v2");
+ ANPSystemInterfaceV2* i = reinterpret_cast<ANPSystemInterfaceV2*>(result);
+ InitSystemInterfaceV2(i);
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ // we no longer hand out any XPCOM objects
+ case NPNVDOMElement:
+ case NPNVDOMWindow:
+ case NPNVserviceManager:
+ // old XPCOM objects, no longer supported, but null out the out
+ // param to avoid crashing plugins that still try to use this.
+ *(nsISupports**)result = nullptr;
+ MOZ_FALLTHROUGH;
+
+ default:
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_getvalue unhandled get value: %d\n", variable));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+NPError
+_setvalue(NPP npp, NPPVariable variable, void *result)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_setvalue called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_SetValue: npp=%p, var=%d\n",
+ (void*)npp, (int)variable));
+
+ if (!npp)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *) npp->ndata;
+
+ NS_ASSERTION(inst, "null instance");
+
+ if (!inst)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ PluginDestructionGuard guard(inst);
+
+ // Cast NPNVariable enum to int to avoid warnings about including switch
+ // cases for android_npapi.h's non-standard ANPInterface values.
+ switch (static_cast<int>(variable)) {
+
+ // we should keep backward compatibility with NPAPI where the
+ // actual pointer value is checked rather than its content
+ // when passing booleans
+ case NPPVpluginWindowBool: {
+#ifdef XP_MACOSX
+ // This setting doesn't apply to OS X (only to Windows and Unix/Linux).
+ // See https://developer.mozilla.org/En/NPN_SetValue#section_5. Return
+ // NPERR_NO_ERROR here to conform to other browsers' behavior on OS X
+ // (e.g. Safari and Opera).
+ return NPERR_NO_ERROR;
+#else
+ NPBool bWindowless = (result == nullptr);
+ return inst->SetWindowless(bWindowless);
+#endif
+ }
+ case NPPVpluginTransparentBool: {
+ NPBool bTransparent = (result != nullptr);
+ return inst->SetTransparent(bTransparent);
+ }
+
+ case NPPVjavascriptPushCallerBool: {
+ return NPERR_NO_ERROR;
+ }
+
+ case NPPVpluginKeepLibraryInMemory: {
+ NPBool bCached = (result != nullptr);
+ inst->SetCached(bCached);
+ return NPERR_NO_ERROR;
+ }
+
+ case NPPVpluginUsesDOMForCursorBool: {
+ bool useDOMForCursor = (result != nullptr);
+ return inst->SetUsesDOMForCursor(useDOMForCursor);
+ }
+
+ case NPPVpluginIsPlayingAudio: {
+ bool isMuted = !result;
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*) npp->ndata;
+ MOZ_ASSERT(inst);
+
+ if (isMuted && !inst->HasAudioChannelAgent()) {
+ return NPERR_NO_ERROR;
+ }
+
+ nsCOMPtr<nsIAudioChannelAgent> agent;
+ nsresult rv = inst->GetOrCreateAudioChannelAgent(getter_AddRefs(agent));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NPERR_NO_ERROR;
+ }
+
+ MOZ_ASSERT(agent);
+
+ if (isMuted) {
+ rv = agent->NotifyStoppedPlaying();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NPERR_NO_ERROR;
+ }
+ } else {
+
+ dom::AudioPlaybackConfig config;
+ rv = agent->NotifyStartedPlaying(&config,
+ dom::AudioChannelService::AudibleState::eAudible);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NPERR_NO_ERROR;
+ }
+
+ rv = inst->WindowVolumeChanged(config.mVolume, config.mMuted);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NPERR_NO_ERROR;
+ }
+
+ // Since we only support for muting now, the implementation of suspend
+ // is equal to muting. Therefore, if we have already muted the plugin,
+ // then we don't need to call WindowSuspendChanged() again.
+ if (config.mMuted) {
+ return NPERR_NO_ERROR;
+ }
+
+ rv = inst->WindowSuspendChanged(config.mSuspend);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NPERR_NO_ERROR;
+ }
+ }
+
+ return NPERR_NO_ERROR;
+ }
+
+#ifndef MOZ_WIDGET_ANDROID
+ // On android, their 'drawing model' uses the same constant!
+ case NPPVpluginDrawingModel: {
+ if (inst) {
+ inst->SetDrawingModel((NPDrawingModel)NS_PTR_TO_INT32(result));
+ return NPERR_NO_ERROR;
+ }
+ else {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+#endif
+
+#ifdef XP_MACOSX
+ case NPPVpluginEventModel: {
+ if (inst) {
+ inst->SetEventModel((NPEventModel)NS_PTR_TO_INT32(result));
+ return NPERR_NO_ERROR;
+ }
+ else {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+#endif
+#ifdef MOZ_WIDGET_ANDROID
+ case kRequestDrawingModel_ANPSetValue:
+ if (inst)
+ inst->SetANPDrawingModel(NS_PTR_TO_INT32(result));
+ return NPERR_NO_ERROR;
+ case kAcceptEvents_ANPSetValue:
+ return NPERR_NO_ERROR;
+#endif
+ default:
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+NPError
+_requestread(NPStream *pstream, NPByteRange *rangeList)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_requestread called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_RequestRead: stream=%p\n",
+ (void*)pstream));
+
+#ifdef PLUGIN_LOGGING
+ for(NPByteRange * range = rangeList; range != nullptr; range = range->next)
+ MOZ_LOG(nsPluginLogging::gNPNLog,PLUGIN_LOG_NOISY,
+ ("%i-%i", range->offset, range->offset + range->length - 1));
+
+ MOZ_LOG(nsPluginLogging::gNPNLog,PLUGIN_LOG_NOISY, ("\n\n"));
+ PR_LogFlush();
+#endif
+
+ if (!pstream || !rangeList || !pstream->ndata)
+ return NPERR_INVALID_PARAM;
+
+ nsNPAPIStreamWrapper* streamWrapper = static_cast<nsNPAPIStreamWrapper*>(pstream->ndata);
+ nsNPAPIPluginStreamListener* streamlistener = streamWrapper->GetStreamListener();
+ if (!streamlistener) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ int32_t streamtype = NP_NORMAL;
+
+ streamlistener->GetStreamType(&streamtype);
+
+ if (streamtype != NP_SEEK)
+ return NPERR_STREAM_NOT_SEEKABLE;
+
+ if (!streamlistener->mStreamListenerPeer)
+ return NPERR_GENERIC_ERROR;
+
+ nsresult rv = streamlistener->mStreamListenerPeer->RequestRead((NPByteRange *)rangeList);
+ if (NS_FAILED(rv))
+ return NPERR_GENERIC_ERROR;
+
+ return NPERR_NO_ERROR;
+}
+
+// Deprecated, only stubbed out
+void* /* OJI type: JRIEnv* */
+_getJavaEnv()
+{
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_GetJavaEnv\n"));
+ return nullptr;
+}
+
+const char *
+_useragent(NPP npp)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_useragent called from the wrong thread\n"));
+ return nullptr;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_UserAgent: npp=%p\n", (void*)npp));
+
+ nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
+ nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+ if (!pluginHost) {
+ return nullptr;
+ }
+
+ const char *retstr;
+ nsresult rv = pluginHost->UserAgent(&retstr);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ return retstr;
+}
+
+void *
+_memalloc (uint32_t size)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,("NPN_memalloc called from the wrong thread\n"));
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPN_MemAlloc: size=%d\n", size));
+ return moz_xmalloc(size);
+}
+
+// Deprecated, only stubbed out
+void* /* OJI type: jref */
+_getJavaPeer(NPP npp)
+{
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_GetJavaPeer: npp=%p\n", (void*)npp));
+ return nullptr;
+}
+
+void
+_pushpopupsenabledstate(NPP npp, NPBool enabled)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_pushpopupsenabledstate called from the wrong thread\n"));
+ return;
+ }
+ nsNPAPIPluginInstance *inst = npp ? (nsNPAPIPluginInstance *)npp->ndata : nullptr;
+ if (!inst)
+ return;
+
+ inst->PushPopupsEnabledState(enabled);
+}
+
+void
+_poppopupsenabledstate(NPP npp)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_poppopupsenabledstate called from the wrong thread\n"));
+ return;
+ }
+ nsNPAPIPluginInstance *inst = npp ? (nsNPAPIPluginInstance *)npp->ndata : nullptr;
+ if (!inst)
+ return;
+
+ inst->PopPopupsEnabledState();
+}
+
+void
+_pluginthreadasynccall(NPP instance, PluginThreadCallback func, void *userData)
+{
+ if (NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,("NPN_pluginthreadasynccall called from the main thread\n"));
+ } else {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,("NPN_pluginthreadasynccall called from a non main thread\n"));
+ }
+ RefPtr<nsPluginThreadRunnable> evt =
+ new nsPluginThreadRunnable(instance, func, userData);
+
+ if (evt && evt->IsValid()) {
+ NS_DispatchToMainThread(evt);
+ }
+}
+
+NPError
+_getvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
+ char **value, uint32_t *len)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getvalueforurl called from the wrong thread\n"));
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (!instance) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ if (!url || !*url || !len) {
+ return NPERR_INVALID_URL;
+ }
+
+ *len = 0;
+
+ switch (variable) {
+ case NPNURLVProxy:
+ {
+ nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
+ nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+ if (pluginHost && NS_SUCCEEDED(pluginHost->FindProxyForURL(url, value))) {
+ *len = *value ? strlen(*value) : 0;
+ return NPERR_NO_ERROR;
+ }
+ break;
+ }
+ case NPNURLVCookie:
+ {
+ nsCOMPtr<nsICookieService> cookieService =
+ do_GetService(NS_COOKIESERVICE_CONTRACTID);
+
+ if (!cookieService)
+ return NPERR_GENERIC_ERROR;
+
+ // Make an nsURI from the url argument
+ nsCOMPtr<nsIURI> uri;
+ if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), nsDependentCString(url)))) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ nsCOMPtr<nsIChannel> channel = GetChannelFromNPP(instance);
+
+ if (NS_FAILED(cookieService->GetCookieString(uri, channel, value)) ||
+ !*value) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ *len = strlen(*value);
+ return NPERR_NO_ERROR;
+ }
+
+ default:
+ // Fall through and return an error...
+ ;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError
+_setvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
+ const char *value, uint32_t len)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_setvalueforurl called from the wrong thread\n"));
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (!instance) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ if (!url || !*url) {
+ return NPERR_INVALID_URL;
+ }
+
+ switch (variable) {
+ case NPNURLVCookie:
+ {
+ if (!value || 0 == len)
+ return NPERR_INVALID_PARAM;
+
+ nsresult rv = NS_ERROR_FAILURE;
+ nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return NPERR_GENERIC_ERROR;
+
+ nsCOMPtr<nsICookieService> cookieService = do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return NPERR_GENERIC_ERROR;
+
+ nsCOMPtr<nsIURI> uriIn;
+ rv = ioService->NewURI(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(uriIn));
+ if (NS_FAILED(rv))
+ return NPERR_GENERIC_ERROR;
+
+ nsCOMPtr<nsIChannel> channel = GetChannelFromNPP(instance);
+
+ char *cookie = (char*)value;
+ char c = cookie[len];
+ cookie[len] = '\0';
+ rv = cookieService->SetCookieString(uriIn, nullptr, cookie, channel);
+ cookie[len] = c;
+ if (NS_SUCCEEDED(rv))
+ return NPERR_NO_ERROR;
+ }
+
+ break;
+ case NPNURLVProxy:
+ // We don't support setting proxy values, fall through...
+ default:
+ // Fall through and return an error...
+ ;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError
+_getauthenticationinfo(NPP instance, const char *protocol, const char *host,
+ int32_t port, const char *scheme, const char *realm,
+ char **username, uint32_t *ulen, char **password,
+ uint32_t *plen)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getauthenticationinfo called from the wrong thread\n"));
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (!instance || !protocol || !host || !scheme || !realm || !username ||
+ !ulen || !password || !plen)
+ return NPERR_INVALID_PARAM;
+
+ *username = nullptr;
+ *password = nullptr;
+ *ulen = 0;
+ *plen = 0;
+
+ nsDependentCString proto(protocol);
+
+ if (!proto.LowerCaseEqualsLiteral("http") &&
+ !proto.LowerCaseEqualsLiteral("https"))
+ return NPERR_GENERIC_ERROR;
+
+ nsCOMPtr<nsIHttpAuthManager> authManager =
+ do_GetService("@mozilla.org/network/http-auth-manager;1");
+ if (!authManager)
+ return NPERR_GENERIC_ERROR;
+
+ nsNPAPIPluginInstance *inst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+ if (!inst)
+ return NPERR_GENERIC_ERROR;
+
+ bool authPrivate = false;
+ if (NS_FAILED(inst->IsPrivateBrowsing(&authPrivate)))
+ return NPERR_GENERIC_ERROR;
+
+ nsIDocument *doc = GetDocumentFromNPP(instance);
+ NS_ENSURE_TRUE(doc, NPERR_GENERIC_ERROR);
+ nsIPrincipal *principal = doc->NodePrincipal();
+
+ nsAutoString unused, uname16, pwd16;
+ if (NS_FAILED(authManager->GetAuthIdentity(proto, nsDependentCString(host),
+ port, nsDependentCString(scheme),
+ nsDependentCString(realm),
+ EmptyCString(), unused, uname16,
+ pwd16, authPrivate, principal))) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ NS_ConvertUTF16toUTF8 uname8(uname16);
+ NS_ConvertUTF16toUTF8 pwd8(pwd16);
+
+ *username = ToNewCString(uname8);
+ *ulen = *username ? uname8.Length() : 0;
+
+ *password = ToNewCString(pwd8);
+ *plen = *password ? pwd8.Length() : 0;
+
+ return NPERR_NO_ERROR;
+}
+
+uint32_t
+_scheduletimer(NPP instance, uint32_t interval, NPBool repeat, PluginTimerFunc timerFunc)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_scheduletimer called from the wrong thread\n"));
+ return 0;
+ }
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+ if (!inst)
+ return 0;
+
+ return inst->ScheduleTimer(interval, repeat, timerFunc);
+}
+
+void
+_unscheduletimer(NPP instance, uint32_t timerID)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_unscheduletimer called from the wrong thread\n"));
+ return;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ // Sometimes Flash calls this with a dead NPP instance. Ensure the one we have
+ // here is valid and maps to a nsNPAPIPluginInstance.
+ nsNPAPIPluginInstance *inst = nsNPAPIPluginInstance::GetFromNPP(instance);
+#else
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+#endif
+ if (!inst)
+ return;
+
+ inst->UnscheduleTimer(timerID);
+}
+
+NPError
+_popupcontextmenu(NPP instance, NPMenu* menu)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_popupcontextmenu called from the wrong thread\n"));
+ return 0;
+ }
+
+#ifdef MOZ_WIDGET_COCOA
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+
+ double pluginX, pluginY;
+ double screenX, screenY;
+
+ const NPCocoaEvent* currentEvent = static_cast<NPCocoaEvent*>(inst->GetCurrentEvent());
+ if (!currentEvent) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // Ensure that the events has an x/y value.
+ if (currentEvent->type != NPCocoaEventMouseDown &&
+ currentEvent->type != NPCocoaEventMouseUp &&
+ currentEvent->type != NPCocoaEventMouseMoved &&
+ currentEvent->type != NPCocoaEventMouseEntered &&
+ currentEvent->type != NPCocoaEventMouseExited &&
+ currentEvent->type != NPCocoaEventMouseDragged) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ pluginX = currentEvent->data.mouse.pluginX;
+ pluginY = currentEvent->data.mouse.pluginY;
+
+ if ((pluginX < 0.0) || (pluginY < 0.0))
+ return NPERR_GENERIC_ERROR;
+
+ NPBool success = _convertpoint(instance,
+ pluginX, pluginY, NPCoordinateSpacePlugin,
+ &screenX, &screenY, NPCoordinateSpaceScreen);
+
+ if (success) {
+ return mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(menu,
+ screenX, screenY,
+ nullptr,
+ nullptr);
+ } else {
+ NS_WARNING("Convertpoint failed, could not created contextmenu.");
+ return NPERR_GENERIC_ERROR;
+ }
+#else
+ NS_WARNING("Not supported on this platform!");
+ return NPERR_GENERIC_ERROR;
+#endif
+}
+
+NPBool
+_convertpoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_convertpoint called from the wrong thread\n"));
+ return 0;
+ }
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+ if (!inst)
+ return false;
+
+ return inst->ConvertPoint(sourceX, sourceY, sourceSpace, destX, destY, destSpace);
+}
+
+void
+_urlredirectresponse(NPP instance, void* notifyData, NPBool allow)
+{
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_convertpoint called from the wrong thread\n"));
+ return;
+ }
+
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+ if (!inst) {
+ return;
+ }
+
+ inst->URLRedirectResponse(notifyData, allow);
+}
+
+NPError
+_initasyncsurface(NPP instance, NPSize *size, NPImageFormat format, void *initData, NPAsyncSurface *surface)
+{
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+ if (!inst) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ return inst->InitAsyncSurface(size, format, initData, surface);
+}
+
+NPError
+_finalizeasyncsurface(NPP instance, NPAsyncSurface *surface)
+{
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+ if (!inst) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ return inst->FinalizeAsyncSurface(surface);
+}
+
+void
+_setcurrentasyncsurface(NPP instance, NPAsyncSurface *surface, NPRect *changed)
+{
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
+ if (!inst) {
+ return;
+ }
+
+ inst->SetCurrentAsyncSurface(surface, changed);
+}
+
+} /* namespace parent */
+} /* namespace plugins */
+} /* namespace mozilla */
diff --git a/dom/plugins/base/nsNPAPIPlugin.h b/dom/plugins/base/nsNPAPIPlugin.h
new file mode 100644
index 000000000..96e630b62
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPlugin.h
@@ -0,0 +1,414 @@
+/* -*- 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/. */
+
+#ifndef nsNPAPIPlugin_h_
+#define nsNPAPIPlugin_h_
+
+#include "prlink.h"
+#include "npfunctions.h"
+#include "nsPluginHost.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/PluginLibrary.h"
+#include "mozilla/RefCounted.h"
+
+#if defined(XP_WIN)
+#define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (__stdcall * _name)
+#else
+#define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (* _name)
+#endif
+
+typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_GETENTRYPOINTS) (NPPluginFuncs* pCallbacks);
+typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGININIT) (const NPNetscapeFuncs* pCallbacks);
+typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT) (const NPNetscapeFuncs* pCallbacks, NPPluginFuncs* fCallbacks);
+typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) ();
+
+// nsNPAPIPlugin is held alive both by active nsPluginTag instances and
+// by active nsNPAPIPluginInstance.
+class nsNPAPIPlugin final
+{
+private:
+ typedef mozilla::PluginLibrary PluginLibrary;
+
+public:
+ nsNPAPIPlugin();
+
+ NS_INLINE_DECL_REFCOUNTING(nsNPAPIPlugin)
+
+ // Constructs and initializes an nsNPAPIPlugin object. A nullptr file path
+ // will prevent this from calling NP_Initialize.
+ static nsresult CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult);
+
+ PluginLibrary* GetLibrary();
+ // PluginFuncs() can't fail but results are only valid if GetLibrary() succeeds
+ NPPluginFuncs* PluginFuncs();
+
+#if defined(XP_MACOSX) && !defined(__LP64__)
+ void SetPluginRefNum(short aRefNum);
+#endif
+
+ // The IPC mechanism notifies the nsNPAPIPlugin if the plugin
+ // crashes and is no longer usable. pluginDumpID/browserDumpID are
+ // the IDs of respective minidumps that were written, or empty if no
+ // minidump was written.
+ void PluginCrashed(const nsAString& pluginDumpID,
+ const nsAString& browserDumpID);
+
+ static bool RunPluginOOP(const nsPluginTag *aPluginTag);
+
+ nsresult Shutdown();
+
+ static nsresult RetainStream(NPStream *pstream, nsISupports **aRetainedPeer);
+
+private:
+ ~nsNPAPIPlugin();
+
+ NPPluginFuncs mPluginFuncs;
+ PluginLibrary* mLibrary;
+};
+
+namespace mozilla {
+namespace plugins {
+namespace parent {
+
+static_assert(sizeof(NPIdentifier) == sizeof(jsid),
+ "NPIdentifier must be binary compatible with jsid.");
+
+inline jsid
+NPIdentifierToJSId(NPIdentifier id)
+{
+ jsid tmp;
+ JSID_BITS(tmp) = (size_t)id;
+ return tmp;
+}
+
+inline NPIdentifier
+JSIdToNPIdentifier(jsid id)
+{
+ return (NPIdentifier)JSID_BITS(id);
+}
+
+inline bool
+NPIdentifierIsString(NPIdentifier id)
+{
+ return JSID_IS_STRING(NPIdentifierToJSId(id));
+}
+
+inline JSString *
+NPIdentifierToString(NPIdentifier id)
+{
+ return JSID_TO_STRING(NPIdentifierToJSId(id));
+}
+
+inline NPIdentifier
+StringToNPIdentifier(JSContext *cx, JSString *str)
+{
+ return JSIdToNPIdentifier(INTERNED_STRING_TO_JSID(cx, str));
+}
+
+inline bool
+NPIdentifierIsInt(NPIdentifier id)
+{
+ return JSID_IS_INT(NPIdentifierToJSId(id));
+}
+
+inline int
+NPIdentifierToInt(NPIdentifier id)
+{
+ return JSID_TO_INT(NPIdentifierToJSId(id));
+}
+
+inline NPIdentifier
+IntToNPIdentifier(int i)
+{
+ return JSIdToNPIdentifier(INT_TO_JSID(i));
+}
+
+JSContext* GetJSContext(NPP npp);
+
+inline bool
+NPStringIdentifierIsPermanent(NPIdentifier id)
+{
+ AutoSafeJSContext cx;
+ return JS_StringHasBeenPinned(cx, NPIdentifierToString(id));
+}
+
+#define NPIdentifier_VOID (JSIdToNPIdentifier(JSID_VOID))
+
+NPObject*
+_getwindowobject(NPP npp);
+
+NPObject*
+_getpluginelement(NPP npp);
+
+NPIdentifier
+_getstringidentifier(const NPUTF8* name);
+
+void
+_getstringidentifiers(const NPUTF8** names, int32_t nameCount,
+ NPIdentifier *identifiers);
+
+bool
+_identifierisstring(NPIdentifier identifiers);
+
+NPIdentifier
+_getintidentifier(int32_t intid);
+
+NPUTF8*
+_utf8fromidentifier(NPIdentifier identifier);
+
+int32_t
+_intfromidentifier(NPIdentifier identifier);
+
+NPObject*
+_createobject(NPP npp, NPClass* aClass);
+
+NPObject*
+_retainobject(NPObject* npobj);
+
+void
+_releaseobject(NPObject* npobj);
+
+bool
+_invoke(NPP npp, NPObject* npobj, NPIdentifier method, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+
+bool
+_invokeDefault(NPP npp, NPObject* npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+
+bool
+_evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result);
+
+bool
+_getproperty(NPP npp, NPObject* npobj, NPIdentifier property,
+ NPVariant *result);
+
+bool
+_setproperty(NPP npp, NPObject* npobj, NPIdentifier property,
+ const NPVariant *value);
+
+bool
+_removeproperty(NPP npp, NPObject* npobj, NPIdentifier property);
+
+bool
+_hasproperty(NPP npp, NPObject* npobj, NPIdentifier propertyName);
+
+bool
+_hasmethod(NPP npp, NPObject* npobj, NPIdentifier methodName);
+
+bool
+_enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier,
+ uint32_t *count);
+
+bool
+_construct(NPP npp, NPObject* npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+
+void
+_releasevariantvalue(NPVariant *variant);
+
+void
+_setexception(NPObject* npobj, const NPUTF8 *message);
+
+void
+_pushpopupsenabledstate(NPP npp, NPBool enabled);
+
+void
+_poppopupsenabledstate(NPP npp);
+
+typedef void(*PluginThreadCallback)(void *);
+
+void
+_pluginthreadasynccall(NPP instance, PluginThreadCallback func,
+ void *userData);
+
+NPError
+_getvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
+ char **value, uint32_t *len);
+
+NPError
+_setvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
+ const char *value, uint32_t len);
+
+NPError
+_getauthenticationinfo(NPP instance, const char *protocol, const char *host,
+ int32_t port, const char *scheme, const char *realm,
+ char **username, uint32_t *ulen, char **password,
+ uint32_t *plen);
+
+typedef void(*PluginTimerFunc)(NPP npp, uint32_t timerID);
+
+uint32_t
+_scheduletimer(NPP instance, uint32_t interval, NPBool repeat, PluginTimerFunc timerFunc);
+
+void
+_unscheduletimer(NPP instance, uint32_t timerID);
+
+NPError
+_popupcontextmenu(NPP instance, NPMenu* menu);
+
+NPBool
+_convertpoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
+
+NPError
+_requestread(NPStream *pstream, NPByteRange *rangeList);
+
+NPError
+_geturlnotify(NPP npp, const char* relativeURL, const char* target,
+ void* notifyData);
+
+NPError
+_getvalue(NPP npp, NPNVariable variable, void *r_value);
+
+NPError
+_setvalue(NPP npp, NPPVariable variable, void *r_value);
+
+NPError
+_geturl(NPP npp, const char* relativeURL, const char* target);
+
+NPError
+_posturlnotify(NPP npp, const char* relativeURL, const char *target,
+ uint32_t len, const char *buf, NPBool file, void* notifyData);
+
+NPError
+_posturl(NPP npp, const char* relativeURL, const char *target, uint32_t len,
+ const char *buf, NPBool file);
+
+NPError
+_newstream(NPP npp, NPMIMEType type, const char* window, NPStream** pstream);
+
+int32_t
+_write(NPP npp, NPStream *pstream, int32_t len, void *buffer);
+
+NPError
+_destroystream(NPP npp, NPStream *pstream, NPError reason);
+
+void
+_status(NPP npp, const char *message);
+
+void
+_memfree (void *ptr);
+
+uint32_t
+_memflush(uint32_t size);
+
+void
+_reloadplugins(NPBool reloadPages);
+
+void
+_invalidaterect(NPP npp, NPRect *invalidRect);
+
+void
+_invalidateregion(NPP npp, NPRegion invalidRegion);
+
+void
+_forceredraw(NPP npp);
+
+const char*
+_useragent(NPP npp);
+
+void*
+_memalloc (uint32_t size);
+
+// Deprecated entry points for the old Java plugin.
+void* /* OJI type: JRIEnv* */
+_getJavaEnv();
+
+void* /* OJI type: jref */
+_getJavaPeer(NPP npp);
+
+void
+_urlredirectresponse(NPP instance, void* notifyData, NPBool allow);
+
+NPError
+_initasyncsurface(NPP instance, NPSize *size, NPImageFormat format, void *initData, NPAsyncSurface *surface);
+
+NPError
+_finalizeasyncsurface(NPP instance, NPAsyncSurface *surface);
+
+void
+_setcurrentasyncsurface(NPP instance, NPAsyncSurface *surface, NPRect *changed);
+
+} /* namespace parent */
+} /* namespace plugins */
+} /* namespace mozilla */
+
+const char *
+PeekException();
+
+void
+PopException();
+
+void
+OnPluginDestroy(NPP instance);
+
+void
+OnShutdown();
+
+/**
+ * within a lexical scope, locks and unlocks the mutex used to
+ * serialize modifications to plugin async callback state.
+ */
+struct MOZ_STACK_CLASS AsyncCallbackAutoLock
+{
+ AsyncCallbackAutoLock();
+ ~AsyncCallbackAutoLock();
+};
+
+class NPPStack
+{
+public:
+ static NPP Peek()
+ {
+ return sCurrentNPP;
+ }
+
+protected:
+ static NPP sCurrentNPP;
+};
+
+// XXXjst: The NPPAutoPusher stack is a bit redundant now that
+// PluginDestructionGuard exists, and could thus be replaced by code
+// that uses the PluginDestructionGuard list of plugins on the
+// stack. But they're not identical, and to minimize code changes
+// we're keeping both for the moment, and making NPPAutoPusher inherit
+// the PluginDestructionGuard class to avoid having to keep two
+// separate objects on the stack since we always want a
+// PluginDestructionGuard where we use an NPPAutoPusher.
+
+class MOZ_STACK_CLASS NPPAutoPusher : public NPPStack,
+ protected PluginDestructionGuard
+{
+public:
+ explicit NPPAutoPusher(NPP aNpp)
+ : PluginDestructionGuard(aNpp),
+ mOldNPP(sCurrentNPP)
+ {
+ NS_ASSERTION(aNpp, "Uh, null aNpp passed to NPPAutoPusher!");
+
+ sCurrentNPP = aNpp;
+ }
+
+ ~NPPAutoPusher()
+ {
+ sCurrentNPP = mOldNPP;
+ }
+
+private:
+ NPP mOldNPP;
+};
+
+class NPPExceptionAutoHolder
+{
+public:
+ NPPExceptionAutoHolder();
+ ~NPPExceptionAutoHolder();
+
+protected:
+ char *mOldException;
+};
+
+#endif // nsNPAPIPlugin_h_
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp
new file mode 100644
index 000000000..4b211ed7e
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -0,0 +1,1861 @@
+/* -*- 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 "mozilla/Logging.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"
+#include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "AudioChannelService.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "ANPBase.h"
+#include <android/log.h>
+#include "android_npapi.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/dom/ScreenOrientation.h"
+#include "mozilla/Hal.h"
+#include "GLContextProvider.h"
+#include "GLContext.h"
+#include "TexturePoolOGL.h"
+#include "SurfaceTypes.h"
+#include "EGLUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::gl;
+
+typedef nsNPAPIPluginInstance::VideoInfo VideoInfo;
+
+class PluginEventRunnable : public Runnable
+{
+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 RefPtr<GLContext> sPluginContext = nullptr;
+
+static bool EnsureGLContext()
+{
+ if (!sPluginContext) {
+ const auto flags = CreateContextFlags::REQUIRE_COMPAT_PROFILE;
+ nsCString discardedFailureId;
+ sPluginContext = GLContextProvider::CreateHeadless(flags, &discardedFailureId);
+ }
+
+ return sPluginContext != nullptr;
+}
+
+static std::map<NPP, nsNPAPIPluginInstance*> sPluginNPPMap;
+
+#endif
+
+using namespace mozilla;
+using namespace mozilla::plugins::parent;
+using namespace mozilla::layers;
+
+static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID);
+
+NS_IMPL_ISUPPORTS(nsNPAPIPluginInstance, nsIAudioChannelAgentCallback)
+
+nsNPAPIPluginInstance::nsNPAPIPluginInstance()
+ : mDrawingModel(kDefaultDrawingModel)
+#ifdef MOZ_WIDGET_ANDROID
+ , mANPDrawingModel(0)
+ , mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary)
+ , mWakeLocked(false)
+ , mFullScreen(false)
+ , mOriginPos(gl::OriginPos::TopLeft)
+#endif
+ , 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
+#ifdef MOZ_WIDGET_ANDROID
+ , mOnScreen(true)
+#endif
+ , mHaveJavaC2PJSObjectQuirk(false)
+ , mCachedParamLength(0)
+ , mCachedParamNames(nullptr)
+ , mCachedParamValues(nullptr)
+ , mMuted(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;
+ }
+
+ 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;
+
+#if MOZ_WIDGET_ANDROID
+ if (mContentSurface)
+ mContentSurface->SetFrameAvailableCallback(nullptr);
+
+ mContentSurface = nullptr;
+
+ std::map<void*, VideoInfo*>::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 nsACString& aMIMEType)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::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) {
+ nsCOMPtr<nsPIDOMWindowOuter> 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) {
+ 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;
+
+#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<nsPIDOMWindowOuter>
+nsNPAPIPluginInstance::GetDOMWindow()
+{
+ if (!mOwner)
+ return nullptr;
+
+ RefPtr<nsPluginInstanceOwner> kungFuDeathGrip(mOwner);
+
+ nsCOMPtr<nsIDocument> 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);
+}
+
+nsresult
+nsNPAPIPluginInstance::GetMode(int32_t *result)
+{
+ if (mOwner)
+ return mOwner->GetMode(result);
+ else
+ return NS_ERROR_FAILURE;
+}
+
+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);
+ }
+
+ // Android expects and empty string instead of null.
+ mCachedParamNames[attributes.Length()] = ToNewUTF8String(NS_LITERAL_STRING("PARAM"));
+ #ifdef MOZ_WIDGET_ANDROID
+ mCachedParamValues[attributes.Length()] = ToNewUTF8String(NS_LITERAL_STRING(""));
+ #else
+ mCachedParamValues[attributes.Length()] = nullptr;
+ #endif
+
+ 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++;
+ }
+
+ int32_t mode;
+ const char* mimetype;
+ NPError error = NPERR_GENERIC_ERROR;
+
+ GetMode(&mode);
+ GetMIMEType(&mimetype);
+
+ CheckJavaC2PJSObjectQuirk(quirkParamLength, mCachedParamNames, mCachedParamValues);
+
+ 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, (uint16_t)mode,
+ quirkParamLength, mCachedParamNames,
+ mCachedParamValues, 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, 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, but Java needs wants this call.
+ if (window && window->type == NPWindowTypeWindow &&
+ (window->width <= 0 || window->height <= 0) &&
+ (nsPluginHost::GetSpecialType(nsDependentCString(mMIMEType)) !=
+ nsPluginHost::eSpecialType_Java)) {
+ 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::NewStreamFromPlugin(const char* type, const char* target,
+ nsIOutputStream* *result)
+{
+ nsPluginStreamToFile* stream = new nsPluginStreamToFile(target, mOwner);
+ return stream->QueryInterface(kIOutputStreamIID, (void**)result);
+}
+
+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;
+
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::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
+ 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;
+#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=%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.
+ if (nsPluginHost::GetSpecialType(nsDependentCString(mMIMEType)) ==
+ nsPluginHost::eSpecialType_Silverlight) {
+ 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) {
+ java::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(RefPtr<PluginEventRunnable>(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) {
+ java::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation);
+ } else if (oldOrientation != dom::eScreenOrientation_None) {
+ // We applied an orientation when we entered fullscreen, but
+ // we don't want it anymore
+ java::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("screen"),
+ mWakeLocked ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE,
+ hal::WAKE_LOCK_NO_CHANGE);
+}
+
+GLContext* nsNPAPIPluginInstance::GLContext()
+{
+ if (!EnsureGLContext())
+ return nullptr;
+
+ return sPluginContext;
+}
+
+already_AddRefed<AndroidSurfaceTexture> nsNPAPIPluginInstance::CreateSurfaceTexture()
+{
+ if (!EnsureGLContext())
+ return nullptr;
+
+ GLuint texture = TexturePoolOGL::AcquireTexture();
+ if (!texture)
+ return nullptr;
+
+ RefPtr<AndroidSurfaceTexture> surface = AndroidSurfaceTexture::Create(TexturePoolOGL::GetGLContext(),
+ texture);
+ if (!surface) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIRunnable> frameCallback = NewRunnableMethod(this, &nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable);
+ surface->SetFrameAvailableCallback(frameCallback);
+ return surface.forget();
+}
+
+void nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable()
+{
+ if (mRunning == RUNNING && mOwner)
+ mOwner->Recomposite();
+}
+
+void* nsNPAPIPluginInstance::AcquireContentWindow()
+{
+ if (!mContentSurface) {
+ mContentSurface = CreateSurfaceTexture();
+
+ if (!mContentSurface)
+ return nullptr;
+ }
+
+ return mContentSurface->NativeWindow();
+}
+
+AndroidSurfaceTexture*
+nsNPAPIPluginInstance::AsSurfaceTexture()
+{
+ if (!mContentSurface)
+ return nullptr;
+
+ return mContentSurface;
+}
+
+void* nsNPAPIPluginInstance::AcquireVideoWindow()
+{
+ RefPtr<AndroidSurfaceTexture> surface = CreateSurfaceTexture();
+ if (!surface) {
+ return nullptr;
+ }
+
+ VideoInfo* info = new VideoInfo(surface);
+
+ void* window = info->mSurfaceTexture->NativeWindow();
+ mVideos.insert(std::pair<void*, VideoInfo*>(window, info));
+
+ return window;
+}
+
+void nsNPAPIPluginInstance::ReleaseVideoWindow(void* window)
+{
+ std::map<void*, VideoInfo*>::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<void*, VideoInfo*>::iterator it;
+
+ it = mVideos.find(window);
+ if (it == mVideos.end())
+ return;
+
+ it->second->mDimensions = aDimensions;
+}
+
+void nsNPAPIPluginInstance::GetVideos(nsTArray<VideoInfo*>& aVideos)
+{
+ std::map<void*, VideoInfo*>::iterator it;
+ for (it = mVideos.begin(); it != mVideos.end(); it++)
+ aVideos.AppendElement(it->second);
+}
+
+nsNPAPIPluginInstance* nsNPAPIPluginInstance::GetFromNPP(NPP npp)
+{
+ std::map<NPP, nsNPAPIPluginInstance*>::iterator it;
+
+ it = sPluginNPPMap.find(npp);
+ if (it == sPluginNPPMap.end())
+ return nullptr;
+
+ return it->second;
+}
+
+#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)
+{
+ 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)
+{
+#if defined(MOZ_WIDGET_ANDROID) || defined(XP_MACOSX)
+ // All OS X plugins are windowless.
+ // 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:
+ 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)
+{
+ 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,
+ 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(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;
+
+ 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<nsPIDOMWindowOuter> 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<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<nsIDocument> 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));
+
+ 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<nsITimer> 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;
+}
+
+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;
+}
+
+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);
+ }
+}
+
+class CarbonEventModelFailureEvent : public Runnable {
+public:
+ nsCOMPtr<nsIContent> mContent;
+
+ explicit 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->GetComposedDoc(), mContent,
+ type, true, true);
+ return NS_OK;
+}
+
+void
+nsNPAPIPluginInstance::CarbonNPAPIFailure()
+{
+ nsCOMPtr<nsIDOMElement> element;
+ GetDOMElement(getter_AddRefs(element));
+ if (!element) {
+ return;
+ }
+
+ nsCOMPtr<nsIContent> content(do_QueryInterface(element));
+ if (!content) {
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> 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->MimeTypes().Length(); ++i) {
+ nsCString type = pluginTag->MimeTypes()[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;
+ }
+
+ RefPtr<nsPluginHost> 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;
+}
+
+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::GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent)
+{
+ if (!mAudioChannelAgent) {
+ nsresult rv;
+ mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
+ if (NS_WARN_IF(!mAudioChannelAgent)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow();
+ if (NS_WARN_IF(!window)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = mAudioChannelAgent->Init(window->GetCurrentInnerWindow(),
+ (int32_t)AudioChannelService::GetDefaultAudioChannel(),
+ this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsIAudioChannelAgent> agent = mAudioChannelAgent;
+ agent.forget(aAgent);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowVolumeChanged(float aVolume, bool aMuted)
+{
+ // We just support mute/unmute
+ nsresult rv = SetMuted(aMuted);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetMuted failed");
+ if (mMuted != aMuted) {
+ mMuted = aMuted;
+ AudioChannelService::AudibleState audible = aMuted ?
+ AudioChannelService::AudibleState::eNotAudible :
+ AudioChannelService::AudibleState::eAudible;
+ mAudioChannelAgent->NotifyStartedAudible(audible,
+ AudioChannelService::AudibleChangedReasons::eVolumeChanged);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowSuspendChanged(nsSuspendedTypes aSuspend)
+{
+ // It doesn't support suspended, so we just do something like mute/unmute.
+ WindowVolumeChanged(1.0, /* useless */
+ aSuspend != nsISuspendedTypes::NONE_SUSPENDED);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowAudioCaptureChanged(bool aCapture)
+{
+ return NS_OK;
+}
+
+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;
+}
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h
new file mode 100644
index 000000000..5f0375637
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -0,0 +1,464 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef nsNPAPIPluginInstance_h_
+#define nsNPAPIPluginInstance_h_
+
+#include "nsSize.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsPIDOMWindow.h"
+#include "nsITimer.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsIURI.h"
+#include "nsIChannel.h"
+#include "nsHashKeys.h"
+#include <prinrval.h>
+#include "js/TypeDecls.h"
+#include "nsIAudioChannelAgent.h"
+#ifdef MOZ_WIDGET_ANDROID
+#include "nsIRunnable.h"
+#include "GLContextTypes.h"
+#include "AndroidSurfaceTexture.h"
+#include "AndroidBridge.h"
+#include <map>
+class PluginEventRunnable;
+#endif
+
+#include "mozilla/EventForwards.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/PluginLibrary.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/WeakPtr.h"
+
+class nsPluginStreamListenerPeer; // browser-initiated stream class
+class nsNPAPIPluginStreamListener; // plugin-initiated stream class
+class nsIPluginInstanceOwner;
+class nsIOutputStream;
+class nsPluginInstanceOwner;
+
+#if defined(OS_WIN)
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelSyncWin;
+#elif defined(MOZ_X11)
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelSyncX;
+#elif defined(XP_MACOSX)
+#ifndef NP_NO_QUICKDRAW
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelQuickDraw; // Not supported
+#else
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelCoreGraphics;
+#endif
+#else
+const NPDrawingModel kDefaultDrawingModel = static_cast<NPDrawingModel>(0);
+#endif
+
+/**
+ * Used to indicate whether it's OK to reenter Gecko and repaint, flush frames,
+ * run scripts, etc, during this plugin call.
+ * When NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO is set, we try to avoid dangerous
+ * Gecko activities when the plugin spins a nested event loop, on a best-effort
+ * basis.
+ */
+enum NSPluginCallReentry {
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO
+};
+
+class nsNPAPITimer
+{
+public:
+ NPP npp;
+ uint32_t id;
+ nsCOMPtr<nsITimer> timer;
+ void (*callback)(NPP npp, uint32_t timerID);
+ bool inCallback;
+ bool needUnschedule;
+};
+
+class nsNPAPIPluginInstance final : public nsIAudioChannelAgentCallback
+ , public mozilla::SupportsWeakPtr<nsNPAPIPluginInstance>
+{
+private:
+ typedef mozilla::PluginLibrary PluginLibrary;
+
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsNPAPIPluginInstance)
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
+
+ nsresult Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const nsACString& aMIMEType);
+ nsresult Start();
+ nsresult Stop();
+ nsresult SetWindow(NPWindow* window);
+ nsresult NewStreamFromPlugin(const char* type, const char* target, nsIOutputStream* *result);
+ nsresult Print(NPPrint* platformPrint);
+ nsresult HandleEvent(void* event, int16_t* result,
+ NSPluginCallReentry aSafeToReenterGecko = NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ nsresult GetValueFromPlugin(NPPVariable variable, void* value);
+ nsresult GetDrawingModel(int32_t* aModel);
+ nsresult IsRemoteDrawingCoreAnimation(bool* aDrawing);
+ nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
+ nsresult CSSZoomFactorChanged(float aCSSZoomFactor);
+ nsresult GetJSObject(JSContext *cx, JSObject** outObject);
+ bool ShouldCache();
+ nsresult IsWindowless(bool* isWindowless);
+ nsresult AsyncSetWindow(NPWindow* window);
+ nsresult GetImageContainer(mozilla::layers::ImageContainer **aContainer);
+ nsresult GetImageSize(nsIntSize* aSize);
+ nsresult NotifyPainted(void);
+ nsresult GetIsOOP(bool* aIsOOP);
+ nsresult SetBackgroundUnknown();
+ nsresult BeginUpdateBackground(nsIntRect* aRect, DrawTarget** aContext);
+ nsresult EndUpdateBackground(nsIntRect* aRect);
+ nsresult IsTransparent(bool* isTransparent);
+ nsresult GetFormValue(nsAString& aValue);
+ nsresult PushPopupsEnabledState(bool aEnabled);
+ nsresult PopPopupsEnabledState();
+ nsresult GetPluginAPIVersion(uint16_t* version);
+ nsresult InvalidateRect(NPRect *invalidRect);
+ nsresult InvalidateRegion(NPRegion invalidRegion);
+ nsresult GetMIMEType(const char* *result);
+#if defined(XP_WIN)
+ nsresult GetScrollCaptureContainer(mozilla::layers::ImageContainer **aContainer);
+#endif
+ nsresult HandledWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData,
+ bool aIsConsumed);
+ nsPluginInstanceOwner* GetOwner();
+ void SetOwner(nsPluginInstanceOwner *aOwner);
+ void DidComposite();
+
+ bool HasAudioChannelAgent() const
+ {
+ return !!mAudioChannelAgent;
+ }
+
+ nsresult GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent);
+
+ nsresult SetMuted(bool aIsMuted);
+
+ nsNPAPIPlugin* GetPlugin();
+
+ nsresult GetNPP(NPP * aNPP);
+
+ NPError SetWindowless(bool aWindowless);
+
+ NPError SetTransparent(bool aTransparent);
+
+ NPError SetWantsAllNetworkStreams(bool aWantsAllNetworkStreams);
+
+ NPError SetUsesDOMForCursor(bool aUsesDOMForCursor);
+ bool UsesDOMForCursor();
+
+ void SetDrawingModel(NPDrawingModel aModel);
+ void RedrawPlugin();
+#ifdef XP_MACOSX
+ void SetEventModel(NPEventModel aModel);
+
+ void* GetCurrentEvent() {
+ return mCurrentPluginEvent;
+ }
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+ void NotifyForeground(bool aForeground);
+ void NotifyOnScreen(bool aOnScreen);
+ void MemoryPressure();
+ void NotifyFullScreen(bool aFullScreen);
+ void NotifySize(nsIntSize size);
+
+ nsIntSize CurrentSize() { return mCurrentSize; }
+
+ bool IsOnScreen() {
+ return mOnScreen;
+ }
+
+ uint32_t GetANPDrawingModel() { return mANPDrawingModel; }
+ void SetANPDrawingModel(uint32_t aModel);
+
+ void* GetJavaSurface();
+
+ void PostEvent(void* event);
+
+ // These are really mozilla::dom::ScreenOrientation, but it's
+ // difficult to include that here
+ uint32_t FullScreenOrientation() { return mFullScreenOrientation; }
+ void SetFullScreenOrientation(uint32_t orientation);
+
+ void SetWakeLock(bool aLock);
+
+ mozilla::gl::GLContext* GLContext();
+
+ // For ANPOpenGL
+ class TextureInfo {
+ public:
+ TextureInfo() :
+ mTexture(0), mWidth(0), mHeight(0), mInternalFormat(0)
+ {
+ }
+
+ TextureInfo(GLuint aTexture, int32_t aWidth, int32_t aHeight, GLuint aInternalFormat) :
+ mTexture(aTexture), mWidth(aWidth), mHeight(aHeight), mInternalFormat(aInternalFormat)
+ {
+ }
+
+ GLuint mTexture;
+ int32_t mWidth;
+ int32_t mHeight;
+ GLuint mInternalFormat;
+ };
+
+ // For ANPNativeWindow
+ void* AcquireContentWindow();
+
+ mozilla::gl::AndroidSurfaceTexture* AsSurfaceTexture();
+
+ // For ANPVideo
+ class VideoInfo {
+ public:
+ VideoInfo(mozilla::gl::AndroidSurfaceTexture* aSurfaceTexture) :
+ mSurfaceTexture(aSurfaceTexture)
+ {
+ }
+
+ ~VideoInfo()
+ {
+ mSurfaceTexture = nullptr;
+ }
+
+ RefPtr<mozilla::gl::AndroidSurfaceTexture> mSurfaceTexture;
+ gfxRect mDimensions;
+ };
+
+ void* AcquireVideoWindow();
+ void ReleaseVideoWindow(void* aWindow);
+ void SetVideoDimensions(void* aWindow, gfxRect aDimensions);
+
+ void GetVideos(nsTArray<VideoInfo*>& aVideos);
+
+ void SetOriginPos(mozilla::gl::OriginPos aOriginPos) {
+ mOriginPos = aOriginPos;
+ }
+ mozilla::gl::OriginPos OriginPos() const { return mOriginPos; }
+
+ static nsNPAPIPluginInstance* GetFromNPP(NPP npp);
+#endif
+
+ nsresult NewStreamListener(const char* aURL, void* notifyData,
+ nsNPAPIPluginStreamListener** listener);
+
+ nsNPAPIPluginInstance();
+
+ // To be called when an instance becomes orphaned, when
+ // it's plugin is no longer guaranteed to be around.
+ void Destroy();
+
+ // Indicates whether the plugin is running normally.
+ bool IsRunning() {
+ return RUNNING == mRunning;
+ }
+ bool HasStartedDestroying() {
+ return mRunning >= DESTROYING;
+ }
+
+ // Indicates whether the plugin is running normally or being shut down
+ bool CanFireNotifications() {
+ return mRunning == RUNNING || mRunning == DESTROYING;
+ }
+
+ // return is only valid when the plugin is not running
+ mozilla::TimeStamp StopTime();
+
+ // cache this NPAPI plugin
+ void SetCached(bool aCache);
+
+ already_AddRefed<nsPIDOMWindowOuter> GetDOMWindow();
+
+ nsresult PrivateModeStateChanged(bool aEnabled);
+
+ nsresult IsPrivateBrowsing(bool *aEnabled);
+
+ nsresult GetDOMElement(nsIDOMElement* *result);
+
+ nsNPAPITimer* TimerWithID(uint32_t id, uint32_t* index);
+ uint32_t ScheduleTimer(uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID));
+ void UnscheduleTimer(uint32_t timerID);
+ NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
+
+
+ nsTArray<nsNPAPIPluginStreamListener*> *StreamListeners();
+
+ nsTArray<nsPluginStreamListenerPeer*> *FileCachedStreamListeners();
+
+ nsresult AsyncSetWindow(NPWindow& window);
+
+ void URLRedirectResponse(void* notifyData, NPBool allow);
+
+ NPError InitAsyncSurface(NPSize *size, NPImageFormat format,
+ void *initData, NPAsyncSurface *surface);
+ NPError FinalizeAsyncSurface(NPAsyncSurface *surface);
+ void SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed);
+
+ // Called when the instance fails to instantiate beceause the Carbon
+ // event model is not supported.
+ void CarbonNPAPIFailure();
+
+ // Returns the contents scale factor of the screen the plugin is drawn on.
+ double GetContentsScaleFactor();
+
+ // Returns the css zoom factor of the document the plugin is drawn on.
+ float GetCSSZoomFactor();
+
+ nsresult GetRunID(uint32_t *aRunID);
+
+ static bool InPluginCallUnsafeForReentry() { return gInUnsafePluginCalls > 0; }
+ static void BeginPluginCall(NSPluginCallReentry aReentryState)
+ {
+ if (aReentryState == NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO) {
+ ++gInUnsafePluginCalls;
+ }
+ }
+ static void EndPluginCall(NSPluginCallReentry aReentryState)
+ {
+ if (aReentryState == NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO) {
+ NS_ASSERTION(gInUnsafePluginCalls > 0, "Must be in plugin call");
+ --gInUnsafePluginCalls;
+ }
+ }
+
+protected:
+
+ virtual ~nsNPAPIPluginInstance();
+
+ nsresult GetTagType(nsPluginTagType *result);
+ nsresult GetMode(int32_t *result);
+
+ // check if this is a Java applet and affected by bug 750480
+ void CheckJavaC2PJSObjectQuirk(uint16_t paramCount,
+ const char* const* names,
+ const char* const* values);
+
+ // The structure used to communicate between the plugin instance and
+ // the browser.
+ NPP_t mNPP;
+
+ NPDrawingModel mDrawingModel;
+
+#ifdef MOZ_WIDGET_ANDROID
+ uint32_t mANPDrawingModel;
+
+ friend class PluginEventRunnable;
+
+ nsTArray<RefPtr<PluginEventRunnable>> mPostedEvents;
+ void PopPostedEvent(PluginEventRunnable* r);
+ void OnSurfaceTextureFrameAvailable();
+
+ uint32_t mFullScreenOrientation;
+ bool mWakeLocked;
+ bool mFullScreen;
+ mozilla::gl::OriginPos mOriginPos;
+
+ RefPtr<mozilla::gl::AndroidSurfaceTexture> mContentSurface;
+#endif
+
+ enum {
+ NOT_STARTED,
+ RUNNING,
+ DESTROYING,
+ DESTROYED
+ } mRunning;
+
+ // these are used to store the windowless properties
+ // which the browser will later query
+ bool mWindowless;
+ bool mTransparent;
+ bool mCached;
+ bool mUsesDOMForCursor;
+
+public:
+ // True while creating the plugin, or calling NPP_SetWindow() on it.
+ bool mInPluginInitCall;
+
+ nsXPIDLCString mFakeURL;
+
+private:
+ RefPtr<nsNPAPIPlugin> mPlugin;
+
+ nsTArray<nsNPAPIPluginStreamListener*> mStreamListeners;
+
+ nsTArray<nsPluginStreamListenerPeer*> mFileCachedStreamListeners;
+
+ nsTArray<PopupControlState> mPopupStates;
+
+ char* mMIMEType;
+
+ // Weak pointer to the owner. The owner nulls this out (by calling
+ // InvalidateOwner()) when it's no longer our owner.
+ nsPluginInstanceOwner *mOwner;
+
+ nsTArray<nsNPAPITimer*> mTimers;
+
+#ifdef XP_MACOSX
+ // non-null during a HandleEvent call
+ void* mCurrentPluginEvent;
+#endif
+
+ // Timestamp for the last time this plugin was stopped.
+ // This is only valid when the plugin is actually stopped!
+ mozilla::TimeStamp mStopTime;
+
+#ifdef MOZ_WIDGET_ANDROID
+ already_AddRefed<mozilla::gl::AndroidSurfaceTexture> CreateSurfaceTexture();
+
+ std::map<void*, VideoInfo*> mVideos;
+ bool mOnScreen;
+
+ nsIntSize mCurrentSize;
+#endif
+
+ // is this instance Java and affected by bug 750480?
+ bool mHaveJavaC2PJSObjectQuirk;
+
+ static uint32_t gInUnsafePluginCalls;
+
+ // The arrays can only be released when the plugin instance is destroyed,
+ // because the plugin, in in-process mode, might keep a reference to them.
+ uint32_t mCachedParamLength;
+ char **mCachedParamNames;
+ char **mCachedParamValues;
+
+ nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
+ bool mMuted;
+};
+
+// On Android, we need to guard against plugin code leaking entries in the local
+// JNI ref table. See https://bugzilla.mozilla.org/show_bug.cgi?id=780831#c21
+#ifdef MOZ_WIDGET_ANDROID
+ #define MAIN_THREAD_JNI_REF_GUARD mozilla::AutoLocalJNIFrame jniFrame
+#else
+ #define MAIN_THREAD_JNI_REF_GUARD
+#endif
+
+void NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState);
+void NS_NotifyPluginCall(NSPluginCallReentry aReentryState);
+
+#define NS_TRY_SAFE_CALL_RETURN(ret, fun, pluginInst, pluginCallReentry) \
+PR_BEGIN_MACRO \
+ MAIN_THREAD_JNI_REF_GUARD; \
+ NS_NotifyBeginPluginCall(pluginCallReentry); \
+ ret = fun; \
+ NS_NotifyPluginCall(pluginCallReentry); \
+PR_END_MACRO
+
+#define NS_TRY_SAFE_CALL_VOID(fun, pluginInst, pluginCallReentry) \
+PR_BEGIN_MACRO \
+ MAIN_THREAD_JNI_REF_GUARD; \
+ NS_NotifyBeginPluginCall(pluginCallReentry); \
+ fun; \
+ NS_NotifyPluginCall(pluginCallReentry); \
+PR_END_MACRO
+
+#endif // nsNPAPIPluginInstance_h_
diff --git a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
new file mode 100644
index 000000000..0f500a1ae
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
@@ -0,0 +1,959 @@
+/* -*- 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 "nsNPAPIPluginStreamListener.h"
+#include "plstr.h"
+#include "prmem.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIFile.h"
+#include "nsNetUtil.h"
+#include "nsPluginHost.h"
+#include "nsNPAPIPlugin.h"
+#include "nsPluginLogging.h"
+#include "nsPluginStreamListenerPeer.h"
+
+#include <stdint.h>
+#include <algorithm>
+
+nsNPAPIStreamWrapper::nsNPAPIStreamWrapper(nsIOutputStream *outputStream,
+ nsNPAPIPluginStreamListener *streamListener)
+{
+ mOutputStream = outputStream;
+ mStreamListener = streamListener;
+
+ memset(&mNPStream, 0, sizeof(mNPStream));
+ mNPStream.ndata = static_cast<void*>(this);
+}
+
+nsNPAPIStreamWrapper::~nsNPAPIStreamWrapper()
+{
+ if (mOutputStream) {
+ mOutputStream->Close();
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsPluginStreamToFile, nsIOutputStream)
+
+nsPluginStreamToFile::nsPluginStreamToFile(const char* target,
+ nsIPluginInstanceOwner* owner)
+: mTarget(PL_strdup(target)),
+mOwner(owner)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> pluginTmp;
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pluginTmp));
+ if (NS_FAILED(rv)) return;
+
+ mTempFile = do_QueryInterface(pluginTmp, &rv);
+ if (NS_FAILED(rv)) return;
+
+ // need to create a file with a unique name - use target as the basis
+ rv = mTempFile->AppendNative(nsDependentCString(target));
+ if (NS_FAILED(rv)) return;
+
+ // Yes, make it unique.
+ rv = mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
+ if (NS_FAILED(rv)) return;
+
+ // create the file
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mTempFile, -1, 00600);
+ if (NS_FAILED(rv))
+ return;
+
+ // construct the URL we'll use later in calls to GetURL()
+ NS_GetURLSpecFromFile(mTempFile, mFileURL);
+
+#ifdef DEBUG
+ printf("File URL = %s\n", mFileURL.get());
+#endif
+}
+
+nsPluginStreamToFile::~nsPluginStreamToFile()
+{
+ // should we be deleting mTempFile here?
+ if (nullptr != mTarget)
+ PL_strfree(mTarget);
+}
+
+NS_IMETHODIMP
+nsPluginStreamToFile::Flush()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginStreamToFile::Write(const char* aBuf, uint32_t aCount,
+ uint32_t *aWriteCount)
+{
+ mOutputStream->Write(aBuf, aCount, aWriteCount);
+ mOutputStream->Flush();
+ mOwner->GetURL(mFileURL.get(), mTarget, nullptr, nullptr, 0, false);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginStreamToFile::WriteFrom(nsIInputStream *inStr, uint32_t count,
+ uint32_t *_retval)
+{
+ NS_NOTREACHED("WriteFrom");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPluginStreamToFile::WriteSegments(nsReadSegmentFun reader, void * closure,
+ uint32_t count, uint32_t *_retval)
+{
+ NS_NOTREACHED("WriteSegments");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPluginStreamToFile::IsNonBlocking(bool *aNonBlocking)
+{
+ *aNonBlocking = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginStreamToFile::Close(void)
+{
+ mOutputStream->Close();
+ mOwner->GetURL(mFileURL.get(), mTarget, nullptr, nullptr, 0, false);
+ return NS_OK;
+}
+
+// nsNPAPIPluginStreamListener Methods
+
+NS_IMPL_ISUPPORTS(nsNPAPIPluginStreamListener,
+ nsITimerCallback, nsIHTTPHeaderListener)
+
+nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst,
+ void* notifyData,
+ const char* aURL)
+ : mStreamBuffer(nullptr)
+ , mNotifyURL(aURL ? PL_strdup(aURL) : nullptr)
+ , mInst(inst)
+ , mStreamBufferSize(0)
+ , mStreamBufferByteCount(0)
+ , mStreamType(NP_NORMAL)
+ , mStreamState(eStreamStopped)
+ , mStreamCleanedUp(false)
+ , mCallNotify(notifyData ? true : false)
+ , mIsSuspended(false)
+ , mIsPluginInitJSStream(mInst->mInPluginInitCall &&
+ aURL && strncmp(aURL, "javascript:",
+ sizeof("javascript:") - 1) == 0)
+ , mRedirectDenied(false)
+ , mResponseHeaderBuf(nullptr)
+ , mStreamStopMode(eNormalStop)
+ , mPendingStopBindingStatus(NS_OK)
+{
+ mNPStreamWrapper = new nsNPAPIStreamWrapper(nullptr, this);
+ mNPStreamWrapper->mNPStream.notifyData = notifyData;
+}
+
+nsNPAPIPluginStreamListener::~nsNPAPIPluginStreamListener()
+{
+ // remove this from the plugin instance's stream list
+ nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners();
+ streamListeners->RemoveElement(this);
+
+ // For those cases when NewStream is never called, we still may need
+ // to fire a notification callback. Return network error as fallback
+ // reason because for other cases, notify should have already been
+ // called for other reasons elsewhere.
+ CallURLNotify(NPRES_NETWORK_ERR);
+
+ // lets get rid of the buffer
+ if (mStreamBuffer) {
+ PR_Free(mStreamBuffer);
+ mStreamBuffer=nullptr;
+ }
+
+ if (mNotifyURL)
+ PL_strfree(mNotifyURL);
+
+ if (mResponseHeaderBuf)
+ PL_strfree(mResponseHeaderBuf);
+
+ if (mNPStreamWrapper) {
+ delete mNPStreamWrapper;
+ }
+}
+
+nsresult
+nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ // Various bits of code in the rest of this method may result in the
+ // deletion of this object. Use a KungFuDeathGrip to keep ourselves
+ // alive during cleanup.
+ RefPtr<nsNPAPIPluginStreamListener> kungFuDeathGrip(this);
+
+ if (mStreamCleanedUp)
+ return NS_OK;
+
+ mStreamCleanedUp = true;
+
+ StopDataPump();
+
+ // Release any outstanding redirect callback.
+ if (mHTTPRedirectCallback) {
+ mHTTPRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+ mHTTPRedirectCallback = nullptr;
+ }
+
+ // Seekable streams have an extra addref when they are created which must
+ // be matched here.
+ if (NP_SEEK == mStreamType && mStreamState == eStreamTypeSet)
+ NS_RELEASE_THIS();
+
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->CancelRequests(NS_BINDING_ABORTED);
+ mStreamListenerPeer = nullptr;
+ }
+
+ if (!mInst || !mInst->CanFireNotifications())
+ return rv;
+
+ PluginDestructionGuard guard(mInst);
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary())
+ return rv;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ if (mStreamState >= eNewStreamCalled && pluginFunctions->destroystream) {
+ NPPAutoPusher nppPusher(npp);
+
+ NPError error;
+ NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n",
+ this, npp, reason, error, mNPStreamWrapper->mNPStream.url));
+
+ if (error == NPERR_NO_ERROR)
+ rv = NS_OK;
+ }
+
+ mStreamState = eStreamStopped;
+
+ // fire notification back to plugin, just like before
+ CallURLNotify(reason);
+
+ return rv;
+}
+
+void
+nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason)
+{
+ if (!mCallNotify || !mInst || !mInst->CanFireNotifications())
+ return;
+
+ PluginDestructionGuard guard(mInst);
+
+ mCallNotify = false; // only do this ONCE and prevent recursion
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary())
+ return;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ if (pluginFunctions->urlnotify) {
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n",
+ this, npp, mNPStreamWrapper->mNPStream.notifyData, reason, mNotifyURL));
+ }
+}
+
+nsresult
+nsNPAPIPluginStreamListener::OnStartBinding(nsPluginStreamListenerPeer* streamPeer)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+ if (!mInst || !mInst->CanFireNotifications() || mStreamCleanedUp)
+ return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(mInst);
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary())
+ return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ if (!pluginFunctions->newstream)
+ return NS_ERROR_FAILURE;
+
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ bool seekable;
+ char* contentType;
+ uint16_t streamType = NP_NORMAL;
+ NPError error;
+
+ streamPeer->GetURL(&mNPStreamWrapper->mNPStream.url);
+ streamPeer->GetLength((uint32_t*)&(mNPStreamWrapper->mNPStream.end));
+ streamPeer->GetLastModified((uint32_t*)&(mNPStreamWrapper->mNPStream.lastmodified));
+ streamPeer->IsSeekable(&seekable);
+ streamPeer->GetContentType(&contentType);
+
+ if (!mResponseHeaders.IsEmpty()) {
+ mResponseHeaderBuf = PL_strdup(mResponseHeaders.get());
+ mNPStreamWrapper->mNPStream.headers = mResponseHeaderBuf;
+ }
+
+ mStreamListenerPeer = streamPeer;
+
+ NPPAutoPusher nppPusher(npp);
+
+ NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n",
+ this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url));
+
+ if (error != NPERR_NO_ERROR)
+ return NS_ERROR_FAILURE;
+
+ mStreamState = eNewStreamCalled;
+
+ if (!SetStreamType(streamType, false)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+bool
+nsNPAPIPluginStreamListener::SetStreamType(uint16_t aType, bool aNeedsResume)
+{
+ switch(aType)
+ {
+ case NP_NORMAL:
+ mStreamType = NP_NORMAL;
+ break;
+ case NP_ASFILEONLY:
+ mStreamType = NP_ASFILEONLY;
+ break;
+ case NP_ASFILE:
+ mStreamType = NP_ASFILE;
+ break;
+ case NP_SEEK:
+ mStreamType = NP_SEEK;
+ // Seekable streams should continue to exist even after OnStopRequest
+ // is fired, so we AddRef ourself an extra time and Release when the
+ // plugin calls NPN_DestroyStream (CleanUpStream). If the plugin never
+ // calls NPN_DestroyStream the stream will be destroyed before the plugin
+ // instance is destroyed.
+ NS_ADDREF_THIS();
+ break;
+ case nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN:
+ MOZ_ASSERT(!aNeedsResume);
+ mStreamType = nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN;
+ SuspendRequest();
+ mStreamStopMode = eDoDeferredStop;
+ // In this case we do not want to execute anything else in this function.
+ return true;
+ default:
+ return false;
+ }
+ mStreamState = eStreamTypeSet;
+ if (aNeedsResume) {
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->OnStreamTypeSet(mStreamType);
+ }
+ ResumeRequest();
+ }
+ return true;
+}
+
+void
+nsNPAPIPluginStreamListener::SuspendRequest()
+{
+ NS_ASSERTION(!mIsSuspended,
+ "Suspending a request that's already suspended!");
+
+ nsresult rv = StartDataPump();
+ if (NS_FAILED(rv))
+ return;
+
+ mIsSuspended = true;
+
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->SuspendRequests();
+ }
+}
+
+void
+nsNPAPIPluginStreamListener::ResumeRequest()
+{
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->ResumeRequests();
+ }
+ mIsSuspended = false;
+}
+
+nsresult
+nsNPAPIPluginStreamListener::StartDataPump()
+{
+ nsresult rv;
+ mDataPumpTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Start pumping data to the plugin every 100ms until it obeys and
+ // eats the data.
+ return mDataPumpTimer->InitWithCallback(this, 100,
+ nsITimer::TYPE_REPEATING_SLACK);
+}
+
+void
+nsNPAPIPluginStreamListener::StopDataPump()
+{
+ if (mDataPumpTimer) {
+ mDataPumpTimer->Cancel();
+ mDataPumpTimer = nullptr;
+ }
+}
+
+// Return true if a javascript: load that was started while the plugin
+// was being initialized is still in progress.
+bool
+nsNPAPIPluginStreamListener::PluginInitJSLoadInProgress()
+{
+ if (!mInst)
+ return false;
+
+ nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners();
+ for (unsigned int i = 0; i < streamListeners->Length(); i++) {
+ if (streamListeners->ElementAt(i)->mIsPluginInitJSStream) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// This method is called when there's more data available off the
+// network, but it's also called from our data pump when we're feeding
+// the plugin data that we already got off the network, but the plugin
+// was unable to consume it at the point it arrived. In the case when
+// the plugin pump calls this method, the input argument will be null,
+// and the length will be the number of bytes available in our
+// internal buffer.
+nsresult
+nsNPAPIPluginStreamListener::OnDataAvailable(nsPluginStreamListenerPeer* streamPeer,
+ nsIInputStream* input,
+ uint32_t length)
+{
+ if (!length || !mInst || !mInst->CanFireNotifications())
+ return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(mInst);
+
+ // Just in case the caller switches plugin info on us.
+ mStreamListenerPeer = streamPeer;
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary())
+ return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ // check out if plugin implements NPP_Write call
+ if (!pluginFunctions->write)
+ return NS_ERROR_FAILURE; // it'll cancel necko transaction
+
+ if (!mStreamBuffer) {
+ // To optimize the mem usage & performance we have to allocate
+ // mStreamBuffer here in first ODA when length of data available
+ // in input stream is known. mStreamBuffer will be freed in DTOR.
+ // we also have to remember the size of that buff to make safe
+ // consecutive Read() calls form input stream into our buff.
+
+ uint32_t contentLength;
+ streamPeer->GetLength(&contentLength);
+
+ mStreamBufferSize = std::max(length, contentLength);
+
+ // Limit the size of the initial buffer to MAX_PLUGIN_NECKO_BUFFER
+ // (16k). This buffer will grow if needed, as in the case where
+ // we're getting data faster than the plugin can process it.
+ mStreamBufferSize = std::min(mStreamBufferSize,
+ uint32_t(MAX_PLUGIN_NECKO_BUFFER));
+
+ mStreamBuffer = (char*) PR_Malloc(mStreamBufferSize);
+ if (!mStreamBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // prepare NPP_ calls params
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ int32_t streamPosition;
+ streamPeer->GetStreamOffset(&streamPosition);
+ int32_t streamOffset = streamPosition;
+
+ if (input) {
+ streamOffset += length;
+
+ // Set new stream offset for the next ODA call regardless of how
+ // following NPP_Write call will behave we pretend to consume all
+ // data from the input stream. It's possible that current steam
+ // position will be overwritten from NPP_RangeRequest call made
+ // from NPP_Write, so we cannot call SetStreamOffset after
+ // NPP_Write.
+ //
+ // Note: there is a special case when data flow should be
+ // temporarily stopped if NPP_WriteReady returns 0 (bug #89270)
+ streamPeer->SetStreamOffset(streamOffset);
+
+ // set new end in case the content is compressed
+ // initial end is less than end of decompressed stream
+ // and some plugins (e.g. acrobat) can fail.
+ if ((int32_t)mNPStreamWrapper->mNPStream.end < streamOffset)
+ mNPStreamWrapper->mNPStream.end = streamOffset;
+ }
+
+ nsresult rv = NS_OK;
+ while (NS_SUCCEEDED(rv) && length > 0) {
+ if (input && length) {
+ if (mStreamBufferSize < mStreamBufferByteCount + length) {
+ // We're in the ::OnDataAvailable() call that we might get
+ // after suspending a request, or we suspended the request
+ // from within this ::OnDataAvailable() call while there's
+ // still data in the input, or we have resumed a previously
+ // suspended request and our buffer is already full, and we
+ // don't have enough space to store what we got off the network.
+ // Reallocate our internal buffer.
+ mStreamBufferSize = mStreamBufferByteCount + length;
+ char *buf = (char*)PR_Realloc(mStreamBuffer, mStreamBufferSize);
+ if (!buf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mStreamBuffer = buf;
+ }
+
+ uint32_t bytesToRead =
+ std::min(length, mStreamBufferSize - mStreamBufferByteCount);
+ MOZ_ASSERT(bytesToRead > 0);
+
+ uint32_t amountRead = 0;
+ rv = input->Read(mStreamBuffer + mStreamBufferByteCount, bytesToRead,
+ &amountRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (amountRead == 0) {
+ NS_NOTREACHED("input->Read() returns no data, it's almost impossible "
+ "to get here");
+
+ break;
+ }
+
+ mStreamBufferByteCount += amountRead;
+ length -= amountRead;
+ } else {
+ // No input, nothing to read. Set length to 0 so that we don't
+ // keep iterating through this outer loop any more.
+
+ length = 0;
+ }
+
+ // Temporary pointer to the beginning of the data we're writing as
+ // we loop and feed the plugin data.
+ char *ptrStreamBuffer = mStreamBuffer;
+
+ // it is possible plugin's NPP_Write() returns 0 byte consumed. We
+ // use zeroBytesWriteCount to count situation like this and break
+ // the loop
+ int32_t zeroBytesWriteCount = 0;
+
+ // mStreamBufferByteCount tells us how many bytes there are in the
+ // buffer. WriteReady returns to us how many bytes the plugin is
+ // ready to handle.
+ while (mStreamBufferByteCount > 0) {
+ int32_t numtowrite;
+ if (pluginFunctions->writeready) {
+ NPPAutoPusher nppPusher(npp);
+
+ NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPP WriteReady called: this=%p, npp=%p, "
+ "return(towrite)=%d, url=%s\n",
+ this, npp, numtowrite, mNPStreamWrapper->mNPStream.url));
+
+ if (mStreamState == eStreamStopped) {
+ // The plugin called NPN_DestroyStream() from within
+ // NPP_WriteReady(), kill the stream.
+
+ return NS_BINDING_ABORTED;
+ }
+
+ // if WriteReady returned 0, the plugin is not ready to handle
+ // the data, suspend the stream (if it isn't already
+ // suspended).
+ //
+ // Also suspend the stream if the stream we're loading is not
+ // a javascript: URL load that was initiated during plugin
+ // initialization and there currently is such a stream
+ // loading. This is done to work around a Windows Media Player
+ // plugin bug where it can't deal with being fed data for
+ // other streams while it's waiting for data from the
+ // javascript: URL loads it requests during
+ // initialization. See bug 386493 for more details.
+
+ if (numtowrite <= 0 ||
+ (!mIsPluginInitJSStream && PluginInitJSLoadInProgress())) {
+ if (!mIsSuspended) {
+ SuspendRequest();
+ }
+
+ // Break out of the inner loop, but keep going through the
+ // outer loop in case there's more data to read from the
+ // input stream.
+
+ break;
+ }
+
+ numtowrite = std::min(numtowrite, mStreamBufferByteCount);
+ } else {
+ // if WriteReady is not supported by the plugin, just write
+ // the whole buffer
+ numtowrite = mStreamBufferByteCount;
+ }
+
+ NPPAutoPusher nppPusher(npp);
+
+ int32_t writeCount = 0; // bytes consumed by plugin instance
+ NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, "
+ "buf=%s, return(written)=%d, url=%s\n",
+ this, npp, streamPosition, numtowrite,
+ ptrStreamBuffer, writeCount, mNPStreamWrapper->mNPStream.url));
+
+ if (mStreamState == eStreamStopped) {
+ // The plugin called NPN_DestroyStream() from within
+ // NPP_Write(), kill the stream.
+ return NS_BINDING_ABORTED;
+ }
+
+ if (writeCount > 0) {
+ NS_ASSERTION(writeCount <= mStreamBufferByteCount,
+ "Plugin read past the end of the available data!");
+
+ writeCount = std::min(writeCount, mStreamBufferByteCount);
+ mStreamBufferByteCount -= writeCount;
+
+ streamPosition += writeCount;
+
+ zeroBytesWriteCount = 0;
+
+ if (mStreamBufferByteCount > 0) {
+ // This alignment code is most likely bogus, but we'll leave
+ // it in for now in case it matters for some plugins on some
+ // architectures. Who knows...
+ if (writeCount % sizeof(intptr_t)) {
+ // memmove will take care about alignment
+ memmove(mStreamBuffer, ptrStreamBuffer + writeCount,
+ mStreamBufferByteCount);
+ ptrStreamBuffer = mStreamBuffer;
+ } else {
+ // if aligned we can use ptrStreamBuffer += to eliminate
+ // memmove()
+ ptrStreamBuffer += writeCount;
+ }
+ }
+ } else if (writeCount == 0) {
+ // if NPP_Write() returns writeCount == 0 lets say 3 times in
+ // a row, suspend the request and continue feeding the plugin
+ // the data we got so far. Once that data is consumed, we'll
+ // resume the request.
+ if (mIsSuspended || ++zeroBytesWriteCount == 3) {
+ if (!mIsSuspended) {
+ SuspendRequest();
+ }
+
+ // Break out of the for loop, but keep going through the
+ // while loop in case there's more data to read from the
+ // input stream.
+
+ break;
+ }
+ } else {
+ // Something's really wrong, kill the stream.
+ rv = NS_ERROR_FAILURE;
+
+ break;
+ }
+ } // end of inner while loop
+
+ if (mStreamBufferByteCount && mStreamBuffer != ptrStreamBuffer) {
+ memmove(mStreamBuffer, ptrStreamBuffer, mStreamBufferByteCount);
+ }
+ }
+
+ if (streamPosition != streamOffset) {
+ // The plugin didn't consume all available data, or consumed some
+ // of our cached data while we're pumping cached data. Adjust the
+ // plugin info's stream offset to match reality, except if the
+ // plugin info's stream offset was set by a re-entering
+ // NPN_RequestRead() call.
+
+ int32_t postWriteStreamPosition;
+ streamPeer->GetStreamOffset(&postWriteStreamPosition);
+
+ if (postWriteStreamPosition == streamOffset) {
+ streamPeer->SetStreamOffset(streamPosition);
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+nsNPAPIPluginStreamListener::OnFileAvailable(nsPluginStreamListenerPeer* streamPeer,
+ const char* fileName)
+{
+ if (!mInst || !mInst->CanFireNotifications())
+ return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(mInst);
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary())
+ return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ if (!pluginFunctions->asfile)
+ return NS_ERROR_FAILURE;
+
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n",
+ this, npp, mNPStreamWrapper->mNPStream.url, fileName));
+
+ return NS_OK;
+}
+
+nsresult
+nsNPAPIPluginStreamListener::OnStopBinding(nsPluginStreamListenerPeer* streamPeer,
+ nsresult status)
+{
+ if (NS_FAILED(status)) {
+ // The stream was destroyed, or died for some reason. Make sure we
+ // cancel the underlying request.
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->CancelRequests(status);
+ }
+ }
+
+ if (!mInst || !mInst->CanFireNotifications()) {
+ StopDataPump();
+ return NS_ERROR_FAILURE;
+ }
+
+ // We need to detect that the stop is due to async stream init completion.
+ if (mStreamStopMode == eDoDeferredStop) {
+ // We shouldn't be delivering this until async init is done
+ mStreamStopMode = eStopPending;
+ mPendingStopBindingStatus = status;
+ if (!mDataPumpTimer) {
+ StartDataPump();
+ }
+ return NS_OK;
+ }
+
+ StopDataPump();
+
+ NPReason reason = NS_FAILED(status) ? NPRES_NETWORK_ERR : NPRES_DONE;
+ if (mRedirectDenied || status == NS_BINDING_ABORTED) {
+ reason = NPRES_USER_BREAK;
+ }
+
+ // The following code can result in the deletion of 'this'. Don't
+ // assume we are alive after this!
+ //
+ // Delay cleanup if the stream is of type NP_SEEK and status isn't
+ // NS_BINDING_ABORTED (meaning the plugin hasn't called NPN_DestroyStream).
+ // This is because even though we're done delivering data the plugin may
+ // want to seek. Eventually either the plugin will call NPN_DestroyStream
+ // or we'll perform cleanup when the instance goes away. See bug 91140.
+ if (mStreamType != NP_SEEK ||
+ (NP_SEEK == mStreamType && NS_BINDING_ABORTED == status)) {
+ return CleanUpStream(reason);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsNPAPIPluginStreamListener::GetStreamType(int32_t *result)
+{
+ *result = mStreamType;
+ return NS_OK;
+}
+
+bool
+nsNPAPIPluginStreamListener::MaybeRunStopBinding()
+{
+ if (mIsSuspended || mStreamStopMode != eStopPending) {
+ return false;
+ }
+ OnStopBinding(mStreamListenerPeer, mPendingStopBindingStatus);
+ mStreamStopMode = eNormalStop;
+ return true;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginStreamListener::Notify(nsITimer *aTimer)
+{
+ NS_ASSERTION(aTimer == mDataPumpTimer, "Uh, wrong timer?");
+
+ int32_t oldStreamBufferByteCount = mStreamBufferByteCount;
+
+ nsresult rv = OnDataAvailable(mStreamListenerPeer, nullptr, mStreamBufferByteCount);
+
+ if (NS_FAILED(rv)) {
+ // We ran into an error, no need to keep firing this timer then.
+ StopDataPump();
+ MaybeRunStopBinding();
+ return NS_OK;
+ }
+
+ if (mStreamBufferByteCount != oldStreamBufferByteCount &&
+ ((mStreamState == eStreamTypeSet && mStreamBufferByteCount < 1024) ||
+ mStreamBufferByteCount == 0)) {
+ // The plugin read some data and we've got less than 1024 bytes in
+ // our buffer (or its empty and the stream is already
+ // done). Resume the request so that we get more data off the
+ // network.
+ ResumeRequest();
+ // Necko will pump data now that we've resumed the request.
+ StopDataPump();
+ }
+
+ MaybeRunStopBinding();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginStreamListener::StatusLine(const char* line)
+{
+ mResponseHeaders.Append(line);
+ mResponseHeaders.Append('\n');
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginStreamListener::NewResponseHeader(const char* headerName,
+ const char* headerValue)
+{
+ mResponseHeaders.Append(headerName);
+ mResponseHeaders.AppendLiteral(": ");
+ mResponseHeaders.Append(headerValue);
+ mResponseHeaders.Append('\n');
+ return NS_OK;
+}
+
+bool
+nsNPAPIPluginStreamListener::HandleRedirectNotification(nsIChannel *oldChannel, nsIChannel *newChannel,
+ nsIAsyncVerifyRedirectCallback* callback)
+{
+ nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(oldChannel);
+ nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel);
+ if (!oldHttpChannel || !newHttpChannel) {
+ return false;
+ }
+
+ if (!mInst || !mInst->CanFireNotifications()) {
+ return false;
+ }
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary()) {
+ return false;
+ }
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+ if (!pluginFunctions->urlredirectnotify) {
+ return false;
+ }
+
+ // A non-null closure is required for redirect handling support.
+ if (mNPStreamWrapper->mNPStream.notifyData) {
+ uint32_t status;
+ if (NS_SUCCEEDED(oldHttpChannel->GetResponseStatus(&status))) {
+ nsCOMPtr<nsIURI> uri;
+ if (NS_SUCCEEDED(newHttpChannel->GetURI(getter_AddRefs(uri))) && uri) {
+ nsAutoCString spec;
+ if (NS_SUCCEEDED(uri->GetAsciiSpec(spec))) {
+ // At this point the plugin will be responsible for making the callback
+ // so save the callback object.
+ mHTTPRedirectCallback = callback;
+
+ NPP npp;
+ mInst->GetNPP(&npp);
+#if defined(XP_WIN)
+ NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData), mInst,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+#else
+ MAIN_THREAD_JNI_REF_GUARD;
+ (*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData);
+#endif
+ return true;
+ }
+ }
+ }
+ }
+
+ callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+ return true;
+}
+
+void
+nsNPAPIPluginStreamListener::URLRedirectResponse(NPBool allow)
+{
+ if (mHTTPRedirectCallback) {
+ mHTTPRedirectCallback->OnRedirectVerifyCallback(allow ? NS_OK : NS_ERROR_FAILURE);
+ mRedirectDenied = allow ? false : true;
+ mHTTPRedirectCallback = nullptr;
+ }
+}
+
+void*
+nsNPAPIPluginStreamListener::GetNotifyData()
+{
+ if (mNPStreamWrapper) {
+ return mNPStreamWrapper->mNPStream.notifyData;
+ }
+ return nullptr;
+}
diff --git a/dom/plugins/base/nsNPAPIPluginStreamListener.h b/dom/plugins/base/nsNPAPIPluginStreamListener.h
new file mode 100644
index 000000000..dd567f99b
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.h
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef nsNPAPIPluginStreamListener_h_
+#define nsNPAPIPluginStreamListener_h_
+
+#include "nscore.h"
+#include "nsIHTTPHeaderListener.h"
+#include "nsIRequest.h"
+#include "nsITimer.h"
+#include "nsCOMArray.h"
+#include "nsIOutputStream.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsString.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "mozilla/PluginLibrary.h"
+
+#define MAX_PLUGIN_NECKO_BUFFER 16384
+
+class nsPluginStreamListenerPeer;
+class nsNPAPIPluginStreamListener;
+class nsNPAPIPluginInstance;
+class nsIChannel;
+
+class nsNPAPIStreamWrapper
+{
+public:
+ nsNPAPIStreamWrapper(nsIOutputStream *outputStream,
+ nsNPAPIPluginStreamListener *streamListener);
+ ~nsNPAPIStreamWrapper();
+
+ nsIOutputStream* GetOutputStream() { return mOutputStream.get(); }
+ nsNPAPIPluginStreamListener* GetStreamListener() { return mStreamListener; }
+
+ NPStream mNPStream;
+protected:
+ nsCOMPtr<nsIOutputStream> mOutputStream; // only valid if not browser initiated
+ nsNPAPIPluginStreamListener* mStreamListener; // only valid if browser initiated
+};
+
+// Used to handle NPN_NewStream() - writes the stream as received by the plugin
+// to a file and at completion (NPN_DestroyStream), tells the browser to load it into
+// a plugin-specified target
+class nsPluginStreamToFile : public nsIOutputStream
+{
+public:
+ nsPluginStreamToFile(const char* target, nsIPluginInstanceOwner* owner);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOUTPUTSTREAM
+protected:
+ virtual ~nsPluginStreamToFile();
+ char* mTarget;
+ nsCString mFileURL;
+ nsCOMPtr<nsIFile> mTempFile;
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+ nsIPluginInstanceOwner* mOwner;
+};
+
+class nsNPAPIPluginStreamListener : public nsITimerCallback,
+ public nsIHTTPHeaderListener
+{
+private:
+ typedef mozilla::PluginLibrary PluginLibrary;
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSIHTTPHEADERLISTENER
+
+ nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst, void* notifyData,
+ const char* aURL);
+
+ nsresult OnStartBinding(nsPluginStreamListenerPeer* streamPeer);
+ nsresult OnDataAvailable(nsPluginStreamListenerPeer* streamPeer,
+ nsIInputStream* input,
+ uint32_t length);
+ nsresult OnFileAvailable(nsPluginStreamListenerPeer* streamPeer,
+ const char* fileName);
+ nsresult OnStopBinding(nsPluginStreamListenerPeer* streamPeer,
+ nsresult status);
+ nsresult GetStreamType(int32_t *result);
+ bool SetStreamType(uint16_t aType, bool aNeedsResume = true);
+
+ bool IsStarted();
+ nsresult CleanUpStream(NPReason reason);
+ void CallURLNotify(NPReason reason);
+ void SetCallNotify(bool aCallNotify) { mCallNotify = aCallNotify; }
+ void SuspendRequest();
+ void ResumeRequest();
+ nsresult StartDataPump();
+ void StopDataPump();
+ bool PluginInitJSLoadInProgress();
+
+ void* GetNotifyData();
+ nsPluginStreamListenerPeer* GetStreamListenerPeer() { return mStreamListenerPeer; }
+ void SetStreamListenerPeer(nsPluginStreamListenerPeer* aPeer) { mStreamListenerPeer = aPeer; }
+
+ // Returns true if the redirect will be handled by NPAPI, false otherwise.
+ bool HandleRedirectNotification(nsIChannel *oldChannel, nsIChannel *newChannel,
+ nsIAsyncVerifyRedirectCallback* callback);
+ void URLRedirectResponse(NPBool allow);
+
+protected:
+
+ enum StreamState
+ {
+ eStreamStopped = 0, // The stream is stopped
+ eNewStreamCalled, // NPP_NewStream was called but has not completed yet
+ eStreamTypeSet // The stream is fully initialized
+ };
+
+ enum StreamStopMode
+ {
+ eNormalStop = 0,
+ eDoDeferredStop,
+ eStopPending
+ };
+
+ virtual ~nsNPAPIPluginStreamListener();
+ bool MaybeRunStopBinding();
+
+ char* mStreamBuffer;
+ char* mNotifyURL;
+ RefPtr<nsNPAPIPluginInstance> mInst;
+ nsNPAPIStreamWrapper *mNPStreamWrapper;
+ uint32_t mStreamBufferSize;
+ int32_t mStreamBufferByteCount;
+ int32_t mStreamType;
+ StreamState mStreamState;
+ bool mStreamCleanedUp;
+ bool mCallNotify;
+ bool mIsSuspended;
+ bool mIsPluginInitJSStream;
+ bool mRedirectDenied;
+ nsCString mResponseHeaders;
+ char* mResponseHeaderBuf;
+ nsCOMPtr<nsITimer> mDataPumpTimer;
+ nsCOMPtr<nsIAsyncVerifyRedirectCallback> mHTTPRedirectCallback;
+ StreamStopMode mStreamStopMode;
+ nsresult mPendingStopBindingStatus;
+
+public:
+ RefPtr<nsPluginStreamListenerPeer> mStreamListenerPeer;
+};
+
+#endif // nsNPAPIPluginStreamListener_h_
diff --git a/dom/plugins/base/nsPluginDirServiceProvider.cpp b/dom/plugins/base/nsPluginDirServiceProvider.cpp
new file mode 100644
index 000000000..92348c182
--- /dev/null
+++ b/dom/plugins/base/nsPluginDirServiceProvider.cpp
@@ -0,0 +1,448 @@
+/* -*- 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 "nsPluginDirServiceProvider.h"
+
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsDependentString.h"
+#include "nsArrayEnumerator.h"
+#include "mozilla/Preferences.h"
+
+#include <windows.h>
+#include "nsIWindowsRegKey.h"
+
+using namespace mozilla;
+
+typedef struct structVer
+{
+ WORD wMajor;
+ WORD wMinor;
+ WORD wRelease;
+ WORD wBuild;
+} verBlock;
+
+static void
+ClearVersion(verBlock *ver)
+{
+ ver->wMajor = 0;
+ ver->wMinor = 0;
+ ver->wRelease = 0;
+ ver->wBuild = 0;
+}
+
+static BOOL
+FileExists(LPCWSTR szFile)
+{
+ return GetFileAttributesW(szFile) != 0xFFFFFFFF;
+}
+
+// Get file version information from a file
+static BOOL
+GetFileVersion(LPCWSTR szFile, verBlock *vbVersion)
+{
+ UINT uLen;
+ UINT dwLen;
+ BOOL bRv;
+ DWORD dwHandle;
+ LPVOID lpData;
+ LPVOID lpBuffer;
+ VS_FIXEDFILEINFO *lpBuffer2;
+
+ ClearVersion(vbVersion);
+ if (FileExists(szFile)) {
+ bRv = TRUE;
+ LPCWSTR lpFilepath = szFile;
+ dwLen = GetFileVersionInfoSizeW(lpFilepath, &dwHandle);
+ lpData = (LPVOID)malloc(dwLen);
+ uLen = 0;
+
+ if (lpData && GetFileVersionInfoW(lpFilepath, dwHandle, dwLen, lpData) != 0) {
+ if (VerQueryValueW(lpData, L"\\", &lpBuffer, &uLen) != 0) {
+ lpBuffer2 = (VS_FIXEDFILEINFO *)lpBuffer;
+
+ vbVersion->wMajor = HIWORD(lpBuffer2->dwFileVersionMS);
+ vbVersion->wMinor = LOWORD(lpBuffer2->dwFileVersionMS);
+ vbVersion->wRelease = HIWORD(lpBuffer2->dwFileVersionLS);
+ vbVersion->wBuild = LOWORD(lpBuffer2->dwFileVersionLS);
+ }
+ }
+
+ free(lpData);
+ } else {
+ /* File does not exist */
+ bRv = FALSE;
+ }
+
+ return bRv;
+}
+
+// Will deep copy ver2 into ver1
+static void
+CopyVersion(verBlock *ver1, verBlock *ver2)
+{
+ ver1->wMajor = ver2->wMajor;
+ ver1->wMinor = ver2->wMinor;
+ ver1->wRelease = ver2->wRelease;
+ ver1->wBuild = ver2->wBuild;
+}
+
+// Convert a string version to a version struct
+static void
+TranslateVersionStr(const WCHAR* szVersion, verBlock *vbVersion)
+{
+ WCHAR* szNum1 = nullptr;
+ WCHAR* szNum2 = nullptr;
+ WCHAR* szNum3 = nullptr;
+ WCHAR* szNum4 = nullptr;
+ WCHAR* szJavaBuild = nullptr;
+
+ WCHAR *strVer = nullptr;
+ if (szVersion) {
+ strVer = wcsdup(szVersion);
+ }
+
+ if (!strVer) {
+ // Out of memory
+ ClearVersion(vbVersion);
+ return;
+ }
+
+ // Java may be using an underscore instead of a dot for the build ID
+ szJavaBuild = wcschr(strVer, '_');
+ if (szJavaBuild) {
+ szJavaBuild[0] = '.';
+ }
+
+#if defined(__MINGW32__)
+ // MSVC 2013 and earlier provided only a non-standard two-argument variant of
+ // wcstok that is generally not thread-safe. For our purposes here, it works
+ // fine, though.
+ auto wcstok = [](wchar_t* strToken, const wchar_t* strDelimit,
+ wchar_t** /*ctx*/) {
+ return ::std::wcstok(strToken, strDelimit);
+ };
+#endif
+
+ wchar_t* ctx = nullptr;
+ szNum1 = wcstok(strVer, L".", &ctx);
+ szNum2 = wcstok(nullptr, L".", &ctx);
+ szNum3 = wcstok(nullptr, L".", &ctx);
+ szNum4 = wcstok(nullptr, L".", &ctx);
+
+ vbVersion->wMajor = szNum1 ? (WORD) _wtoi(szNum1) : 0;
+ vbVersion->wMinor = szNum2 ? (WORD) _wtoi(szNum2) : 0;
+ vbVersion->wRelease = szNum3 ? (WORD) _wtoi(szNum3) : 0;
+ vbVersion->wBuild = szNum4 ? (WORD) _wtoi(szNum4) : 0;
+
+ free(strVer);
+}
+
+// Compare two version struct, return zero if the same
+static int
+CompareVersion(verBlock vbVersionOld, verBlock vbVersionNew)
+{
+ if (vbVersionOld.wMajor > vbVersionNew.wMajor) {
+ return 4;
+ } else if (vbVersionOld.wMajor < vbVersionNew.wMajor) {
+ return -4;
+ }
+
+ if (vbVersionOld.wMinor > vbVersionNew.wMinor) {
+ return 3;
+ } else if (vbVersionOld.wMinor < vbVersionNew.wMinor) {
+ return -3;
+ }
+
+ if (vbVersionOld.wRelease > vbVersionNew.wRelease) {
+ return 2;
+ } else if (vbVersionOld.wRelease < vbVersionNew.wRelease) {
+ return -2;
+ }
+
+ if (vbVersionOld.wBuild > vbVersionNew.wBuild) {
+ return 1;
+ } else if (vbVersionOld.wBuild < vbVersionNew.wBuild) {
+ return -1;
+ }
+
+ /* the versions are all the same */
+ return 0;
+}
+
+//*****************************************************************************
+// nsPluginDirServiceProvider::Constructor/Destructor
+//*****************************************************************************
+
+nsPluginDirServiceProvider::nsPluginDirServiceProvider()
+{
+}
+
+nsPluginDirServiceProvider::~nsPluginDirServiceProvider()
+{
+}
+
+//*****************************************************************************
+// nsPluginDirServiceProvider::nsISupports
+//*****************************************************************************
+
+NS_IMPL_ISUPPORTS(nsPluginDirServiceProvider,
+ nsIDirectoryServiceProvider)
+
+//*****************************************************************************
+// nsPluginDirServiceProvider::nsIDirectoryServiceProvider
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsPluginDirServiceProvider::GetFile(const char *charProp, bool *persistant,
+ nsIFile **_retval)
+{
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ NS_ENSURE_ARG(charProp);
+
+ *_retval = nullptr;
+ *persistant = false;
+
+ nsCOMPtr<nsIWindowsRegKey> regKey =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1");
+ NS_ENSURE_TRUE(regKey, NS_ERROR_FAILURE);
+
+ if (nsCRT::strcmp(charProp, NS_WIN_QUICKTIME_SCAN_KEY) == 0) {
+ nsAdoptingCString strVer = Preferences::GetCString(charProp);
+ if (!strVer) {
+ return NS_ERROR_FAILURE;
+ }
+ verBlock minVer;
+ TranslateVersionStr(NS_ConvertASCIItoUTF16(strVer).get(), &minVer);
+
+ // Look for the Quicktime system installation plugins directory
+ verBlock qtVer;
+ ClearVersion(&qtVer);
+
+ // First we need to check the version of Quicktime via checking
+ // the EXE's version table
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+ NS_LITERAL_STRING("software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\QuickTimePlayer.exe"),
+ nsIWindowsRegKey::ACCESS_READ);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString path;
+ rv = regKey->ReadStringValue(NS_LITERAL_STRING(""), path);
+ if (NS_SUCCEEDED(rv)) {
+ GetFileVersion(path.get(), &qtVer);
+ }
+ regKey->Close();
+ }
+ if (CompareVersion(qtVer, minVer) < 0)
+ return rv;
+
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+ NS_LITERAL_STRING("software\\Apple Computer, Inc.\\QuickTime"),
+ nsIWindowsRegKey::ACCESS_READ);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString path;
+ rv = regKey->ReadStringValue(NS_LITERAL_STRING("InstallDir"), path);
+ if (NS_SUCCEEDED(rv)) {
+ path += NS_LITERAL_STRING("\\Plugins");
+ rv = NS_NewLocalFile(path, true,
+ getter_AddRefs(localFile));
+ }
+ }
+ } else if (nsCRT::strcmp(charProp, NS_WIN_WMP_SCAN_KEY) == 0) {
+ nsAdoptingCString strVer = Preferences::GetCString(charProp);
+ if (!strVer) {
+ return NS_ERROR_FAILURE;
+ }
+ verBlock minVer;
+ TranslateVersionStr(NS_ConvertASCIItoUTF16(strVer).get(), &minVer);
+
+ // Look for Windows Media Player system installation plugins directory
+ verBlock wmpVer;
+ ClearVersion(&wmpVer);
+
+ // First we need to check the version of WMP
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+ NS_LITERAL_STRING("software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\wmplayer.exe"),
+ nsIWindowsRegKey::ACCESS_READ);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString path;
+ rv = regKey->ReadStringValue(NS_LITERAL_STRING(""), path);
+ if (NS_SUCCEEDED(rv)) {
+ GetFileVersion(path.get(), &wmpVer);
+ }
+ regKey->Close();
+ }
+ if (CompareVersion(wmpVer, minVer) < 0)
+ return rv;
+
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+ NS_LITERAL_STRING("software\\Microsoft\\MediaPlayer"),
+ nsIWindowsRegKey::ACCESS_READ);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString path;
+ rv = regKey->ReadStringValue(NS_LITERAL_STRING("Installation Directory"),
+ path);
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_NewLocalFile(path, true,
+ getter_AddRefs(localFile));
+ }
+ }
+ } else if (nsCRT::strcmp(charProp, NS_WIN_ACROBAT_SCAN_KEY) == 0) {
+ nsAdoptingCString strVer = Preferences::GetCString(charProp);
+ if (!strVer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ verBlock minVer;
+ TranslateVersionStr(NS_ConvertASCIItoUTF16(strVer).get(), &minVer);
+
+ // Look for Adobe Acrobat system installation plugins directory
+ verBlock maxVer;
+ ClearVersion(&maxVer);
+
+ nsAutoString newestPath;
+
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+ NS_LITERAL_STRING("software\\Adobe\\Acrobat Reader"),
+ nsIWindowsRegKey::ACCESS_READ);
+ if (NS_FAILED(rv)) {
+ rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+ NS_LITERAL_STRING("software\\Adobe\\Adobe Acrobat"),
+ nsIWindowsRegKey::ACCESS_READ);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // We must enumerate through the keys because what if there is
+ // more than one version?
+ uint32_t childCount = 0;
+ regKey->GetChildCount(&childCount);
+
+ for (uint32_t index = 0; index < childCount; ++index) {
+ nsAutoString childName;
+ rv = regKey->GetChildName(index, childName);
+ if (NS_SUCCEEDED(rv)) {
+ verBlock curVer;
+ TranslateVersionStr(childName.get(), &curVer);
+
+ childName += NS_LITERAL_STRING("\\InstallPath");
+
+ nsCOMPtr<nsIWindowsRegKey> childKey;
+ rv = regKey->OpenChild(childName, nsIWindowsRegKey::ACCESS_QUERY_VALUE,
+ getter_AddRefs(childKey));
+ if (NS_SUCCEEDED(rv)) {
+ // We have a sub key
+ nsAutoString path;
+ rv = childKey->ReadStringValue(NS_LITERAL_STRING(""), path);
+ if (NS_SUCCEEDED(rv)) {
+ if (CompareVersion(curVer, maxVer) >= 0 &&
+ CompareVersion(curVer, minVer) >= 0) {
+ newestPath = path;
+ CopyVersion(&maxVer, &curVer);
+ }
+ }
+ }
+ }
+ }
+
+ if (!newestPath.IsEmpty()) {
+ newestPath += NS_LITERAL_STRING("\\browser");
+ rv = NS_NewLocalFile(newestPath, true,
+ getter_AddRefs(localFile));
+ }
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ localFile.forget(_retval);
+ return NS_OK;
+}
+
+nsresult
+nsPluginDirServiceProvider::GetPLIDDirectories(nsISimpleEnumerator **aEnumerator)
+{
+ NS_ENSURE_ARG_POINTER(aEnumerator);
+ *aEnumerator = nullptr;
+
+ nsCOMArray<nsIFile> dirs;
+
+ GetPLIDDirectoriesWithRootKey(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, dirs);
+ GetPLIDDirectoriesWithRootKey(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, dirs);
+
+ return NS_NewArrayEnumerator(aEnumerator, dirs);
+}
+
+nsresult
+nsPluginDirServiceProvider::GetPLIDDirectoriesWithRootKey(uint32_t aKey, nsCOMArray<nsIFile> &aDirs)
+{
+ nsCOMPtr<nsIWindowsRegKey> regKey =
+ do_CreateInstance("@mozilla.org/windows-registry-key;1");
+ NS_ENSURE_TRUE(regKey, NS_ERROR_FAILURE);
+
+ nsresult rv = regKey->Open(aKey,
+ NS_LITERAL_STRING("Software\\MozillaPlugins"),
+ nsIWindowsRegKey::ACCESS_READ);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ uint32_t childCount = 0;
+ regKey->GetChildCount(&childCount);
+
+ for (uint32_t index = 0; index < childCount; ++index) {
+ nsAutoString childName;
+ rv = regKey->GetChildName(index, childName);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIWindowsRegKey> childKey;
+ rv = regKey->OpenChild(childName, nsIWindowsRegKey::ACCESS_QUERY_VALUE,
+ getter_AddRefs(childKey));
+ if (NS_SUCCEEDED(rv) && childKey) {
+ nsAutoString path;
+ rv = childKey->ReadStringValue(NS_LITERAL_STRING("Path"), path);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIFile> localFile;
+ if (NS_SUCCEEDED(NS_NewLocalFile(path, true,
+ getter_AddRefs(localFile))) &&
+ localFile) {
+ // Some vendors use a path directly to the DLL so chop off
+ // the filename
+ bool isDir = false;
+ if (NS_SUCCEEDED(localFile->IsDirectory(&isDir)) && !isDir) {
+ nsCOMPtr<nsIFile> temp;
+ localFile->GetParent(getter_AddRefs(temp));
+ if (temp)
+ localFile = temp;
+ }
+
+ // Now we check to make sure it's actually on disk and
+ // To see if we already have this directory in the array
+ bool isFileThere = false;
+ bool isDupEntry = false;
+ if (NS_SUCCEEDED(localFile->Exists(&isFileThere)) && isFileThere) {
+ int32_t c = aDirs.Count();
+ for (int32_t i = 0; i < c; i++) {
+ nsIFile *dup = static_cast<nsIFile*>(aDirs[i]);
+ if (dup &&
+ NS_SUCCEEDED(dup->Equals(localFile, &isDupEntry)) &&
+ isDupEntry) {
+ break;
+ }
+ }
+
+ if (!isDupEntry) {
+ aDirs.AppendObject(localFile);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginDirServiceProvider.h b/dom/plugins/base/nsPluginDirServiceProvider.h
new file mode 100644
index 000000000..1fc735d02
--- /dev/null
+++ b/dom/plugins/base/nsPluginDirServiceProvider.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef nsPluginDirServiceProvider_h_
+#define nsPluginDirServiceProvider_h_
+
+#include "nsIDirectoryService.h"
+
+#if defined (XP_WIN)
+#include "nsCOMArray.h"
+#endif
+
+class nsISimpleEnumerator;
+
+// Note: Our directory service provider scan keys are prefs which are check
+// for minimum versions compatibility
+#define NS_WIN_ACROBAT_SCAN_KEY "plugin.scan.Acrobat"
+#define NS_WIN_QUICKTIME_SCAN_KEY "plugin.scan.Quicktime"
+#define NS_WIN_WMP_SCAN_KEY "plugin.scan.WindowsMediaPlayer"
+
+//*****************************************************************************
+// class nsPluginDirServiceProvider
+//*****************************************************************************
+
+class nsPluginDirServiceProvider : public nsIDirectoryServiceProvider
+{
+public:
+ nsPluginDirServiceProvider();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+
+#ifdef XP_WIN
+ static nsresult GetPLIDDirectories(nsISimpleEnumerator **aEnumerator);
+private:
+ static nsresult GetPLIDDirectoriesWithRootKey(uint32_t aKey,
+ nsCOMArray<nsIFile> &aDirs);
+#endif
+
+protected:
+ virtual ~nsPluginDirServiceProvider();
+};
+
+#endif // nsPluginDirServiceProvider_h_
diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp
new file mode 100644
index 000000000..6ee23f38b
--- /dev/null
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -0,0 +1,4239 @@
+/* -*- 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/. */
+
+/* nsPluginHost.cpp - top-level plugin management code */
+
+#include "nscore.h"
+#include "nsPluginHost.h"
+
+#include <cstdlib>
+#include <stdio.h>
+#include "prio.h"
+#include "prmem.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsObjectLoadingContent.h"
+#include "nsIHTTPHeaderListener.h"
+#include "nsIHttpHeaderVisitor.h"
+#include "nsIObserverService.h"
+#include "nsIHttpProtocolHandler.h"
+#include "nsIHttpChannel.h"
+#include "nsIUploadChannel.h"
+#include "nsIByteRangeRequest.h"
+#include "nsIStreamListener.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIURL.h"
+#include "nsTArray.h"
+#include "nsReadableUtils.h"
+#include "nsProtocolProxyService.h"
+#include "nsIStreamConverterService.h"
+#include "nsIFile.h"
+#if defined(XP_MACOSX)
+#include "nsILocalFileMac.h"
+#endif
+#include "nsISeekableStream.h"
+#include "nsNetUtil.h"
+#include "nsIFileStreams.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStringStream.h"
+#include "nsIProgressEventSink.h"
+#include "nsIDocument.h"
+#include "nsPluginLogging.h"
+#include "nsIScriptChannel.h"
+#include "nsIBlocklistService.h"
+#include "nsVersionComparator.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsIWritablePropertyBag2.h"
+#include "nsICategoryManager.h"
+#include "nsPluginStreamListenerPeer.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/FakePluginTagInitBinding.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/plugins/PluginAsyncSurrogate.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/plugins/PluginTypes.h"
+#include "mozilla/Preferences.h"
+
+#include "nsEnumeratorUtils.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMCID.h"
+#include "nsISupportsPrimitives.h"
+
+#include "nsXULAppAPI.h"
+#include "nsIXULRuntime.h"
+
+// for the dialog
+#include "nsIWindowWatcher.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMWindow.h"
+
+#include "nsNetCID.h"
+#include "mozilla/Sprintf.h"
+#include "nsThreadUtils.h"
+#include "nsIInputStreamTee.h"
+#include "nsQueryObject.h"
+
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsPluginDirServiceProvider.h"
+
+#include "nsUnicharUtils.h"
+#include "nsPluginManifestLineReader.h"
+
+#include "nsIWeakReferenceUtils.h"
+#include "nsIPresShell.h"
+#include "nsPluginNativeWindow.h"
+#include "nsIContentPolicy.h"
+#include "nsContentPolicyUtils.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Telemetry.h"
+#include "nsIImageLoadingContent.h"
+#include "mozilla/Preferences.h"
+#include "nsVersionComparator.h"
+#include "nsNullPrincipal.h"
+
+#if defined(XP_WIN)
+#include "nsIWindowMediator.h"
+#include "nsIBaseWindow.h"
+#include "windows.h"
+#include "winbase.h"
+#endif
+
+#ifdef ANDROID
+#include <android/log.h>
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#endif
+
+#if MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+#include "npapi.h"
+
+using namespace mozilla;
+using mozilla::TimeStamp;
+using mozilla::plugins::PluginTag;
+using mozilla::plugins::PluginAsyncSurrogate;
+using mozilla::dom::FakePluginTagInit;
+
+// Null out a strong ref to a linked list iteratively to avoid
+// exhausting the stack (bug 486349).
+#define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \
+ { \
+ while (list_) { \
+ type_ temp = list_->mNext_; \
+ list_->mNext_ = nullptr; \
+ list_ = temp; \
+ } \
+ }
+
+// this is the name of the directory which will be created
+// to cache temporary files.
+#define kPluginTmpDirName NS_LITERAL_CSTRING("plugtmp")
+
+static const char *kPrefWhitelist = "plugin.allowed_types";
+static const char *kPrefLoadInParentPrefix = "plugin.load_in_parent_process.";
+static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
+static const char *kPrefJavaMIME = "plugin.java.mime";
+
+// How long we wait before unloading an idle plugin process.
+// Defaults to 30 seconds.
+static const char *kPrefUnloadPluginTimeoutSecs = "dom.ipc.plugins.unloadTimeoutSecs";
+static const uint32_t kDefaultPluginUnloadingTimeout = 30;
+
+static const char *kPluginRegistryVersion = "0.18";
+
+static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
+
+#define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
+
+#ifdef PLUGIN_LOGGING
+LazyLogModule nsPluginLogging::gNPNLog(NPN_LOG_NAME);
+LazyLogModule nsPluginLogging::gNPPLog(NPP_LOG_NAME);
+LazyLogModule nsPluginLogging::gPluginLog(PLUGIN_LOG_NAME);
+#endif
+
+// #defines for plugin cache and prefs
+#define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
+// Raise this from '10' to '50' to work around a bug in Apple's current Java
+// plugins on OS X Lion and SnowLeopard. See bug 705931.
+#define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
+
+nsIFile *nsPluginHost::sPluginTempDir;
+nsPluginHost *nsPluginHost::sInst;
+
+/* to cope with short read */
+/* we should probably put this into a global library now that this is the second
+ time we need this. */
+static
+int32_t
+busy_beaver_PR_Read(PRFileDesc *fd, void * start, int32_t len)
+{
+ int n;
+ int32_t remaining = len;
+
+ while (remaining > 0)
+ {
+ n = PR_Read(fd, start, remaining);
+ if (n < 0)
+ {
+ /* may want to repeat if errno == EINTR */
+ if( (len - remaining) == 0 ) // no octet is ever read
+ return -1;
+ break;
+ }
+ else
+ {
+ remaining -= n;
+ char *cp = (char *) start;
+ cp += n;
+ start = cp;
+ }
+ }
+ return len - remaining;
+}
+
+NS_IMPL_ISUPPORTS0(nsInvalidPluginTag)
+
+nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime)
+: mFullPath(aFullPath),
+ mLastModifiedTime(aLastModifiedTime),
+ mSeen(false)
+{}
+
+nsInvalidPluginTag::~nsInvalidPluginTag()
+{}
+
+// Helper to check for a MIME in a comma-delimited preference
+static bool
+IsTypeInList(const nsCString& aMimeType, nsCString aTypeList)
+{
+ nsAutoCString searchStr;
+ searchStr.Assign(',');
+ searchStr.Append(aTypeList);
+ searchStr.Append(',');
+
+ nsACString::const_iterator start, end;
+
+ searchStr.BeginReading(start);
+ searchStr.EndReading(end);
+
+ nsAutoCString commaSeparated;
+ commaSeparated.Assign(',');
+ commaSeparated += aMimeType;
+ commaSeparated.Append(',');
+
+ // Lower-case the search string and MIME type to properly handle a mixed-case
+ // type, as MIME types are case insensitive.
+ ToLowerCase(searchStr);
+ ToLowerCase(commaSeparated);
+
+ return FindInReadable(commaSeparated, start, end);
+}
+
+// flat file reg funcs
+static
+bool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token)
+{
+ do {
+ if (*reader.LinePtr() == '[') {
+ char* p = reader.LinePtr() + (reader.LineLength() - 1);
+ if (*p != ']')
+ break;
+ *p = 0;
+
+ char* values[1];
+ if (1 != reader.ParseLine(values, 1))
+ break;
+ // ignore the leading '['
+ if (PL_strcmp(values[0]+1, token)) {
+ break; // it's wrong token
+ }
+ return true;
+ }
+ } while (reader.NextLine());
+ return false;
+}
+
+static bool UnloadPluginsASAP()
+{
+ return (Preferences::GetUint(kPrefUnloadPluginTimeoutSecs, kDefaultPluginUnloadingTimeout) == 0);
+}
+
+nsPluginHost::nsPluginHost()
+ // No need to initialize members to nullptr, false etc because this class
+ // has a zeroing operator new.
+{
+ // Bump the pluginchanged epoch on startup. This insures content gets a
+ // good plugin list the first time it requests it. Normally we'd just
+ // init this to 1, but due to the unique nature of our ctor we need to do
+ // this manually.
+ if (XRE_IsParentProcess()) {
+ IncrementChromeEpoch();
+ } else {
+ // When NPAPI requests the proxy setting by calling |FindProxyForURL|,
+ // the service is requested and initialized asynchronously, but
+ // |FindProxyForURL| is synchronous, so we should initialize this earlier.
+ nsCOMPtr<nsIProtocolProxyService> proxyService =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
+ }
+
+ // check to see if pref is set at startup to let plugins take over in
+ // full page mode for certain image mime types that we handle internally
+ mOverrideInternalTypes =
+ Preferences::GetBool("plugin.override_internal_types", false);
+
+ mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
+
+ Preferences::AddStrongObserver(this, "plugin.disable");
+
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService) {
+ obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ obsService->AddObserver(this, "blocklist-updated", false);
+#ifdef MOZ_WIDGET_ANDROID
+ obsService->AddObserver(this, "application-foreground", false);
+ obsService->AddObserver(this, "application-background", false);
+#endif
+ }
+
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
+ MOZ_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
+
+ PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
+ PR_LogFlush();
+#endif
+}
+
+nsPluginHost::~nsPluginHost()
+{
+ PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
+
+ UnloadPlugins();
+ sInst = nullptr;
+}
+
+NS_IMPL_ISUPPORTS(nsPluginHost,
+ nsIPluginHost,
+ nsIObserver,
+ nsITimerCallback,
+ nsISupportsWeakReference)
+
+already_AddRefed<nsPluginHost>
+nsPluginHost::GetInst()
+{
+ if (!sInst) {
+ sInst = new nsPluginHost();
+ if (!sInst)
+ return nullptr;
+ NS_ADDREF(sInst);
+ }
+
+ RefPtr<nsPluginHost> inst = sInst;
+ return inst.forget();
+}
+
+bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
+{
+ if (!aPluginTag || !aPluginTag->mPlugin) {
+ return false;
+ }
+
+ if (aPluginTag->mContentProcessRunningCount) {
+ return true;
+ }
+
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ nsNPAPIPluginInstance *instance = mInstances[i].get();
+ if (instance &&
+ instance->GetPlugin() == aPluginTag->mPlugin &&
+ instance->IsRunning()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+nsresult nsPluginHost::ReloadPlugins()
+{
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::ReloadPlugins Begin\n"));
+
+ nsresult rv = NS_OK;
+
+ // this will create the initial plugin list out of cache
+ // if it was not created yet
+ if (!mPluginsLoaded)
+ return LoadPlugins();
+
+ // we are re-scanning plugins. New plugins may have been added, also some
+ // plugins may have been removed, so we should probably shut everything down
+ // but don't touch running (active and not stopped) plugins
+
+ // check if plugins changed, no need to do anything else
+ // if no changes to plugins have been made
+ // false instructs not to touch the plugin list, just to
+ // look for possible changes
+ bool pluginschanged = true;
+ FindPlugins(false, &pluginschanged);
+
+ // if no changed detected, return an appropriate error code
+ if (!pluginschanged)
+ return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
+
+ // shutdown plugins and kill the list if there are no running plugins
+ RefPtr<nsPluginTag> prev;
+ RefPtr<nsPluginTag> next;
+
+ for (RefPtr<nsPluginTag> p = mPlugins; p != nullptr;) {
+ next = p->mNext;
+
+ // only remove our plugin from the list if it's not running.
+ if (!IsRunningPlugin(p)) {
+ if (p == mPlugins)
+ mPlugins = next;
+ else
+ prev->mNext = next;
+
+ p->mNext = nullptr;
+
+ // attempt to unload plugins whenever they are removed from the list
+ p->TryUnloadPlugin(false);
+
+ p = next;
+ continue;
+ }
+
+ prev = p;
+ p = next;
+ }
+
+ // set flags
+ mPluginsLoaded = false;
+
+ // load them again
+ rv = LoadPlugins();
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::ReloadPlugins End\n"));
+
+ return rv;
+}
+
+#define NS_RETURN_UASTRING_SIZE 128
+
+nsresult nsPluginHost::UserAgent(const char **retstring)
+{
+ static char resultString[NS_RETURN_UASTRING_SIZE];
+ nsresult res;
+
+ nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
+ if (NS_FAILED(res))
+ return res;
+
+ nsAutoCString uaString;
+ res = http->GetUserAgent(uaString);
+
+ if (NS_SUCCEEDED(res)) {
+ if (NS_RETURN_UASTRING_SIZE > uaString.Length()) {
+ PL_strcpy(resultString, uaString.get());
+ } else {
+ // Copy as much of UA string as we can (terminate at right-most space).
+ PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE);
+ for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) {
+ if (i == 0) {
+ resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0';
+ }
+ else if (resultString[i] == ' ') {
+ resultString[i] = '\0';
+ break;
+ }
+ }
+ }
+ *retstring = resultString;
+ }
+ else {
+ *retstring = nullptr;
+ }
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UserAgent return=%s\n", *retstring));
+
+ return res;
+}
+
+nsresult nsPluginHost::GetURL(nsISupports* pluginInst,
+ const char* url,
+ const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost,
+ const char* referrer,
+ bool forceJSEnabled)
+{
+ return GetURLWithHeaders(static_cast<nsNPAPIPluginInstance*>(pluginInst),
+ url, target, streamListener, altHost, referrer,
+ forceJSEnabled, 0, nullptr);
+}
+
+nsresult nsPluginHost::GetURLWithHeaders(nsNPAPIPluginInstance* pluginInst,
+ const char* url,
+ const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost,
+ const char* referrer,
+ bool forceJSEnabled,
+ uint32_t getHeadersLength,
+ const char* getHeaders)
+{
+ // we can only send a stream back to the plugin (as specified by a
+ // null target) if we also have a nsNPAPIPluginStreamListener to talk to
+ if (!target && !streamListener) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ nsresult rv = NS_OK;
+
+ if (target) {
+ RefPtr<nsPluginInstanceOwner> owner = pluginInst->GetOwner();
+ if (owner) {
+ rv = owner->GetURL(url, target, nullptr, nullptr, 0, true);
+ }
+ }
+
+ if (streamListener) {
+ rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), pluginInst,
+ streamListener, nullptr,
+ getHeaders, getHeadersLength);
+ }
+ return rv;
+}
+
+nsresult nsPluginHost::PostURL(nsISupports* pluginInst,
+ const char* url,
+ uint32_t postDataLen,
+ const char* postData,
+ bool isFile,
+ const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost,
+ const char* referrer,
+ bool forceJSEnabled,
+ uint32_t postHeadersLength,
+ const char* postHeaders)
+{
+ nsresult rv;
+
+ // we can only send a stream back to the plugin (as specified
+ // by a null target) if we also have a nsNPAPIPluginStreamListener
+ // to talk to also
+ if (!target && !streamListener)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ nsNPAPIPluginInstance* instance = static_cast<nsNPAPIPluginInstance*>(pluginInst);
+
+ nsCOMPtr<nsIInputStream> postStream;
+ if (isFile) {
+ nsCOMPtr<nsIFile> file;
+ rv = CreateTempFileToPost(postData, getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIInputStream> fileStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream),
+ file,
+ PR_RDONLY,
+ 0600,
+ nsIFileInputStream::DELETE_ON_CLOSE |
+ nsIFileInputStream::CLOSE_ON_EOF);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = NS_NewBufferedInputStream(getter_AddRefs(postStream), fileStream, 8192);
+ if (NS_FAILED(rv))
+ return rv;
+ } else {
+ char *dataToPost;
+ uint32_t newDataToPostLen;
+ ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost, &newDataToPostLen);
+ if (!dataToPost)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIStringInputStream> sis = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
+ if (!sis) {
+ free(dataToPost);
+ return rv;
+ }
+
+ // data allocated by ParsePostBufferToFixHeaders() is managed and
+ // freed by the string stream.
+ postDataLen = newDataToPostLen;
+ sis->AdoptData(dataToPost, postDataLen);
+ postStream = sis;
+ }
+
+ if (target) {
+ RefPtr<nsPluginInstanceOwner> owner = instance->GetOwner();
+ if (owner) {
+ rv = owner->GetURL(url, target, postStream,
+ (void*)postHeaders, postHeadersLength, true);
+ }
+ }
+
+ // if we don't have a target, just create a stream.
+ if (streamListener) {
+ rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), instance,
+ streamListener,
+ postStream, postHeaders, postHeadersLength);
+ }
+ return rv;
+}
+
+/* This method queries the prefs for proxy information.
+ * It has been tested and is known to work in the following three cases
+ * when no proxy host or port is specified
+ * when only the proxy host is specified
+ * when only the proxy port is specified
+ * This method conforms to the return code specified in
+ * http://developer.netscape.com/docs/manuals/proxy/adminnt/autoconf.htm#1020923
+ * with the exception that multiple values are not implemented.
+ */
+
+nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result)
+{
+ if (!url || !result) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsresult res;
+
+ nsCOMPtr<nsIProtocolProxyService> proxyService =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
+ if (NS_FAILED(res) || !proxyService)
+ return res;
+
+ RefPtr<nsProtocolProxyService> rawProxyService = do_QueryObject(proxyService);
+ if (!rawProxyService) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // make a temporary channel from the argument url
+ nsCOMPtr<nsIURI> uri;
+ res = NS_NewURI(getter_AddRefs(uri), nsDependentCString(url));
+ NS_ENSURE_SUCCESS(res, res);
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
+ // The following channel is never openend, so it does not matter what
+ // securityFlags we pass; let's follow the principle of least privilege.
+ nsCOMPtr<nsIChannel> tempChannel;
+ res = NS_NewChannel(getter_AddRefs(tempChannel), uri, nullPrincipal,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+ nsIContentPolicy::TYPE_OTHER);
+ NS_ENSURE_SUCCESS(res, res);
+
+ nsCOMPtr<nsIProxyInfo> pi;
+
+ // Remove this deprecated call in the future (see Bug 778201):
+ res = rawProxyService->DeprecatedBlockingResolve(tempChannel, 0, getter_AddRefs(pi));
+ if (NS_FAILED(res))
+ return res;
+
+ nsAutoCString host, type;
+ int32_t port = -1;
+
+ // These won't fail, and even if they do... we'll be ok.
+ if (pi) {
+ pi->GetType(type);
+ pi->GetHost(host);
+ pi->GetPort(&port);
+ }
+
+ if (!pi || host.IsEmpty() || port <= 0 || host.EqualsLiteral("direct")) {
+ *result = PL_strdup("DIRECT");
+ } else if (type.EqualsLiteral("http")) {
+ *result = PR_smprintf("PROXY %s:%d", host.get(), port);
+ } else if (type.EqualsLiteral("socks4")) {
+ *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
+ } else if (type.EqualsLiteral("socks")) {
+ // XXX - this is socks5, but there is no API for us to tell the
+ // plugin that fact. SOCKS for now, in case the proxy server
+ // speaks SOCKS4 as well. See bug 78176
+ // For a long time this was returning an http proxy type, so
+ // very little is probably broken by this
+ *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
+ } else {
+ NS_ASSERTION(false, "Unknown proxy type!");
+ *result = PL_strdup("DIRECT");
+ }
+
+ if (nullptr == *result)
+ res = NS_ERROR_OUT_OF_MEMORY;
+
+ return res;
+}
+
+nsresult nsPluginHost::UnloadPlugins()
+{
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n"));
+
+ if (!mPluginsLoaded)
+ return NS_OK;
+
+ // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
+ // for those plugins who want it
+ DestroyRunningInstances(nullptr);
+
+ nsPluginTag *pluginTag;
+ for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
+ pluginTag->TryUnloadPlugin(true);
+ }
+
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mPlugins, mNext);
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
+
+ // Lets remove any of the temporary files that we created.
+ if (sPluginTempDir) {
+ sPluginTempDir->Remove(true);
+ NS_RELEASE(sPluginTempDir);
+ }
+
+#ifdef XP_WIN
+ if (mPrivateDirServiceProvider) {
+ nsCOMPtr<nsIDirectoryService> dirService =
+ do_GetService(kDirectoryServiceContractID);
+ if (dirService)
+ dirService->UnregisterProvider(mPrivateDirServiceProvider);
+ mPrivateDirServiceProvider = nullptr;
+ }
+#endif /* XP_WIN */
+
+ mPluginsLoaded = false;
+
+ return NS_OK;
+}
+
+void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag)
+{
+ bool hasInstance = false;
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) {
+ hasInstance = true;
+ break;
+ }
+ }
+
+ // We have some options for unloading plugins if they have no instances.
+ //
+ // Unloading plugins immediately can be bad - some plugins retain state
+ // between instances even when there are none. This is largely limited to
+ // going from one page to another, so state is retained without an instance
+ // for only a very short period of time. In order to allow this to work
+ // we don't unload plugins immediately by default. This is supported
+ // via a hidden user pref though.
+ //
+ // Another reason not to unload immediately is that loading is expensive,
+ // and it is better to leave popular plugins loaded.
+ //
+ // Our default behavior is to try to unload a plugin after a pref-controlled
+ // delay once its last instance is destroyed. This seems like a reasonable
+ // compromise that allows us to reclaim memory while allowing short state
+ // retention and avoid perf hits for loading popular plugins.
+ if (!hasInstance) {
+ if (UnloadPluginsASAP()) {
+ aPluginTag->TryUnloadPlugin(false);
+ } else {
+ if (aPluginTag->mUnloadTimer) {
+ aPluginTag->mUnloadTimer->Cancel();
+ } else {
+ aPluginTag->mUnloadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ }
+ uint32_t unloadTimeout = Preferences::GetUint(kPrefUnloadPluginTimeoutSecs,
+ kDefaultPluginUnloadingTimeout);
+ aPluginTag->mUnloadTimer->InitWithCallback(this,
+ 1000 * unloadTimeout,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+ }
+}
+
+nsresult
+nsPluginHost::GetPluginTempDir(nsIFile **aDir)
+{
+ if (!sPluginTempDir) {
+ nsCOMPtr<nsIFile> tmpDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+ getter_AddRefs(tmpDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = tmpDir->AppendNative(kPluginTmpDirName);
+
+ // make it unique, and mode == 0700, not world-readable
+ rv = tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ tmpDir.swap(sPluginTempDir);
+ }
+
+ return sPluginTempDir->Clone(aDir);
+}
+
+nsresult
+nsPluginHost::InstantiatePluginInstance(const nsACString& aMimeType, nsIURI* aURL,
+ nsObjectLoadingContent *aContent,
+ nsPluginInstanceOwner** aOwner)
+{
+ NS_ENSURE_ARG_POINTER(aOwner);
+
+#ifdef PLUGIN_LOGGING
+ nsAutoCString urlSpec;
+ if (aURL)
+ aURL->GetAsciiSpec(urlSpec);
+
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
+ PromiseFlatCString(aMimeType).get(), urlSpec.get()));
+
+ PR_LogFlush();
+#endif
+
+ if (aMimeType.IsEmpty()) {
+ NS_NOTREACHED("Attempting to spawn a plugin with no mime type");
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
+ if (!instanceOwner) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
+ nsresult rv = instanceOwner->Init(ourContent);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsPluginTagType tagType;
+ rv = instanceOwner->GetTagType(&tagType);
+ if (NS_FAILED(rv)) {
+ instanceOwner->Destroy();
+ return rv;
+ }
+
+ if (tagType != nsPluginTagType_Embed &&
+ tagType != nsPluginTagType_Applet &&
+ tagType != nsPluginTagType_Object) {
+ instanceOwner->Destroy();
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
+ if (NS_FAILED(rv)) {
+ instanceOwner->Destroy();
+ return NS_ERROR_FAILURE;
+ }
+ const bool isAsyncInit = (rv == NS_PLUGIN_INIT_PENDING);
+
+ RefPtr<nsNPAPIPluginInstance> instance;
+ rv = instanceOwner->GetInstance(getter_AddRefs(instance));
+ if (NS_FAILED(rv)) {
+ instanceOwner->Destroy();
+ return rv;
+ }
+
+ // Async init plugins will initiate their own widget creation.
+ if (!isAsyncInit && instance) {
+ CreateWidget(instanceOwner);
+ }
+
+ // At this point we consider instantiation to be successful. Do not return an error.
+ instanceOwner.forget(aOwner);
+
+#ifdef PLUGIN_LOGGING
+ nsAutoCString urlSpec2;
+ if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
+
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%d, url=%s\n",
+ PromiseFlatCString(aMimeType).get(), rv, urlSpec2.get()));
+
+ PR_LogFlush();
+#endif
+
+ return NS_OK;
+}
+
+nsPluginTag*
+nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary)
+{
+ nsPluginTag* pluginTag;
+ for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
+ if (pluginTag->mLibrary == aLibrary) {
+ return pluginTag;
+ }
+ }
+ return nullptr;
+}
+
+nsPluginTag*
+nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin)
+{
+ nsPluginTag* pluginTag;
+ for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
+ if (pluginTag->mPlugin == aPlugin) {
+ return pluginTag;
+ }
+ }
+ // a plugin should never exist without a corresponding tag
+ NS_ERROR("TagForPlugin has failed");
+ return nullptr;
+}
+
+nsresult nsPluginHost::SetUpPluginInstance(const nsACString &aMimeType,
+ nsIURI *aURL,
+ nsPluginInstanceOwner *aOwner)
+{
+ NS_ENSURE_ARG_POINTER(aOwner);
+
+ nsresult rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+
+ // If we failed to load a plugin instance we'll try again after
+ // reloading our plugin list. Only do that once per document to
+ // avoid redundant high resource usage on pages with multiple
+ // unkown instance types. We'll do that by caching the document.
+ nsCOMPtr<nsIDocument> document;
+ aOwner->GetDocument(getter_AddRefs(document));
+
+ nsCOMPtr<nsIDocument> currentdocument = do_QueryReferent(mCurrentDocument);
+ if (document == currentdocument) {
+ return rv;
+ }
+
+ mCurrentDocument = do_GetWeakReference(document);
+
+ // Don't try to set up an instance again if nothing changed.
+ if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
+ return rv;
+ }
+
+ return TrySetUpPluginInstance(aMimeType, aURL, aOwner);
+}
+
+nsresult
+nsPluginHost::TrySetUpPluginInstance(const nsACString &aMimeType,
+ nsIURI *aURL,
+ nsPluginInstanceOwner *aOwner)
+{
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
+ PromiseFlatCString(aMimeType).get(), aOwner,
+ aURL ? aURL->GetSpecOrDefault().get() : ""));
+
+ PR_LogFlush();
+#endif
+
+#ifdef XP_WIN
+ bool changed;
+ if ((mRegKeyHKLM && NS_SUCCEEDED(mRegKeyHKLM->HasChanged(&changed)) && changed) ||
+ (mRegKeyHKCU && NS_SUCCEEDED(mRegKeyHKCU->HasChanged(&changed)) && changed)) {
+ ReloadPlugins();
+ }
+#endif
+
+ RefPtr<nsNPAPIPlugin> plugin;
+ GetPlugin(aMimeType, getter_AddRefs(plugin));
+ if (!plugin) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPluginTag* pluginTag = FindNativePluginForType(aMimeType, true);
+
+ NS_ASSERTION(pluginTag, "Must have plugin tag here!");
+
+ plugin->GetLibrary()->SetHasLocalInstance();
+
+#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_CRASHREPORTER)
+ if (pluginTag->mIsFlashPlugin) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FlashVersion"), pluginTag->Version());
+ }
+#endif
+
+ RefPtr<nsNPAPIPluginInstance> instance = new nsNPAPIPluginInstance();
+
+ // This will create the owning reference. The connection must be made between the
+ // instance and the instance owner before initialization. Plugins can call into
+ // the browser during initialization.
+ aOwner->SetInstance(instance.get());
+
+ // Add the instance to the instances list before we call NPP_New so that
+ // it is "in play" before NPP_New happens. Take it out if NPP_New fails.
+ mInstances.AppendElement(instance.get());
+
+ // this should not addref the instance or owner
+ // except in some cases not Java, see bug 140931
+ // our COM pointer will free the peer
+ nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
+ if (NS_FAILED(rv)) {
+ mInstances.RemoveElement(instance.get());
+ aOwner->SetInstance(nullptr);
+ return rv;
+ }
+
+ // Cancel the plugin unload timer since we are creating
+ // an instance for it.
+ if (pluginTag->mUnloadTimer) {
+ pluginTag->mUnloadTimer->Cancel();
+ }
+
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
+ ("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%d, owner=%p, url=%s\n",
+ PromiseFlatCString(aMimeType).get(), rv, aOwner,
+ aURL ? aURL->GetSpecOrDefault().get() : ""));
+
+ PR_LogFlush();
+#endif
+
+ return rv;
+}
+
+bool
+nsPluginHost::HavePluginForType(const nsACString & aMimeType,
+ PluginFilter aFilter)
+{
+ bool checkEnabled = aFilter & eExcludeDisabled;
+ bool allowFake = !(aFilter & eExcludeFake);
+ return FindPluginForType(aMimeType, allowFake, checkEnabled);
+}
+
+nsIInternalPluginTag*
+nsPluginHost::FindPluginForType(const nsACString& aMimeType,
+ bool aIncludeFake, bool aCheckEnabled)
+{
+ if (aIncludeFake) {
+ nsFakePluginTag* fakeTag = FindFakePluginForType(aMimeType, aCheckEnabled);
+ if (fakeTag) {
+ return fakeTag;
+ }
+ }
+
+ return FindNativePluginForType(aMimeType, aCheckEnabled);
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetPluginTagForType(const nsACString& aMimeType,
+ uint32_t aExcludeFlags,
+ nsIPluginTag** aResult)
+{
+ bool includeFake = !(aExcludeFlags & eExcludeFake);
+ bool includeDisabled = !(aExcludeFlags & eExcludeDisabled);
+
+ // First look for an enabled plugin.
+ RefPtr<nsIInternalPluginTag> tag = FindPluginForType(aMimeType, includeFake,
+ true);
+ if (!tag && includeDisabled) {
+ tag = FindPluginForType(aMimeType, includeFake, false);
+ }
+
+ if (tag) {
+ tag.forget(aResult);
+ return NS_OK;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetStateForType(const nsACString &aMimeType,
+ uint32_t aExcludeFlags,
+ uint32_t* aResult)
+{
+ nsCOMPtr<nsIPluginTag> tag;
+ nsresult rv = GetPluginTagForType(aMimeType, aExcludeFlags,
+ getter_AddRefs(tag));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return tag->GetEnabledState(aResult);
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetBlocklistStateForType(const nsACString &aMimeType,
+ uint32_t aExcludeFlags,
+ uint32_t *aState)
+{
+ nsCOMPtr<nsIPluginTag> tag;
+ nsresult rv = GetPluginTagForType(aMimeType,
+ aExcludeFlags,
+ getter_AddRefs(tag));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return tag->GetBlocklistState(aState);
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType,
+ uint32_t aExcludeFlags,
+ nsACString &aPermissionString)
+{
+ nsCOMPtr<nsIPluginTag> tag;
+ nsresult rv = GetPluginTagForType(aMimeType, aExcludeFlags,
+ getter_AddRefs(tag));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return GetPermissionStringForTag(tag, aExcludeFlags, aPermissionString);
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetPermissionStringForTag(nsIPluginTag* aTag,
+ uint32_t aExcludeFlags,
+ nsACString &aPermissionString)
+{
+ NS_ENSURE_TRUE(aTag, NS_ERROR_FAILURE);
+
+ aPermissionString.Truncate();
+ uint32_t blocklistState;
+ nsresult rv = aTag->GetBlocklistState(&blocklistState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
+ blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
+ aPermissionString.AssignLiteral("plugin-vulnerable:");
+ }
+ else {
+ aPermissionString.AssignLiteral("plugin:");
+ }
+
+ nsCString niceName;
+ rv = aTag->GetNiceName(niceName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(!niceName.IsEmpty(), NS_ERROR_FAILURE);
+
+ aPermissionString.Append(niceName);
+
+ return NS_OK;
+}
+
+bool
+nsPluginHost::HavePluginForExtension(const nsACString & aExtension,
+ /* out */ nsACString & aMimeType,
+ PluginFilter aFilter)
+{
+ bool checkEnabled = aFilter & eExcludeDisabled;
+ bool allowFake = !(aFilter & eExcludeFake);
+ return FindNativePluginForExtension(aExtension, aMimeType, checkEnabled) ||
+ (allowFake &&
+ FindFakePluginForExtension(aExtension, aMimeType, checkEnabled));
+}
+
+void
+nsPluginHost::GetPlugins(nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray,
+ bool aIncludeDisabled)
+{
+ aPluginArray.Clear();
+
+ LoadPlugins();
+
+ // Append fake plugins, then normal plugins.
+
+ uint32_t numFake = mFakePlugins.Length();
+ for (uint32_t i = 0; i < numFake; i++) {
+ aPluginArray.AppendElement(mFakePlugins[i]);
+ }
+
+ // Regular plugins
+ nsPluginTag* plugin = mPlugins;
+ while (plugin != nullptr) {
+ if (plugin->IsEnabled() || aIncludeDisabled) {
+ aPluginArray.AppendElement(plugin);
+ }
+ plugin = plugin->mNext;
+ }
+}
+
+// FIXME-jsplugins Check users for order of fake v non-fake
+NS_IMETHODIMP
+nsPluginHost::GetPluginTags(uint32_t* aPluginCount, nsIPluginTag*** aResults)
+{
+ LoadPlugins();
+
+ uint32_t count = 0;
+ uint32_t fakeCount = mFakePlugins.Length();
+ RefPtr<nsPluginTag> plugin = mPlugins;
+ while (plugin != nullptr) {
+ count++;
+ plugin = plugin->mNext;
+ }
+
+ *aResults = static_cast<nsIPluginTag**>
+ (moz_xmalloc((fakeCount + count) * sizeof(**aResults)));
+ if (!*aResults)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ *aPluginCount = count + fakeCount;
+
+ plugin = mPlugins;
+ for (uint32_t i = 0; i < count; i++) {
+ (*aResults)[i] = plugin;
+ NS_ADDREF((*aResults)[i]);
+ plugin = plugin->mNext;
+ }
+
+ for (uint32_t i = 0; i < fakeCount; i++) {
+ (*aResults)[i + count] = static_cast<nsIInternalPluginTag*>(mFakePlugins[i]);
+ NS_ADDREF((*aResults)[i + count]);
+ }
+
+ return NS_OK;
+}
+
+nsPluginTag*
+nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
+{
+ // We prefer the plugin with the highest version number.
+ /// XXX(johns): This seems to assume the only time multiple plugins will have
+ /// the same MIME type is if they're multiple versions of the same
+ /// plugin -- but since plugin filenames and pretty names can both
+ /// update, it's probably less arbitrary than just going at it
+ /// alphabetically.
+
+ if (matches.IsEmpty()) {
+ return nullptr;
+ }
+
+ nsPluginTag *preferredPlugin = matches[0];
+ for (unsigned int i = 1; i < matches.Length(); i++) {
+ if (mozilla::Version(matches[i]->Version().get()) > preferredPlugin->Version().get()) {
+ preferredPlugin = matches[i];
+ }
+ }
+
+ return preferredPlugin;
+}
+
+nsFakePluginTag*
+nsPluginHost::FindFakePluginForExtension(const nsACString & aExtension,
+ /* out */ nsACString & aMimeType,
+ bool aCheckEnabled)
+{
+ if (aExtension.IsEmpty()) {
+ return nullptr;
+ }
+
+ int32_t numFakePlugins = mFakePlugins.Length();
+ for (int32_t i = 0; i < numFakePlugins; i++) {
+ nsFakePluginTag *plugin = mFakePlugins[i];
+ bool active;
+ if ((!aCheckEnabled ||
+ (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) &&
+ plugin->HasExtension(aExtension, aMimeType)) {
+ return plugin;
+ }
+ }
+
+ return nullptr;
+}
+
+nsFakePluginTag*
+nsPluginHost::FindFakePluginForType(const nsACString & aMimeType,
+ bool aCheckEnabled)
+{
+ int32_t numFakePlugins = mFakePlugins.Length();
+ for (int32_t i = 0; i < numFakePlugins; i++) {
+ nsFakePluginTag *plugin = mFakePlugins[i];
+ bool active;
+ if ((!aCheckEnabled ||
+ (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) &&
+ plugin->HasMimeType(aMimeType)) {
+ return plugin;
+ }
+ }
+
+ return nullptr;
+}
+
+nsPluginTag*
+nsPluginHost::FindNativePluginForType(const nsACString & aMimeType,
+ bool aCheckEnabled)
+{
+ if (aMimeType.IsEmpty()) {
+ return nullptr;
+ }
+
+ LoadPlugins();
+
+ InfallibleTArray<nsPluginTag*> matchingPlugins;
+
+ nsPluginTag *plugin = mPlugins;
+ while (plugin) {
+ if ((!aCheckEnabled || plugin->IsActive()) &&
+ plugin->HasMimeType(aMimeType)) {
+ matchingPlugins.AppendElement(plugin);
+ }
+ plugin = plugin->mNext;
+ }
+
+ return FindPreferredPlugin(matchingPlugins);
+}
+
+nsPluginTag*
+nsPluginHost::FindNativePluginForExtension(const nsACString & aExtension,
+ /* out */ nsACString & aMimeType,
+ bool aCheckEnabled)
+{
+ if (aExtension.IsEmpty()) {
+ return nullptr;
+ }
+
+ LoadPlugins();
+
+ InfallibleTArray<nsPluginTag*> matchingPlugins;
+ nsCString matchingMime; // Don't mutate aMimeType unless returning a match
+ nsPluginTag *plugin = mPlugins;
+
+ while (plugin) {
+ if (!aCheckEnabled || plugin->IsActive()) {
+ if (plugin->HasExtension(aExtension, matchingMime)) {
+ matchingPlugins.AppendElement(plugin);
+ }
+ }
+ plugin = plugin->mNext;
+ }
+
+ nsPluginTag *preferredPlugin = FindPreferredPlugin(matchingPlugins);
+ if (!preferredPlugin) {
+ return nullptr;
+ }
+
+ // Re-fetch the matching type because of how FindPreferredPlugin works...
+ preferredPlugin->HasExtension(aExtension, aMimeType);
+ return preferredPlugin;
+}
+
+static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag,
+ nsNPAPIPlugin **aOutNPAPIPlugin)
+{
+ // If this is an in-process plugin we'll need to load it here if we haven't already.
+ if (!nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
+ if (aPluginTag->mFullPath.IsEmpty())
+ return NS_ERROR_FAILURE;
+ nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
+ file->InitWithPath(NS_ConvertUTF8toUTF16(aPluginTag->mFullPath));
+ nsPluginFile pluginFile(file);
+ PRLibrary* pluginLibrary = nullptr;
+
+ if (NS_FAILED(pluginFile.LoadPlugin(&pluginLibrary)) || !pluginLibrary)
+ return NS_ERROR_FAILURE;
+
+ aPluginTag->mLibrary = pluginLibrary;
+ }
+
+ nsresult rv;
+ rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
+
+ return rv;
+}
+
+nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
+{
+ RefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
+ if (!plugin) {
+ nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aPluginTag->mPlugin = plugin;
+ }
+ return NS_OK;
+}
+
+nsresult
+nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // If plugins haven't been scanned yet, do so now
+ LoadPlugins();
+
+ nsPluginTag* pluginTag = PluginWithId(aPluginId);
+ if (pluginTag) {
+ // When setting up a bridge, double check with chrome to see if this plugin
+ // is blocked hard. Note this does not protect against vulnerable plugins
+ // that the user has explicitly allowed. :(
+ if (pluginTag->IsBlocklisted()) {
+ return NS_ERROR_PLUGIN_BLOCKLISTED;
+ }
+
+ nsresult rv = EnsurePluginLoaded(pluginTag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // We only get here if a content process doesn't have a PluginModuleParent
+ // for the given plugin already. Therefore, this counter is counting the
+ // number of outstanding PluginModuleParents for the plugin, excluding the
+ // one from the chrome process.
+ pluginTag->mContentProcessRunningCount++;
+ NS_ADDREF(*aPlugin = pluginTag->mPlugin);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+class nsPluginUnloadRunnable : public Runnable
+{
+public:
+ explicit nsPluginUnloadRunnable(uint32_t aPluginId) : mPluginId(aPluginId) {}
+
+ NS_IMETHOD Run() override
+ {
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (!host) {
+ return NS_OK;
+ }
+ nsPluginTag* pluginTag = host->PluginWithId(mPluginId);
+ if (!pluginTag) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0);
+ pluginTag->mContentProcessRunningCount--;
+
+ if (!pluginTag->mContentProcessRunningCount) {
+ if (!host->IsRunningPlugin(pluginTag)) {
+ pluginTag->TryUnloadPlugin(false);
+ }
+ }
+ return NS_OK;
+ }
+
+protected:
+ uint32_t mPluginId;
+};
+
+void
+nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // This is called in response to a message from the plugin. Don't unload the
+ // plugin until the message handler is off the stack.
+ RefPtr<nsPluginUnloadRunnable> runnable =
+ new nsPluginUnloadRunnable(aPluginId);
+ NS_DispatchToMainThread(runnable);
+}
+
+nsresult nsPluginHost::GetPlugin(const nsACString &aMimeType,
+ nsNPAPIPlugin** aPlugin)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+ *aPlugin = nullptr;
+
+ // If plugins haven't been scanned yet, do so now
+ LoadPlugins();
+
+ nsPluginTag* pluginTag = FindNativePluginForType(aMimeType, true);
+ if (pluginTag) {
+ rv = NS_OK;
+ PLUGIN_LOG(PLUGIN_LOG_BASIC,
+ ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
+ PromiseFlatCString(aMimeType).get(), pluginTag->FileName().get()));
+
+#ifdef DEBUG
+ if (!pluginTag->FileName().IsEmpty())
+ printf("For %s found plugin %s\n",
+ PromiseFlatCString(aMimeType).get(), pluginTag->FileName().get());
+#endif
+
+ rv = EnsurePluginLoaded(pluginTag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ADDREF(*aPlugin = pluginTag->mPlugin);
+ return NS_OK;
+ }
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::GetPlugin End mime=%s, rv=%d, plugin=%p name=%s\n",
+ PromiseFlatCString(aMimeType).get(), rv, *aPlugin,
+ (pluginTag ? pluginTag->FileName().get() : "(not found)")));
+
+ return rv;
+}
+
+// Normalize 'host' to ACE.
+nsresult
+nsPluginHost::NormalizeHostname(nsCString& host)
+{
+ if (IsASCII(host)) {
+ ToLowerCase(host);
+ return NS_OK;
+ }
+
+ if (!mIDNService) {
+ nsresult rv;
+ mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return mIDNService->ConvertUTF8toACE(host, host);
+}
+
+// Enumerate a 'sites' array returned by GetSitesWithData and determine if
+// any of them have a base domain in common with 'domain'; if so, append them
+// to the 'result' array. If 'firstMatchOnly' is true, return after finding the
+// first match.
+nsresult
+nsPluginHost::EnumerateSiteData(const nsACString& domain,
+ const InfallibleTArray<nsCString>& sites,
+ InfallibleTArray<nsCString>& result,
+ bool firstMatchOnly)
+{
+ NS_ASSERTION(!domain.IsVoid(), "null domain string");
+
+ nsresult rv;
+ if (!mTLDService) {
+ mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Get the base domain from the domain.
+ nsCString baseDomain;
+ rv = mTLDService->GetBaseDomainFromHost(domain, 0, baseDomain);
+ bool isIP = rv == NS_ERROR_HOST_IS_IP_ADDRESS;
+ if (isIP || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+ // The base domain is the site itself. However, we must be careful to
+ // normalize.
+ baseDomain = domain;
+ rv = NormalizeHostname(baseDomain);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Enumerate the array of sites with data.
+ for (uint32_t i = 0; i < sites.Length(); ++i) {
+ const nsCString& site = sites[i];
+
+ // Check if the site is an IP address.
+ bool siteIsIP =
+ site.Length() >= 2 && site.First() == '[' && site.Last() == ']';
+ if (siteIsIP != isIP)
+ continue;
+
+ nsCString siteBaseDomain;
+ if (siteIsIP) {
+ // Strip the '[]'.
+ siteBaseDomain = Substring(site, 1, site.Length() - 2);
+ } else {
+ // Determine the base domain of the site.
+ rv = mTLDService->GetBaseDomainFromHost(site, 0, siteBaseDomain);
+ if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+ // The base domain is the site itself. However, we must be careful to
+ // normalize.
+ siteBaseDomain = site;
+ rv = NormalizeHostname(siteBaseDomain);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ // At this point, we can do an exact comparison of the two domains.
+ if (baseDomain != siteBaseDomain) {
+ continue;
+ }
+
+ // Append the site to the result array.
+ result.AppendElement(site);
+
+ // If we're supposed to return early, do so.
+ if (firstMatchOnly) {
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginHost::RegisterFakePlugin(JS::Handle<JS::Value> aInitDictionary,
+ JSContext* aCx,
+ nsIFakePluginTag **aResult)
+{
+ FakePluginTagInit initDictionary;
+ if (!initDictionary.Init(aCx, aInitDictionary)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsFakePluginTag> newTag;
+ nsresult rv = nsFakePluginTag::Create(initDictionary, getter_AddRefs(newTag));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (auto existingTag : mFakePlugins) {
+ if (newTag->HandlerURIMatches(existingTag->HandlerURI())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ mFakePlugins.AppendElement(newTag);
+ // FIXME-jsplugins do we need to register with the category manager here? For
+ // shumway, for now, probably not.
+ newTag.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginHost::UnregisterFakePlugin(const nsACString& aHandlerURI)
+{
+ nsCOMPtr<nsIURI> handlerURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(handlerURI), aHandlerURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t i = 0; i < mFakePlugins.Length(); ++i) {
+ if (mFakePlugins[i]->HandlerURIMatches(handlerURI)) {
+ mFakePlugins.RemoveElementAt(i);
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+// FIXME-jsplugins Is this method actually needed?
+NS_IMETHODIMP
+nsPluginHost::GetFakePlugin(const nsACString & aMimeType,
+ nsIFakePluginTag** aResult)
+{
+ RefPtr<nsFakePluginTag> result = FindFakePluginForType(aMimeType, false);
+ if (result) {
+ result.forget(aResult);
+ return NS_OK;
+ }
+
+ *aResult = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+#define ClearDataFromSitesClosure_CID {0x9fb21761, 0x2403, 0x41ad, {0x9e, 0xfd, 0x36, 0x7e, 0xc4, 0x4f, 0xa4, 0x5e}}
+
+
+// Class to hold all the data we need need for IterateMatchesAndClear and ClearDataFromSites
+class ClearDataFromSitesClosure : public nsIClearSiteDataCallback, public nsIGetSitesWithDataCallback {
+public:
+ ClearDataFromSitesClosure(nsIPluginTag* plugin, const nsACString& domain, uint64_t flags,
+ int64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback,
+ nsPluginHost* host) :
+ domain(domain), callback(callback), tag(plugin), flags(flags), maxAge(maxAge), host(host) {}
+ NS_DECL_ISUPPORTS
+
+ // Callback from NPP_ClearSiteData, continue to iterate the matches and clear
+ NS_IMETHOD Callback(nsresult rv) override {
+ if (NS_FAILED(rv)) {
+ callback->Callback(rv);
+ return NS_OK;
+ }
+ if (!matches.Length()) {
+ callback->Callback(NS_OK);
+ return NS_OK;
+ }
+
+ const nsCString match(matches[0]);
+ matches.RemoveElement(match);
+ PluginLibrary* library = static_cast<nsPluginTag*>(tag)->mPlugin->GetLibrary();
+ rv = library->NPP_ClearSiteData(match.get(), flags, maxAge, this);
+ if (NS_FAILED(rv)) {
+ callback->Callback(rv);
+ return NS_OK;
+ }
+ return NS_OK;
+ }
+
+ // Callback from NPP_GetSitesWithData, kick the iteration off to clear the data
+ NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& sites) override
+ {
+ // Enumerate the sites and build a list of matches.
+ nsresult rv = host->EnumerateSiteData(domain, sites, matches, false);
+ Callback(rv);
+ return NS_OK;
+ }
+
+ nsCString domain;
+ nsCOMPtr<nsIClearSiteDataCallback> callback;
+ InfallibleTArray<nsCString> matches;
+ nsIPluginTag* tag;
+ uint64_t flags;
+ int64_t maxAge;
+ nsPluginHost* host;
+ NS_DECLARE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure_CID)
+ private:
+ virtual ~ClearDataFromSitesClosure() {}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure, ClearDataFromSitesClosure_CID)
+
+NS_IMPL_ADDREF(ClearDataFromSitesClosure)
+NS_IMPL_RELEASE(ClearDataFromSitesClosure)
+
+NS_INTERFACE_MAP_BEGIN(ClearDataFromSitesClosure)
+ NS_INTERFACE_MAP_ENTRY(nsIClearSiteDataCallback)
+ NS_INTERFACE_MAP_ENTRY(nsIGetSitesWithDataCallback)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClearSiteDataCallback)
+NS_INTERFACE_MAP_END
+
+// FIXME-jsplugins what should this do for fake plugins?
+NS_IMETHODIMP
+nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
+ uint64_t flags, int64_t maxAge, nsIClearSiteDataCallback* callbackFunc)
+{
+ nsCOMPtr<nsIClearSiteDataCallback> callback(callbackFunc);
+ // maxAge must be either a nonnegative integer or -1.
+ NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
+
+ // Caller may give us a tag object that is no longer live.
+ if (!IsLiveTag(plugin)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
+
+ if (!tag->IsEnabled()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // We only ensure support for clearing Flash site data for now.
+ // We will also attempt to clear data for any plugin that happens
+ // to be loaded already.
+ if (!tag->mIsFlashPlugin && !tag->mPlugin) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure the plugin is loaded.
+ nsresult rv = EnsurePluginLoaded(tag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ PluginLibrary* library = tag->mPlugin->GetLibrary();
+
+ // If 'domain' is the null string, clear everything.
+ if (domain.IsVoid()) {
+ return library->NPP_ClearSiteData(nullptr, flags, maxAge, callback);
+ }
+ nsCOMPtr<nsIGetSitesWithDataCallback> closure(new ClearDataFromSitesClosure(plugin, domain, flags,
+ maxAge, callback, this));
+ rv = library->NPP_GetSitesWithData(closure);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+}
+
+#define GetSitesClosure_CID {0x4c9268ac, 0x2fd1, 0x4f2a, {0x9a, 0x10, 0x7a, 0x09, 0xf1, 0xb7, 0x60, 0x3a}}
+
+// Closure to contain the data needed to handle the callback from NPP_GetSitesWithData
+class GetSitesClosure : public nsIGetSitesWithDataCallback {
+public:
+ NS_DECL_ISUPPORTS
+ GetSitesClosure(const nsACString& domain, nsPluginHost* host)
+ : domain(domain), host(host), keepWaiting(true)
+ {
+ }
+ NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& sites) override {
+ retVal = HandleGetSites(sites);
+ keepWaiting = false;
+ return NS_OK;
+ }
+
+ nsresult HandleGetSites(InfallibleTArray<nsCString>& sites) {
+ // If there's no data, we're done.
+ if (sites.IsEmpty()) {
+ result = false;
+ return NS_OK;
+ }
+
+ // If 'domain' is the null string, and there's data for at least one site,
+ // we're done.
+ if (domain.IsVoid()) {
+ result = true;
+ return NS_OK;
+ }
+
+ // Enumerate the sites and determine if there's a match.
+ InfallibleTArray<nsCString> matches;
+ nsresult rv = host->EnumerateSiteData(domain, sites, matches, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ result = !matches.IsEmpty();
+ return NS_OK;
+ }
+
+ nsCString domain;
+ RefPtr<nsPluginHost> host;
+ bool result;
+ bool keepWaiting;
+ nsresult retVal;
+ NS_DECLARE_STATIC_IID_ACCESSOR(GetSitesClosure_CID)
+ private:
+ virtual ~GetSitesClosure() {
+ }
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(GetSitesClosure, GetSitesClosure_CID)
+
+NS_IMPL_ISUPPORTS(GetSitesClosure, GetSitesClosure, nsIGetSitesWithDataCallback)
+
+// This will spin the event loop while waiting on an async
+// call to GetSitesWithData
+NS_IMETHODIMP
+nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
+ bool* result)
+{
+ // Caller may give us a tag object that is no longer live.
+ if (!IsLiveTag(plugin)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // FIXME-jsplugins audit casts
+ nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
+
+ // We only ensure support for clearing Flash site data for now.
+ // We will also attempt to clear data for any plugin that happens
+ // to be loaded already.
+ if (!tag->mIsFlashPlugin && !tag->mPlugin) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure the plugin is loaded.
+ nsresult rv = EnsurePluginLoaded(tag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ PluginLibrary* library = tag->mPlugin->GetLibrary();
+
+ // Get the list of sites from the plugin
+ nsCOMPtr<GetSitesClosure> closure(new GetSitesClosure(domain, this));
+ rv = library->NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback>(do_QueryInterface(closure)));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Spin the event loop while we wait for the async call to GetSitesWithData
+ while (closure->keepWaiting) {
+ NS_ProcessNextEvent(nullptr, true);
+ }
+ *result = closure->result;
+ return closure->retVal;
+}
+
+nsPluginHost::SpecialType
+nsPluginHost::GetSpecialType(const nsACString & aMIMEType)
+{
+ if (aMIMEType.LowerCaseEqualsASCII("application/x-test")) {
+ return eSpecialType_Test;
+ }
+
+ if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
+ aMIMEType.LowerCaseEqualsASCII("application/futuresplash") ||
+ aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash-test")) {
+ return eSpecialType_Flash;
+ }
+
+ if (aMIMEType.LowerCaseEqualsASCII("application/x-silverlight") ||
+ aMIMEType.LowerCaseEqualsASCII("application/x-silverlight-2") ||
+ aMIMEType.LowerCaseEqualsASCII("application/x-silverlight-test")) {
+ return eSpecialType_Silverlight;
+ }
+
+ if (aMIMEType.LowerCaseEqualsASCII("audio/x-pn-realaudio-plugin")) {
+ NS_WARNING("You are loading RealPlayer");
+ return eSpecialType_RealPlayer;
+ }
+
+ if (aMIMEType.LowerCaseEqualsASCII("application/pdf")) {
+ return eSpecialType_PDF;
+ }
+
+ if (aMIMEType.LowerCaseEqualsASCII("application/vnd.unity")) {
+ return eSpecialType_Unity;
+ }
+
+ // Java registers variants of its MIME with parameters, e.g.
+ // application/x-java-vm;version=1.3
+ const nsACString &noParam = Substring(aMIMEType, 0, aMIMEType.FindChar(';'));
+
+ // The java mime pref may well not be one of these,
+ // e.g. application/x-java-test used in the test suite
+ nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
+ if ((!javaMIME.IsEmpty() && noParam.LowerCaseEqualsASCII(javaMIME)) ||
+ noParam.LowerCaseEqualsASCII("application/x-java-vm") ||
+ noParam.LowerCaseEqualsASCII("application/x-java-applet") ||
+ noParam.LowerCaseEqualsASCII("application/x-java-bean")) {
+ return eSpecialType_Java;
+ }
+
+ return eSpecialType_None;
+}
+
+// Check whether or not a tag is a live, valid tag, and that it's loaded.
+bool
+nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag)
+{
+ nsCOMPtr<nsIInternalPluginTag> internalTag(do_QueryInterface(aPluginTag));
+ uint32_t fakeCount = mFakePlugins.Length();
+ for (uint32_t i = 0; i < fakeCount; i++) {
+ if (mFakePlugins[i] == internalTag) {
+ return true;
+ }
+ }
+
+ nsPluginTag* tag;
+ for (tag = mPlugins; tag; tag = tag->mNext) {
+ if (tag == internalTag) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// FIXME-jsplugins what should happen with jsplugins here, if anything?
+nsPluginTag*
+nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag)
+{
+ for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
+ if (tag->HasSameNameAndMimes(aPluginTag)) {
+ return tag;
+ }
+ }
+ return nullptr;
+}
+
+// Don't have to worry about fake plugins here, since this is only used during
+// the plugin directory scan, which doesn't pick up fake plugins.
+nsPluginTag*
+nsPluginHost::FirstPluginWithPath(const nsCString& path)
+{
+ for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
+ if (tag->mFullPath.Equals(path)) {
+ return tag;
+ }
+ }
+ return nullptr;
+}
+
+nsPluginTag*
+nsPluginHost::PluginWithId(uint32_t aId)
+{
+ for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
+ if (tag->mId == aId) {
+ return tag;
+ }
+ }
+ return nullptr;
+}
+
+namespace {
+
+int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
+{
+ PRTime fileModTime = 0;
+
+#if defined(XP_MACOSX)
+ // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
+ // is a much better guide to when it was last modified than the date of
+ // its package directory. See bug 313700.
+ nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(localfile);
+ if (localFileMac) {
+ localFileMac->GetBundleContentsLastModifiedTime(&fileModTime);
+ } else {
+ localfile->GetLastModifiedTime(&fileModTime);
+ }
+#else
+ localfile->GetLastModifiedTime(&fileModTime);
+#endif
+
+ return fileModTime;
+}
+
+bool
+GetPluginIsFromExtension(const nsCOMPtr<nsIFile>& pluginFile,
+ const nsCOMArray<nsIFile>& extensionDirs)
+{
+ for (uint32_t i = 0; i < extensionDirs.Length(); ++i) {
+ bool contains;
+ if (NS_FAILED(extensionDirs[i]->Contains(pluginFile, &contains)) || !contains) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void
+GetExtensionDirectories(nsCOMArray<nsIFile>& dirs)
+{
+ nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+ if (!dirService) {
+ return;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> list;
+ nsresult rv = dirService->Get(XRE_EXTENSIONS_DIR_LIST,
+ NS_GET_IID(nsISimpleEnumerator),
+ getter_AddRefs(list));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ bool more;
+ while (NS_SUCCEEDED(list->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> next;
+ if (NS_FAILED(list->GetNext(getter_AddRefs(next)))) {
+ break;
+ }
+ nsCOMPtr<nsIFile> file = do_QueryInterface(next);
+ if (file) {
+ file->Normalize();
+ dirs.AppendElement(file.forget());
+ }
+ }
+}
+
+struct CompareFilesByTime
+{
+ bool
+ LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
+ {
+ return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
+ }
+
+ bool
+ Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
+ {
+ return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
+ }
+};
+
+} // namespace
+
+bool
+nsPluginHost::ShouldAddPlugin(nsPluginTag* aPluginTag)
+{
+#if defined(XP_WIN) && (defined(__x86_64__) || defined(_M_X64))
+ // On 64-bit windows, the only plugins we should load are flash and
+ // silverlight. Use library filename and MIME type to check.
+ if (StringBeginsWith(aPluginTag->FileName(), NS_LITERAL_CSTRING("NPSWF"), nsCaseInsensitiveCStringComparator()) &&
+ (aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-shockwave-flash")) ||
+ aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-shockwave-flash-test")))) {
+ return true;
+ }
+ if (StringBeginsWith(aPluginTag->FileName(), NS_LITERAL_CSTRING("npctrl"), nsCaseInsensitiveCStringComparator()) &&
+ (aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-silverlight-test")) ||
+ aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-silverlight-2")) ||
+ aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-silverlight")))) {
+ return true;
+ }
+ // Accept the test plugin MIME types, so mochitests still work.
+ if (aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-test")) ||
+ aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-Second-Test")) ||
+ aPluginTag->HasMimeType(NS_LITERAL_CSTRING("application/x-java-test"))) {
+ return true;
+ }
+#ifdef PLUGIN_LOGGING
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("ShouldAddPlugin : Ignoring non-flash plugin library %s\n", aPluginTag->FileName().get()));
+#endif // PLUGIN_LOGGING
+ return false;
+#else
+ return true;
+#endif // defined(XP_WIN) && (defined(__x86_64__) || defined(_M_X64))
+}
+
+void
+nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
+{
+ if (!ShouldAddPlugin(aPluginTag)) {
+ return;
+ }
+ aPluginTag->mNext = mPlugins;
+ mPlugins = aPluginTag;
+
+ if (aPluginTag->IsActive()) {
+ nsAdoptingCString disableFullPage =
+ Preferences::GetCString(kPrefDisableFullPage);
+ for (uint32_t i = 0; i < aPluginTag->MimeTypes().Length(); i++) {
+ if (!IsTypeInList(aPluginTag->MimeTypes()[i], disableFullPage)) {
+ RegisterWithCategoryManager(aPluginTag->MimeTypes()[i],
+ ePluginRegister);
+ }
+ }
+ }
+}
+
+static bool
+PluginInfoIsFlash(const nsPluginInfo& info)
+{
+ if (!info.fName || strcmp(info.fName, "Shockwave Flash") != 0) {
+ return false;
+ }
+ for (uint32_t i = 0; i < info.fVariantCount; ++i) {
+ if (info.fMimeTypeArray[i] &&
+ (!strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash") ||
+ !strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash-test"))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
+
+nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
+ bool aCreatePluginList,
+ bool *aPluginsChanged)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ NS_ENSURE_ARG_POINTER(aPluginsChanged);
+ nsresult rv;
+
+ *aPluginsChanged = false;
+
+#ifdef PLUGIN_LOGGING
+ nsAutoCString dirPath;
+ pluginsDir->GetNativePath(dirPath);
+ PLUGIN_LOG(PLUGIN_LOG_BASIC,
+ ("nsPluginHost::ScanPluginsDirectory dir=%s\n", dirPath.get()));
+#endif
+
+ bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
+
+ nsCOMPtr<nsISimpleEnumerator> iter;
+ rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
+ if (NS_FAILED(rv))
+ return rv;
+
+ AutoTArray<nsCOMPtr<nsIFile>, 6> pluginFiles;
+
+ bool hasMore;
+ while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> supports;
+ rv = iter->GetNext(getter_AddRefs(supports));
+ if (NS_FAILED(rv))
+ continue;
+ nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
+ if (NS_FAILED(rv))
+ continue;
+
+ // Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll crash.
+ // See bug 197855.
+ dirEntry->Normalize();
+
+ if (nsPluginsDir::IsPluginFile(dirEntry)) {
+ pluginFiles.AppendElement(dirEntry);
+ }
+ }
+
+ pluginFiles.Sort(CompareFilesByTime());
+
+ nsCOMArray<nsIFile> extensionDirs;
+ GetExtensionDirectories(extensionDirs);
+
+ for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
+ nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
+
+ nsString utf16FilePath;
+ rv = localfile->GetPath(utf16FilePath);
+ if (NS_FAILED(rv))
+ continue;
+
+ const int64_t fileModTime = GetPluginLastModifiedTime(localfile);
+ const bool fromExtension = GetPluginIsFromExtension(localfile, extensionDirs);
+
+ // Look for it in our cache
+ NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
+ RefPtr<nsPluginTag> pluginTag;
+ RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
+
+ bool seenBefore = false;
+
+ if (pluginTag) {
+ seenBefore = true;
+ // If plugin changed, delete cachedPluginTag and don't use cache
+ if (fileModTime != pluginTag->mLastModifiedTime) {
+ // Plugins has changed. Don't use cached plugin info.
+ pluginTag = nullptr;
+
+ // plugin file changed, flag this fact
+ *aPluginsChanged = true;
+ }
+
+ // If we're not creating a list and we already know something changed then
+ // we're done.
+ if (!aCreatePluginList) {
+ if (*aPluginsChanged) {
+ return NS_OK;
+ }
+ continue;
+ }
+ }
+
+ bool isKnownInvalidPlugin = false;
+ for (RefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
+ invalidPlugins; invalidPlugins = invalidPlugins->mNext) {
+ // If already marked as invalid, ignore it
+ if (invalidPlugins->mFullPath.Equals(filePath.get()) &&
+ invalidPlugins->mLastModifiedTime == fileModTime) {
+ if (aCreatePluginList) {
+ invalidPlugins->mSeen = true;
+ }
+ isKnownInvalidPlugin = true;
+ break;
+ }
+ }
+ if (isKnownInvalidPlugin) {
+ continue;
+ }
+
+ // if it is not found in cache info list or has been changed, create a new one
+ if (!pluginTag) {
+ nsPluginFile pluginFile(localfile);
+
+ // create a tag describing this plugin.
+ PRLibrary *library = nullptr;
+ nsPluginInfo info;
+ memset(&info, 0, sizeof(info));
+ nsresult res;
+ // Opening a block for the telemetry AutoTimer
+ {
+ Telemetry::AutoTimer<Telemetry::PLUGIN_LOAD_METADATA> telemetry;
+ res = pluginFile.GetPluginInfo(info, &library);
+ }
+ // if we don't have mime type don't proceed, this is not a plugin
+ if (NS_FAILED(res) || !info.fMimeTypeArray ||
+ (flashOnly && !PluginInfoIsFlash(info))) {
+ RefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(filePath.get(),
+ fileModTime);
+ pluginFile.FreePluginInfo(info);
+
+ if (aCreatePluginList) {
+ invalidTag->mSeen = true;
+ }
+ invalidTag->mNext = mInvalidPlugins;
+ if (mInvalidPlugins) {
+ mInvalidPlugins->mPrev = invalidTag;
+ }
+ mInvalidPlugins = invalidTag;
+
+ // Mark aPluginsChanged so pluginreg is rewritten
+ *aPluginsChanged = true;
+ continue;
+ }
+
+ pluginTag = new nsPluginTag(&info, fileModTime, fromExtension);
+ pluginFile.FreePluginInfo(info);
+ pluginTag->mLibrary = library;
+ uint32_t state;
+ rv = pluginTag->GetBlocklistState(&state);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If the blocklist says it is risky and we have never seen this
+ // plugin before, then disable it.
+ if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
+ pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
+ }
+
+ // Plugin unloading is tag-based. If we created a new tag and loaded
+ // the library in the process then we want to attempt to unload it here.
+ // Only do this if the pref is set for aggressive unloading.
+ if (UnloadPluginsASAP()) {
+ pluginTag->TryUnloadPlugin(false);
+ }
+ }
+
+ // do it if we still want it
+ if (!seenBefore) {
+ // We have a valid new plugin so report that plugins have changed.
+ *aPluginsChanged = true;
+ }
+
+ // Avoid adding different versions of the same plugin if they are running
+ // in-process, otherwise we risk undefined behaviour.
+ if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
+ if (HaveSamePlugin(pluginTag)) {
+ continue;
+ }
+ }
+
+ // Don't add the same plugin again if it hasn't changed
+ if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
+ if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
+ continue;
+ }
+ }
+
+ // If we're not creating a plugin list, simply looking for changes,
+ // then we're done.
+ if (!aCreatePluginList) {
+ return NS_OK;
+ }
+
+ AddPluginTag(pluginTag);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
+ bool aCreatePluginList,
+ bool *aPluginsChanged)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ bool hasMore;
+ while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> supports;
+ nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
+ if (NS_FAILED(rv))
+ continue;
+ nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
+ if (NS_FAILED(rv))
+ continue;
+
+ // don't pass aPluginsChanged directly to prevent it from been reset
+ bool pluginschanged = false;
+ ScanPluginsDirectory(nextDir, aCreatePluginList, &pluginschanged);
+
+ if (pluginschanged)
+ *aPluginsChanged = true;
+
+ // if changes are detected and we are not creating the list, do not proceed
+ if (!aCreatePluginList && *aPluginsChanged)
+ break;
+ }
+ return NS_OK;
+}
+
+void
+nsPluginHost::IncrementChromeEpoch()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ mPluginEpoch++;
+}
+
+uint32_t
+nsPluginHost::ChromeEpoch()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ return mPluginEpoch;
+}
+
+uint32_t
+nsPluginHost::ChromeEpochForContent()
+{
+ MOZ_ASSERT(XRE_IsContentProcess());
+ return mPluginEpoch;
+}
+
+void
+nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch)
+{
+ MOZ_ASSERT(XRE_IsContentProcess());
+ mPluginEpoch = aEpoch;
+}
+
+#ifdef XP_WIN
+static void
+WatchRegKey(uint32_t aRoot, nsCOMPtr<nsIWindowsRegKey>& aKey)
+{
+ if (aKey) {
+ return;
+ }
+
+ aKey = do_CreateInstance("@mozilla.org/windows-registry-key;1");
+ if (!aKey) {
+ return;
+ }
+ nsresult rv = aKey->Open(aRoot,
+ NS_LITERAL_STRING("Software\\MozillaPlugins"),
+ nsIWindowsRegKey::ACCESS_READ | nsIWindowsRegKey::ACCESS_NOTIFY);
+ if (NS_FAILED(rv)) {
+ aKey = nullptr;
+ return;
+ }
+ aKey->StartWatching(true);
+}
+#endif
+
+nsresult nsPluginHost::LoadPlugins()
+{
+#ifdef ANDROID
+ if (XRE_IsContentProcess()) {
+ return NS_OK;
+ }
+#endif
+ // do not do anything if it is already done
+ // use ReloadPlugins() to enforce loading
+ if (mPluginsLoaded)
+ return NS_OK;
+
+ if (mPluginsDisabled)
+ return NS_OK;
+
+#ifdef XP_WIN
+ WatchRegKey(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, mRegKeyHKLM);
+ WatchRegKey(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, mRegKeyHKCU);
+#endif
+
+ bool pluginschanged;
+ nsresult rv = FindPlugins(true, &pluginschanged);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // only if plugins have changed will we notify plugin-change observers
+ if (pluginschanged) {
+ if (XRE_IsParentProcess()) {
+ IncrementChromeEpoch();
+ }
+
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService)
+ obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged)
+{
+ MOZ_ASSERT(XRE_IsContentProcess());
+
+ dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+ nsresult rv;
+ nsTArray<PluginTag> plugins;
+ uint32_t parentEpoch;
+ if (!cp->SendFindPlugins(ChromeEpochForContent(), &rv, &plugins, &parentEpoch) ||
+ NS_FAILED(rv)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (parentEpoch != ChromeEpochForContent()) {
+ *aPluginsChanged = true;
+ if (!aCreatePluginList) {
+ return NS_OK;
+ }
+
+ // Don't do this if aCreatePluginList is false. Otherwise, when we actually
+ // want to create the list, we'll come back here and do nothing.
+ SetChromeEpochForContent(parentEpoch);
+
+ for (size_t i = 0; i < plugins.Length(); i++) {
+ PluginTag& tag = plugins[i];
+
+ // Don't add the same plugin again.
+ if (nsPluginTag* existing = PluginWithId(tag.id())) {
+ UpdateInMemoryPluginInfo(existing);
+ continue;
+ }
+
+ nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
+ tag.name().get(),
+ tag.description().get(),
+ tag.filename().get(),
+ "", // aFullPath
+ tag.version().get(),
+ nsTArray<nsCString>(tag.mimeTypes()),
+ nsTArray<nsCString>(tag.mimeDescriptions()),
+ nsTArray<nsCString>(tag.extensions()),
+ tag.isJavaPlugin(),
+ tag.isFlashPlugin(),
+ tag.supportsAsyncInit(),
+ tag.supportsAsyncRender(),
+ tag.lastModifiedTime(),
+ tag.isFromExtension(),
+ tag.sandboxLevel());
+ AddPluginTag(pluginTag);
+ }
+ }
+
+ mPluginsLoaded = true;
+ return NS_OK;
+}
+
+// if aCreatePluginList is false we will just scan for plugins
+// and see if any changes have been made to the plugins.
+// This is needed in ReloadPlugins to prevent possible recursive reloads
+nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
+{
+ Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
+
+ NS_ENSURE_ARG_POINTER(aPluginsChanged);
+
+ *aPluginsChanged = false;
+
+ if (XRE_IsContentProcess()) {
+ return FindPluginsInContent(aCreatePluginList, aPluginsChanged);
+ }
+
+ nsresult rv;
+
+ // Read cached plugins info. If the profile isn't yet available then don't
+ // scan for plugins
+ if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
+ return NS_OK;
+
+#ifdef XP_WIN
+ // Failure here is not a show-stopper so just warn.
+ rv = EnsurePrivateDirServiceProvider();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register dir service provider.");
+#endif /* XP_WIN */
+
+ nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsISimpleEnumerator> dirList;
+
+ // Scan plugins directories;
+ // don't pass aPluginsChanged directly, to prevent its
+ // possible reset in subsequent ScanPluginsDirectory calls
+ bool pluginschanged = false;
+
+ // Scan the app-defined list of plugin dirs.
+ rv = dirService->Get(NS_APP_PLUGINS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dirList));
+ if (NS_SUCCEEDED(rv)) {
+ ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
+
+ if (pluginschanged)
+ *aPluginsChanged = true;
+
+ // if we are just looking for possible changes,
+ // no need to proceed if changes are detected
+ if (!aCreatePluginList && *aPluginsChanged) {
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
+ return NS_OK;
+ }
+ } else {
+#ifdef ANDROID
+ LOG("getting plugins dir failed");
+#endif
+ }
+
+ mPluginsLoaded = true; // at this point 'some' plugins have been loaded,
+ // the rest is optional
+
+#ifdef XP_WIN
+ bool bScanPLIDs = Preferences::GetBool("plugin.scan.plid.all", false);
+
+ // Now lets scan any PLID directories
+ if (bScanPLIDs && mPrivateDirServiceProvider) {
+ rv = mPrivateDirServiceProvider->GetPLIDDirectories(getter_AddRefs(dirList));
+ if (NS_SUCCEEDED(rv)) {
+ ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
+
+ if (pluginschanged)
+ *aPluginsChanged = true;
+
+ // if we are just looking for possible changes,
+ // no need to proceed if changes are detected
+ if (!aCreatePluginList && *aPluginsChanged) {
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
+ return NS_OK;
+ }
+ }
+ }
+
+
+ // Scan the installation paths of our popular plugins if the prefs are enabled
+
+ // This table controls the order of scanning
+ const char* const prefs[] = {NS_WIN_ACROBAT_SCAN_KEY,
+ NS_WIN_QUICKTIME_SCAN_KEY,
+ NS_WIN_WMP_SCAN_KEY};
+
+ uint32_t size = sizeof(prefs) / sizeof(prefs[0]);
+
+ for (uint32_t i = 0; i < size; i+=1) {
+ nsCOMPtr<nsIFile> dirToScan;
+ bool bExists;
+ if (NS_SUCCEEDED(dirService->Get(prefs[i], NS_GET_IID(nsIFile), getter_AddRefs(dirToScan))) &&
+ dirToScan &&
+ NS_SUCCEEDED(dirToScan->Exists(&bExists)) &&
+ bExists) {
+
+ ScanPluginsDirectory(dirToScan, aCreatePluginList, &pluginschanged);
+
+ if (pluginschanged)
+ *aPluginsChanged = true;
+
+ // if we are just looking for possible changes,
+ // no need to proceed if changes are detected
+ if (!aCreatePluginList && *aPluginsChanged) {
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
+ return NS_OK;
+ }
+ }
+ }
+#endif
+
+ // We should also consider plugins to have changed if any plugins have been removed.
+ // We'll know if any were removed if they weren't taken out of the cached plugins list
+ // during our scan, thus we can assume something was removed if the cached plugins list
+ // contains anything.
+ if (!*aPluginsChanged && mCachedPlugins) {
+ *aPluginsChanged = true;
+ }
+
+ // Remove unseen invalid plugins
+ RefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
+ while (invalidPlugins) {
+ if (!invalidPlugins->mSeen) {
+ RefPtr<nsInvalidPluginTag> invalidPlugin = invalidPlugins;
+
+ if (invalidPlugin->mPrev) {
+ invalidPlugin->mPrev->mNext = invalidPlugin->mNext;
+ }
+ else {
+ mInvalidPlugins = invalidPlugin->mNext;
+ }
+ if (invalidPlugin->mNext) {
+ invalidPlugin->mNext->mPrev = invalidPlugin->mPrev;
+ }
+
+ invalidPlugins = invalidPlugin->mNext;
+
+ invalidPlugin->mPrev = nullptr;
+ invalidPlugin->mNext = nullptr;
+ }
+ else {
+ invalidPlugins->mSeen = false;
+ invalidPlugins = invalidPlugins->mNext;
+ }
+ }
+
+ // if we are not creating the list, there is no need to proceed
+ if (!aCreatePluginList) {
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
+ return NS_OK;
+ }
+
+ // if we are creating the list, it is already done;
+ // update the plugins info cache if changes are detected
+ if (*aPluginsChanged)
+ WritePluginInfo();
+
+ // No more need for cached plugins. Clear it up.
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
+
+ return NS_OK;
+}
+
+nsresult
+mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ return host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+}
+
+nsresult
+nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // Load plugins so that the epoch is correct.
+ nsresult rv = LoadPlugins();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aNewPluginEpoch = ChromeEpoch();
+ if (aPluginEpoch == ChromeEpoch()) {
+ return NS_OK;
+ }
+
+ nsTArray<nsCOMPtr<nsIInternalPluginTag>> plugins;
+ GetPlugins(plugins, true);
+
+ for (size_t i = 0; i < plugins.Length(); i++) {
+ nsCOMPtr<nsIInternalPluginTag> basetag = plugins[i];
+
+ nsCOMPtr<nsIFakePluginTag> faketag = do_QueryInterface(basetag);
+ if (faketag) {
+ /// FIXME-jsplugins - We need to make content processes properly
+ /// aware of jsplugins (and add a nsIInternalPluginTag->AsNative() to
+ /// avoid this hacky static cast)
+ continue;
+ }
+
+ /// FIXME-jsplugins - We need to cleanup the various plugintag classes
+ /// to be more sane and avoid this dance
+ nsPluginTag *tag = static_cast<nsPluginTag *>(basetag.get());
+
+ aPlugins->AppendElement(PluginTag(tag->mId,
+ tag->Name(),
+ tag->Description(),
+ tag->MimeTypes(),
+ tag->MimeDescriptions(),
+ tag->Extensions(),
+ tag->mIsJavaPlugin,
+ tag->mIsFlashPlugin,
+ tag->mSupportsAsyncInit,
+ tag->mSupportsAsyncRender,
+ tag->FileName(),
+ tag->Version(),
+ tag->mLastModifiedTime,
+ tag->IsFromExtension(),
+ tag->mSandboxLevel));
+ }
+ return NS_OK;
+}
+
+void
+nsPluginHost::UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag)
+{
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
+
+ if (!aPluginTag) {
+ return;
+ }
+
+ // Update types with category manager
+ nsAdoptingCString disableFullPage =
+ Preferences::GetCString(kPrefDisableFullPage);
+ for (uint32_t i = 0; i < aPluginTag->MimeTypes().Length(); i++) {
+ nsRegisterType shouldRegister;
+
+ if (IsTypeInList(aPluginTag->MimeTypes()[i], disableFullPage)) {
+ shouldRegister = ePluginUnregister;
+ } else {
+ nsPluginTag *plugin = FindNativePluginForType(aPluginTag->MimeTypes()[i],
+ true);
+ shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
+ }
+
+ RegisterWithCategoryManager(aPluginTag->MimeTypes()[i], shouldRegister);
+ }
+
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService)
+ obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
+}
+
+// This function is not relevant for fake plugins.
+void
+nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ ReadPluginInfo();
+ WritePluginInfo();
+
+ IncrementChromeEpoch();
+
+ UpdateInMemoryPluginInfo(aPluginTag);
+}
+
+/* static */ bool
+nsPluginHost::IsTypeWhitelisted(const char *aMimeType)
+{
+ nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist);
+ if (!whitelist.Length()) {
+ return true;
+ }
+ nsDependentCString wrap(aMimeType);
+ return IsTypeInList(wrap, whitelist);
+}
+
+/* static */ bool
+nsPluginHost::ShouldLoadTypeInParent(const nsACString& aMimeType)
+{
+ nsCString prefName(kPrefLoadInParentPrefix);
+ prefName += aMimeType;
+ return Preferences::GetBool(prefName.get(), false);
+}
+
+void
+nsPluginHost::RegisterWithCategoryManager(const nsCString& aMimeType,
+ nsRegisterType aType)
+{
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n",
+ aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
+
+ nsCOMPtr<nsICategoryManager> catMan =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!catMan) {
+ return;
+ }
+
+ const char *contractId =
+ "@mozilla.org/content/plugin/document-loader-factory;1";
+
+ if (aType == ePluginRegister) {
+ catMan->AddCategoryEntry("Gecko-Content-Viewers",
+ aMimeType.get(),
+ contractId,
+ false, /* persist: broken by bug 193031 */
+ mOverrideInternalTypes,
+ nullptr);
+ } else {
+ if (aType == ePluginMaybeUnregister) {
+ // Bail out if this type is still used by an enabled plugin
+ if (HavePluginForType(aMimeType)) {
+ return;
+ }
+ } else {
+ MOZ_ASSERT(aType == ePluginUnregister, "Unknown nsRegisterType");
+ }
+
+ // Only delete the entry if a plugin registered for it
+ nsXPIDLCString value;
+ nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
+ aMimeType.get(),
+ getter_Copies(value));
+ if (NS_SUCCEEDED(rv) && strcmp(value, contractId) == 0) {
+ catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
+ aMimeType.get(),
+ true);
+ }
+ }
+}
+
+nsresult
+nsPluginHost::WritePluginInfo()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(mPluginRegFile));
+
+ if (!mPluginRegFile)
+ return NS_ERROR_FAILURE;
+
+ PRFileDesc* fd = nullptr;
+
+ nsCOMPtr<nsIFile> pluginReg;
+
+ rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString filename(kPluginRegistryFilename);
+ filename.AppendLiteral(".tmp");
+ rv = pluginReg->AppendNative(filename);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
+ if (!runtime) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString arch;
+ rv = runtime->GetXPCOMABI(arch);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
+
+ PR_fprintf(fd, "Generated File. Do not edit.\n");
+
+ PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c%c\nArch%c%s%c%c\n",
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ kPluginRegistryVersion,
+ flashOnly ? 't' : 'f',
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER,
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ arch.get(),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER);
+
+ // Store all plugins in the mPlugins list - all plugins currently in use.
+ PR_fprintf(fd, "\n[PLUGINS]\n");
+
+ for (nsPluginTag *tag = mPlugins; tag; tag = tag->mNext) {
+ // store each plugin info into the registry
+ // filename & fullpath are on separate line
+ // because they can contain field delimiter char
+ PR_fprintf(fd, "%s%c%c\n%s%c%c\n%s%c%c\n",
+ (tag->FileName().get()),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER,
+ (tag->mFullPath.get()),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER,
+ (tag->Version().get()),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER);
+
+ // lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension
+ PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%c\n",
+ tag->mLastModifiedTime,
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ false, // did store whether or not to unload in-process plugins
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ 0, // legacy field for flags
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ tag->IsFromExtension(),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER);
+
+ //description, name & mtypecount are on separate line
+ PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
+ (tag->Description().get()),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER,
+ (tag->Name().get()),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER,
+ tag->MimeTypes().Length());
+
+ // Add in each mimetype this plugin supports
+ for (uint32_t i = 0; i < tag->MimeTypes().Length(); i++) {
+ PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n",
+ i,PLUGIN_REGISTRY_FIELD_DELIMITER,
+ (tag->MimeTypes()[i].get()),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ (tag->MimeDescriptions()[i].get()),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ (tag->Extensions()[i].get()),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER);
+ }
+ }
+
+ PR_fprintf(fd, "\n[INVALID]\n");
+
+ RefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
+ while (invalidPlugins) {
+ // fullPath
+ PR_fprintf(fd, "%s%c%c\n",
+ (!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get() : ""),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER);
+
+ // lastModifiedTimeStamp
+ PR_fprintf(fd, "%lld%c%c\n",
+ invalidPlugins->mLastModifiedTime,
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
+ PLUGIN_REGISTRY_END_OF_LINE_MARKER);
+
+ invalidPlugins = invalidPlugins->mNext;
+ }
+
+ PRStatus prrc;
+ prrc = PR_Close(fd);
+ if (prrc != PR_SUCCESS) {
+ // we should obtain a refined value based on prrc;
+ rv = NS_ERROR_FAILURE;
+ MOZ_ASSERT(false, "PR_Close() failed.");
+ return rv;
+ }
+ nsCOMPtr<nsIFile> parent;
+ rv = pluginReg->GetParent(getter_AddRefs(parent));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
+ return rv;
+}
+
+nsresult
+nsPluginHost::ReadPluginInfo()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
+ const long PLUGIN_REG_MAX_MIMETYPES = 1000;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(mPluginRegFile));
+
+ if (!mPluginRegFile) {
+ // There is no profile yet, this will tell us if there is going to be one
+ // in the future.
+ directoryService->Get(NS_APP_PROFILE_DIR_STARTUP, NS_GET_IID(nsIFile),
+ getter_AddRefs(mPluginRegFile));
+ if (!mPluginRegFile)
+ return NS_ERROR_FAILURE;
+ else
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ PRFileDesc* fd = nullptr;
+
+ nsCOMPtr<nsIFile> pluginReg;
+
+ rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = pluginReg->AppendNative(kPluginRegistryFilename);
+ if (NS_FAILED(rv))
+ return rv;
+
+ int64_t fileSize;
+ rv = pluginReg->GetFileSize(&fileSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (fileSize > INT32_MAX) {
+ return NS_ERROR_FAILURE;
+ }
+ int32_t flen = int32_t(fileSize);
+ if (flen == 0) {
+ NS_WARNING("Plugins Registry Empty!");
+ return NS_OK; // ERROR CONDITION
+ }
+
+ nsPluginManifestLineReader reader;
+ char* registry = reader.Init(flen);
+ if (!registry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = pluginReg->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // set rv to return an error on goto out
+ rv = NS_ERROR_FAILURE;
+
+ // We know how many octes we are supposed to read.
+ // So let use the busy_beaver_PR_Read version.
+ int32_t bread = busy_beaver_PR_Read(fd, registry, flen);
+
+ PRStatus prrc;
+ prrc = PR_Close(fd);
+ if (prrc != PR_SUCCESS) {
+ // Strange error: this is one of those "Should not happen" error.
+ // we may want to report something more refined than NS_ERROR_FAILURE.
+ MOZ_ASSERT(false, "PR_Close() failed.");
+ return rv;
+ }
+
+ // short read error, so to speak.
+ if (flen > bread)
+ return rv;
+
+ if (!ReadSectionHeader(reader, "HEADER"))
+ return rv;;
+
+ if (!reader.NextLine())
+ return rv;
+
+ char* values[6];
+
+ // VersionLiteral, kPluginRegistryVersion
+ if (2 != reader.ParseLine(values, 2))
+ return rv;
+
+ // VersionLiteral
+ if (PL_strcmp(values[0], "Version"))
+ return rv;
+
+ // If we're reading an old registry, ignore it
+ // If we flipped the flash-only pref, ignore it
+ bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
+ nsAutoCString expectedVersion(kPluginRegistryVersion);
+ expectedVersion.Append(flashOnly ? 't' : 'f');
+
+ if (!expectedVersion.Equals(values[1])) {
+ return rv;
+ }
+
+ char* archValues[6];
+ if (!reader.NextLine()) {
+ return rv;
+ }
+
+ // ArchLiteral, Architecture
+ if (2 != reader.ParseLine(archValues, 2)) {
+ return rv;
+ }
+
+ // ArchLiteral
+ if (PL_strcmp(archValues[0], "Arch")) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
+ if (!runtime) {
+ return rv;
+ }
+
+ nsAutoCString arch;
+ if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
+ return rv;
+ }
+
+ // If this is a registry from a different architecture then don't attempt to read it
+ if (PL_strcmp(archValues[1], arch.get())) {
+ return rv;
+ }
+
+ if (!ReadSectionHeader(reader, "PLUGINS"))
+ return rv;
+
+ while (reader.NextLine()) {
+ if (*reader.LinePtr() == '[') {
+ break;
+ }
+
+ const char* filename = reader.LinePtr();
+ if (!reader.NextLine())
+ return rv;
+
+ const char* fullpath = reader.LinePtr();
+ if (!reader.NextLine())
+ return rv;
+
+ const char *version;
+ version = reader.LinePtr();
+ if (!reader.NextLine())
+ return rv;
+
+ // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension
+ if (4 != reader.ParseLine(values, 4))
+ return rv;
+
+ int64_t lastmod = nsCRT::atoll(values[0]);
+ bool fromExtension = atoi(values[3]);
+ if (!reader.NextLine())
+ return rv;
+
+ char *description = reader.LinePtr();
+ if (!reader.NextLine())
+ return rv;
+
+#if MOZ_WIDGET_ANDROID
+ // Flash on Android does not populate the version field, but it is tacked on to the description.
+ // For example, "Shockwave Flash 11.1 r115"
+ if (PL_strncmp("Shockwave Flash ", description, 16) == 0 && description[16]) {
+ version = &description[16];
+ }
+#endif
+
+ const char *name = reader.LinePtr();
+ if (!reader.NextLine())
+ return rv;
+
+ long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10);
+ if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN ||
+ mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
+ char **mimetypes;
+ char **mimedescriptions;
+ char **extensions;
+ char **heapalloced = 0;
+ if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
+ heapalloced = new char *[mimetypecount * 3];
+ mimetypes = heapalloced;
+ } else {
+ mimetypes = stackalloced;
+ }
+ mimedescriptions = mimetypes + mimetypecount;
+ extensions = mimedescriptions + mimetypecount;
+
+ int mtr = 0; //mimetype read
+ for (; mtr < mimetypecount; mtr++) {
+ if (!reader.NextLine())
+ break;
+
+ //line number|mimetype|description|extension
+ if (4 != reader.ParseLine(values, 4))
+ break;
+ int line = atoi(values[0]);
+ if (line != mtr)
+ break;
+ mimetypes[mtr] = values[1];
+ mimedescriptions[mtr] = values[2];
+ extensions[mtr] = values[3];
+ }
+
+ if (mtr != mimetypecount) {
+ if (heapalloced) {
+ delete [] heapalloced;
+ }
+ return rv;
+ }
+
+ RefPtr<nsPluginTag> tag = new nsPluginTag(name,
+ description,
+ filename,
+ fullpath,
+ version,
+ (const char* const*)mimetypes,
+ (const char* const*)mimedescriptions,
+ (const char* const*)extensions,
+ mimetypecount, lastmod, fromExtension, true);
+ if (heapalloced)
+ delete [] heapalloced;
+
+ // Import flags from registry into prefs for old registry versions
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
+ ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->FileName().get()));
+
+ if (!ShouldAddPlugin(tag)) {
+ continue;
+ }
+
+ tag->mNext = mCachedPlugins;
+ mCachedPlugins = tag;
+ }
+
+// On Android we always want to try to load a plugin again (Flash). Bug 935676.
+#ifndef MOZ_WIDGET_ANDROID
+ if (!ReadSectionHeader(reader, "INVALID")) {
+ return rv;
+ }
+
+ while (reader.NextLine()) {
+ const char *fullpath = reader.LinePtr();
+ if (!reader.NextLine()) {
+ return rv;
+ }
+
+ const char *lastModifiedTimeStamp = reader.LinePtr();
+ int64_t lastmod = nsCRT::atoll(lastModifiedTimeStamp);
+
+ RefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
+
+ invalidTag->mNext = mInvalidPlugins;
+ if (mInvalidPlugins) {
+ mInvalidPlugins->mPrev = invalidTag;
+ }
+ mInvalidPlugins = invalidTag;
+ }
+#endif
+
+ return NS_OK;
+}
+
+void
+nsPluginHost::RemoveCachedPluginsInfo(const char *filePath, nsPluginTag **result)
+{
+ RefPtr<nsPluginTag> prev;
+ RefPtr<nsPluginTag> tag = mCachedPlugins;
+ while (tag)
+ {
+ if (tag->mFullPath.Equals(filePath)) {
+ // Found it. Remove it from our list
+ if (prev)
+ prev->mNext = tag->mNext;
+ else
+ mCachedPlugins = tag->mNext;
+ tag->mNext = nullptr;
+ *result = tag;
+ NS_ADDREF(*result);
+ break;
+ }
+ prev = tag;
+ tag = tag->mNext;
+ }
+}
+
+#ifdef XP_WIN
+nsresult
+nsPluginHost::EnsurePrivateDirServiceProvider()
+{
+ if (!mPrivateDirServiceProvider) {
+ nsresult rv;
+ mPrivateDirServiceProvider = new nsPluginDirServiceProvider();
+ nsCOMPtr<nsIDirectoryService> dirService(do_GetService(kDirectoryServiceContractID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ rv = dirService->RegisterProvider(mPrivateDirServiceProvider);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ return NS_OK;
+}
+#endif /* XP_WIN */
+
+nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL,
+ nsNPAPIPluginInstance *aInstance,
+ nsNPAPIPluginStreamListener* aListener,
+ nsIInputStream *aPostStream,
+ const char *aHeadersData,
+ uint32_t aHeadersDataLen)
+{
+ nsCOMPtr<nsIURI> url;
+ nsAutoString absUrl;
+ nsresult rv;
+
+ if (aURL.Length() <= 0)
+ return NS_OK;
+
+ // get the base URI for the plugin to create an absolute url
+ // in case aURL is relative
+ RefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
+ if (owner) {
+ nsCOMPtr<nsIURI> baseURI = owner->GetBaseURI();
+ rv = NS_MakeAbsoluteURI(absUrl, aURL, baseURI);
+ }
+
+ if (absUrl.IsEmpty())
+ absUrl.Assign(aURL);
+
+ rv = NS_NewURI(getter_AddRefs(url), absUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsPluginStreamListenerPeer> listenerPeer = new nsPluginStreamListenerPeer();
+ NS_ENSURE_TRUE(listenerPeer, NS_ERROR_OUT_OF_MEMORY);
+
+ rv = listenerPeer->Initialize(url, aInstance, aListener);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMElement> element;
+ nsCOMPtr<nsIDocument> doc;
+ if (owner) {
+ owner->GetDOMElement(getter_AddRefs(element));
+ owner->GetDocument(getter_AddRefs(doc));
+ }
+
+ nsCOMPtr<nsINode> requestingNode(do_QueryInterface(element));
+ NS_ENSURE_TRUE(requestingNode, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIChannel> channel;
+ // @arg loadgroup:
+ // do not add this internal plugin's channel on the
+ // load group otherwise this channel could be canceled
+ // form |nsDocShell::OnLinkClickSync| bug 166613
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ url,
+ requestingNode,
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
+ nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
+ nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
+ nullptr, // aLoadGroup
+ listenerPeer,
+ nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI |
+ nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (doc) {
+ // And if it's a script allow it to execute against the
+ // document's script context.
+ nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
+ if (scriptChannel) {
+ scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
+ // Plug-ins seem to depend on javascript: URIs running synchronously
+ scriptChannel->SetExecuteAsync(false);
+ }
+ }
+
+ // deal with headers and post data
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ if (httpChannel) {
+ if (!aPostStream) {
+ // Only set the Referer header for GET requests because IIS throws
+ // errors about malformed requests if we include it in POSTs. See
+ // bug 724465.
+ nsCOMPtr<nsIURI> referer;
+ net::ReferrerPolicy referrerPolicy = net::RP_Default;
+
+ nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(element);
+ if (olc)
+ olc->GetSrcURI(getter_AddRefs(referer));
+
+
+ if (!referer) {
+ if (!doc) {
+ return NS_ERROR_FAILURE;
+ }
+ referer = doc->GetDocumentURI();
+ referrerPolicy = doc->GetReferrerPolicy();
+ }
+
+ rv = httpChannel->SetReferrerWithPolicy(referer, referrerPolicy);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+
+ if (aPostStream) {
+ // XXX it's a bit of a hack to rewind the postdata stream
+ // here but it has to be done in case the post data is
+ // being reused multiple times.
+ nsCOMPtr<nsISeekableStream>
+ postDataSeekable(do_QueryInterface(aPostStream));
+ if (postDataSeekable)
+ postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+
+ nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
+ NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
+
+ uploadChannel->SetUploadStream(aPostStream, EmptyCString(), -1);
+ }
+
+ if (aHeadersData) {
+ rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ rv = channel->AsyncOpen2(listenerPeer);
+ if (NS_SUCCEEDED(rv))
+ listenerPeer->TrackRequest(channel);
+ return rv;
+}
+
+nsresult
+nsPluginHost::AddHeadersToChannel(const char *aHeadersData,
+ uint32_t aHeadersDataLen,
+ nsIChannel *aGenericChannel)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIHttpChannel> aChannel = do_QueryInterface(aGenericChannel);
+ if (!aChannel) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // used during the manipulation of the String from the aHeadersData
+ nsAutoCString headersString;
+ nsAutoCString oneHeader;
+ nsAutoCString headerName;
+ nsAutoCString headerValue;
+ int32_t crlf = 0;
+ int32_t colon = 0;
+
+ // Turn the char * buffer into an nsString.
+ headersString = aHeadersData;
+
+ // Iterate over the nsString: for each "\r\n" delimited chunk,
+ // add the value as a header to the nsIHTTPChannel
+ while (true) {
+ crlf = headersString.Find("\r\n", true);
+ if (-1 == crlf) {
+ rv = NS_OK;
+ return rv;
+ }
+ headersString.Mid(oneHeader, 0, crlf);
+ headersString.Cut(0, crlf + 2);
+ oneHeader.StripWhitespace();
+ colon = oneHeader.Find(":");
+ if (-1 == colon) {
+ rv = NS_ERROR_NULL_POINTER;
+ return rv;
+ }
+ oneHeader.Left(headerName, colon);
+ colon++;
+ oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
+
+ // FINALLY: we can set the header!
+
+ rv = aChannel->SetRequestHeader(headerName, headerValue, true);
+ if (NS_FAILED(rv)) {
+ rv = NS_ERROR_NULL_POINTER;
+ return rv;
+ }
+ }
+}
+
+nsresult
+nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+ if (PluginDestructionGuard::DelayDestroy(aInstance)) {
+ return NS_OK;
+ }
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance));
+
+ if (aInstance->HasStartedDestroying()) {
+ return NS_OK;
+ }
+
+ Telemetry::AutoTimer<Telemetry::PLUGIN_SHUTDOWN_MS> timer;
+ aInstance->Stop();
+
+ // if the instance does not want to be 'cached' just remove it
+ bool doCache = aInstance->ShouldCache();
+ if (doCache) {
+ // try to get the max cached instances from a pref or use default
+ uint32_t cachedInstanceLimit =
+ Preferences::GetUint(NS_PREF_MAX_NUM_CACHED_INSTANCES,
+ DEFAULT_NUMBER_OF_STOPPED_INSTANCES);
+ if (StoppedInstanceCount() >= cachedInstanceLimit) {
+ nsNPAPIPluginInstance *oldestInstance = FindOldestStoppedInstance();
+ if (oldestInstance) {
+ nsPluginTag* pluginTag = TagForPlugin(oldestInstance->GetPlugin());
+ oldestInstance->Destroy();
+ mInstances.RemoveElement(oldestInstance);
+ // TODO: Remove this check once bug 752422 was investigated
+ if (pluginTag) {
+ OnPluginInstanceDestroyed(pluginTag);
+ }
+ }
+ }
+ } else {
+ nsPluginTag* pluginTag = TagForPlugin(aInstance->GetPlugin());
+ aInstance->Destroy();
+ mInstances.RemoveElement(aInstance);
+ // TODO: Remove this check once bug 752422 was investigated
+ if (pluginTag) {
+ OnPluginInstanceDestroyed(pluginTag);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginHost::NewPluginStreamListener(nsIURI* aURI,
+ nsNPAPIPluginInstance* aInstance,
+ nsIStreamListener **aStreamListener)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ NS_ENSURE_ARG_POINTER(aStreamListener);
+
+ RefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
+ nsresult rv = listener->Initialize(aURI, aInstance, nullptr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ listener.forget(aStreamListener);
+
+ return NS_OK;
+}
+
+void nsPluginHost::CreateWidget(nsPluginInstanceOwner* aOwner)
+{
+ aOwner->CreateWidget();
+
+ // If we've got a native window, the let the plugin know about it.
+ aOwner->CallSetWindow();
+}
+
+NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *someData)
+{
+ if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
+ OnShutdown();
+ UnloadPlugins();
+ sInst->Release();
+ }
+ if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
+ mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
+ // Unload or load plugins as needed
+ if (mPluginsDisabled) {
+ UnloadPlugins();
+ } else {
+ LoadPlugins();
+ }
+ }
+ if (!strcmp("blocklist-updated", aTopic)) {
+ nsPluginTag* plugin = mPlugins;
+ while (plugin) {
+ plugin->InvalidateBlocklistState();
+ plugin = plugin->mNext;
+ }
+ }
+#ifdef MOZ_WIDGET_ANDROID
+ if (!strcmp("application-background", aTopic)) {
+ for(uint32_t i = 0; i < mInstances.Length(); i++) {
+ mInstances[i]->NotifyForeground(false);
+ }
+ }
+ if (!strcmp("application-foreground", aTopic)) {
+ for(uint32_t i = 0; i < mInstances.Length(); i++) {
+ if (mInstances[i]->IsOnScreen())
+ mInstances[i]->NotifyForeground(true);
+ }
+ }
+ if (!strcmp("memory-pressure", aTopic)) {
+ for(uint32_t i = 0; i < mInstances.Length(); i++) {
+ mInstances[i]->MemoryPressure();
+ }
+ }
+#endif
+ return NS_OK;
+}
+
+nsresult
+nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen,
+ char **outPostData, uint32_t *outPostDataLen)
+{
+ if (!inPostData || !outPostData || !outPostDataLen)
+ return NS_ERROR_NULL_POINTER;
+
+ *outPostData = 0;
+ *outPostDataLen = 0;
+
+ const char CR = '\r';
+ const char LF = '\n';
+ const char CRLFCRLF[] = {CR,LF,CR,LF,'\0'}; // C string"\r\n\r\n"
+ const char ContentLenHeader[] = "Content-length";
+
+ AutoTArray<const char*, 8> singleLF;
+ const char *pSCntlh = 0;// pointer to start of ContentLenHeader in inPostData
+ const char *pSod = 0; // pointer to start of data in inPostData
+ const char *pEoh = 0; // pointer to end of headers in inPostData
+ const char *pEod = inPostData + inPostDataLen; // pointer to end of inPostData
+ if (*inPostData == LF) {
+ // If no custom headers are required, simply add a blank
+ // line ('\n') to the beginning of the file or buffer.
+ // so *inPostData == '\n' is valid
+ pSod = inPostData + 1;
+ } else {
+ const char *s = inPostData; //tmp pointer to sourse inPostData
+ while (s < pEod) {
+ if (!pSCntlh &&
+ (*s == 'C' || *s == 'c') &&
+ (s + sizeof(ContentLenHeader) - 1 < pEod) &&
+ (!PL_strncasecmp(s, ContentLenHeader, sizeof(ContentLenHeader) - 1)))
+ {
+ // lets assume this is ContentLenHeader for now
+ const char *p = pSCntlh = s;
+ p += sizeof(ContentLenHeader) - 1;
+ // search for first CR or LF == end of ContentLenHeader
+ for (; p < pEod; p++) {
+ if (*p == CR || *p == LF) {
+ // got delimiter,
+ // one more check; if previous char is a digit
+ // most likely pSCntlh points to the start of ContentLenHeader
+ if (*(p-1) >= '0' && *(p-1) <= '9') {
+ s = p;
+ }
+ break; //for loop
+ }
+ }
+ if (pSCntlh == s) { // curret ptr is the same
+ pSCntlh = 0; // that was not ContentLenHeader
+ break; // there is nothing to parse, break *WHILE LOOP* here
+ }
+ }
+
+ if (*s == CR) {
+ if (pSCntlh && // only if ContentLenHeader is found we are looking for end of headers
+ ((s + sizeof(CRLFCRLF)-1) <= pEod) &&
+ !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF)-1))
+ {
+ s += sizeof(CRLFCRLF)-1;
+ pEoh = pSod = s; // data stars here
+ break;
+ }
+ } else if (*s == LF) {
+ if (*(s-1) != CR) {
+ singleLF.AppendElement(s);
+ }
+ if (pSCntlh && (s+1 < pEod) && (*(s+1) == LF)) {
+ s++;
+ singleLF.AppendElement(s);
+ s++;
+ pEoh = pSod = s; // data stars here
+ break;
+ }
+ }
+ s++;
+ }
+ }
+
+ // deal with output buffer
+ if (!pSod) { // lets assume whole buffer is a data
+ pSod = inPostData;
+ }
+
+ uint32_t newBufferLen = 0;
+ uint32_t dataLen = pEod - pSod;
+ uint32_t headersLen = pEoh ? pSod - inPostData : 0;
+
+ char *p; // tmp ptr into new output buf
+ if (headersLen) { // we got a headers
+ // this function does not make any assumption on correctness
+ // of ContentLenHeader value in this case.
+
+ newBufferLen = dataLen + headersLen;
+ // in case there were single LFs in headers
+ // reserve an extra space for CR will be added before each single LF
+ int cntSingleLF = singleLF.Length();
+ newBufferLen += cntSingleLF;
+
+ if (!(*outPostData = p = (char*)moz_xmalloc(newBufferLen)))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // deal with single LF
+ const char *s = inPostData;
+ if (cntSingleLF) {
+ for (int i=0; i<cntSingleLF; i++) {
+ const char *plf = singleLF.ElementAt(i); // ptr to single LF in headers
+ int n = plf - s; // bytes to copy
+ if (n) { // for '\n\n' there is nothing to memcpy
+ memcpy(p, s, n);
+ p += n;
+ }
+ *p++ = CR;
+ s = plf;
+ *p++ = *s++;
+ }
+ }
+ // are we done with headers?
+ headersLen = pEoh - s;
+ if (headersLen) { // not yet
+ memcpy(p, s, headersLen); // copy the rest
+ p += headersLen;
+ }
+ } else if (dataLen) { // no ContentLenHeader is found but there is a data
+ // make new output buffer big enough
+ // to keep ContentLenHeader+value followed by data
+ uint32_t l = sizeof(ContentLenHeader) + sizeof(CRLFCRLF) + 32;
+ newBufferLen = dataLen + l;
+ if (!(*outPostData = p = (char*)moz_xmalloc(newBufferLen)))
+ return NS_ERROR_OUT_OF_MEMORY;
+ headersLen = snprintf(p, l,"%s: %u%s", ContentLenHeader, dataLen, CRLFCRLF);
+ if (headersLen == l) { // if snprintf has ate all extra space consider this as an error
+ free(p);
+ *outPostData = 0;
+ return NS_ERROR_FAILURE;
+ }
+ p += headersLen;
+ newBufferLen = headersLen + dataLen;
+ }
+ // at this point we've done with headers.
+ // there is a possibility that input buffer has only headers info in it
+ // which already parsed and copied into output buffer.
+ // copy the data
+ if (dataLen) {
+ memcpy(p, pSod, dataLen);
+ }
+
+ *outPostDataLen = newBufferLen;
+
+ return NS_OK;
+}
+
+nsresult
+nsPluginHost::CreateTempFileToPost(const char *aPostDataURL, nsIFile **aTmpFile)
+{
+ nsresult rv;
+ int64_t fileSize;
+ nsAutoCString filename;
+
+ // stat file == get size & convert file:///c:/ to c: if needed
+ nsCOMPtr<nsIFile> inFile;
+ rv = NS_GetFileFromURLSpec(nsDependentCString(aPostDataURL),
+ getter_AddRefs(inFile));
+ if (NS_FAILED(rv)) {
+ nsCOMPtr<nsIFile> localFile;
+ rv = NS_NewNativeLocalFile(nsDependentCString(aPostDataURL), false,
+ getter_AddRefs(localFile));
+ if (NS_FAILED(rv)) return rv;
+ inFile = localFile;
+ }
+ rv = inFile->GetFileSize(&fileSize);
+ if (NS_FAILED(rv)) return rv;
+ rv = inFile->GetNativePath(filename);
+ if (NS_FAILED(rv)) return rv;
+
+ if (fileSize != 0) {
+ nsCOMPtr<nsIInputStream> inStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), inFile);
+ if (NS_FAILED(rv)) return rv;
+
+ // Create a temporary file to write the http Content-length:
+ // %ld\r\n\" header and "\r\n" == end of headers for post data to
+
+ nsCOMPtr<nsIFile> tempFile;
+ rv = GetPluginTempDir(getter_AddRefs(tempFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString inFileName;
+ inFile->GetNativeLeafName(inFileName);
+ // XXX hack around bug 70083
+ inFileName.Insert(NS_LITERAL_CSTRING("post-"), 0);
+ rv = tempFile->AppendNative(inFileName);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ // make it unique, and mode == 0600, not world-readable
+ rv = tempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIOutputStream> outStream;
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
+ tempFile,
+ (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
+ 0600); // 600 so others can't read our form data
+ }
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Post data file couldn't be created!");
+ if (NS_FAILED(rv))
+ return rv;
+
+ char buf[1024];
+ uint32_t br, bw;
+ bool firstRead = true;
+ while (1) {
+ // Read() mallocs if buffer is null
+ rv = inStream->Read(buf, 1024, &br);
+ if (NS_FAILED(rv) || (int32_t)br <= 0)
+ break;
+ if (firstRead) {
+ //"For protocols in which the headers must be distinguished from the body,
+ // such as HTTP, the buffer or file should contain the headers, followed by
+ // a blank line, then the body. If no custom headers are required, simply
+ // add a blank line ('\n') to the beginning of the file or buffer.
+
+ char *parsedBuf;
+ // assuming first 1K (or what we got) has all headers in,
+ // lets parse it through nsPluginHost::ParsePostBufferToFixHeaders()
+ ParsePostBufferToFixHeaders((const char *)buf, br, &parsedBuf, &bw);
+ rv = outStream->Write(parsedBuf, bw, &br);
+ free(parsedBuf);
+ if (NS_FAILED(rv) || (bw != br))
+ break;
+
+ firstRead = false;
+ continue;
+ }
+ bw = br;
+ rv = outStream->Write(buf, bw, &br);
+ if (NS_FAILED(rv) || (bw != br))
+ break;
+ }
+
+ inStream->Close();
+ outStream->Close();
+ if (NS_SUCCEEDED(rv))
+ tempFile.forget(aTmpFile);
+ }
+ return rv;
+}
+
+nsresult
+nsPluginHost::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
+{
+ return PLUG_NewPluginNativeWindow(aPluginNativeWindow);
+}
+
+nsresult
+nsPluginHost::GetPluginName(nsNPAPIPluginInstance *aPluginInstance,
+ const char** aPluginName)
+{
+ nsNPAPIPluginInstance *instance = static_cast<nsNPAPIPluginInstance*>(aPluginInstance);
+ if (!instance)
+ return NS_ERROR_FAILURE;
+
+ nsNPAPIPlugin* plugin = instance->GetPlugin();
+ if (!plugin)
+ return NS_ERROR_FAILURE;
+
+ *aPluginName = TagForPlugin(plugin)->Name().get();
+
+ return NS_OK;
+}
+
+nsresult
+nsPluginHost::GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance,
+ nsIPluginTag **aPluginTag)
+{
+ NS_ENSURE_ARG_POINTER(aPluginInstance);
+ NS_ENSURE_ARG_POINTER(aPluginTag);
+
+ nsNPAPIPlugin *plugin = aPluginInstance->GetPlugin();
+ if (!plugin)
+ return NS_ERROR_FAILURE;
+
+ *aPluginTag = TagForPlugin(plugin);
+
+ NS_ADDREF(*aPluginTag);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer)
+{
+ RefPtr<nsPluginTag> pluginTag = mPlugins;
+ while (pluginTag) {
+ if (pluginTag->mUnloadTimer == timer) {
+ if (!IsRunningPlugin(pluginTag)) {
+ pluginTag->TryUnloadPlugin(false);
+ }
+ return NS_OK;
+ }
+ pluginTag = pluginTag->mNext;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+#ifdef XP_WIN
+// Re-enable any top level browser windows that were disabled by modal dialogs
+// displayed by the crashed plugin.
+static void
+CheckForDisabledWindows()
+{
+ nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!wm)
+ return;
+
+ nsCOMPtr<nsISimpleEnumerator> windowList;
+ wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
+ if (!windowList)
+ return;
+
+ bool haveWindows;
+ do {
+ windowList->HasMoreElements(&haveWindows);
+ if (!haveWindows)
+ return;
+
+ nsCOMPtr<nsISupports> supportsWindow;
+ windowList->GetNext(getter_AddRefs(supportsWindow));
+ nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
+ if (baseWin) {
+ nsCOMPtr<nsIWidget> widget;
+ baseWin->GetMainWidget(getter_AddRefs(widget));
+ if (widget && !widget->GetParent() &&
+ widget->IsVisible() &&
+ !widget->IsEnabled()) {
+ nsIWidget* child = widget->GetFirstChild();
+ bool enable = true;
+ while (child) {
+ if (child->WindowType() == eWindowType_dialog) {
+ enable = false;
+ break;
+ }
+ child = child->GetNextSibling();
+ }
+ if (enable) {
+ widget->Enable(true);
+ }
+ }
+ }
+ } while (haveWindows);
+}
+#endif
+
+void
+nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin,
+ const nsAString& pluginDumpID,
+ const nsAString& browserDumpID)
+{
+ nsPluginTag* crashedPluginTag = TagForPlugin(aPlugin);
+ MOZ_ASSERT(crashedPluginTag);
+
+ // Notify the app's observer that a plugin crashed so it can submit
+ // a crashreport.
+ bool submittedCrashReport = false;
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ nsCOMPtr<nsIWritablePropertyBag2> propbag =
+ do_CreateInstance("@mozilla.org/hash-property-bag;1");
+ if (obsService && propbag) {
+ uint32_t runID = 0;
+ PluginLibrary* library = aPlugin->GetLibrary();
+
+ if (!NS_WARN_IF(!library)) {
+ library->GetRunID(&runID);
+ }
+ propbag->SetPropertyAsUint32(NS_LITERAL_STRING("runID"), runID);
+
+ nsCString pluginName;
+ crashedPluginTag->GetName(pluginName);
+ propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginName"),
+ NS_ConvertUTF8toUTF16(pluginName));
+ propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"),
+ pluginDumpID);
+ propbag->SetPropertyAsAString(NS_LITERAL_STRING("browserDumpID"),
+ browserDumpID);
+ propbag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
+ submittedCrashReport);
+ obsService->NotifyObservers(propbag, "plugin-crashed", nullptr);
+ // see if an observer submitted a crash report.
+ propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
+ &submittedCrashReport);
+ }
+
+ // Invalidate each nsPluginInstanceTag for the crashed plugin
+
+ for (uint32_t i = mInstances.Length(); i > 0; i--) {
+ nsNPAPIPluginInstance* instance = mInstances[i - 1];
+ if (instance->GetPlugin() == aPlugin) {
+ // notify the content node (nsIObjectLoadingContent) that the
+ // plugin has crashed
+ nsCOMPtr<nsIDOMElement> domElement;
+ instance->GetDOMElement(getter_AddRefs(domElement));
+ nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement));
+ if (objectContent) {
+ objectContent->PluginCrashed(crashedPluginTag, pluginDumpID, browserDumpID,
+ submittedCrashReport);
+ }
+
+ instance->Destroy();
+ mInstances.RemoveElement(instance);
+ OnPluginInstanceDestroyed(crashedPluginTag);
+ }
+ }
+
+ // Only after all instances have been invalidated is it safe to null
+ // out nsPluginTag.mPlugin. The next time we try to create an
+ // instance of this plugin we reload it (launch a new plugin process).
+
+ crashedPluginTag->mPlugin = nullptr;
+ crashedPluginTag->mContentProcessRunningCount = 0;
+
+#ifdef XP_WIN
+ CheckForDisabledWindows();
+#endif
+}
+
+nsNPAPIPluginInstance*
+nsPluginHost::FindInstance(const char *mimetype)
+{
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ nsNPAPIPluginInstance* instance = mInstances[i];
+
+ const char* mt;
+ nsresult rv = instance->GetMIMEType(&mt);
+ if (NS_FAILED(rv))
+ continue;
+
+ if (PL_strcasecmp(mt, mimetype) == 0)
+ return instance;
+ }
+
+ return nullptr;
+}
+
+nsNPAPIPluginInstance*
+nsPluginHost::FindOldestStoppedInstance()
+{
+ nsNPAPIPluginInstance *oldestInstance = nullptr;
+ TimeStamp oldestTime = TimeStamp::Now();
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ nsNPAPIPluginInstance *instance = mInstances[i];
+ if (instance->IsRunning())
+ continue;
+
+ TimeStamp time = instance->StopTime();
+ if (time < oldestTime) {
+ oldestTime = time;
+ oldestInstance = instance;
+ }
+ }
+
+ return oldestInstance;
+}
+
+uint32_t
+nsPluginHost::StoppedInstanceCount()
+{
+ uint32_t stoppedCount = 0;
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ nsNPAPIPluginInstance *instance = mInstances[i];
+ if (!instance->IsRunning())
+ stoppedCount++;
+ }
+ return stoppedCount;
+}
+
+nsTArray< RefPtr<nsNPAPIPluginInstance> >*
+nsPluginHost::InstanceArray()
+{
+ return &mInstances;
+}
+
+void
+nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag)
+{
+ for (int32_t i = mInstances.Length(); i > 0; i--) {
+ nsNPAPIPluginInstance *instance = mInstances[i - 1];
+ if (instance->IsRunning() && (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) {
+ instance->SetWindow(nullptr);
+ instance->Stop();
+
+ // Get rid of all the instances without the possibility of caching.
+ nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin());
+ instance->SetWindow(nullptr);
+
+ nsCOMPtr<nsIDOMElement> domElement;
+ instance->GetDOMElement(getter_AddRefs(domElement));
+ nsCOMPtr<nsIObjectLoadingContent> objectContent =
+ do_QueryInterface(domElement);
+
+ instance->Destroy();
+
+ mInstances.RemoveElement(instance);
+ OnPluginInstanceDestroyed(pluginTag);
+
+ // Notify owning content that we destroyed its plugin out from under it
+ if (objectContent) {
+ objectContent->PluginDestroyed();
+ }
+ }
+ }
+}
+
+// Runnable that does an async destroy of a plugin.
+
+class nsPluginDestroyRunnable : public Runnable,
+ public PRCList
+{
+public:
+ explicit nsPluginDestroyRunnable(nsNPAPIPluginInstance *aInstance)
+ : mInstance(aInstance)
+ {
+ PR_INIT_CLIST(this);
+ PR_APPEND_LINK(this, &sRunnableListHead);
+ }
+
+ virtual ~nsPluginDestroyRunnable()
+ {
+ PR_REMOVE_LINK(this);
+ }
+
+ NS_IMETHOD Run() override
+ {
+ RefPtr<nsNPAPIPluginInstance> instance;
+
+ // Null out mInstance to make sure this code in another runnable
+ // will do the right thing even if someone was holding on to this
+ // runnable longer than we expect.
+ instance.swap(mInstance);
+
+ if (PluginDestructionGuard::DelayDestroy(instance)) {
+ // It's still not safe to destroy the plugin, it's now up to the
+ // outermost guard on the stack to take care of the destruction.
+ return NS_OK;
+ }
+
+ nsPluginDestroyRunnable *r =
+ static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(&sRunnableListHead));
+
+ while (r != &sRunnableListHead) {
+ if (r != this && r->mInstance == instance) {
+ // There's another runnable scheduled to tear down
+ // instance. Let it do the job.
+ return NS_OK;
+ }
+ r = static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(r));
+ }
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("Doing delayed destroy of instance %p\n", instance.get()));
+
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (host)
+ host->StopPluginInstance(instance);
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("Done with delayed destroy of instance %p\n", instance.get()));
+
+ return NS_OK;
+ }
+
+protected:
+ RefPtr<nsNPAPIPluginInstance> mInstance;
+
+ static PRCList sRunnableListHead;
+};
+
+PRCList nsPluginDestroyRunnable::sRunnableListHead =
+ PR_INIT_STATIC_CLIST(&nsPluginDestroyRunnable::sRunnableListHead);
+
+PRCList PluginDestructionGuard::sListHead =
+ PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead);
+
+PluginDestructionGuard::PluginDestructionGuard(nsNPAPIPluginInstance *aInstance)
+ : mInstance(aInstance)
+{
+ Init();
+}
+
+PluginDestructionGuard::PluginDestructionGuard(PluginAsyncSurrogate *aSurrogate)
+ : mInstance(static_cast<nsNPAPIPluginInstance*>(aSurrogate->GetNPP()->ndata))
+{
+ InitAsync();
+}
+
+PluginDestructionGuard::PluginDestructionGuard(NPP npp)
+ : mInstance(npp ? static_cast<nsNPAPIPluginInstance*>(npp->ndata) : nullptr)
+{
+ Init();
+}
+
+PluginDestructionGuard::~PluginDestructionGuard()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
+
+ PR_REMOVE_LINK(this);
+
+ if (mDelayedDestroy) {
+ // We've attempted to destroy the plugin instance we're holding on
+ // to while we were guarding it. Do the actual destroy now, off of
+ // a runnable.
+ RefPtr<nsPluginDestroyRunnable> evt =
+ new nsPluginDestroyRunnable(mInstance);
+
+ NS_DispatchToMainThread(evt);
+ }
+}
+
+// static
+bool
+PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance *aInstance)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
+ NS_ASSERTION(aInstance, "Uh, I need an instance!");
+
+ // Find the first guard on the stack and make it do a delayed
+ // destroy upon destruction.
+
+ PluginDestructionGuard *g =
+ static_cast<PluginDestructionGuard*>(PR_LIST_HEAD(&sListHead));
+
+ while (g != &sListHead) {
+ if (g->mInstance == aInstance) {
+ g->mDelayedDestroy = true;
+
+ return true;
+ }
+ g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));
+ }
+
+ return false;
+}
diff --git a/dom/plugins/base/nsPluginHost.h b/dom/plugins/base/nsPluginHost.h
new file mode 100644
index 000000000..27e90c991
--- /dev/null
+++ b/dom/plugins/base/nsPluginHost.h
@@ -0,0 +1,465 @@
+/* -*- 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/. */
+
+#ifndef nsPluginHost_h_
+#define nsPluginHost_h_
+
+#include "nsIPluginHost.h"
+#include "nsIObserver.h"
+#include "nsCOMPtr.h"
+#include "prlink.h"
+#include "prclist.h"
+#include "nsIPluginTag.h"
+#include "nsPluginsDir.h"
+#include "nsPluginDirServiceProvider.h"
+#include "nsWeakPtr.h"
+#include "nsIPrompt.h"
+#include "nsWeakReference.h"
+#include "MainThreadUtils.h"
+#include "nsTArray.h"
+#include "nsTObserverArray.h"
+#include "nsITimer.h"
+#include "nsPluginTags.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsIIDNService.h"
+#include "nsCRT.h"
+
+#ifdef XP_WIN
+#include <minwindef.h>
+#include "nsIWindowsRegKey.h"
+#endif
+
+namespace mozilla {
+namespace plugins {
+class PluginAsyncSurrogate;
+class PluginTag;
+} // namespace plugins
+} // namespace mozilla
+
+class nsNPAPIPlugin;
+class nsIFile;
+class nsIChannel;
+class nsPluginNativeWindow;
+class nsObjectLoadingContent;
+class nsPluginInstanceOwner;
+class nsPluginUnloadRunnable;
+class nsNPAPIPluginInstance;
+class nsNPAPIPluginStreamListener;
+class nsIPluginInstanceOwner;
+class nsIInputStream;
+class nsIStreamListener;
+#ifndef npapi_h_
+struct _NPP;
+typedef _NPP* NPP;
+#endif
+
+class nsInvalidPluginTag : public nsISupports
+{
+ virtual ~nsInvalidPluginTag();
+public:
+ explicit nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime = 0);
+
+ NS_DECL_ISUPPORTS
+
+ nsCString mFullPath;
+ int64_t mLastModifiedTime;
+ bool mSeen;
+
+ RefPtr<nsInvalidPluginTag> mPrev;
+ RefPtr<nsInvalidPluginTag> mNext;
+};
+
+class nsPluginHost final : public nsIPluginHost,
+ public nsIObserver,
+ public nsITimerCallback,
+ public nsSupportsWeakReference
+{
+ friend class nsPluginTag;
+ friend class nsFakePluginTag;
+ virtual ~nsPluginHost();
+
+public:
+ nsPluginHost();
+
+ static already_AddRefed<nsPluginHost> GetInst();
+
+ NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPLUGINHOST
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSITIMERCALLBACK
+
+ nsresult LoadPlugins();
+ nsresult UnloadPlugins();
+
+ nsresult SetUpPluginInstance(const nsACString &aMimeType,
+ nsIURI *aURL,
+ nsPluginInstanceOwner *aOwner);
+
+ // Acts like a bitfield
+ enum PluginFilter {
+ eExcludeNone = nsIPluginHost::EXCLUDE_NONE,
+ eExcludeDisabled = nsIPluginHost::EXCLUDE_DISABLED,
+ eExcludeFake = nsIPluginHost::EXCLUDE_FAKE
+ };
+ // FIXME-jsplugins comment about fake
+ bool HavePluginForType(const nsACString & aMimeType,
+ PluginFilter aFilter = eExcludeDisabled);
+
+ // FIXME-jsplugins what if fake has different extensions
+ bool HavePluginForExtension(const nsACString & aExtension,
+ /* out */ nsACString & aMimeType,
+ PluginFilter aFilter = eExcludeDisabled);
+
+ void GetPlugins(nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray,
+ bool aIncludeDisabled = false);
+
+ nsresult FindPluginsForContent(uint32_t aPluginEpoch,
+ nsTArray<mozilla::plugins::PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch);
+
+ nsresult GetURL(nsISupports* pluginInst,
+ const char* url,
+ const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost,
+ const char* referrer,
+ bool forceJSEnabled);
+ nsresult PostURL(nsISupports* pluginInst,
+ const char* url,
+ uint32_t postDataLen,
+ const char* postData,
+ bool isFile,
+ const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost,
+ const char* referrer,
+ bool forceJSEnabled,
+ uint32_t postHeadersLength,
+ const char* postHeaders);
+
+ nsresult FindProxyForURL(const char* url, char* *result);
+ nsresult UserAgent(const char **retstring);
+ nsresult ParsePostBufferToFixHeaders(const char *inPostData,
+ uint32_t inPostDataLen,
+ char **outPostData,
+ uint32_t *outPostDataLen);
+ nsresult CreateTempFileToPost(const char *aPostDataURL, nsIFile **aTmpFile);
+ nsresult NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow);
+
+ void AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, bool isVisible);
+ void RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame);
+
+ nsresult GetPluginName(nsNPAPIPluginInstance *aPluginInstance,
+ const char** aPluginName);
+ nsresult StopPluginInstance(nsNPAPIPluginInstance* aInstance);
+ nsresult GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance,
+ nsIPluginTag **aPluginTag);
+
+ nsresult
+ NewPluginURLStream(const nsString& aURL,
+ nsNPAPIPluginInstance *aInstance,
+ nsNPAPIPluginStreamListener *aListener,
+ nsIInputStream *aPostStream = nullptr,
+ const char *aHeadersData = nullptr,
+ uint32_t aHeadersDataLen = 0);
+
+ nsresult
+ GetURLWithHeaders(nsNPAPIPluginInstance *pluginInst,
+ const char* url,
+ const char* target = nullptr,
+ nsNPAPIPluginStreamListener* streamListener = nullptr,
+ const char* altHost = nullptr,
+ const char* referrer = nullptr,
+ bool forceJSEnabled = false,
+ uint32_t getHeadersLength = 0,
+ const char* getHeaders = nullptr);
+
+ nsresult
+ AddHeadersToChannel(const char *aHeadersData, uint32_t aHeadersDataLen,
+ nsIChannel *aGenericChannel);
+
+ static nsresult GetPluginTempDir(nsIFile **aDir);
+
+ // Helper that checks if a type is whitelisted in plugin.allowed_types.
+ // Always returns true if plugin.allowed_types is not set
+ static bool IsTypeWhitelisted(const char *aType);
+
+ // Helper that checks if a plugin of a given MIME type can be loaded by the
+ // parent process. It checks the plugin.load_in_parent_process.<mime> pref.
+ // Always returns false if plugin.load_in_parent_process.<mime> is not set.
+ static bool ShouldLoadTypeInParent(const nsACString& aMimeType);
+
+ // checks whether aType is a type we recognize for potential special handling
+ enum SpecialType { eSpecialType_None,
+ // Needed to whitelist for async init support
+ eSpecialType_Test,
+ // Informs some decisions about OOP and quirks
+ eSpecialType_Flash,
+ // Binds to the <applet> tag, has various special
+ // rules around opening channels, codebase, ...
+ eSpecialType_Java,
+ // Some IPC quirks
+ eSpecialType_Silverlight,
+ // Native widget quirks
+ eSpecialType_PDF,
+ // Native widget quirks
+ eSpecialType_RealPlayer,
+ // Native widget quirks
+ eSpecialType_Unity };
+ static SpecialType GetSpecialType(const nsACString & aMIMEType);
+
+ static nsresult PostPluginUnloadEvent(PRLibrary* aLibrary);
+
+ void PluginCrashed(nsNPAPIPlugin* plugin,
+ const nsAString& pluginDumpID,
+ const nsAString& browserDumpID);
+
+ nsNPAPIPluginInstance *FindInstance(const char *mimetype);
+ nsNPAPIPluginInstance *FindOldestStoppedInstance();
+ uint32_t StoppedInstanceCount();
+
+ nsTArray< RefPtr<nsNPAPIPluginInstance> > *InstanceArray();
+
+ // Return the tag for |aLibrary| if found, nullptr if not.
+ nsPluginTag* FindTagForLibrary(PRLibrary* aLibrary);
+
+ // The last argument should be false if we already have an in-flight stream
+ // and don't need to set up a new stream.
+ nsresult InstantiatePluginInstance(const nsACString& aMimeType, nsIURI* aURL,
+ nsObjectLoadingContent *aContent,
+ nsPluginInstanceOwner** aOwner);
+
+ // Does not accept nullptr and should never fail.
+ nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
+
+ nsPluginTag* PluginWithId(uint32_t aId);
+
+ nsresult GetPlugin(const nsACString &aMimeType, nsNPAPIPlugin** aPlugin);
+ nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin);
+ void NotifyContentModuleDestroyed(uint32_t aPluginId);
+
+ nsresult NewPluginStreamListener(nsIURI* aURL,
+ nsNPAPIPluginInstance* aInstance,
+ nsIStreamListener **aStreamListener);
+
+ void CreateWidget(nsPluginInstanceOwner* aOwner);
+
+ nsresult EnumerateSiteData(const nsACString& domain,
+ const InfallibleTArray<nsCString>& sites,
+ InfallibleTArray<nsCString>& result,
+ bool firstMatchOnly);
+
+private:
+ friend class nsPluginUnloadRunnable;
+
+ void DestroyRunningInstances(nsPluginTag* aPluginTag);
+
+ // Writes updated plugins settings to disk and unloads the plugin
+ // if it is now disabled. Should only be called by the plugin tag in question
+ void UpdatePluginInfo(nsPluginTag* aPluginTag);
+
+ nsresult TrySetUpPluginInstance(const nsACString &aMimeType, nsIURI *aURL,
+ nsPluginInstanceOwner *aOwner);
+
+ // FIXME-jsplugins comment here about when things may be fake
+ nsPluginTag*
+ FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches);
+
+ // Find a plugin for the given type. If aIncludeFake is true a fake plugin
+ // will be preferred if one exists; otherwise a fake plugin will never be
+ // returned. If aCheckEnabled is false, disabled plugins can be returned.
+ nsIInternalPluginTag* FindPluginForType(const nsACString& aMimeType,
+ bool aIncludeFake, bool aCheckEnabled);
+
+ // Find specifically a fake plugin for the given type. If aCheckEnabled is
+ // false, disabled plugins can be returned.
+ nsFakePluginTag* FindFakePluginForType(const nsACString & aMimeType,
+ bool aCheckEnabled);
+
+ // Find specifically a fake plugin for the given extension. If aCheckEnabled
+ // is false, disabled plugins can be returned. aMimeType will be filled in
+ // with the MIME type the plugin is registered for.
+ nsFakePluginTag* FindFakePluginForExtension(const nsACString & aExtension,
+ /* out */ nsACString & aMimeType,
+ bool aCheckEnabled);
+
+ // Find specifically a native (NPAPI) plugin for the given type. If
+ // aCheckEnabled is false, disabled plugins can be returned.
+ nsPluginTag* FindNativePluginForType(const nsACString & aMimeType,
+ bool aCheckEnabled);
+
+ // Find specifically a native (NPAPI) plugin for the given extension. If
+ // aCheckEnabled is false, disabled plugins can be returned. aMimeType will
+ // be filled in with the MIME type the plugin is registered for.
+ nsPluginTag* FindNativePluginForExtension(const nsACString & aExtension,
+ /* out */ nsACString & aMimeType,
+ bool aCheckEnabled);
+
+ nsresult
+ FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
+
+ nsresult FindPluginsInContent(bool aCreatePluginList, bool * aPluginsChanged);
+
+ nsresult
+ FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
+
+ // FIXME revisit, no ns prefix
+ // Registers or unregisters the given mime type with the category manager
+ enum nsRegisterType { ePluginRegister,
+ ePluginUnregister,
+ // Checks if this type should still be registered first
+ ePluginMaybeUnregister };
+ void RegisterWithCategoryManager(const nsCString& aMimeType,
+ nsRegisterType aType);
+
+ void AddPluginTag(nsPluginTag* aPluginTag);
+
+ nsresult
+ ScanPluginsDirectory(nsIFile *pluginsDir,
+ bool aCreatePluginList,
+ bool *aPluginsChanged);
+
+ nsresult
+ ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
+ bool aCreatePluginList,
+ bool *aPluginsChanged);
+
+ nsresult EnsurePluginLoaded(nsPluginTag* aPluginTag);
+
+ bool IsRunningPlugin(nsPluginTag * aPluginTag);
+
+ // Stores all plugins info into the registry
+ nsresult WritePluginInfo();
+
+ // Loads all cached plugins info into mCachedPlugins
+ nsresult ReadPluginInfo();
+
+ // Given a file path, returns the plugins info from our cache
+ // and removes it from the cache.
+ void RemoveCachedPluginsInfo(const char *filePath,
+ nsPluginTag **result);
+
+ // Checks to see if a tag object is in our list of live tags.
+ bool IsLiveTag(nsIPluginTag* tag);
+
+ // Checks our list of live tags for an equivalent tag.
+ nsPluginTag* HaveSamePlugin(const nsPluginTag * aPluginTag);
+
+ // Returns the first plugin at |path|
+ nsPluginTag* FirstPluginWithPath(const nsCString& path);
+
+ nsresult EnsurePrivateDirServiceProvider();
+
+ void OnPluginInstanceDestroyed(nsPluginTag* aPluginTag);
+
+ // To be used by the chrome process whenever the set of plugins changes.
+ void IncrementChromeEpoch();
+
+ // To be used by the chrome process; returns the current epoch.
+ uint32_t ChromeEpoch();
+
+ // To be used by the content process to get/set the last observed epoch value
+ // from the chrome process.
+ uint32_t ChromeEpochForContent();
+ void SetChromeEpochForContent(uint32_t aEpoch);
+
+ void UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag);
+
+ // On certain platforms, we only want to load certain plugins. This function
+ // centralizes loading rules.
+ bool ShouldAddPlugin(nsPluginTag* aPluginTag);
+
+ RefPtr<nsPluginTag> mPlugins;
+ RefPtr<nsPluginTag> mCachedPlugins;
+ RefPtr<nsInvalidPluginTag> mInvalidPlugins;
+
+ nsTArray< RefPtr<nsFakePluginTag> > mFakePlugins;
+
+ bool mPluginsLoaded;
+
+ // set by pref plugin.override_internal_types
+ bool mOverrideInternalTypes;
+
+ // set by pref plugin.disable
+ bool mPluginsDisabled;
+
+ // Any instances in this array will have valid plugin objects via GetPlugin().
+ // When removing an instance it might not die - be sure to null out it's plugin.
+ nsTArray< RefPtr<nsNPAPIPluginInstance> > mInstances;
+
+ nsCOMPtr<nsIFile> mPluginRegFile;
+#ifdef XP_WIN
+ RefPtr<nsPluginDirServiceProvider> mPrivateDirServiceProvider;
+
+ // In order to reload plugins when they change, we watch the registry via
+ // this object.
+ nsCOMPtr<nsIWindowsRegKey> mRegKeyHKLM;
+ nsCOMPtr<nsIWindowsRegKey> mRegKeyHKCU;
+#endif
+
+ nsCOMPtr<nsIEffectiveTLDService> mTLDService;
+ nsCOMPtr<nsIIDNService> mIDNService;
+
+ // Helpers for ClearSiteData and SiteHasData.
+ nsresult NormalizeHostname(nsCString& host);
+
+ nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only
+
+ // This epoch increases each time we load the list of plugins from disk.
+ // In the chrome process, this stores the actual epoch.
+ // In the content process, this stores the last epoch value observed
+ // when reading plugins from chrome.
+ uint32_t mPluginEpoch;
+
+ static nsIFile *sPluginTempDir;
+
+ // We need to hold a global ptr to ourselves because we register for
+ // two different CIDs for some reason...
+ static nsPluginHost* sInst;
+};
+
+class PluginDestructionGuard : protected PRCList
+{
+public:
+ explicit PluginDestructionGuard(nsNPAPIPluginInstance *aInstance);
+ explicit PluginDestructionGuard(mozilla::plugins::PluginAsyncSurrogate *aSurrogate);
+ explicit PluginDestructionGuard(NPP npp);
+
+ ~PluginDestructionGuard();
+
+ static bool DelayDestroy(nsNPAPIPluginInstance *aInstance);
+
+protected:
+ void Init()
+ {
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
+
+ mDelayedDestroy = false;
+
+ PR_INIT_CLIST(this);
+ PR_INSERT_BEFORE(this, &sListHead);
+ }
+
+ void InitAsync()
+ {
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
+
+ mDelayedDestroy = false;
+
+ PR_INIT_CLIST(this);
+ // Instances with active surrogates must be inserted *after* sListHead so
+ // that they appear to be at the bottom of the stack
+ PR_INSERT_AFTER(this, &sListHead);
+ }
+
+ RefPtr<nsNPAPIPluginInstance> mInstance;
+ bool mDelayedDestroy;
+
+ static PRCList sListHead;
+};
+
+#endif // nsPluginHost_h_
diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp
new file mode 100644
index 000000000..b7651be1a
--- /dev/null
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -0,0 +1,3894 @@
+/* -*- 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 <cairo-xlib.h>
+#include "gfxXlibSurface.h"
+/* X headers suck */
+enum { XKeyPress = KeyPress };
+#include "mozilla/X11Util.h"
+using mozilla::DefaultXDisplay;
+#endif
+
+#include "nsPluginInstanceOwner.h"
+
+#include "gfxUtils.h"
+#include "nsIRunnable.h"
+#include "nsContentUtils.h"
+#include "nsRect.h"
+#include "nsSize.h"
+#include "nsDisplayList.h"
+#include "ImageLayers.h"
+#include "GLImages.h"
+#include "nsPluginFrame.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 "nsObjectLoadingContent.h"
+#include "nsAttrName.h"
+#include "nsIFocusManager.h"
+#include "nsFocusManager.h"
+#include "nsIDOMDragEvent.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIScrollableFrame.h"
+#include "nsIDocShell.h"
+#include "ImageContainer.h"
+#include "nsIDOMHTMLCollection.h"
+#include "GLContext.h"
+#include "EGLUtils.h"
+#include "nsIContentInlines.h"
+#include "mozilla/MiscEvents.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "mozilla/dom/TabChild.h"
+#include "nsFrameSelection.h"
+#include "PuppetWidget.h"
+#include "nsPIWindowRoot.h"
+#include "mozilla/IMEStateManager.h"
+#include "mozilla/TextComposition.h"
+#include "mozilla/AutoRestore.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 <wtypes.h>
+#include <winuser.h>
+#include "mozilla/widget/WinMessages.h"
+#endif // #ifdef XP_WIN
+
+#ifdef XP_MACOSX
+#include "ComplexTextInputPanel.h"
+#include "nsIDOMXULDocument.h"
+#include "nsIDOMXULCommandDispatcher.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "ANPBase.h"
+#include "AndroidBridge.h"
+#include "ClientLayerManager.h"
+#include "nsWindow.h"
+
+static nsPluginInstanceOwner* sFullScreenInstance = nullptr;
+
+using namespace mozilla::dom;
+
+#include <android/log.h>
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::layers;
+
+static inline nsPoint AsNsPoint(const nsIntPoint &p) {
+ return nsPoint(p.x, p.y);
+}
+
+// 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
+{
+ virtual ~nsPluginDOMContextMenuListener();
+
+public:
+ explicit nsPluginDOMContextMenuListener(nsIContent* aContent);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ void Destroy(nsIContent* aContent);
+
+ nsEventStatus ProcessEvent(const WidgetGUIEvent& anEvent)
+ {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+};
+
+class AsyncPaintWaitEvent : public Runnable
+{
+public:
+ AsyncPaintWaitEvent(nsIContent* aContent, bool aFinished) :
+ mContent(aContent), mFinished(aFinished)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent,
+ mFinished ? NS_LITERAL_STRING("MozPaintWaitFinished") : NS_LITERAL_STRING("MozPaintWait"),
+ true, true);
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIContent> mContent;
+ bool mFinished;
+};
+
+void
+nsPluginInstanceOwner::NotifyPaintWaiter(nsDisplayListBuilder* aBuilder)
+{
+ // This is notification for reftests about async plugin paint start
+ if (!mWaitingForPaint && !IsUpToDate() && aBuilder->ShouldSyncDecodeImages()) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ nsCOMPtr<nsIRunnable> event = new AsyncPaintWaitEvent(content, false);
+ // Run this event as soon as it's safe to do so, since listeners need to
+ // receive it immediately
+ nsContentUtils::AddScriptRunner(event);
+ mWaitingForPaint = true;
+ }
+}
+
+#if MOZ_WIDGET_ANDROID
+static void
+AttachToContainerAsSurfaceTexture(ImageContainer* container,
+ nsNPAPIPluginInstance* instance,
+ const LayoutDeviceRect& rect,
+ RefPtr<Image>* out_image)
+{
+ MOZ_ASSERT(out_image);
+ MOZ_ASSERT(!*out_image);
+
+ mozilla::gl::AndroidSurfaceTexture* surfTex = instance->AsSurfaceTexture();
+ if (!surfTex) {
+ return;
+ }
+
+ RefPtr<Image> img = new SurfaceTextureImage(
+ surfTex,
+ gfx::IntSize::Truncate(rect.width, rect.height),
+ instance->OriginPos());
+ *out_image = img;
+}
+#endif
+
+bool
+nsPluginInstanceOwner::NeedsScrollImageLayer()
+{
+#if defined(XP_WIN)
+ // If this is a windowed plugin and we're doing layout in the content
+ // process, force the creation of an image layer for the plugin. We'll
+ // paint to this when scrolling.
+ return XRE_IsContentProcess() &&
+ mPluginWindow &&
+ mPluginWindow->type == NPWindowTypeWindow;
+#else
+ return false;
+#endif
+}
+
+already_AddRefed<ImageContainer>
+nsPluginInstanceOwner::GetImageContainer()
+{
+ if (!mInstance)
+ return nullptr;
+
+ RefPtr<ImageContainer> container;
+
+#if MOZ_WIDGET_ANDROID
+ 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.
+ float resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution();
+ ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution)).Size();
+ mInstance->NotifySize(nsIntSize::Truncate(screenSize.width, screenSize.height));
+
+ container = LayerManager::CreateImageContainer();
+
+ if (r.width && r.height) {
+ // Try to get it as an EGLImage first.
+ RefPtr<Image> img;
+ AttachToContainerAsSurfaceTexture(container, mInstance, r, &img);
+
+ if (img) {
+ container->SetCurrentImageInTransaction(img);
+ }
+ }
+#else
+ if (NeedsScrollImageLayer()) {
+ // windowed plugin under e10s
+#if defined(XP_WIN)
+ mInstance->GetScrollCaptureContainer(getter_AddRefs(container));
+#endif
+ } else {
+ // async windowless rendering
+ mInstance->GetImageContainer(getter_AddRefs(container));
+ }
+#endif
+
+ return container.forget();
+}
+
+void
+nsPluginInstanceOwner::DidComposite()
+{
+ if (mInstance) {
+ mInstance->DidComposite();
+ }
+}
+
+void
+nsPluginInstanceOwner::SetBackgroundUnknown()
+{
+ if (mInstance) {
+ mInstance->SetBackgroundUnknown();
+ }
+}
+
+already_AddRefed<mozilla::gfx::DrawTarget>
+nsPluginInstanceOwner::BeginUpdateBackground(const nsIntRect& aRect)
+{
+ nsIntRect rect = aRect;
+ RefPtr<DrawTarget> dt;
+ if (mInstance &&
+ NS_SUCCEEDED(mInstance->BeginUpdateBackground(&rect, getter_AddRefs(dt)))) {
+ return dt.forget();
+ }
+ return nullptr;
+}
+
+void
+nsPluginInstanceOwner::EndUpdateBackground(const nsIntRect& aRect)
+{
+ nsIntRect rect = aRect;
+ if (mInstance) {
+ mInstance->EndUpdateBackground(&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()
+ : mPluginWindow(nullptr)
+{
+ // create nsPluginNativeWindow object, it is derived from NPWindow
+ // struct and allows to manipulate native window procedure
+ nsCOMPtr<nsIPluginHost> pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
+ mPluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+ if (mPluginHost)
+ mPluginHost->NewPluginNativeWindow(&mPluginWindow);
+
+ mPluginFrame = nullptr;
+ mWidgetCreationComplete = false;
+#ifdef XP_MACOSX
+ mSentInitialTopLevelWindowEvent = false;
+ mLastWindowIsActive = false;
+ mLastContentFocused = false;
+ mLastScaleFactor = 1.0;
+ mShouldBlurOnActivate = false;
+#endif
+ mLastCSSZoomFactor = 1.0;
+ mContentFocused = false;
+ mWidgetVisible = true;
+ mPluginWindowVisible = false;
+ mPluginDocumentActiveState = true;
+ 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
+
+#ifdef XP_WIN
+ mGotCompositionData = false;
+ mSentStartComposition = false;
+ mPluginDidNotHandleIMEComposition = false;
+#endif
+}
+
+nsPluginInstanceOwner::~nsPluginInstanceOwner()
+{
+ if (mWaitingForPaint) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (content) {
+ // 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<nsIRunnable> event = new AsyncPaintWaitEvent(content, true);
+ NS_DispatchToMainThread(event);
+ }
+ }
+
+ mPluginFrame = nullptr;
+
+ PLUG_DeletePluginNativeWindow(mPluginWindow);
+ mPluginWindow = nullptr;
+
+#ifdef MOZ_WIDGET_ANDROID
+ RemovePluginView();
+#endif
+
+ if (mInstance) {
+ mInstance->SetOwner(nullptr);
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsPluginInstanceOwner,
+ nsIPluginInstanceOwner,
+ nsIDOMEventListener,
+ nsIPrivacyTransitionObserver,
+ nsIKeyEventInPluginCallback,
+ 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<nsIDocument> doc;
+ GetDocument(getter_AddRefs(doc));
+ if (doc) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = doc->GetWindow()) {
+ nsCOMPtr<nsIDocShell> 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<nsIDocument> doc;
+ nsresult rv = GetDocument(getter_AddRefs(doc));
+ nsCOMPtr<nsIPluginDocument> pDoc (do_QueryInterface(doc));
+
+ if (pDoc) {
+ *aMode = NP_FULL;
+ } else {
+ *aMode = NP_EMBED;
+ }
+
+ return rv;
+}
+
+void nsPluginInstanceOwner::GetAttributes(nsTArray<MozPluginParameter>& attributes)
+{
+ nsCOMPtr<nsIObjectLoadingContent> content = do_QueryReferent(mContent);
+ nsObjectLoadingContent *loadingContent =
+ static_cast<nsObjectLoadingContent*>(content.get());
+
+ loadingContent->GetPluginAttributes(attributes);
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetDOMElement(nsIDOMElement* *result)
+{
+ return CallQueryReferent(mContent.get(), 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,
+ bool aDoCheckLoadURIChecks)
+{
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (!content) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (content->IsEditable()) {
+ return NS_OK;
+ }
+
+ nsIDocument *doc = content->GetUncomposedDoc();
+ 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<nsISupports> container = presContext->GetContainerWeak();
+ NS_ENSURE_TRUE(container,NS_ERROR_FAILURE);
+ nsCOMPtr<nsILinkHandler> lh = do_QueryInterface(container);
+ NS_ENSURE_TRUE(lh, NS_ERROR_FAILURE);
+
+ nsAutoString unitarget;
+ if ((0 == PL_strcmp(aTarget, "newwindow")) ||
+ (0 == PL_strcmp(aTarget, "_new"))) {
+ unitarget.AssignASCII("_blank");
+ }
+ else if (0 == PL_strcmp(aTarget, "_current")) {
+ unitarget.AssignASCII("_self");
+ }
+ else {
+ unitarget.AssignASCII(aTarget); // XXX could this be nonascii?
+ }
+
+ nsCOMPtr<nsIURI> baseURI = GetBaseURI();
+
+ // Create an absolute URL
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, baseURI);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ if (aDoCheckLoadURIChecks) {
+ nsCOMPtr<nsIScriptSecurityManager> secMan(
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
+
+ rv = secMan->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
+ nsIScriptSecurityManager::STANDARD);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIInputStream> headersDataStream;
+ if (aPostStream && aHeadersData) {
+ if (!aHeadersDataLen)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIStringInputStream> 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(content, uri, unitarget.get(), NullString(),
+ aPostStream, headersDataStream, true);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(nsIDocument* *aDocument)
+{
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (!aDocument || !content) {
+ 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 = content->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 && (!mPluginFrame || IsUpToDate())) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ // 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<nsIRunnable> event = new AsyncPaintWaitEvent(content, true);
+ NS_DispatchToMainThread(event);
+ mWaitingForPaint = false;
+ }
+
+ if (!mPluginFrame || !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.
+ RefPtr<ImageContainer> container;
+ mInstance->GetImageContainer(getter_AddRefs(container));
+#endif
+
+#ifndef XP_MACOSX
+ // Silverlight calls invalidate for windowed plugins so this needs to work.
+ if (mWidget) {
+ mWidget->Invalidate(
+ LayoutDeviceIntRect(invalidRect->left, invalidRect->top,
+ invalidRect->right - invalidRect->left,
+ invalidRect->bottom - invalidRect->top));
+ // Plugin instances also call invalidate when plugin windows are hidden
+ // during scrolling. In this case fall through so we invalidate the
+ // underlying layer.
+ if (!NeedsScrollImageLayer()) {
+ 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);
+ mPluginFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN, &rect);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRegion(NPRegion invalidRegion)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPluginInstanceOwner::RedrawPlugin()
+{
+ if (mPluginFrame) {
+ mPluginFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN);
+ }
+ return NS_OK;
+}
+
+#if defined(XP_WIN)
+nsIWidget*
+nsPluginInstanceOwner::GetContainingWidgetIfOffset()
+{
+ MOZ_ASSERT(mPluginFrame, "Caller should have checked for null mPluginFrame.");
+
+ // 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 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 (!XRE_IsContentProcess() &&
+ 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 = mPluginFrame->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.
+ return win;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+static already_AddRefed<nsIWidget>
+GetRootWidgetForPluginFrame(const nsPluginFrame* aPluginFrame)
+{
+ MOZ_ASSERT(aPluginFrame);
+
+ nsViewManager* vm =
+ aPluginFrame->PresContext()->GetPresShell()->GetViewManager();
+ if (!vm) {
+ NS_WARNING("Could not find view manager for plugin frame.");
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIWidget> rootWidget;
+ vm->GetRootWidget(getter_AddRefs(rootWidget));
+ return rootWidget.forget();
+}
+#endif
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value)
+{
+ if (!mPluginFrame) {
+ 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;
+ nsIWidget* offsetContainingWidget = GetContainingWidgetIfOffset();
+ if (offsetContainingWidget) {
+ *pvalue = (void*)offsetContainingWidget->GetNativeData(NS_NATIVE_WINDOW);
+ if (*pvalue) {
+ return NS_OK;
+ }
+ }
+
+ // simply return the topmost document window
+ nsCOMPtr<nsIWidget> widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (widget) {
+ *pvalue = widget->GetNativeData(NS_NATIVE_SHAREABLE_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_X11)
+ // X11 window managers want the toplevel window for WM_TRANSIENT_FOR.
+ nsIWidget* win = mPluginFrame->GetNearestWidget();
+ if (!win)
+ return NS_ERROR_FAILURE;
+ *static_cast<Window*>(value) = (long unsigned int)win->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#if defined(XP_WIN)
+void
+nsPluginInstanceOwner::SetWidgetWindowAsParent(HWND aWindowToAdopt)
+{
+ if (!mWidget) {
+ NS_ERROR("mWidget should exist before this gets called.");
+ return;
+ }
+
+ mWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW,
+ reinterpret_cast<uintptr_t>(aWindowToAdopt));
+}
+
+nsresult
+nsPluginInstanceOwner::SetNetscapeWindowAsParent(HWND aWindowToAdopt)
+{
+ if (!mPluginFrame) {
+ NS_WARNING("Plugin owner has no plugin frame.");
+ return NS_ERROR_FAILURE;
+ }
+
+ // If there is a containing window that is offset then ask that to adopt.
+ nsIWidget* offsetWidget = GetContainingWidgetIfOffset();
+ if (offsetWidget) {
+ offsetWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW,
+ reinterpret_cast<uintptr_t>(aWindowToAdopt));
+ return NS_OK;
+ }
+
+ // Otherwise ask the topmost document window to adopt.
+ nsCOMPtr<nsIWidget> rootWidget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (!rootWidget) {
+ NS_ASSERTION(rootWidget, "Couldn't get topmost document's widget.");
+ return NS_ERROR_FAILURE;
+ }
+
+ rootWidget->SetNativeData(NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW,
+ reinterpret_cast<uintptr_t>(aWindowToAdopt));
+ return NS_OK;
+}
+
+bool
+nsPluginInstanceOwner::GetCompositionString(uint32_t aType,
+ nsTArray<uint8_t>* aDist,
+ int32_t* aLength)
+{
+ // Mark pkugin calls ImmGetCompositionStringW correctly
+ mGotCompositionData = true;
+
+ RefPtr<TextComposition> composition = GetTextComposition();
+ if (NS_WARN_IF(!composition)) {
+ return false;
+ }
+
+ switch(aType) {
+ case GCS_COMPSTR: {
+ if (!composition->IsComposing()) {
+ *aLength = 0;
+ return true;
+ }
+
+ uint32_t len = composition->LastData().Length() * sizeof(char16_t);
+ if (len) {
+ aDist->SetLength(len);
+ memcpy(aDist->Elements(), composition->LastData().get(), len);
+ }
+ *aLength = len;
+ return true;
+ }
+
+ case GCS_RESULTSTR: {
+ if (composition->IsComposing()) {
+ *aLength = 0;
+ return true;
+ }
+
+ uint32_t len = composition->LastData().Length() * sizeof(char16_t);
+ if (len) {
+ aDist->SetLength(len);
+ memcpy(aDist->Elements(), composition->LastData().get(), len);
+ }
+ *aLength = len;
+ return true;
+ }
+
+ case GCS_CURSORPOS: {
+ *aLength = 0;
+ TextRangeArray* ranges = composition->GetLastRanges();
+ if (!ranges) {
+ return true;
+ }
+ *aLength = ranges->GetCaretPosition();
+ if (*aLength < 0) {
+ return false;
+ }
+ return true;
+ }
+
+ case GCS_COMPATTR: {
+ TextRangeArray* ranges = composition->GetLastRanges();
+ if (!ranges || ranges->IsEmpty()) {
+ *aLength = 0;
+ return true;
+ }
+
+ aDist->SetLength(composition->LastData().Length());
+ memset(aDist->Elements(), ATTR_INPUT, aDist->Length());
+
+ for (TextRange& range : *ranges) {
+ uint8_t type = ATTR_INPUT;
+ switch(range.mRangeType) {
+ case TextRangeType::eRawClause:
+ type = ATTR_INPUT;
+ break;
+ case TextRangeType::eSelectedRawClause:
+ type = ATTR_TARGET_NOTCONVERTED;
+ break;
+ case TextRangeType::eConvertedClause:
+ type = ATTR_CONVERTED;
+ break;
+ case TextRangeType::eSelectedClause:
+ type = ATTR_TARGET_CONVERTED;
+ break;
+ default:
+ continue;
+ }
+
+ size_t minLen = std::min<size_t>(range.mEndOffset, aDist->Length());
+ for (size_t i = range.mStartOffset; i < minLen; i++) {
+ (*aDist)[i] = type;
+ }
+ }
+ *aLength = aDist->Length();
+ return true;
+ }
+
+ case GCS_COMPCLAUSE: {
+ RefPtr<TextRangeArray> ranges = composition->GetLastRanges();
+ if (!ranges || ranges->IsEmpty()) {
+ aDist->SetLength(sizeof(uint32_t));
+ memset(aDist->Elements(), 0, sizeof(uint32_t));
+ *aLength = aDist->Length();
+ return true;
+ }
+ AutoTArray<uint32_t, 16> clauses;
+ clauses.AppendElement(0);
+ for (TextRange& range : *ranges) {
+ if (!range.IsClause()) {
+ continue;
+ }
+ clauses.AppendElement(range.mEndOffset);
+ }
+
+ aDist->SetLength(clauses.Length() * sizeof(uint32_t));
+ memcpy(aDist->Elements(), clauses.Elements(), aDist->Length());
+ *aLength = aDist->Length();
+ return true;
+ }
+
+ case GCS_RESULTREADSTR: {
+ // When returning error causes unexpected error, so we return 0 instead.
+ *aLength = 0;
+ return true;
+ }
+
+ case GCS_RESULTCLAUSE: {
+ // When returning error causes unexpected error, so we return 0 instead.
+ *aLength = 0;
+ return true;
+ }
+
+ default:
+ NS_WARNING(
+ nsPrintfCString("Unsupported type %x of ImmGetCompositionStringW hook",
+ aType).get());
+ break;
+ }
+
+ return false;
+}
+
+bool
+nsPluginInstanceOwner::SetCandidateWindow(
+ const widget::CandidateWindowPosition& aPosition)
+{
+ if (NS_WARN_IF(!mPluginFrame)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+ if (!widget) {
+ widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (NS_WARN_IF(!widget)) {
+ return false;
+ }
+ }
+
+ widget->SetCandidateWindowForPlugin(aPosition);
+ return true;
+}
+
+bool
+nsPluginInstanceOwner::RequestCommitOrCancel(bool aCommitted)
+{
+ nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+ if (!widget) {
+ widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (NS_WARN_IF(!widget)) {
+ return false;
+ }
+ }
+
+ if (aCommitted) {
+ widget->NotifyIME(widget::REQUEST_TO_COMMIT_COMPOSITION);
+ } else {
+ widget->NotifyIME(widget::REQUEST_TO_CANCEL_COMPOSITION);
+ }
+ return true;
+}
+
+#endif // #ifdef XP_WIN
+
+void
+nsPluginInstanceOwner::HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData,
+ bool aIsConsumed)
+{
+ if (NS_WARN_IF(!mInstance)) {
+ return;
+ }
+ DebugOnly<nsresult> rv =
+ mInstance->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HandledWindowedPluginKeyEvent fail");
+}
+
+void
+nsPluginInstanceOwner::OnWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData)
+{
+ if (NS_WARN_IF(!mPluginFrame)) {
+ // Notifies the plugin process of the key event being not consumed by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return;
+ }
+
+ nsCOMPtr<nsIWidget> widget = mPluginFrame->PresContext()->GetRootWidget();
+ if (NS_WARN_IF(!widget)) {
+ // Notifies the plugin process of the key event being not consumed by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return;
+ }
+
+ nsresult rv = widget->OnWindowedPluginKeyEvent(aKeyEventData, this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Notifies the plugin process of the key event being not consumed by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return;
+ }
+
+ // If the key event is posted to another process, we need to wait a call
+ // of HandledWindowedPluginKeyEvent(). So, nothing to do here in this case.
+ if (rv == NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY) {
+ return;
+ }
+
+ // Otherwise, the key event is handled synchronously. Let's notify the
+ // plugin process of the key event's result.
+ bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
+ HandledWindowedPluginKeyEvent(aKeyEventData, consumed);
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel)
+{
+#ifdef XP_MACOSX
+ mEventModel = static_cast<NPEventModel>(eventModel);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#ifdef XP_MACOSX
+NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
+ nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double *destX, double *destY,
+ NPCoordinateSpace destSpace)
+{
+ NS_ENSURE_TRUE(widget && widget->GetOwningTabChild() && pluginFrame, false);
+ // Caller has to want a result.
+ NS_ENSURE_TRUE(destX || destY, false);
+
+ if (sourceSpace == destSpace) {
+ if (destX) {
+ *destX = sourceX;
+ }
+ if (destY) {
+ *destY = sourceY;
+ }
+ return true;
+ }
+
+ nsPresContext* presContext = pluginFrame->PresContext();
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+
+ PuppetWidget *puppetWidget = static_cast<PuppetWidget*>(widget);
+ PuppetWidget *rootWidget = static_cast<PuppetWidget*>(widget->GetTopLevelWidget());
+ if (!rootWidget) {
+ return false;
+ }
+ nsPoint chromeSize = AsNsPoint(rootWidget->GetChromeDimensions()) / scaleFactor;
+ nsIntSize intScreenDims = rootWidget->GetScreenDimensions();
+ nsSize screenDims = nsSize(intScreenDims.width / scaleFactor,
+ intScreenDims.height / scaleFactor);
+ int32_t screenH = screenDims.height;
+ nsPoint windowPosition = AsNsPoint(rootWidget->GetWindowPosition()) / scaleFactor;
+
+ // Window size is tab size + chrome size.
+ LayoutDeviceIntRect tabContentBounds = puppetWidget->GetBounds();
+ tabContentBounds.ScaleInverseRoundOut(scaleFactor);
+ int32_t windowH = tabContentBounds.height + int(chromeSize.y);
+
+ nsPoint pluginPosition = AsNsPoint(pluginFrame->GetScreenRect().TopLeft());
+
+ // Convert (sourceX, sourceY) to 'real' (not PuppetWidget) screen space.
+ // In OSX, the Y-axis increases upward, which is the reverse of ours.
+ // We want OSX coordinates for window and screen so those equations are swapped.
+ nsPoint sourcePoint(sourceX, sourceY);
+ nsPoint screenPoint;
+ switch (sourceSpace) {
+ case NPCoordinateSpacePlugin:
+ screenPoint = sourcePoint + pluginPosition +
+ pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
+ break;
+ case NPCoordinateSpaceWindow:
+ screenPoint = nsPoint(sourcePoint.x, windowH-sourcePoint.y) +
+ windowPosition;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ screenPoint = sourcePoint + windowPosition;
+ break;
+ case NPCoordinateSpaceScreen:
+ screenPoint = nsPoint(sourcePoint.x, screenH-sourcePoint.y);
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ screenPoint = sourcePoint;
+ break;
+ default:
+ return false;
+ }
+
+ // Convert from screen to dest space.
+ nsPoint destPoint;
+ switch (destSpace) {
+ case NPCoordinateSpacePlugin:
+ destPoint = screenPoint - pluginPosition -
+ pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
+ break;
+ case NPCoordinateSpaceWindow:
+ destPoint = screenPoint - windowPosition;
+ destPoint.y = windowH - destPoint.y;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ destPoint = screenPoint - windowPosition;
+ break;
+ case NPCoordinateSpaceScreen:
+ destPoint = nsPoint(screenPoint.x, screenH-screenPoint.y);
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ destPoint = screenPoint;
+ break;
+ default:
+ return false;
+ }
+
+ if (destX) {
+ *destX = destPoint.x;
+ }
+ if (destY) {
+ *destY = destPoint.y;
+ }
+
+ return true;
+}
+
+NPBool nsPluginInstanceOwner::ConvertPointNoPuppet(nsIWidget *widget,
+ nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double *destX, double *destY,
+ NPCoordinateSpace destSpace)
+{
+ NS_ENSURE_TRUE(widget && pluginFrame, false);
+ // Caller has to want a result.
+ NS_ENSURE_TRUE(destX || destY, false);
+
+ if (sourceSpace == destSpace) {
+ if (destX) {
+ *destX = sourceX;
+ }
+ if (destY) {
+ *destY = sourceY;
+ }
+ return true;
+ }
+
+ nsPresContext* presContext = pluginFrame->PresContext();
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+
+ nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
+ if (!screenMgr) {
+ return false;
+ }
+ nsCOMPtr<nsIScreen> screen;
+ screenMgr->ScreenForNativeWidget(widget->GetNativeData(NS_NATIVE_WINDOW), getter_AddRefs(screen));
+ if (!screen) {
+ return false;
+ }
+
+ int32_t screenX, screenY, screenWidth, screenHeight;
+ screen->GetRect(&screenX, &screenY, &screenWidth, &screenHeight);
+ screenHeight /= scaleFactor;
+
+ LayoutDeviceIntRect windowScreenBounds = widget->GetScreenBounds();
+ windowScreenBounds.ScaleInverseRoundOut(scaleFactor);
+ int32_t windowX = windowScreenBounds.x;
+ int32_t windowY = windowScreenBounds.y;
+ int32_t windowHeight = windowScreenBounds.height;
+
+ nsIntRect pluginScreenRect = pluginFrame->GetScreenRect();
+
+ double screenXGecko, screenYGecko;
+ switch (sourceSpace) {
+ case NPCoordinateSpacePlugin:
+ screenXGecko = pluginScreenRect.x + sourceX;
+ screenYGecko = pluginScreenRect.y + sourceY;
+ break;
+ case NPCoordinateSpaceWindow:
+ screenXGecko = windowX + sourceX;
+ screenYGecko = windowY + (windowHeight - sourceY);
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ screenXGecko = windowX + sourceX;
+ screenYGecko = windowY + sourceY;
+ break;
+ case NPCoordinateSpaceScreen:
+ screenXGecko = sourceX;
+ screenYGecko = screenHeight - sourceY;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ screenXGecko = sourceX;
+ screenYGecko = sourceY;
+ break;
+ default:
+ return false;
+ }
+
+ double destXCocoa, destYCocoa;
+ switch (destSpace) {
+ case NPCoordinateSpacePlugin:
+ destXCocoa = screenXGecko - pluginScreenRect.x;
+ destYCocoa = screenYGecko - pluginScreenRect.y;
+ break;
+ case NPCoordinateSpaceWindow:
+ destXCocoa = screenXGecko - windowX;
+ destYCocoa = windowHeight - (screenYGecko - windowY);
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ destXCocoa = screenXGecko - windowX;
+ destYCocoa = screenYGecko - windowY;
+ break;
+ case NPCoordinateSpaceScreen:
+ destXCocoa = screenXGecko;
+ destYCocoa = screenHeight - screenYGecko;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ destXCocoa = screenXGecko;
+ destYCocoa = screenYGecko;
+ break;
+ default:
+ return false;
+ }
+
+ if (destX) {
+ *destX = destXCocoa;
+ }
+ if (destY) {
+ *destY = destYCocoa;
+ }
+
+ return true;
+}
+#endif // XP_MACOSX
+
+NPBool nsPluginInstanceOwner::ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace)
+{
+#ifdef XP_MACOSX
+ if (!mPluginFrame) {
+ return false;
+ }
+
+ MOZ_ASSERT(mPluginFrame->GetNearestWidget());
+
+ if (nsIWidget::UsePuppetWidgets()) {
+ return ConvertPointPuppet(static_cast<PuppetWidget*>(mPluginFrame->GetNearestWidget()),
+ mPluginFrame, sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
+ }
+
+ return ConvertPointNoPuppet(mPluginFrame->GetNearestWidget(),
+ mPluginFrame, sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
+#else
+ 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;
+
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (content->IsHTMLElement(nsGkAtoms::applet))
+ *result = nsPluginTagType_Applet;
+ else if (content->IsHTMLElement(nsGkAtoms::embed))
+ *result = nsPluginTagType_Embed;
+ else if (content->IsHTMLElement(nsGkAtoms::object))
+ *result = nsPluginTagType_Object;
+
+ return NS_OK;
+}
+
+void nsPluginInstanceOwner::GetParameters(nsTArray<MozPluginParameter>& parameters)
+{
+ nsCOMPtr<nsIObjectLoadingContent> content = do_QueryReferent(mContent);
+ nsObjectLoadingContent *loadingContent =
+ static_cast<nsObjectLoadingContent*>(content.get());
+
+ loadingContent->GetPluginParameters(parameters);
+}
+
+#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;
+}
+
+NPEventModel nsPluginInstanceOwner::GetEventModel()
+{
+ return mEventModel;
+}
+
+#define DEFAULT_REFRESH_RATE 20 // 50 FPS
+
+nsCOMPtr<nsITimer> *nsPluginInstanceOwner::sCATimer = nullptr;
+nsTArray<nsPluginInstanceOwner*> *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 &&
+ nsPluginHost::GetSpecialType(nsDependentCString(mime)) ==
+ nsPluginHost::eSpecialType_Flash) {
+ return;
+ }
+
+ if (!sCARefreshListeners) {
+ sCARefreshListeners = new nsTArray<nsPluginInstanceOwner*>();
+ }
+
+ if (sCARefreshListeners->Contains(this)) {
+ return;
+ }
+
+ sCARefreshListeners->AppendElement(this);
+
+ if (!sCATimer) {
+ sCATimer = new nsCOMPtr<nsITimer>();
+ }
+
+ 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::SetPluginPort()
+{
+ void* pluginPort = GetPluginPort();
+ if (!pluginPort || !mPluginWindow)
+ return;
+ mPluginWindow->window = pluginPort;
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+nsresult nsPluginInstanceOwner::ContentsScaleFactorChanged(double aContentsScaleFactor)
+{
+ if (!mInstance) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ return mInstance->ContentsScaleFactorChanged(aContentsScaleFactor);
+}
+#endif
+
+
+// static
+uint32_t
+nsPluginInstanceOwner::GetEventloopNestingLevel()
+{
+ nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+ uint32_t currentLevel = 0;
+ if (appShell) {
+ appShell->GetEventloopNestingLevel(&currentLevel);
+#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;
+ while (f) {
+ if (f->GetContent() && nsLayoutUtils::HasDisplayPort(f->GetContent()))
+ 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.ScaleToOtherAppUnits(currAPD, apd);
+ docOffset.x = docOffset.y = 0;
+ }
+ currAPD = newAPD;
+ docOffset += newOffset;
+ }
+ }
+
+ offset += docOffset.ScaleToOtherAppUnits(currAPD, apd);
+
+ return offset;
+}
+
+LayoutDeviceRect nsPluginInstanceOwner::GetPluginRect()
+{
+ // Get the offset of the content relative to the page
+ nsRect bounds = mPluginFrame->GetContentRectRelativeToSelf() + GetOffsetRootContent(mPluginFrame);
+ LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromAppUnitsToNearest(bounds, mPluginFrame->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*)jni::GetGeckoThreadEnv()->NewGlobalRef((jobject)mJavaView);
+ }
+
+ if (mFullScreen) {
+ java::GeckoAppShell::AddFullScreenPluginView(jni::Object::Ref::From(jobject(mJavaView)));
+ sFullScreenInstance = this;
+ }
+
+ return true;
+}
+
+void nsPluginInstanceOwner::RemovePluginView()
+{
+ if (!mInstance || !mJavaView)
+ return;
+
+ if (mFullScreen) {
+ java::GeckoAppShell::RemoveFullScreenPluginView(jni::Object::Ref::From(jobject(mJavaView)));
+ }
+ jni::GetGeckoThreadEnv()->DeleteGlobalRef((jobject)mJavaView);
+ mJavaView = nullptr;
+
+ if (mFullScreen)
+ sFullScreenInstance = nullptr;
+}
+
+void
+nsPluginInstanceOwner::GetVideos(nsTArray<nsNPAPIPluginInstance::VideoInfo*>& aVideos)
+{
+ if (!mInstance)
+ return;
+
+ mInstance->GetVideos(aVideos);
+}
+
+already_AddRefed<ImageContainer>
+nsPluginInstanceOwner::GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInfo* aVideoInfo)
+{
+ RefPtr<ImageContainer> container = LayerManager::CreateImageContainer();
+
+ if (aVideoInfo->mDimensions.width && aVideoInfo->mDimensions.height) {
+ RefPtr<Image> img = new SurfaceTextureImage(
+ aVideoInfo->mSurfaceTexture,
+ gfx::IntSize::Truncate(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height),
+ gl::OriginPos::BottomLeft);
+ 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::Recomposite() {
+ nsIWidget* const widget = mPluginFrame->GetNearestWidget();
+ NS_ENSURE_TRUE_VOID(widget);
+
+ LayerManager* const lm = widget->GetLayerManager();
+ NS_ENSURE_TRUE_VOID(lm);
+
+ ClientLayerManager* const clm = lm->AsClientLayerManager();
+ NS_ENSURE_TRUE_VOID(clm && clm->GetRoot());
+
+ clm->SendInvalidRegion(
+ clm->GetRoot()->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
+ clm->Composite();
+}
+
+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 = jni::GetGeckoThreadEnv();
+
+ if (sFullScreenInstance && sFullScreenInstance->mInstance &&
+ env->IsSameObject(view, (jobject)sFullScreenInstance->mInstance->GetJavaSurface())) {
+ sFullScreenInstance->ExitFullScreen();
+ }
+}
+
+#endif
+
+void
+nsPluginInstanceOwner::NotifyHostAsyncInitFailed()
+{
+ nsCOMPtr<nsIObjectLoadingContent> content = do_QueryReferent(mContent);
+ content->StopPluginInstance();
+}
+
+void
+nsPluginInstanceOwner::NotifyHostCreateWidget()
+{
+ mPluginHost->CreateWidget(this);
+#ifdef XP_MACOSX
+ FixUpPluginWindow(ePluginPaintEnable);
+#else
+ if (mPluginFrame) {
+ mPluginFrame->InvalidateFrame();
+ } else {
+ CallSetWindow();
+ }
+#endif
+}
+
+void
+nsPluginInstanceOwner::NotifyDestroyPending()
+{
+ if (!mInstance) {
+ return;
+ }
+ bool isOOP = false;
+ if (NS_FAILED(mInstance->GetIsOOP(&isOOP)) || !isOOP) {
+ return;
+ }
+ NPP npp = nullptr;
+ if (NS_FAILED(mInstance->GetNPP(&npp)) || !npp) {
+ return;
+ }
+ PluginAsyncSurrogate::NotifyDestroyPending(npp);
+}
+
+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->WidgetEventPtr();
+ if (theEvent) {
+ WidgetGUIEvent focusEvent(theEvent->IsTrusted(), theEvent->mMessage,
+ 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->WidgetEventPtr()->AsKeyboardEvent();
+ if (keyEvent && keyEvent->mClass == eKeyboardEventClass) {
+ 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 (mPluginFrame && mPluginWindow &&
+ mPluginWindow->type == NPWindowTypeDrawable) {
+
+ nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ nsCOMPtr<nsIDOMElement> elem = do_QueryReferent(mContent);
+ fm->SetFocus(elem, 0);
+ }
+ }
+
+ WidgetMouseEvent* mouseEvent =
+ aMouseEvent->WidgetEventPtr()->AsMouseEvent();
+ if (mouseEvent && mouseEvent->mClass == eMouseEventClass) {
+ 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->WidgetEventPtr()->AsMouseEvent();
+ if (mouseEvent && mouseEvent->mClass == eMouseEventClass) {
+ nsEventStatus rv = ProcessEvent(*mouseEvent);
+ if (nsEventStatus_eConsumeNoDefault == rv) {
+ aMouseEvent->PreventDefault();
+ if (!aAllowPropagate) {
+ aMouseEvent->StopPropagation();
+ }
+ }
+ if (mouseEvent->mMessage == eMouseUp) {
+ mLastMouseDownButtonType = -1;
+ }
+ }
+ return NS_OK;
+}
+
+#ifdef XP_WIN
+void
+nsPluginInstanceOwner::CallDefaultProc(const WidgetGUIEvent* aEvent)
+{
+ nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+ if (!widget) {
+ widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (NS_WARN_IF(!widget)) {
+ return;
+ }
+ }
+
+ const NPEvent* npEvent =
+ static_cast<const NPEvent*>(aEvent->mPluginEvent);
+ if (NS_WARN_IF(!npEvent)) {
+ return;
+ }
+
+ WidgetPluginEvent pluginEvent(true, ePluginInputEvent, widget);
+ pluginEvent.mPluginEvent.Copy(*npEvent);
+ widget->DefaultProcOfPluginEvent(pluginEvent);
+}
+
+already_AddRefed<TextComposition>
+nsPluginInstanceOwner::GetTextComposition()
+{
+ if (NS_WARN_IF(!mPluginFrame)) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+ if (!widget) {
+ widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (NS_WARN_IF(!widget)) {
+ return nullptr;
+ }
+ }
+
+ RefPtr<TextComposition> composition =
+ IMEStateManager::GetTextCompositionFor(widget);
+ if (NS_WARN_IF(!composition)) {
+ return nullptr;
+ }
+
+ return composition.forget();
+}
+
+void
+nsPluginInstanceOwner::HandleNoConsumedCompositionMessage(
+ WidgetCompositionEvent* aCompositionEvent,
+ const NPEvent* aPluginEvent)
+{
+ nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+ if (!widget) {
+ widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (NS_WARN_IF(!widget)) {
+ return;
+ }
+ }
+
+ NPEvent npevent;
+ if (aPluginEvent->lParam & GCS_RESULTSTR) {
+ // GCS_RESULTSTR's default proc will generate WM_CHAR. So emulate it.
+ for (size_t i = 0; i < aCompositionEvent->mData.Length(); i++) {
+ WidgetPluginEvent charEvent(true, ePluginInputEvent, widget);
+ npevent.event = WM_CHAR;
+ npevent.wParam = aCompositionEvent->mData[i];
+ npevent.lParam = 0;
+ charEvent.mPluginEvent.Copy(npevent);
+ ProcessEvent(charEvent);
+ }
+ return;
+ }
+ if (!mSentStartComposition) {
+ // We post WM_IME_COMPOSITION to default proc, but
+ // WM_IME_STARTCOMPOSITION isn't post yet. We should post it at first.
+ WidgetPluginEvent startEvent(true, ePluginInputEvent, widget);
+ npevent.event = WM_IME_STARTCOMPOSITION;
+ npevent.wParam = 0;
+ npevent.lParam = 0;
+ startEvent.mPluginEvent.Copy(npevent);
+ CallDefaultProc(&startEvent);
+ mSentStartComposition = true;
+ }
+
+ CallDefaultProc(aCompositionEvent);
+}
+#endif
+
+nsresult
+nsPluginInstanceOwner::DispatchCompositionToPlugin(nsIDOMEvent* aEvent)
+{
+#ifdef XP_WIN
+ if (!mPluginWindow) {
+ // CompositionEvent isn't cancellable. So it is unnecessary to call
+ // PreventDefaults() to consume event
+ return NS_OK;
+ }
+ WidgetCompositionEvent* compositionEvent =
+ aEvent->WidgetEventPtr()->AsCompositionEvent();
+ if (NS_WARN_IF(!compositionEvent)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (compositionEvent->mMessage == eCompositionChange) {
+ RefPtr<TextComposition> composition = GetTextComposition();
+ if (NS_WARN_IF(!composition)) {
+ return NS_ERROR_FAILURE;
+ }
+ TextComposition::CompositionChangeEventHandlingMarker
+ compositionChangeEventHandlingMarker(composition, compositionEvent);
+ }
+
+ const NPEvent* pPluginEvent =
+ static_cast<const NPEvent*>(compositionEvent->mPluginEvent);
+ if (pPluginEvent && pPluginEvent->event == WM_IME_COMPOSITION &&
+ mPluginDidNotHandleIMEComposition) {
+ // This is a workaround when running windowed and windowless Flash on
+ // same process.
+ // Flash with protected mode calls IMM APIs on own render process. This
+ // is a bug of Flash's protected mode.
+ // ImmGetCompositionString with GCS_RESULTSTR returns *LAST* committed
+ // string. So when windowed mode Flash handles IME composition,
+ // windowless plugin can get windowed mode's commited string by that API.
+ // So we never post WM_IME_COMPOSITION when plugin doesn't call
+ // ImmGetCompositionString() during WM_IME_COMPOSITION correctly.
+ HandleNoConsumedCompositionMessage(compositionEvent, pPluginEvent);
+ aEvent->StopImmediatePropagation();
+ return NS_OK;
+ }
+
+ // Protected mode Flash returns noDefault by NPP_HandleEvent, but
+ // composition information into plugin is invalid because plugin's bug.
+ // So if plugin doesn't get composition data by WM_IME_COMPOSITION, we
+ // recongnize it isn't handled
+ AutoRestore<bool> restore(mGotCompositionData);
+ mGotCompositionData = false;
+
+ nsEventStatus status = ProcessEvent(*compositionEvent);
+ aEvent->StopImmediatePropagation();
+
+ // Composition event isn't handled by plugin, so we have to call default proc.
+
+ if (NS_WARN_IF(!pPluginEvent)) {
+ return NS_OK;
+ }
+
+ if (pPluginEvent->event == WM_IME_STARTCOMPOSITION) {
+ // Flash's protected mode lies that composition event is handled, but it
+ // cannot do it well. So even if handled, we should post this message when
+ // no IMM API calls during WM_IME_COMPOSITION.
+ if (nsEventStatus_eConsumeNoDefault != status) {
+ CallDefaultProc(compositionEvent);
+ mSentStartComposition = true;
+ } else {
+ mSentStartComposition = false;
+ }
+ mPluginDidNotHandleIMEComposition = false;
+ return NS_OK;
+ }
+
+ if (pPluginEvent->event == WM_IME_ENDCOMPOSITION) {
+ // Always post WM_END_COMPOSITION to default proc. Because Flash may lie
+ // that it doesn't handle composition well, but event is handled.
+ // Even if posting this message, default proc do nothing if unnecessary.
+ CallDefaultProc(compositionEvent);
+ return NS_OK;
+ }
+
+ if (pPluginEvent->event == WM_IME_COMPOSITION && !mGotCompositionData) {
+ // If plugin doesn't handle WM_IME_COMPOSITION correctly, we don't send
+ // composition event until end composition.
+ mPluginDidNotHandleIMEComposition = true;
+
+ HandleNoConsumedCompositionMessage(compositionEvent, pPluginEvent);
+ }
+#endif // #ifdef XP_WIN
+ 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);
+
+#ifdef XP_MACOSX
+ if (eventType.EqualsLiteral("activate") ||
+ eventType.EqualsLiteral("deactivate")) {
+ WindowFocusMayHaveChanged();
+ return NS_OK;
+ }
+ if (eventType.EqualsLiteral("MozPerformDelayedBlur")) {
+ if (mShouldBlurOnActivate) {
+ WidgetGUIEvent blurEvent(true, eBlur, nullptr);
+ ProcessEvent(blurEvent);
+ mShouldBlurOnActivate = false;
+ }
+ return NS_OK;
+ }
+#endif
+
+ 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")) {
+ 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);
+ }
+ if (eventType.EqualsLiteral("compositionstart") ||
+ eventType.EqualsLiteral("compositionend") ||
+ eventType.EqualsLiteral("text")) {
+ return DispatchCompositionToPlugin(aEvent);
+ }
+
+ nsCOMPtr<nsIDOMDragEvent> dragEvent(do_QueryInterface(aEvent));
+ if (dragEvent && mInstance) {
+ WidgetEvent* ievent = aEvent->WidgetEventPtr();
+ if (ievent && ievent->IsTrusted() &&
+ ievent->mMessage != eDragEnter && ievent->mMessage != eDragOver) {
+ 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
+
+#ifdef XP_MACOSX
+
+// Returns whether or not content is the content that is or would be
+// focused if the top-level chrome window was active.
+static bool
+ContentIsFocusedWithinWindow(nsIContent* aContent)
+{
+ nsPIDOMWindowOuter* outerWindow = aContent->OwnerDoc()->GetWindow();
+ if (!outerWindow) {
+ return false;
+ }
+
+ nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot();
+ if (!rootWindow) {
+ return false;
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return false;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> focusedFrame;
+ nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedDescendant(rootWindow, true, getter_AddRefs(focusedFrame));
+ return (focusedContent.get() == aContent);
+}
+
+static NPCocoaEventType
+CocoaEventTypeForEvent(const WidgetGUIEvent& anEvent, nsIFrame* aObjectFrame)
+{
+ const NPCocoaEvent* event = static_cast<const NPCocoaEvent*>(anEvent.mPluginEvent);
+ if (event) {
+ return event->type;
+ }
+
+ switch (anEvent.mMessage) {
+ case eMouseOver:
+ return NPCocoaEventMouseEntered;
+ case eMouseOut:
+ return NPCocoaEventMouseExited;
+ case eMouseMove: {
+ // We don't know via information on events from the widget code whether or not
+ // we're dragging. The widget code just generates mouse move events from native
+ // drag events. If anybody is capturing, this is a drag event.
+ if (nsIPresShell::GetCapturingContent()) {
+ return NPCocoaEventMouseDragged;
+ }
+
+ return NPCocoaEventMouseMoved;
+ }
+ case eMouseDown:
+ return NPCocoaEventMouseDown;
+ case eMouseUp:
+ return NPCocoaEventMouseUp;
+ case eKeyDown:
+ return NPCocoaEventKeyDown;
+ case eKeyUp:
+ return NPCocoaEventKeyUp;
+ case eFocus:
+ case eBlur:
+ return NPCocoaEventFocusChanged;
+ case eLegacyMouseLineOrPageScroll:
+ return NPCocoaEventScrollWheel;
+ default:
+ return (NPCocoaEventType)0;
+ }
+}
+
+static NPCocoaEvent
+TranslateToNPCocoaEvent(WidgetGUIEvent* anEvent, nsIFrame* aObjectFrame)
+{
+ NPCocoaEvent cocoaEvent;
+ InitializeNPCocoaEvent(&cocoaEvent);
+ cocoaEvent.type = CocoaEventTypeForEvent(*anEvent, aObjectFrame);
+
+ if (anEvent->mMessage == eMouseMove ||
+ anEvent->mMessage == eMouseDown ||
+ anEvent->mMessage == eMouseUp ||
+ anEvent->mMessage == eLegacyMouseLineOrPageScroll ||
+ anEvent->mMessage == eMouseOver ||
+ anEvent->mMessage == eMouseOut)
+ {
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(anEvent, aObjectFrame) -
+ aObjectFrame->GetContentRectRelativeToSelf().TopLeft();
+ nsPresContext* presContext = aObjectFrame->PresContext();
+ // Plugin event coordinates need to be translated from device pixels
+ // into "display pixels" in HiDPI modes.
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ aObjectFrame->PresContext()->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+ size_t intScaleFactor = ceil(scaleFactor);
+ nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x) / intScaleFactor,
+ presContext->AppUnitsToDevPixels(pt.y) / intScaleFactor);
+ cocoaEvent.data.mouse.pluginX = double(ptPx.x);
+ cocoaEvent.data.mouse.pluginY = double(ptPx.y);
+ }
+
+ switch (anEvent->mMessage) {
+ case eMouseDown:
+ case eMouseUp: {
+ WidgetMouseEvent* mouseEvent = anEvent->AsMouseEvent();
+ if (mouseEvent) {
+ switch (mouseEvent->button) {
+ case WidgetMouseEvent::eLeftButton:
+ cocoaEvent.data.mouse.buttonNumber = 0;
+ break;
+ case WidgetMouseEvent::eRightButton:
+ cocoaEvent.data.mouse.buttonNumber = 1;
+ break;
+ case WidgetMouseEvent::eMiddleButton:
+ cocoaEvent.data.mouse.buttonNumber = 2;
+ break;
+ default:
+ NS_WARNING("Mouse button we don't know about?");
+ }
+ cocoaEvent.data.mouse.clickCount = mouseEvent->mClickCount;
+ } else {
+ NS_WARNING("eMouseUp/DOWN is not a WidgetMouseEvent?");
+ }
+ break;
+ }
+ case eLegacyMouseLineOrPageScroll: {
+ WidgetWheelEvent* wheelEvent = anEvent->AsWheelEvent();
+ if (wheelEvent) {
+ cocoaEvent.data.mouse.deltaX = wheelEvent->mLineOrPageDeltaX;
+ cocoaEvent.data.mouse.deltaY = wheelEvent->mLineOrPageDeltaY;
+ } else {
+ NS_WARNING("eLegacyMouseLineOrPageScroll is not a WidgetWheelEvent? "
+ "(could be, haven't checked)");
+ }
+ break;
+ }
+ case eKeyDown:
+ case eKeyUp:
+ {
+ WidgetKeyboardEvent* keyEvent = anEvent->AsKeyboardEvent();
+
+ // That keyEvent->mPluginTextEventString is non-empty is a signal that we should
+ // create a text event for the plugin, instead of a key event.
+ if (anEvent->mMessage == eKeyDown &&
+ !keyEvent->mPluginTextEventString.IsEmpty()) {
+ cocoaEvent.type = NPCocoaEventTextInput;
+ const char16_t* pluginTextEventString = keyEvent->mPluginTextEventString.get();
+ cocoaEvent.data.text.text = (NPNSString*)
+ ::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(pluginTextEventString),
+ keyEvent->mPluginTextEventString.Length());
+ } else {
+ cocoaEvent.data.key.keyCode = keyEvent->mNativeKeyCode;
+ cocoaEvent.data.key.isARepeat = keyEvent->mIsRepeat;
+ cocoaEvent.data.key.modifierFlags = keyEvent->mNativeModifierFlags;
+ const char16_t* nativeChars = keyEvent->mNativeCharacters.get();
+ cocoaEvent.data.key.characters = (NPNSString*)
+ ::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(nativeChars),
+ keyEvent->mNativeCharacters.Length());
+ const char16_t* nativeCharsIgnoringModifiers = keyEvent->mNativeCharactersIgnoringModifiers.get();
+ cocoaEvent.data.key.charactersIgnoringModifiers = (NPNSString*)
+ ::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(nativeCharsIgnoringModifiers),
+ keyEvent->mNativeCharactersIgnoringModifiers.Length());
+ }
+ break;
+ }
+ case eFocus:
+ case eBlur:
+ cocoaEvent.data.focus.hasFocus = (anEvent->mMessage == eFocus);
+ break;
+ default:
+ break;
+ }
+ return cocoaEvent;
+}
+
+void nsPluginInstanceOwner::PerformDelayedBlurs()
+{
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ nsCOMPtr<EventTarget> windowRoot = content->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(),
+ windowRoot,
+ NS_LITERAL_STRING("MozPerformDelayedBlur"),
+ false, false, nullptr);
+}
+
+#endif
+
+nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
+{
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ if (!mInstance || !mPluginFrame) {
+ return nsEventStatus_eIgnore;
+ }
+
+#ifdef XP_MACOSX
+ NPEventModel eventModel = GetEventModel();
+ if (eventModel != NPEventModelCocoa) {
+ return nsEventStatus_eIgnore;
+ }
+
+ // In the Cocoa event model, focus is per-window. Don't tell a plugin it lost
+ // focus unless it lost focus within the window. For example, ignore a blur
+ // event if it's coming due to the plugin's window deactivating.
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (anEvent.mMessage == eBlur && ContentIsFocusedWithinWindow(content)) {
+ mShouldBlurOnActivate = true;
+ return nsEventStatus_eIgnore;
+ }
+
+ // Also, don't tell the plugin it gained focus again after we've already given
+ // it focus. This might happen if it has focus, its window is blurred, then the
+ // window is made active again. The plugin never lost in-window focus, so it
+ // shouldn't get a focus event again.
+ if (anEvent.mMessage == eFocus && mLastContentFocused == true) {
+ mShouldBlurOnActivate = false;
+ return nsEventStatus_eIgnore;
+ }
+
+ // Now, if we're going to send a focus event, update mLastContentFocused and
+ // tell any plugins in our window that we have taken focus, so they should
+ // perform any delayed blurs.
+ if (anEvent.mMessage == eFocus || anEvent.mMessage == eBlur) {
+ mLastContentFocused = (anEvent.mMessage == eFocus);
+ mShouldBlurOnActivate = false;
+ PerformDelayedBlurs();
+ }
+
+ NPCocoaEvent cocoaEvent = TranslateToNPCocoaEvent(const_cast<WidgetGUIEvent*>(&anEvent), mPluginFrame);
+ if (cocoaEvent.type == (NPCocoaEventType)0) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (cocoaEvent.type == NPCocoaEventTextInput) {
+ mInstance->HandleEvent(&cocoaEvent, nullptr);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ int16_t response = kNPEventNotHandled;
+ mInstance->HandleEvent(&cocoaEvent,
+ &response,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+ if ((response == kNPEventStartIME) && (cocoaEvent.type == NPCocoaEventKeyDown)) {
+ nsIWidget* widget = mPluginFrame->GetNearestWidget();
+ if (widget) {
+ const WidgetKeyboardEvent* keyEvent = anEvent.AsKeyboardEvent();
+ double screenX, screenY;
+ ConvertPoint(0.0, mPluginFrame->GetScreenRect().height,
+ NPCoordinateSpacePlugin, &screenX, &screenY,
+ NPCoordinateSpaceScreen);
+ nsAutoString outText;
+ if (NS_SUCCEEDED(widget->StartPluginIME(*keyEvent, screenX, screenY, outText)) &&
+ !outText.IsEmpty()) {
+ CFStringRef cfString =
+ ::CFStringCreateWithCharacters(kCFAllocatorDefault,
+ reinterpret_cast<const UniChar*>(outText.get()),
+ outText.Length());
+ NPCocoaEvent textEvent;
+ InitializeNPCocoaEvent(&textEvent);
+ textEvent.type = NPCocoaEventTextInput;
+ textEvent.data.text.text = (NPNSString*)cfString;
+ mInstance->HandleEvent(&textEvent, nullptr);
+ }
+ }
+ }
+
+ bool handled = (response == kNPEventHandled || response == kNPEventStartIME);
+ bool leftMouseButtonDown = (anEvent.mMessage == eMouseDown) &&
+ (anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton);
+ if (handled && !(leftMouseButtonDown && !mContentFocused)) {
+ rv = nsEventStatus_eConsumeNoDefault;
+ }
+#endif
+
+#ifdef XP_WIN
+ // this code supports windowless plugins
+ const NPEvent *pPluginEvent = static_cast<const NPEvent*>(anEvent.mPluginEvent);
+ // we can get synthetic events from the EventStateManager... these
+ // have no pluginEvent
+ NPEvent pluginEvent;
+ if (anEvent.mClass == eMouseEventClass ||
+ anEvent.mClass == eWheelEventClass) {
+ if (!pPluginEvent) {
+ // XXX Should extend this list to synthesize events for more event
+ // types
+ pluginEvent.event = 0;
+ bool initWParamWithCurrentState = true;
+ switch (anEvent.mMessage) {
+ case eMouseMove: {
+ pluginEvent.event = WM_MOUSEMOVE;
+ break;
+ }
+ case eMouseDown: {
+ static const int downMsgs[] =
+ { WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN };
+ static const int dblClickMsgs[] =
+ { WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK, WM_RBUTTONDBLCLK };
+ const WidgetMouseEvent* mouseEvent = anEvent.AsMouseEvent();
+ if (mouseEvent->mClickCount == 2) {
+ pluginEvent.event = dblClickMsgs[mouseEvent->button];
+ } else {
+ pluginEvent.event = downMsgs[mouseEvent->button];
+ }
+ break;
+ }
+ case eMouseUp: {
+ static const int upMsgs[] =
+ { WM_LBUTTONUP, WM_MBUTTONUP, WM_RBUTTONUP };
+ const WidgetMouseEvent* mouseEvent = anEvent.AsMouseEvent();
+ pluginEvent.event = upMsgs[mouseEvent->button];
+ break;
+ }
+ // For plugins which don't support high-resolution scroll, we should
+ // generate legacy resolution wheel messages. I.e., the delta value
+ // should be WHEEL_DELTA * n.
+ case eWheel: {
+ const WidgetWheelEvent* wheelEvent = anEvent.AsWheelEvent();
+ int32_t delta = 0;
+ if (wheelEvent->mLineOrPageDeltaY) {
+ switch (wheelEvent->mDeltaMode) {
+ case nsIDOMWheelEvent::DOM_DELTA_PAGE:
+ pluginEvent.event = WM_MOUSEWHEEL;
+ delta = -WHEEL_DELTA * wheelEvent->mLineOrPageDeltaY;
+ break;
+ case nsIDOMWheelEvent::DOM_DELTA_LINE: {
+ UINT linesPerWheelDelta = 0;
+ if (NS_WARN_IF(!::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
+ &linesPerWheelDelta, 0))) {
+ // Use system default scroll amount, 3, when
+ // SPI_GETWHEELSCROLLLINES isn't available.
+ linesPerWheelDelta = 3;
+ }
+ if (!linesPerWheelDelta) {
+ break;
+ }
+ pluginEvent.event = WM_MOUSEWHEEL;
+ delta = -WHEEL_DELTA / linesPerWheelDelta;
+ delta *= wheelEvent->mLineOrPageDeltaY;
+ break;
+ }
+ case nsIDOMWheelEvent::DOM_DELTA_PIXEL:
+ default:
+ // We don't support WM_GESTURE with this path.
+ MOZ_ASSERT(!pluginEvent.event);
+ break;
+ }
+ } else if (wheelEvent->mLineOrPageDeltaX) {
+ switch (wheelEvent->mDeltaMode) {
+ case nsIDOMWheelEvent::DOM_DELTA_PAGE:
+ pluginEvent.event = WM_MOUSEHWHEEL;
+ delta = -WHEEL_DELTA * wheelEvent->mLineOrPageDeltaX;
+ break;
+ case nsIDOMWheelEvent::DOM_DELTA_LINE: {
+ pluginEvent.event = WM_MOUSEHWHEEL;
+ UINT charsPerWheelDelta = 0;
+ // FYI: SPI_GETWHEELSCROLLCHARS is available on Vista or later.
+ if (::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0,
+ &charsPerWheelDelta, 0)) {
+ // Use system default scroll amount, 3, when
+ // SPI_GETWHEELSCROLLCHARS isn't available.
+ charsPerWheelDelta = 3;
+ }
+ if (!charsPerWheelDelta) {
+ break;
+ }
+ delta = WHEEL_DELTA / charsPerWheelDelta;
+ delta *= wheelEvent->mLineOrPageDeltaX;
+ break;
+ }
+ case nsIDOMWheelEvent::DOM_DELTA_PIXEL:
+ default:
+ // We don't support WM_GESTURE with this path.
+ MOZ_ASSERT(!pluginEvent.event);
+ break;
+ }
+ }
+
+ if (!pluginEvent.event) {
+ break;
+ }
+
+ initWParamWithCurrentState = false;
+ int32_t modifiers =
+ (wheelEvent->IsControl() ? MK_CONTROL : 0) |
+ (wheelEvent->IsShift() ? MK_SHIFT : 0) |
+ (wheelEvent->IsLeftButtonPressed() ? MK_LBUTTON : 0) |
+ (wheelEvent->IsMiddleButtonPressed() ? MK_MBUTTON : 0) |
+ (wheelEvent->IsRightButtonPressed() ? MK_RBUTTON : 0) |
+ (wheelEvent->Is4thButtonPressed() ? MK_XBUTTON1 : 0) |
+ (wheelEvent->Is5thButtonPressed() ? MK_XBUTTON2 : 0);
+ pluginEvent.wParam = MAKEWPARAM(modifiers, delta);
+ pPluginEvent = &pluginEvent;
+ break;
+ }
+ // don't synthesize anything for eMouseDoubleClick, 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 && initWParamWithCurrentState) {
+ 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.mMessage == eMouseDown ||
+ anEvent.mMessage == eMouseUp ||
+ anEvent.mMessage == eMouseDoubleClick ||
+ anEvent.mMessage == eMouseOver ||
+ anEvent.mMessage == eMouseOut ||
+ anEvent.mMessage == eMouseMove ||
+ anEvent.mMessage == eWheel,
+ "Incorrect event type for coordinate translation");
+ nsPoint pt =
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
+ mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
+ nsPresContext* presContext = mPluginFrame->PresContext();
+ nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x),
+ presContext->AppUnitsToDevPixels(pt.y));
+ nsIntPoint widgetPtPx = ptPx + mPluginFrame->GetWindowOriginInPixels(true);
+ const_cast<NPEvent*>(pPluginEvent)->lParam = MAKELPARAM(widgetPtPx.x, widgetPtPx.y);
+ }
+ }
+ else if (!pPluginEvent) {
+ switch (anEvent.mMessage) {
+ case eFocus:
+ pluginEvent.event = WM_SETFOCUS;
+ pluginEvent.wParam = 0;
+ pluginEvent.lParam = 0;
+ pPluginEvent = &pluginEvent;
+ break;
+ case eBlur:
+ pluginEvent.event = WM_KILLFOCUS;
+ pluginEvent.wParam = 0;
+ pluginEvent.lParam = 0;
+ pPluginEvent = &pluginEvent;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (pPluginEvent && !pPluginEvent->event) {
+ // Don't send null events to plugins.
+ NS_WARNING("nsPluginFrame ProcessEvent: trying to send null event to plugin.");
+ return rv;
+ }
+
+ if (pPluginEvent) {
+ int16_t response = kNPEventNotHandled;
+ mInstance->HandleEvent(const_cast<NPEvent*>(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.mWidget;
+ XEvent pluginEvent = XEvent();
+ pluginEvent.type = 0;
+
+ switch(anEvent.mClass) {
+ case eMouseEventClass:
+ {
+ switch (anEvent.mMessage) {
+ case eMouseClick:
+ case eMouseDoubleClick:
+ // Button up/down events sent instead.
+ return rv;
+ default:
+ break;
+ }
+
+ // Get reference point relative to plugin origin.
+ const nsPresContext* presContext = mPluginFrame->PresContext();
+ nsPoint appPoint =
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
+ mPluginFrame->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.mRefPoint + widget->WidgetToScreenOffset();
+ }
+#ifdef MOZ_WIDGET_GTK
+ Window root = GDK_ROOT_WINDOW();
+#else
+ Window root = X11None; // Could XQueryTree, but this is not important.
+#endif
+
+ switch (anEvent.mMessage) {
+ case eMouseOver:
+ case eMouseOut:
+ {
+ XCrossingEvent& event = pluginEvent.xcrossing;
+ event.type = anEvent.mMessage == eMouseOver ?
+ EnterNotify : LeaveNotify;
+ event.root = root;
+ event.time = anEvent.mTime;
+ 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 = X11None;
+ event.mode = -1;
+ event.detail = NotifyDetailNone;
+ event.same_screen = True;
+ event.focus = mContentFocused;
+ }
+ break;
+ case eMouseMove:
+ {
+ XMotionEvent& event = pluginEvent.xmotion;
+ event.type = MotionNotify;
+ event.root = root;
+ event.time = anEvent.mTime;
+ 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 = X11None;
+ event.is_hint = NotifyNormal;
+ event.same_screen = True;
+ }
+ break;
+ case eMouseDown:
+ case eMouseUp:
+ {
+ XButtonEvent& event = pluginEvent.xbutton;
+ event.type = anEvent.mMessage == eMouseDown ?
+ ButtonPress : ButtonRelease;
+ event.root = root;
+ event.time = anEvent.mTime;
+ 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 = X11None;
+ event.same_screen = True;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ //XXX case eMouseScrollEventClass: not received.
+
+ case eKeyboardEventClass:
+ if (anEvent.mPluginEvent)
+ {
+ XKeyEvent &event = pluginEvent.xkey;
+#ifdef MOZ_WIDGET_GTK
+ event.root = GDK_ROOT_WINDOW();
+ event.time = anEvent.mTime;
+ const GdkEventKey* gdkEvent =
+ static_cast<const GdkEventKey*>(anEvent.mPluginEvent);
+ event.keycode = gdkEvent->hardware_keycode;
+ event.state = gdkEvent->state;
+ switch (anEvent.mMessage)
+ {
+ case eKeyDown:
+ // Handle eKeyDown for modifier key presses
+ // For non-modifiers we get eKeyPress
+ if (gdkEvent->is_modifier)
+ event.type = XKeyPress;
+ break;
+ case eKeyPress:
+ event.type = XKeyPress;
+ break;
+ case eKeyUp:
+ event.type = KeyRelease;
+ break;
+ default:
+ break;
+ }
+#endif
+
+ // Information that could be obtained from pluginEvent but we may not
+ // want to promise to provide:
+ event.subwindow = X11None;
+ 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.mMessage) {
+ case eFocus:
+ case eBlur:
+ {
+ XFocusChangeEvent &event = pluginEvent.xfocus;
+ event.type = anEvent.mMessage == eFocus ? FocusIn : FocusOut;
+ // information lost:
+ event.mode = -1;
+ event.detail = NotifyDetailNone;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!pluginEvent.type) {
+ return rv;
+ }
+
+ // Fill in (useless) generic event information.
+ XAnyEvent& event = pluginEvent.xany;
+ event.display = widget ?
+ static_cast<Display*>(widget->GetNativeData(NS_NATIVE_DISPLAY)) : nullptr;
+ event.window = X11None; // 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<nsIDOMElement> elem = do_QueryReferent(mContent);
+ fm->SetFocus(elem, 0);
+ }
+ }
+ switch(anEvent.mClass) {
+ case eMouseEventClass:
+ {
+ switch (anEvent.mMessage) {
+ case eMouseClick:
+ case eMouseDoubleClick:
+ // Button up/down events sent instead.
+ return rv;
+ default:
+ break;
+ }
+
+ // Get reference point relative to plugin origin.
+ const nsPresContext* presContext = mPluginFrame->PresContext();
+ nsPoint appPoint =
+ nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
+ mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
+ nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x),
+ presContext->AppUnitsToDevPixels(appPoint.y));
+
+ switch (anEvent.mMessage) {
+ case eMouseMove:
+ {
+ // are these going to be touch events?
+ // pluginPoint.x;
+ // pluginPoint.y;
+ }
+ break;
+ case eMouseDown:
+ {
+ 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 eMouseUp:
+ {
+ 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;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case eKeyboardEventClass:
+ {
+ const WidgetKeyboardEvent& keyEvent = *anEvent.AsKeyboardEvent();
+ LOG("Firing eKeyboardEventClass %d %d\n",
+ keyEvent.mKeyCode, keyEvent.mCharCode);
+ // pluginEvent is initialized by nsWindow::InitKeyEvent().
+ const ANPEvent* pluginEvent = static_cast<const ANPEvent*>(keyEvent.mPluginEvent);
+ if (pluginEvent) {
+ MOZ_ASSERT(pluginEvent->inSize == sizeof(ANPEvent));
+ MOZ_ASSERT(pluginEvent->eventType == kKey_ANPEventType);
+ mInstance->HandleEvent(const_cast<ANPEvent*>(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();
+#endif
+
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+
+ // unregister context menu listener
+ if (mCXMenuListener) {
+ mCXMenuListener->Destroy(content);
+ mCXMenuListener = nullptr;
+ }
+
+ content->RemoveEventListener(NS_LITERAL_STRING("focus"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("blur"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("click"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("dblclick"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("mouseover"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false);
+ content->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("drop"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("drag"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("dragenter"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("dragover"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("dragleave"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("dragexit"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("dragstart"), this, true);
+ content->RemoveEventListener(NS_LITERAL_STRING("dragend"), this, true);
+ content->RemoveSystemEventListener(NS_LITERAL_STRING("compositionstart"),
+ this, true);
+ content->RemoveSystemEventListener(NS_LITERAL_STRING("compositionend"),
+ this, true);
+ content->RemoveSystemEventListener(NS_LITERAL_STRING("text"), this, true);
+
+#if MOZ_WIDGET_ANDROID
+ RemovePluginView();
+#endif
+
+ if (mWidget) {
+ if (mPluginWindow) {
+ mPluginWindow->SetPluginWidget(nullptr);
+ }
+
+ nsCOMPtr<nsIPluginWidget> 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 || !mPluginFrame)
+ 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);
+ }
+
+ DoCocoaEventDrawRect(dirtyRectCopy, cgContext);
+}
+
+void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext)
+{
+ if (!mInstance || !mPluginFrame)
+ 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 || !mPluginFrame)
+ 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 || !mPluginFrame || !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 RefPtr<gfxImageSurface> pluginSurface;
+
+ if (pluginSurface == nullptr ||
+ aFrameRect.width != pluginSurface->Width() ||
+ aFrameRect.height != pluginSurface->Height()) {
+
+ pluginSurface = new gfxImageSurface(gfx::IntSize(aFrameRect.width, aFrameRect.height),
+ SurfaceFormat::A8R8G8B8_UINT32);
+ if (!pluginSurface)
+ return;
+ }
+
+ // Clears buffer. I think this is needed.
+ gfxUtils::ClearThebesSurface(pluginSurface);
+
+ 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->SetOp(gfx::CompositionOp::OP_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 || !mPluginFrame)
+ 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->SetMatrix(
+ aContext->CurrentMatrix().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<NPSetWindowCallbackStruct*>(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 = do_GetWeakReference(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);
+ nsPluginFrame* objFrame = static_cast<nsPluginFrame*>(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);
+
+ aContent->AddEventListener(NS_LITERAL_STRING("focus"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("blur"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("mouseup"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("mousedown"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("mousemove"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("click"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("dblclick"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("mouseover"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("mouseout"), this, false,
+ false);
+ aContent->AddEventListener(NS_LITERAL_STRING("keypress"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("keydown"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("keyup"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("drop"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("drag"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("dragenter"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("dragover"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("dragleave"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("dragexit"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, true);
+ aContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, true);
+ aContent->AddSystemEventListener(NS_LITERAL_STRING("compositionstart"),
+ this, true);
+ aContent->AddSystemEventListener(NS_LITERAL_STRING("compositionend"), this,
+ true);
+ aContent->AddSystemEventListener(NS_LITERAL_STRING("text"), this, true);
+
+ return NS_OK;
+}
+
+void* nsPluginInstanceOwner::GetPluginPort()
+{
+ void* result = nullptr;
+ if (mWidget) {
+#ifdef XP_WIN
+ if (!mPluginWindow || mPluginWindow->type == NPWindowTypeWindow)
+#endif
+ result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT); // HWND/gdk window
+ }
+
+ return result;
+}
+
+void nsPluginInstanceOwner::ReleasePluginPort(void * pluginPort)
+{
+}
+
+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) {
+ // Try to get a parent widget, on some platforms widget creation will fail without
+ // a parent.
+ nsCOMPtr<nsIWidget> parentWidget;
+ nsIDocument *doc = nullptr;
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (content) {
+ doc = content->OwnerDoc();
+ parentWidget = nsContentUtils::WidgetForDocument(doc);
+#ifndef XP_MACOSX
+ // If we're running in the content process, we need a remote widget created in chrome.
+ if (XRE_IsContentProcess()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetTop()) {
+ dom::TabChild* tc = dom::TabChild::GetFrom(topWindow);
+ if (tc) {
+ // This returns a PluginWidgetProxy which remotes a number of calls.
+ rv = tc->CreatePluginWidget(parentWidget.get(), getter_AddRefs(mWidget));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+ }
+ }
+#endif // XP_MACOSX
+ }
+
+#ifndef XP_MACOSX
+ // A failure here is terminal since we can't fall back on the non-e10s code
+ // path below.
+ if (!mWidget && XRE_IsContentProcess()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+#endif // XP_MACOSX
+
+ if (!mWidget) {
+ // native (single process)
+ mWidget = do_CreateInstance(kWidgetCID, &rv);
+ nsWidgetInitData initData;
+ initData.mWindowType = eWindowType_plugin;
+ initData.mUnicode = false;
+ initData.clipChildren = true;
+ initData.clipSiblings = true;
+ rv = mWidget->Create(parentWidget.get(), nullptr,
+ LayoutDeviceIntRect(0, 0, 0, 0), &initData);
+ if (NS_FAILED(rv)) {
+ mWidget->Destroy();
+ mWidget = nullptr;
+ return rv;
+ }
+ }
+
+ mWidget->EnableDragDrop(true);
+ mWidget->Show(false);
+ mWidget->Enable(false);
+ }
+
+ if (mPluginFrame) {
+ // nullptr widget is fine, will result in windowless setup.
+ mPluginFrame->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<NPSetWindowCallbackStruct*>(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 = GetPluginPort();
+ // tell the plugin window about the widget
+ mPluginWindow->SetPluginWidget(mWidget);
+
+ // tell the widget about the current plugin instance owner.
+ nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
+ if (pluginWidget) {
+ pluginWidget->SetPluginInstanceOwner(this);
+ }
+ }
+
+#ifdef XP_MACOSX
+ if (GetDrawingModel() == NPDrawingModelCoreAnimation) {
+ AddToCARefreshTimer();
+ }
+#endif
+
+ 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 (!mPluginWindow || !mInstance || !mPluginFrame) {
+ return;
+ }
+
+ SetPluginPort();
+
+ LayoutDeviceIntSize widgetClip = mPluginFrame->GetWidgetlessClipRect().Size();
+
+ mPluginWindow->x = 0;
+ mPluginWindow->y = 0;
+
+ NPRect oldClipRect = mPluginWindow->clipRect;
+
+ // fix up the clipping region
+ mPluginWindow->clipRect.top = 0;
+ mPluginWindow->clipRect.left = 0;
+
+ if (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)
+ {
+ if (UseAsyncRendering()) {
+ mInstance->AsyncSetWindow(mPluginWindow);
+ }
+ else {
+ mPluginWindow->CallSetWindow(mInstance);
+ }
+ }
+
+ // 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;
+
+ bool isActive = WindowIsActive();
+ SendWindowFocusChanged(isActive);
+ mLastWindowIsActive = isActive;
+ }
+}
+
+void
+nsPluginInstanceOwner::WindowFocusMayHaveChanged()
+{
+ if (!mSentInitialTopLevelWindowEvent) {
+ return;
+ }
+
+ bool isActive = WindowIsActive();
+ if (isActive != mLastWindowIsActive) {
+ SendWindowFocusChanged(isActive);
+ mLastWindowIsActive = isActive;
+ }
+}
+
+bool
+nsPluginInstanceOwner::WindowIsActive()
+{
+ if (!mPluginFrame) {
+ return false;
+ }
+
+ EventStates docState = mPluginFrame->GetContent()->OwnerDoc()->GetDocumentState();
+ return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
+}
+
+void
+nsPluginInstanceOwner::SendWindowFocusChanged(bool aIsActive)
+{
+ if (!mInstance) {
+ return;
+ }
+
+ NPCocoaEvent cocoaEvent;
+ InitializeNPCocoaEvent(&cocoaEvent);
+ cocoaEvent.type = NPCocoaEventWindowFocusChanged;
+ cocoaEvent.data.focus.hasFocus = aIsActive;
+ mInstance->HandleEvent(&cocoaEvent,
+ nullptr,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+}
+
+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 = mPluginFrame->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);
+}
+#endif // XP_MACOSX
+
+void
+nsPluginInstanceOwner::ResolutionMayHaveChanged()
+{
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ double scaleFactor = 1.0;
+ GetContentsScaleFactor(&scaleFactor);
+ if (scaleFactor != mLastScaleFactor) {
+ ContentsScaleFactorChanged(scaleFactor);
+ mLastScaleFactor = scaleFactor;
+ }
+#endif
+ float zoomFactor = 1.0;
+ GetCSSZoomFactor(&zoomFactor);
+ if (zoomFactor != mLastCSSZoomFactor) {
+ if (mInstance) {
+ mInstance->CSSZoomFactorChanged(zoomFactor);
+ }
+ mLastCSSZoomFactor = zoomFactor;
+ }
+
+}
+
+void
+nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ mPluginDocumentActiveState = aIsActive;
+#ifndef XP_MACOSX
+ 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 // #ifdef MOZ_WIDGET_ANDROID
+
+ // We don't have a connection to PluginWidgetParent in the chrome
+ // process when dealing with tab visibility changes, so this needs
+ // to be forwarded over after the active state is updated. If we
+ // don't hide plugin widgets in hidden tabs, the native child window
+ // in chrome will remain visible after a tab switch.
+ if (mWidget && XRE_IsContentProcess()) {
+ mWidget->Show(aIsActive);
+ mWidget->Enable(aIsActive);
+ }
+#endif // #ifndef XP_MACOSX
+}
+
+NS_IMETHODIMP
+nsPluginInstanceOwner::CallSetWindow()
+{
+ if (!mWidgetCreationComplete) {
+ // No widget yet, we can't run this code
+ return NS_OK;
+ }
+ if (mPluginFrame) {
+ mPluginFrame->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) || defined(XP_WIN)
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ nsIPresShell* presShell = nsContentUtils::FindPresShellForDocument(content->OwnerDoc());
+ if (presShell) {
+ scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ presShell->GetPresContext()->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+ }
+#endif
+ *result = scaleFactor;
+ return NS_OK;
+}
+
+void
+nsPluginInstanceOwner::GetCSSZoomFactor(float *result)
+{
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ nsIPresShell* presShell = nsContentUtils::FindPresShellForDocument(content->OwnerDoc());
+ if (presShell) {
+ *result = presShell->GetPresContext()->DeviceContext()->GetFullZoom();
+ } else {
+ *result = 1.0;
+ }
+}
+
+void nsPluginInstanceOwner::SetFrame(nsPluginFrame *aFrame)
+{
+ // Don't do anything if the frame situation hasn't changed.
+ if (mPluginFrame == aFrame) {
+ return;
+ }
+
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+
+ // If we already have a frame that is changing or going away...
+ if (mPluginFrame) {
+ if (content && content->OwnerDoc() && content->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<EventTarget> windowRoot = content->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ if (windowRoot) {
+ windowRoot->RemoveEventListener(NS_LITERAL_STRING("activate"),
+ this, false);
+ windowRoot->RemoveEventListener(NS_LITERAL_STRING("deactivate"),
+ this, false);
+ windowRoot->RemoveEventListener(NS_LITERAL_STRING("MozPerformDelayedBlur"),
+ this, false);
+ }
+ }
+
+ // Make sure the old frame isn't holding a reference to us.
+ mPluginFrame->SetInstanceOwner(nullptr);
+ }
+
+ // Swap in the new frame (or no frame)
+ mPluginFrame = aFrame;
+
+ // Set up a new frame
+ if (mPluginFrame) {
+ mPluginFrame->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) {
+ mPluginFrame->PrepForDrawing(mWidget);
+ }
+ mPluginFrame->FixupWindow(mPluginFrame->GetContentRectRelativeToSelf().Size());
+ mPluginFrame->InvalidateFrame();
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ const nsIContent* content = aFrame->GetContent();
+ if (fm && content) {
+ mContentFocused = (content == fm->GetFocusedContent());
+ }
+
+ // Register for widget-focus events on the window root.
+ if (content && content->OwnerDoc() && content->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<EventTarget> windowRoot = content->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ if (windowRoot) {
+ windowRoot->AddEventListener(NS_LITERAL_STRING("activate"),
+ this, false, false);
+ windowRoot->AddEventListener(NS_LITERAL_STRING("deactivate"),
+ this, false, false);
+ windowRoot->AddEventListener(NS_LITERAL_STRING("MozPerformDelayedBlur"),
+ this, false, false);
+ }
+ }
+ }
+}
+
+nsPluginFrame* nsPluginInstanceOwner::GetFrame()
+{
+ return mPluginFrame;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::PrivateModeChanged(bool aEnabled)
+{
+ return mInstance ? mInstance->PrivateModeStateChanged(aEnabled) : NS_OK;
+}
+
+already_AddRefed<nsIURI> nsPluginInstanceOwner::GetBaseURI() const
+{
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (!content) {
+ return nullptr;
+ }
+ return content->GetBaseURI();
+}
+
+// static
+void
+nsPluginInstanceOwner::GeneratePluginEvent(
+ const WidgetCompositionEvent* aSrcCompositionEvent,
+ WidgetCompositionEvent* aDistCompositionEvent)
+{
+#ifdef XP_WIN
+ NPEvent newEvent;
+ switch (aDistCompositionEvent->mMessage) {
+ case eCompositionChange: {
+ newEvent.event = WM_IME_COMPOSITION;
+ newEvent.wParam = 0;
+ if (aSrcCompositionEvent &&
+ (aSrcCompositionEvent->mMessage == eCompositionCommit ||
+ aSrcCompositionEvent->mMessage == eCompositionCommitAsIs)) {
+ newEvent.lParam = GCS_RESULTSTR;
+ } else {
+ newEvent.lParam = GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE;
+ }
+ TextRangeArray* ranges = aDistCompositionEvent->mRanges;
+ if (ranges && ranges->HasCaret()) {
+ newEvent.lParam |= GCS_CURSORPOS;
+ }
+ break;
+ }
+
+ case eCompositionStart:
+ newEvent.event = WM_IME_STARTCOMPOSITION;
+ newEvent.wParam = 0;
+ newEvent.lParam = 0;
+ break;
+
+ case eCompositionEnd:
+ newEvent.event = WM_IME_ENDCOMPOSITION;
+ newEvent.wParam = 0;
+ newEvent.lParam = 0;
+ break;
+
+ default:
+ return;
+ }
+ aDistCompositionEvent->mPluginEvent.Copy(newEvent);
+#endif
+}
+
+// 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);
+}
diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h
new file mode 100644
index 000000000..589bcb02c
--- /dev/null
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -0,0 +1,420 @@
+/* -*- 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/. */
+
+#ifndef nsPluginInstanceOwner_h_
+#define nsPluginInstanceOwner_h_
+
+#include "mozilla/Attributes.h"
+#include "npapi.h"
+#include "nsCOMPtr.h"
+#include "nsIKeyEventInPluginCallback.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsIPrivacyTransitionObserver.h"
+#include "nsIDOMEventListener.h"
+#include "nsPluginHost.h"
+#include "nsPluginNativeWindow.h"
+#include "nsWeakReference.h"
+#include "gfxRect.h"
+
+#ifdef XP_MACOSX
+#include "mozilla/gfx/QuartzSupport.h"
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+class nsIInputStream;
+class nsPluginDOMContextMenuListener;
+class nsPluginFrame;
+class nsDisplayListBuilder;
+
+#if defined(MOZ_X11) || defined(ANDROID)
+class gfxContext;
+#endif
+
+namespace mozilla {
+class TextComposition;
+namespace dom {
+struct MozPluginParameter;
+} // namespace dom
+namespace widget {
+class PuppetWidget;
+} // namespace widget
+} // namespace mozilla
+
+using mozilla::widget::PuppetWidget;
+
+#ifdef MOZ_X11
+#include "gfxXlibNativeRenderer.h"
+#endif
+
+class nsPluginInstanceOwner final : public nsIPluginInstanceOwner
+ , public nsIDOMEventListener
+ , public nsIPrivacyTransitionObserver
+ , public nsIKeyEventInPluginCallback
+ , public nsSupportsWeakReference
+{
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ nsPluginInstanceOwner();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPLUGININSTANCEOWNER
+ NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
+
+ NS_IMETHOD GetURL(const char *aURL, const char *aTarget,
+ nsIInputStream *aPostStream,
+ void *aHeadersData, uint32_t aHeadersDataLen,
+ bool aDoCheckLoadURIChecks) override;
+
+ NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace) override;
+
+ NPError InitAsyncSurface(NPSize *size, NPImageFormat format,
+ void *initData, NPAsyncSurface *surface) override;
+ NPError FinalizeAsyncSurface(NPAsyncSurface *surface) override;
+ void SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed) override;
+
+ /**
+ * Get the type of the HTML tag that was used ot instantiate this
+ * plugin. Currently supported tags are EMBED, OBJECT and APPLET.
+ */
+ NS_IMETHOD GetTagType(nsPluginTagType *aResult);
+
+ void GetParameters(nsTArray<mozilla::dom::MozPluginParameter>& parameters);
+ void GetAttributes(nsTArray<mozilla::dom::MozPluginParameter>& attributes);
+
+ /**
+ * Returns the DOM element corresponding to the tag which references
+ * this plugin in the document.
+ *
+ * @param aDOMElement - resulting DOM element
+ * @result - NS_OK if this operation was successful
+ */
+ NS_IMETHOD GetDOMElement(nsIDOMElement* * aResult);
+
+ // nsIDOMEventListener interfaces
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ nsresult ProcessMouseDown(nsIDOMEvent* aKeyEvent);
+ nsresult ProcessKeyPress(nsIDOMEvent* aKeyEvent);
+ nsresult Destroy();
+
+#ifdef XP_WIN
+ void Paint(const RECT& aDirty, HDC aDC);
+#elif defined(XP_MACOSX)
+ void Paint(const gfxRect& aDirtyRect, CGContextRef cgContext);
+ void RenderCoreAnimation(CGContextRef aCGContext, int aWidth, int aHeight);
+ void DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext);
+#elif defined(MOZ_X11) || defined(ANDROID)
+ void Paint(gfxContext* aContext,
+ const gfxRect& aFrameRect,
+ const gfxRect& aDirtyRect);
+#endif
+
+ //locals
+
+ nsresult Init(nsIContent* aContent);
+
+ void* GetPluginPort();
+ void ReleasePluginPort(void* pluginPort);
+
+ nsEventStatus ProcessEvent(const mozilla::WidgetGUIEvent& anEvent);
+
+ static void GeneratePluginEvent(
+ const mozilla::WidgetCompositionEvent* aSrcCompositionEvent,
+ mozilla::WidgetCompositionEvent* aDistCompositionEvent);
+
+#if defined(XP_WIN)
+ void SetWidgetWindowAsParent(HWND aWindowToAdopt);
+ nsresult SetNetscapeWindowAsParent(HWND aWindowToAdopt);
+#endif
+
+#ifdef XP_MACOSX
+ enum { ePluginPaintEnable, ePluginPaintDisable };
+
+ void WindowFocusMayHaveChanged();
+
+ bool WindowIsActive();
+ void SendWindowFocusChanged(bool aIsActive);
+ NPDrawingModel GetDrawingModel();
+ bool IsRemoteDrawingCoreAnimation();
+
+ NPEventModel GetEventModel();
+ static void CARefresh(nsITimer *aTimer, void *aClosure);
+ void AddToCARefreshTimer();
+ void RemoveFromCARefreshTimer();
+ // This calls into the plugin (NPP_SetWindow) and can run script.
+ void FixUpPluginWindow(int32_t inPaintState);
+ void HidePluginWindow();
+ // Set plugin port info in the plugin (in the 'window' member of the
+ // NPWindow structure passed to the plugin by SetWindow()).
+ void SetPluginPort();
+#else // XP_MACOSX
+ void UpdateWindowPositionAndClipRect(bool aSetWindow);
+ void UpdateWindowVisibility(bool aVisible);
+#endif // XP_MACOSX
+
+ void ResolutionMayHaveChanged();
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
+#endif
+
+ void UpdateDocumentActiveState(bool aIsActive);
+
+ void SetFrame(nsPluginFrame *aFrame);
+ nsPluginFrame* GetFrame();
+
+ uint32_t GetLastEventloopNestingLevel() const {
+ return mLastEventloopNestingLevel;
+ }
+
+ static uint32_t GetEventloopNestingLevel();
+
+ void ConsiderNewEventloopNestingLevel() {
+ uint32_t currentLevel = GetEventloopNestingLevel();
+
+ if (currentLevel < mLastEventloopNestingLevel) {
+ mLastEventloopNestingLevel = currentLevel;
+ }
+ }
+
+ const char* GetPluginName()
+ {
+ if (mInstance && mPluginHost) {
+ const char* name = nullptr;
+ if (NS_SUCCEEDED(mPluginHost->GetPluginName(mInstance, &name)) && name)
+ return name;
+ }
+ return "";
+ }
+
+#ifdef MOZ_X11
+ void GetPluginDescription(nsACString& aDescription)
+ {
+ aDescription.Truncate();
+ if (mInstance && mPluginHost) {
+ nsCOMPtr<nsIPluginTag> pluginTag;
+
+ mPluginHost->GetPluginTagForInstance(mInstance,
+ getter_AddRefs(pluginTag));
+ if (pluginTag) {
+ pluginTag->GetDescription(aDescription);
+ }
+ }
+ }
+#endif
+
+ bool SendNativeEvents()
+ {
+#ifdef XP_WIN
+ // XXX we should remove the plugin name check
+ return mPluginWindow->type == NPWindowTypeDrawable &&
+ (MatchPluginName("Shockwave Flash") ||
+ MatchPluginName("Test Plug-in"));
+#elif defined(MOZ_X11) || defined(XP_MACOSX)
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ bool MatchPluginName(const char *aPluginName)
+ {
+ return strncmp(GetPluginName(), aPluginName, strlen(aPluginName)) == 0;
+ }
+
+ void NotifyPaintWaiter(nsDisplayListBuilder* aBuilder);
+
+ // Returns the image container that has our currently displayed image.
+ already_AddRefed<mozilla::layers::ImageContainer> GetImageContainer();
+ // Returns true if this is windowed plugin that can return static captures
+ // for scroll operations.
+ bool NeedsScrollImageLayer();
+
+ void DidComposite();
+
+ /**
+ * Returns the bounds of the current async-rendered surface. This can only
+ * change in response to messages received by the event loop (i.e. not during
+ * painting).
+ */
+ nsIntSize GetCurrentImageSize();
+
+ // Methods to update the background image we send to async plugins.
+ // The eventual target of these operations is PluginInstanceParent,
+ // but it takes several hops to get there.
+ void SetBackgroundUnknown();
+ already_AddRefed<DrawTarget> BeginUpdateBackground(const nsIntRect& aRect);
+ void EndUpdateBackground(const nsIntRect& aRect);
+
+ bool UseAsyncRendering();
+
+ already_AddRefed<nsIURI> GetBaseURI() const;
+
+#ifdef MOZ_WIDGET_ANDROID
+ // Returns the image container for the specified VideoInfo
+ void GetVideos(nsTArray<nsNPAPIPluginInstance::VideoInfo*>& aVideos);
+ already_AddRefed<mozilla::layers::ImageContainer> GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInfo* aVideoInfo);
+
+ void Invalidate();
+ void Recomposite();
+
+ void RequestFullScreen();
+ void ExitFullScreen();
+
+ // Called from nsAppShell when we removed the fullscreen view.
+ static void ExitFullScreen(jobject view);
+#endif
+
+ void NotifyHostAsyncInitFailed();
+ void NotifyHostCreateWidget();
+ void NotifyDestroyPending();
+
+ bool GetCompositionString(uint32_t aIndex, nsTArray<uint8_t>* aString,
+ int32_t* aLength);
+ bool SetCandidateWindow(
+ const mozilla::widget::CandidateWindowPosition& aPosition);
+ bool RequestCommitOrCancel(bool aCommitted);
+
+ // See nsIKeyEventInPluginCallback
+ virtual void HandledWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData,
+ bool aIsConsumed) override;
+
+ /**
+ * OnWindowedPluginKeyEvent() is called when the plugin process receives
+ * native key event directly.
+ *
+ * @param aNativeKeyData The key event which was received by the
+ * plugin process directly.
+ */
+ void OnWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aNativeKeyData);
+
+ void GetCSSZoomFactor(float *result);
+private:
+ virtual ~nsPluginInstanceOwner();
+
+ // return FALSE if LayerSurface dirty (newly created and don't have valid plugin content yet)
+ bool IsUpToDate()
+ {
+ nsIntSize size;
+ return NS_SUCCEEDED(mInstance->GetImageSize(&size)) &&
+ size == nsIntSize(mPluginWindow->width, mPluginWindow->height);
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ mozilla::LayoutDeviceRect GetPluginRect();
+ bool AddPluginView(const mozilla::LayoutDeviceRect& aRect = mozilla::LayoutDeviceRect(0, 0, 0, 0));
+ void RemovePluginView();
+
+ bool mFullScreen;
+ void* mJavaView;
+#endif
+
+#if defined(XP_WIN)
+ nsIWidget* GetContainingWidgetIfOffset();
+ already_AddRefed<mozilla::TextComposition> GetTextComposition();
+ void HandleNoConsumedCompositionMessage(
+ mozilla::WidgetCompositionEvent* aCompositionEvent,
+ const NPEvent* aPluginEvent);
+ bool mGotCompositionData;
+ bool mSentStartComposition;
+ bool mPluginDidNotHandleIMEComposition;
+#endif
+
+ nsPluginNativeWindow *mPluginWindow;
+ RefPtr<nsNPAPIPluginInstance> mInstance;
+ nsPluginFrame *mPluginFrame;
+ nsWeakPtr mContent; // WEAK, content owns us
+ nsCString mDocumentBase;
+ bool mWidgetCreationComplete;
+ nsCOMPtr<nsIWidget> mWidget;
+ RefPtr<nsPluginHost> mPluginHost;
+
+#ifdef XP_MACOSX
+ static nsCOMPtr<nsITimer> *sCATimer;
+ static nsTArray<nsPluginInstanceOwner*> *sCARefreshListeners;
+ bool mSentInitialTopLevelWindowEvent;
+ bool mLastWindowIsActive;
+ bool mLastContentFocused;
+ // True if, the next time the window is activated, we should blur ourselves.
+ bool mShouldBlurOnActivate;
+#endif
+ double mLastScaleFactor;
+ double mLastCSSZoomFactor;
+ // Initially, the event loop nesting level we were created on, it's updated
+ // if we detect the appshell is on a lower level as long as we're not stopped.
+ // We delay DoStopPlugin() until the appshell reaches this level or lower.
+ uint32_t mLastEventloopNestingLevel;
+ bool mContentFocused;
+ bool mWidgetVisible; // used on Mac to store our widget's visible state
+#ifdef MOZ_X11
+ // Used with windowless plugins only, initialized in CreateWidget().
+ bool mFlash10Quirks;
+#endif
+ bool mPluginWindowVisible;
+ bool mPluginDocumentActiveState;
+
+#ifdef XP_MACOSX
+ NPEventModel mEventModel;
+ // This is a hack! UseAsyncRendering() can incorrectly return false
+ // when we don't have an object frame (possible as of bug 90268).
+ // We hack around this by always returning true if we've ever
+ // returned true.
+ bool mUseAsyncRendering;
+#endif
+
+ // pointer to wrapper for nsIDOMContextMenuListener
+ RefPtr<nsPluginDOMContextMenuListener> mCXMenuListener;
+
+ nsresult DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent);
+ nsresult DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
+ bool aAllowPropagate = false);
+ nsresult DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent);
+ nsresult DispatchCompositionToPlugin(nsIDOMEvent* aEvent);
+
+#ifdef XP_WIN
+ void CallDefaultProc(const mozilla::WidgetGUIEvent* aEvent);
+#endif
+
+#ifdef XP_MACOSX
+ static NPBool ConvertPointPuppet(PuppetWidget *widget, nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace);
+ static NPBool ConvertPointNoPuppet(nsIWidget *widget, nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace);
+ void PerformDelayedBlurs();
+#endif // XP_MACOSX
+
+ int mLastMouseDownButtonType;
+
+#ifdef MOZ_X11
+ class Renderer : public gfxXlibNativeRenderer
+ {
+ public:
+ Renderer(NPWindow* aWindow, nsPluginInstanceOwner* aInstanceOwner,
+ const nsIntSize& aPluginSize, const nsIntRect& aDirtyRect)
+ : mWindow(aWindow), mInstanceOwner(aInstanceOwner),
+ mPluginSize(aPluginSize), mDirtyRect(aDirtyRect)
+ {}
+ virtual nsresult DrawWithXlib(cairo_surface_t* surface,
+ nsIntPoint offset,
+ nsIntRect* clipRects, uint32_t numClipRects) override;
+ private:
+ NPWindow* mWindow;
+ nsPluginInstanceOwner* mInstanceOwner;
+ const nsIntSize& mPluginSize;
+ const nsIntRect& mDirtyRect;
+ };
+#endif
+
+ bool mWaitingForPaint;
+};
+
+#endif // nsPluginInstanceOwner_h_
+
diff --git a/dom/plugins/base/nsPluginLogging.h b/dom/plugins/base/nsPluginLogging.h
new file mode 100644
index 000000000..b51d3531e
--- /dev/null
+++ b/dom/plugins/base/nsPluginLogging.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Plugin Module Logging usage instructions and includes */
+////////////////////////////////////////////////////////////////////////////////
+#ifndef nsPluginLogging_h__
+#define nsPluginLogging_h__
+
+#include "mozilla/Logging.h"
+
+#ifndef PLUGIN_LOGGING // allow external override
+#define PLUGIN_LOGGING 1 // master compile-time switch for pluging logging
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Basic Plugin Logging Usage Instructions
+//
+// 1. Set this environment variable: MOZ_LOG=<name>:<level>
+
+// Choose the <name> and <level> from this list (no quotes):
+
+// Log Names <name>
+#define NPN_LOG_NAME "PluginNPN"
+#define NPP_LOG_NAME "PluginNPP"
+#define PLUGIN_LOG_NAME "Plugin"
+
+// Levels <level>
+#define PLUGIN_LOG_ALWAYS mozilla::LogLevel::Error
+#define PLUGIN_LOG_BASIC mozilla::LogLevel::Info
+#define PLUGIN_LOG_NORMAL mozilla::LogLevel::Debug
+#define PLUGIN_LOG_NOISY mozilla::LogLevel::Verbose
+
+// 2. You can combine logs and levels by separating them with a comma:
+// My favorite Win32 Example: SET MOZ_LOG=Plugin:5,PluginNPP:5,PluginNPN:5
+
+// 3. Instead of output going to the console, you can log to a file. Additionally, set the
+// MOZ_LOG_FILE environment variable to point to the full path of a file.
+// My favorite Win32 Example: SET MOZ_LOG_FILE=c:\temp\pluginLog.txt
+
+// 4. For complete information see the Gecko Developer guide:
+// https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Gecko_Logging
+
+
+#ifdef PLUGIN_LOGGING
+
+class nsPluginLogging
+{
+public:
+ static mozilla::LazyLogModule gNPNLog; // 4.x NP API, calls into navigator
+ static mozilla::LazyLogModule gNPPLog; // 4.x NP API, calls into plugin
+ static mozilla::LazyLogModule gPluginLog; // general plugin log
+};
+
+#endif // PLUGIN_LOGGING
+
+// Quick-use macros
+#ifdef PLUGIN_LOGGING
+ #define NPN_PLUGIN_LOG(a, b) \
+ PR_BEGIN_MACRO \
+ MOZ_LOG(nsPluginLogging::gNPNLog, a, b); \
+ PR_LogFlush(); \
+ PR_END_MACRO
+#else
+ #define NPN_PLUGIN_LOG(a, b)
+#endif
+
+#ifdef PLUGIN_LOGGING
+ #define NPP_PLUGIN_LOG(a, b) \
+ PR_BEGIN_MACRO \
+ MOZ_LOG(nsPluginLogging::gNPPLog, a, b); \
+ PR_LogFlush(); \
+ PR_END_MACRO
+#else
+ #define NPP_PLUGIN_LOG(a, b)
+#endif
+
+#ifdef PLUGIN_LOGGING
+ #define PLUGIN_LOG(a, b) \
+ PR_BEGIN_MACRO \
+ MOZ_LOG(nsPluginLogging::gPluginLog, a, b); \
+ PR_LogFlush(); \
+ PR_END_MACRO
+#else
+ #define PLUGIN_LOG(a, b)
+#endif
+
+#endif // nsPluginLogging_h__
+
diff --git a/dom/plugins/base/nsPluginManifestLineReader.h b/dom/plugins/base/nsPluginManifestLineReader.h
new file mode 100644
index 000000000..be7d77044
--- /dev/null
+++ b/dom/plugins/base/nsPluginManifestLineReader.h
@@ -0,0 +1,102 @@
+/* -*- 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/. */
+
+#ifndef nsPluginManifestLineReader_h_
+#define nsPluginManifestLineReader_h_
+
+#include "nspr.h"
+#include "nsDebug.h"
+
+#ifdef XP_WIN
+#define PLUGIN_REGISTRY_FIELD_DELIMITER '|'
+#else
+#define PLUGIN_REGISTRY_FIELD_DELIMITER ':'
+#endif
+
+#define PLUGIN_REGISTRY_END_OF_LINE_MARKER '$'
+
+class nsPluginManifestLineReader
+{
+ public:
+ nsPluginManifestLineReader() {mBase = mCur = mNext = mLimit = 0;}
+ ~nsPluginManifestLineReader() { if (mBase) delete[] mBase; mBase=0;}
+
+ char* Init(uint32_t flen)
+ {
+ mBase = mCur = mNext = new char[flen + 1];
+ if (mBase) {
+ mLimit = mBase + flen;
+ *mLimit = 0;
+ }
+ mLength = 0;
+ return mBase;
+ }
+
+ bool NextLine()
+ {
+ if (mNext >= mLimit)
+ return false;
+
+ mCur = mNext;
+ mLength = 0;
+
+ char *lastDelimiter = 0;
+ while(mNext < mLimit) {
+ if (IsEOL(*mNext)) {
+ if (lastDelimiter) {
+ if (lastDelimiter && *(mNext - 1) != PLUGIN_REGISTRY_END_OF_LINE_MARKER)
+ return false;
+ *lastDelimiter = '\0';
+ } else {
+ *mNext = '\0';
+ }
+
+ for (++mNext; mNext < mLimit; ++mNext) {
+ if (!IsEOL(*mNext))
+ break;
+ }
+ return true;
+ }
+ if (*mNext == PLUGIN_REGISTRY_FIELD_DELIMITER)
+ lastDelimiter = mNext;
+ ++mNext;
+ ++mLength;
+ }
+ return false;
+ }
+
+ int ParseLine(char** chunks, int maxChunks)
+ {
+ NS_ASSERTION(mCur && maxChunks && chunks, "bad call to ParseLine");
+ int found = 0;
+ chunks[found++] = mCur;
+
+ if (found < maxChunks) {
+ for (char* cur = mCur; *cur; cur++) {
+ if (*cur == PLUGIN_REGISTRY_FIELD_DELIMITER) {
+ *cur = 0;
+ chunks[found++] = cur + 1;
+ if (found == maxChunks)
+ break;
+ }
+ }
+ }
+ return found;
+ }
+
+ char* LinePtr() { return mCur; }
+ uint32_t LineLength() { return mLength; }
+
+ bool IsEOL(char c) {return c == '\n' || c == '\r';}
+
+ char* mBase;
+ private:
+ char* mCur;
+ uint32_t mLength;
+ char* mNext;
+ char* mLimit;
+};
+
+#endif
diff --git a/dom/plugins/base/nsPluginModule.cpp b/dom/plugins/base/nsPluginModule.cpp
new file mode 100644
index 000000000..539db8852
--- /dev/null
+++ b/dom/plugins/base/nsPluginModule.cpp
@@ -0,0 +1,29 @@
+/* -*- 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/ModuleUtils.h"
+#include "nsPluginHost.h"
+#include "nsPluginsCID.h"
+
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsPluginHost, nsPluginHost::GetInst)
+NS_DEFINE_NAMED_CID(NS_PLUGIN_HOST_CID);
+
+static const mozilla::Module::CIDEntry kPluginCIDs[] = {
+ { &kNS_PLUGIN_HOST_CID, false, nullptr, nsPluginHostConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kPluginContracts[] = {
+ { MOZ_PLUGIN_HOST_CONTRACTID, &kNS_PLUGIN_HOST_CID },
+ { nullptr }
+};
+
+static const mozilla::Module kPluginModule = {
+ mozilla::Module::kVersion,
+ kPluginCIDs,
+ kPluginContracts
+};
+
+NSMODULE_DEFN(nsPluginModule) = &kPluginModule;
diff --git a/dom/plugins/base/nsPluginNativeWindow.cpp b/dom/plugins/base/nsPluginNativeWindow.cpp
new file mode 100644
index 000000000..f9baf5b81
--- /dev/null
+++ b/dom/plugins/base/nsPluginNativeWindow.cpp
@@ -0,0 +1,53 @@
+/* -*- 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/. */
+
+/**
+ * This file is the default implementation of plugin native window
+ * empty stubs, it should be replaced with real platform implementation
+ * for every platform
+ */
+
+#include "nsDebug.h"
+#include "nsPluginNativeWindow.h"
+
+class nsPluginNativeWindowPLATFORM : public nsPluginNativeWindow {
+public:
+ nsPluginNativeWindowPLATFORM();
+ virtual ~nsPluginNativeWindowPLATFORM();
+};
+
+nsPluginNativeWindowPLATFORM::nsPluginNativeWindowPLATFORM() : nsPluginNativeWindow()
+{
+ // initialize the struct fields
+ window = nullptr;
+ x = 0;
+ y = 0;
+ width = 0;
+ height = 0;
+ memset(&clipRect, 0, sizeof(clipRect));
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ ws_info = nullptr;
+#endif
+ type = NPWindowTypeWindow;
+}
+
+nsPluginNativeWindowPLATFORM::~nsPluginNativeWindowPLATFORM()
+{
+}
+
+nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+ *aPluginNativeWindow = new nsPluginNativeWindowPLATFORM();
+ return NS_OK;
+}
+
+nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+ nsPluginNativeWindowPLATFORM *p = (nsPluginNativeWindowPLATFORM *)aPluginNativeWindow;
+ delete p;
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginNativeWindow.h b/dom/plugins/base/nsPluginNativeWindow.h
new file mode 100644
index 000000000..7c54d8a97
--- /dev/null
+++ b/dom/plugins/base/nsPluginNativeWindow.h
@@ -0,0 +1,80 @@
+/* -*- 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/. */
+
+#ifndef _nsPluginNativeWindow_h_
+#define _nsPluginNativeWindow_h_
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsNPAPIPluginInstance.h"
+#include "npapi.h"
+#include "nsIWidget.h"
+
+/**
+ * base class for native plugin window implementations
+ */
+class nsPluginNativeWindow : public NPWindow
+{
+public:
+ nsPluginNativeWindow() : NPWindow() {
+ MOZ_COUNT_CTOR(nsPluginNativeWindow);
+ }
+
+ virtual ~nsPluginNativeWindow() {
+ MOZ_COUNT_DTOR(nsPluginNativeWindow);
+ }
+
+ /**
+ * !!! CAUTION !!!
+ *
+ * The base class |nsPluginWindow| is defined as a struct in nsplugindefs.h,
+ * thus it does not have a destructor of its own.
+ * One should never attempt to delete |nsPluginNativeWindow| object instance
+ * (or derivatives) using a pointer of |nsPluginWindow *| type. Should such
+ * necessity occur it must be properly casted first.
+ */
+
+public:
+ nsresult GetPluginInstance(RefPtr<nsNPAPIPluginInstance> &aPluginInstance) {
+ aPluginInstance = mPluginInstance;
+ return NS_OK;
+ }
+ nsresult SetPluginInstance(nsNPAPIPluginInstance *aPluginInstance) {
+ if (mPluginInstance != aPluginInstance)
+ mPluginInstance = aPluginInstance;
+ return NS_OK;
+ }
+
+ nsresult GetPluginWidget(nsIWidget **aWidget) const {
+ NS_IF_ADDREF(*aWidget = mWidget);
+ return NS_OK;
+ }
+ nsresult SetPluginWidget(nsIWidget *aWidget) {
+ mWidget = aWidget;
+ return NS_OK;
+ }
+
+public:
+ virtual nsresult CallSetWindow(RefPtr<nsNPAPIPluginInstance> &aPluginInstance) {
+ // null aPluginInstance means that we want to call SetWindow(null)
+ if (aPluginInstance)
+ aPluginInstance->SetWindow(this);
+ else if (mPluginInstance)
+ mPluginInstance->SetWindow(nullptr);
+
+ SetPluginInstance(aPluginInstance);
+ return NS_OK;
+ }
+
+protected:
+ RefPtr<nsNPAPIPluginInstance> mPluginInstance;
+ nsCOMPtr<nsIWidget> mWidget;
+};
+
+nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow);
+nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow);
+
+#endif //_nsPluginNativeWindow_h_
diff --git a/dom/plugins/base/nsPluginNativeWindowGtk.cpp b/dom/plugins/base/nsPluginNativeWindowGtk.cpp
new file mode 100644
index 000000000..bfb9510e0
--- /dev/null
+++ b/dom/plugins/base/nsPluginNativeWindowGtk.cpp
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=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/. */
+
+/**
+ * This file is the Gtk2 implementation of plugin native window.
+ */
+
+#include "nsDebug.h"
+#include "nsPluginNativeWindowGtk.h"
+#include "nsNPAPIPlugin.h"
+#include "npapi.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+
+#if (GTK_MAJOR_VERSION == 3)
+#include <gtk/gtkx.h>
+#else
+#include "gtk2xtbin.h"
+#endif
+#include "mozilla/X11Util.h"
+
+static void plug_added_cb(GtkWidget *widget, gpointer data);
+static gboolean plug_removed_cb (GtkWidget *widget, gpointer data);
+static void socket_unrealize_cb (GtkWidget *widget, gpointer data);
+
+nsPluginNativeWindowGtk::nsPluginNativeWindowGtk() : nsPluginNativeWindow()
+{
+ // initialize the struct fields
+ window = nullptr;
+ x = 0;
+ y = 0;
+ width = 0;
+ height = 0;
+ memset(&clipRect, 0, sizeof(clipRect));
+ ws_info = &mWsInfo;
+ type = NPWindowTypeWindow;
+ mSocketWidget = 0;
+ mWsInfo.type = 0;
+ mWsInfo.display = nullptr;
+ mWsInfo.visual = nullptr;
+ mWsInfo.colormap = 0;
+ mWsInfo.depth = 0;
+}
+
+nsPluginNativeWindowGtk::~nsPluginNativeWindowGtk()
+{
+ if(mSocketWidget) {
+ gtk_widget_destroy(mSocketWidget);
+ }
+}
+
+nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+ *aPluginNativeWindow = new nsPluginNativeWindowGtk();
+ return NS_OK;
+}
+
+nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+ nsPluginNativeWindowGtk *p = (nsPluginNativeWindowGtk *)aPluginNativeWindow;
+ delete p;
+ return NS_OK;
+}
+
+nsresult nsPluginNativeWindowGtk::CallSetWindow(RefPtr<nsNPAPIPluginInstance> &aPluginInstance)
+{
+ if (aPluginInstance) {
+ if (type == NPWindowTypeWindow &&
+ XRE_IsContentProcess()) {
+ // In this case, most of the initialization code here has already happened
+ // in the chrome process. The window we have in content is the XID of the
+ // socket widget we need to hand to plugins.
+ SetWindow((XID)window);
+ } else if (type == NPWindowTypeWindow) {
+ if (!mSocketWidget) {
+ nsresult rv;
+
+ // The documentation on the types for many variables in NP(N|P)_GetValue
+ // is vague. Often boolean values are NPBool (1 byte), but
+ // https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins
+ // treats NPPVpluginNeedsXEmbed as PRBool (int), and
+ // on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|.
+ // thus we can't use NPBool for needsXEmbed, or the three bytes above
+ // it on the stack would get clobbered. so protect with the larger bool.
+ int needsXEmbed = 0;
+ rv = aPluginInstance->GetValueFromPlugin(NPPVpluginNeedsXEmbed, &needsXEmbed);
+ // If the call returned an error code make sure we still use our default value.
+ if (NS_FAILED(rv)) {
+ needsXEmbed = 0;
+ }
+#ifdef DEBUG
+ printf("nsPluginNativeWindowGtk: NPPVpluginNeedsXEmbed=%d\n", needsXEmbed);
+#endif
+
+ bool isOOPPlugin = aPluginInstance->GetPlugin()->GetLibrary()->IsOOP();
+ if (needsXEmbed || isOOPPlugin) {
+ bool enableXtFocus = !needsXEmbed;
+ rv = CreateXEmbedWindow(enableXtFocus);
+ }
+ else {
+#if (MOZ_WIDGET_GTK == 2)
+ rv = CreateXtWindow();
+#else
+ return NS_ERROR_FAILURE;
+#endif
+ }
+
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (!mSocketWidget) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure to resize and re-place the window if required.
+ SetAllocation();
+ // Need to reset "window" each time as nsPluginFrame::DidReflow sets it
+ // to the ancestor window.
+#if (MOZ_WIDGET_GTK == 2)
+ if (GTK_IS_XTBIN(mSocketWidget)) {
+ // Point the NPWindow structures window to the actual X window
+ SetWindow(GTK_XTBIN(mSocketWidget)->xtwindow);
+ }
+ else { // XEmbed or OOP&Xt
+ SetWindow(gtk_socket_get_id(GTK_SOCKET(mSocketWidget)));
+ }
+#else
+ // Gtk3 supports only OOP by GtkSocket
+ SetWindow(gtk_socket_get_id(GTK_SOCKET(mSocketWidget)));
+#endif
+
+#ifdef DEBUG
+ printf("nsPluginNativeWindowGtk: call SetWindow with xid=%p\n", (void *)window);
+#endif
+ } // NPWindowTypeWindow
+ aPluginInstance->SetWindow(this);
+ } else if (mPluginInstance) {
+ mPluginInstance->SetWindow(nullptr);
+ }
+
+ SetPluginInstance(aPluginInstance);
+ return NS_OK;
+}
+
+nsresult nsPluginNativeWindowGtk::CreateXEmbedWindow(bool aEnableXtFocus) {
+ NS_ASSERTION(!mSocketWidget,"Already created a socket widget!");
+ GdkDisplay *display = gdk_display_get_default();
+ GdkWindow *parent_win = gdk_x11_window_lookup_for_display(display, GetWindow());
+ mSocketWidget = gtk_socket_new();
+
+ //attach the socket to the container widget
+ gtk_widget_set_parent_window(mSocketWidget, parent_win);
+
+ // enable/disable focus event handlers,
+ // see plugin_window_filter_func() for details
+ g_object_set_data(G_OBJECT(mSocketWidget), "enable-xt-focus", (void *)aEnableXtFocus);
+
+ g_signal_connect(mSocketWidget, "plug_added",
+ G_CALLBACK(plug_added_cb), nullptr);
+
+ // Make sure to handle the plug_removed signal. If we don't the
+ // socket will automatically be destroyed when the plug is
+ // removed, which means we're destroying it more than once.
+ // SYNTAX ERROR.
+ g_signal_connect(mSocketWidget, "plug_removed",
+ G_CALLBACK(plug_removed_cb), nullptr);
+
+ g_signal_connect(mSocketWidget, "unrealize",
+ G_CALLBACK(socket_unrealize_cb), nullptr);
+
+ g_signal_connect(mSocketWidget, "destroy",
+ G_CALLBACK(gtk_widget_destroyed), &mSocketWidget);
+
+ gpointer user_data = nullptr;
+ gdk_window_get_user_data(parent_win, &user_data);
+
+ GtkContainer *container = GTK_CONTAINER(user_data);
+ gtk_container_add(container, mSocketWidget);
+ gtk_widget_realize(mSocketWidget);
+
+ // The GtkSocket has a visible window, but the plugin's XEmbed plug will
+ // cover this window. Normally GtkSockets let the X server paint their
+ // background and this would happen immediately (before the plug is
+ // created). Setting the background to None prevents the server from
+ // painting this window, avoiding flicker.
+ // TODO GTK3
+#if (MOZ_WIDGET_GTK == 2)
+ gdk_window_set_back_pixmap(gtk_widget_get_window(mSocketWidget), nullptr, FALSE);
+#endif
+
+ // Resize before we show
+ SetAllocation();
+
+ gtk_widget_show(mSocketWidget);
+
+ gdk_flush();
+ SetWindow(gtk_socket_get_id(GTK_SOCKET(mSocketWidget)));
+
+ // Fill out the ws_info structure.
+ // (The windowless case is done in nsPluginFrame.cpp.)
+ GdkWindow *gdkWindow = gdk_x11_window_lookup_for_display(display, GetWindow());
+ if(!gdkWindow)
+ return NS_ERROR_FAILURE;
+
+ mWsInfo.display = GDK_WINDOW_XDISPLAY(gdkWindow);
+#if (MOZ_WIDGET_GTK == 2)
+ mWsInfo.colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(gdkWindow));
+ GdkVisual* gdkVisual = gdk_drawable_get_visual(gdkWindow);
+ mWsInfo.depth = gdkVisual->depth;
+#else
+ mWsInfo.colormap = X11None;
+ GdkVisual* gdkVisual = gdk_window_get_visual(gdkWindow);
+ mWsInfo.depth = gdk_visual_get_depth(gdkVisual);
+#endif
+ mWsInfo.visual = GDK_VISUAL_XVISUAL(gdkVisual);
+
+ return NS_OK;
+}
+
+void nsPluginNativeWindowGtk::SetAllocation() {
+ if (!mSocketWidget)
+ return;
+
+ GtkAllocation new_allocation;
+ new_allocation.x = 0;
+ new_allocation.y = 0;
+ new_allocation.width = width;
+ new_allocation.height = height;
+ gtk_widget_size_allocate(mSocketWidget, &new_allocation);
+}
+
+#if (MOZ_WIDGET_GTK == 2)
+nsresult nsPluginNativeWindowGtk::CreateXtWindow() {
+ NS_ASSERTION(!mSocketWidget,"Already created a socket widget!");
+
+#ifdef DEBUG
+ printf("About to create new xtbin of %i X %i from %p...\n",
+ width, height, (void*)window);
+#endif
+ GdkDisplay *display = gdk_display_get_default();
+ GdkWindow *gdkWindow = gdk_x11_window_lookup_for_display(display, GetWindow());
+ mSocketWidget = gtk_xtbin_new(gdkWindow, 0);
+ // Check to see if creating the xtbin failed for some reason.
+ // if it did, we can't go any further.
+ if (!mSocketWidget)
+ return NS_ERROR_FAILURE;
+
+ g_signal_connect(mSocketWidget, "destroy",
+ G_CALLBACK(gtk_widget_destroyed), &mSocketWidget);
+
+ gtk_widget_set_size_request(mSocketWidget, width, height);
+
+#ifdef DEBUG
+ printf("About to show xtbin(%p)...\n", (void*)mSocketWidget); fflush(nullptr);
+#endif
+ gtk_widget_show(mSocketWidget);
+#ifdef DEBUG
+ printf("completed gtk_widget_show(%p)\n", (void*)mSocketWidget); fflush(nullptr);
+#endif
+
+ // Fill out the ws_info structure.
+ GtkXtBin* xtbin = GTK_XTBIN(mSocketWidget);
+ // The xtbin has its own Display structure.
+ mWsInfo.display = xtbin->xtdisplay;
+ mWsInfo.colormap = xtbin->xtclient.xtcolormap;
+ mWsInfo.visual = xtbin->xtclient.xtvisual;
+ mWsInfo.depth = xtbin->xtclient.xtdepth;
+ // Leave mWsInfo.type = 0 - Who knows what this is meant to be?
+
+ XFlush(mWsInfo.display);
+
+ return NS_OK;
+}
+#endif
+
+static void
+plug_window_finalize_cb(gpointer socket, GObject* plug_window)
+{
+ g_object_unref(socket);
+}
+
+static void
+plug_added_cb(GtkWidget *socket, gpointer data)
+{
+ // The plug window has been embedded, and gtk_socket_add_window() has added
+ // a filter to the socket's plug_window, passing the socket as data for the
+ // filter, so the socket must live as long as events may be received on the
+ // plug window.
+ //
+ // https://git.gnome.org/browse/gtk+/tree/gtk/gtksocket.c?h=3.18.7#n1124
+ g_object_ref(socket);
+ // When the socket is unrealized, perhaps during gtk_widget_destroy() from
+ // ~nsPluginNativeWindowGtk, the plug is removed. The plug in the child
+ // process then destroys its widget and window. When the browser process
+ // receives the DestroyNotify event for the plug window, GDK releases its
+ // reference to plugWindow. This is typically the last reference and so the
+ // weak ref callback triggers release of the socket.
+ GdkWindow* plugWindow = gtk_socket_get_plug_window(GTK_SOCKET(socket));
+ g_object_weak_ref(G_OBJECT(plugWindow), plug_window_finalize_cb, socket);
+}
+
+/* static */
+gboolean
+plug_removed_cb (GtkWidget *widget, gpointer data)
+{
+ // Gee, thanks for the info!
+ return TRUE;
+}
+
+static void
+socket_unrealize_cb(GtkWidget *widget, gpointer data)
+{
+ // Unmap and reparent any child windows that GDK does not yet know about.
+ // (See bug 540114 comment 10.)
+ GdkWindow* socket_window = gtk_widget_get_window(widget);
+ GdkDisplay* gdkDisplay = gdk_display_get_default();
+ Display* display = GDK_DISPLAY_XDISPLAY(gdkDisplay);
+
+ // Ignore X errors that may happen if windows get destroyed (possibly
+ // requested by the plugin) between XQueryTree and when we operate on them.
+ gdk_error_trap_push();
+
+ Window root, parent;
+ Window* children;
+ unsigned int nchildren;
+ if (!XQueryTree(display, gdk_x11_window_get_xid(socket_window),
+ &root, &parent, &children, &nchildren))
+ return;
+
+ for (unsigned int i = 0; i < nchildren; ++i) {
+ Window child = children[i];
+ if (!gdk_x11_window_lookup_for_display(gdkDisplay, child)) {
+ // This window is not known to GDK.
+ XUnmapWindow(display, child);
+ XReparentWindow(display, child, DefaultRootWindow(display), 0, 0);
+ }
+ }
+
+ if (children) XFree(children);
+
+ mozilla::FinishX(display);
+#if (MOZ_WIDGET_GTK == 3)
+ gdk_error_trap_pop_ignored();
+#else
+ gdk_error_trap_pop();
+#endif
+}
diff --git a/dom/plugins/base/nsPluginNativeWindowGtk.h b/dom/plugins/base/nsPluginNativeWindowGtk.h
new file mode 100644
index 000000000..872d7f017
--- /dev/null
+++ b/dom/plugins/base/nsPluginNativeWindowGtk.h
@@ -0,0 +1,51 @@
+/* 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/. */
+
+#ifndef _nsPluginNativeWindowGdk_h_
+#define _nsPluginNativeWindowGdk_h_
+
+#include "nsPluginNativeWindow.h"
+#include "npapi.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+#if (GTK_MAJOR_VERSION == 3)
+#include <gtk/gtkx.h>
+#else
+#include "gtk2xtbin.h"
+#endif
+#include "mozilla/X11Util.h"
+
+class nsPluginNativeWindowGtk : public nsPluginNativeWindow {
+public:
+ nsPluginNativeWindowGtk();
+ virtual ~nsPluginNativeWindowGtk();
+
+ virtual nsresult CallSetWindow(RefPtr<nsNPAPIPluginInstance> &aPluginInstance);
+ nsresult CreateXEmbedWindow(bool aEnableXtFocus);
+ void SetAllocation();
+
+ XID GetWindow() const
+ {
+ return static_cast<XID>(reinterpret_cast<uintptr_t>(window));
+ }
+
+private:
+ void SetWindow(XID aWindow)
+ {
+ window = reinterpret_cast<void*>(static_cast<uintptr_t>(aWindow));
+ }
+
+ NPSetWindowCallbackStruct mWsInfo;
+ /**
+ * Either a GtkSocket or a special GtkXtBin widget (derived from GtkSocket)
+ * that encapsulates the Xt toolkit within a Gtk Application.
+ */
+ GtkWidget* mSocketWidget;
+#if (MOZ_WIDGET_GTK == 2)
+ nsresult CreateXtWindow();
+#endif
+};
+
+#endif
diff --git a/dom/plugins/base/nsPluginNativeWindowWin.cpp b/dom/plugins/base/nsPluginNativeWindowWin.cpp
new file mode 100644
index 000000000..106dcaf77
--- /dev/null
+++ b/dom/plugins/base/nsPluginNativeWindowWin.cpp
@@ -0,0 +1,748 @@
+/* -*- 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/BasicEvents.h"
+#include "mozilla/DebugOnly.h"
+
+#include "windows.h"
+#include "windowsx.h"
+
+// XXXbz windowsx.h defines GetFirstChild, GetNextSibling,
+// GetPrevSibling are macros, apparently... Eeevil. We have functions
+// called that on some classes, so undef them.
+#undef GetFirstChild
+#undef GetNextSibling
+#undef GetPrevSibling
+
+#include "nsDebug.h"
+
+#include "nsWindowsDllInterceptor.h"
+#include "nsPluginNativeWindow.h"
+#include "nsThreadUtils.h"
+#include "nsTWeakRef.h"
+#include "nsCrashOnException.h"
+
+using namespace mozilla;
+
+#define NP_POPUP_API_VERSION 16
+
+#define nsMajorVersion(v) (((int32_t)(v) >> 16) & 0xffff)
+#define nsMinorVersion(v) ((int32_t)(v) & 0xffff)
+#define versionOK(suppliedV, requiredV) \
+ (nsMajorVersion(suppliedV) == nsMajorVersion(requiredV) \
+ && nsMinorVersion(suppliedV) >= nsMinorVersion(requiredV))
+
+
+#define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION TEXT("MozillaPluginWindowPropertyAssociation")
+#define NS_PLUGIN_CUSTOM_MSG_ID TEXT("MozFlashUserRelay")
+#define WM_USER_FLASH WM_USER+1
+static UINT sWM_FLASHBOUNCEMSG = 0;
+
+typedef nsTWeakRef<class nsPluginNativeWindowWin> PluginWindowWeakRef;
+
+/**
+ * PLEvent handling code
+ */
+class PluginWindowEvent : public Runnable {
+public:
+ PluginWindowEvent();
+ void Init(const PluginWindowWeakRef &ref, HWND hWnd, UINT msg, WPARAM wParam,
+ LPARAM lParam);
+ void Clear();
+ HWND GetWnd() { return mWnd; };
+ UINT GetMsg() { return mMsg; };
+ WPARAM GetWParam() { return mWParam; };
+ LPARAM GetLParam() { return mLParam; };
+ bool InUse() { return mWnd != nullptr; };
+
+ NS_DECL_NSIRUNNABLE
+
+protected:
+ PluginWindowWeakRef mPluginWindowRef;
+ HWND mWnd;
+ UINT mMsg;
+ WPARAM mWParam;
+ LPARAM mLParam;
+};
+
+PluginWindowEvent::PluginWindowEvent()
+{
+ Clear();
+}
+
+void PluginWindowEvent::Clear()
+{
+ mWnd = nullptr;
+ mMsg = 0;
+ mWParam = 0;
+ mLParam = 0;
+}
+
+void PluginWindowEvent::Init(const PluginWindowWeakRef &ref, HWND aWnd,
+ UINT aMsg, WPARAM aWParam, LPARAM aLParam)
+{
+ NS_ASSERTION(aWnd != nullptr, "invalid plugin event value");
+ NS_ASSERTION(mWnd == nullptr, "event already in use");
+ mPluginWindowRef = ref;
+ mWnd = aWnd;
+ mMsg = aMsg;
+ mWParam = aWParam;
+ mLParam = aLParam;
+}
+
+/**
+ * nsPluginNativeWindow Windows specific class declaration
+ */
+
+class nsPluginNativeWindowWin : public nsPluginNativeWindow {
+public:
+ nsPluginNativeWindowWin();
+ virtual ~nsPluginNativeWindowWin();
+
+ virtual nsresult CallSetWindow(RefPtr<nsNPAPIPluginInstance> &aPluginInstance);
+
+private:
+ nsresult SubclassAndAssociateWindow();
+ nsresult UndoSubclassAndAssociateWindow();
+
+public:
+ // locals
+ WNDPROC GetPrevWindowProc();
+ void SetPrevWindowProc(WNDPROC proc) { mPluginWinProc = proc; }
+ WNDPROC GetWindowProc();
+ PluginWindowEvent * GetPluginWindowEvent(HWND aWnd,
+ UINT aMsg,
+ WPARAM aWParam,
+ LPARAM aLParam);
+
+private:
+ WNDPROC mPluginWinProc;
+ WNDPROC mPrevWinProc;
+ PluginWindowWeakRef mWeakRef;
+ RefPtr<PluginWindowEvent> mCachedPluginWindowEvent;
+
+ HWND mParentWnd;
+ LONG_PTR mParentProc;
+public:
+ nsPluginHost::SpecialType mPluginType;
+};
+
+static bool sInMessageDispatch = false;
+static bool sInPreviousMessageDispatch = false;
+static UINT sLastMsg = 0;
+
+static bool ProcessFlashMessageDelayed(nsPluginNativeWindowWin * aWin, nsNPAPIPluginInstance * aInst,
+ HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ NS_ENSURE_TRUE(aWin, false);
+ NS_ENSURE_TRUE(aInst, false);
+
+ if (msg == sWM_FLASHBOUNCEMSG) {
+ // See PluginWindowEvent::Run() below.
+ NS_ASSERTION((sWM_FLASHBOUNCEMSG != 0), "RegisterWindowMessage failed in flash plugin WM_USER message handling!");
+ ::CallWindowProc((WNDPROC)aWin->GetWindowProc(), hWnd, WM_USER_FLASH, wParam, lParam);
+ return true;
+ }
+
+ if (msg != WM_USER_FLASH)
+ return false; // no need to delay
+
+ // do stuff
+ nsCOMPtr<nsIRunnable> pwe = aWin->GetPluginWindowEvent(hWnd, msg, wParam, lParam);
+ if (pwe) {
+ NS_DispatchToCurrentThread(pwe);
+ return true;
+ }
+ return false;
+}
+
+class nsDelayedPopupsEnabledEvent : public Runnable
+{
+public:
+ nsDelayedPopupsEnabledEvent(nsNPAPIPluginInstance *inst)
+ : mInst(inst)
+ {}
+
+ NS_DECL_NSIRUNNABLE
+
+private:
+ RefPtr<nsNPAPIPluginInstance> mInst;
+};
+
+NS_IMETHODIMP nsDelayedPopupsEnabledEvent::Run()
+{
+ mInst->PushPopupsEnabledState(false);
+ return NS_OK;
+}
+
+static LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+/**
+ * New plugin window procedure
+ *
+ * e10s note - this subclass, and the hooks we set below using WindowsDllInterceptor
+ * are currently not in use when running with e10s. (Utility calls like CallSetWindow
+ * are still in use in the content process.) We would like to keep things this away,
+ * essentially making all the hacks here obsolete. Some of the mitigation work here has
+ * already been supplanted by code in PluginInstanceChild. The rest we eventually want
+ * to rip out.
+ */
+static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ nsPluginNativeWindowWin * win = (nsPluginNativeWindowWin *)::GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+ if (!win)
+ return TRUE;
+
+ // The DispatchEvent(ePluginActivate) below can trigger a reentrant focus
+ // event which might destroy us. Hold a strong ref on the plugin instance
+ // to prevent that, bug 374229.
+ RefPtr<nsNPAPIPluginInstance> inst;
+ win->GetPluginInstance(inst);
+
+ // Real may go into a state where it recursivly dispatches the same event
+ // when subclassed. If this is Real, lets examine the event and drop it
+ // on the floor if we get into this recursive situation. See bug 192914.
+ if (win->mPluginType == nsPluginHost::eSpecialType_RealPlayer) {
+ if (sInMessageDispatch && msg == sLastMsg)
+ return true;
+ // Cache the last message sent
+ sLastMsg = msg;
+ }
+
+ bool enablePopups = false;
+
+ // Activate/deactivate mouse capture on the plugin widget
+ // here, before we pass the Windows event to the plugin
+ // because its possible our widget won't get paired events
+ // (see bug 131007) and we'll look frozen. Note that this
+ // is also done in ChildWindow::DispatchMouseEvent.
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN: {
+ nsCOMPtr<nsIWidget> widget;
+ win->GetPluginWidget(getter_AddRefs(widget));
+ if (widget)
+ widget->CaptureMouse(true);
+ break;
+ }
+ case WM_LBUTTONUP:
+ enablePopups = true;
+
+ // fall through
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP: {
+ nsCOMPtr<nsIWidget> widget;
+ win->GetPluginWidget(getter_AddRefs(widget));
+ if (widget)
+ widget->CaptureMouse(false);
+ break;
+ }
+ case WM_KEYDOWN:
+ // Ignore repeating keydown messages...
+ if ((lParam & 0x40000000) != 0) {
+ break;
+ }
+
+ // fall through
+ case WM_KEYUP:
+ enablePopups = true;
+
+ break;
+
+ case WM_MOUSEACTIVATE: {
+ // If a child window of this plug-in is already focused,
+ // don't focus the parent to avoid focus dance. We'll
+ // receive a follow up WM_SETFOCUS which will notify
+ // the appropriate window anyway.
+ HWND focusedWnd = ::GetFocus();
+ if (!::IsChild((HWND)win->window, focusedWnd)) {
+ // Notify the dom / focus manager the plugin has focus when one of
+ // it's child windows receives it. OOPP specific - this code is
+ // critical in notifying the dom of focus changes when the plugin
+ // window in the child process receives focus via a mouse click.
+ // WM_MOUSEACTIVATE is sent by nsWindow via a custom window event
+ // sent from PluginInstanceParent in response to focus events sent
+ // from the child. (bug 540052) Note, this gui event could also be
+ // sent directly from widget.
+ nsCOMPtr<nsIWidget> widget;
+ win->GetPluginWidget(getter_AddRefs(widget));
+ if (widget) {
+ WidgetGUIEvent event(true, ePluginActivate, widget);
+ nsEventStatus status;
+ widget->DispatchEvent(&event, status);
+ }
+ }
+ }
+ break;
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS: {
+ // RealPlayer can crash, don't process the message for those,
+ // see bug 328675.
+ if (win->mPluginType == nsPluginHost::eSpecialType_RealPlayer && msg == sLastMsg)
+ return TRUE;
+ // Make sure setfocus and killfocus get through to the widget procedure
+ // even if they are eaten by the plugin. Also make sure we aren't calling
+ // recursively.
+ WNDPROC prevWndProc = win->GetPrevWindowProc();
+ if (prevWndProc && !sInPreviousMessageDispatch) {
+ sInPreviousMessageDispatch = true;
+ ::CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
+ sInPreviousMessageDispatch = false;
+ }
+ break;
+ }
+ }
+
+ // Macromedia Flash plugin may flood the message queue with some special messages
+ // (WM_USER+1) causing 100% CPU consumption and GUI freeze, see mozilla bug 132759;
+ // we can prevent this from happening by delaying the processing such messages;
+ if (win->mPluginType == nsPluginHost::eSpecialType_Flash) {
+ if (ProcessFlashMessageDelayed(win, inst, hWnd, msg, wParam, lParam))
+ return TRUE;
+ }
+
+ if (enablePopups && inst) {
+ uint16_t apiVersion;
+ if (NS_SUCCEEDED(inst->GetPluginAPIVersion(&apiVersion)) &&
+ !versionOK(apiVersion, NP_POPUP_API_VERSION)) {
+ inst->PushPopupsEnabledState(true);
+ }
+ }
+
+ sInMessageDispatch = true;
+ LRESULT res;
+ WNDPROC proc = (WNDPROC)win->GetWindowProc();
+ if (PluginWndProc == proc) {
+ NS_WARNING("Previous plugin window procedure references PluginWndProc! "
+ "Report this bug!");
+ res = CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam);
+ } else {
+ res = CallWindowProc(proc, hWnd, msg, wParam, lParam);
+ }
+ sInMessageDispatch = false;
+
+ if (inst) {
+ // Popups are enabled (were enabled before the call to
+ // CallWindowProc()). Some plugins (at least the flash player)
+ // post messages from their key handlers etc that delay the actual
+ // processing, so we need to delay the disabling of popups so that
+ // popups remain enabled when the flash player ends up processing
+ // the actual key handlers. We do this by posting an event that
+ // does the disabling, this way our disabling will happen after
+ // the handlers in the plugin are done.
+
+ // Note that it's not fatal if any of this fails (which won't
+ // happen unless we're out of memory anyways) since the plugin
+ // code will pop any popup state pushed by this plugin on
+ // destruction.
+
+ nsCOMPtr<nsIRunnable> event = new nsDelayedPopupsEnabledEvent(inst);
+ if (event)
+ NS_DispatchToCurrentThread(event);
+ }
+
+ return res;
+}
+
+static LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return mozilla::CallWindowProcCrashProtected(PluginWndProcInternal, hWnd, msg, wParam, lParam);
+}
+
+/*
+ * Flash will reset the subclass of our widget at various times.
+ * (Notably when entering and exiting full screen mode.) This
+ * occurs independent of the main plugin window event procedure.
+ * We trap these subclass calls to prevent our subclass hook from
+ * getting dropped.
+ * Note, ascii versions can be nixed once flash versions < 10.1
+ * are considered obsolete.
+ */
+static WindowsDllInterceptor sUser32Intercept;
+
+#ifdef _WIN64
+typedef LONG_PTR
+ (WINAPI *User32SetWindowLongPtrA)(HWND hWnd,
+ int nIndex,
+ LONG_PTR dwNewLong);
+typedef LONG_PTR
+ (WINAPI *User32SetWindowLongPtrW)(HWND hWnd,
+ int nIndex,
+ LONG_PTR dwNewLong);
+static User32SetWindowLongPtrA sUser32SetWindowLongAHookStub = nullptr;
+static User32SetWindowLongPtrW sUser32SetWindowLongWHookStub = nullptr;
+#else
+typedef LONG
+(WINAPI *User32SetWindowLongA)(HWND hWnd,
+ int nIndex,
+ LONG dwNewLong);
+typedef LONG
+(WINAPI *User32SetWindowLongW)(HWND hWnd,
+ int nIndex,
+ LONG dwNewLong);
+static User32SetWindowLongA sUser32SetWindowLongAHookStub = nullptr;
+static User32SetWindowLongW sUser32SetWindowLongWHookStub = nullptr;
+#endif
+static inline bool
+SetWindowLongHookCheck(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong)
+{
+ nsPluginNativeWindowWin * win =
+ (nsPluginNativeWindowWin *)GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+ if (!win || (win && win->mPluginType != nsPluginHost::eSpecialType_Flash) ||
+ (nIndex == GWLP_WNDPROC &&
+ newLong == reinterpret_cast<LONG_PTR>(PluginWndProc)))
+ return true;
+ return false;
+}
+
+#ifdef _WIN64
+LONG_PTR WINAPI
+SetWindowLongPtrAHook(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong)
+#else
+LONG WINAPI
+SetWindowLongAHook(HWND hWnd,
+ int nIndex,
+ LONG newLong)
+#endif
+{
+ if (SetWindowLongHookCheck(hWnd, nIndex, newLong))
+ return sUser32SetWindowLongAHookStub(hWnd, nIndex, newLong);
+
+ // Set flash's new subclass to get the result.
+ LONG_PTR proc = sUser32SetWindowLongAHookStub(hWnd, nIndex, newLong);
+
+ // We already checked this in SetWindowLongHookCheck
+ nsPluginNativeWindowWin * win =
+ (nsPluginNativeWindowWin *)GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+
+ // Hook our subclass back up, just like we do on setwindow.
+ win->SetPrevWindowProc(
+ reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(hWnd, nIndex,
+ reinterpret_cast<LONG_PTR>(PluginWndProc))));
+ return proc;
+}
+
+#ifdef _WIN64
+LONG_PTR WINAPI
+SetWindowLongPtrWHook(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong)
+#else
+LONG WINAPI
+SetWindowLongWHook(HWND hWnd,
+ int nIndex,
+ LONG newLong)
+#endif
+{
+ if (SetWindowLongHookCheck(hWnd, nIndex, newLong))
+ return sUser32SetWindowLongWHookStub(hWnd, nIndex, newLong);
+
+ // Set flash's new subclass to get the result.
+ LONG_PTR proc = sUser32SetWindowLongWHookStub(hWnd, nIndex, newLong);
+
+ // We already checked this in SetWindowLongHookCheck
+ nsPluginNativeWindowWin * win =
+ (nsPluginNativeWindowWin *)GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+
+ // Hook our subclass back up, just like we do on setwindow.
+ win->SetPrevWindowProc(
+ reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(hWnd, nIndex,
+ reinterpret_cast<LONG_PTR>(PluginWndProc))));
+ return proc;
+}
+
+static void
+HookSetWindowLongPtr()
+{
+ sUser32Intercept.Init("user32.dll");
+#ifdef _WIN64
+ if (!sUser32SetWindowLongAHookStub)
+ sUser32Intercept.AddHook("SetWindowLongPtrA",
+ reinterpret_cast<intptr_t>(SetWindowLongPtrAHook),
+ (void**) &sUser32SetWindowLongAHookStub);
+ if (!sUser32SetWindowLongWHookStub)
+ sUser32Intercept.AddHook("SetWindowLongPtrW",
+ reinterpret_cast<intptr_t>(SetWindowLongPtrWHook),
+ (void**) &sUser32SetWindowLongWHookStub);
+#else
+ if (!sUser32SetWindowLongAHookStub)
+ sUser32Intercept.AddHook("SetWindowLongA",
+ reinterpret_cast<intptr_t>(SetWindowLongAHook),
+ (void**) &sUser32SetWindowLongAHookStub);
+ if (!sUser32SetWindowLongWHookStub)
+ sUser32Intercept.AddHook("SetWindowLongW",
+ reinterpret_cast<intptr_t>(SetWindowLongWHook),
+ (void**) &sUser32SetWindowLongWHookStub);
+#endif
+}
+
+/**
+ * nsPluginNativeWindowWin implementation
+ */
+nsPluginNativeWindowWin::nsPluginNativeWindowWin() : nsPluginNativeWindow()
+{
+ // initialize the struct fields
+ window = nullptr;
+ x = 0;
+ y = 0;
+ width = 0;
+ height = 0;
+
+ mPrevWinProc = nullptr;
+ mPluginWinProc = nullptr;
+ mPluginType = nsPluginHost::eSpecialType_None;
+
+ mParentWnd = nullptr;
+ mParentProc = 0;
+
+ if (!sWM_FLASHBOUNCEMSG) {
+ sWM_FLASHBOUNCEMSG = ::RegisterWindowMessage(NS_PLUGIN_CUSTOM_MSG_ID);
+ }
+}
+
+nsPluginNativeWindowWin::~nsPluginNativeWindowWin()
+{
+ // clear weak reference to self to prevent any pending events from
+ // dereferencing this.
+ mWeakRef.forget();
+}
+
+WNDPROC nsPluginNativeWindowWin::GetPrevWindowProc()
+{
+ return mPrevWinProc;
+}
+
+WNDPROC nsPluginNativeWindowWin::GetWindowProc()
+{
+ return mPluginWinProc;
+}
+
+NS_IMETHODIMP PluginWindowEvent::Run()
+{
+ nsPluginNativeWindowWin *win = mPluginWindowRef.get();
+ if (!win)
+ return NS_OK;
+
+ HWND hWnd = GetWnd();
+ if (!hWnd)
+ return NS_OK;
+
+ RefPtr<nsNPAPIPluginInstance> inst;
+ win->GetPluginInstance(inst);
+
+ if (GetMsg() == WM_USER_FLASH) {
+ // XXX Unwind issues related to runnable event callback depth for this
+ // event and destruction of the plugin. (Bug 493601)
+ ::PostMessage(hWnd, sWM_FLASHBOUNCEMSG, GetWParam(), GetLParam());
+ }
+ else {
+ // Currently not used, but added so that processing events here
+ // is more generic.
+ ::CallWindowProc(win->GetWindowProc(),
+ hWnd,
+ GetMsg(),
+ GetWParam(),
+ GetLParam());
+ }
+
+ Clear();
+ return NS_OK;
+}
+
+PluginWindowEvent *
+nsPluginNativeWindowWin::GetPluginWindowEvent(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam)
+{
+ if (!mWeakRef) {
+ mWeakRef = this;
+ if (!mWeakRef)
+ return nullptr;
+ }
+
+ PluginWindowEvent *event;
+
+ // We have the ability to alloc if needed in case in the future some plugin
+ // should post multiple PostMessages. However, this could lead to many
+ // alloc's per second which could become a performance issue. See bug 169247.
+ if (!mCachedPluginWindowEvent)
+ {
+ event = new PluginWindowEvent();
+ mCachedPluginWindowEvent = event;
+ }
+ else if (mCachedPluginWindowEvent->InUse())
+ {
+ event = new PluginWindowEvent();
+ }
+ else
+ {
+ event = mCachedPluginWindowEvent;
+ }
+
+ event->Init(mWeakRef, aWnd, aMsg, aWParam, aLParam);
+ return event;
+}
+
+nsresult nsPluginNativeWindowWin::CallSetWindow(RefPtr<nsNPAPIPluginInstance> &aPluginInstance)
+{
+ // Note, 'window' can be null
+
+ // check the incoming instance, null indicates that window is going away and we are
+ // not interested in subclassing business any more, undo and don't subclass
+ if (!aPluginInstance) {
+ UndoSubclassAndAssociateWindow();
+ // release plugin instance
+ SetPluginInstance(nullptr);
+ nsPluginNativeWindow::CallSetWindow(aPluginInstance);
+ return NS_OK;
+ }
+
+ // check plugin mime type and cache it if it will need special treatment later
+ if (mPluginType == nsPluginHost::eSpecialType_None) {
+ const char* mimetype = nullptr;
+ if (NS_SUCCEEDED(aPluginInstance->GetMIMEType(&mimetype)) && mimetype) {
+ mPluginType = nsPluginHost::GetSpecialType(nsDependentCString(mimetype));
+ }
+ }
+
+ // With e10s we execute in the content process and as such we don't
+ // have access to native widgets. CallSetWindow and skip native widget
+ // subclassing.
+ if (!XRE_IsParentProcess()) {
+ nsPluginNativeWindow::CallSetWindow(aPluginInstance);
+ return NS_OK;
+ }
+
+ if (window) {
+ // grab the widget procedure before the plug-in does a subclass in
+ // setwindow. We'll use this in PluginWndProc for forwarding focus
+ // events to the widget.
+ WNDPROC currentWndProc =
+ (WNDPROC)::GetWindowLongPtr((HWND)window, GWLP_WNDPROC);
+ if (!mPrevWinProc && currentWndProc != PluginWndProc)
+ mPrevWinProc = currentWndProc;
+
+ // PDF plugin v7.0.9, v8.1.3, and v9.0 subclass parent window, bug 531551
+ // V8.2.2 and V9.1 don't have such problem.
+ if (mPluginType == nsPluginHost::eSpecialType_PDF) {
+ HWND parent = ::GetParent((HWND)window);
+ if (mParentWnd != parent) {
+ NS_ASSERTION(!mParentWnd, "Plugin's parent window changed");
+ mParentWnd = parent;
+ mParentProc = ::GetWindowLongPtr(mParentWnd, GWLP_WNDPROC);
+ }
+ }
+ }
+
+ nsPluginNativeWindow::CallSetWindow(aPluginInstance);
+
+ SubclassAndAssociateWindow();
+
+ if (window && mPluginType == nsPluginHost::eSpecialType_Flash &&
+ !GetPropW((HWND)window, L"PluginInstanceParentProperty")) {
+ HookSetWindowLongPtr();
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginNativeWindowWin::SubclassAndAssociateWindow()
+{
+ if (type != NPWindowTypeWindow || !window)
+ return NS_ERROR_FAILURE;
+
+ HWND hWnd = (HWND)window;
+
+ // check if we need to subclass
+ WNDPROC currentWndProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
+ if (currentWndProc == PluginWndProc)
+ return NS_OK;
+
+ // If the plugin reset the subclass, set it back.
+ if (mPluginWinProc) {
+#ifdef DEBUG
+ NS_WARNING("A plugin cleared our subclass - resetting.");
+ if (currentWndProc != mPluginWinProc) {
+ NS_WARNING("Procedures do not match up, discarding old subclass value.");
+ }
+ if (mPrevWinProc && currentWndProc == mPrevWinProc) {
+ NS_WARNING("The new procedure is our widget procedure?");
+ }
+#endif
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PluginWndProc);
+ return NS_OK;
+ }
+
+ LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
+ // Out of process plugins must not have the WS_CLIPCHILDREN style set on their
+ // parent windows or else synchronous paints (via UpdateWindow() and others)
+ // will cause deadlocks.
+ if (::GetPropW(hWnd, L"PluginInstanceParentProperty"))
+ style &= ~WS_CLIPCHILDREN;
+ else
+ style |= WS_CLIPCHILDREN;
+ SetWindowLongPtr(hWnd, GWL_STYLE, style);
+
+ mPluginWinProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PluginWndProc);
+ if (!mPluginWinProc)
+ return NS_ERROR_FAILURE;
+
+ DebugOnly<nsPluginNativeWindowWin *> win = (nsPluginNativeWindowWin *)::GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+ NS_ASSERTION(!win || (win == this), "plugin window already has property and this is not us");
+
+ if (!::SetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION, (HANDLE)this))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+nsresult nsPluginNativeWindowWin::UndoSubclassAndAssociateWindow()
+{
+ // remove window property
+ HWND hWnd = (HWND)window;
+ if (IsWindow(hWnd))
+ ::RemoveProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+
+ // restore the original win proc
+ // but only do this if this were us last time
+ if (mPluginWinProc) {
+ WNDPROC currentWndProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
+ if (currentWndProc == PluginWndProc)
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)mPluginWinProc);
+ mPluginWinProc = nullptr;
+
+ LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
+ style &= ~WS_CLIPCHILDREN;
+ SetWindowLongPtr(hWnd, GWL_STYLE, style);
+ }
+
+ if (mPluginType == nsPluginHost::eSpecialType_Flash && mParentWnd) {
+ ::SetWindowLongPtr(mParentWnd, GWLP_WNDPROC, mParentProc);
+ mParentWnd = nullptr;
+ mParentProc = 0;
+ }
+
+ return NS_OK;
+}
+
+nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+
+ *aPluginNativeWindow = new nsPluginNativeWindowWin();
+ return NS_OK;
+}
+
+nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+ nsPluginNativeWindowWin *p = (nsPluginNativeWindowWin *)aPluginNativeWindow;
+ delete p;
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.cpp b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
new file mode 100644
index 000000000..26e0318e3
--- /dev/null
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -0,0 +1,1424 @@
+/* -*- 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 "nsPluginStreamListenerPeer.h"
+#include "nsIContentPolicy.h"
+#include "nsContentPolicyUtils.h"
+#include "nsIDOMElement.h"
+#include "nsIStreamConverterService.h"
+#include "nsIStreamLoader.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIFileChannel.h"
+#include "nsMimeTypes.h"
+#include "nsISupportsPrimitives.h"
+#include "nsNetCID.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsPluginLogging.h"
+#include "nsIURI.h"
+#include "nsIURL.h"
+#include "nsPluginHost.h"
+#include "nsIByteRangeRequest.h"
+#include "nsIMultiPartChannel.h"
+#include "nsIInputStreamTee.h"
+#include "nsPrintfCString.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIDocument.h"
+#include "nsIWebNavigation.h"
+#include "nsContentUtils.h"
+#include "nsNetUtil.h"
+#include "nsPluginNativeWindow.h"
+#include "GeckoProfiler.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsDataHashtable.h"
+#include "nsNullPrincipal.h"
+
+#define BYTERANGE_REQUEST_CONTEXT 0x01020304
+
+// nsPluginByteRangeStreamListener
+
+class nsPluginByteRangeStreamListener
+ : public nsIStreamListener
+ , public nsIInterfaceRequestor
+{
+public:
+ explicit nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+private:
+ virtual ~nsPluginByteRangeStreamListener();
+
+ nsCOMPtr<nsIStreamListener> mStreamConverter;
+ nsWeakPtr mWeakPtrPluginStreamListenerPeer;
+ bool mRemoveByteRangeRequest;
+};
+
+NS_IMPL_ISUPPORTS(nsPluginByteRangeStreamListener,
+ nsIRequestObserver,
+ nsIStreamListener,
+ nsIInterfaceRequestor)
+
+nsPluginByteRangeStreamListener::nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr)
+{
+ mWeakPtrPluginStreamListenerPeer = aWeakPtr;
+ mRemoveByteRangeRequest = false;
+}
+
+nsPluginByteRangeStreamListener::~nsPluginByteRangeStreamListener()
+{
+ mStreamConverter = nullptr;
+ mWeakPtrPluginStreamListenerPeer = nullptr;
+}
+
+/**
+ * Unwrap any byte-range requests so that we can check whether the base channel
+ * is being tracked properly.
+ */
+static nsCOMPtr<nsIRequest>
+GetBaseRequest(nsIRequest* r)
+{
+ nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(r);
+ if (!mp)
+ return r;
+
+ nsCOMPtr<nsIChannel> base;
+ mp->GetBaseChannel(getter_AddRefs(base));
+ return already_AddRefed<nsIRequest>(base.forget());
+}
+
+NS_IMETHODIMP
+nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
+ if (!finalStreamListener)
+ return NS_ERROR_FAILURE;
+
+ nsPluginStreamListenerPeer *pslp =
+ static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
+
+#ifdef DEBUG
+ nsCOMPtr<nsIRequest> baseRequest = GetBaseRequest(request);
+#endif
+ NS_ASSERTION(pslp->mRequests.IndexOfObject(baseRequest) != -1,
+ "Untracked byte-range request?");
+
+ nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ rv = serv->AsyncConvertData(MULTIPART_BYTERANGES,
+ "*/*",
+ finalStreamListener,
+ nullptr,
+ getter_AddRefs(mStreamConverter));
+ if (NS_SUCCEEDED(rv)) {
+ rv = mStreamConverter->OnStartRequest(request, ctxt);
+ if (NS_SUCCEEDED(rv))
+ return rv;
+ }
+ }
+ mStreamConverter = nullptr;
+
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
+ if (!httpChannel) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t responseCode = 0;
+ rv = httpChannel->GetResponseStatus(&responseCode);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (responseCode != 200) {
+ uint32_t wantsAllNetworkStreams = 0;
+ rv = pslp->GetPluginInstance()->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
+ &wantsAllNetworkStreams);
+ // If the call returned an error code make sure we still use our default value.
+ if (NS_FAILED(rv)) {
+ wantsAllNetworkStreams = 0;
+ }
+
+ if (!wantsAllNetworkStreams){
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // if server cannot continue with byte range (206 status) and sending us whole object (200 status)
+ // reset this seekable stream & try serve it to plugin instance as a file
+ mStreamConverter = finalStreamListener;
+ mRemoveByteRangeRequest = true;
+
+ rv = pslp->ServeStreamAsFile(request, ctxt);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
+ nsresult status)
+{
+ if (!mStreamConverter)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
+ if (!finalStreamListener)
+ return NS_ERROR_FAILURE;
+
+ nsPluginStreamListenerPeer *pslp =
+ static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
+ bool found = pslp->mRequests.RemoveObject(request);
+ if (!found) {
+ NS_ERROR("OnStopRequest received for untracked byte-range request!");
+ }
+
+ if (mRemoveByteRangeRequest) {
+ // remove byte range request from container
+ nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(ctxt);
+ if (container) {
+ uint32_t byteRangeRequest = 0;
+ container->GetData(&byteRangeRequest);
+ if (byteRangeRequest == BYTERANGE_REQUEST_CONTEXT) {
+ // to allow properly finish nsPluginStreamListenerPeer->OnStopRequest()
+ // set it to something that is not the byte range request.
+ container->SetData(0);
+ }
+ } else {
+ NS_WARNING("Bad state of nsPluginByteRangeStreamListener");
+ }
+ }
+
+ return mStreamConverter->OnStopRequest(request, ctxt, status);
+}
+
+// CachedFileHolder
+
+CachedFileHolder::CachedFileHolder(nsIFile* cacheFile)
+: mFile(cacheFile)
+{
+ NS_ASSERTION(mFile, "Empty CachedFileHolder");
+}
+
+CachedFileHolder::~CachedFileHolder()
+{
+ mFile->Remove(false);
+}
+
+void
+CachedFileHolder::AddRef()
+{
+ ++mRefCnt;
+ NS_LOG_ADDREF(this, mRefCnt, "CachedFileHolder", sizeof(*this));
+}
+
+void
+CachedFileHolder::Release()
+{
+ --mRefCnt;
+ NS_LOG_RELEASE(this, mRefCnt, "CachedFileHolder");
+ if (0 == mRefCnt)
+ delete this;
+}
+
+
+NS_IMETHODIMP
+nsPluginByteRangeStreamListener::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
+ nsIInputStream *inStr,
+ uint64_t sourceOffset,
+ uint32_t count)
+{
+ if (!mStreamConverter)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
+ if (!finalStreamListener)
+ return NS_ERROR_FAILURE;
+
+ return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
+}
+
+NS_IMETHODIMP
+nsPluginByteRangeStreamListener::GetInterface(const nsIID& aIID, void** result)
+{
+ // Forward interface requests to our parent
+ nsCOMPtr<nsIInterfaceRequestor> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
+ if (!finalStreamListener)
+ return NS_ERROR_FAILURE;
+
+ return finalStreamListener->GetInterface(aIID, result);
+}
+
+// nsPluginStreamListenerPeer
+
+NS_IMPL_ISUPPORTS(nsPluginStreamListenerPeer,
+ nsIStreamListener,
+ nsIRequestObserver,
+ nsIHttpHeaderVisitor,
+ nsISupportsWeakReference,
+ nsIInterfaceRequestor,
+ nsIChannelEventSink)
+
+nsPluginStreamListenerPeer::nsPluginStreamListenerPeer()
+{
+ mStreamType = NP_NORMAL;
+ mStartBinding = false;
+ mAbort = false;
+ mRequestFailed = false;
+
+ mPendingRequests = 0;
+ mHaveFiredOnStartRequest = false;
+ mDataForwardToRequest = nullptr;
+
+ mUseLocalCache = false;
+ mSeekable = false;
+ mModified = 0;
+ mStreamOffset = 0;
+ mStreamComplete = 0;
+}
+
+nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer()
+{
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginStreamListenerPeer::dtor this=%p, url=%s\n",this, mURLSpec.get()));
+#endif
+
+ if (mPStreamListener) {
+ mPStreamListener->SetStreamListenerPeer(nullptr);
+ }
+
+ // close FD of mFileCacheOutputStream if it's still open
+ // or we won't be able to remove the cache file
+ if (mFileCacheOutputStream)
+ mFileCacheOutputStream = nullptr;
+
+ delete mDataForwardToRequest;
+
+ if (mPluginInstance)
+ mPluginInstance->FileCachedStreamListeners()->RemoveElement(this);
+}
+
+// Called as a result of GetURL and PostURL, or by the host in the case of the
+// initial plugin stream.
+nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL,
+ nsNPAPIPluginInstance *aInstance,
+ nsNPAPIPluginStreamListener* aListener)
+{
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n",
+ aInstance, aURL ? aURL->GetSpecOrDefault().get() : ""));
+
+ PR_LogFlush();
+#endif
+
+ // Not gonna work out
+ if (!aInstance) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mURL = aURL;
+
+ NS_ASSERTION(mPluginInstance == nullptr,
+ "nsPluginStreamListenerPeer::Initialize mPluginInstance != nullptr");
+ mPluginInstance = aInstance;
+
+ // If the plugin did not request this stream, e.g. the initial stream, we wont
+ // have a nsNPAPIPluginStreamListener yet - this will be handled by
+ // SetUpStreamListener
+ if (aListener) {
+ mPStreamListener = aListener;
+ mPStreamListener->SetStreamListenerPeer(this);
+ }
+
+ mPendingRequests = 1;
+
+ mDataForwardToRequest = new nsDataHashtable<nsUint32HashKey, uint32_t>();
+
+ return NS_OK;
+}
+
+// SetupPluginCacheFile is called if we have to save the stream to disk.
+//
+// These files will be deleted when the host is destroyed.
+//
+// TODO? What if we fill up the the dest dir?
+nsresult
+nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel)
+{
+ nsresult rv = NS_OK;
+
+ bool useExistingCacheFile = false;
+ RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+
+ // Look for an existing cache file for the URI.
+ nsTArray< RefPtr<nsNPAPIPluginInstance> > *instances = pluginHost->InstanceArray();
+ for (uint32_t i = 0; i < instances->Length(); i++) {
+ // most recent streams are at the end of list
+ nsTArray<nsPluginStreamListenerPeer*> *streamListeners = instances->ElementAt(i)->FileCachedStreamListeners();
+ for (int32_t i = streamListeners->Length() - 1; i >= 0; --i) {
+ nsPluginStreamListenerPeer *lp = streamListeners->ElementAt(i);
+ if (lp && lp->mLocalCachedFileHolder) {
+ useExistingCacheFile = lp->UseExistingPluginCacheFile(this);
+ if (useExistingCacheFile) {
+ mLocalCachedFileHolder = lp->mLocalCachedFileHolder;
+ break;
+ }
+ }
+ if (useExistingCacheFile)
+ break;
+ }
+ }
+
+ // Create a new cache file if one could not be found.
+ if (!useExistingCacheFile) {
+ nsCOMPtr<nsIFile> pluginTmp;
+ rv = nsPluginHost::GetPluginTempDir(getter_AddRefs(pluginTmp));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Get the filename from the channel
+ nsCOMPtr<nsIURI> uri;
+ rv = channel->GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+ if (!url)
+ return NS_ERROR_FAILURE;
+
+ nsAutoCString filename;
+ url->GetFileName(filename);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Create a file to save our stream into. Should we scramble the name?
+ filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0);
+ rv = pluginTmp->AppendNative(filename);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Yes, make it unique.
+ rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // create a file output stream to write to...
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // save the file.
+ mLocalCachedFileHolder = new CachedFileHolder(pluginTmp);
+ }
+
+ // add this listenerPeer to list of stream peers for this instance
+ mPluginInstance->FileCachedStreamListeners()->AppendElement(this);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request,
+ nsISupports* aContext)
+{
+ nsresult rv = NS_OK;
+ PROFILER_LABEL("nsPluginStreamListenerPeer", "OnStartRequest",
+ js::ProfileEntry::Category::OTHER);
+
+ nsCOMPtr<nsIRequest> baseRequest = GetBaseRequest(request);
+ if (mRequests.IndexOfObject(baseRequest) == -1) {
+ NS_ASSERTION(mRequests.Count() == 0,
+ "Only our initial stream should be unknown!");
+ TrackRequest(request);
+ }
+
+ if (mHaveFiredOnStartRequest) {
+ return NS_OK;
+ }
+
+ mHaveFiredOnStartRequest = true;
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
+
+ // deal with 404 (Not Found) HTTP response,
+ // just return, this causes the request to be ignored.
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ if (httpChannel) {
+ uint32_t responseCode = 0;
+ rv = httpChannel->GetResponseStatus(&responseCode);
+ if (NS_FAILED(rv)) {
+ // NPP_Notify() will be called from OnStopRequest
+ // in nsNPAPIPluginStreamListener::CleanUpStream
+ // return error will cancel this request
+ // ...and we also need to tell the plugin that
+ mRequestFailed = true;
+ return NS_ERROR_FAILURE;
+ }
+
+ if (responseCode > 206) { // not normal
+ uint32_t wantsAllNetworkStreams = 0;
+
+ // We don't always have an instance here already, but if we do, check
+ // to see if it wants all streams.
+ if (mPluginInstance) {
+ rv = mPluginInstance->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
+ &wantsAllNetworkStreams);
+ // If the call returned an error code make sure we still use our default value.
+ if (NS_FAILED(rv)) {
+ wantsAllNetworkStreams = 0;
+ }
+ }
+
+ if (!wantsAllNetworkStreams) {
+ mRequestFailed = true;
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ nsAutoCString contentType;
+ rv = channel->GetContentType(contentType);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Check ShouldProcess with content policy
+ RefPtr<nsPluginInstanceOwner> owner;
+ if (mPluginInstance) {
+ owner = mPluginInstance->GetOwner();
+ }
+ nsCOMPtr<nsIDOMElement> element;
+ nsCOMPtr<nsIDocument> doc;
+ if (owner) {
+ owner->GetDOMElement(getter_AddRefs(element));
+ owner->GetDocument(getter_AddRefs(doc));
+ }
+ nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
+
+ int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+ rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
+ mURL,
+ principal,
+ element,
+ contentType,
+ nullptr,
+ &shouldLoad);
+ if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+ mRequestFailed = true;
+ return NS_ERROR_CONTENT_BLOCKED;
+ }
+
+ // Get the notification callbacks from the channel and save it as
+ // week ref we'll use it in nsPluginStreamInfo::RequestRead() when
+ // we'll create channel for byte range request.
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ channel->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (callbacks)
+ mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks);
+
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ channel->GetLoadGroup(getter_AddRefs(loadGroup));
+ if (loadGroup)
+ mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup);
+
+ int64_t length;
+ rv = channel->GetContentLength(&length);
+
+ // it's possible for the server to not send a Content-Length.
+ // we should still work in this case.
+ if (NS_FAILED(rv) || length < 0 || length > UINT32_MAX) {
+ // check out if this is file channel
+ nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel);
+ if (fileChannel) {
+ // file does not exist
+ mRequestFailed = true;
+ return NS_ERROR_FAILURE;
+ }
+ mLength = 0;
+ }
+ else {
+ mLength = uint32_t(length);
+ }
+
+ nsCOMPtr<nsIURI> aURL;
+ rv = channel->GetURI(getter_AddRefs(aURL));
+ if (NS_FAILED(rv))
+ return rv;
+
+ aURL->GetSpec(mURLSpec);
+
+ if (!contentType.IsEmpty())
+ mContentType = contentType;
+
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY,
+ ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n",
+ this, request, contentType.get(), mURLSpec.get()));
+
+ PR_LogFlush();
+#endif
+
+ // Set up the stream listener...
+ rv = SetUpStreamListener(request, aURL);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
+ nsISupports* aContext,
+ int64_t aProgress,
+ int64_t aProgressMax)
+{
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest *request,
+ nsISupports* aContext,
+ nsresult aStatus,
+ const char16_t* aStatusArg)
+{
+ return NS_OK;
+}
+
+nsresult
+nsPluginStreamListenerPeer::GetContentType(char** result)
+{
+ *result = const_cast<char*>(mContentType.get());
+ return NS_OK;
+}
+
+
+nsresult
+nsPluginStreamListenerPeer::IsSeekable(bool* result)
+{
+ *result = mSeekable;
+ return NS_OK;
+}
+
+nsresult
+nsPluginStreamListenerPeer::GetLength(uint32_t* result)
+{
+ *result = mLength;
+ return NS_OK;
+}
+
+nsresult
+nsPluginStreamListenerPeer::GetLastModified(uint32_t* result)
+{
+ *result = mModified;
+ return NS_OK;
+}
+
+nsresult
+nsPluginStreamListenerPeer::GetURL(const char** result)
+{
+ *result = mURLSpec.get();
+ return NS_OK;
+}
+
+void
+nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACString &rangeRequest,
+ int32_t *numRequests)
+{
+ rangeRequest.Truncate();
+ *numRequests = 0;
+ //the string should look like this: bytes=500-700,601-999
+ if (!aRangeList)
+ return;
+
+ int32_t requestCnt = 0;
+ nsAutoCString string("bytes=");
+
+ for (NPByteRange * range = aRangeList; range != nullptr; range = range->next) {
+ // XXX zero length?
+ if (!range->length)
+ continue;
+
+ // XXX needs to be fixed for negative offsets
+ string.AppendInt(range->offset);
+ string.Append('-');
+ string.AppendInt(range->offset + range->length - 1);
+ if (range->next)
+ string.Append(',');
+
+ requestCnt++;
+ }
+
+ // get rid of possible trailing comma
+ string.Trim(",", false);
+
+ rangeRequest = string;
+ *numRequests = requestCnt;
+ return;
+}
+
+// XXX: Converting the channel within nsPluginStreamListenerPeer
+// to use asyncOpen2() and do not want to touch the fragile logic
+// of byte range requests. Hence we just introduce this lightweight
+// wrapper to proxy the context.
+class PluginContextProxy final : public nsIStreamListener
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ PluginContextProxy(nsIStreamListener *aListener, nsISupports* aContext)
+ : mListener(aListener)
+ , mContext(aContext)
+ {
+ MOZ_ASSERT(aListener);
+ MOZ_ASSERT(aContext);
+ }
+
+ NS_IMETHOD
+ OnDataAvailable(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsIInputStream *aIStream,
+ uint64_t aSourceOffset,
+ uint32_t aLength) override
+ {
+ // Proxy OnDataAvailable using the internal context
+ return mListener->OnDataAvailable(aRequest,
+ mContext,
+ aIStream,
+ aSourceOffset,
+ aLength);
+ }
+
+ NS_IMETHOD
+ OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override
+ {
+ // Proxy OnStartRequest using the internal context
+ return mListener->OnStartRequest(aRequest, mContext);
+ }
+
+ NS_IMETHOD
+ OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+ nsresult aStatusCode) override
+ {
+ // Proxy OnStopRequest using the inernal context
+ return mListener->OnStopRequest(aRequest,
+ mContext,
+ aStatusCode);
+ }
+
+private:
+ ~PluginContextProxy() {}
+ nsCOMPtr<nsIStreamListener> mListener;
+ nsCOMPtr<nsISupports> mContext;
+};
+
+NS_IMPL_ISUPPORTS(PluginContextProxy, nsIStreamListener)
+
+nsresult
+nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
+{
+ nsAutoCString rangeString;
+ int32_t numRequests;
+
+ MakeByteRangeString(rangeList, rangeString, &numRequests);
+
+ if (numRequests == 0)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = NS_OK;
+
+ RefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner();
+ nsCOMPtr<nsIDOMElement> element;
+ nsCOMPtr<nsIDocument> doc;
+ if (owner) {
+ rv = owner->GetDOMElement(getter_AddRefs(element));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = owner->GetDocument(getter_AddRefs(doc));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks);
+ nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup);
+
+ nsCOMPtr<nsIChannel> channel;
+ nsCOMPtr<nsINode> requestingNode(do_QueryInterface(element));
+ if (requestingNode) {
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ mURL,
+ requestingNode,
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ loadGroup,
+ callbacks,
+ nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
+ }
+ else {
+ // In this else branch we really don't know where the load is coming
+ // from. Let's fall back to using the SystemPrincipal for such Plugins.
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ mURL,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ loadGroup,
+ callbacks,
+ nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
+ }
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ if (!httpChannel)
+ return NS_ERROR_FAILURE;
+
+ httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
+
+ mAbort = true; // instruct old stream listener to cancel
+ // the request on the next ODA.
+
+ nsCOMPtr<nsIStreamListener> converter;
+
+ if (numRequests == 1) {
+ converter = this;
+ // set current stream offset equal to the first offset in the range list
+ // it will work for single byte range request
+ // for multy range we'll reset it in ODA
+ SetStreamOffset(rangeList->offset);
+ } else {
+ nsWeakPtr weakpeer =
+ do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this));
+ converter = new nsPluginByteRangeStreamListener(weakpeer);
+ }
+
+ mPendingRequests += numRequests;
+
+ nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = container->SetData(BYTERANGE_REQUEST_CONTEXT);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<PluginContextProxy> pluginContextProxy =
+ new PluginContextProxy(converter, container);
+ rv = channel->AsyncOpen2(pluginContextProxy);
+ NS_ENSURE_SUCCESS(rv, rv);
+ TrackRequest(channel);
+ return NS_OK;
+}
+
+nsresult
+nsPluginStreamListenerPeer::GetStreamOffset(int32_t* result)
+{
+ *result = mStreamOffset;
+ return NS_OK;
+}
+
+nsresult
+nsPluginStreamListenerPeer::SetStreamOffset(int32_t value)
+{
+ mStreamOffset = value;
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
+ nsISupports* aContext)
+{
+ if (!mPluginInstance)
+ return NS_ERROR_FAILURE;
+
+ // mPluginInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up
+ mPluginInstance->Stop();
+ mPluginInstance->Start();
+ RefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner();
+ if (owner) {
+ NPWindow* window = nullptr;
+ owner->GetWindow(window);
+#if (MOZ_WIDGET_GTK == 2)
+ // Should call GetPluginPort() here.
+ // This part is copied from nsPluginInstanceOwner::GetPluginPort().
+ nsCOMPtr<nsIWidget> widget;
+ ((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget));
+ if (widget) {
+ window->window = widget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
+ }
+#endif
+ owner->CallSetWindow();
+ }
+
+ mSeekable = false;
+ mPStreamListener->OnStartBinding(this);
+ mStreamOffset = 0;
+
+ // force the plugin to use stream as file
+ mStreamType = NP_ASFILE;
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ if (channel) {
+ SetupPluginCacheFile(channel);
+ }
+
+ // unset mPendingRequests
+ mPendingRequests = 0;
+
+ return NS_OK;
+}
+
+bool
+nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi)
+{
+ NS_ENSURE_TRUE(psi, false);
+
+ if (psi->mLength == mLength &&
+ psi->mModified == mModified &&
+ mStreamComplete &&
+ mURLSpec.Equals(psi->mURLSpec))
+ {
+ return true;
+ }
+ return false;
+}
+
+NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
+ nsISupports* aContext,
+ nsIInputStream *aIStream,
+ uint64_t sourceOffset,
+ uint32_t aLength)
+{
+ nsCOMPtr<nsIRequest> baseRequest = GetBaseRequest(request);
+ if (mRequests.IndexOfObject(baseRequest) == -1) {
+ MOZ_ASSERT(false, "Received OnDataAvailable for untracked request.");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (mRequestFailed)
+ return NS_ERROR_FAILURE;
+
+ if (mAbort) {
+ uint32_t byteRangeRequest = 0; // set it to something that is not the byte range request.
+ nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
+ if (container)
+ container->GetData(&byteRangeRequest);
+
+ if (byteRangeRequest != BYTERANGE_REQUEST_CONTEXT) {
+ // this is not one of our range requests
+ mAbort = false;
+ return NS_BINDING_ABORTED;
+ }
+ }
+
+ nsresult rv = NS_OK;
+
+ if (!mPStreamListener)
+ return NS_ERROR_FAILURE;
+
+ const char * url = nullptr;
+ GetURL(&url);
+
+ PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%llu, length=%u, url=%s\n",
+ this, request, sourceOffset, aLength, url ? url : "no url set"));
+
+ // if the plugin has requested an AsFileOnly stream, then don't
+ // call OnDataAvailable
+ if (mStreamType != NP_ASFILEONLY) {
+ // get the absolute offset of the request, if one exists.
+ nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
+ if (brr) {
+ if (!mDataForwardToRequest)
+ return NS_ERROR_FAILURE;
+
+ int64_t absoluteOffset64 = 0;
+ brr->GetStartRange(&absoluteOffset64);
+
+ // XXX handle 64-bit for real
+ int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
+
+ // we need to track how much data we have forwarded to the
+ // plugin.
+
+ // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130
+ //
+ // Why couldn't this be tracked on the plugin info, and not in a
+ // *hash table*?
+ int32_t amtForwardToPlugin = mDataForwardToRequest->Get(absoluteOffset);
+ mDataForwardToRequest->Put(absoluteOffset, (amtForwardToPlugin + aLength));
+
+ SetStreamOffset(absoluteOffset + amtForwardToPlugin);
+ }
+
+ nsCOMPtr<nsIInputStream> stream = aIStream;
+
+ // if we are caching the file ourselves to disk, we want to 'tee' off
+ // the data as the plugin read from the stream. We do this by the magic
+ // of an input stream tee.
+
+ if (mFileCacheOutputStream) {
+ rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ rv = mPStreamListener->OnDataAvailable(this,
+ stream,
+ aLength);
+
+ // if a plugin returns an error, the peer must kill the stream
+ // else the stream and PluginStreamListener leak
+ if (NS_FAILED(rv))
+ request->Cancel(rv);
+ }
+ else
+ {
+ // if we don't read from the stream, OnStopRequest will never be called
+ char* buffer = new char[aLength];
+ uint32_t amountRead, amountWrote = 0;
+ rv = aIStream->Read(buffer, aLength, &amountRead);
+
+ // if we are caching this to disk ourselves, lets write the bytes out.
+ if (mFileCacheOutputStream) {
+ while (amountWrote < amountRead && NS_SUCCEEDED(rv)) {
+ rv = mFileCacheOutputStream->Write(buffer, amountRead, &amountWrote);
+ }
+ }
+ delete [] buffer;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
+ nsISupports* aContext,
+ nsresult aStatus)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(request);
+ if (!mp) {
+ bool found = mRequests.RemoveObject(request);
+ if (!found) {
+ NS_ERROR("Received OnStopRequest for untracked request.");
+ }
+ }
+
+ PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
+ this, aStatus, request));
+
+ // for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return.
+ nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
+ if (brr) {
+ int64_t absoluteOffset64 = 0;
+ brr->GetStartRange(&absoluteOffset64);
+ // XXX support 64-bit offsets
+ int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
+
+ // remove the request from our data forwarding count hash.
+ mDataForwardToRequest->Remove(absoluteOffset);
+
+
+ PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ (" ::OnStopRequest for ByteRangeRequest Started=%d\n",
+ absoluteOffset));
+ } else {
+ // if this is not byte range request and
+ // if we are writting the stream to disk ourselves,
+ // close & tear it down here
+ mFileCacheOutputStream = nullptr;
+ }
+
+ // if we still have pending stuff to do, lets not close the plugin socket.
+ if (--mPendingRequests > 0)
+ return NS_OK;
+
+ // we keep our connections around...
+ nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
+ if (container) {
+ uint32_t byteRangeRequest = 0; // something other than the byte range request.
+ container->GetData(&byteRangeRequest);
+ if (byteRangeRequest == BYTERANGE_REQUEST_CONTEXT) {
+ // this is one of our range requests
+ return NS_OK;
+ }
+ }
+
+ if (!mPStreamListener)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ if (!channel)
+ return NS_ERROR_FAILURE;
+ // Set the content type to ensure we don't pass null to the plugin
+ nsAutoCString aContentType;
+ rv = channel->GetContentType(aContentType);
+ if (NS_FAILED(rv) && !mRequestFailed)
+ return rv;
+
+ if (!aContentType.IsEmpty())
+ mContentType = aContentType;
+
+ // set error status if stream failed so we notify the plugin
+ if (mRequestFailed)
+ aStatus = NS_ERROR_FAILURE;
+
+ if (NS_FAILED(aStatus)) {
+ // on error status cleanup the stream
+ // and return w/o OnFileAvailable()
+ mPStreamListener->OnStopBinding(this, aStatus);
+ return NS_OK;
+ }
+
+ // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
+ if (mStreamType >= NP_ASFILE) {
+ nsCOMPtr<nsIFile> localFile;
+ if (mLocalCachedFileHolder)
+ localFile = mLocalCachedFileHolder->file();
+ else {
+ // see if it is a file channel.
+ nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
+ if (fileChannel) {
+ fileChannel->GetFile(getter_AddRefs(localFile));
+ }
+ }
+
+ if (localFile) {
+ OnFileAvailable(localFile);
+ }
+ }
+
+ if (mStartBinding) {
+ // On start binding has been called
+ mPStreamListener->OnStopBinding(this, aStatus);
+ } else {
+ // OnStartBinding hasn't been called, so complete the action.
+ mPStreamListener->OnStartBinding(this);
+ mPStreamListener->OnStopBinding(this, aStatus);
+ }
+
+ if (NS_SUCCEEDED(aStatus)) {
+ mStreamComplete = true;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
+ nsIURI* aURL)
+{
+ nsresult rv = NS_OK;
+
+ // If we don't yet have a stream listener, we need to get
+ // one from the plugin.
+ // NOTE: this should only happen when a stream was NOT created
+ // with GetURL or PostURL (i.e. it's the initial stream we
+ // send to the plugin as determined by the SRC or DATA attribute)
+ if (!mPStreamListener) {
+ if (!mPluginInstance) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsNPAPIPluginStreamListener> streamListener;
+ rv = mPluginInstance->NewStreamListener(nullptr, nullptr,
+ getter_AddRefs(streamListener));
+ if (NS_FAILED(rv) || !streamListener) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mPStreamListener = static_cast<nsNPAPIPluginStreamListener*>(streamListener.get());
+ }
+
+ mPStreamListener->SetStreamListenerPeer(this);
+
+ // get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
+
+ /*
+ * Assumption
+ * By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
+ * called, all the headers have been read.
+ */
+ if (httpChannel) {
+ // Reassemble the HTTP response status line and provide it to our
+ // listener. Would be nice if we could get the raw status line,
+ // but nsIHttpChannel doesn't currently provide that.
+ // Status code: required; the status line isn't useful without it.
+ uint32_t statusNum;
+ if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&statusNum)) &&
+ statusNum < 1000) {
+ // HTTP version: provide if available. Defaults to empty string.
+ nsCString ver;
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
+ do_QueryInterface(channel);
+ if (httpChannelInternal) {
+ uint32_t major, minor;
+ if (NS_SUCCEEDED(httpChannelInternal->GetResponseVersion(&major,
+ &minor))) {
+ ver = nsPrintfCString("/%lu.%lu", major, minor);
+ }
+ }
+
+ // Status text: provide if available. Defaults to "OK".
+ nsCString statusText;
+ if (NS_FAILED(httpChannel->GetResponseStatusText(statusText))) {
+ statusText = "OK";
+ }
+
+ // Assemble everything and pass to listener.
+ nsPrintfCString status("HTTP%s %lu %s", ver.get(), statusNum,
+ statusText.get());
+ static_cast<nsIHTTPHeaderListener*>(mPStreamListener)->StatusLine(status.get());
+ }
+
+ // Also provide all HTTP response headers to our listener.
+ httpChannel->VisitResponseHeaders(this);
+
+ mSeekable = false;
+ // first we look for a content-encoding header. If we find one, we tell the
+ // plugin that stream is not seekable, because the plugin always sees
+ // uncompressed data, so it can't make meaningful range requests on a
+ // compressed entity. Also, we force the plugin to use
+ // nsPluginStreamType_AsFile stream type and we have to save decompressed
+ // file into local plugin cache, because necko cache contains original
+ // compressed file.
+ nsAutoCString contentEncoding;
+ if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"),
+ contentEncoding))) {
+ mUseLocalCache = true;
+ } else {
+ // set seekability (seekable if the stream has a known length and if the
+ // http server accepts byte ranges).
+ uint32_t length;
+ GetLength(&length);
+ if (length) {
+ nsAutoCString range;
+ if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("accept-ranges"), range)) &&
+ range.Equals(NS_LITERAL_CSTRING("bytes"), nsCaseInsensitiveCStringComparator())) {
+ mSeekable = true;
+ }
+ }
+ }
+
+ // we require a content len
+ // get Last-Modified header for plugin info
+ nsAutoCString lastModified;
+ if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), lastModified)) &&
+ !lastModified.IsEmpty()) {
+ PRTime time64;
+ PR_ParseTimeString(lastModified.get(), true, &time64); //convert string time to integer time
+
+ // Convert PRTime to unix-style time_t, i.e. seconds since the epoch
+ double fpTime = double(time64);
+ mModified = (uint32_t)(fpTime * 1e-6 + 0.5);
+ }
+ }
+
+ MOZ_ASSERT(!mRequest);
+ mRequest = request;
+
+ rv = mPStreamListener->OnStartBinding(this);
+
+ mStartBinding = true;
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ int32_t streamType = NP_NORMAL;
+ mPStreamListener->GetStreamType(&streamType);
+
+ if (streamType != STREAM_TYPE_UNKNOWN) {
+ OnStreamTypeSet(streamType);
+ }
+
+ return NS_OK;
+}
+
+void
+nsPluginStreamListenerPeer::OnStreamTypeSet(const int32_t aStreamType)
+{
+ MOZ_ASSERT(aStreamType != STREAM_TYPE_UNKNOWN);
+ MOZ_ASSERT(mRequest);
+ mStreamType = aStreamType;
+ if (!mUseLocalCache && mStreamType >= NP_ASFILE) {
+ // check it out if this is not a file channel.
+ nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(mRequest);
+ if (!fileChannel) {
+ mUseLocalCache = true;
+ }
+ }
+
+ if (mUseLocalCache) {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRequest);
+ SetupPluginCacheFile(channel);
+ }
+}
+
+nsresult
+nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile)
+{
+ nsresult rv;
+ if (!mPStreamListener)
+ return NS_ERROR_FAILURE;
+
+ nsAutoCString path;
+ rv = aFile->GetNativePath(path);
+ if (NS_FAILED(rv)) return rv;
+
+ if (path.IsEmpty()) {
+ NS_WARNING("empty path");
+ return NS_OK;
+ }
+
+ rv = mPStreamListener->OnFileAvailable(this, path.get());
+ return rv;
+}
+
+NS_IMETHODIMP
+nsPluginStreamListenerPeer::VisitHeader(const nsACString &header, const nsACString &value)
+{
+ return mPStreamListener->NewResponseHeader(PromiseFlatCString(header).get(),
+ PromiseFlatCString(value).get());
+}
+
+nsresult
+nsPluginStreamListenerPeer::GetInterfaceGlobal(const nsIID& aIID, void** result)
+{
+ if (!mPluginInstance) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner();
+ if (!owner) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDocument> doc;
+ nsresult rv = owner->GetDocument(getter_AddRefs(doc));
+ if (NS_FAILED(rv) || !doc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPIDOMWindowOuter *window = doc->GetWindow();
+ if (!window) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
+ nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(webNav);
+ if (!ir) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return ir->GetInterface(aIID, result);
+}
+
+NS_IMETHODIMP
+nsPluginStreamListenerPeer::GetInterface(const nsIID& aIID, void** result)
+{
+ // Provide nsIChannelEventSink ourselves, otherwise let our document's
+ // script global object owner provide the interface.
+ if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
+ return QueryInterface(aIID, result);
+ }
+
+ return GetInterfaceGlobal(aIID, result);
+}
+
+/**
+ * Proxy class which forwards async redirect notifications back to the necko
+ * callback, keeping nsPluginStreamListenerPeer::mRequests in sync with
+ * which channel is active.
+ */
+class ChannelRedirectProxyCallback : public nsIAsyncVerifyRedirectCallback
+{
+public:
+ ChannelRedirectProxyCallback(nsPluginStreamListenerPeer* listener,
+ nsIAsyncVerifyRedirectCallback* parent,
+ nsIChannel* oldChannel,
+ nsIChannel* newChannel)
+ : mWeakListener(do_GetWeakReference(static_cast<nsIStreamListener*>(listener)))
+ , mParent(parent)
+ , mOldChannel(oldChannel)
+ , mNewChannel(newChannel)
+ {
+ }
+
+ ChannelRedirectProxyCallback() {}
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult) override
+ {
+ if (NS_SUCCEEDED(aResult)) {
+ nsCOMPtr<nsIStreamListener> listener = do_QueryReferent(mWeakListener);
+ if (listener)
+ static_cast<nsPluginStreamListenerPeer*>(listener.get())->ReplaceRequest(mOldChannel, mNewChannel);
+ }
+ return mParent->OnRedirectVerifyCallback(aResult);
+ }
+
+private:
+ virtual ~ChannelRedirectProxyCallback() {}
+
+ nsWeakPtr mWeakListener;
+ nsCOMPtr<nsIAsyncVerifyRedirectCallback> mParent;
+ nsCOMPtr<nsIChannel> mOldChannel;
+ nsCOMPtr<nsIChannel> mNewChannel;
+};
+
+NS_IMPL_ISUPPORTS(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback)
+
+
+NS_IMETHODIMP
+nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
+ uint32_t flags, nsIAsyncVerifyRedirectCallback* callback)
+{
+ // Disallow redirects if we don't have a stream listener.
+ if (!mPStreamListener) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback =
+ new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel);
+
+ // Give NPAPI a chance to control redirects.
+ bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback);
+ if (notificationHandled) {
+ return NS_OK;
+ }
+
+ // Don't allow cross-origin 307 POST redirects.
+ nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel));
+ if (oldHttpChannel) {
+ uint32_t responseStatus;
+ nsresult rv = oldHttpChannel->GetResponseStatus(&responseStatus);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (responseStatus == 307) {
+ nsAutoCString method;
+ rv = oldHttpChannel->GetRequestMethod(method);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (method.EqualsLiteral("POST")) {
+ rv = nsContentUtils::CheckSameOrigin(oldChannel, newChannel);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+ }
+
+ // Fall back to channel event sink for window.
+ nsCOMPtr<nsIChannelEventSink> channelEventSink;
+ nsresult rv = GetInterfaceGlobal(NS_GET_IID(nsIChannelEventSink), getter_AddRefs(channelEventSink));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return channelEventSink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, proxyCallback);
+}
diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.h b/dom/plugins/base/nsPluginStreamListenerPeer.h
new file mode 100644
index 000000000..32f629b40
--- /dev/null
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.h
@@ -0,0 +1,185 @@
+/* -*- 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/. */
+
+#ifndef nsPluginStreamListenerPeer_h_
+#define nsPluginStreamListenerPeer_h_
+
+#include "nscore.h"
+#include "nsIFile.h"
+#include "nsIStreamListener.h"
+#include "nsIProgressEventSink.h"
+#include "nsIHttpHeaderVisitor.h"
+#include "nsWeakReference.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIChannelEventSink.h"
+
+class nsIChannel;
+
+/**
+ * When a plugin requests opens multiple requests to the same URL and
+ * the request must be satified by saving a file to disk, each stream
+ * listener holds a reference to the backing file: the file is only removed
+ * when all the listeners are done.
+ */
+class CachedFileHolder
+{
+public:
+ explicit CachedFileHolder(nsIFile* cacheFile);
+ ~CachedFileHolder();
+
+ void AddRef();
+ void Release();
+
+ nsIFile* file() const { return mFile; }
+
+private:
+ nsAutoRefCnt mRefCnt;
+ nsCOMPtr<nsIFile> mFile;
+};
+
+class nsPluginStreamListenerPeer : public nsIStreamListener,
+public nsIProgressEventSink,
+public nsIHttpHeaderVisitor,
+public nsSupportsWeakReference,
+public nsIInterfaceRequestor,
+public nsIChannelEventSink
+{
+ virtual ~nsPluginStreamListenerPeer();
+
+public:
+ nsPluginStreamListenerPeer();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROGRESSEVENTSINK
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIHTTPHEADERVISITOR
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSICHANNELEVENTSINK
+
+ // Called by RequestRead
+ void
+ MakeByteRangeString(NPByteRange* aRangeList, nsACString &string, int32_t *numRequests);
+
+ bool UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi);
+
+ // Called by GetURL and PostURL (via NewStream) or by the host in the case of
+ // the initial plugin stream.
+ nsresult Initialize(nsIURI *aURL,
+ nsNPAPIPluginInstance *aInstance,
+ nsNPAPIPluginStreamListener *aListener);
+
+ nsresult OnFileAvailable(nsIFile* aFile);
+
+ nsresult ServeStreamAsFile(nsIRequest *request, nsISupports *ctxt);
+
+ nsNPAPIPluginInstance *GetPluginInstance() { return mPluginInstance; }
+
+ nsresult RequestRead(NPByteRange* rangeList);
+ nsresult GetLength(uint32_t* result);
+ nsresult GetURL(const char** result);
+ nsresult GetLastModified(uint32_t* result);
+ nsresult IsSeekable(bool* result);
+ nsresult GetContentType(char** result);
+ nsresult GetStreamOffset(int32_t* result);
+ nsresult SetStreamOffset(int32_t value);
+
+ void TrackRequest(nsIRequest* request)
+ {
+ mRequests.AppendObject(request);
+ }
+
+ void ReplaceRequest(nsIRequest* oldRequest, nsIRequest* newRequest)
+ {
+ int32_t i = mRequests.IndexOfObject(oldRequest);
+ if (i == -1) {
+ NS_ASSERTION(mRequests.Count() == 0,
+ "Only our initial stream should be unknown!");
+ mRequests.AppendObject(oldRequest);
+ }
+ else {
+ mRequests.ReplaceObjectAt(newRequest, i);
+ }
+ }
+
+ void CancelRequests(nsresult status)
+ {
+ // Copy the array to avoid modification during the loop.
+ nsCOMArray<nsIRequest> requestsCopy(mRequests);
+ for (int32_t i = 0; i < requestsCopy.Count(); ++i)
+ requestsCopy[i]->Cancel(status);
+ }
+
+ void SuspendRequests() {
+ nsCOMArray<nsIRequest> requestsCopy(mRequests);
+ for (int32_t i = 0; i < requestsCopy.Count(); ++i)
+ requestsCopy[i]->Suspend();
+ }
+
+ void ResumeRequests() {
+ nsCOMArray<nsIRequest> requestsCopy(mRequests);
+ for (int32_t i = 0; i < requestsCopy.Count(); ++i)
+ requestsCopy[i]->Resume();
+ }
+
+ // Called by nsNPAPIPluginStreamListener
+ void OnStreamTypeSet(const int32_t aStreamType);
+
+ enum {
+ STREAM_TYPE_UNKNOWN = UINT16_MAX
+ };
+
+private:
+ nsresult SetUpStreamListener(nsIRequest* request, nsIURI* aURL);
+ nsresult SetupPluginCacheFile(nsIChannel* channel);
+ nsresult GetInterfaceGlobal(const nsIID& aIID, void** result);
+
+ nsCOMPtr<nsIURI> mURL;
+ nsCString mURLSpec; // Have to keep this member because GetURL hands out char*
+ RefPtr<nsNPAPIPluginStreamListener> mPStreamListener;
+
+ // Set to true if we request failed (like with a HTTP response of 404)
+ bool mRequestFailed;
+
+ /*
+ * Set to true after nsNPAPIPluginStreamListener::OnStartBinding() has
+ * been called. Checked in ::OnStopRequest so we can call the
+ * plugin's OnStartBinding if, for some reason, it has not already
+ * been called.
+ */
+ bool mStartBinding;
+ bool mHaveFiredOnStartRequest;
+ // these get passed to the plugin stream listener
+ uint32_t mLength;
+ int32_t mStreamType;
+
+ // local cached file, we save the content into local cache if browser cache is not available,
+ // or plugin asks stream as file and it expects file extension until bug 90558 got fixed
+ RefPtr<CachedFileHolder> mLocalCachedFileHolder;
+ nsCOMPtr<nsIOutputStream> mFileCacheOutputStream;
+ nsDataHashtable<nsUint32HashKey, uint32_t>* mDataForwardToRequest;
+
+ nsCString mContentType;
+ bool mUseLocalCache;
+ nsCOMPtr<nsIRequest> mRequest;
+ bool mSeekable;
+ uint32_t mModified;
+ RefPtr<nsNPAPIPluginInstance> mPluginInstance;
+ int32_t mStreamOffset;
+ bool mStreamComplete;
+
+public:
+ bool mAbort;
+ int32_t mPendingRequests;
+ nsWeakPtr mWeakPtrChannelCallbacks;
+ nsWeakPtr mWeakPtrChannelLoadGroup;
+ nsCOMArray<nsIRequest> mRequests;
+};
+
+#endif // nsPluginStreamListenerPeer_h_
diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp
new file mode 100644
index 000000000..ddc3968fd
--- /dev/null
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -0,0 +1,1045 @@
+/* -*- 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 "nsPluginTags.h"
+
+#include "prlink.h"
+#include "plstr.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsPluginsDir.h"
+#include "nsPluginHost.h"
+#include "nsIBlocklistService.h"
+#include "nsIUnicodeDecoder.h"
+#include "nsIPlatformCharset.h"
+#include "nsPluginLogging.h"
+#include "nsNPAPIPlugin.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+#include "nsNetUtil.h"
+#include <cctype>
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/FakePluginTagInitBinding.h"
+
+using mozilla::dom::EncodingUtils;
+using mozilla::dom::FakePluginTagInit;
+using namespace mozilla;
+
+// These legacy flags are used in the plugin registry. The states are now
+// stored in prefs, but we still need to be able to import them.
+#define NS_PLUGIN_FLAG_ENABLED 0x0001 // is this plugin enabled?
+// no longer used 0x0002 // reuse only if regenerating pluginreg.dat
+#define NS_PLUGIN_FLAG_FROMCACHE 0x0004 // this plugintag info was loaded from cache
+// no longer used 0x0008 // reuse only if regenerating pluginreg.dat
+#define NS_PLUGIN_FLAG_CLICKTOPLAY 0x0020 // this is a click-to-play plugin
+
+static const char kPrefDefaultEnabledState[] = "plugin.default.state";
+static const char kPrefDefaultEnabledStateXpi[] = "plugin.defaultXpi.state";
+
+// check comma delimited extensions
+static bool ExtensionInList(const nsCString& aExtensionList,
+ const nsACString& aExtension)
+{
+ nsCCharSeparatedTokenizer extensions(aExtensionList, ',');
+ while (extensions.hasMoreTokens()) {
+ const nsCSubstring& extension = extensions.nextToken();
+ if (extension.Equals(aExtension, nsCaseInsensitiveCStringComparator())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Search for an extension in an extensions array, and return its
+// matching mime type
+static bool SearchExtensions(const nsTArray<nsCString> & aExtensions,
+ const nsTArray<nsCString> & aMimeTypes,
+ const nsACString & aFindExtension,
+ nsACString & aMatchingType)
+{
+ uint32_t mimes = aMimeTypes.Length();
+ MOZ_ASSERT(mimes == aExtensions.Length(),
+ "These arrays should have matching elements");
+
+ aMatchingType.Truncate();
+
+ for (uint32_t i = 0; i < mimes; i++) {
+ if (ExtensionInList(aExtensions[i], aFindExtension)) {
+ aMatchingType = aMimeTypes[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static nsCString
+MakeNiceFileName(const nsCString & aFileName)
+{
+ nsCString niceName = aFileName;
+ int32_t niceNameLength = aFileName.RFind(".");
+ NS_ASSERTION(niceNameLength != kNotFound, "aFileName doesn't have a '.'?");
+ while (niceNameLength > 0) {
+ char chr = aFileName[niceNameLength - 1];
+ if (!std::isalpha(chr))
+ niceNameLength--;
+ else
+ break;
+ }
+
+ // If it turns out that niceNameLength <= 0, we'll fall back and use the
+ // entire aFileName (which we've already taken care of, a few lines back).
+ if (niceNameLength > 0) {
+ niceName.Truncate(niceNameLength);
+ }
+
+ ToLowerCase(niceName);
+ return niceName;
+}
+
+static nsCString
+MakePrefNameForPlugin(const char* const subname, nsIInternalPluginTag* aTag)
+{
+ nsCString pref;
+ nsAutoCString pluginName(aTag->GetNiceFileName());
+
+ if (pluginName.IsEmpty()) {
+ // Use filename if nice name fails
+ pluginName = aTag->FileName();
+ if (pluginName.IsEmpty()) {
+ MOZ_ASSERT_UNREACHABLE("Plugin with no filename or nice name in list");
+ pluginName.AssignLiteral("unknown-plugin-name");
+ }
+ }
+
+ pref.AssignLiteral("plugin.");
+ pref.Append(subname);
+ pref.Append('.');
+ pref.Append(pluginName);
+
+ return pref;
+}
+
+static nsresult
+CStringArrayToXPCArray(nsTArray<nsCString> & aArray,
+ uint32_t* aCount,
+ char16_t*** aResults)
+{
+ uint32_t count = aArray.Length();
+ if (!count) {
+ *aResults = nullptr;
+ *aCount = 0;
+ return NS_OK;
+ }
+
+ *aResults =
+ static_cast<char16_t**>(moz_xmalloc(count * sizeof(**aResults)));
+ *aCount = count;
+
+ for (uint32_t i = 0; i < count; i++) {
+ (*aResults)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(aArray[i]));
+ }
+
+ return NS_OK;
+}
+
+static nsCString
+GetStatePrefNameForPlugin(nsIInternalPluginTag* aTag)
+{
+ return MakePrefNameForPlugin("state", aTag);
+}
+
+static nsresult
+IsEnabledStateLockedForPlugin(nsIInternalPluginTag* aTag,
+ bool* aIsEnabledStateLocked)
+{
+ *aIsEnabledStateLocked = false;
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ if (NS_WARN_IF(!prefs)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ Unused << prefs->PrefIsLocked(GetStatePrefNameForPlugin(aTag).get(),
+ aIsEnabledStateLocked);
+
+ return NS_OK;
+}
+
+/* nsIInternalPluginTag */
+nsIInternalPluginTag::nsIInternalPluginTag()
+{
+}
+
+nsIInternalPluginTag::nsIInternalPluginTag(const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aVersion)
+ : mName(aName)
+ , mDescription(aDescription)
+ , mFileName(aFileName)
+ , mVersion(aVersion)
+{
+}
+
+nsIInternalPluginTag::nsIInternalPluginTag(const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aVersion,
+ const nsTArray<nsCString>& aMimeTypes,
+ const nsTArray<nsCString>& aMimeDescriptions,
+ const nsTArray<nsCString>& aExtensions)
+ : mName(aName)
+ , mDescription(aDescription)
+ , mFileName(aFileName)
+ , mVersion(aVersion)
+ , mMimeTypes(aMimeTypes)
+ , mMimeDescriptions(aMimeDescriptions)
+ , mExtensions(aExtensions)
+{
+}
+
+nsIInternalPluginTag::~nsIInternalPluginTag()
+{
+}
+
+bool
+nsIInternalPluginTag::HasExtension(const nsACString& aExtension,
+ nsACString& aMatchingType) const
+{
+ return SearchExtensions(mExtensions, mMimeTypes, aExtension, aMatchingType);
+}
+
+bool
+nsIInternalPluginTag::HasMimeType(const nsACString& aMimeType) const
+{
+ return mMimeTypes.Contains(aMimeType,
+ nsCaseInsensitiveCStringArrayComparator());
+}
+
+/* nsPluginTag */
+
+uint32_t nsPluginTag::sNextId;
+
+nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
+ int64_t aLastModifiedTime,
+ bool fromExtension)
+ : nsIInternalPluginTag(aPluginInfo->fName, aPluginInfo->fDescription,
+ aPluginInfo->fFileName, aPluginInfo->fVersion),
+ mId(sNextId++),
+ mContentProcessRunningCount(0),
+ mHadLocalInstance(false),
+ mLibrary(nullptr),
+ mIsJavaPlugin(false),
+ mIsFlashPlugin(false),
+ mSupportsAsyncInit(false),
+ mSupportsAsyncRender(false),
+ mFullPath(aPluginInfo->fFullPath),
+ mLastModifiedTime(aLastModifiedTime),
+ mSandboxLevel(0),
+ mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
+ mCachedBlocklistStateValid(false),
+ mIsFromExtension(fromExtension)
+{
+ InitMime(aPluginInfo->fMimeTypeArray,
+ aPluginInfo->fMimeDescriptionArray,
+ aPluginInfo->fExtensionArray,
+ aPluginInfo->fVariantCount);
+ InitSandboxLevel();
+ EnsureMembersAreUTF8();
+ FixupVersion();
+}
+
+nsPluginTag::nsPluginTag(const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aFullPath,
+ const char* aVersion,
+ const char* const* aMimeTypes,
+ const char* const* aMimeDescriptions,
+ const char* const* aExtensions,
+ int32_t aVariants,
+ int64_t aLastModifiedTime,
+ bool fromExtension,
+ bool aArgsAreUTF8)
+ : nsIInternalPluginTag(aName, aDescription, aFileName, aVersion),
+ mId(sNextId++),
+ mContentProcessRunningCount(0),
+ mHadLocalInstance(false),
+ mLibrary(nullptr),
+ mIsJavaPlugin(false),
+ mIsFlashPlugin(false),
+ mSupportsAsyncInit(false),
+ mSupportsAsyncRender(false),
+ mFullPath(aFullPath),
+ mLastModifiedTime(aLastModifiedTime),
+ mSandboxLevel(0),
+ mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
+ mCachedBlocklistStateValid(false),
+ mIsFromExtension(fromExtension)
+{
+ InitMime(aMimeTypes, aMimeDescriptions, aExtensions,
+ static_cast<uint32_t>(aVariants));
+ InitSandboxLevel();
+ if (!aArgsAreUTF8)
+ EnsureMembersAreUTF8();
+ FixupVersion();
+}
+
+nsPluginTag::nsPluginTag(uint32_t aId,
+ const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aFullPath,
+ const char* aVersion,
+ nsTArray<nsCString> aMimeTypes,
+ nsTArray<nsCString> aMimeDescriptions,
+ nsTArray<nsCString> aExtensions,
+ bool aIsJavaPlugin,
+ bool aIsFlashPlugin,
+ bool aSupportsAsyncInit,
+ bool aSupportsAsyncRender,
+ int64_t aLastModifiedTime,
+ bool aFromExtension,
+ int32_t aSandboxLevel)
+ : nsIInternalPluginTag(aName, aDescription, aFileName, aVersion, aMimeTypes,
+ aMimeDescriptions, aExtensions),
+ mId(aId),
+ mContentProcessRunningCount(0),
+ mLibrary(nullptr),
+ mIsJavaPlugin(aIsJavaPlugin),
+ mIsFlashPlugin(aIsFlashPlugin),
+ mSupportsAsyncInit(aSupportsAsyncInit),
+ mSupportsAsyncRender(aSupportsAsyncRender),
+ mLastModifiedTime(aLastModifiedTime),
+ mSandboxLevel(aSandboxLevel),
+ mNiceFileName(),
+ mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
+ mCachedBlocklistStateValid(false),
+ mIsFromExtension(aFromExtension)
+{
+}
+
+nsPluginTag::~nsPluginTag()
+{
+ NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
+}
+
+NS_IMPL_ISUPPORTS(nsPluginTag, nsPluginTag, nsIInternalPluginTag, nsIPluginTag)
+
+void nsPluginTag::InitMime(const char* const* aMimeTypes,
+ const char* const* aMimeDescriptions,
+ const char* const* aExtensions,
+ uint32_t aVariantCount)
+{
+ if (!aMimeTypes) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < aVariantCount; i++) {
+ if (!aMimeTypes[i]) {
+ continue;
+ }
+
+ nsAutoCString mimeType(aMimeTypes[i]);
+
+ // Convert the MIME type, which is case insensitive, to lowercase in order
+ // to properly handle a mixed-case type.
+ ToLowerCase(mimeType);
+
+ if (!nsPluginHost::IsTypeWhitelisted(mimeType.get())) {
+ continue;
+ }
+
+ // Look for certain special plugins.
+ switch (nsPluginHost::GetSpecialType(mimeType)) {
+ case nsPluginHost::eSpecialType_Java:
+ mIsJavaPlugin = true;
+ mSupportsAsyncInit = true;
+ break;
+ case nsPluginHost::eSpecialType_Flash:
+ // VLC sometimes claims to implement the Flash MIME type, and we want
+ // to allow users to control that separately from Adobe Flash.
+ if (Name().EqualsLiteral("Shockwave Flash")) {
+ mIsFlashPlugin = true;
+ mSupportsAsyncInit = true;
+ }
+ break;
+ case nsPluginHost::eSpecialType_Silverlight:
+ case nsPluginHost::eSpecialType_Unity:
+ case nsPluginHost::eSpecialType_Test:
+ mSupportsAsyncInit = true;
+ break;
+ case nsPluginHost::eSpecialType_None:
+ default:
+#ifndef RELEASE_OR_BETA
+ // Allow async init for all plugins on Nightly and Aurora
+ mSupportsAsyncInit = true;
+#endif
+ break;
+ }
+
+ // Fill in our MIME type array.
+ mMimeTypes.AppendElement(mimeType);
+
+ // Now fill in the MIME descriptions.
+ if (aMimeDescriptions && aMimeDescriptions[i]) {
+ // we should cut off the list of suffixes which the mime
+ // description string may have, see bug 53895
+ // it is usually in form "some description (*.sf1, *.sf2)"
+ // so we can search for the opening round bracket
+ char cur = '\0';
+ char pre = '\0';
+ char * p = PL_strrchr(aMimeDescriptions[i], '(');
+ if (p && (p != aMimeDescriptions[i])) {
+ if ((p - 1) && *(p - 1) == ' ') {
+ pre = *(p - 1);
+ *(p - 1) = '\0';
+ } else {
+ cur = *p;
+ *p = '\0';
+ }
+ }
+ mMimeDescriptions.AppendElement(nsCString(aMimeDescriptions[i]));
+ // restore the original string
+ if (cur != '\0') {
+ *p = cur;
+ }
+ if (pre != '\0') {
+ *(p - 1) = pre;
+ }
+ } else {
+ mMimeDescriptions.AppendElement(nsCString());
+ }
+
+ // Now fill in the extensions.
+ if (aExtensions && aExtensions[i]) {
+ mExtensions.AppendElement(nsCString(aExtensions[i]));
+ } else {
+ mExtensions.AppendElement(nsCString());
+ }
+ }
+}
+
+void
+nsPluginTag::InitSandboxLevel()
+{
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ nsAutoCString sandboxPref("dom.ipc.plugins.sandbox-level.");
+ sandboxPref.Append(GetNiceFileName());
+ if (NS_FAILED(Preferences::GetInt(sandboxPref.get(), &mSandboxLevel))) {
+ mSandboxLevel = Preferences::GetInt("dom.ipc.plugins.sandbox-level.default"
+);
+ }
+
+#if defined(_AMD64_)
+ // As level 2 is now the default NPAPI sandbox level for 64-bit flash, we
+ // don't want to allow a lower setting unless this environment variable is
+ // set. This should be changed if the firefox.js pref file is changed.
+ if (mIsFlashPlugin &&
+ !PR_GetEnv("MOZ_ALLOW_WEAKER_SANDBOX") && mSandboxLevel < 2) {
+ mSandboxLevel = 2;
+ }
+#endif
+#endif
+}
+
+#if !defined(XP_WIN) && !defined(XP_MACOSX)
+static nsresult ConvertToUTF8(nsIUnicodeDecoder *aUnicodeDecoder,
+ nsAFlatCString& aString)
+{
+ int32_t numberOfBytes = aString.Length();
+ int32_t outUnicodeLen;
+ nsAutoString buffer;
+ nsresult rv = aUnicodeDecoder->GetMaxLength(aString.get(), numberOfBytes,
+ &outUnicodeLen);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!buffer.SetLength(outUnicodeLen, fallible))
+ return NS_ERROR_OUT_OF_MEMORY;
+ rv = aUnicodeDecoder->Convert(aString.get(), &numberOfBytes,
+ buffer.BeginWriting(), &outUnicodeLen);
+ NS_ENSURE_SUCCESS(rv, rv);
+ buffer.SetLength(outUnicodeLen);
+ CopyUTF16toUTF8(buffer, aString);
+
+ return NS_OK;
+}
+#endif
+
+nsresult nsPluginTag::EnsureMembersAreUTF8()
+{
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ return NS_OK;
+#else
+ nsresult rv;
+
+ nsCOMPtr<nsIPlatformCharset> pcs =
+ do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIUnicodeDecoder> decoder;
+
+ nsAutoCString charset;
+ rv = pcs->GetCharset(kPlatformCharsetSel_FileName, charset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!charset.LowerCaseEqualsLiteral("utf-8")) {
+ decoder = EncodingUtils::DecoderForEncoding(charset);
+ ConvertToUTF8(decoder, mFileName);
+ ConvertToUTF8(decoder, mFullPath);
+ }
+
+ // The description of the plug-in and the various MIME type descriptions
+ // should be encoded in the standard plain text file encoding for this system.
+ // XXX should we add kPlatformCharsetSel_PluginResource?
+ rv = pcs->GetCharset(kPlatformCharsetSel_PlainTextInFile, charset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!charset.LowerCaseEqualsLiteral("utf-8")) {
+ decoder = EncodingUtils::DecoderForEncoding(charset);
+ ConvertToUTF8(decoder, mName);
+ ConvertToUTF8(decoder, mDescription);
+ for (uint32_t i = 0; i < mMimeDescriptions.Length(); ++i) {
+ ConvertToUTF8(decoder, mMimeDescriptions[i]);
+ }
+ }
+ return NS_OK;
+#endif
+}
+
+void nsPluginTag::FixupVersion()
+{
+#if defined(XP_LINUX)
+ if (mIsFlashPlugin) {
+ mVersion.ReplaceChar(',', '.');
+ }
+#endif
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetDescription(nsACString& aDescription)
+{
+ aDescription = mDescription;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetFilename(nsACString& aFileName)
+{
+ aFileName = mFileName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetFullpath(nsACString& aFullPath)
+{
+ aFullPath = mFullPath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetVersion(nsACString& aVersion)
+{
+ aVersion = mVersion;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetName(nsACString& aName)
+{
+ aName = mName;
+ return NS_OK;
+}
+
+bool
+nsPluginTag::IsActive()
+{
+ return IsEnabled() && !IsBlocklisted();
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetActive(bool *aResult)
+{
+ *aResult = IsActive();
+ return NS_OK;
+}
+
+bool
+nsPluginTag::IsEnabled()
+{
+ const PluginState state = GetPluginState();
+ return (state == ePluginState_Enabled) || (state == ePluginState_Clicktoplay);
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetDisabled(bool* aDisabled)
+{
+ *aDisabled = !IsEnabled();
+ return NS_OK;
+}
+
+bool
+nsPluginTag::IsBlocklisted()
+{
+ uint32_t blocklistState;
+ nsresult rv = GetBlocklistState(&blocklistState);
+ return NS_FAILED(rv) || blocklistState == nsIBlocklistService::STATE_BLOCKED;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetBlocklisted(bool* aBlocklisted)
+{
+ *aBlocklisted = IsBlocklisted();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetIsEnabledStateLocked(bool* aIsEnabledStateLocked)
+{
+ return IsEnabledStateLockedForPlugin(this, aIsEnabledStateLocked);
+}
+
+bool
+nsPluginTag::IsClicktoplay()
+{
+ const PluginState state = GetPluginState();
+ return (state == ePluginState_Clicktoplay);
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetClicktoplay(bool *aClicktoplay)
+{
+ *aClicktoplay = IsClicktoplay();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetEnabledState(uint32_t *aEnabledState)
+{
+ int32_t enabledState;
+ nsresult rv = Preferences::GetInt(GetStatePrefNameForPlugin(this).get(),
+ &enabledState);
+ if (NS_SUCCEEDED(rv) &&
+ enabledState >= nsIPluginTag::STATE_DISABLED &&
+ enabledState <= nsIPluginTag::STATE_ENABLED) {
+ *aEnabledState = (uint32_t)enabledState;
+ return rv;
+ }
+
+ const char* const pref = mIsFromExtension ? kPrefDefaultEnabledStateXpi
+ : kPrefDefaultEnabledState;
+
+ enabledState = Preferences::GetInt(pref, nsIPluginTag::STATE_ENABLED);
+ if (enabledState >= nsIPluginTag::STATE_DISABLED &&
+ enabledState <= nsIPluginTag::STATE_ENABLED) {
+ *aEnabledState = (uint32_t)enabledState;
+ return NS_OK;
+ }
+
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+nsPluginTag::SetEnabledState(uint32_t aEnabledState)
+{
+ if (aEnabledState >= ePluginState_MaxValue)
+ return NS_ERROR_ILLEGAL_VALUE;
+ uint32_t oldState = nsIPluginTag::STATE_DISABLED;
+ GetEnabledState(&oldState);
+ if (oldState != aEnabledState) {
+ Preferences::SetInt(GetStatePrefNameForPlugin(this).get(), aEnabledState);
+ if (RefPtr<nsPluginHost> host = nsPluginHost::GetInst()) {
+ host->UpdatePluginInfo(this);
+ }
+ }
+ return NS_OK;
+}
+
+nsPluginTag::PluginState
+nsPluginTag::GetPluginState()
+{
+ uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
+ GetEnabledState(&enabledState);
+ return (PluginState)enabledState;
+}
+
+void
+nsPluginTag::SetPluginState(PluginState state)
+{
+ static_assert((uint32_t)nsPluginTag::ePluginState_Disabled == nsIPluginTag::STATE_DISABLED, "nsPluginTag::ePluginState_Disabled must match nsIPluginTag::STATE_DISABLED");
+ static_assert((uint32_t)nsPluginTag::ePluginState_Clicktoplay == nsIPluginTag::STATE_CLICKTOPLAY, "nsPluginTag::ePluginState_Clicktoplay must match nsIPluginTag::STATE_CLICKTOPLAY");
+ static_assert((uint32_t)nsPluginTag::ePluginState_Enabled == nsIPluginTag::STATE_ENABLED, "nsPluginTag::ePluginState_Enabled must match nsIPluginTag::STATE_ENABLED");
+ SetEnabledState((uint32_t)state);
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetMimeTypes(uint32_t* aCount, char16_t*** aResults)
+{
+ return CStringArrayToXPCArray(mMimeTypes, aCount, aResults);
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetMimeDescriptions(uint32_t* aCount, char16_t*** aResults)
+{
+ return CStringArrayToXPCArray(mMimeDescriptions, aCount, aResults);
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetExtensions(uint32_t* aCount, char16_t*** aResults)
+{
+ return CStringArrayToXPCArray(mExtensions, aCount, aResults);
+}
+
+bool
+nsPluginTag::HasSameNameAndMimes(const nsPluginTag *aPluginTag) const
+{
+ NS_ENSURE_TRUE(aPluginTag, false);
+
+ if ((!mName.Equals(aPluginTag->mName)) ||
+ (mMimeTypes.Length() != aPluginTag->mMimeTypes.Length())) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < mMimeTypes.Length(); i++) {
+ if (!mMimeTypes[i].Equals(aPluginTag->mMimeTypes[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetLoaded(bool* aIsLoaded)
+{
+ *aIsLoaded = !!mPlugin;
+ return NS_OK;
+}
+
+void nsPluginTag::TryUnloadPlugin(bool inShutdown)
+{
+ // We never want to send NPP_Shutdown to an in-process plugin unless
+ // this process is shutting down.
+ if (!mPlugin) {
+ return;
+ }
+ if (inShutdown || mPlugin->GetLibrary()->IsOOP()) {
+ mPlugin->Shutdown();
+ mPlugin = nullptr;
+ }
+}
+
+const nsCString&
+nsPluginTag::GetNiceFileName()
+{
+ if (!mNiceFileName.IsEmpty()) {
+ return mNiceFileName;
+ }
+
+ if (mIsFlashPlugin) {
+ mNiceFileName.AssignLiteral("flash");
+ return mNiceFileName;
+ }
+
+ if (mIsJavaPlugin) {
+ mNiceFileName.AssignLiteral("java");
+ return mNiceFileName;
+ }
+
+ mNiceFileName = MakeNiceFileName(mFileName);
+ return mNiceFileName;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetNiceName(nsACString & aResult)
+{
+ aResult = GetNiceFileName();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetBlocklistState(uint32_t *aResult)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+ *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
+ return NS_OK;
+#else
+ if (mCachedBlocklistStateValid) {
+ *aResult = mCachedBlocklistState;
+ return NS_OK;
+ }
+
+ if (!XRE_IsParentProcess()) {
+ *aResult = nsIBlocklistService::STATE_BLOCKED;
+ dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+ if (!cp->SendGetBlocklistState(mId, aResult)) {
+ return NS_OK;
+ }
+ } else {
+ nsCOMPtr<nsIBlocklistService> blocklist =
+ do_GetService("@mozilla.org/extensions/blocklist;1");
+
+ if (!blocklist) {
+ *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
+ return NS_OK;
+ }
+
+ // The EmptyString()s are so we use the currently running application
+ // and toolkit versions
+ if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(),
+ EmptyString(), aResult))) {
+ *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
+ return NS_OK;
+ }
+ }
+
+ MOZ_ASSERT(*aResult <= UINT16_MAX);
+ mCachedBlocklistState = (uint16_t) *aResult;
+ mCachedBlocklistStateValid = true;
+ return NS_OK;
+#endif // defined(MOZ_WIDGET_ANDROID)
+}
+
+void
+nsPluginTag::InvalidateBlocklistState()
+{
+ mCachedBlocklistStateValid = false;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime)
+{
+ MOZ_ASSERT(aLastModifiedTime);
+ *aLastModifiedTime = mLastModifiedTime;
+ return NS_OK;
+}
+
+bool nsPluginTag::IsFromExtension() const
+{
+ return mIsFromExtension;
+}
+
+/* nsFakePluginTag */
+
+nsFakePluginTag::nsFakePluginTag()
+ : mState(nsPluginTag::ePluginState_Disabled)
+{
+}
+
+nsFakePluginTag::~nsFakePluginTag()
+{
+}
+
+NS_IMPL_ADDREF(nsFakePluginTag)
+NS_IMPL_RELEASE(nsFakePluginTag)
+NS_INTERFACE_TABLE_HEAD(nsFakePluginTag)
+ NS_INTERFACE_TABLE_BEGIN
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsFakePluginTag, nsIPluginTag,
+ nsIInternalPluginTag)
+ NS_INTERFACE_TABLE_ENTRY(nsFakePluginTag, nsIInternalPluginTag)
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsFakePluginTag, nsISupports,
+ nsIInternalPluginTag)
+ NS_INTERFACE_TABLE_ENTRY(nsFakePluginTag, nsIFakePluginTag)
+ NS_INTERFACE_TABLE_END
+NS_INTERFACE_TABLE_TAIL
+
+/* static */
+nsresult
+nsFakePluginTag::Create(const FakePluginTagInit& aInitDictionary,
+ nsFakePluginTag** aPluginTag)
+{
+ NS_ENSURE_TRUE(!aInitDictionary.mMimeEntries.IsEmpty(), NS_ERROR_INVALID_ARG);
+
+ RefPtr<nsFakePluginTag> tag = new nsFakePluginTag();
+ nsresult rv = NS_NewURI(getter_AddRefs(tag->mHandlerURI),
+ aInitDictionary.mHandlerURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CopyUTF16toUTF8(aInitDictionary.mNiceName, tag->mNiceName);
+ CopyUTF16toUTF8(aInitDictionary.mFullPath, tag->mFullPath);
+ CopyUTF16toUTF8(aInitDictionary.mName, tag->mName);
+ CopyUTF16toUTF8(aInitDictionary.mDescription, tag->mDescription);
+ CopyUTF16toUTF8(aInitDictionary.mFileName, tag->mFileName);
+ CopyUTF16toUTF8(aInitDictionary.mVersion, tag->mVersion);
+
+ for (const FakePluginMimeEntry& mimeEntry : aInitDictionary.mMimeEntries) {
+ CopyUTF16toUTF8(mimeEntry.mType, *tag->mMimeTypes.AppendElement());
+ CopyUTF16toUTF8(mimeEntry.mDescription,
+ *tag->mMimeDescriptions.AppendElement());
+ CopyUTF16toUTF8(mimeEntry.mExtension, *tag->mExtensions.AppendElement());
+ }
+
+ tag.forget(aPluginTag);
+ return NS_OK;
+}
+
+bool
+nsFakePluginTag::HandlerURIMatches(nsIURI* aURI)
+{
+ bool equals = false;
+ return NS_SUCCEEDED(mHandlerURI->Equals(aURI, &equals)) && equals;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetHandlerURI(nsIURI **aResult)
+{
+ NS_IF_ADDREF(*aResult = mHandlerURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetDescription(/* utf-8 */ nsACString& aResult)
+{
+ aResult = mDescription;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetFilename(/* utf-8 */ nsACString& aResult)
+{
+ aResult = mFileName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetFullpath(/* utf-8 */ nsACString& aResult)
+{
+ aResult = mFullPath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetVersion(/* utf-8 */ nsACString& aResult)
+{
+ aResult = mVersion;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetName(/* utf-8 */ nsACString& aResult)
+{
+ aResult = mName;
+ return NS_OK;
+}
+
+const nsCString&
+nsFakePluginTag::GetNiceFileName()
+{
+ // We don't try to mimic the special-cased flash/java names if the fake plugin
+ // claims one of their MIME types, but do allow directly setting niceName if
+ // emulating those is desired.
+ if (mNiceName.IsEmpty() && !mFileName.IsEmpty()) {
+ mNiceName = MakeNiceFileName(mFileName);
+ }
+
+ return mNiceName;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetNiceName(/* utf-8 */ nsACString& aResult)
+{
+ aResult = GetNiceFileName();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetBlocklistState(uint32_t* aResult)
+{
+ // Fake tags don't currently support blocklisting
+ *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetBlocklisted(bool* aBlocklisted)
+{
+ // Fake tags can't be blocklisted
+ *aBlocklisted = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetIsEnabledStateLocked(bool* aIsEnabledStateLocked)
+{
+ return IsEnabledStateLockedForPlugin(this, aIsEnabledStateLocked);
+}
+
+bool
+nsFakePluginTag::IsEnabled()
+{
+ return mState == nsPluginTag::ePluginState_Enabled ||
+ mState == nsPluginTag::ePluginState_Clicktoplay;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetDisabled(bool* aDisabled)
+{
+ *aDisabled = !IsEnabled();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetClicktoplay(bool* aClicktoplay)
+{
+ *aClicktoplay = (mState == nsPluginTag::ePluginState_Clicktoplay);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetEnabledState(uint32_t* aEnabledState)
+{
+ *aEnabledState = (uint32_t)mState;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::SetEnabledState(uint32_t aEnabledState)
+{
+ // There are static asserts above enforcing that this enum matches
+ mState = (nsPluginTag::PluginState)aEnabledState;
+ // FIXME-jsplugins update
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetMimeTypes(uint32_t* aCount, char16_t*** aResults)
+{
+ return CStringArrayToXPCArray(mMimeTypes, aCount, aResults);
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetMimeDescriptions(uint32_t* aCount, char16_t*** aResults)
+{
+ return CStringArrayToXPCArray(mMimeDescriptions, aCount, aResults);
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetExtensions(uint32_t* aCount, char16_t*** aResults)
+{
+ return CStringArrayToXPCArray(mExtensions, aCount, aResults);
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetActive(bool *aResult)
+{
+ // Fake plugins can't be blocklisted, so this is just !Disabled
+ *aResult = IsEnabled();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime)
+{
+ // FIXME-jsplugins What should this return, if anything?
+ MOZ_ASSERT(aLastModifiedTime);
+ *aLastModifiedTime = 0;
+ return NS_OK;
+}
+
+// We don't load fake plugins out of a library, so they should always be there.
+NS_IMETHODIMP
+nsFakePluginTag::GetLoaded(bool* ret)
+{
+ *ret = true;
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginTags.h b/dom/plugins/base/nsPluginTags.h
new file mode 100644
index 000000000..f1f03169b
--- /dev/null
+++ b/dom/plugins/base/nsPluginTags.h
@@ -0,0 +1,236 @@
+/* -*- 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/. */
+
+#ifndef nsPluginTags_h_
+#define nsPluginTags_h_
+
+#include "mozilla/Attributes.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsIPluginTag.h"
+#include "nsITimer.h"
+#include "nsString.h"
+
+class nsIURI;
+struct PRLibrary;
+struct nsPluginInfo;
+class nsNPAPIPlugin;
+
+namespace mozilla {
+namespace dom {
+struct FakePluginTagInit;
+} // namespace dom
+} // namespace mozilla
+
+// An interface representing plugin tags internally.
+#define NS_IINTERNALPLUGINTAG_IID \
+{ 0xe8fdd227, 0x27da, 0x46ee, \
+ { 0xbe, 0xf3, 0x1a, 0xef, 0x5a, 0x8f, 0xc5, 0xb4 } }
+
+#define NS_PLUGINTAG_IID \
+ { 0xcce2e8b9, 0x9702, 0x4d4b, \
+ { 0xbe, 0xa4, 0x7c, 0x1e, 0x13, 0x1f, 0xaf, 0x78 } }
+class nsIInternalPluginTag : public nsIPluginTag
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IINTERNALPLUGINTAG_IID)
+
+ nsIInternalPluginTag();
+ nsIInternalPluginTag(const char* aName, const char* aDescription,
+ const char* aFileName, const char* aVersion);
+ nsIInternalPluginTag(const char* aName, const char* aDescription,
+ const char* aFileName, const char* aVersion,
+ const nsTArray<nsCString>& aMimeTypes,
+ const nsTArray<nsCString>& aMimeDescriptions,
+ const nsTArray<nsCString>& aExtensions);
+
+ virtual bool IsEnabled() = 0;
+ virtual const nsCString& GetNiceFileName() = 0;
+
+ const nsCString& Name() const { return mName; }
+ const nsCString& Description() const { return mDescription; }
+
+ const nsTArray<nsCString>& MimeTypes() const { return mMimeTypes; }
+
+ const nsTArray<nsCString>& MimeDescriptions() const {
+ return mMimeDescriptions;
+ }
+
+ const nsTArray<nsCString>& Extensions() const { return mExtensions; }
+
+ const nsCString& FileName() const { return mFileName; }
+
+ const nsCString& Version() const { return mVersion; }
+
+ // Returns true if this plugin claims it supports this MIME type. The
+ // comparison is done ASCII-case-insensitively.
+ bool HasMimeType(const nsACString & aMimeType) const;
+
+ // Returns true if this plugin claims it supports the given extension. In
+ // that case, aMatchingType is set to the MIME type the plugin claims
+ // corresponds to this extension. The match on aExtension is done
+ // ASCII-case-insensitively.
+ bool HasExtension(const nsACString & aExtension,
+ /* out */ nsACString & aMatchingType) const;
+protected:
+ ~nsIInternalPluginTag();
+
+ nsCString mName; // UTF-8
+ nsCString mDescription; // UTF-8
+ nsCString mFileName; // UTF-8
+ nsCString mVersion; // UTF-8
+ nsTArray<nsCString> mMimeTypes; // UTF-8
+ nsTArray<nsCString> mMimeDescriptions; // UTF-8
+ nsTArray<nsCString> mExtensions; // UTF-8
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIInternalPluginTag, NS_IINTERNALPLUGINTAG_IID)
+
+// A linked-list of plugin information that is used for instantiating plugins
+// and reflecting plugin information into JavaScript.
+class nsPluginTag final : public nsIInternalPluginTag
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_PLUGINTAG_IID)
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPLUGINTAG
+
+ // These must match the STATE_* values in nsIPluginTag.idl
+ enum PluginState {
+ ePluginState_Disabled = 0,
+ ePluginState_Clicktoplay = 1,
+ ePluginState_Enabled = 2,
+ ePluginState_MaxValue = 3,
+ };
+
+ nsPluginTag(nsPluginInfo* aPluginInfo,
+ int64_t aLastModifiedTime,
+ bool fromExtension);
+ nsPluginTag(const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aFullPath,
+ const char* aVersion,
+ const char* const* aMimeTypes,
+ const char* const* aMimeDescriptions,
+ const char* const* aExtensions,
+ int32_t aVariants,
+ int64_t aLastModifiedTime,
+ bool fromExtension,
+ bool aArgsAreUTF8 = false);
+ nsPluginTag(uint32_t aId,
+ const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aFullPath,
+ const char* aVersion,
+ nsTArray<nsCString> aMimeTypes,
+ nsTArray<nsCString> aMimeDescriptions,
+ nsTArray<nsCString> aExtensions,
+ bool aIsJavaPlugin,
+ bool aIsFlashPlugin,
+ bool aSupportsAsyncInit,
+ bool aSupportsAsyncRender,
+ int64_t aLastModifiedTime,
+ bool aFromExtension,
+ int32_t aSandboxLevel);
+
+ void TryUnloadPlugin(bool inShutdown);
+
+ // plugin is enabled and not blocklisted
+ bool IsActive();
+
+ bool IsEnabled() override;
+ void SetEnabled(bool enabled);
+ bool IsClicktoplay();
+ bool IsBlocklisted();
+
+ PluginState GetPluginState();
+ void SetPluginState(PluginState state);
+
+ bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
+ const nsCString& GetNiceFileName() override;
+
+ bool IsFromExtension() const;
+
+ RefPtr<nsPluginTag> mNext;
+ uint32_t mId;
+
+ // Number of PluginModuleParents living in all content processes.
+ size_t mContentProcessRunningCount;
+
+ // True if we've ever created an instance of this plugin in the current process.
+ bool mHadLocalInstance;
+
+ PRLibrary *mLibrary;
+ RefPtr<nsNPAPIPlugin> mPlugin;
+ bool mIsJavaPlugin;
+ bool mIsFlashPlugin;
+ bool mSupportsAsyncInit;
+ bool mSupportsAsyncRender;
+ nsCString mFullPath; // UTF-8
+ int64_t mLastModifiedTime;
+ nsCOMPtr<nsITimer> mUnloadTimer;
+ int32_t mSandboxLevel;
+
+ void InvalidateBlocklistState();
+
+private:
+ virtual ~nsPluginTag();
+
+ nsCString mNiceFileName; // UTF-8
+ uint16_t mCachedBlocklistState;
+ bool mCachedBlocklistStateValid;
+ bool mIsFromExtension;
+
+ void InitMime(const char* const* aMimeTypes,
+ const char* const* aMimeDescriptions,
+ const char* const* aExtensions,
+ uint32_t aVariantCount);
+ void InitSandboxLevel();
+ nsresult EnsureMembersAreUTF8();
+ void FixupVersion();
+
+ static uint32_t sNextId;
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(nsPluginTag, NS_PLUGINTAG_IID)
+
+// A class representing "fake" plugin tags; that is plugin tags not
+// corresponding to actual NPAPI plugins. In practice these are all
+// JS-implemented plugins; maybe we want a better name for this class?
+class nsFakePluginTag : public nsIInternalPluginTag,
+ public nsIFakePluginTag
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPLUGINTAG
+ NS_DECL_NSIFAKEPLUGINTAG
+
+ static nsresult Create(const mozilla::dom::FakePluginTagInit& aInitDictionary,
+ nsFakePluginTag** aPluginTag);
+
+ bool IsEnabled() override;
+ const nsCString& GetNiceFileName() override;
+
+ bool HandlerURIMatches(nsIURI* aURI);
+
+ nsIURI* HandlerURI() const { return mHandlerURI; }
+
+private:
+ nsFakePluginTag();
+ virtual ~nsFakePluginTag();
+
+ // The URI of the handler for our fake plugin.
+ // FIXME-jsplugins do we need to sanity check these?
+ nsCOMPtr<nsIURI> mHandlerURI;
+
+ nsCString mFullPath;
+ nsCString mNiceName;
+
+ nsPluginTag::PluginState mState;
+};
+
+#endif // nsPluginTags_h_
diff --git a/dom/plugins/base/nsPluginsCID.h b/dom/plugins/base/nsPluginsCID.h
new file mode 100644
index 000000000..eba2d250d
--- /dev/null
+++ b/dom/plugins/base/nsPluginsCID.h
@@ -0,0 +1,13 @@
+/* -*- 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/. */
+
+#ifndef nsPluginsCID_h_
+#define nsPluginsCID_h_
+
+#define NS_PLUGIN_HOST_CID \
+{ 0x23E8FD98, 0xA625, 0x4B08, \
+{ 0xBE, 0x1A, 0xF7, 0xCC, 0x18, 0xA5, 0xB1, 0x06 } }
+
+#endif // nsPluginsCID_h_
diff --git a/dom/plugins/base/nsPluginsDir.h b/dom/plugins/base/nsPluginsDir.h
new file mode 100644
index 000000000..925bbb48b
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDir.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef nsPluginsDir_h_
+#define nsPluginsDir_h_
+
+#include "nsError.h"
+#include "nsIFile.h"
+
+/**
+ * nsPluginsDir is nearly obsolete. Directory Service should be used instead.
+ * It exists for the sake of one static function.
+ */
+
+class nsPluginsDir {
+public:
+ /**
+ * Determines whether or not the given file is actually a plugin file.
+ */
+ static bool IsPluginFile(nsIFile* file);
+};
+
+struct PRLibrary;
+
+struct nsPluginInfo {
+ char* fName; // name of the plugin
+ char* fDescription; // etc.
+ uint32_t fVariantCount;
+ char** fMimeTypeArray;
+ char** fMimeDescriptionArray;
+ char** fExtensionArray;
+ char* fFileName;
+ char* fFullPath;
+ char* fVersion;
+ bool fSupportsAsyncRender;
+};
+
+/**
+ * Provides cross-platform access to a plugin file. Deals with reading
+ * properties from the plugin file, and loading the plugin's shared
+ * library. Insulates core nsIPluginHost implementations from these
+ * details.
+ */
+class nsPluginFile {
+ PRLibrary* pLibrary;
+ nsCOMPtr<nsIFile> mPlugin;
+public:
+ /**
+ * If spec corresponds to a valid plugin file, constructs a reference
+ * to a plugin file on disk. Plugins are typically located using the
+ * nsPluginsDir class.
+ */
+ explicit nsPluginFile(nsIFile* spec);
+ virtual ~nsPluginFile();
+
+ /**
+ * Loads the plugin into memory using NSPR's shared-library loading
+ * mechanism. Handles platform differences in loading shared libraries.
+ */
+ nsresult LoadPlugin(PRLibrary **outLibrary);
+
+ /**
+ * Obtains all of the information currently available for this plugin.
+ * Has a library outparam which will be non-null if a library load was required.
+ */
+ nsresult GetPluginInfo(nsPluginInfo &outPluginInfo, PRLibrary **outLibrary);
+
+ /**
+ * Should be called after GetPluginInfo to free all allocated stuff
+ */
+ nsresult FreePluginInfo(nsPluginInfo &PluginInfo);
+};
+
+#endif /* nsPluginsDir_h_ */
diff --git a/dom/plugins/base/nsPluginsDirDarwin.cpp b/dom/plugins/base/nsPluginsDirDarwin.cpp
new file mode 100644
index 000000000..6edc4fd6a
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirDarwin.cpp
@@ -0,0 +1,591 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ nsPluginsDirDarwin.cpp
+
+ Mac OS X implementation of the nsPluginsDir/nsPluginsFile classes.
+
+ by Patrick C. Beard.
+ */
+
+#include "GeckoChildProcessHost.h"
+#include "base/process_util.h"
+
+#include "prlink.h"
+#include "prnetdb.h"
+#include "nsXPCOM.h"
+
+#include "nsPluginsDir.h"
+#include "nsNPAPIPlugin.h"
+#include "nsPluginsDirUtils.h"
+
+#include "nsILocalFileMac.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsCocoaFeatures.h"
+#if defined(MOZ_CRASHREPORTER)
+#include "nsExceptionHandler.h"
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <Carbon/Carbon.h>
+#include <CoreServices/CoreServices.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+
+typedef NS_NPAPIPLUGIN_CALLBACK(const char *, NP_GETMIMEDESCRIPTION) ();
+typedef NS_NPAPIPLUGIN_CALLBACK(OSErr, BP_GETSUPPORTEDMIMETYPES) (BPSupportedMIMETypes *mimeInfo, UInt32 flags);
+
+/*
+** Returns a CFBundleRef if the path refers to a Mac OS X bundle directory.
+** The caller is responsible for calling CFRelease() to deallocate.
+*/
+static CFBundleRef getPluginBundle(const char* path)
+{
+ CFBundleRef bundle = nullptr;
+ CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, path,
+ kCFStringEncodingUTF8);
+ if (pathRef) {
+ CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(nullptr, pathRef,
+ kCFURLPOSIXPathStyle,
+ true);
+ if (bundleURL) {
+ bundle = ::CFBundleCreate(nullptr, bundleURL);
+ ::CFRelease(bundleURL);
+ }
+ ::CFRelease(pathRef);
+ }
+ return bundle;
+}
+
+static nsresult toCFURLRef(nsIFile* file, CFURLRef& outURL)
+{
+ nsCOMPtr<nsILocalFileMac> lfm = do_QueryInterface(file);
+ if (!lfm)
+ return NS_ERROR_FAILURE;
+ CFURLRef url;
+ nsresult rv = lfm->GetCFURL(&url);
+ if (NS_SUCCEEDED(rv))
+ outURL = url;
+
+ return rv;
+}
+
+bool nsPluginsDir::IsPluginFile(nsIFile* file)
+{
+ nsCString fileName;
+ file->GetNativeLeafName(fileName);
+ /*
+ * Don't load the VDP fake plugin, to avoid tripping a bad bug in OS X
+ * 10.5.3 (see bug 436575).
+ */
+ if (!strcmp(fileName.get(), "VerifiedDownloadPlugin.plugin")) {
+ NS_WARNING("Preventing load of VerifiedDownloadPlugin.plugin (see bug 436575)");
+ return false;
+ }
+ return true;
+}
+
+// Caller is responsible for freeing returned buffer.
+static char* CFStringRefToUTF8Buffer(CFStringRef cfString)
+{
+ const char* buffer = ::CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
+ if (buffer) {
+ return PL_strdup(buffer);
+ }
+
+ int bufferLength =
+ ::CFStringGetMaximumSizeForEncoding(::CFStringGetLength(cfString),
+ kCFStringEncodingUTF8) + 1;
+ char* newBuffer = static_cast<char*>(moz_xmalloc(bufferLength));
+ if (!newBuffer) {
+ return nullptr;
+ }
+
+ if (!::CFStringGetCString(cfString, newBuffer, bufferLength,
+ kCFStringEncodingUTF8)) {
+ free(newBuffer);
+ return nullptr;
+ }
+
+ newBuffer = static_cast<char*>(moz_xrealloc(newBuffer,
+ strlen(newBuffer) + 1));
+ return newBuffer;
+}
+
+class AutoCFTypeObject {
+public:
+ explicit AutoCFTypeObject(CFTypeRef aObject)
+ {
+ mObject = aObject;
+ }
+ ~AutoCFTypeObject()
+ {
+ ::CFRelease(mObject);
+ }
+private:
+ CFTypeRef mObject;
+};
+
+static Boolean MimeTypeEnabled(CFDictionaryRef mimeDict) {
+ if (!mimeDict) {
+ return true;
+ }
+
+ CFTypeRef value;
+ if (::CFDictionaryGetValueIfPresent(mimeDict, CFSTR("WebPluginTypeEnabled"), &value)) {
+ if (value && ::CFGetTypeID(value) == ::CFBooleanGetTypeID()) {
+ return ::CFBooleanGetValue(static_cast<CFBooleanRef>(value));
+ }
+ }
+ return true;
+}
+
+static CFDictionaryRef ParsePlistForMIMETypesFilename(CFBundleRef bundle)
+{
+ CFTypeRef mimeFileName = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypesFilename"));
+ if (!mimeFileName || ::CFGetTypeID(mimeFileName) != ::CFStringGetTypeID()) {
+ return nullptr;
+ }
+
+ FSRef homeDir;
+ if (::FSFindFolder(kUserDomain, kCurrentUserFolderType, kDontCreateFolder, &homeDir) != noErr) {
+ return nullptr;
+ }
+
+ CFURLRef userDirURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, &homeDir);
+ if (!userDirURL) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject userDirURLAutorelease(userDirURL);
+ CFStringRef mimeFilePath = ::CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("Library/Preferences/%@"), static_cast<CFStringRef>(mimeFileName));
+ if (!mimeFilePath) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject mimeFilePathAutorelease(mimeFilePath);
+ CFURLRef mimeFileURL = ::CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorDefault, mimeFilePath, kCFURLPOSIXPathStyle, false, userDirURL);
+ if (!mimeFileURL) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject mimeFileURLAutorelease(mimeFileURL);
+ SInt32 errorCode = 0;
+ CFDataRef mimeFileData = nullptr;
+ Boolean result = ::CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, mimeFileURL, &mimeFileData, nullptr, nullptr, &errorCode);
+ if (!result) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject mimeFileDataAutorelease(mimeFileData);
+ if (errorCode != 0) {
+ return nullptr;
+ }
+
+ CFPropertyListRef propertyList = ::CFPropertyListCreateFromXMLData(kCFAllocatorDefault, mimeFileData, kCFPropertyListImmutable, nullptr);
+ if (!propertyList) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject propertyListAutorelease(propertyList);
+ if (::CFGetTypeID(propertyList) != ::CFDictionaryGetTypeID()) {
+ return nullptr;
+ }
+
+ CFTypeRef mimeTypes = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyList), CFSTR("WebPluginMIMETypes"));
+ if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() || ::CFDictionaryGetCount(static_cast<CFDictionaryRef>(mimeTypes)) == 0) {
+ return nullptr;
+ }
+
+ return static_cast<CFDictionaryRef>(::CFRetain(mimeTypes));
+}
+
+static void ParsePlistPluginInfo(nsPluginInfo& info, CFBundleRef bundle)
+{
+ CFDictionaryRef mimeDict = ParsePlistForMIMETypesFilename(bundle);
+
+ if (!mimeDict) {
+ CFTypeRef mimeTypes = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes"));
+ if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() || ::CFDictionaryGetCount(static_cast<CFDictionaryRef>(mimeTypes)) == 0)
+ return;
+ mimeDict = static_cast<CFDictionaryRef>(::CFRetain(mimeTypes));
+ }
+
+ AutoCFTypeObject mimeDictAutorelease(mimeDict);
+ int mimeDictKeyCount = ::CFDictionaryGetCount(mimeDict);
+
+ // Allocate memory for mime data
+ int mimeDataArraySize = mimeDictKeyCount * sizeof(char*);
+ info.fMimeTypeArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
+ if (!info.fMimeTypeArray)
+ return;
+ memset(info.fMimeTypeArray, 0, mimeDataArraySize);
+ info.fExtensionArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
+ if (!info.fExtensionArray)
+ return;
+ memset(info.fExtensionArray, 0, mimeDataArraySize);
+ info.fMimeDescriptionArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
+ if (!info.fMimeDescriptionArray)
+ return;
+ memset(info.fMimeDescriptionArray, 0, mimeDataArraySize);
+
+ // Allocate memory for mime dictionary keys and values
+ mozilla::UniquePtr<CFTypeRef[]> keys(new CFTypeRef[mimeDictKeyCount]);
+ if (!keys)
+ return;
+ mozilla::UniquePtr<CFTypeRef[]> values(new CFTypeRef[mimeDictKeyCount]);
+ if (!values)
+ return;
+
+ info.fVariantCount = 0;
+
+ ::CFDictionaryGetKeysAndValues(mimeDict, keys.get(), values.get());
+ for (int i = 0; i < mimeDictKeyCount; i++) {
+ CFTypeRef mimeString = keys[i];
+ if (!mimeString || ::CFGetTypeID(mimeString) != ::CFStringGetTypeID()) {
+ continue;
+ }
+ CFTypeRef mimeDict = values[i];
+ if (mimeDict && ::CFGetTypeID(mimeDict) == ::CFDictionaryGetTypeID()) {
+ if (!MimeTypeEnabled(static_cast<CFDictionaryRef>(mimeDict))) {
+ continue;
+ }
+ info.fMimeTypeArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(mimeString));
+ if (!info.fMimeTypeArray[info.fVariantCount]) {
+ continue;
+ }
+ CFTypeRef extensions = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(mimeDict), CFSTR("WebPluginExtensions"));
+ if (extensions && ::CFGetTypeID(extensions) == ::CFArrayGetTypeID()) {
+ int extensionCount = ::CFArrayGetCount(static_cast<CFArrayRef>(extensions));
+ CFMutableStringRef extensionList = ::CFStringCreateMutable(kCFAllocatorDefault, 0);
+ for (int j = 0; j < extensionCount; j++) {
+ CFTypeRef extension = ::CFArrayGetValueAtIndex(static_cast<CFArrayRef>(extensions), j);
+ if (extension && ::CFGetTypeID(extension) == ::CFStringGetTypeID()) {
+ if (j > 0)
+ ::CFStringAppend(extensionList, CFSTR(","));
+ ::CFStringAppend(static_cast<CFMutableStringRef>(extensionList), static_cast<CFStringRef>(extension));
+ }
+ }
+ info.fExtensionArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(extensionList));
+ ::CFRelease(extensionList);
+ }
+ CFTypeRef description = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(mimeDict), CFSTR("WebPluginTypeDescription"));
+ if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID())
+ info.fMimeDescriptionArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(description));
+ }
+ info.fVariantCount++;
+ }
+}
+
+nsPluginFile::nsPluginFile(nsIFile *spec)
+ : mPlugin(spec)
+{
+}
+
+nsPluginFile::~nsPluginFile() {}
+
+nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
+{
+ if (!mPlugin)
+ return NS_ERROR_NULL_POINTER;
+
+ // 64-bit NSPR does not (yet) support bundles. So in 64-bit builds we need
+ // (for now) to load the bundle's executable. However this can cause
+ // problems: CFBundleCreate() doesn't run the bundle's executable's
+ // initialization code, while NSAddImage() and dlopen() do run it. So using
+ // NSPR's dyld loading mechanisms here (NSAddImage() or dlopen()) can cause
+ // a bundle's initialization code to run earlier than expected, and lead to
+ // crashes. See bug 577967.
+#ifdef __LP64__
+ char executablePath[PATH_MAX];
+ executablePath[0] = '\0';
+ nsAutoCString bundlePath;
+ mPlugin->GetNativePath(bundlePath);
+ CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, bundlePath.get(),
+ kCFStringEncodingUTF8);
+ if (pathRef) {
+ CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(nullptr, pathRef,
+ kCFURLPOSIXPathStyle,
+ true);
+ if (bundleURL) {
+ CFBundleRef bundle = ::CFBundleCreate(nullptr, bundleURL);
+ if (bundle) {
+ CFURLRef executableURL = ::CFBundleCopyExecutableURL(bundle);
+ if (executableURL) {
+ if (!::CFURLGetFileSystemRepresentation(executableURL, true, (UInt8*)&executablePath, PATH_MAX))
+ executablePath[0] = '\0';
+ ::CFRelease(executableURL);
+ }
+ ::CFRelease(bundle);
+ }
+ ::CFRelease(bundleURL);
+ }
+ ::CFRelease(pathRef);
+ }
+#else
+ nsAutoCString bundlePath;
+ mPlugin->GetNativePath(bundlePath);
+ const char *executablePath = bundlePath.get();
+#endif
+
+ *outLibrary = PR_LoadLibrary(executablePath);
+ pLibrary = *outLibrary;
+ if (!pLibrary) {
+ return NS_ERROR_FAILURE;
+ }
+#ifdef DEBUG
+ printf("[loaded plugin %s]\n", bundlePath.get());
+#endif
+ return NS_OK;
+}
+
+static char* p2cstrdup(StringPtr pstr)
+{
+ int len = pstr[0];
+ char* cstr = static_cast<char*>(moz_xmalloc(len + 1));
+ if (cstr) {
+ memmove(cstr, pstr + 1, len);
+ cstr[len] = '\0';
+ }
+ return cstr;
+}
+
+static char* GetNextPluginStringFromHandle(Handle h, short *index)
+{
+ char *ret = p2cstrdup((unsigned char*)(*h + *index));
+ *index += (ret ? strlen(ret) : 0) + 1;
+ return ret;
+}
+
+static bool IsCompatibleArch(nsIFile *file)
+{
+ CFURLRef pluginURL = nullptr;
+ if (NS_FAILED(toCFURLRef(file, pluginURL)))
+ return false;
+
+ bool isPluginFile = false;
+
+ CFBundleRef pluginBundle = ::CFBundleCreate(kCFAllocatorDefault, pluginURL);
+ if (pluginBundle) {
+ UInt32 packageType, packageCreator;
+ ::CFBundleGetPackageInfo(pluginBundle, &packageType, &packageCreator);
+ if (packageType == 'BRPL' || packageType == 'IEPL' || packageType == 'NSPL') {
+ // Get path to plugin as a C string.
+ char executablePath[PATH_MAX];
+ executablePath[0] = '\0';
+ if (!::CFURLGetFileSystemRepresentation(pluginURL, true, (UInt8*)&executablePath, PATH_MAX)) {
+ executablePath[0] = '\0';
+ }
+
+ uint32_t pluginLibArchitectures;
+ nsresult rv = mozilla::ipc::GeckoChildProcessHost::GetArchitecturesForBinary(executablePath, &pluginLibArchitectures);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ uint32_t supportedArchitectures =
+#ifdef __LP64__
+ mozilla::ipc::GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
+#else
+ base::GetCurrentProcessArchitecture();
+#endif
+
+ // Consider the plugin architecture valid if there is any overlap in the masks.
+ isPluginFile = !!(supportedArchitectures & pluginLibArchitectures);
+ }
+ ::CFRelease(pluginBundle);
+ }
+
+ ::CFRelease(pluginURL);
+ return isPluginFile;
+}
+
+/**
+ * Obtains all of the information currently available for this plugin.
+ */
+nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
+{
+ *outLibrary = nullptr;
+
+ nsresult rv = NS_OK;
+
+ if (!IsCompatibleArch(mPlugin)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // clear out the info, except for the first field.
+ memset(&info, 0, sizeof(info));
+
+ // Try to get a bundle reference.
+ nsAutoCString path;
+ if (NS_FAILED(rv = mPlugin->GetNativePath(path)))
+ return rv;
+ CFBundleRef bundle = getPluginBundle(path.get());
+
+ // fill in full path
+ info.fFullPath = PL_strdup(path.get());
+
+ // fill in file name
+ nsAutoCString fileName;
+ if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName)))
+ return rv;
+ info.fFileName = PL_strdup(fileName.get());
+
+ // Get fName
+ if (bundle) {
+ CFTypeRef name = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginName"));
+ if (name && ::CFGetTypeID(name) == ::CFStringGetTypeID())
+ info.fName = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(name));
+ }
+
+ // Get fDescription
+ if (bundle) {
+ CFTypeRef description = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginDescription"));
+ if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID())
+ info.fDescription = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(description));
+ }
+
+ // Get fVersion
+ if (bundle) {
+ // Look for the release version first
+ CFTypeRef version = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString"));
+ if (!version) // try the build version
+ version = ::CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
+ if (version && ::CFGetTypeID(version) == ::CFStringGetTypeID())
+ info.fVersion = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(version));
+ }
+
+ // The last thing we need to do is get MIME data
+ // fVariantCount, fMimeTypeArray, fExtensionArray, fMimeDescriptionArray
+
+ // First look for data in a bundle plist
+ if (bundle) {
+ ParsePlistPluginInfo(info, bundle);
+ ::CFRelease(bundle);
+ if (info.fVariantCount > 0)
+ return NS_OK;
+ }
+
+ // Don't load "fbplugin" or any plugins whose name starts with "fbplugin_"
+ // (Facebook plugins) if we're running on OS X 10.10 (Yosemite) or later.
+ // A "fbplugin" file crashes on load, in the call to LoadPlugin() below.
+ // See bug 1086977.
+ if (nsCocoaFeatures::OnYosemiteOrLater()) {
+ if (fileName.EqualsLiteral("fbplugin") ||
+ StringBeginsWith(fileName, NS_LITERAL_CSTRING("fbplugin_"))) {
+ nsAutoCString msg;
+ msg.AppendPrintf("Preventing load of %s (see bug 1086977)",
+ fileName.get());
+ NS_WARNING(msg.get());
+ return NS_ERROR_FAILURE;
+ }
+#if defined(MOZ_CRASHREPORTER)
+ // The block above assumes that "fbplugin" is the filename of the plugin
+ // to be blocked, or that the filename starts with "fbplugin_". But we
+ // don't yet know for sure if this is always true. So for the time being
+ // record extra information in our crash logs.
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"),
+ fileName);
+#endif
+ }
+
+ // It's possible that our plugin has 2 entry points that'll give us mime type
+ // info. Quicktime does this to get around the need of having admin rights to
+ // change mime info in the resource fork. We need to use this info instead of
+ // the resource. See bug 113464.
+
+ // Sadly we have to load the library for this to work.
+ rv = LoadPlugin(outLibrary);
+#if defined(MOZ_CRASHREPORTER)
+ if (nsCocoaFeatures::OnYosemiteOrLater()) {
+ // If we didn't crash in LoadPlugin(), change the previous annotation so we
+ // don't sow confusion.
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"),
+ NS_LITERAL_CSTRING("Didn't crash, please ignore"));
+ }
+#endif
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Try to get data from NP_GetMIMEDescription
+ if (pLibrary) {
+ NP_GETMIMEDESCRIPTION pfnGetMimeDesc = (NP_GETMIMEDESCRIPTION)PR_FindFunctionSymbol(pLibrary, NP_GETMIMEDESCRIPTION_NAME);
+ if (pfnGetMimeDesc)
+ ParsePluginMimeDescription(pfnGetMimeDesc(), info);
+ if (info.fVariantCount)
+ return NS_OK;
+ }
+
+ // We'll fill this in using BP_GetSupportedMIMETypes and/or resource fork data
+ BPSupportedMIMETypes mi = {kBPSupportedMIMETypesStructVers_1, nullptr, nullptr};
+
+ // Try to get data from BP_GetSupportedMIMETypes
+ if (pLibrary) {
+ BP_GETSUPPORTEDMIMETYPES pfnMime = (BP_GETSUPPORTEDMIMETYPES)PR_FindFunctionSymbol(pLibrary, "BP_GetSupportedMIMETypes");
+ if (pfnMime && noErr == pfnMime(&mi, 0) && mi.typeStrings) {
+ info.fVariantCount = (**(short**)mi.typeStrings) / 2;
+ ::HLock(mi.typeStrings);
+ if (mi.infoStrings) // it's possible some plugins have infoStrings missing
+ ::HLock(mi.infoStrings);
+ }
+ }
+
+ // Fill in the info struct based on the data in the BPSupportedMIMETypes struct
+ int variantCount = info.fVariantCount;
+ info.fMimeTypeArray = static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
+ if (!info.fMimeTypeArray)
+ return NS_ERROR_OUT_OF_MEMORY;
+ info.fExtensionArray = static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
+ if (!info.fExtensionArray)
+ return NS_ERROR_OUT_OF_MEMORY;
+ if (mi.infoStrings) {
+ info.fMimeDescriptionArray = static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
+ if (!info.fMimeDescriptionArray)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ short mimeIndex = 2;
+ short descriptionIndex = 2;
+ for (int i = 0; i < variantCount; i++) {
+ info.fMimeTypeArray[i] = GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex);
+ info.fExtensionArray[i] = GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex);
+ if (mi.infoStrings)
+ info.fMimeDescriptionArray[i] = GetNextPluginStringFromHandle(mi.infoStrings, &descriptionIndex);
+ }
+
+ ::HUnlock(mi.typeStrings);
+ ::DisposeHandle(mi.typeStrings);
+ if (mi.infoStrings) {
+ ::HUnlock(mi.infoStrings);
+ ::DisposeHandle(mi.infoStrings);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
+{
+ free(info.fName);
+ free(info.fDescription);
+ int variantCount = info.fVariantCount;
+ for (int i = 0; i < variantCount; i++) {
+ free(info.fMimeTypeArray[i]);
+ free(info.fExtensionArray[i]);
+ free(info.fMimeDescriptionArray[i]);
+ }
+ free(info.fMimeTypeArray);
+ free(info.fMimeDescriptionArray);
+ free(info.fExtensionArray);
+ free(info.fFileName);
+ free(info.fFullPath);
+ free(info.fVersion);
+
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginsDirUnix.cpp b/dom/plugins/base/nsPluginsDirUnix.cpp
new file mode 100644
index 000000000..6d112b4fe
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirUnix.cpp
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsIMemory.h"
+#include "nsPluginsDir.h"
+#include "nsPluginsDirUtils.h"
+#include "prmem.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prio.h"
+#include <sys/stat.h>
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+
+#define LOCAL_PLUGIN_DLL_SUFFIX ".so"
+#if defined(__hpux)
+#define DEFAULT_X11_PATH "/usr/lib/X11R6/"
+#undef LOCAL_PLUGIN_DLL_SUFFIX
+#define LOCAL_PLUGIN_DLL_SUFFIX ".sl"
+#define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so"
+#elif defined(_AIX)
+#define DEFAULT_X11_PATH "/usr/lib"
+#define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".a"
+#elif defined(SOLARIS)
+#define DEFAULT_X11_PATH "/usr/openwin/lib/"
+#elif defined(LINUX)
+#define DEFAULT_X11_PATH "/usr/X11R6/lib/"
+#elif defined(__APPLE__)
+#define DEFAULT_X11_PATH "/usr/X11R6/lib"
+#undef LOCAL_PLUGIN_DLL_SUFFIX
+#define LOCAL_PLUGIN_DLL_SUFFIX ".dylib"
+#define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so"
+#else
+#define DEFAULT_X11_PATH ""
+#endif
+
+#if (MOZ_WIDGET_GTK == 2)
+
+#define PLUGIN_MAX_LEN_OF_TMP_ARR 512
+
+static void DisplayPR_LoadLibraryErrorMessage(const char *libName)
+{
+ char errorMsg[PLUGIN_MAX_LEN_OF_TMP_ARR] = "Cannot get error from NSPR.";
+ if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
+ PR_GetErrorText(errorMsg);
+
+ fprintf(stderr, "LoadPlugin: failed to initialize shared library %s [%s]\n",
+ libName, errorMsg);
+}
+
+static void SearchForSoname(const char* name, char** soname)
+{
+ if (!(name && soname))
+ return;
+ PRDir *fdDir = PR_OpenDir(DEFAULT_X11_PATH);
+ if (!fdDir)
+ return;
+
+ int n = strlen(name);
+ PRDirEntry *dirEntry;
+ while ((dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH))) {
+ if (!PL_strncmp(dirEntry->name, name, n)) {
+ if (dirEntry->name[n] == '.' && dirEntry->name[n+1] && !dirEntry->name[n+2]) {
+ // name.N, wild guess this is what we need
+ char out[PLUGIN_MAX_LEN_OF_TMP_ARR] = DEFAULT_X11_PATH;
+ PL_strcat(out, dirEntry->name);
+ *soname = PL_strdup(out);
+ break;
+ }
+ }
+ }
+
+ PR_CloseDir(fdDir);
+}
+
+static bool LoadExtraSharedLib(const char *name, char **soname, bool tryToGetSoname)
+{
+ bool ret = true;
+ PRLibSpec tempSpec;
+ PRLibrary *handle;
+ tempSpec.type = PR_LibSpec_Pathname;
+ tempSpec.value.pathname = name;
+ handle = PR_LoadLibraryWithFlags(tempSpec, PR_LD_NOW|PR_LD_GLOBAL);
+ if (!handle) {
+ ret = false;
+ DisplayPR_LoadLibraryErrorMessage(name);
+ if (tryToGetSoname) {
+ SearchForSoname(name, soname);
+ if (*soname) {
+ ret = LoadExtraSharedLib((const char *) *soname, nullptr, false);
+ }
+ }
+ }
+ return ret;
+}
+
+#define PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS 32
+#define PREF_PLUGINS_SONAME "plugin.soname.list"
+#if defined(SOLARIS) || defined(HPUX)
+#define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX ":libXm" LOCAL_PLUGIN_DLL_SUFFIX
+#else
+#define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX
+#endif
+/*
+ this function looks for
+ user_pref("plugin.soname.list", "/usr/X11R6/lib/libXt.so.6:libXext.so");
+ in user's pref.js
+ and loads all libs in specified order
+*/
+
+static void LoadExtraSharedLibs()
+{
+ // check out if user's prefs.js has libs name
+ nsresult res;
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &res));
+ if (NS_SUCCEEDED(res) && (prefs != nullptr)) {
+ char *sonameList = nullptr;
+ bool prefSonameListIsSet = true;
+ res = prefs->GetCharPref(PREF_PLUGINS_SONAME, &sonameList);
+ if (!sonameList) {
+ // pref is not set, lets use hardcoded list
+ prefSonameListIsSet = false;
+ sonameList = PL_strdup(DEFAULT_EXTRA_LIBS_LIST);
+ }
+ if (sonameList) {
+ char *arrayOfLibs[PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS] = {0};
+ int numOfLibs = 0;
+ char *nextToken;
+ char *p = nsCRT::strtok(sonameList,":",&nextToken);
+ if (p) {
+ while (p && numOfLibs < PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS) {
+ arrayOfLibs[numOfLibs++] = p;
+ p = nsCRT::strtok(nextToken,":",&nextToken);
+ }
+ } else // there is just one lib
+ arrayOfLibs[numOfLibs++] = sonameList;
+
+ char sonameListToSave[PLUGIN_MAX_LEN_OF_TMP_ARR] = "";
+ for (int i=0; i<numOfLibs; i++) {
+ // trim out head/tail white spaces (just in case)
+ bool head = true;
+ p = arrayOfLibs[i];
+ while (*p) {
+ if (*p == ' ' || *p == '\t') {
+ if (head) {
+ arrayOfLibs[i] = ++p;
+ } else {
+ *p = 0;
+ }
+ } else {
+ head = false;
+ p++;
+ }
+ }
+ if (!arrayOfLibs[i][0]) {
+ continue; // null string
+ }
+ bool tryToGetSoname = true;
+ if (PL_strchr(arrayOfLibs[i], '/')) {
+ //assuming it's real name, try to stat it
+ struct stat st;
+ if (stat((const char*) arrayOfLibs[i], &st)) {
+ //get just a file name
+ arrayOfLibs[i] = PL_strrchr(arrayOfLibs[i], '/') + 1;
+ } else
+ tryToGetSoname = false;
+ }
+ char *soname = nullptr;
+ if (LoadExtraSharedLib(arrayOfLibs[i], &soname, tryToGetSoname)) {
+ //construct soname's list to save in prefs
+ p = soname ? soname : arrayOfLibs[i];
+ int n = PLUGIN_MAX_LEN_OF_TMP_ARR -
+ (strlen(sonameListToSave) + strlen(p));
+ if (n > 0) {
+ PL_strcat(sonameListToSave, p);
+ PL_strcat(sonameListToSave,":");
+ }
+ if (soname) {
+ PL_strfree(soname); // it's from strdup
+ }
+ if (numOfLibs > 1)
+ arrayOfLibs[i][strlen(arrayOfLibs[i])] = ':'; //restore ":" in sonameList
+ }
+ }
+
+ // Check whether sonameListToSave is a empty String, Bug: 329205
+ if (sonameListToSave[0])
+ for (p = &sonameListToSave[strlen(sonameListToSave) - 1]; *p == ':'; p--)
+ *p = 0; //delete tail ":" delimiters
+
+ if (!prefSonameListIsSet || PL_strcmp(sonameList, sonameListToSave)) {
+ // if user specified some bogus soname I overwrite it here,
+ // otherwise it'll decrease performance by calling popen() in SearchForSoname
+ // every time for each bogus name
+ prefs->SetCharPref(PREF_PLUGINS_SONAME, (const char *)sonameListToSave);
+ }
+ PL_strfree(sonameList);
+ }
+ }
+}
+#endif //MOZ_WIDGET_GTK == 2
+
+/* nsPluginsDir implementation */
+
+bool nsPluginsDir::IsPluginFile(nsIFile* file)
+{
+ nsAutoCString filename;
+ if (NS_FAILED(file->GetNativeLeafName(filename)))
+ return false;
+
+#ifdef ANDROID
+ // It appears that if you load
+ // 'libstagefright_honeycomb.so' on froyo, or
+ // 'libstagefright_froyo.so' on honeycomb, we will abort.
+ // Since these are just helper libs, we can ignore.
+ const char *cFile = filename.get();
+ if (strstr(cFile, "libstagefright") != nullptr)
+ return false;
+#endif
+
+ NS_NAMED_LITERAL_CSTRING(dllSuffix, LOCAL_PLUGIN_DLL_SUFFIX);
+ if (filename.Length() > dllSuffix.Length() &&
+ StringEndsWith(filename, dllSuffix))
+ return true;
+
+#ifdef LOCAL_PLUGIN_DLL_ALT_SUFFIX
+ NS_NAMED_LITERAL_CSTRING(dllAltSuffix, LOCAL_PLUGIN_DLL_ALT_SUFFIX);
+ if (filename.Length() > dllAltSuffix.Length() &&
+ StringEndsWith(filename, dllAltSuffix))
+ return true;
+#endif
+ return false;
+}
+
+/* nsPluginFile implementation */
+
+nsPluginFile::nsPluginFile(nsIFile* file)
+: mPlugin(file)
+{
+}
+
+nsPluginFile::~nsPluginFile()
+{
+}
+
+nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
+{
+ PRLibSpec libSpec;
+ libSpec.type = PR_LibSpec_Pathname;
+ bool exists = false;
+ mPlugin->Exists(&exists);
+ if (!exists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ nsresult rv;
+ nsAutoCString path;
+ rv = mPlugin->GetNativePath(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ libSpec.value.pathname = path.get();
+
+#if (MOZ_WIDGET_GTK == 2)
+
+ // Normally, Mozilla isn't linked against libXt and libXext
+ // since it's a Gtk/Gdk application. On the other hand,
+ // legacy plug-ins expect the libXt and libXext symbols
+ // to already exist in the global name space. This plug-in
+ // wrapper is linked against libXt and libXext, but since
+ // we never call on any of these libraries, plug-ins still
+ // fail to resolve Xt symbols when trying to do a dlopen
+ // at runtime. Explicitly opening Xt/Xext into the global
+ // namespace before attempting to load the plug-in seems to
+ // work fine.
+
+
+#if defined(SOLARIS) || defined(HPUX)
+ // Acrobat/libXm: Lazy resolving might cause crash later (bug 211587)
+ *outLibrary = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW);
+ pLibrary = *outLibrary;
+#else
+ // Some dlopen() doesn't recover from a failed PR_LD_NOW (bug 223744)
+ *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
+ pLibrary = *outLibrary;
+#endif
+ if (!pLibrary) {
+ LoadExtraSharedLibs();
+ // try reload plugin once more
+ *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
+ pLibrary = *outLibrary;
+ if (!pLibrary) {
+ DisplayPR_LoadLibraryErrorMessage(libSpec.value.pathname);
+ return NS_ERROR_FAILURE;
+ }
+ }
+#else
+ *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
+ pLibrary = *outLibrary;
+#endif // MOZ_WIDGET_GTK == 2
+
+#ifdef DEBUG
+ printf("LoadPlugin() %s returned %lx\n",
+ libSpec.value.pathname, (unsigned long)pLibrary);
+#endif
+
+ if (!pLibrary) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
+{
+ *outLibrary = nullptr;
+
+ info.fVersion = nullptr;
+
+ // Sadly we have to load the library for this to work.
+ nsresult rv = LoadPlugin(outLibrary);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const char* (*npGetPluginVersion)() =
+ (const char* (*)()) PR_FindFunctionSymbol(pLibrary, "NP_GetPluginVersion");
+ if (npGetPluginVersion) {
+ info.fVersion = PL_strdup(npGetPluginVersion());
+ }
+
+ const char* (*npGetMIMEDescription)() =
+ (const char* (*)()) PR_FindFunctionSymbol(pLibrary, "NP_GetMIMEDescription");
+ if (!npGetMIMEDescription) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const char* mimedescr = npGetMIMEDescription();
+ if (!mimedescr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = ParsePluginMimeDescription(mimedescr, info);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsAutoCString path;
+ if (NS_FAILED(rv = mPlugin->GetNativePath(path)))
+ return rv;
+ info.fFullPath = PL_strdup(path.get());
+
+ nsAutoCString fileName;
+ if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName)))
+ return rv;
+ info.fFileName = PL_strdup(fileName.get());
+
+ NP_GetValueFunc npGetValue = (NP_GetValueFunc)PR_FindFunctionSymbol(pLibrary, "NP_GetValue");
+ if (!npGetValue) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const char *name = nullptr;
+ npGetValue(nullptr, NPPVpluginNameString, &name);
+ if (name) {
+ info.fName = PL_strdup(name);
+ }
+ else {
+ info.fName = PL_strdup(fileName.get());
+ }
+
+ const char *description = nullptr;
+ npGetValue(nullptr, NPPVpluginDescriptionString, &description);
+ if (description) {
+ info.fDescription = PL_strdup(description);
+ }
+ else {
+ info.fDescription = PL_strdup("");
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
+{
+ if (info.fName != nullptr)
+ PL_strfree(info.fName);
+
+ if (info.fDescription != nullptr)
+ PL_strfree(info.fDescription);
+
+ for (uint32_t i = 0; i < info.fVariantCount; i++) {
+ if (info.fMimeTypeArray[i] != nullptr)
+ PL_strfree(info.fMimeTypeArray[i]);
+
+ if (info.fMimeDescriptionArray[i] != nullptr)
+ PL_strfree(info.fMimeDescriptionArray[i]);
+
+ if (info.fExtensionArray[i] != nullptr)
+ PL_strfree(info.fExtensionArray[i]);
+ }
+
+ PR_FREEIF(info.fMimeTypeArray);
+ PR_FREEIF(info.fMimeDescriptionArray);
+ PR_FREEIF(info.fExtensionArray);
+
+ if (info.fFullPath != nullptr)
+ PL_strfree(info.fFullPath);
+
+ if (info.fFileName != nullptr)
+ PL_strfree(info.fFileName);
+
+ if (info.fVersion != nullptr)
+ PL_strfree(info.fVersion);
+
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginsDirUtils.h b/dom/plugins/base/nsPluginsDirUtils.h
new file mode 100644
index 000000000..178dc6968
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirUtils.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef nsPluginsDirUtils_h___
+#define nsPluginsDirUtils_h___
+
+#include "nsPluginsDir.h"
+#include "nsTArray.h"
+#include "prmem.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Output format from NPP_GetMIMEDescription: "...mime type[;version]:[extension]:[desecription];..."
+// The ambiguity of mime description could cause the browser fail to parse the MIME types
+// correctly.
+// E.g. "mime type::desecription;" // correct w/o ext
+// "mime type:desecription;" // wrong w/o ext
+//
+static nsresult
+ParsePluginMimeDescription(const char *mdesc, nsPluginInfo &info)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+ if (!mdesc || !*mdesc)
+ return rv;
+
+ char *mdescDup = PL_strdup(mdesc); // make a dup of intput string we'll change it content
+ char anEmptyString[] = "";
+ AutoTArray<char*, 8> tmpMimeTypeArr;
+ char delimiters[] = {':',':',';'};
+ int mimeTypeVariantCount = 0;
+ char *p = mdescDup; // make a dup of intput string we'll change it content
+ while(p) {
+ char *ptrMimeArray[] = {anEmptyString, anEmptyString, anEmptyString};
+
+ // It's easy to point out ptrMimeArray[0] to the string sounds like
+ // "Mime type is not specified, plugin will not function properly."
+ // and show this on "about:plugins" page, but we have to mark this particular
+ // mime type of given plugin as disable on "about:plugins" page,
+ // which feature is not implemented yet.
+ // So we'll ignore, without any warnings, an empty description strings,
+ // in other words, if after parsing ptrMimeArray[0] == anEmptyString is true.
+ // It is possible do not to registry a plugin at all if it returns
+ // an empty string on GetMIMEDescription() call,
+ // e.g. plugger returns "" if pluggerrc file is not found.
+
+ char *s = p;
+ int i;
+ for (i = 0; i < (int) sizeof(delimiters) && (p = PL_strchr(s, delimiters[i])); i++) {
+ ptrMimeArray[i] = s; // save start ptr
+ *p++ = 0; // overwrite delimiter
+ s = p; // move forward
+ }
+ if (i == 2)
+ ptrMimeArray[i] = s;
+ // fill out the temp array
+ // the order is important, it should be the same in for loop below
+ if (ptrMimeArray[0] != anEmptyString) {
+ tmpMimeTypeArr.AppendElement(ptrMimeArray[0]);
+ tmpMimeTypeArr.AppendElement(ptrMimeArray[1]);
+ tmpMimeTypeArr.AppendElement(ptrMimeArray[2]);
+ mimeTypeVariantCount++;
+ }
+ }
+
+ // fill out info structure
+ if (mimeTypeVariantCount) {
+ info.fVariantCount = mimeTypeVariantCount;
+ // we can do these 3 mallocs at once, later on code cleanup
+ info.fMimeTypeArray = (char **)PR_Malloc(mimeTypeVariantCount * sizeof(char *));
+ info.fMimeDescriptionArray = (char **)PR_Malloc(mimeTypeVariantCount * sizeof(char *));
+ info.fExtensionArray = (char **)PR_Malloc(mimeTypeVariantCount * sizeof(char *));
+
+ int j,i;
+ for (j = i = 0; i < mimeTypeVariantCount; i++) {
+ // the order is important, do not change it
+ // we can get rid of PL_strdup here, later on code cleanup
+ info.fMimeTypeArray[i] = PL_strdup(tmpMimeTypeArr.ElementAt(j++));
+ info.fExtensionArray[i] = PL_strdup(tmpMimeTypeArr.ElementAt(j++));
+ info.fMimeDescriptionArray[i] = PL_strdup(tmpMimeTypeArr.ElementAt(j++));
+ }
+ rv = NS_OK;
+ }
+ if (mdescDup)
+ PR_Free(mdescDup);
+ return rv;
+}
+
+#endif /* nsPluginsDirUtils_h___ */
diff --git a/dom/plugins/base/nsPluginsDirWin.cpp b/dom/plugins/base/nsPluginsDirWin.cpp
new file mode 100644
index 000000000..8c2d26ca2
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirWin.cpp
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+/*
+ nsPluginsDirWin.cpp
+
+ Windows implementation of the nsPluginsDir/nsPluginsFile classes.
+
+ by Alex Musil
+ */
+
+#include "mozilla/ArrayUtils.h" // ArrayLength
+#include "mozilla/DebugOnly.h"
+
+#include "nsPluginsDir.h"
+#include "prlink.h"
+#include "plstr.h"
+#include "prmem.h"
+#include "prprf.h"
+
+#include "windows.h"
+#include "winbase.h"
+
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsUnicharUtils.h"
+
+#include <shlwapi.h>
+#define SHOCKWAVE_BASE_FILENAME L"np32dsw"
+/**
+ * Determines whether or not SetDllDirectory should be called for this plugin.
+ *
+ * @param pluginFilePath The full path of the plugin file
+ * @return true if SetDllDirectory can be called for the plugin
+ */
+bool
+ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath)
+{
+ LPCWSTR passedInFilename = PathFindFileName(pluginFilePath);
+ if (!passedInFilename) {
+ return true;
+ }
+
+ // Somewhere in the middle of 11.6 version of Shockwave, naming of the DLL
+ // after its version number is introduced.
+ if (!wcsicmp(passedInFilename, SHOCKWAVE_BASE_FILENAME L".dll")) {
+ return false;
+ }
+
+ // Shockwave versions before 1202122 will break if you call SetDllDirectory
+ const uint64_t kFixedShockwaveVersion = 1202122;
+ uint64_t version;
+ int found = swscanf(passedInFilename, SHOCKWAVE_BASE_FILENAME L"_%llu.dll",
+ &version);
+ if (found && version < kFixedShockwaveVersion) {
+ return false;
+ }
+
+ // We always want to call SetDllDirectory otherwise
+ return true;
+}
+
+using namespace mozilla;
+
+/* Local helper functions */
+
+static char* GetKeyValue(void* verbuf, const WCHAR* key,
+ UINT language, UINT codepage)
+{
+ WCHAR keybuf[64]; // plenty for the template below, with the longest key
+ // we use (currently "FileDescription")
+ const WCHAR keyFormat[] = L"\\StringFileInfo\\%04X%04X\\%ls";
+ WCHAR *buf = nullptr;
+ UINT blen;
+
+ if (_snwprintf_s(keybuf, ArrayLength(keybuf), _TRUNCATE,
+ keyFormat, language, codepage, key) < 0)
+ {
+ NS_NOTREACHED("plugin info key too long for buffer!");
+ return nullptr;
+ }
+
+ if (::VerQueryValueW(verbuf, keybuf, (void **)&buf, &blen) == 0 ||
+ buf == nullptr || blen == 0)
+ {
+ return nullptr;
+ }
+
+ return PL_strdup(NS_ConvertUTF16toUTF8(buf, blen).get());
+}
+
+static char* GetVersion(void* verbuf)
+{
+ VS_FIXEDFILEINFO *fileInfo;
+ UINT fileInfoLen;
+
+ ::VerQueryValueW(verbuf, L"\\", (void **)&fileInfo, &fileInfoLen);
+
+ if (fileInfo) {
+ return PR_smprintf("%ld.%ld.%ld.%ld",
+ HIWORD(fileInfo->dwFileVersionMS),
+ LOWORD(fileInfo->dwFileVersionMS),
+ HIWORD(fileInfo->dwFileVersionLS),
+ LOWORD(fileInfo->dwFileVersionLS));
+ }
+
+ return nullptr;
+}
+
+// Returns a boolean indicating if the key's value contains a string
+// entry equal to "1" or "0". No entry for the key returns false.
+static bool GetBooleanFlag(void* verbuf, const WCHAR* key,
+ UINT language, UINT codepage)
+{
+ char* flagStr = GetKeyValue(verbuf, key, language, codepage);
+ if (!flagStr) {
+ return false;
+ }
+ bool result = (PL_strncmp("1", flagStr, 1) == 0);
+ PL_strfree(flagStr);
+ return result;
+}
+
+static uint32_t CalculateVariantCount(char* mimeTypes)
+{
+ uint32_t variants = 1;
+
+ if (!mimeTypes)
+ return 0;
+
+ char* index = mimeTypes;
+ while (*index) {
+ if (*index == '|')
+ variants++;
+
+ ++index;
+ }
+ return variants;
+}
+
+static char** MakeStringArray(uint32_t variants, char* data)
+{
+ // The number of variants has been calculated based on the mime
+ // type array. Plugins are not explicitely required to match
+ // this number in two other arrays: file extention array and mime
+ // description array, and some of them actually don't.
+ // We should handle such situations gracefully
+
+ if ((variants <= 0) || !data)
+ return nullptr;
+
+ char ** array = (char **)PR_Calloc(variants, sizeof(char *));
+ if (!array)
+ return nullptr;
+
+ char * start = data;
+
+ for (uint32_t i = 0; i < variants; i++) {
+ char * p = PL_strchr(start, '|');
+ if (p)
+ *p = 0;
+
+ array[i] = PL_strdup(start);
+
+ if (!p) {
+ // nothing more to look for, fill everything left
+ // with empty strings and break
+ while(++i < variants)
+ array[i] = PL_strdup("");
+
+ break;
+ }
+
+ start = ++p;
+ }
+ return array;
+}
+
+static void FreeStringArray(uint32_t variants, char ** array)
+{
+ if ((variants == 0) || !array)
+ return;
+
+ for (uint32_t i = 0; i < variants; i++) {
+ if (array[i]) {
+ PL_strfree(array[i]);
+ array[i] = nullptr;
+ }
+ }
+ PR_Free(array);
+}
+
+static bool CanLoadPlugin(char16ptr_t aBinaryPath)
+{
+#if defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64)
+ bool canLoad = false;
+
+ HANDLE file = CreateFileW(aBinaryPath, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file != INVALID_HANDLE_VALUE) {
+ HANDLE map = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0,
+ GetFileSize(file, nullptr), nullptr);
+ if (map != nullptr) {
+ LPVOID mapView = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
+ if (mapView != nullptr) {
+ if (((IMAGE_DOS_HEADER*)mapView)->e_magic == IMAGE_DOS_SIGNATURE) {
+ long peImageHeaderStart = (((IMAGE_DOS_HEADER*)mapView)->e_lfanew);
+ if (peImageHeaderStart != 0L) {
+ DWORD arch = (((IMAGE_NT_HEADERS*)((LPBYTE)mapView + peImageHeaderStart))->FileHeader.Machine);
+#ifdef _M_IX86
+ canLoad = (arch == IMAGE_FILE_MACHINE_I386);
+#elif defined(_M_X64)
+ canLoad = (arch == IMAGE_FILE_MACHINE_AMD64);
+#elif defined(_M_IA64)
+ canLoad = (arch == IMAGE_FILE_MACHINE_IA64);
+#endif
+ }
+ }
+ UnmapViewOfFile(mapView);
+ }
+ CloseHandle(map);
+ }
+ CloseHandle(file);
+ }
+
+ return canLoad;
+#else
+ // Assume correct binaries for unhandled cases.
+ return true;
+#endif
+}
+
+/* nsPluginsDir implementation */
+
+// The file name must be in the form "np*.dll"
+bool nsPluginsDir::IsPluginFile(nsIFile* file)
+{
+ nsAutoCString path;
+ if (NS_FAILED(file->GetNativePath(path)))
+ return false;
+
+ const char *cPath = path.get();
+
+ // this is most likely a path, so skip to the filename
+ const char* filename = PL_strrchr(cPath, '\\');
+ if (filename)
+ ++filename;
+ else
+ filename = cPath;
+
+ char* extension = PL_strrchr(filename, '.');
+ if (extension)
+ ++extension;
+
+ uint32_t fullLength = strlen(filename);
+ uint32_t extLength = extension ? strlen(extension) : 0;
+ if (fullLength >= 7 && extLength == 3) {
+ if (!PL_strncasecmp(filename, "np", 2) && !PL_strncasecmp(extension, "dll", 3)) {
+ // don't load OJI-based Java plugins
+ if (!PL_strncasecmp(filename, "npoji", 5) ||
+ !PL_strncasecmp(filename, "npjava", 6))
+ return false;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* nsPluginFile implementation */
+
+nsPluginFile::nsPluginFile(nsIFile* file)
+: mPlugin(file)
+{
+ // nada
+}
+
+nsPluginFile::~nsPluginFile()
+{
+ // nada
+}
+
+/**
+ * Loads the plugin into memory using NSPR's shared-library loading
+ * mechanism. Handles platform differences in loading shared libraries.
+ */
+nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
+{
+ if (!mPlugin)
+ return NS_ERROR_NULL_POINTER;
+
+ bool protectCurrentDirectory = true;
+
+ nsAutoString pluginFilePath;
+ mPlugin->GetPath(pluginFilePath);
+ protectCurrentDirectory =
+ ShouldProtectPluginCurrentDirectory(pluginFilePath.BeginReading());
+
+ nsAutoString pluginFolderPath = pluginFilePath;
+ int32_t idx = pluginFilePath.RFindChar('\\');
+ pluginFolderPath.SetLength(idx);
+
+ BOOL restoreOrigDir = FALSE;
+ WCHAR aOrigDir[MAX_PATH + 1];
+ DWORD dwCheck = GetCurrentDirectoryW(MAX_PATH, aOrigDir);
+ NS_ASSERTION(dwCheck <= MAX_PATH + 1, "Error in Loading plugin");
+
+ if (dwCheck <= MAX_PATH + 1) {
+ restoreOrigDir = SetCurrentDirectoryW(pluginFolderPath.get());
+ NS_ASSERTION(restoreOrigDir, "Error in Loading plugin");
+ }
+
+ if (protectCurrentDirectory) {
+ SetDllDirectory(nullptr);
+ }
+
+ nsresult rv = mPlugin->Load(outLibrary);
+ if (NS_FAILED(rv))
+ *outLibrary = nullptr;
+
+ if (protectCurrentDirectory) {
+ SetDllDirectory(L"");
+ }
+
+ if (restoreOrigDir) {
+ DebugOnly<BOOL> bCheck = SetCurrentDirectoryW(aOrigDir);
+ NS_ASSERTION(bCheck, "Error in Loading plugin");
+ }
+
+ return rv;
+}
+
+/**
+ * Obtains all of the information currently available for this plugin.
+ */
+nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
+{
+ *outLibrary = nullptr;
+
+ nsresult rv = NS_OK;
+ DWORD zerome, versionsize;
+ void* verbuf = nullptr;
+
+ if (!mPlugin)
+ return NS_ERROR_NULL_POINTER;
+
+ nsAutoString fullPath;
+ if (NS_FAILED(rv = mPlugin->GetPath(fullPath)))
+ return rv;
+
+ if (!CanLoadPlugin(fullPath.get()))
+ return NS_ERROR_FAILURE;
+
+ nsAutoString fileName;
+ if (NS_FAILED(rv = mPlugin->GetLeafName(fileName)))
+ return rv;
+
+ LPCWSTR lpFilepath = fullPath.get();
+
+ versionsize = ::GetFileVersionInfoSizeW(lpFilepath, &zerome);
+
+ if (versionsize > 0)
+ verbuf = PR_Malloc(versionsize);
+ if (!verbuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (::GetFileVersionInfoW(lpFilepath, 0, versionsize, verbuf))
+ {
+ // TODO: get appropriately-localized info from plugin file
+ UINT lang = 1033; // language = English, 0x409
+ UINT cp = 1252; // codepage = Western, 0x4E4
+ info.fName = GetKeyValue(verbuf, L"ProductName", lang, cp);
+ info.fDescription = GetKeyValue(verbuf, L"FileDescription", lang, cp);
+ info.fSupportsAsyncRender = GetBooleanFlag(verbuf, L"AsyncDrawingSupport", lang, cp);
+
+ char *mimeType = GetKeyValue(verbuf, L"MIMEType", lang, cp);
+ char *mimeDescription = GetKeyValue(verbuf, L"FileOpenName", lang, cp);
+ char *extensions = GetKeyValue(verbuf, L"FileExtents", lang, cp);
+
+ info.fVariantCount = CalculateVariantCount(mimeType);
+ info.fMimeTypeArray = MakeStringArray(info.fVariantCount, mimeType);
+ info.fMimeDescriptionArray = MakeStringArray(info.fVariantCount, mimeDescription);
+ info.fExtensionArray = MakeStringArray(info.fVariantCount, extensions);
+ info.fFullPath = PL_strdup(NS_ConvertUTF16toUTF8(fullPath).get());
+ info.fFileName = PL_strdup(NS_ConvertUTF16toUTF8(fileName).get());
+ info.fVersion = GetVersion(verbuf);
+
+ PL_strfree(mimeType);
+ PL_strfree(mimeDescription);
+ PL_strfree(extensions);
+ }
+ else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ PR_Free(verbuf);
+
+ return rv;
+}
+
+nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
+{
+ if (info.fName)
+ PL_strfree(info.fName);
+
+ if (info.fDescription)
+ PL_strfree(info.fDescription);
+
+ if (info.fMimeTypeArray)
+ FreeStringArray(info.fVariantCount, info.fMimeTypeArray);
+
+ if (info.fMimeDescriptionArray)
+ FreeStringArray(info.fVariantCount, info.fMimeDescriptionArray);
+
+ if (info.fExtensionArray)
+ FreeStringArray(info.fVariantCount, info.fExtensionArray);
+
+ if (info.fFullPath)
+ PL_strfree(info.fFullPath);
+
+ if (info.fFileName)
+ PL_strfree(info.fFileName);
+
+ if (info.fVersion)
+ PR_smprintf_free(info.fVersion);
+
+ ZeroMemory((void *)&info, sizeof(info));
+
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nspluginroot.idl b/dom/plugins/base/nspluginroot.idl
new file mode 100644
index 000000000..6c931d371
--- /dev/null
+++ b/dom/plugins/base/nspluginroot.idl
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+native REFNSIID(REFNSIID);
+native nativeVoid(void *);
+native nativeChar(const char * *);
+[ptr] native constVoidPtr(const void);
+[ref] native PRUint32Ref(uint32_t);
+[ref] native PRUint16Ref(uint16_t);
+[ref] native constCharStarConstStar(const char* const*);
+[ptr] native constCharPtr(const char);
+[ref] native constCharStarRef(const char *);
+
+native NPWindowType(NPWindowType);
+native NPWindow(NPWindow);
+[ptr] native NPWindowPtr(NPWindow);
+[ref] native NPWindowStarRef(NPWindow *);
+[ptr] native NPPrintPtr(NPPrint);
+native NPByteRange(NPByteRange);
+[ptr] native NPByteRangePtr(NPByteRange);
+native NPPVariable(NPPVariable);
+native NPNVariable(NPNVariable);
+[ptr] native NPRectPtr(NPRect);
+native NPRegion(NPRegion);
+native NPDrawingModel(NPDrawingModel);
+native NPEventModel(NPEventModel);
+
+[ptr] native JRIEnvPtr(JRIEnv);
+native jref(jref);