summaryrefslogtreecommitdiffstats
path: root/dom/plugins/ipc
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/plugins/ipc
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/plugins/ipc')
-rw-r--r--dom/plugins/ipc/AStream.h27
-rw-r--r--dom/plugins/ipc/BrowserStreamChild.cpp304
-rw-r--r--dom/plugins/ipc/BrowserStreamChild.h173
-rw-r--r--dom/plugins/ipc/BrowserStreamParent.cpp222
-rw-r--r--dom/plugins/ipc/BrowserStreamParent.h78
-rw-r--r--dom/plugins/ipc/ChildAsyncCall.cpp52
-rw-r--r--dom/plugins/ipc/ChildAsyncCall.h41
-rw-r--r--dom/plugins/ipc/ChildTimer.cpp40
-rw-r--r--dom/plugins/ipc/ChildTimer.h58
-rw-r--r--dom/plugins/ipc/D3D11SurfaceHolder.cpp87
-rw-r--r--dom/plugins/ipc/D3D11SurfaceHolder.h50
-rw-r--r--dom/plugins/ipc/MiniShmParent.cpp218
-rw-r--r--dom/plugins/ipc/MiniShmParent.h96
-rw-r--r--dom/plugins/ipc/NPEventAndroid.h55
-rw-r--r--dom/plugins/ipc/NPEventOSX.h194
-rw-r--r--dom/plugins/ipc/NPEventUnix.h96
-rw-r--r--dom/plugins/ipc/NPEventWindows.h160
-rw-r--r--dom/plugins/ipc/PBrowserStream.ipdl69
-rw-r--r--dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl37
-rw-r--r--dom/plugins/ipc/PPluginInstance.ipdl329
-rw-r--r--dom/plugins/ipc/PPluginModule.ipdl171
-rw-r--r--dom/plugins/ipc/PPluginScriptableObject.ipdl102
-rw-r--r--dom/plugins/ipc/PPluginStream.ipdl37
-rw-r--r--dom/plugins/ipc/PPluginSurface.ipdl18
-rw-r--r--dom/plugins/ipc/PStreamNotify.ipdl39
-rw-r--r--dom/plugins/ipc/PluginAsyncSurrogate.cpp998
-rw-r--r--dom/plugins/ipc/PluginAsyncSurrogate.h188
-rw-r--r--dom/plugins/ipc/PluginBackgroundDestroyer.cpp40
-rw-r--r--dom/plugins/ipc/PluginBackgroundDestroyer.h56
-rw-r--r--dom/plugins/ipc/PluginBridge.h44
-rw-r--r--dom/plugins/ipc/PluginDataResolver.h26
-rw-r--r--dom/plugins/ipc/PluginHangUIParent.cpp445
-rw-r--r--dom/plugins/ipc/PluginHangUIParent.h159
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.cpp4684
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.h713
-rw-r--r--dom/plugins/ipc/PluginInstanceParent.cpp2509
-rw-r--r--dom/plugins/ipc/PluginInstanceParent.h475
-rw-r--r--dom/plugins/ipc/PluginInterposeOSX.h137
-rw-r--r--dom/plugins/ipc/PluginInterposeOSX.mm1158
-rw-r--r--dom/plugins/ipc/PluginLibrary.h115
-rw-r--r--dom/plugins/ipc/PluginMessageUtils.cpp155
-rw-r--r--dom/plugins/ipc/PluginMessageUtils.h750
-rw-r--r--dom/plugins/ipc/PluginModuleChild.cpp2681
-rw-r--r--dom/plugins/ipc/PluginModuleChild.h387
-rwxr-xr-xdom/plugins/ipc/PluginModuleParent.cpp3384
-rw-r--r--dom/plugins/ipc/PluginModuleParent.h686
-rw-r--r--dom/plugins/ipc/PluginProcessChild.cpp149
-rw-r--r--dom/plugins/ipc/PluginProcessChild.h55
-rw-r--r--dom/plugins/ipc/PluginProcessParent.cpp257
-rw-r--r--dom/plugins/ipc/PluginProcessParent.h94
-rw-r--r--dom/plugins/ipc/PluginQuirks.cpp78
-rw-r--r--dom/plugins/ipc/PluginQuirks.h68
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectChild.cpp1298
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectChild.h342
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectParent.cpp1393
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectParent.h233
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectUtils-inl.h166
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectUtils.h306
-rw-r--r--dom/plugins/ipc/PluginStreamChild.cpp64
-rw-r--r--dom/plugins/ipc/PluginStreamChild.h55
-rw-r--r--dom/plugins/ipc/PluginStreamParent.cpp72
-rw-r--r--dom/plugins/ipc/PluginStreamParent.h46
-rw-r--r--dom/plugins/ipc/PluginSurfaceParent.cpp34
-rw-r--r--dom/plugins/ipc/PluginSurfaceParent.h40
-rw-r--r--dom/plugins/ipc/PluginTypes.ipdlh35
-rw-r--r--dom/plugins/ipc/PluginUtilsOSX.h92
-rw-r--r--dom/plugins/ipc/PluginUtilsOSX.mm462
-rw-r--r--dom/plugins/ipc/PluginUtilsWin.cpp237
-rw-r--r--dom/plugins/ipc/PluginUtilsWin.h23
-rw-r--r--dom/plugins/ipc/PluginWidgetChild.cpp71
-rw-r--r--dom/plugins/ipc/PluginWidgetChild.h39
-rw-r--r--dom/plugins/ipc/PluginWidgetParent.cpp237
-rw-r--r--dom/plugins/ipc/PluginWidgetParent.h65
-rw-r--r--dom/plugins/ipc/StreamNotifyChild.h64
-rw-r--r--dom/plugins/ipc/StreamNotifyParent.h48
-rw-r--r--dom/plugins/ipc/hangui/HangUIDlg.h18
-rw-r--r--dom/plugins/ipc/hangui/HangUIDlg.rc26
-rw-r--r--dom/plugins/ipc/hangui/MiniShmBase.h334
-rw-r--r--dom/plugins/ipc/hangui/MiniShmChild.cpp173
-rw-r--r--dom/plugins/ipc/hangui/MiniShmChild.h68
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUI.h54
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUIChild.cpp425
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUIChild.h113
-rw-r--r--dom/plugins/ipc/hangui/module.ver6
-rw-r--r--dom/plugins/ipc/hangui/moz.build27
-rw-r--r--dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest38
-rw-r--r--dom/plugins/ipc/interpose/moz.build13
-rw-r--r--dom/plugins/ipc/interpose/plugin_child_interpose.mm134
-rw-r--r--dom/plugins/ipc/moz.build153
89 files changed, 30248 insertions, 0 deletions
diff --git a/dom/plugins/ipc/AStream.h b/dom/plugins/ipc/AStream.h
new file mode 100644
index 000000000..dd48e844b
--- /dev/null
+++ b/dom/plugins/ipc/AStream.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 mozilla_plugins_AStream_h
+#define mozilla_plugins_AStream_h
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * When we are passed NPStream->{ndata,pdata} in {NPN,NPP}_DestroyStream, we
+ * don't know whether it's a plugin stream or a browser stream. This abstract
+ * class lets us cast to the right type of object and send the appropriate
+ * message.
+ */
+class AStream
+{
+public:
+ virtual bool IsBrowserStream() = 0;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
diff --git a/dom/plugins/ipc/BrowserStreamChild.cpp b/dom/plugins/ipc/BrowserStreamChild.cpp
new file mode 100644
index 000000000..4fdfc1d47
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamChild.cpp
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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/plugins/BrowserStreamChild.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/plugins/PluginInstanceChild.h"
+#include "mozilla/plugins/StreamNotifyChild.h"
+
+namespace mozilla {
+namespace plugins {
+
+BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance,
+ const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ StreamNotifyChild* notifyData,
+ const nsCString& headers)
+ : mInstance(instance)
+ , mStreamStatus(kStreamOpen)
+ , mDestroyPending(NOT_DESTROYED)
+ , mNotifyPending(false)
+ , mStreamAsFilePending(false)
+ , mInstanceDying(false)
+ , mState(CONSTRUCTING)
+ , mURL(url)
+ , mHeaders(headers)
+ , mStreamNotify(notifyData)
+ , mDeliveryTracker(this)
+{
+ PLUGIN_LOG_DEBUG(("%s (%s, %i, %i, %p, %s)", FULLFUNCTION,
+ url.get(), length, lastmodified, (void*) notifyData,
+ headers.get()));
+
+ AssertPluginThread();
+
+ memset(&mStream, 0, sizeof(mStream));
+ mStream.ndata = static_cast<AStream*>(this);
+ mStream.url = NullableStringGet(mURL);
+ mStream.end = length;
+ mStream.lastmodified = lastmodified;
+ mStream.headers = NullableStringGet(mHeaders);
+ if (notifyData) {
+ mStream.notifyData = notifyData->mClosure;
+ notifyData->SetAssociatedStream(this);
+ }
+}
+
+NPError
+BrowserStreamChild::StreamConstructed(
+ const nsCString& mimeType,
+ const bool& seekable,
+ uint16_t* stype)
+{
+ NPError rv = NPERR_NO_ERROR;
+
+ *stype = NP_NORMAL;
+ rv = mInstance->mPluginIface->newstream(
+ &mInstance->mData, const_cast<char*>(NullableStringGet(mimeType)),
+ &mStream, seekable, stype);
+ if (rv != NPERR_NO_ERROR) {
+ mState = DELETING;
+ if (mStreamNotify) {
+ mStreamNotify->SetAssociatedStream(nullptr);
+ mStreamNotify = nullptr;
+ }
+ }
+ else {
+ mState = ALIVE;
+ }
+
+ return rv;
+}
+
+BrowserStreamChild::~BrowserStreamChild()
+{
+ NS_ASSERTION(!mStreamNotify, "Should have nulled it by now!");
+}
+
+bool
+BrowserStreamChild::RecvWrite(const int32_t& offset,
+ const uint32_t& newlength,
+ const Buffer& data)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ AssertPluginThread();
+
+ if (ALIVE != mState)
+ NS_RUNTIMEABORT("Unexpected state: received data after NPP_DestroyStream?");
+
+ if (kStreamOpen != mStreamStatus)
+ return true;
+
+ mStream.end = newlength;
+
+ NS_ASSERTION(data.Length() > 0, "Empty data");
+
+ PendingData* newdata = mPendingData.AppendElement();
+ newdata->offset = offset;
+ newdata->data = data;
+ newdata->curpos = 0;
+
+ EnsureDeliveryPending();
+
+ return true;
+}
+
+bool
+BrowserStreamChild::RecvNPP_StreamAsFile(const nsCString& fname)
+{
+ PLUGIN_LOG_DEBUG(("%s (fname=%s)", FULLFUNCTION, fname.get()));
+
+ AssertPluginThread();
+
+ if (ALIVE != mState)
+ NS_RUNTIMEABORT("Unexpected state: received file after NPP_DestroyStream?");
+
+ if (kStreamOpen != mStreamStatus)
+ return true;
+
+ mStreamAsFilePending = true;
+ mStreamAsFileName = fname;
+ EnsureDeliveryPending();
+
+ return true;
+}
+
+bool
+BrowserStreamChild::RecvNPP_DestroyStream(const NPReason& reason)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ if (ALIVE != mState)
+ NS_RUNTIMEABORT("Unexpected state: recevied NPP_DestroyStream twice?");
+
+ mState = DYING;
+ mDestroyPending = DESTROY_PENDING;
+ if (NPRES_DONE != reason)
+ mStreamStatus = reason;
+
+ EnsureDeliveryPending();
+ return true;
+}
+
+bool
+BrowserStreamChild::Recv__delete__()
+{
+ AssertPluginThread();
+
+ if (DELETING != mState)
+ NS_RUNTIMEABORT("Bad state, not DELETING");
+
+ return true;
+}
+
+NPError
+BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ AssertPluginThread();
+
+ if (ALIVE != mState || kStreamOpen != mStreamStatus)
+ return NPERR_GENERIC_ERROR;
+
+ IPCByteRanges ranges;
+ for (; aRangeList; aRangeList = aRangeList->next) {
+ IPCByteRange br = {aRangeList->offset, aRangeList->length};
+ ranges.AppendElement(br);
+ }
+
+ NPError result;
+ CallNPN_RequestRead(ranges, &result);
+ return result;
+}
+
+void
+BrowserStreamChild::NPN_DestroyStream(NPReason reason)
+{
+ mStreamStatus = reason;
+ if (ALIVE == mState)
+ SendNPN_DestroyStream(reason);
+
+ EnsureDeliveryPending();
+}
+
+void
+BrowserStreamChild::EnsureDeliveryPending()
+{
+ MessageLoop::current()->PostTask(
+ mDeliveryTracker.NewRunnableMethod(&BrowserStreamChild::Deliver));
+}
+
+void
+BrowserStreamChild::Deliver()
+{
+ while (kStreamOpen == mStreamStatus && mPendingData.Length()) {
+ if (DeliverPendingData() && kStreamOpen == mStreamStatus) {
+ SetSuspendedTimer();
+ return;
+ }
+ }
+ ClearSuspendedTimer();
+
+ NS_ASSERTION(kStreamOpen != mStreamStatus || 0 == mPendingData.Length(),
+ "Exit out of the data-delivery loop with pending data");
+ mPendingData.Clear();
+
+ // NPP_StreamAsFile() is documented (at MDN) to be called "when the stream
+ // is complete" -- i.e. after all calls to NPP_WriteReady() and NPP_Write()
+ // have finished. We make these calls asynchronously (from
+ // DeliverPendingData()). So we need to make sure all the "pending data"
+ // has been "delivered" before calling NPP_StreamAsFile() (also
+ // asynchronously). Doing this resolves bug 687610, bug 670036 and possibly
+ // also other bugs.
+ if (mStreamAsFilePending) {
+ if (mStreamStatus == kStreamOpen)
+ mInstance->mPluginIface->asfile(&mInstance->mData, &mStream,
+ mStreamAsFileName.get());
+ mStreamAsFilePending = false;
+ }
+
+ if (DESTROY_PENDING == mDestroyPending) {
+ mDestroyPending = DESTROYED;
+ if (mState != DYING)
+ NS_RUNTIMEABORT("mDestroyPending but state not DYING");
+
+ NS_ASSERTION(NPRES_DONE != mStreamStatus, "Success status set too early!");
+ if (kStreamOpen == mStreamStatus)
+ mStreamStatus = NPRES_DONE;
+
+ (void) mInstance->mPluginIface
+ ->destroystream(&mInstance->mData, &mStream, mStreamStatus);
+ }
+ if (DESTROYED == mDestroyPending && mNotifyPending) {
+ NS_ASSERTION(mStreamNotify, "mDestroyPending but no mStreamNotify?");
+
+ mNotifyPending = false;
+ mStreamNotify->NPP_URLNotify(mStreamStatus);
+ delete mStreamNotify;
+ mStreamNotify = nullptr;
+ }
+ if (DYING == mState && DESTROYED == mDestroyPending
+ && !mStreamNotify && !mInstanceDying) {
+ SendStreamDestroyed();
+ mState = DELETING;
+ }
+}
+
+bool
+BrowserStreamChild::DeliverPendingData()
+{
+ if (mState != ALIVE && mState != DYING)
+ NS_RUNTIMEABORT("Unexpected state");
+
+ NS_ASSERTION(mPendingData.Length(), "Called from Deliver with empty pending");
+
+ while (mPendingData[0].curpos < static_cast<int32_t>(mPendingData[0].data.Length())) {
+ int32_t r = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
+ if (kStreamOpen != mStreamStatus)
+ return false;
+ if (0 == r) // plugin wants to suspend delivery
+ return true;
+
+ r = mInstance->mPluginIface->write(
+ &mInstance->mData, &mStream,
+ mPendingData[0].offset + mPendingData[0].curpos, // offset
+ mPendingData[0].data.Length() - mPendingData[0].curpos, // length
+ const_cast<char*>(mPendingData[0].data.BeginReading() + mPendingData[0].curpos));
+ if (kStreamOpen != mStreamStatus)
+ return false;
+ if (0 == r)
+ return true;
+ if (r < 0) { // error condition
+ NPN_DestroyStream(NPRES_NETWORK_ERR);
+ return false;
+ }
+ mPendingData[0].curpos += r;
+ }
+ mPendingData.RemoveElementAt(0);
+ return false;
+}
+
+void
+BrowserStreamChild::SetSuspendedTimer()
+{
+ if (mSuspendedTimer.IsRunning())
+ return;
+ mSuspendedTimer.Start(
+ base::TimeDelta::FromMilliseconds(100), // 100ms copied from Mozilla plugin host
+ this, &BrowserStreamChild::Deliver);
+}
+
+void
+BrowserStreamChild::ClearSuspendedTimer()
+{
+ mSuspendedTimer.Stop();
+}
+
+} /* namespace plugins */
+} /* namespace mozilla */
diff --git a/dom/plugins/ipc/BrowserStreamChild.h b/dom/plugins/ipc/BrowserStreamChild.h
new file mode 100644
index 000000000..ad334e4a3
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamChild.h
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 mozilla_plugins_BrowserStreamChild_h
+#define mozilla_plugins_BrowserStreamChild_h 1
+
+#include "mozilla/plugins/PBrowserStreamChild.h"
+#include "mozilla/plugins/AStream.h"
+#include "base/task.h"
+#include "base/timer.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceChild;
+class StreamNotifyChild;
+
+class BrowserStreamChild : public PBrowserStreamChild, public AStream
+{
+public:
+ BrowserStreamChild(PluginInstanceChild* instance,
+ const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ StreamNotifyChild* notifyData,
+ const nsCString& headers);
+ virtual ~BrowserStreamChild();
+
+ virtual bool IsBrowserStream() override { return true; }
+
+ NPError StreamConstructed(
+ const nsCString& mimeType,
+ const bool& seekable,
+ uint16_t* stype);
+
+ virtual bool RecvWrite(const int32_t& offset,
+ const uint32_t& newsize,
+ const Buffer& data) override;
+ virtual bool RecvNPP_StreamAsFile(const nsCString& fname) override;
+ virtual bool RecvNPP_DestroyStream(const NPReason& reason) override;
+ virtual bool Recv__delete__() override;
+
+ void EnsureCorrectInstance(PluginInstanceChild* i)
+ {
+ if (i != mInstance)
+ NS_RUNTIMEABORT("Incorrect stream instance");
+ }
+ void EnsureCorrectStream(NPStream* s)
+ {
+ if (s != &mStream)
+ NS_RUNTIMEABORT("Incorrect stream data");
+ }
+
+ NPError NPN_RequestRead(NPByteRange* aRangeList);
+ void NPN_DestroyStream(NPReason reason);
+
+ void NotifyPending() {
+ NS_ASSERTION(!mNotifyPending, "Pending twice?");
+ mNotifyPending = true;
+ EnsureDeliveryPending();
+ }
+
+ /**
+ * During instance destruction, artificially cancel all outstanding streams.
+ *
+ * @return false if we are already in the DELETING state.
+ */
+ bool InstanceDying() {
+ if (DELETING == mState)
+ return false;
+
+ mInstanceDying = true;
+ return true;
+ }
+
+ void FinishDelivery() {
+ NS_ASSERTION(mInstanceDying, "Should only be called after InstanceDying");
+ NS_ASSERTION(DELETING != mState, "InstanceDying didn't work?");
+ mStreamStatus = NPRES_USER_BREAK;
+ Deliver();
+ NS_ASSERTION(!mStreamNotify, "Didn't deliver NPN_URLNotify?");
+ }
+
+private:
+ friend class StreamNotifyChild;
+ using PBrowserStreamChild::SendNPN_DestroyStream;
+
+ /**
+ * Post an event to ensure delivery of pending data/destroy/urlnotify events
+ * outside of the current RPC stack.
+ */
+ void EnsureDeliveryPending();
+
+ /**
+ * Deliver data, destruction, notify scheduling
+ * or cancelling the suspended timer as needed.
+ */
+ void Deliver();
+
+ /**
+ * Deliver one chunk of pending data.
+ * @return true if the plugin indicated a pause was necessary
+ */
+ bool DeliverPendingData();
+
+ void SetSuspendedTimer();
+ void ClearSuspendedTimer();
+
+ PluginInstanceChild* mInstance;
+ NPStream mStream;
+
+ static const NPReason kStreamOpen = -1;
+
+ /**
+ * The plugin's notion of whether a stream has been "closed" (no more
+ * data delivery) differs from the plugin host due to asynchronous delivery
+ * of data and NPN_DestroyStream. While the plugin-visible stream is open,
+ * mStreamStatus should be kStreamOpen (-1). mStreamStatus will be a
+ * failure code if either the parent or child indicates stream failure.
+ */
+ NPReason mStreamStatus;
+
+ /**
+ * Delivery of NPP_DestroyStream and NPP_URLNotify must be postponed until
+ * all data has been delivered.
+ */
+ enum {
+ NOT_DESTROYED, // NPP_DestroyStream not yet received
+ DESTROY_PENDING, // NPP_DestroyStream received, not yet delivered
+ DESTROYED // NPP_DestroyStream delivered, NPP_URLNotify may still be pending
+ } mDestroyPending;
+ bool mNotifyPending;
+ bool mStreamAsFilePending;
+ nsCString mStreamAsFileName;
+
+ // When NPP_Destroy is called for our instance (manager), this flag is set
+ // cancels the stream and avoids sending StreamDestroyed.
+ bool mInstanceDying;
+
+ enum {
+ CONSTRUCTING,
+ ALIVE,
+ DYING,
+ DELETING
+ } mState;
+ nsCString mURL;
+ nsCString mHeaders;
+ StreamNotifyChild* mStreamNotify;
+
+ struct PendingData
+ {
+ int32_t offset;
+ Buffer data;
+ int32_t curpos;
+ };
+ nsTArray<PendingData> mPendingData;
+
+ /**
+ * Asynchronous RecvWrite messages are never delivered to the plugin
+ * immediately, because that may be in the midst of an unexpected RPC
+ * stack frame. It instead posts a runnable using this tracker to cancel
+ * in case we are destroyed.
+ */
+ ScopedRunnableMethodFactory<BrowserStreamChild> mDeliveryTracker;
+ base::RepeatingTimer<BrowserStreamChild> mSuspendedTimer;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif /* mozilla_plugins_BrowserStreamChild_h */
diff --git a/dom/plugins/ipc/BrowserStreamParent.cpp b/dom/plugins/ipc/BrowserStreamParent.cpp
new file mode 100644
index 000000000..26dd3c943
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamParent.cpp
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "BrowserStreamParent.h"
+#include "PluginAsyncSurrogate.h"
+#include "PluginInstanceParent.h"
+#include "nsNPAPIPlugin.h"
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+// How much data are we willing to send across the wire
+// in one chunk?
+static const int32_t kSendDataChunk = 0xffff;
+
+namespace mozilla {
+namespace plugins {
+
+BrowserStreamParent::BrowserStreamParent(PluginInstanceParent* npp,
+ NPStream* stream)
+ : mNPP(npp)
+ , mStream(stream)
+ , mDeferredDestroyReason(NPRES_DONE)
+ , mState(INITIALIZING)
+{
+ mStream->pdata = static_cast<AStream*>(this);
+ nsNPAPIStreamWrapper* wrapper =
+ reinterpret_cast<nsNPAPIStreamWrapper*>(mStream->ndata);
+ if (wrapper) {
+ mStreamListener = wrapper->GetStreamListener();
+ }
+}
+
+BrowserStreamParent::~BrowserStreamParent()
+{
+ mStream->pdata = nullptr;
+}
+
+void
+BrowserStreamParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Implement me! Bug 1005159
+}
+
+bool
+BrowserStreamParent::RecvAsyncNPP_NewStreamResult(const NPError& rv,
+ const uint16_t& stype)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ PluginAsyncSurrogate* surrogate = mNPP->GetAsyncSurrogate();
+ MOZ_ASSERT(surrogate);
+ surrogate->AsyncCallArriving();
+ if (mState == DEFERRING_DESTROY) {
+ // We've been asked to destroy ourselves before init was complete.
+ mState = DYING;
+ Unused << SendNPP_DestroyStream(mDeferredDestroyReason);
+ return true;
+ }
+
+ NPError error = rv;
+ if (error == NPERR_NO_ERROR) {
+ if (!mStreamListener) {
+ return false;
+ }
+ if (mStreamListener->SetStreamType(stype)) {
+ mState = ALIVE;
+ } else {
+ error = NPERR_GENERIC_ERROR;
+ }
+ }
+
+ if (error != NPERR_NO_ERROR) {
+ surrogate->DestroyAsyncStream(mStream);
+ Unused << PBrowserStreamParent::Send__delete__(this);
+ }
+
+ return true;
+}
+
+bool
+BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges,
+ NPError* result)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ switch (mState) {
+ case INITIALIZING:
+ NS_ERROR("Requesting a read before initialization has completed");
+ *result = NPERR_GENERIC_ERROR;
+ return false;
+
+ case ALIVE:
+ break;
+
+ case DYING:
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+
+ default:
+ NS_ERROR("Unexpected state");
+ return false;
+ }
+
+ if (!mStream)
+ return false;
+
+ if (ranges.Length() > INT32_MAX)
+ return false;
+
+ UniquePtr<NPByteRange[]> rp(new NPByteRange[ranges.Length()]);
+ for (uint32_t i = 0; i < ranges.Length(); ++i) {
+ rp[i].offset = ranges[i].offset;
+ rp[i].length = ranges[i].length;
+ rp[i].next = &rp[i + 1];
+ }
+ rp[ranges.Length() - 1].next = nullptr;
+
+ *result = mNPP->mNPNIface->requestread(mStream, rp.get());
+ return true;
+}
+
+bool
+BrowserStreamParent::RecvNPN_DestroyStream(const NPReason& reason)
+{
+ switch (mState) {
+ case ALIVE:
+ break;
+
+ case DYING:
+ return true;
+
+ default:
+ NS_ERROR("Unexpected state");
+ return false;
+ }
+
+ mNPP->mNPNIface->destroystream(mNPP->mNPP, mStream, reason);
+ return true;
+}
+
+void
+BrowserStreamParent::NPP_DestroyStream(NPReason reason)
+{
+ NS_ASSERTION(ALIVE == mState || INITIALIZING == mState,
+ "NPP_DestroyStream called twice?");
+ bool stillInitializing = INITIALIZING == mState;
+ if (stillInitializing) {
+ mState = DEFERRING_DESTROY;
+ mDeferredDestroyReason = reason;
+ } else {
+ mState = DYING;
+ Unused << SendNPP_DestroyStream(reason);
+ }
+}
+
+bool
+BrowserStreamParent::RecvStreamDestroyed()
+{
+ if (DYING != mState) {
+ NS_ERROR("Unexpected state");
+ return false;
+ }
+
+ mStreamPeer = nullptr;
+
+ mState = DELETING;
+ return Send__delete__(this);
+}
+
+int32_t
+BrowserStreamParent::WriteReady()
+{
+ if (mState == INITIALIZING) {
+ return 0;
+ }
+ return kSendDataChunk;
+}
+
+int32_t
+BrowserStreamParent::Write(int32_t offset,
+ int32_t len,
+ void* buffer)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ NS_ASSERTION(ALIVE == mState, "Sending data after NPP_DestroyStream?");
+ NS_ASSERTION(len > 0, "Non-positive length to NPP_Write");
+
+ if (len > kSendDataChunk)
+ len = kSendDataChunk;
+
+ return SendWrite(offset,
+ mStream->end,
+ nsCString(static_cast<char*>(buffer), len)) ?
+ len : -1;
+}
+
+void
+BrowserStreamParent::StreamAsFile(const char* fname)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ NS_ASSERTION(ALIVE == mState,
+ "Calling streamasfile after NPP_DestroyStream?");
+
+ // Make sure our stream survives until the plugin process tells us we've
+ // been destroyed (until RecvStreamDestroyed() is called). Since we retain
+ // mStreamPeer at most once, we won't get in trouble if StreamAsFile() is
+ // called more than once.
+ if (!mStreamPeer) {
+ nsNPAPIPlugin::RetainStream(mStream, getter_AddRefs(mStreamPeer));
+ }
+
+ Unused << SendNPP_StreamAsFile(nsCString(fname));
+ return;
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/BrowserStreamParent.h b/dom/plugins/ipc/BrowserStreamParent.h
new file mode 100644
index 000000000..410f2313f
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamParent.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 mozilla_plugins_BrowserStreamParent_h
+#define mozilla_plugins_BrowserStreamParent_h
+
+#include "mozilla/plugins/PBrowserStreamParent.h"
+#include "mozilla/plugins/AStream.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsPluginStreamListenerPeer.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceParent;
+
+class BrowserStreamParent : public PBrowserStreamParent, public AStream
+{
+ friend class PluginModuleParent;
+ friend class PluginInstanceParent;
+
+public:
+ BrowserStreamParent(PluginInstanceParent* npp,
+ NPStream* stream);
+ virtual ~BrowserStreamParent();
+
+ virtual bool IsBrowserStream() override { return true; }
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool RecvAsyncNPP_NewStreamResult(
+ const NPError& rv,
+ const uint16_t& stype) override;
+
+ virtual bool AnswerNPN_RequestRead(const IPCByteRanges& ranges,
+ NPError* result) override;
+
+ virtual bool RecvNPN_DestroyStream(const NPReason& reason) override;
+
+ virtual bool RecvStreamDestroyed() override;
+
+ int32_t WriteReady();
+ int32_t Write(int32_t offset, int32_t len, void* buffer);
+ void StreamAsFile(const char* fname);
+
+ void NPP_DestroyStream(NPReason reason);
+
+ void SetAlive()
+ {
+ if (mState == INITIALIZING) {
+ mState = ALIVE;
+ }
+ }
+
+private:
+ using PBrowserStreamParent::SendNPP_DestroyStream;
+
+ PluginInstanceParent* mNPP;
+ NPStream* mStream;
+ nsCOMPtr<nsISupports> mStreamPeer;
+ RefPtr<nsNPAPIPluginStreamListener> mStreamListener;
+ NPReason mDeferredDestroyReason;
+
+ enum {
+ INITIALIZING,
+ DEFERRING_DESTROY,
+ ALIVE,
+ DYING,
+ DELETING
+ } mState;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
diff --git a/dom/plugins/ipc/ChildAsyncCall.cpp b/dom/plugins/ipc/ChildAsyncCall.cpp
new file mode 100644
index 000000000..6adc05d55
--- /dev/null
+++ b/dom/plugins/ipc/ChildAsyncCall.cpp
@@ -0,0 +1,52 @@
+/* -*- 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 "ChildAsyncCall.h"
+#include "PluginInstanceChild.h"
+
+namespace mozilla {
+namespace plugins {
+
+ChildAsyncCall::ChildAsyncCall(PluginInstanceChild* instance,
+ PluginThreadCallback aFunc, void* aUserData)
+ : mInstance(instance)
+ , mFunc(aFunc)
+ , mData(aUserData)
+{
+}
+
+nsresult
+ChildAsyncCall::Cancel()
+{
+ mInstance = nullptr;
+ mFunc = nullptr;
+ mData = nullptr;
+ return NS_OK;
+}
+
+void
+ChildAsyncCall::RemoveFromAsyncList()
+{
+ if (mInstance) {
+ MutexAutoLock lock(mInstance->mAsyncCallMutex);
+ mInstance->mPendingAsyncCalls.RemoveElement(this);
+ }
+}
+
+NS_IMETHODIMP
+ChildAsyncCall::Run()
+{
+ RemoveFromAsyncList();
+
+ if (mFunc)
+ mFunc(mData);
+
+ return NS_OK;
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/ChildAsyncCall.h b/dom/plugins/ipc/ChildAsyncCall.h
new file mode 100644
index 000000000..ae5c45a93
--- /dev/null
+++ b/dom/plugins/ipc/ChildAsyncCall.h
@@ -0,0 +1,41 @@
+/* -*- 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/. */
+
+#ifndef mozilla_plugins_ChildAsyncCall_h
+#define mozilla_plugins_ChildAsyncCall_h
+
+#include "PluginMessageUtils.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace plugins {
+
+typedef void (*PluginThreadCallback)(void*);
+
+class PluginInstanceChild;
+
+class ChildAsyncCall : public CancelableRunnable
+{
+public:
+ ChildAsyncCall(PluginInstanceChild* instance,
+ PluginThreadCallback aFunc, void* aUserData);
+
+ NS_IMETHOD Run() override;
+ nsresult Cancel() override;
+
+protected:
+ PluginInstanceChild* mInstance;
+ PluginThreadCallback mFunc;
+ void* mData;
+
+ void RemoveFromAsyncList();
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_ChildAsyncCall_h
diff --git a/dom/plugins/ipc/ChildTimer.cpp b/dom/plugins/ipc/ChildTimer.cpp
new file mode 100644
index 000000000..5bcf5498b
--- /dev/null
+++ b/dom/plugins/ipc/ChildTimer.cpp
@@ -0,0 +1,40 @@
+/* -*- 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 "ChildTimer.h"
+#include "PluginInstanceChild.h"
+#include "nsComponentManagerUtils.h"
+
+namespace mozilla {
+namespace plugins {
+
+ChildTimer::ChildTimer(PluginInstanceChild* instance,
+ uint32_t interval,
+ bool repeat,
+ TimerFunc func)
+ : mInstance(instance)
+ , mFunc(func)
+ , mRepeating(repeat)
+ , mID(gNextTimerID++)
+{
+ mTimer.Start(base::TimeDelta::FromMilliseconds(interval),
+ this, &ChildTimer::Run);
+}
+
+uint32_t
+ChildTimer::gNextTimerID = 1;
+
+void
+ChildTimer::Run()
+{
+ if (!mRepeating)
+ mTimer.Stop();
+ mFunc(mInstance->GetNPP(), mID);
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/ChildTimer.h b/dom/plugins/ipc/ChildTimer.h
new file mode 100644
index 000000000..194fa77ab
--- /dev/null
+++ b/dom/plugins/ipc/ChildTimer.h
@@ -0,0 +1,58 @@
+/* -*- 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/. */
+
+#ifndef mozilla_plugins_ChildTimer_h
+#define mozilla_plugins_ChildTimer_h
+
+#include "PluginMessageUtils.h"
+#include "npapi.h"
+#include "base/timer.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceChild;
+typedef void (*TimerFunc)(NPP npp, uint32_t timerID);
+
+class ChildTimer
+{
+public:
+ /**
+ * If initialization failed, ID() will return 0.
+ */
+ ChildTimer(PluginInstanceChild* instance,
+ uint32_t interval,
+ bool repeat,
+ TimerFunc func);
+ ~ChildTimer() { }
+
+ uint32_t ID() const { return mID; }
+
+ class IDComparator
+ {
+ public:
+ bool Equals(ChildTimer* t, uint32_t id) const {
+ return t->ID() == id;
+ }
+ };
+
+private:
+ PluginInstanceChild* mInstance;
+ TimerFunc mFunc;
+ bool mRepeating;
+ uint32_t mID;
+ base::RepeatingTimer<ChildTimer> mTimer;
+
+ void Run();
+
+ static uint32_t gNextTimerID;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_ChildTimer_h
diff --git a/dom/plugins/ipc/D3D11SurfaceHolder.cpp b/dom/plugins/ipc/D3D11SurfaceHolder.cpp
new file mode 100644
index 000000000..f02146c20
--- /dev/null
+++ b/dom/plugins/ipc/D3D11SurfaceHolder.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "nsDebug.h"
+#include "D3D11SurfaceHolder.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include <d3d11.h>
+
+namespace mozilla {
+namespace plugins {
+
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+D3D11SurfaceHolder::D3D11SurfaceHolder(ID3D11Texture2D* back,
+ SurfaceFormat format,
+ const IntSize& size)
+ : mDevice11(DeviceManagerDx::Get()->GetContentDevice()),
+ mBack(back),
+ mFormat(format),
+ mSize(size)
+{
+}
+
+D3D11SurfaceHolder::~D3D11SurfaceHolder()
+{
+}
+
+bool
+D3D11SurfaceHolder::IsValid()
+{
+ // If a TDR occurred, platform devices will be recreated.
+ if (DeviceManagerDx::Get()->GetContentDevice() != mDevice11) {
+ return false;
+ }
+ return true;
+}
+
+bool
+D3D11SurfaceHolder::CopyToTextureClient(TextureClient* aClient)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ D3D11TextureData* data = aClient->GetInternalData()->AsD3D11TextureData();
+ if (!data) {
+ // We don't support this yet. We expect to have a D3D11 compositor, and
+ // therefore D3D11 surfaces.
+ NS_WARNING("Plugin DXGI surface has unsupported TextureClient");
+ return false;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ mDevice11->GetImmediateContext(getter_AddRefs(context));
+ if (!context) {
+ NS_WARNING("Could not get an immediate D3D11 context");
+ return false;
+ }
+
+ TextureClientAutoLock autoLock(aClient, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return false;
+ }
+
+ RefPtr<IDXGIKeyedMutex> mutex;
+ HRESULT hr = mBack->QueryInterface(__uuidof(IDXGIKeyedMutex), (void **)getter_AddRefs(mutex));
+ if (FAILED(hr) || !mutex) {
+ NS_WARNING("Could not acquire an IDXGIKeyedMutex");
+ return false;
+ }
+
+ {
+ AutoTextureLock lock(mutex, hr);
+ if (hr == WAIT_ABANDONED || hr == WAIT_TIMEOUT || FAILED(hr)) {
+ NS_WARNING("Could not acquire DXGI surface lock - plugin forgot to release?");
+ return false;
+ }
+
+ context->CopyResource(data->GetD3D11Texture(), mBack);
+ }
+ return true;
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/D3D11SurfaceHolder.h b/dom/plugins/ipc/D3D11SurfaceHolder.h
new file mode 100644
index 000000000..16cd2d182
--- /dev/null
+++ b/dom/plugins/ipc/D3D11SurfaceHolder.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 _include_dom_plugins_ipc_D3D11SurfaceHolder_h__
+#define _include_dom_plugins_ipc_D3D11SurfaceHolder_h__
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace layers {
+class D3D11ShareHandleImage;
+class TextureClient;
+} // namespace layers
+
+namespace plugins {
+
+class D3D11SurfaceHolder
+{
+public:
+ D3D11SurfaceHolder(ID3D11Texture2D* back, gfx::SurfaceFormat format, const gfx::IntSize& size);
+
+ NS_INLINE_DECL_REFCOUNTING(D3D11SurfaceHolder);
+
+ bool IsValid();
+ bool CopyToTextureClient(layers::TextureClient* aClient);
+
+ gfx::SurfaceFormat GetFormat() const {
+ return mFormat;
+ }
+ const gfx::IntSize& GetSize() const {
+ return mSize;
+ }
+
+private:
+ ~D3D11SurfaceHolder();
+
+private:
+ RefPtr<ID3D11Device> mDevice11;
+ RefPtr<ID3D11Texture2D> mBack;
+ gfx::SurfaceFormat mFormat;
+ gfx::IntSize mSize;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // _include_dom_plugins_ipc_D3D11nSurfaceHolder_h__
diff --git a/dom/plugins/ipc/MiniShmParent.cpp b/dom/plugins/ipc/MiniShmParent.cpp
new file mode 100644
index 000000000..eb0b16c8a
--- /dev/null
+++ b/dom/plugins/ipc/MiniShmParent.cpp
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "MiniShmParent.h"
+
+#include "base/scoped_handle.h"
+
+#include <sstream>
+
+namespace mozilla {
+namespace plugins {
+
+// static
+const unsigned int MiniShmParent::kDefaultMiniShmSectionSize = 0x1000;
+
+MiniShmParent::MiniShmParent()
+ : mSectionSize(0),
+ mParentEvent(nullptr),
+ mParentGuard(nullptr),
+ mChildEvent(nullptr),
+ mChildGuard(nullptr),
+ mRegWait(nullptr),
+ mFileMapping(nullptr),
+ mView(nullptr),
+ mIsConnected(false),
+ mTimeout(INFINITE)
+{
+}
+
+MiniShmParent::~MiniShmParent()
+{
+ CleanUp();
+}
+
+void
+MiniShmParent::CleanUp()
+{
+ if (mRegWait) {
+ ::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
+ mRegWait = nullptr;
+ }
+ if (mParentEvent) {
+ ::CloseHandle(mParentEvent);
+ mParentEvent = nullptr;
+ }
+ if (mParentGuard) {
+ ::CloseHandle(mParentGuard);
+ mParentGuard = nullptr;
+ }
+ if (mChildEvent) {
+ ::CloseHandle(mChildEvent);
+ mChildEvent = nullptr;
+ }
+ if (mChildGuard) {
+ ::CloseHandle(mChildGuard);
+ mChildGuard = nullptr;
+ }
+ if (mView) {
+ ::UnmapViewOfFile(mView);
+ mView = nullptr;
+ }
+ if (mFileMapping) {
+ ::CloseHandle(mFileMapping);
+ mFileMapping = nullptr;
+ }
+}
+
+nsresult
+MiniShmParent::Init(MiniShmObserver* aObserver, const DWORD aTimeout,
+ const unsigned int aSectionSize)
+{
+ if (!aObserver || !aSectionSize || (aSectionSize % 0x1000) || !aTimeout) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (mFileMapping) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+ SECURITY_ATTRIBUTES securityAttributes = {sizeof(securityAttributes),
+ nullptr,
+ TRUE};
+ ScopedHandle parentEvent(::CreateEvent(&securityAttributes,
+ FALSE,
+ FALSE,
+ nullptr));
+ if (!parentEvent.IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+ ScopedHandle parentGuard(::CreateEvent(&securityAttributes,
+ FALSE,
+ TRUE,
+ nullptr));
+ if (!parentGuard.IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+ ScopedHandle childEvent(::CreateEvent(&securityAttributes,
+ FALSE,
+ FALSE,
+ nullptr));
+ if (!childEvent.IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+ ScopedHandle childGuard(::CreateEvent(&securityAttributes,
+ FALSE,
+ TRUE,
+ nullptr));
+ if (!childGuard.IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+ ScopedHandle mapping(::CreateFileMapping(INVALID_HANDLE_VALUE,
+ &securityAttributes,
+ PAGE_READWRITE,
+ 0,
+ aSectionSize,
+ nullptr));
+ if (!mapping.IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+ ScopedMappedFileView view(::MapViewOfFile(mapping,
+ FILE_MAP_WRITE,
+ 0, 0, 0));
+ if (!view.IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv = SetView(view, aSectionSize, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = SetGuard(childGuard, aTimeout);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MiniShmInit* initStruct = nullptr;
+ rv = GetWritePtrInternal(initStruct);
+ NS_ENSURE_SUCCESS(rv, rv);
+ initStruct->mParentEvent = parentEvent;
+ initStruct->mParentGuard = parentGuard;
+ initStruct->mChildEvent = childEvent;
+ initStruct->mChildGuard = childGuard;
+
+ if (!::RegisterWaitForSingleObject(&mRegWait,
+ parentEvent,
+ &SOnEvent,
+ this,
+ INFINITE,
+ WT_EXECUTEDEFAULT)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mParentEvent = parentEvent.Take();
+ mParentGuard = parentGuard.Take();
+ mChildEvent = childEvent.Take();
+ mChildGuard = childGuard.Take();
+ mFileMapping = mapping.Take();
+ mView = view.Take();
+ mSectionSize = aSectionSize;
+ SetObserver(aObserver);
+ mTimeout = aTimeout;
+ return NS_OK;
+}
+
+nsresult
+MiniShmParent::GetCookie(std::wstring& cookie)
+{
+ if (!mFileMapping) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ std::wostringstream oss;
+ oss << mFileMapping;
+ if (!oss) {
+ return NS_ERROR_FAILURE;
+ }
+ cookie = oss.str();
+ return NS_OK;
+}
+
+nsresult
+MiniShmParent::Send()
+{
+ if (!mChildEvent) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (!::SetEvent(mChildEvent)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+bool
+MiniShmParent::IsConnected() const
+{
+ return mIsConnected;
+}
+
+void
+MiniShmParent::OnEvent()
+{
+ if (mIsConnected) {
+ MiniShmBase::OnEvent();
+ } else {
+ FinalizeConnection();
+ }
+ ::SetEvent(mParentGuard);
+}
+
+void
+MiniShmParent::FinalizeConnection()
+{
+ const MiniShmInitComplete* initCompleteStruct = nullptr;
+ nsresult rv = GetReadPtr(initCompleteStruct);
+ mIsConnected = NS_SUCCEEDED(rv) && initCompleteStruct->mSucceeded;
+ if (mIsConnected) {
+ OnConnect();
+ }
+}
+
+} // namespace plugins
+} // namespace mozilla
+
diff --git a/dom/plugins/ipc/MiniShmParent.h b/dom/plugins/ipc/MiniShmParent.h
new file mode 100644
index 000000000..dc6cd8b18
--- /dev/null
+++ b/dom/plugins/ipc/MiniShmParent.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 mozilla_plugins_MiniShmParent_h
+#define mozilla_plugins_MiniShmParent_h
+
+#include "MiniShmBase.h"
+
+#include <string>
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This class provides a lightweight shared memory interface for a parent
+ * process in Win32.
+ * This code assumes that there is a parent-child relationship between
+ * processes, as it creates inheritable handles.
+ * Note that this class is *not* an IPDL actor.
+ *
+ * @see MiniShmChild
+ */
+class MiniShmParent : public MiniShmBase
+{
+public:
+ MiniShmParent();
+ virtual ~MiniShmParent();
+
+ static const unsigned int kDefaultMiniShmSectionSize;
+
+ /**
+ * Initialize shared memory on the parent side.
+ *
+ * @param aObserver A MiniShmObserver object to receive event notifications.
+ * @param aTimeout Timeout in milliseconds.
+ * @param aSectionSize Desired size of the shared memory section. This is
+ * expected to be a multiple of 0x1000 (4KiB).
+ * @return nsresult error code
+ */
+ nsresult
+ Init(MiniShmObserver* aObserver, const DWORD aTimeout,
+ const unsigned int aSectionSize = kDefaultMiniShmSectionSize);
+
+ /**
+ * Destroys the shared memory section. Useful to explicitly release
+ * resources if it is known that they won't be needed again.
+ */
+ void
+ CleanUp();
+
+ /**
+ * Provides a cookie string that should be passed to MiniShmChild
+ * during its initialization.
+ *
+ * @param aCookie A std::wstring variable to receive the cookie.
+ * @return nsresult error code
+ */
+ nsresult
+ GetCookie(std::wstring& aCookie);
+
+ virtual nsresult
+ Send() override;
+
+ bool
+ IsConnected() const;
+
+protected:
+ void
+ OnEvent() override;
+
+private:
+ void
+ FinalizeConnection();
+
+ unsigned int mSectionSize;
+ HANDLE mParentEvent;
+ HANDLE mParentGuard;
+ HANDLE mChildEvent;
+ HANDLE mChildGuard;
+ HANDLE mRegWait;
+ HANDLE mFileMapping;
+ LPVOID mView;
+ bool mIsConnected;
+ DWORD mTimeout;
+
+ DISALLOW_COPY_AND_ASSIGN(MiniShmParent);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_MiniShmParent_h
+
diff --git a/dom/plugins/ipc/NPEventAndroid.h b/dom/plugins/ipc/NPEventAndroid.h
new file mode 100644
index 000000000..f664af857
--- /dev/null
+++ b/dom/plugins/ipc/NPEventAndroid.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* 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 is a NPEventX11.h derived stub for Android
+// Plugins aren't actually supported yet
+
+#ifndef mozilla_dom_plugins_NPEventAndroid_h
+#define mozilla_dom_plugins_NPEventAndroid_h
+
+#include "npapi.h"
+
+namespace mozilla {
+
+namespace plugins {
+
+struct NPRemoteEvent {
+ NPEvent event;
+};
+
+}
+
+}
+
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteEvent>
+{
+ typedef mozilla::plugins::NPRemoteEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteBytes(&aParam, sizeof(paramType));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType));
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ // TODO
+ aLog->append(L"(AndroidEvent)");
+ }
+};
+
+} // namespace IPC
+
+
+#endif // mozilla_dom_plugins_NPEventAndroid_h
diff --git a/dom/plugins/ipc/NPEventOSX.h b/dom/plugins/ipc/NPEventOSX.h
new file mode 100644
index 000000000..ca1736a8a
--- /dev/null
+++ b/dom/plugins/ipc/NPEventOSX.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* 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 mozilla_dom_plugins_NPEventOSX_h
+#define mozilla_dom_plugins_NPEventOSX_h 1
+
+
+#include "npapi.h"
+
+namespace mozilla {
+
+namespace plugins {
+
+struct NPRemoteEvent {
+ NPCocoaEvent event;
+ double contentsScaleFactor;
+};
+
+} // namespace plugins
+
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteEvent>
+{
+ typedef mozilla::plugins::NPRemoteEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteInt(aParam.event.type);
+ aMsg->WriteUInt32(aParam.event.version);
+ switch (aParam.event.type) {
+ case NPCocoaEventMouseDown:
+ case NPCocoaEventMouseUp:
+ case NPCocoaEventMouseMoved:
+ case NPCocoaEventMouseEntered:
+ case NPCocoaEventMouseExited:
+ case NPCocoaEventMouseDragged:
+ case NPCocoaEventScrollWheel:
+ aMsg->WriteUInt32(aParam.event.data.mouse.modifierFlags);
+ aMsg->WriteDouble(aParam.event.data.mouse.pluginX);
+ aMsg->WriteDouble(aParam.event.data.mouse.pluginY);
+ aMsg->WriteInt32(aParam.event.data.mouse.buttonNumber);
+ aMsg->WriteInt32(aParam.event.data.mouse.clickCount);
+ aMsg->WriteDouble(aParam.event.data.mouse.deltaX);
+ aMsg->WriteDouble(aParam.event.data.mouse.deltaY);
+ aMsg->WriteDouble(aParam.event.data.mouse.deltaZ);
+ break;
+ case NPCocoaEventKeyDown:
+ case NPCocoaEventKeyUp:
+ case NPCocoaEventFlagsChanged:
+ aMsg->WriteUInt32(aParam.event.data.key.modifierFlags);
+ WriteParam(aMsg, aParam.event.data.key.characters);
+ WriteParam(aMsg, aParam.event.data.key.charactersIgnoringModifiers);
+ aMsg->WriteUnsignedChar(aParam.event.data.key.isARepeat);
+ aMsg->WriteUInt16(aParam.event.data.key.keyCode);
+ break;
+ case NPCocoaEventFocusChanged:
+ case NPCocoaEventWindowFocusChanged:
+ aMsg->WriteUnsignedChar(aParam.event.data.focus.hasFocus);
+ break;
+ case NPCocoaEventDrawRect:
+ // We don't write out the context pointer, it would always be
+ // nullptr and is just filled in as such on the read.
+ aMsg->WriteDouble(aParam.event.data.draw.x);
+ aMsg->WriteDouble(aParam.event.data.draw.y);
+ aMsg->WriteDouble(aParam.event.data.draw.width);
+ aMsg->WriteDouble(aParam.event.data.draw.height);
+ break;
+ case NPCocoaEventTextInput:
+ WriteParam(aMsg, aParam.event.data.text.text);
+ break;
+ default:
+ NS_NOTREACHED("Attempted to serialize unknown event type.");
+ return;
+ }
+ aMsg->WriteDouble(aParam.contentsScaleFactor);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ int type = 0;
+ if (!aMsg->ReadInt(aIter, &type)) {
+ return false;
+ }
+ aResult->event.type = static_cast<NPCocoaEventType>(type);
+
+ if (!aMsg->ReadUInt32(aIter, &aResult->event.version)) {
+ return false;
+ }
+
+ switch (aResult->event.type) {
+ case NPCocoaEventMouseDown:
+ case NPCocoaEventMouseUp:
+ case NPCocoaEventMouseMoved:
+ case NPCocoaEventMouseEntered:
+ case NPCocoaEventMouseExited:
+ case NPCocoaEventMouseDragged:
+ case NPCocoaEventScrollWheel:
+ if (!aMsg->ReadUInt32(aIter, &aResult->event.data.mouse.modifierFlags)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.pluginX)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.pluginY)) {
+ return false;
+ }
+ if (!aMsg->ReadInt32(aIter, &aResult->event.data.mouse.buttonNumber)) {
+ return false;
+ }
+ if (!aMsg->ReadInt32(aIter, &aResult->event.data.mouse.clickCount)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.deltaX)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.deltaY)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.deltaZ)) {
+ return false;
+ }
+ break;
+ case NPCocoaEventKeyDown:
+ case NPCocoaEventKeyUp:
+ case NPCocoaEventFlagsChanged:
+ if (!aMsg->ReadUInt32(aIter, &aResult->event.data.key.modifierFlags)) {
+ return false;
+ }
+ if (!ReadParam(aMsg, aIter, &aResult->event.data.key.characters)) {
+ return false;
+ }
+ if (!ReadParam(aMsg, aIter, &aResult->event.data.key.charactersIgnoringModifiers)) {
+ return false;
+ }
+ if (!aMsg->ReadUnsignedChar(aIter, &aResult->event.data.key.isARepeat)) {
+ return false;
+ }
+ if (!aMsg->ReadUInt16(aIter, &aResult->event.data.key.keyCode)) {
+ return false;
+ }
+ break;
+ case NPCocoaEventFocusChanged:
+ case NPCocoaEventWindowFocusChanged:
+ if (!aMsg->ReadUnsignedChar(aIter, &aResult->event.data.focus.hasFocus)) {
+ return false;
+ }
+ break;
+ case NPCocoaEventDrawRect:
+ aResult->event.data.draw.context = nullptr;
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.draw.x)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.draw.y)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.draw.width)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.draw.height)) {
+ return false;
+ }
+ break;
+ case NPCocoaEventTextInput:
+ if (!ReadParam(aMsg, aIter, &aResult->event.data.text.text)) {
+ return false;
+ }
+ break;
+ default:
+ NS_NOTREACHED("Attempted to de-serialize unknown event type.");
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->contentsScaleFactor)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(L"(NPCocoaEvent)");
+ }
+};
+
+} // namespace IPC
+
+#endif // ifndef mozilla_dom_plugins_NPEventOSX_h
diff --git a/dom/plugins/ipc/NPEventUnix.h b/dom/plugins/ipc/NPEventUnix.h
new file mode 100644
index 000000000..4cc9a5456
--- /dev/null
+++ b/dom/plugins/ipc/NPEventUnix.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* 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 mozilla_dom_plugins_NPEventUnix_h
+#define mozilla_dom_plugins_NPEventUnix_h 1
+
+#include "npapi.h"
+
+#ifdef MOZ_X11
+#include "mozilla/X11Util.h"
+#endif
+
+namespace mozilla {
+
+namespace plugins {
+
+struct NPRemoteEvent {
+ NPEvent event;
+};
+
+}
+
+}
+
+
+//
+// XEvent is defined as a union of all more specific X*Events.
+// Luckily, as of xorg 1.6.0 / X protocol 11 rev 0, the only pointer
+// field contained in any of these specific X*Event structs is a
+// |Display*|. So to simplify serializing these XEvents, we make the
+//
+// ********** XXX ASSUMPTION XXX **********
+//
+// that the process to which the event is forwarded shares the same
+// display as the process on which the event originated.
+//
+// With this simplification, serialization becomes a simple memcpy to
+// the output stream. Deserialization starts as just a memcpy from
+// the input stream, BUT we then have to write the correct |Display*|
+// into the right field of each X*Event that contains one.
+//
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteEvent> // synonym for XEvent
+{
+ typedef mozilla::plugins::NPRemoteEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteBytes(&aParam, sizeof(paramType));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ if (!aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType))) {
+ return false;
+ }
+
+#ifdef MOZ_X11
+ SetXDisplay(aResult->event);
+#endif
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ // TODO
+ aLog->append(L"(XEvent)");
+ }
+
+#ifdef MOZ_X11
+private:
+ static void SetXDisplay(XEvent& ev)
+ {
+ Display* display = mozilla::DefaultXDisplay();
+ if (ev.type >= KeyPress) {
+ ev.xany.display = display;
+ }
+ else {
+ // XXX assuming that this is an error event
+ // (type == 0? not clear from Xlib.h)
+ ev.xerror.display = display;
+ }
+ }
+#endif
+};
+
+} // namespace IPC
+
+
+#endif // ifndef mozilla_dom_plugins_NPEventX11_h
diff --git a/dom/plugins/ipc/NPEventWindows.h b/dom/plugins/ipc/NPEventWindows.h
new file mode 100644
index 000000000..faf93a601
--- /dev/null
+++ b/dom/plugins/ipc/NPEventWindows.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* 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 mozilla_dom_plugins_NPEventWindows_h
+#define mozilla_dom_plugins_NPEventWindows_h 1
+
+#ifndef WM_MOUSEHWHEEL
+#define WM_MOUSEHWHEEL (0x020E)
+#endif
+
+#include "npapi.h"
+namespace mozilla {
+
+namespace plugins {
+
+// We use an NPRemoteEvent struct so that we can store the extra data on
+// the stack so that we don't need to worry about managing the memory.
+struct NPRemoteEvent
+{
+ NPEvent event;
+ union {
+ RECT rect;
+ WINDOWPOS windowpos;
+ } lParamData;
+ double contentsScaleFactor;
+};
+
+}
+
+}
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteEvent>
+{
+ typedef mozilla::plugins::NPRemoteEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ // Make a non-const copy of aParam so that we can muck with
+ // its insides for tranport
+ paramType paramCopy;
+
+ paramCopy.event = aParam.event;
+
+ // We can't blindly ipc events because they may sometimes contain
+ // pointers to memory in the sending process. For example, the
+ // WM_IME_CONTROL with the IMC_GETCOMPOSITIONFONT message has lParam
+ // set to a pointer to a LOGFONT structure.
+ switch (paramCopy.event.event) {
+ case WM_WINDOWPOSCHANGED:
+ // The lParam parameter of WM_WINDOWPOSCHANGED holds a pointer to
+ // a WINDOWPOS structure that contains information about the
+ // window's new size and position
+ paramCopy.lParamData.windowpos = *(reinterpret_cast<WINDOWPOS*>(paramCopy.event.lParam));
+ break;
+ case WM_PAINT:
+ // The lParam parameter of WM_PAINT holds a pointer to an RECT
+ // structure specifying the bounding box of the update area.
+ paramCopy.lParamData.rect = *(reinterpret_cast<RECT*>(paramCopy.event.lParam));
+ break;
+
+ // the white list of events that we will ipc to the client
+ case WM_CHAR:
+ case WM_SYSCHAR:
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ case WM_CONTEXTMENU:
+
+ case WM_CUT:
+ case WM_COPY:
+ case WM_PASTE:
+ case WM_CLEAR:
+ case WM_UNDO:
+
+ case WM_MOUSELEAVE:
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+
+ case WM_MOUSEWHEEL:
+ case WM_MOUSEHWHEEL:
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+
+ case WM_IME_STARTCOMPOSITION:
+ case WM_IME_COMPOSITION:
+ case WM_IME_ENDCOMPOSITION:
+ case WM_IME_CHAR:
+ case WM_IME_SETCONTEXT:
+ case WM_IME_COMPOSITIONFULL:
+ case WM_IME_KEYDOWN:
+ case WM_IME_KEYUP:
+ case WM_IME_SELECT:
+ case WM_INPUTLANGCHANGEREQUEST:
+ case WM_INPUTLANGCHANGE:
+ break;
+
+ default:
+ // RegisterWindowMessage events should be passed.
+ if (paramCopy.event.event >= 0xC000)
+ break;
+
+ // FIXME/bug 567465: temporarily work around unhandled
+ // events by forwarding a "dummy event". The eventual
+ // fix will be to stop trying to send these events
+ // entirely.
+ paramCopy.event.event = WM_NULL;
+ break;
+ }
+
+ aMsg->WriteBytes(&paramCopy, sizeof(paramType));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ if (!aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType))) {
+ return false;
+ }
+
+ if (aResult->event.event == WM_PAINT) {
+ // restore the lParam to point at the RECT
+ aResult->event.lParam = reinterpret_cast<LPARAM>(&aResult->lParamData.rect);
+ } else if (aResult->event.event == WM_WINDOWPOSCHANGED) {
+ // restore the lParam to point at the WINDOWPOS
+ aResult->event.lParam = reinterpret_cast<LPARAM>(&aResult->lParamData.windowpos);
+ }
+
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(L"(WINEvent)");
+ }
+
+};
+
+} // namespace IPC
+
+#endif // ifndef mozilla_dom_plugins_NPEventWindows_h
diff --git a/dom/plugins/ipc/PBrowserStream.ipdl b/dom/plugins/ipc/PBrowserStream.ipdl
new file mode 100644
index 000000000..dbd238750
--- /dev/null
+++ b/dom/plugins/ipc/PBrowserStream.ipdl
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 protocol PPluginInstance;
+
+
+using mozilla::plugins::Buffer from "mozilla/plugins/PluginMessageUtils.h";
+using mozilla::plugins::IPCByteRanges from "mozilla/plugins/PluginMessageUtils.h";
+
+using NPError from "npapi.h";
+using NPReason from "npapi.h";
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * NPBrowserStream represents a NPStream sent from the browser to the plugin.
+ */
+
+intr protocol PBrowserStream
+{
+ manager PPluginInstance;
+
+child:
+ async Write(int32_t offset, uint32_t newlength,
+ Buffer data);
+ async NPP_StreamAsFile(nsCString fname);
+
+ /**
+ * NPP_DestroyStream may race with other messages: the child acknowledges
+ * the message with StreamDestroyed before this actor is deleted.
+ */
+ async NPP_DestroyStream(NPReason reason);
+ async __delete__();
+
+parent:
+ async AsyncNPP_NewStreamResult(NPError rv, uint16_t stype);
+ intr NPN_RequestRead(IPCByteRanges ranges)
+ returns (NPError result);
+ async NPN_DestroyStream(NPReason reason);
+ async StreamDestroyed();
+
+/*
+ TODO: turn on state machine.
+
+ // need configurable start state: if the constructor
+ // returns an error in result, start state should
+ // be DELETING.
+start state ALIVE:
+ send Write goto ALIVE;
+ call NPP_StreamAsFile goto ALIVE;
+ send NPP_DestroyStream goto ALIVE;
+ answer NPN_RequestRead goto ALIVE;
+ recv NPN_DestroyStream goto DYING;
+
+state DYING:
+ answer NPN_RequestRead goto DYING;
+ recv NPN_DestroyStream goto DYING;
+ recv StreamDestroyed goto DELETING;
+
+state DELETING:
+ send __delete__;
+*/
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl b/dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl
new file mode 100644
index 000000000..cc8ff558e
--- /dev/null
+++ b/dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 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 protocol PPluginInstance;
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This protocol exists to allow us to correctly destroy background
+ * surfaces. The browser owns the surfaces, but shares a "reference"
+ * with the plugin. The browser needs to notify the plugin when the
+ * background is going to be destroyed, but it can't rely on the
+ * plugin to destroy it because the plugin may crash at any time. So
+ * the plugin instance relinquishes destruction of the its old
+ * background to actors of this protocol, which can deal with crashy
+ * corner cases more easily than the instance.
+ */
+protocol PPluginBackgroundDestroyer {
+ manager PPluginInstance;
+
+ // The ctor message for this protocol serves double-duty as
+ // notification that that the background is stale.
+
+parent:
+ async __delete__();
+
+state DESTROYING:
+ recv __delete__;
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PPluginInstance.ipdl b/dom/plugins/ipc/PPluginInstance.ipdl
new file mode 100644
index 000000000..525e0a145
--- /dev/null
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -0,0 +1,329 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 protocol PPluginBackgroundDestroyer;
+include protocol PPluginModule;
+include protocol PPluginScriptableObject;
+include protocol PBrowserStream;
+include protocol PPluginStream;
+include protocol PStreamNotify;
+include protocol PPluginSurface;
+
+include "mozilla/GfxMessageUtils.h";
+
+using NPError from "npapi.h";
+using struct mozilla::plugins::NPRemoteWindow from "mozilla/plugins/PluginMessageUtils.h";
+using struct mozilla::plugins::NPRemoteEvent from "mozilla/plugins/PluginMessageUtils.h";
+using NPRect from "npapi.h";
+using NPNURLVariable from "npapi.h";
+using NPCoordinateSpace from "npapi.h";
+using NPNVariable from "npapi.h";
+using mozilla::plugins::NativeWindowHandle from "mozilla/plugins/PluginMessageUtils.h";
+using gfxSurfaceType from "gfxTypes.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/2D.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
+using mozilla::plugins::WindowsSharedMemoryHandle from "mozilla/plugins/PluginMessageUtils.h";
+using mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h";
+using nsIntRect from "nsRect.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.h";
+using struct mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
+using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
+
+namespace mozilla {
+namespace plugins {
+
+struct IOSurfaceDescriptor {
+ uint32_t surfaceId;
+ double contentsScaleFactor;
+};
+
+union SurfaceDescriptor {
+ Shmem;
+ SurfaceDescriptorX11;
+ PPluginSurface; // used on Windows
+ IOSurfaceDescriptor; // used on OSX 10.5+
+ // Descriptor can be null here in case
+ // 1) of first Show call (prevSurface is null)
+ // 2) when child is going to destroy
+ // and it just want to grab prevSurface
+ // back without giving new surface
+ null_t;
+};
+
+union OptionalShmem {
+ Shmem;
+ null_t;
+};
+
+intr protocol PPluginInstance
+{
+ manager PPluginModule;
+
+ manages PPluginBackgroundDestroyer;
+ manages PPluginScriptableObject;
+ manages PBrowserStream;
+ manages PPluginStream;
+ manages PStreamNotify;
+ manages PPluginSurface;
+
+child:
+ intr __delete__();
+
+ // This is only used on Windows and, for windowed plugins, must be called
+ // before the first call to NPP_SetWindow.
+ intr CreateChildPluginWindow()
+ returns (NativeWindowHandle childPluginWindow);
+
+ // This is only used on Windows and, for windowless plugins.
+ async CreateChildPopupSurrogate(NativeWindowHandle netscapeWindow);
+
+ intr NPP_SetWindow(NPRemoteWindow window);
+
+ intr NPP_GetValue_NPPVpluginWantsAllNetworkStreams()
+ returns (bool value, NPError result);
+
+ // this message is not used on non-X platforms
+ intr NPP_GetValue_NPPVpluginNeedsXEmbed()
+ returns (bool value, NPError result);
+
+ intr NPP_GetValue_NPPVpluginScriptableNPObject()
+ returns (nullable PPluginScriptableObject value, NPError result);
+
+ intr NPP_SetValue_NPNVprivateModeBool(bool value) returns (NPError result);
+ intr NPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId()
+ returns (nsCString plug_id, NPError result);
+
+ intr NPP_SetValue_NPNVCSSZoomFactor(double value) returns (NPError result);
+
+ intr NPP_SetValue_NPNVmuteAudioBool(bool muted) returns (NPError result);
+
+ intr NPP_HandleEvent(NPRemoteEvent event)
+ returns (int16_t handled);
+ // special cases where we need to a shared memory buffer
+ intr NPP_HandleEvent_Shmem(NPRemoteEvent event, Shmem buffer)
+ returns (int16_t handled, Shmem rtnbuffer);
+ // special cases where we need an iosurface
+ intr NPP_HandleEvent_IOSurface(NPRemoteEvent event, uint32_t surfaceid)
+ returns (int16_t handled);
+ // special cases of HandleEvent to make mediating races simpler
+ intr Paint(NPRemoteEvent event)
+ returns (int16_t handled);
+ // this is only used on windows to forward WM_WINDOWPOSCHANGE
+ async WindowPosChanged(NPRemoteEvent event);
+ // used on OS X to tell the child the contents scale factor
+ // of its parent has changed
+ async ContentsScaleFactorChanged(double aContentsScaleFactor);
+
+ // ********************** Async plugins rendering
+ // see https://wiki.mozilla.org/Gecko:AsyncPluginPainting
+ // **********************
+
+ // Async version of SetWindow call
+ // @param surfaceType - gfxASurface::gfxSurfaceType
+ // plugin child must create offscreen buffer
+ // with type equals to surfaceType
+ async AsyncSetWindow(gfxSurfaceType surfaceType, NPRemoteWindow window);
+
+ // There is now an opaque background behind this instance (or the
+ // background was updated). The changed area is |rect|. The
+ // browser owns the background surface, and it's read-only from
+ // within the plugin process. |background| is either null_t to
+ // refer to the existing background or a fresh descriptor.
+ async UpdateBackground(SurfaceDescriptor background, nsIntRect rect);
+
+ async NPP_DidComposite();
+
+ intr NPP_Destroy()
+ returns (NPError rv);
+
+ // HandledWindowedPluginKeyEvent() is always called after posting a native
+ // key event with OnWindowedPluginKeyEvent().
+ //
+ // @param aKeyEventData The key event which was posted to the parent
+ // process.
+ // @param aIsConsumed true if aKeyEventData is consumed in the
+ // parent process. Otherwise, false.
+ async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData,
+ bool aIsConsumed);
+
+parent:
+ intr NPN_GetValue_NPNVWindowNPObject()
+ returns (nullable PPluginScriptableObject value, NPError result);
+ intr NPN_GetValue_NPNVPluginElementNPObject()
+ returns (nullable PPluginScriptableObject value, NPError result);
+ intr NPN_GetValue_NPNVprivateModeBool()
+ returns (bool value, NPError result);
+ intr NPN_GetValue_NPNVnetscapeWindow()
+ returns (NativeWindowHandle value, NPError result);
+ intr NPN_GetValue_NPNVdocumentOrigin()
+ returns (nsCString value, NPError result);
+ intr NPN_GetValue_DrawingModelSupport(NPNVariable model)
+ returns (bool value);
+ intr NPN_GetValue_SupportsAsyncBitmapSurface()
+ returns (bool value);
+ intr NPN_GetValue_SupportsAsyncDXGISurface()
+ returns (bool value);
+ intr NPN_GetValue_PreferredDXGIAdapter()
+ returns (DxgiAdapterDesc desc);
+
+ intr NPN_SetValue_NPPVpluginWindow(bool windowed)
+ returns (NPError result);
+ intr NPN_SetValue_NPPVpluginTransparent(bool transparent)
+ returns (NPError result);
+ intr NPN_SetValue_NPPVpluginUsesDOMForCursor(bool useDOMForCursor)
+ returns (NPError result);
+ intr NPN_SetValue_NPPVpluginDrawingModel(int drawingModel)
+ returns (NPError result);
+ intr NPN_SetValue_NPPVpluginEventModel(int eventModel)
+ returns (NPError result);
+ intr NPN_SetValue_NPPVpluginIsPlayingAudio(bool isAudioPlaying)
+ returns (NPError result);
+
+ intr NPN_GetURL(nsCString url, nsCString target)
+ returns (NPError result);
+ intr NPN_PostURL(nsCString url, nsCString target, nsCString buffer, bool file)
+ returns (NPError result);
+
+ /**
+ * Covers both NPN_GetURLNotify and NPN_PostURLNotify.
+ * @TODO This would be more readable as an overloaded method,
+ * but IPDL doesn't allow that for constructors.
+ */
+ intr PStreamNotify(nsCString url, nsCString target, bool post,
+ nsCString buffer, bool file)
+ returns (NPError result);
+
+ async NPN_InvalidateRect(NPRect rect);
+
+ // Clear the current plugin image.
+ sync RevokeCurrentDirectSurface();
+
+ // Create a new DXGI shared surface with the given format and size. The
+ // returned handle, on success, can be opened as an ID3D10Texture2D or
+ // ID3D11Texture2D on a corresponding device.
+ sync InitDXGISurface(SurfaceFormat format, IntSize size)
+ returns (WindowsHandle handle, NPError result);
+
+ // Destroy a surface previously allocated with InitDXGISurface().
+ sync FinalizeDXGISurface(WindowsHandle handle);
+
+ // Set the current plugin image to the bitmap in the given shmem buffer. The
+ // format must be B8G8R8A8 or B8G8R8X8.
+ sync ShowDirectBitmap(Shmem buffer,
+ SurfaceFormat format,
+ uint32_t stride,
+ IntSize size,
+ IntRect dirty);
+
+ // Set the current plugin image to the DXGI surface in |handle|.
+ sync ShowDirectDXGISurface(WindowsHandle handle,
+ IntRect dirty);
+
+ // Give |newSurface|, containing this instance's updated pixels, to
+ // the browser for compositing. When this method returns, any surface
+ // previously passed to Show may be destroyed.
+ //
+ // @param rect - actually updated rectangle, comparing to prevSurface content
+ // could be used for partial render of layer to topLevel context
+ // @param newSurface - remotable surface
+ // @param prevSurface - if the previous surface was shared-memory, returns
+ // the shmem for reuse
+ sync Show(NPRect updatedRect, SurfaceDescriptor newSurface)
+ returns (SurfaceDescriptor prevSurface);
+
+ async PPluginSurface(WindowsSharedMemoryHandle handle,
+ IntSize size,
+ bool transparent);
+
+ intr NPN_PushPopupsEnabledState(bool aState);
+
+ intr NPN_PopPopupsEnabledState();
+
+ intr NPN_GetValueForURL(NPNURLVariable variable, nsCString url)
+ returns (nsCString value, NPError result);
+
+ intr NPN_SetValueForURL(NPNURLVariable variable, nsCString url,
+ nsCString value)
+ returns (NPError result);
+
+ intr NPN_GetAuthenticationInfo(nsCString protocol_, nsCString host,
+ int32_t port, nsCString scheme,
+ nsCString realm)
+ returns (nsCString username, nsCString password, NPError result);
+
+ intr NPN_ConvertPoint(double sourceX, bool ignoreDestX, double sourceY, bool ignoreDestY, NPCoordinateSpace sourceSpace,
+ NPCoordinateSpace destSpace)
+ returns (double destX, double destY, bool result);
+
+ async RedrawPlugin();
+
+ // Send notification that a plugin tried to negotiate Carbon NPAPI so that
+ // users can be notified that restarting the browser in i386 mode may allow
+ // them to use the plugin.
+ sync NegotiatedCarbon();
+
+ // Notifies the parent of its NPP_New result code.
+ async AsyncNPP_NewResult(NPError aResult);
+
+ // Sends a native window to be adopted by the native window that would be
+ // returned by NPN_GetValue_NPNVnetscapeWindow. Only used on Windows.
+ async SetNetscapeWindowAsParent(NativeWindowHandle childWindow);
+
+ sync GetCompositionString(uint32_t aType)
+ returns (uint8_t[] aDist, int32_t aLength);
+ // Set candidate window position.
+ //
+ // @param aPosition position information of candidate window
+ async SetCandidateWindow(CandidateWindowPosition aPosition);
+ async RequestCommitOrCancel(bool aCommitted);
+
+ // Notifies the parent process of a plugin instance receiving key event
+ // directly.
+ //
+ // @param aKeyEventData The native key event which will be sent to
+ // plugin from native event handler.
+ async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData);
+
+both:
+ async PPluginScriptableObject();
+
+child:
+ /* NPP_NewStream */
+ async PBrowserStream(nsCString url,
+ uint32_t length,
+ uint32_t lastmodified,
+ nullable PStreamNotify notifyData,
+ nsCString headers);
+
+ // Implements the legacy (synchronous) version of NPP_NewStream for when
+ // async plugin init is preffed off.
+ intr NPP_NewStream(PBrowserStream actor, nsCString mimeType, bool seekable)
+ returns (NPError rv,
+ uint16_t stype);
+
+ // Implements the async plugin init version of NPP_NewStream.
+ async AsyncNPP_NewStream(PBrowserStream actor, nsCString mimeType, bool seekable);
+
+parent:
+ /* NPN_NewStream */
+ intr PPluginStream(nsCString mimeType,
+ nsCString target)
+ returns (NPError result);
+
+parent:
+ intr PluginFocusChange(bool gotFocus);
+
+child:
+ intr SetPluginFocus();
+ intr UpdateWindow();
+
+ async PPluginBackgroundDestroyer();
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PPluginModule.ipdl b/dom/plugins/ipc/PPluginModule.ipdl
new file mode 100644
index 000000000..ecde41b63
--- /dev/null
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 protocol PPluginInstance;
+include protocol PPluginScriptableObject;
+include protocol PCrashReporter;
+include protocol PContent;
+include ProfilerTypes;
+
+using NPError from "npapi.h";
+using NPNVariable from "npapi.h";
+using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
+using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
+using struct nsID from "nsID.h";
+using struct mozilla::plugins::NPAudioDeviceChangeDetailsIPC from "mozilla/plugins/PluginMessageUtils.h";
+
+namespace mozilla {
+namespace plugins {
+
+struct PluginSettings
+{
+ // These settings correspond to NPNVariable. They are fetched from
+ // mozilla::plugins::parent::_getvalue.
+ bool javascriptEnabled;
+ bool asdEnabled;
+ bool isOffline;
+ bool supportsXembed;
+ bool supportsWindowless;
+
+ // These settings come from elsewhere.
+ nsCString userAgent;
+ bool nativeCursorsSupported;
+};
+
+intr protocol PPluginModule
+{
+ bridges PContent, PPluginModule;
+
+ manages PPluginInstance;
+ manages PCrashReporter;
+
+both:
+ // Window-specific message which instructs the interrupt mechanism to enter
+ // a nested event loop for the current interrupt call.
+ async ProcessNativeEventsInInterruptCall();
+
+child:
+ async DisableFlashProtectedMode();
+
+ // Sync query to check if a Flash library indicates it
+ // supports async rendering mode.
+ intr ModuleSupportsAsyncRender()
+ returns (bool result);
+
+ // Forces the child process to update its plugin function table.
+ intr NP_GetEntryPoints()
+ returns (NPError rv);
+
+ intr NP_Initialize(PluginSettings settings)
+ returns (NPError rv);
+
+ async AsyncNP_Initialize(PluginSettings settings);
+
+ async PPluginInstance(nsCString aMimeType,
+ uint16_t aMode,
+ nsCString[] aNames,
+ nsCString[] aValues);
+
+ // Implements the synchronous version of NPP_New for when async plugin init
+ // is preffed off.
+ intr SyncNPP_New(PPluginInstance aActor)
+ returns (NPError rv);
+
+ // Implements the async plugin init version of NPP_New.
+ async AsyncNPP_New(PPluginInstance aActor);
+
+ intr NP_Shutdown()
+ returns (NPError rv);
+
+ intr OptionalFunctionsSupported()
+ returns (bool aURLRedirectNotify, bool aClearSiteData,
+ bool aGetSitesWithData);
+
+ async NPP_ClearSiteData(nsCString site, uint64_t flags, uint64_t maxAge, uint64_t aCallbackId);
+
+ async NPP_GetSitesWithData(uint64_t aCallbackId);
+
+ // Windows specific message to set up an audio session in the plugin process
+ async SetAudioSessionData(nsID aID,
+ nsString aDisplayName,
+ nsString aIconPath);
+
+ async SetParentHangTimeout(uint32_t seconds);
+
+ intr PCrashReporter()
+ returns (NativeThreadId tid, uint32_t processType);
+
+ /**
+ * Control the Gecko Profiler in the plugin process.
+ */
+ async StartProfiler(ProfilerInitParams params);
+ async StopProfiler();
+
+ async GatherProfile();
+
+ async SettingChanged(PluginSettings settings);
+
+ async NPP_SetValue_NPNVaudioDeviceChangeDetails(NPAudioDeviceChangeDetailsIPC changeDetails);
+
+parent:
+ async NP_InitializeResult(NPError aError);
+
+ /**
+ * This message is only used on X11 platforms.
+ *
+ * Send a dup of the plugin process's X socket to the parent
+ * process. In theory, this scheme keeps the plugin's X resources
+ * around until after both the plugin process shuts down *and* the
+ * parent process closes the dup fd. This is used to prevent the
+ * parent process from crashing on X errors if, e.g., the plugin
+ * crashes *just before* a repaint and the parent process tries to
+ * use the newly-invalid surface.
+ */
+ async BackUpXResources(FileDescriptor aXSocketFd);
+
+ // Wake up and process a few native events. Periodically called by
+ // Gtk-specific code upon detecting that the plugin process has
+ // entered a nested event loop. If the browser doesn't process
+ // native events, then "livelock" and some other glitches can occur.
+ intr ProcessSomeEvents();
+
+ // OS X Specific calls to manage the plugin's window
+ // when interposing system calls.
+ async PluginShowWindow(uint32_t aWindowId, bool aModal,
+ int32_t aX, int32_t aY,
+ size_t aWidth, size_t aHeight);
+ async PluginHideWindow(uint32_t aWindowId);
+
+ // OS X Specific calls to allow the plugin to manage the cursor.
+ async SetCursor(NSCursorInfo cursorInfo);
+ async ShowCursor(bool show);
+ async PushCursor(NSCursorInfo cursorInfo);
+ async PopCursor();
+
+ sync NPN_SetException(nsCString message);
+
+ async NPN_ReloadPlugins(bool aReloadPages);
+
+ // Notifies the chrome process that a PluginModuleChild linked to a content
+ // process was destroyed. The chrome process may choose to asynchronously shut
+ // down the plugin process in response.
+ async NotifyContentModuleDestroyed();
+
+ async Profile(nsCString aProfile);
+
+ // Answers to request about site data
+ async ReturnClearSiteData(NPError aRv, uint64_t aCallbackId);
+
+ async ReturnSitesWithData(nsCString[] aSites, uint64_t aCallbackId);
+
+ intr GetKeyState(int32_t aVirtKey)
+ returns (int16_t aState);
+
+ intr NPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(bool shouldRegister)
+ returns (NPError result);
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PPluginScriptableObject.ipdl b/dom/plugins/ipc/PPluginScriptableObject.ipdl
new file mode 100644
index 000000000..15d6cc148
--- /dev/null
+++ b/dom/plugins/ipc/PPluginScriptableObject.ipdl
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 protocol PPluginInstance;
+include PluginTypes;
+
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace plugins {
+
+union Variant {
+ void_t;
+ null_t;
+ bool;
+ int;
+ double;
+ nsCString;
+ nullable PPluginScriptableObject;
+};
+
+intr protocol PPluginScriptableObject
+{
+ manager PPluginInstance;
+
+both:
+ async __delete__();
+
+parent:
+ intr NPN_Evaluate(nsCString aScript)
+ returns (Variant aResult,
+ bool aSuccess);
+
+child:
+ intr Invalidate();
+
+both:
+ // NPClass methods
+ intr HasMethod(PluginIdentifier aId)
+ returns (bool aHasMethod);
+
+ intr Invoke(PluginIdentifier aId,
+ Variant[] aArgs)
+ returns (Variant aResult,
+ bool aSuccess);
+
+ intr InvokeDefault(Variant[] aArgs)
+ returns (Variant aResult,
+ bool aSuccess);
+
+ intr HasProperty(PluginIdentifier aId)
+ returns (bool aHasProperty);
+
+ intr SetProperty(PluginIdentifier aId,
+ Variant aValue)
+ returns (bool aSuccess);
+
+ intr RemoveProperty(PluginIdentifier aId)
+ returns (bool aSuccess);
+
+ intr Enumerate()
+ returns (PluginIdentifier[] aProperties,
+ bool aSuccess);
+
+ intr Construct(Variant[] aArgs)
+ returns (Variant aResult,
+ bool aSuccess);
+
+ // Objects are initially unprotected, and the Protect and Unprotect functions
+ // only affect protocol objects that represent NPObjects created in the same
+ // process (rather than protocol objects that are a proxy for an NPObject
+ // created in another process). Protocol objects representing local NPObjects
+ // are protected after an NPObject has been associated with the protocol
+ // object. Sending the protocol object as an argument to the other process
+ // temporarily protects the protocol object again for the duration of the call.
+ async Protect();
+ async Unprotect();
+
+ /**
+ * GetProperty is slightly wonky due to the way we support NPObjects that have
+ * methods and properties with the same name. When child calls parent we
+ * simply return a property. When parent calls child, however, we need to do
+ * several checks at once and return all the results simultaneously.
+ */
+parent:
+ intr GetParentProperty(PluginIdentifier aId)
+ returns (Variant aResult,
+ bool aSuccess);
+
+child:
+ intr GetChildProperty(PluginIdentifier aId)
+ returns (bool aHasProperty,
+ bool aHasMethod,
+ Variant aResult,
+ bool aSuccess);
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PPluginStream.ipdl b/dom/plugins/ipc/PPluginStream.ipdl
new file mode 100644
index 000000000..49ba00e79
--- /dev/null
+++ b/dom/plugins/ipc/PPluginStream.ipdl
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 protocol PPluginInstance;
+
+
+using mozilla::plugins::Buffer from "mozilla/plugins/PluginMessageUtils.h";
+using NPError from "npapi.h";
+using NPReason from "npapi.h";
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * PPluginStream represents an NPStream sent from the plugin to the browser.
+ */
+
+intr protocol PPluginStream
+{
+ manager PPluginInstance;
+
+parent:
+ intr NPN_Write(Buffer data) returns (int32_t written);
+
+both:
+ /**
+ * ~PPluginStream is for both NPN_DestroyStream and NPP_DestroyStream.
+ * @param artificial True when the stream is closed as a by-product of
+ * some other call (such as a failure in NPN_Write).
+ */
+ intr __delete__(NPReason reason, bool artificial);
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PPluginSurface.ipdl b/dom/plugins/ipc/PPluginSurface.ipdl
new file mode 100644
index 000000000..7be038c60
--- /dev/null
+++ b/dom/plugins/ipc/PPluginSurface.ipdl
@@ -0,0 +1,18 @@
+/* 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 protocol PPluginInstance;
+
+namespace mozilla {
+namespace plugins {
+
+async protocol PPluginSurface {
+ manager PPluginInstance;
+
+parent:
+ async __delete__();
+};
+
+}
+}
diff --git a/dom/plugins/ipc/PStreamNotify.ipdl b/dom/plugins/ipc/PStreamNotify.ipdl
new file mode 100644
index 000000000..3e196acab
--- /dev/null
+++ b/dom/plugins/ipc/PStreamNotify.ipdl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 protocol PPluginInstance;
+
+
+using NPReason from "npapi.h";
+
+namespace mozilla {
+namespace plugins {
+
+intr protocol PStreamNotify
+{
+ manager PPluginInstance;
+
+parent:
+
+ /**
+ * Represents NPN_URLRedirectResponse
+ */
+ async RedirectNotifyResponse(bool allow);
+
+child:
+ /**
+ * Represents NPP_URLRedirectNotify
+ */
+ async RedirectNotify(nsCString url, int32_t status);
+
+ /**
+ * Represents NPP_URLNotify
+ */
+ async __delete__(NPReason reason);
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginAsyncSurrogate.cpp b/dom/plugins/ipc/PluginAsyncSurrogate.cpp
new file mode 100644
index 000000000..da07116cc
--- /dev/null
+++ b/dom/plugins/ipc/PluginAsyncSurrogate.cpp
@@ -0,0 +1,998 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "PluginAsyncSurrogate.h"
+
+#include "base/message_loop.h"
+#include "base/message_pump_default.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/plugins/PluginInstanceParent.h"
+#include "mozilla/plugins/PluginModuleParent.h"
+#include "mozilla/plugins/PluginScriptableObjectParent.h"
+#include "mozilla/Telemetry.h"
+#include "nsJSNPRuntime.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsPluginStreamListenerPeer.h"
+#include "npruntime.h"
+#include "nsThreadUtils.h"
+#include "PluginMessageUtils.h"
+
+namespace mozilla {
+namespace plugins {
+
+AsyncNPObject::AsyncNPObject(PluginAsyncSurrogate* aSurrogate)
+ : NPObject()
+ , mSurrogate(aSurrogate)
+ , mRealObject(nullptr)
+{
+}
+
+AsyncNPObject::~AsyncNPObject()
+{
+ if (mRealObject) {
+ --mRealObject->asyncWrapperCount;
+ parent::_releaseobject(mRealObject);
+ mRealObject = nullptr;
+ }
+}
+
+NPObject*
+AsyncNPObject::GetRealObject()
+{
+ if (mRealObject) {
+ return mRealObject;
+ }
+ PluginInstanceParent* instance = PluginInstanceParent::Cast(mSurrogate->GetNPP());
+ if (!instance) {
+ return nullptr;
+ }
+ NPObject* realObject = nullptr;
+ NPError err = instance->NPP_GetValue(NPPVpluginScriptableNPObject,
+ &realObject);
+ if (err != NPERR_NO_ERROR) {
+ return nullptr;
+ }
+ if (realObject->_class != PluginScriptableObjectParent::GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ parent::_releaseobject(realObject);
+ return nullptr;
+ }
+ mRealObject = static_cast<ParentNPObject*>(realObject);
+ ++mRealObject->asyncWrapperCount;
+ return mRealObject;
+}
+
+class MOZ_STACK_CLASS RecursionGuard
+{
+public:
+ RecursionGuard()
+ : mIsRecursive(sHasEntered)
+ {
+ if (!mIsRecursive) {
+ sHasEntered = true;
+ }
+ }
+
+ ~RecursionGuard()
+ {
+ if (!mIsRecursive) {
+ sHasEntered = false;
+ }
+ }
+
+ inline bool
+ IsRecursive()
+ {
+ return mIsRecursive;
+ }
+
+private:
+ bool mIsRecursive;
+ static bool sHasEntered;
+};
+
+bool RecursionGuard::sHasEntered = false;
+
+PluginAsyncSurrogate::PluginAsyncSurrogate(PluginModuleParent* aParent)
+ : mParent(aParent)
+ , mMode(0)
+ , mWindow(nullptr)
+ , mAcceptCalls(false)
+ , mInstantiated(false)
+ , mAsyncSetWindow(false)
+ , mInitCancelled(false)
+ , mDestroyPending(false)
+ , mAsyncCallsInFlight(0)
+{
+ MOZ_ASSERT(aParent);
+}
+
+PluginAsyncSurrogate::~PluginAsyncSurrogate()
+{
+}
+
+bool
+PluginAsyncSurrogate::Init(NPMIMEType aPluginType, NPP aInstance, uint16_t aMode,
+ int16_t aArgc, char* aArgn[], char* aArgv[])
+{
+ mMimeType = aPluginType;
+ nsNPAPIPluginInstance* instance =
+ static_cast<nsNPAPIPluginInstance*>(aInstance->ndata);
+ MOZ_ASSERT(instance);
+ mInstance = instance;
+ mMode = aMode;
+ for (int i = 0; i < aArgc; ++i) {
+ mNames.AppendElement(NullableString(aArgn[i]));
+ mValues.AppendElement(NullableString(aArgv[i]));
+ }
+ return true;
+}
+
+/* static */ bool
+PluginAsyncSurrogate::Create(PluginModuleParent* aParent, NPMIMEType aPluginType,
+ NPP aInstance, uint16_t aMode, int16_t aArgc,
+ char* aArgn[], char* aArgv[])
+{
+ RefPtr<PluginAsyncSurrogate> surrogate(new PluginAsyncSurrogate(aParent));
+ if (!surrogate->Init(aPluginType, aInstance, aMode, aArgc, aArgn, aArgv)) {
+ return false;
+ }
+ PluginAsyncSurrogate* rawSurrogate = nullptr;
+ surrogate.forget(&rawSurrogate);
+ aInstance->pdata = static_cast<PluginDataResolver*>(rawSurrogate);
+ return true;
+}
+
+/* static */ PluginAsyncSurrogate*
+PluginAsyncSurrogate::Cast(NPP aInstance)
+{
+ MOZ_ASSERT(aInstance);
+ PluginDataResolver* resolver =
+ reinterpret_cast<PluginDataResolver*>(aInstance->pdata);
+ if (!resolver) {
+ return nullptr;
+ }
+ return resolver->GetAsyncSurrogate();
+}
+
+nsresult
+PluginAsyncSurrogate::NPP_New(NPError* aError)
+{
+ if (!mInstance) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsresult rv = mParent->NPP_NewInternal(mMimeType.BeginWriting(), GetNPP(),
+ mMode, mNames, mValues, nullptr,
+ aError);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+void
+PluginAsyncSurrogate::NP_GetEntryPoints(NPPluginFuncs* aFuncs)
+{
+ aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+ aFuncs->destroy = &NPP_Destroy;
+ aFuncs->getvalue = &NPP_GetValue;
+ aFuncs->setvalue = &NPP_SetValue;
+ aFuncs->newstream = &NPP_NewStream;
+ aFuncs->setwindow = &NPP_SetWindow;
+ aFuncs->writeready = &NPP_WriteReady;
+ aFuncs->event = &NPP_HandleEvent;
+ aFuncs->destroystream = &NPP_DestroyStream;
+ // We need to set these so that content code doesn't make assumptions
+ // about these operations not being supported
+ aFuncs->write = &PluginModuleParent::NPP_Write;
+ aFuncs->asfile = &PluginModuleParent::NPP_StreamAsFile;
+}
+
+/* static */ void
+PluginAsyncSurrogate::NotifyDestroyPending(NPP aInstance)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ if (!surrogate) {
+ return;
+ }
+ surrogate->NotifyDestroyPending();
+}
+
+NPP
+PluginAsyncSurrogate::GetNPP()
+{
+ MOZ_ASSERT(mInstance);
+ NPP npp;
+ DebugOnly<nsresult> rv = mInstance->GetNPP(&npp);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return npp;
+}
+
+void
+PluginAsyncSurrogate::NotifyDestroyPending()
+{
+ mDestroyPending = true;
+ nsJSNPRuntime::OnPluginDestroyPending(GetNPP());
+}
+
+NPError
+PluginAsyncSurrogate::NPP_Destroy(NPSavedData** aSave)
+{
+ NotifyDestroyPending();
+ if (!WaitForInit()) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return PluginModuleParent::NPP_Destroy(GetNPP(), aSave);
+}
+
+NPError
+PluginAsyncSurrogate::NPP_GetValue(NPPVariable aVariable, void* aRetval)
+{
+ if (aVariable != NPPVpluginScriptableNPObject) {
+ if (!WaitForInit()) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ PluginInstanceParent* instance = PluginInstanceParent::Cast(GetNPP());
+ MOZ_ASSERT(instance);
+ return instance->NPP_GetValue(aVariable, aRetval);
+ }
+
+ NPObject* npobject = parent::_createobject(GetNPP(),
+ const_cast<NPClass*>(GetClass()));
+ MOZ_ASSERT(npobject);
+ MOZ_ASSERT(npobject->_class == GetClass());
+ MOZ_ASSERT(npobject->referenceCount == 1);
+ *(NPObject**)aRetval = npobject;
+ return npobject ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_SetValue(NPNVariable aVariable, void* aValue)
+{
+ if (!WaitForInit()) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return PluginModuleParent::NPP_SetValue(GetNPP(), aVariable, aValue);
+}
+
+NPError
+PluginAsyncSurrogate::NPP_NewStream(NPMIMEType aType, NPStream* aStream,
+ NPBool aSeekable, uint16_t* aStype)
+{
+ mPendingNewStreamCalls.AppendElement(PendingNewStreamCall(aType, aStream,
+ aSeekable));
+ if (aStype) {
+ *aStype = nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN;
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_SetWindow(NPWindow* aWindow)
+{
+ mWindow = aWindow;
+ mAsyncSetWindow = false;
+ return NPERR_NO_ERROR;
+}
+
+nsresult
+PluginAsyncSurrogate::AsyncSetWindow(NPWindow* aWindow)
+{
+ mWindow = aWindow;
+ mAsyncSetWindow = true;
+ return NS_OK;
+}
+
+void
+PluginAsyncSurrogate::NPP_Print(NPPrint* aPrintInfo)
+{
+ // Do nothing, we've got nothing to print right now
+}
+
+int16_t
+PluginAsyncSurrogate::NPP_HandleEvent(void* event)
+{
+ // Drop the event -- the plugin isn't around to handle it
+ return false;
+}
+
+int32_t
+PluginAsyncSurrogate::NPP_WriteReady(NPStream* aStream)
+{
+ // We'll tell the browser to retry in a bit. Eventually NPP_WriteReady
+ // will resolve to the plugin's NPP_WriteReady and this should all just work.
+ return 0;
+}
+
+NPError
+PluginAsyncSurrogate::NPP_DestroyStream(NPStream* aStream, NPReason aReason)
+{
+ for (uint32_t idx = 0, len = mPendingNewStreamCalls.Length(); idx < len; ++idx) {
+ PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[idx];
+ if (curPendingCall.mStream == aStream) {
+ mPendingNewStreamCalls.RemoveElementAt(idx);
+ break;
+ }
+ }
+ return NPERR_NO_ERROR;
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_Destroy(NPP aInstance, NPSavedData** aSave)
+{
+ PluginAsyncSurrogate* rawSurrogate = Cast(aInstance);
+ MOZ_ASSERT(rawSurrogate);
+ PluginModuleParent* module = rawSurrogate->GetParent();
+ if (module && !module->IsInitialized()) {
+ // Take ownership of pdata's surrogate since we're going to release it
+ RefPtr<PluginAsyncSurrogate> surrogate(dont_AddRef(rawSurrogate));
+ aInstance->pdata = nullptr;
+ // We haven't actually called NPP_New yet, so we should remove the
+ // surrogate for this instance.
+ bool removeOk = module->RemovePendingSurrogate(surrogate);
+ MOZ_ASSERT(removeOk);
+ if (!removeOk) {
+ return NPERR_GENERIC_ERROR;
+ }
+ surrogate->mInitCancelled = true;
+ return NPERR_NO_ERROR;
+ }
+ return rawSurrogate->NPP_Destroy(aSave);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_GetValue(NPP aInstance, NPPVariable aVariable,
+ void* aRetval)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_GetValue(aVariable, aRetval);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_SetValue(NPP aInstance, NPNVariable aVariable,
+ void* aValue)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_SetValue(aVariable, aValue);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_NewStream(NPP aInstance, NPMIMEType aType,
+ NPStream* aStream, NPBool aSeekable,
+ uint16_t* aStype)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_NewStream(aType, aStream, aSeekable, aStype);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_SetWindow(NPP aInstance, NPWindow* aWindow)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_SetWindow(aWindow);
+}
+
+/* static */ void
+PluginAsyncSurrogate::NPP_Print(NPP aInstance, NPPrint* aPrintInfo)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ surrogate->NPP_Print(aPrintInfo);
+}
+
+/* static */ int16_t
+PluginAsyncSurrogate::NPP_HandleEvent(NPP aInstance, void* aEvent)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_HandleEvent(aEvent);
+}
+
+/* static */ int32_t
+PluginAsyncSurrogate::NPP_WriteReady(NPP aInstance, NPStream* aStream)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_WriteReady(aStream);
+}
+
+/* static */ NPError
+PluginAsyncSurrogate::NPP_DestroyStream(NPP aInstance,
+ NPStream* aStream,
+ NPReason aReason)
+{
+ PluginAsyncSurrogate* surrogate = Cast(aInstance);
+ MOZ_ASSERT(surrogate);
+ return surrogate->NPP_DestroyStream(aStream, aReason);
+}
+
+PluginAsyncSurrogate::PendingNewStreamCall::PendingNewStreamCall(
+ NPMIMEType aType, NPStream* aStream, NPBool aSeekable)
+ : mType(NullableString(aType))
+ , mStream(aStream)
+ , mSeekable(aSeekable)
+{
+}
+
+/* static */ nsNPAPIPluginStreamListener*
+PluginAsyncSurrogate::GetStreamListener(NPStream* aStream)
+{
+ nsNPAPIStreamWrapper* wrapper =
+ reinterpret_cast<nsNPAPIStreamWrapper*>(aStream->ndata);
+ if (!wrapper) {
+ return nullptr;
+ }
+ return wrapper->GetStreamListener();
+}
+
+void
+PluginAsyncSurrogate::DestroyAsyncStream(NPStream* aStream)
+{
+ MOZ_ASSERT(aStream);
+ nsNPAPIPluginStreamListener* streamListener = GetStreamListener(aStream);
+ MOZ_ASSERT(streamListener);
+ // streamListener was suspended during async init. We must resume the stream
+ // request prior to calling _destroystream for cleanup to work correctly.
+ streamListener->ResumeRequest();
+ if (!mInstance) {
+ return;
+ }
+ parent::_destroystream(GetNPP(), aStream, NPRES_DONE);
+}
+
+/* static */ bool
+PluginAsyncSurrogate::SetStreamType(NPStream* aStream, uint16_t aStreamType)
+{
+ nsNPAPIPluginStreamListener* streamListener = GetStreamListener(aStream);
+ if (!streamListener) {
+ return false;
+ }
+ return streamListener->SetStreamType(aStreamType);
+}
+
+void
+PluginAsyncSurrogate::OnInstanceCreated(PluginInstanceParent* aInstance)
+{
+ if (!mDestroyPending) {
+ // If NPP_Destroy has already been called then these streams have already
+ // been cleaned up on the browser side and are no longer valid.
+ for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
+ PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
+ uint16_t streamType = NP_NORMAL;
+ NPError curError = aInstance->NPP_NewStream(
+ const_cast<char*>(NullableStringGet(curPendingCall.mType)),
+ curPendingCall.mStream, curPendingCall.mSeekable,
+ &streamType);
+ if (curError != NPERR_NO_ERROR) {
+ // If we failed here then the send failed and we need to clean up
+ DestroyAsyncStream(curPendingCall.mStream);
+ }
+ }
+ }
+ mPendingNewStreamCalls.Clear();
+ mInstantiated = true;
+}
+
+/**
+ * During asynchronous initialization it might be necessary to wait for the
+ * plugin to complete its initialization. This typically occurs when the result
+ * of a plugin call depends on the plugin being fully instantiated. For example,
+ * if some JS calls into the plugin, the call must be executed synchronously to
+ * preserve correctness.
+ *
+ * This function works by pumping the plugin's IPC channel for events until
+ * initialization has completed.
+ */
+bool
+PluginAsyncSurrogate::WaitForInit()
+{
+ if (mInitCancelled) {
+ return false;
+ }
+ if (mAcceptCalls) {
+ return true;
+ }
+ Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGINASYNCSURROGATE_WAITFORINIT_MS>
+ timer(mParent->GetHistogramKey());
+ bool result = false;
+ MOZ_ASSERT(mParent);
+ if (mParent->IsChrome()) {
+ PluginProcessParent* process = static_cast<PluginModuleChromeParent*>(mParent)->Process();
+ MOZ_ASSERT(process);
+ process->SetCallRunnableImmediately(true);
+ if (!process->WaitUntilConnected()) {
+ return false;
+ }
+ }
+ if (!mParent->WaitForIPCConnection()) {
+ return false;
+ }
+ if (!mParent->IsChrome()) {
+ // For e10s content processes, we need to spin the content channel until the
+ // protocol bridging has occurred.
+ dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+ mozilla::ipc::MessageChannel* contentChannel = cp->GetIPCChannel();
+ MOZ_ASSERT(contentChannel);
+ while (!mParent->mNPInitialized) {
+ if (mParent->mShutdown) {
+ // Since we are pumping the message channel for events, it may be
+ // possible for module initialization to fail during this loop. We must
+ // return false if this happens or else we'll be permanently stuck.
+ return false;
+ }
+ result = contentChannel->WaitForIncomingMessage();
+ if (!result) {
+ return result;
+ }
+ }
+ }
+ mozilla::ipc::MessageChannel* channel = mParent->GetIPCChannel();
+ MOZ_ASSERT(channel);
+ while (!mAcceptCalls) {
+ if (mInitCancelled) {
+ // Since we are pumping the message channel for events, it may be
+ // possible for plugin instantiation to fail during this loop. We must
+ // return false if this happens or else we'll be permanently stuck.
+ return false;
+ }
+ result = channel->WaitForIncomingMessage();
+ if (!result) {
+ break;
+ }
+ }
+ return result;
+}
+
+void
+PluginAsyncSurrogate::AsyncCallDeparting()
+{
+ ++mAsyncCallsInFlight;
+ if (!mPluginDestructionGuard) {
+ mPluginDestructionGuard = MakeUnique<PluginDestructionGuard>(this);
+ }
+}
+
+void
+PluginAsyncSurrogate::AsyncCallArriving()
+{
+ MOZ_ASSERT(mAsyncCallsInFlight > 0);
+ if (--mAsyncCallsInFlight == 0) {
+ mPluginDestructionGuard.reset(nullptr);
+ }
+}
+
+void
+PluginAsyncSurrogate::NotifyAsyncInitFailed()
+{
+ if (!mDestroyPending) {
+ // Clean up any pending NewStream requests
+ for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) {
+ PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i];
+ DestroyAsyncStream(curPendingCall.mStream);
+ }
+ }
+ mPendingNewStreamCalls.Clear();
+
+ // Make sure that any WaitForInit calls on this surrogate will fail, or else
+ // we'll be perma-blocked
+ mInitCancelled = true;
+
+ if (!mInstance) {
+ return;
+ }
+ nsPluginInstanceOwner* owner = mInstance->GetOwner();
+ if (owner) {
+ owner->NotifyHostAsyncInitFailed();
+ }
+}
+
+// static
+NPObject*
+PluginAsyncSurrogate::ScriptableAllocate(NPP aInstance, NPClass* aClass)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aClass != GetClass()) {
+ NS_ERROR("Huh?! Wrong class!");
+ return nullptr;
+ }
+
+ return new AsyncNPObject(Cast(aInstance));
+}
+
+// static
+void
+PluginAsyncSurrogate::ScriptableInvalidate(NPObject* aObject)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return;
+ }
+ realObject->_class->invalidate(realObject);
+}
+
+// static
+void
+PluginAsyncSurrogate::ScriptableDeallocate(NPObject* aObject)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ delete object;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableHasMethod(NPObject* aObject,
+ NPIdentifier aName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ RecursionGuard guard;
+ if (guard.IsRecursive()) {
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ MOZ_ASSERT(object);
+ bool checkPluginObject = !object->mSurrogate->mInstantiated &&
+ !object->mSurrogate->mAcceptCalls;
+
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ bool result = realObject->_class->hasMethod(realObject, aName);
+ if (!result && checkPluginObject) {
+ // We may be calling into this object because properties in the WebIDL
+ // object hadn't been set yet. Now that we're further along in
+ // initialization, we should try again.
+ const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs();
+ NPObject* pluginObject = nullptr;
+ NPError nperror = npn->getvalue(object->mSurrogate->GetNPP(),
+ NPNVPluginElementNPObject,
+ (void*)&pluginObject);
+ if (nperror == NPERR_NO_ERROR) {
+ NPPAutoPusher nppPusher(object->mSurrogate->GetNPP());
+ result = pluginObject->_class->hasMethod(pluginObject, aName);
+ npn->releaseobject(pluginObject);
+ NPUTF8* idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ }
+ }
+ return result;
+}
+
+bool
+PluginAsyncSurrogate::GetPropertyHelper(NPObject* aObject, NPIdentifier aName,
+ bool* aHasProperty, bool* aHasMethod,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ if (!aObject) {
+ return false;
+ }
+
+ RecursionGuard guard;
+ if (guard.IsRecursive()) {
+ return false;
+ }
+
+ if (!WaitForInit()) {
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ if (realObject->_class != PluginScriptableObjectParent::GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ PluginScriptableObjectParent* actor =
+ static_cast<ParentNPObject*>(realObject)->parent;
+ if (!actor) {
+ return false;
+ }
+ bool success = actor->GetPropertyHelper(aName, aHasProperty, aHasMethod, aResult);
+ if (!success) {
+ const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs();
+ NPObject* pluginObject = nullptr;
+ NPError nperror = npn->getvalue(GetNPP(), NPNVPluginElementNPObject,
+ (void*)&pluginObject);
+ if (nperror == NPERR_NO_ERROR) {
+ NPPAutoPusher nppPusher(GetNPP());
+ bool hasProperty = nsJSObjWrapper::HasOwnProperty(pluginObject, aName);
+ NPUTF8* idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ bool hasMethod = false;
+ if (hasProperty) {
+ hasMethod = pluginObject->_class->hasMethod(pluginObject, aName);
+ success = pluginObject->_class->getProperty(pluginObject, aName, aResult);
+ idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ }
+ *aHasProperty = hasProperty;
+ *aHasMethod = hasMethod;
+ npn->releaseobject(pluginObject);
+ }
+ }
+ return success;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableInvoke(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->invoke(realObject, aName, aArgs, aArgCount, aResult);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableInvokeDefault(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->invokeDefault(realObject, aArgs, aArgCount, aResult);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableHasProperty(NPObject* aObject,
+ NPIdentifier aName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ RecursionGuard guard;
+ if (guard.IsRecursive()) {
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ MOZ_ASSERT(object);
+ bool checkPluginObject = !object->mSurrogate->mInstantiated &&
+ !object->mSurrogate->mAcceptCalls;
+
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ bool result = realObject->_class->hasProperty(realObject, aName);
+ const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs();
+ NPUTF8* idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ if (!result && checkPluginObject) {
+ // We may be calling into this object because properties in the WebIDL
+ // object hadn't been set yet. Now that we're further along in
+ // initialization, we should try again.
+ NPObject* pluginObject = nullptr;
+ NPError nperror = npn->getvalue(object->mSurrogate->GetNPP(),
+ NPNVPluginElementNPObject,
+ (void*)&pluginObject);
+ if (nperror == NPERR_NO_ERROR) {
+ NPPAutoPusher nppPusher(object->mSurrogate->GetNPP());
+ result = nsJSObjWrapper::HasOwnProperty(pluginObject, aName);
+ npn->releaseobject(pluginObject);
+ idstr = npn->utf8fromidentifier(aName);
+ npn->memfree(idstr);
+ }
+ }
+ return result;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableGetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ // See GetPropertyHelper below.
+ NS_NOTREACHED("Shouldn't ever call this directly!");
+ return false;
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableSetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aValue)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->setProperty(realObject, aName, aValue);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableRemoveProperty(NPObject* aObject,
+ NPIdentifier aName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->removeProperty(realObject, aName);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableEnumerate(NPObject* aObject,
+ NPIdentifier** aIdentifiers,
+ uint32_t* aCount)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->enumerate(realObject, aIdentifiers, aCount);
+}
+
+// static
+bool
+PluginAsyncSurrogate::ScriptableConstruct(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ AsyncNPObject* object = static_cast<AsyncNPObject*>(aObject);
+ if (!object->mSurrogate->WaitForInit()) {
+ return false;
+ }
+ NPObject* realObject = object->GetRealObject();
+ if (!realObject) {
+ return false;
+ }
+ return realObject->_class->construct(realObject, aArgs, aArgCount, aResult);
+}
+
+const NPClass PluginAsyncSurrogate::sNPClass = {
+ NP_CLASS_STRUCT_VERSION,
+ PluginAsyncSurrogate::ScriptableAllocate,
+ PluginAsyncSurrogate::ScriptableDeallocate,
+ PluginAsyncSurrogate::ScriptableInvalidate,
+ PluginAsyncSurrogate::ScriptableHasMethod,
+ PluginAsyncSurrogate::ScriptableInvoke,
+ PluginAsyncSurrogate::ScriptableInvokeDefault,
+ PluginAsyncSurrogate::ScriptableHasProperty,
+ PluginAsyncSurrogate::ScriptableGetProperty,
+ PluginAsyncSurrogate::ScriptableSetProperty,
+ PluginAsyncSurrogate::ScriptableRemoveProperty,
+ PluginAsyncSurrogate::ScriptableEnumerate,
+ PluginAsyncSurrogate::ScriptableConstruct
+};
+
+PushSurrogateAcceptCalls::PushSurrogateAcceptCalls(PluginInstanceParent* aInstance)
+ : mSurrogate(nullptr)
+ , mPrevAcceptCallsState(false)
+{
+ MOZ_ASSERT(aInstance);
+ mSurrogate = aInstance->GetAsyncSurrogate();
+ if (mSurrogate) {
+ mPrevAcceptCallsState = mSurrogate->SetAcceptingCalls(true);
+ }
+}
+
+PushSurrogateAcceptCalls::~PushSurrogateAcceptCalls()
+{
+ if (mSurrogate) {
+ mSurrogate->SetAcceptingCalls(mPrevAcceptCallsState);
+ }
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginAsyncSurrogate.h b/dom/plugins/ipc/PluginAsyncSurrogate.h
new file mode 100644
index 000000000..5b6315715
--- /dev/null
+++ b/dom/plugins/ipc/PluginAsyncSurrogate.h
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 dom_plugins_ipc_PluginAsyncSurrogate_h
+#define dom_plugins_ipc_PluginAsyncSurrogate_h
+
+#include "mozilla/UniquePtr.h"
+#include "npapi.h"
+#include "npfunctions.h"
+#include "npruntime.h"
+#include "nsISupportsImpl.h"
+#include "nsPluginHost.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "PluginDataResolver.h"
+
+namespace mozilla {
+namespace plugins {
+
+struct ParentNPObject;
+class PluginInstanceParent;
+class PluginModuleParent;
+
+class PluginAsyncSurrogate : public PluginDataResolver
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(PluginAsyncSurrogate)
+
+ bool Init(NPMIMEType aPluginType, NPP aInstance, uint16_t aMode,
+ int16_t aArgc, char* aArgn[], char* aArgv[]);
+ nsresult NPP_New(NPError* aError);
+ NPError NPP_Destroy(NPSavedData** aSave);
+ NPError NPP_GetValue(NPPVariable aVariable, void* aRetval);
+ NPError NPP_SetValue(NPNVariable aVariable, void* aValue);
+ NPError NPP_NewStream(NPMIMEType aType, NPStream* aStream, NPBool aSeekable,
+ uint16_t* aStype);
+ NPError NPP_SetWindow(NPWindow* aWindow);
+ nsresult AsyncSetWindow(NPWindow* aWindow);
+ void NPP_Print(NPPrint* aPrintInfo);
+ int16_t NPP_HandleEvent(void* aEvent);
+ int32_t NPP_WriteReady(NPStream* aStream);
+ NPError NPP_DestroyStream(NPStream* aStream, NPReason aReason);
+ void OnInstanceCreated(PluginInstanceParent* aInstance);
+ static bool Create(PluginModuleParent* aParent, NPMIMEType aPluginType,
+ NPP aInstance, uint16_t aMode, int16_t aArgc,
+ char* aArgn[], char* aArgv[]);
+ static const NPClass* GetClass() { return &sNPClass; }
+ static void NP_GetEntryPoints(NPPluginFuncs* aFuncs);
+ static PluginAsyncSurrogate* Cast(NPP aInstance);
+ static void NotifyDestroyPending(NPP aInstance);
+ void NotifyDestroyPending();
+
+ virtual PluginAsyncSurrogate*
+ GetAsyncSurrogate() { return this; }
+
+ virtual PluginInstanceParent*
+ GetInstance() { return nullptr; }
+
+ NPP GetNPP();
+
+ bool GetPropertyHelper(NPObject* aObject, NPIdentifier aName,
+ bool* aHasProperty, bool* aHasMethod,
+ NPVariant* aResult);
+
+ PluginModuleParent* GetParent() { return mParent; }
+
+ bool IsDestroyPending() const { return mDestroyPending; }
+
+ bool SetAcceptingCalls(bool aAccept)
+ {
+ bool prevState = mAcceptCalls;
+ if (mInstantiated) {
+ aAccept = true;
+ }
+ mAcceptCalls = aAccept;
+ return prevState;
+ }
+
+ void AsyncCallDeparting();
+ void AsyncCallArriving();
+
+ void NotifyAsyncInitFailed();
+ void DestroyAsyncStream(NPStream* aStream);
+
+private:
+ explicit PluginAsyncSurrogate(PluginModuleParent* aParent);
+ virtual ~PluginAsyncSurrogate();
+
+ bool WaitForInit();
+
+ static bool SetStreamType(NPStream* aStream, uint16_t aStreamType);
+
+ static NPError NPP_Destroy(NPP aInstance, NPSavedData** aSave);
+ static NPError NPP_GetValue(NPP aInstance, NPPVariable aVariable, void* aRetval);
+ static NPError NPP_SetValue(NPP aInstance, NPNVariable aVariable, void* aValue);
+ static NPError NPP_NewStream(NPP aInstance, NPMIMEType aType, NPStream* aStream,
+ NPBool aSeekable, uint16_t* aStype);
+ static NPError NPP_SetWindow(NPP aInstance, NPWindow* aWindow);
+ static void NPP_Print(NPP aInstance, NPPrint* aPrintInfo);
+ static int16_t NPP_HandleEvent(NPP aInstance, void* aEvent);
+ static int32_t NPP_WriteReady(NPP aInstance, NPStream* aStream);
+ static NPError NPP_DestroyStream(NPP aInstance, NPStream* aStream,
+ NPReason aReason);
+
+ static NPObject* ScriptableAllocate(NPP aInstance, NPClass* aClass);
+ static void ScriptableInvalidate(NPObject* aObject);
+ static void ScriptableDeallocate(NPObject* aObject);
+ static bool ScriptableHasMethod(NPObject* aObject, NPIdentifier aName);
+ static bool ScriptableInvoke(NPObject* aObject, NPIdentifier aName,
+ const NPVariant* aArgs, uint32_t aArgCount,
+ NPVariant* aResult);
+ static bool ScriptableInvokeDefault(NPObject* aObject, const NPVariant* aArgs,
+ uint32_t aArgCount, NPVariant* aResult);
+ static bool ScriptableHasProperty(NPObject* aObject, NPIdentifier aName);
+ static bool ScriptableGetProperty(NPObject* aObject, NPIdentifier aName,
+ NPVariant* aResult);
+ static bool ScriptableSetProperty(NPObject* aObject, NPIdentifier aName,
+ const NPVariant* aValue);
+ static bool ScriptableRemoveProperty(NPObject* aObject, NPIdentifier aName);
+ static bool ScriptableEnumerate(NPObject* aObject, NPIdentifier** aIdentifiers,
+ uint32_t* aCount);
+ static bool ScriptableConstruct(NPObject* aObject, const NPVariant* aArgs,
+ uint32_t aArgCount, NPVariant* aResult);
+ static nsNPAPIPluginStreamListener* GetStreamListener(NPStream* aStream);
+
+private:
+ struct PendingNewStreamCall
+ {
+ PendingNewStreamCall(NPMIMEType aType, NPStream* aStream, NPBool aSeekable);
+ ~PendingNewStreamCall() {}
+ nsCString mType;
+ NPStream* mStream;
+ NPBool mSeekable;
+ };
+
+private:
+ PluginModuleParent* mParent;
+ // These values are used to construct the plugin instance
+ nsCString mMimeType;
+ mozilla::WeakPtr<nsNPAPIPluginInstance> mInstance;
+ uint16_t mMode;
+ InfallibleTArray<nsCString> mNames;
+ InfallibleTArray<nsCString> mValues;
+ // This is safe to store as a pointer because the spec says it will remain
+ // valid until destruction or a subsequent NPP_SetWindow call.
+ NPWindow* mWindow;
+ nsTArray<PendingNewStreamCall> mPendingNewStreamCalls;
+ UniquePtr<PluginDestructionGuard> mPluginDestructionGuard;
+
+ bool mAcceptCalls;
+ bool mInstantiated;
+ bool mAsyncSetWindow;
+ bool mInitCancelled;
+ bool mDestroyPending;
+ int32_t mAsyncCallsInFlight;
+
+ static const NPClass sNPClass;
+};
+
+struct AsyncNPObject : NPObject
+{
+ explicit AsyncNPObject(PluginAsyncSurrogate* aSurrogate);
+ ~AsyncNPObject();
+
+ NPObject* GetRealObject();
+
+ RefPtr<PluginAsyncSurrogate> mSurrogate;
+ ParentNPObject* mRealObject;
+};
+
+class MOZ_STACK_CLASS PushSurrogateAcceptCalls
+{
+public:
+ explicit PushSurrogateAcceptCalls(PluginInstanceParent* aInstance);
+ ~PushSurrogateAcceptCalls();
+
+private:
+ PluginAsyncSurrogate* mSurrogate;
+ bool mPrevAcceptCallsState;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_ipc_PluginAsyncSurrogate_h
diff --git a/dom/plugins/ipc/PluginBackgroundDestroyer.cpp b/dom/plugins/ipc/PluginBackgroundDestroyer.cpp
new file mode 100644
index 000000000..3f822d1a3
--- /dev/null
+++ b/dom/plugins/ipc/PluginBackgroundDestroyer.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 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 "PluginBackgroundDestroyer.h"
+#include "gfxSharedImageSurface.h"
+
+using namespace mozilla;
+using namespace plugins;
+
+PluginBackgroundDestroyerParent::PluginBackgroundDestroyerParent(gfxASurface* aDyingBackground)
+ : mDyingBackground(aDyingBackground)
+{
+}
+
+PluginBackgroundDestroyerParent::~PluginBackgroundDestroyerParent()
+{
+}
+
+void
+PluginBackgroundDestroyerParent::ActorDestroy(ActorDestroyReason why)
+{
+ switch(why) {
+ case Deletion:
+ case AncestorDeletion:
+ if (gfxSharedImageSurface::IsSharedImage(mDyingBackground)) {
+ gfxSharedImageSurface* s =
+ static_cast<gfxSharedImageSurface*>(mDyingBackground.get());
+ DeallocShmem(s->GetShmem());
+ }
+ break;
+ default:
+ // We're shutting down or crashed, let automatic cleanup
+ // take care of our shmem, if we have one.
+ break;
+ }
+}
diff --git a/dom/plugins/ipc/PluginBackgroundDestroyer.h b/dom/plugins/ipc/PluginBackgroundDestroyer.h
new file mode 100644
index 000000000..226fdff8a
--- /dev/null
+++ b/dom/plugins/ipc/PluginBackgroundDestroyer.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 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/. */
+
+#ifndef dom_plugins_PluginBackgroundDestroyer
+#define dom_plugins_PluginBackgroundDestroyer
+
+#include "mozilla/plugins/PPluginBackgroundDestroyerChild.h"
+#include "mozilla/plugins/PPluginBackgroundDestroyerParent.h"
+
+#include "gfxSharedImageSurface.h"
+
+class gfxASurface;
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * When instances of this class are destroyed, the old background goes
+ * along with them, completing the destruction process (whether or not
+ * the plugin stayed alive long enough to ack).
+ */
+class PluginBackgroundDestroyerParent : public PPluginBackgroundDestroyerParent {
+public:
+ explicit PluginBackgroundDestroyerParent(gfxASurface* aDyingBackground);
+
+ virtual ~PluginBackgroundDestroyerParent();
+
+private:
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ RefPtr<gfxASurface> mDyingBackground;
+};
+
+/**
+ * This class exists solely to instruct its instance to release its
+ * current background, a new one may be coming.
+ */
+class PluginBackgroundDestroyerChild : public PPluginBackgroundDestroyerChild {
+public:
+ PluginBackgroundDestroyerChild() { }
+ virtual ~PluginBackgroundDestroyerChild() { }
+
+private:
+ // Implementing this for good hygiene.
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ { }
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_PluginBackgroundDestroyer
diff --git a/dom/plugins/ipc/PluginBridge.h b/dom/plugins/ipc/PluginBridge.h
new file mode 100644
index 000000000..f3cb56edc
--- /dev/null
+++ b/dom/plugins/ipc/PluginBridge.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 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 mozilla_plugins_PluginBridge_h
+#define mozilla_plugins_PluginBridge_h
+
+#include "base/process.h"
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+} // namespace dom
+
+namespace plugins {
+
+bool
+SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent,
+ bool aForceBridgeNow, nsresult* aResult, uint32_t* aRunID);
+
+nsresult
+FindPluginsForContent(uint32_t aPluginEpoch,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch);
+
+void
+TakeFullMinidump(uint32_t aPluginId,
+ base::ProcessId aContentProcessId,
+ const nsAString& aBrowserDumpId,
+ nsString& aDumpId);
+
+void
+TerminatePlugin(uint32_t aPluginId,
+ base::ProcessId aContentProcessId,
+ const nsCString& aMonitorDescription,
+ const nsAString& aDumpId);
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginBridge_h
diff --git a/dom/plugins/ipc/PluginDataResolver.h b/dom/plugins/ipc/PluginDataResolver.h
new file mode 100644
index 000000000..8371c4df7
--- /dev/null
+++ b/dom/plugins/ipc/PluginDataResolver.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 dom_plugins_ipc_PluginDataResolver_h
+#define dom_plugins_ipc_PluginDataResolver_h
+
+namespace mozilla {
+namespace plugins {
+
+class PluginAsyncSurrogate;
+class PluginInstanceParent;
+
+class PluginDataResolver
+{
+public:
+ virtual PluginAsyncSurrogate* GetAsyncSurrogate() = 0;
+ virtual PluginInstanceParent* GetInstance() = 0;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_ipc_PluginDataResolver_h
diff --git a/dom/plugins/ipc/PluginHangUIParent.cpp b/dom/plugins/ipc/PluginHangUIParent.cpp
new file mode 100644
index 000000000..5114f2e9a
--- /dev/null
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -0,0 +1,445 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "PluginHangUI.h"
+
+#include "PluginHangUIParent.h"
+
+#include "mozilla/Telemetry.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/plugins/PluginModuleParent.h"
+
+#include "nsContentUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsIProperties.h"
+#include "nsIWindowMediator.h"
+#include "nsIWinTaskbar.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+
+#include "WidgetUtils.h"
+
+#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
+
+using base::ProcessHandle;
+
+using mozilla::widget::WidgetUtils;
+
+using std::string;
+using std::vector;
+
+namespace {
+class nsPluginHangUITelemetry : public mozilla::Runnable
+{
+public:
+ nsPluginHangUITelemetry(int aResponseCode, int aDontAskCode,
+ uint32_t aResponseTimeMs, uint32_t aTimeoutMs)
+ : mResponseCode(aResponseCode),
+ mDontAskCode(aDontAskCode),
+ mResponseTimeMs(aResponseTimeMs),
+ mTimeoutMs(aTimeoutMs)
+ {
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::PLUGIN_HANG_UI_USER_RESPONSE, mResponseCode);
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::PLUGIN_HANG_UI_DONT_ASK, mDontAskCode);
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::PLUGIN_HANG_UI_RESPONSE_TIME, mResponseTimeMs);
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::PLUGIN_HANG_TIME, mTimeoutMs + mResponseTimeMs);
+ return NS_OK;
+ }
+
+private:
+ int mResponseCode;
+ int mDontAskCode;
+ uint32_t mResponseTimeMs;
+ uint32_t mTimeoutMs;
+};
+} // namespace
+
+namespace mozilla {
+namespace plugins {
+
+PluginHangUIParent::PluginHangUIParent(PluginModuleChromeParent* aModule,
+ const int32_t aHangUITimeoutPref,
+ const int32_t aChildTimeoutPref)
+ : mMutex("mozilla::plugins::PluginHangUIParent::mMutex"),
+ mModule(aModule),
+ mTimeoutPrefMs(static_cast<uint32_t>(aHangUITimeoutPref) * 1000U),
+ mIPCTimeoutMs(static_cast<uint32_t>(aChildTimeoutPref) * 1000U),
+ mMainThreadMessageLoop(MessageLoop::current()),
+ mIsShowing(false),
+ mLastUserResponse(0),
+ mHangUIProcessHandle(nullptr),
+ mMainWindowHandle(nullptr),
+ mRegWait(nullptr),
+ mShowEvent(nullptr),
+ mShowTicks(0),
+ mResponseTicks(0)
+{
+}
+
+PluginHangUIParent::~PluginHangUIParent()
+{
+ { // Scope for lock
+ MutexAutoLock lock(mMutex);
+ UnwatchHangUIChildProcess(true);
+ }
+ if (mShowEvent) {
+ ::CloseHandle(mShowEvent);
+ }
+ if (mHangUIProcessHandle) {
+ ::CloseHandle(mHangUIProcessHandle);
+ }
+}
+
+bool
+PluginHangUIParent::DontShowAgain() const
+{
+ return (mLastUserResponse & HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN);
+}
+
+bool
+PluginHangUIParent::WasLastHangStopped() const
+{
+ return (mLastUserResponse & HANGUI_USER_RESPONSE_STOP);
+}
+
+unsigned int
+PluginHangUIParent::LastShowDurationMs() const
+{
+ // We only return something if there was a user response
+ if (!mLastUserResponse) {
+ return 0;
+ }
+ return static_cast<unsigned int>(mResponseTicks - mShowTicks);
+}
+
+bool
+PluginHangUIParent::Init(const nsString& aPluginName)
+{
+ if (mHangUIProcessHandle) {
+ return false;
+ }
+
+ nsresult rv;
+ rv = mMiniShm.Init(this, ::IsDebuggerPresent() ? INFINITE : mIPCTimeoutMs);
+ NS_ENSURE_SUCCESS(rv, false);
+ nsCOMPtr<nsIProperties>
+ directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
+ if (!directoryService) {
+ return false;
+ }
+ nsCOMPtr<nsIFile> greDir;
+ rv = directoryService->Get(NS_GRE_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(greDir));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ nsAutoString path;
+ greDir->GetPath(path);
+
+ FilePath exePath(path.get());
+ exePath = exePath.AppendASCII(MOZ_HANGUI_PROCESS_NAME);
+ CommandLine commandLine(exePath.value());
+
+ nsXPIDLString localizedStr;
+ const char16_t* formatParams[] = { aPluginName.get() };
+ rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+ "PluginHangUIMessage",
+ formatParams,
+ localizedStr);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ commandLine.AppendLooseValue(localizedStr.get());
+
+ const char* keys[] = { "PluginHangUITitle",
+ "PluginHangUIWaitButton",
+ "PluginHangUIStopButton",
+ "DontAskAgain" };
+ for (unsigned int i = 0; i < ArrayLength(keys); ++i) {
+ rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+ keys[i],
+ localizedStr);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ commandLine.AppendLooseValue(localizedStr.get());
+ }
+
+ rv = GetHangUIOwnerWindowHandle(mMainWindowHandle);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ nsAutoString hwndStr;
+ hwndStr.AppendPrintf("%p", mMainWindowHandle);
+ commandLine.AppendLooseValue(hwndStr.get());
+
+ ScopedHandle procHandle(::OpenProcess(SYNCHRONIZE,
+ TRUE,
+ GetCurrentProcessId()));
+ if (!procHandle.IsValid()) {
+ return false;
+ }
+ nsAutoString procHandleStr;
+ procHandleStr.AppendPrintf("%p", procHandle.Get());
+ commandLine.AppendLooseValue(procHandleStr.get());
+
+ // On Win7+, pass the application user model to the child, so it can
+ // register with it. This insures windows created by the Hang UI
+ // properly group with the parent app on the Win7 taskbar.
+ nsCOMPtr<nsIWinTaskbar> taskbarInfo = do_GetService(NS_TASKBAR_CONTRACTID);
+ if (taskbarInfo) {
+ bool isSupported = false;
+ taskbarInfo->GetAvailable(&isSupported);
+ nsAutoString appId;
+ if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) {
+ commandLine.AppendLooseValue(appId.get());
+ } else {
+ commandLine.AppendLooseValue(L"-");
+ }
+ } else {
+ commandLine.AppendLooseValue(L"-");
+ }
+
+ nsAutoString ipcTimeoutStr;
+ ipcTimeoutStr.AppendInt(mIPCTimeoutMs);
+ commandLine.AppendLooseValue(ipcTimeoutStr.get());
+
+ std::wstring ipcCookie;
+ rv = mMiniShm.GetCookie(ipcCookie);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ commandLine.AppendLooseValue(ipcCookie);
+
+ ScopedHandle showEvent(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
+ if (!showEvent.IsValid()) {
+ return false;
+ }
+ mShowEvent = showEvent.Get();
+
+ MutexAutoLock lock(mMutex);
+ STARTUPINFO startupInfo = { sizeof(STARTUPINFO) };
+ PROCESS_INFORMATION processInfo = { nullptr };
+ BOOL isProcessCreated = ::CreateProcess(exePath.value().c_str(),
+ const_cast<wchar_t*>(commandLine.command_line_string().c_str()),
+ nullptr,
+ nullptr,
+ TRUE,
+ DETACHED_PROCESS,
+ nullptr,
+ nullptr,
+ &startupInfo,
+ &processInfo);
+ if (isProcessCreated) {
+ ::CloseHandle(processInfo.hThread);
+ mHangUIProcessHandle = processInfo.hProcess;
+ ::RegisterWaitForSingleObject(&mRegWait,
+ processInfo.hProcess,
+ &SOnHangUIProcessExit,
+ this,
+ INFINITE,
+ WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE);
+ ::WaitForSingleObject(mShowEvent, ::IsDebuggerPresent() ? INFINITE
+ : mIPCTimeoutMs);
+ // Setting this to true even if we time out on mShowEvent. This timeout
+ // typically occurs when the machine is thrashing so badly that
+ // plugin-hang-ui.exe is taking a while to start. If we didn't set
+ // this to true, Firefox would keep spawning additional plugin-hang-ui
+ // processes, which is not what we want.
+ mIsShowing = true;
+ }
+ mShowEvent = nullptr;
+ return !(!isProcessCreated);
+}
+
+// static
+VOID CALLBACK PluginHangUIParent::SOnHangUIProcessExit(PVOID aContext,
+ BOOLEAN aIsTimer)
+{
+ PluginHangUIParent* object = static_cast<PluginHangUIParent*>(aContext);
+ MutexAutoLock lock(object->mMutex);
+ // If the Hang UI child process died unexpectedly, act as if the UI cancelled
+ if (object->IsShowing()) {
+ object->RecvUserResponse(HANGUI_USER_RESPONSE_CANCEL);
+ // Firefox window was disabled automatically when the Hang UI was shown.
+ // If plugin-hang-ui.exe was unexpectedly terminated, we need to re-enable.
+ ::EnableWindow(object->mMainWindowHandle, TRUE);
+ }
+}
+
+// A precondition for this function is that the caller has locked mMutex
+bool
+PluginHangUIParent::UnwatchHangUIChildProcess(bool aWait)
+{
+ mMutex.AssertCurrentThreadOwns();
+ if (mRegWait) {
+ // If aWait is false then we want to pass a nullptr (i.e. default
+ // constructor) completionEvent
+ ScopedHandle completionEvent;
+ if (aWait) {
+ completionEvent.Set(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
+ if (!completionEvent.IsValid()) {
+ return false;
+ }
+ }
+
+ // if aWait == false and UnregisterWaitEx fails with ERROR_IO_PENDING,
+ // it is okay to clear mRegWait; Windows is telling us that the wait's
+ // callback is running but will be cleaned up once the callback returns.
+ if (::UnregisterWaitEx(mRegWait, completionEvent) ||
+ (!aWait && ::GetLastError() == ERROR_IO_PENDING)) {
+ mRegWait = nullptr;
+ if (aWait) {
+ // We must temporarily unlock mMutex while waiting for the registered
+ // wait callback to complete, or else we could deadlock.
+ MutexAutoUnlock unlock(mMutex);
+ ::WaitForSingleObject(completionEvent, INFINITE);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+PluginHangUIParent::Cancel()
+{
+ MutexAutoLock lock(mMutex);
+ bool result = mIsShowing && SendCancel();
+ if (result) {
+ mIsShowing = false;
+ }
+ return result;
+}
+
+bool
+PluginHangUIParent::SendCancel()
+{
+ PluginHangUICommand* cmd = nullptr;
+ nsresult rv = mMiniShm.GetWritePtr(cmd);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ cmd->mCode = PluginHangUICommand::HANGUI_CMD_CANCEL;
+ return NS_SUCCEEDED(mMiniShm.Send());
+}
+
+// A precondition for this function is that the caller has locked mMutex
+bool
+PluginHangUIParent::RecvUserResponse(const unsigned int& aResponse)
+{
+ mMutex.AssertCurrentThreadOwns();
+ if (!mIsShowing && !(aResponse & HANGUI_USER_RESPONSE_CANCEL)) {
+ // Don't process a user response if a cancellation is already pending
+ return true;
+ }
+ mLastUserResponse = aResponse;
+ mResponseTicks = ::GetTickCount();
+ mIsShowing = false;
+ // responseCode: 1 = Stop, 2 = Continue, 3 = Cancel
+ int responseCode;
+ if (aResponse & HANGUI_USER_RESPONSE_STOP) {
+ // User clicked Stop
+ mModule->TerminateChildProcess(mMainThreadMessageLoop,
+ mozilla::ipc::kInvalidProcessId,
+ NS_LITERAL_CSTRING("ModalHangUI"),
+ EmptyString());
+ responseCode = 1;
+ } else if(aResponse & HANGUI_USER_RESPONSE_CONTINUE) {
+ mModule->OnHangUIContinue();
+ // User clicked Continue
+ responseCode = 2;
+ } else {
+ // Dialog was cancelled
+ responseCode = 3;
+ }
+ int dontAskCode = (aResponse & HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN) ? 1 : 0;
+ nsCOMPtr<nsIRunnable> workItem = new nsPluginHangUITelemetry(responseCode,
+ dontAskCode,
+ LastShowDurationMs(),
+ mTimeoutPrefMs);
+ NS_DispatchToMainThread(workItem);
+ return true;
+}
+
+nsresult
+PluginHangUIParent::GetHangUIOwnerWindowHandle(NativeWindowHandle& windowHandle)
+{
+ windowHandle = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIWindowMediator> winMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<mozIDOMWindowProxy> navWin;
+ rv = winMediator->GetMostRecentWindow(u"navigator:browser",
+ getter_AddRefs(navWin));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!navWin) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
+ nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(win);
+ if (!widget) {
+ return NS_ERROR_FAILURE;
+ }
+
+ windowHandle = reinterpret_cast<NativeWindowHandle>(widget->GetNativeData(NS_NATIVE_WINDOW));
+ if (!windowHandle) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+void
+PluginHangUIParent::OnMiniShmEvent(MiniShmBase *aMiniShmObj)
+{
+ const PluginHangUIResponse* response = nullptr;
+ nsresult rv = aMiniShmObj->GetReadPtr(response);
+ NS_ASSERTION(NS_SUCCEEDED(rv),
+ "Couldn't obtain read pointer OnMiniShmEvent");
+ if (NS_SUCCEEDED(rv)) {
+ // The child process has returned a response so we shouldn't worry about
+ // its state anymore.
+ MutexAutoLock lock(mMutex);
+ UnwatchHangUIChildProcess(false);
+ RecvUserResponse(response->mResponseBits);
+ }
+}
+
+void
+PluginHangUIParent::OnMiniShmConnect(MiniShmBase* aMiniShmObj)
+{
+ PluginHangUICommand* cmd = nullptr;
+ nsresult rv = aMiniShmObj->GetWritePtr(cmd);
+ NS_ASSERTION(NS_SUCCEEDED(rv),
+ "Couldn't obtain write pointer OnMiniShmConnect");
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ cmd->mCode = PluginHangUICommand::HANGUI_CMD_SHOW;
+ if (NS_SUCCEEDED(aMiniShmObj->Send())) {
+ mShowTicks = ::GetTickCount();
+ }
+ ::SetEvent(mShowEvent);
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginHangUIParent.h b/dom/plugins/ipc/PluginHangUIParent.h
new file mode 100644
index 000000000..8a6b2e6cb
--- /dev/null
+++ b/dom/plugins/ipc/PluginHangUIParent.h
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 mozilla_plugins_PluginHangUIParent_h
+#define mozilla_plugins_PluginHangUIParent_h
+
+#include "nsString.h"
+
+#include "base/process.h"
+#include "base/process_util.h"
+
+#include "mozilla/Mutex.h"
+#include "mozilla/plugins/PluginMessageUtils.h"
+
+#include "MiniShmParent.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginModuleChromeParent;
+
+/**
+ * This class is responsible for launching and communicating with the
+ * plugin-hang-ui process.
+ *
+ * NOTE: PluginHangUIParent is *not* an IPDL actor! In this case, "Parent"
+ * is describing the fact that firefox is the parent process to the
+ * plugin-hang-ui process, which is the PluginHangUIChild.
+ * PluginHangUIParent and PluginHangUIChild are a matched pair.
+ * @see PluginHangUIChild
+ */
+class PluginHangUIParent : public MiniShmObserver
+{
+public:
+ PluginHangUIParent(PluginModuleChromeParent* aModule,
+ const int32_t aHangUITimeoutPref,
+ const int32_t aChildTimeoutPref);
+ virtual ~PluginHangUIParent();
+
+ /**
+ * Spawn the plugin-hang-ui.exe child process and terminate the given
+ * plugin container process if the user elects to stop the hung plugin.
+ *
+ * @param aPluginName Human-readable name of the affected plugin.
+ * @return true if the plugin hang ui process was successfully launched,
+ * otherwise false.
+ */
+ bool
+ Init(const nsString& aPluginName);
+
+ /**
+ * If the Plugin Hang UI is being shown, send a cancel notification to the
+ * Plugin Hang UI child process.
+ *
+ * @return true if the UI was shown and the cancel command was successfully
+ * sent to the child process, otherwise false.
+ */
+ bool
+ Cancel();
+
+ /**
+ * Returns whether the Plugin Hang UI is currently being displayed.
+ *
+ * @return true if the Plugin Hang UI is showing, otherwise false.
+ */
+ bool
+ IsShowing() const { return mIsShowing; }
+
+ /**
+ * Returns whether this Plugin Hang UI instance has been shown. Note
+ * that this does not necessarily mean that the UI is showing right now.
+ *
+ * @return true if the Plugin Hang UI has shown, otherwise false.
+ */
+ bool
+ WasShown() const { return mIsShowing || mLastUserResponse != 0; }
+
+ /**
+ * Returns whether the user checked the "Don't ask me again" checkbox.
+ *
+ * @return true if the user does not want to see the Hang UI again.
+ */
+ bool
+ DontShowAgain() const;
+
+ /**
+ * Returns whether the user clicked stop during the last time that the
+ * Plugin Hang UI was displayed, if applicable.
+ *
+ * @return true if the UI was shown and the user chose to stop the
+ * plugin, otherwise false
+ */
+ bool
+ WasLastHangStopped() const;
+
+ /**
+ * @return unsigned int containing the response bits from the last
+ * time the Plugin Hang UI ran.
+ */
+ unsigned int
+ LastUserResponse() const { return mLastUserResponse; }
+
+ /**
+ * @return unsigned int containing the number of milliseconds that
+ * the Plugin Hang UI was displayed before the user responded.
+ * Returns 0 if the Plugin Hang UI has not been shown or was cancelled.
+ */
+ unsigned int
+ LastShowDurationMs() const;
+
+ virtual void
+ OnMiniShmEvent(MiniShmBase* aMiniShmObj) override;
+
+ virtual void
+ OnMiniShmConnect(MiniShmBase* aMiniShmObj) override;
+
+private:
+ nsresult
+ GetHangUIOwnerWindowHandle(NativeWindowHandle& windowHandle);
+
+ bool
+ SendCancel();
+
+ bool
+ RecvUserResponse(const unsigned int& aResponse);
+
+ bool
+ UnwatchHangUIChildProcess(bool aWait);
+
+ static
+ VOID CALLBACK SOnHangUIProcessExit(PVOID aContext, BOOLEAN aIsTimer);
+
+private:
+ Mutex mMutex;
+ PluginModuleChromeParent* mModule;
+ const uint32_t mTimeoutPrefMs;
+ const uint32_t mIPCTimeoutMs;
+ MessageLoop* mMainThreadMessageLoop;
+ bool mIsShowing;
+ unsigned int mLastUserResponse;
+ base::ProcessHandle mHangUIProcessHandle;
+ NativeWindowHandle mMainWindowHandle;
+ HANDLE mRegWait;
+ HANDLE mShowEvent;
+ DWORD mShowTicks;
+ DWORD mResponseTicks;
+ MiniShmParent mMiniShm;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginHangUIParent);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginHangUIParent_h
+
diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp
new file mode 100644
index 000000000..af9db9103
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -0,0 +1,4684 @@
+/* -*- 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/. */
+
+#include "PluginBackgroundDestroyer.h"
+#include "PluginInstanceChild.h"
+#include "PluginModuleChild.h"
+#include "BrowserStreamChild.h"
+#include "PluginStreamChild.h"
+#include "StreamNotifyChild.h"
+#include "PluginProcessChild.h"
+#include "gfxASurface.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#include "nsNPAPIPluginInstance.h"
+#include "mozilla/gfx/2D.h"
+#ifdef MOZ_X11
+#include "gfxXlibSurface.h"
+#endif
+#ifdef XP_WIN
+#include "mozilla/D3DMessageUtils.h"
+#include "mozilla/gfx/SharedDIBSurface.h"
+#include "nsCrashOnException.h"
+#include "gfxWindowsPlatform.h"
+extern const wchar_t* kFlashFullscreenClass;
+using mozilla::gfx::SharedDIBSurface;
+#endif
+#include "gfxSharedImageSurface.h"
+#include "gfxUtils.h"
+#include "gfxAlphaRecovery.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "ImageContainer.h"
+
+using namespace mozilla;
+using mozilla::ipc::ProcessChild;
+using namespace mozilla::plugins;
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+using namespace mozilla::widget;
+using namespace std;
+
+#ifdef MOZ_WIDGET_GTK
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+#include "gtk2xtbin.h"
+
+#elif defined(OS_WIN)
+
+#include <windows.h>
+#include <windowsx.h>
+
+#include "mozilla/widget/WinMessages.h"
+#include "mozilla/widget/WinModifierKeyState.h"
+#include "mozilla/widget/WinNativeEventData.h"
+#include "nsWindowsDllInterceptor.h"
+#include "X11UndefineNone.h"
+
+typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu,
+ UINT uFlags,
+ int x,
+ int y,
+ int nReserved,
+ HWND hWnd,
+ CONST RECT *prcRect);
+static WindowsDllInterceptor sUser32Intercept;
+static HWND sWinlessPopupSurrogateHWND = nullptr;
+static User32TrackPopupMenu sUser32TrackPopupMenuStub = nullptr;
+
+typedef HIMC (WINAPI *Imm32ImmGetContext)(HWND hWND);
+typedef BOOL (WINAPI *Imm32ImmReleaseContext)(HWND hWND, HIMC hIMC);
+typedef LONG (WINAPI *Imm32ImmGetCompositionString)(HIMC hIMC,
+ DWORD dwIndex,
+ LPVOID lpBuf,
+ DWORD dwBufLen);
+typedef BOOL (WINAPI *Imm32ImmSetCandidateWindow)(HIMC hIMC,
+ LPCANDIDATEFORM lpCandidate);
+typedef BOOL (WINAPI *Imm32ImmNotifyIME)(HIMC hIMC, DWORD dwAction,
+ DWORD dwIndex, DWORD dwValue);
+static WindowsDllInterceptor sImm32Intercept;
+static Imm32ImmGetContext sImm32ImmGetContextStub = nullptr;
+static Imm32ImmReleaseContext sImm32ImmReleaseContextStub = nullptr;
+static Imm32ImmGetCompositionString sImm32ImmGetCompositionStringStub = nullptr;
+static Imm32ImmSetCandidateWindow sImm32ImmSetCandidateWindowStub = nullptr;
+static Imm32ImmNotifyIME sImm32ImmNotifyIME = nullptr;
+static PluginInstanceChild* sCurrentPluginInstance = nullptr;
+static const HIMC sHookIMC = (const HIMC)0xefefefef;
+
+using mozilla::gfx::SharedDIB;
+
+// Flash WM_USER message delay time for PostDelayedTask. Borrowed
+// from Chromium's web plugin delegate src. See 'flash msg throttling
+// helpers' section for details.
+const int kFlashWMUSERMessageThrottleDelayMs = 5;
+
+static const TCHAR kPluginIgnoreSubclassProperty[] = TEXT("PluginIgnoreSubclassProperty");
+
+#elif defined(XP_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#include "PluginUtilsOSX.h"
+#endif // defined(XP_MACOSX)
+
+/**
+ * We can't use gfxPlatform::CreateDrawTargetForSurface() because calling
+ * gfxPlatform::GetPlatform() instantiates the prefs service, and that's not
+ * allowed from processes other than the main process. So we have our own
+ * version here.
+ */
+static RefPtr<DrawTarget>
+CreateDrawTargetForSurface(gfxASurface *aSurface)
+{
+ SurfaceFormat format = aSurface->GetSurfaceFormat();
+ RefPtr<DrawTarget> drawTarget =
+ Factory::CreateDrawTargetForCairoSurface(aSurface->CairoSurface(),
+ aSurface->GetSize(),
+ &format);
+ if (!drawTarget) {
+ NS_RUNTIMEABORT("CreateDrawTargetForSurface failed in plugin");
+ }
+ return drawTarget;
+}
+
+bool PluginInstanceChild::sIsIMEComposing = false;
+
+PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
+ const nsCString& aMimeType,
+ const uint16_t& aMode,
+ const InfallibleTArray<nsCString>& aNames,
+ const InfallibleTArray<nsCString>& aValues)
+ : mPluginIface(aPluginIface)
+ , mMimeType(aMimeType)
+ , mMode(aMode)
+ , mNames(aNames)
+ , mValues(aValues)
+#if defined(XP_DARWIN) || defined (XP_WIN)
+ , mContentsScaleFactor(1.0)
+#endif
+ , mPostingKeyEvents(0)
+ , mPostingKeyEventsOutdated(0)
+ , mDrawingModel(kDefaultDrawingModel)
+ , mCurrentDirectSurface(nullptr)
+ , mAsyncInvalidateMutex("PluginInstanceChild::mAsyncInvalidateMutex")
+ , mAsyncInvalidateTask(0)
+ , mCachedWindowActor(nullptr)
+ , mCachedElementActor(nullptr)
+#ifdef MOZ_WIDGET_GTK
+ , mXEmbed(false)
+#endif // MOZ_WIDGET_GTK
+#if defined(OS_WIN)
+ , mPluginWindowHWND(0)
+ , mPluginWndProc(0)
+ , mPluginParentHWND(0)
+ , mCachedWinlessPluginHWND(0)
+ , mWinlessPopupSurrogateHWND(0)
+ , mWinlessThrottleOldWndProc(0)
+ , mWinlessHiddenMsgHWND(0)
+ , mUnityGetMessageHook(NULL)
+ , mUnitySendMessageHook(NULL)
+#endif // OS_WIN
+ , mAsyncCallMutex("PluginInstanceChild::mAsyncCallMutex")
+#if defined(MOZ_WIDGET_COCOA)
+#if defined(__i386__)
+ , mEventModel(NPEventModelCarbon)
+#endif
+ , mShColorSpace(nullptr)
+ , mShContext(nullptr)
+ , mCGLayer(nullptr)
+ , mCurrentEvent(nullptr)
+#endif
+ , mLayersRendering(false)
+#ifdef XP_WIN
+ , mCurrentSurfaceActor(nullptr)
+ , mBackSurfaceActor(nullptr)
+#endif
+ , mAccumulatedInvalidRect(0,0,0,0)
+ , mIsTransparent(false)
+ , mSurfaceType(gfxSurfaceType::Max)
+ , mPendingPluginCall(false)
+ , mDoAlphaExtraction(false)
+ , mHasPainted(false)
+ , mSurfaceDifferenceRect(0,0,0,0)
+ , mDestroyed(false)
+#ifdef XP_WIN
+ , mLastKeyEventConsumed(false)
+#endif // #ifdef XP_WIN
+ , mStackDepth(0)
+{
+ memset(&mWindow, 0, sizeof(mWindow));
+ mWindow.type = NPWindowTypeWindow;
+ mData.ndata = (void*) this;
+ mData.pdata = nullptr;
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ mWindow.ws_info = &mWsInfo;
+ memset(&mWsInfo, 0, sizeof(mWsInfo));
+#ifdef MOZ_WIDGET_GTK
+ mWsInfo.display = nullptr;
+ mXtClient.top_widget = nullptr;
+#else
+ mWsInfo.display = DefaultXDisplay();
+#endif
+#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
+#if defined(OS_WIN)
+ InitPopupMenuHook();
+ if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
+ SetUnityHooks();
+ }
+ InitImm32Hook();
+#endif // OS_WIN
+}
+
+PluginInstanceChild::~PluginInstanceChild()
+{
+#if defined(OS_WIN)
+ NS_ASSERTION(!mPluginWindowHWND, "Destroying PluginInstanceChild without NPP_Destroy?");
+ if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
+ ClearUnityHooks();
+ }
+ // In the event that we registered for audio device changes, stop.
+ PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+ if (chromeInstance) {
+ NPError rv = chromeInstance->PluginRequiresAudioDeviceChanges(this, false);
+ }
+#endif
+#if defined(MOZ_WIDGET_COCOA)
+ if (mShColorSpace) {
+ ::CGColorSpaceRelease(mShColorSpace);
+ }
+ if (mShContext) {
+ ::CGContextRelease(mShContext);
+ }
+ if (mCGLayer) {
+ PluginUtilsOSX::ReleaseCGLayer(mCGLayer);
+ }
+ if (mDrawingModel == NPDrawingModelCoreAnimation) {
+ UnscheduleTimer(mCARefreshTimer);
+ }
+#endif
+}
+
+NPError
+PluginInstanceChild::DoNPP_New()
+{
+ // unpack the arguments into a C format
+ int argc = mNames.Length();
+ NS_ASSERTION(argc == (int) mValues.Length(),
+ "argn.length != argv.length");
+
+ UniquePtr<char*[]> argn(new char*[1 + argc]);
+ UniquePtr<char*[]> argv(new char*[1 + argc]);
+ argn[argc] = 0;
+ argv[argc] = 0;
+
+ for (int i = 0; i < argc; ++i) {
+ argn[i] = const_cast<char*>(NullableStringGet(mNames[i]));
+ argv[i] = const_cast<char*>(NullableStringGet(mValues[i]));
+ }
+
+ NPP npp = GetNPP();
+
+ NPError rv = mPluginIface->newp((char*)NullableStringGet(mMimeType), npp,
+ mMode, argc, argn.get(), argv.get(), 0);
+ if (NPERR_NO_ERROR != rv) {
+ return rv;
+ }
+
+ Initialize();
+
+#if defined(XP_MACOSX) && defined(__i386__)
+ // If an i386 Mac OS X plugin has selected the Carbon event model then
+ // we have to fail. We do not support putting Carbon event model plugins
+ // out of process. Note that Carbon is the default model so out of process
+ // plugins need to actively negotiate something else in order to work
+ // out of process.
+ if (EventModel() == NPEventModelCarbon) {
+ // Send notification that a plugin tried to negotiate Carbon NPAPI so that
+ // users can be notified that restarting the browser in i386 mode may allow
+ // them to use the plugin.
+ SendNegotiatedCarbon();
+
+ // Fail to instantiate.
+ rv = NPERR_MODULE_LOAD_FAILED_ERROR;
+ }
+#endif
+
+ return rv;
+}
+
+int
+PluginInstanceChild::GetQuirks()
+{
+ return PluginModuleChild::GetChrome()->GetQuirks();
+}
+
+NPError
+PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
+ NPObject** aObject)
+{
+ PluginScriptableObjectChild* actor = nullptr;
+ NPError result = NPERR_NO_ERROR;
+
+ switch (aValue) {
+ case NPNVWindowNPObject:
+ if (!(actor = mCachedWindowActor)) {
+ PPluginScriptableObjectChild* actorProtocol;
+ CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result);
+ if (result == NPERR_NO_ERROR) {
+ actor = mCachedWindowActor =
+ static_cast<PluginScriptableObjectChild*>(actorProtocol);
+ NS_ASSERTION(actor, "Null actor!");
+ PluginModuleChild::sBrowserFuncs.retainobject(
+ actor->GetObject(false));
+ }
+ }
+ break;
+
+ case NPNVPluginElementNPObject:
+ if (!(actor = mCachedElementActor)) {
+ PPluginScriptableObjectChild* actorProtocol;
+ CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol,
+ &result);
+ if (result == NPERR_NO_ERROR) {
+ actor = mCachedElementActor =
+ static_cast<PluginScriptableObjectChild*>(actorProtocol);
+ NS_ASSERTION(actor, "Null actor!");
+ PluginModuleChild::sBrowserFuncs.retainobject(
+ actor->GetObject(false));
+ }
+ }
+ break;
+
+ default:
+ NS_NOTREACHED("Don't know what to do with this value type!");
+ }
+
+#ifdef DEBUG
+ {
+ NPError currentResult;
+ PPluginScriptableObjectChild* currentActor = nullptr;
+
+ switch (aValue) {
+ case NPNVWindowNPObject:
+ CallNPN_GetValue_NPNVWindowNPObject(&currentActor,
+ &currentResult);
+ break;
+ case NPNVPluginElementNPObject:
+ CallNPN_GetValue_NPNVPluginElementNPObject(&currentActor,
+ &currentResult);
+ break;
+ default:
+ MOZ_ASSERT(false);
+ }
+
+ // Make sure that the current actor returned by the parent matches our
+ // cached actor!
+ NS_ASSERTION(!currentActor ||
+ static_cast<PluginScriptableObjectChild*>(currentActor) ==
+ actor, "Cached actor is out of date!");
+ }
+#endif
+
+ if (result != NPERR_NO_ERROR) {
+ return result;
+ }
+
+ NPObject* object = actor->GetObject(false);
+ NS_ASSERTION(object, "Null object?!");
+
+ *aObject = PluginModuleChild::sBrowserFuncs.retainobject(object);
+ return NPERR_NO_ERROR;
+
+}
+
+NPError
+PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
+ void* aValue)
+{
+ PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar));
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ switch(aVar) {
+
+#if defined(MOZ_X11)
+ case NPNVToolkit:
+ *((NPNToolkitType*)aValue) = NPNVGtk2;
+ return NPERR_NO_ERROR;
+
+ case NPNVxDisplay:
+ if (!mWsInfo.display) {
+ // We are called before Initialize() so we have to call it now.
+ Initialize();
+ NS_ASSERTION(mWsInfo.display, "We should have a valid display!");
+ }
+ *(void **)aValue = mWsInfo.display;
+ return NPERR_NO_ERROR;
+
+#elif defined(OS_WIN)
+ case NPNVToolkit:
+ return NPERR_GENERIC_ERROR;
+#endif
+ case NPNVprivateModeBool: {
+ bool v = false;
+ NPError result;
+ if (!CallNPN_GetValue_NPNVprivateModeBool(&v, &result)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ *static_cast<NPBool*>(aValue) = v;
+ return result;
+ }
+
+ case NPNVdocumentOrigin: {
+ nsCString v;
+ NPError result;
+ if (!CallNPN_GetValue_NPNVdocumentOrigin(&v, &result)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ if (result == NPERR_NO_ERROR ||
+ (GetQuirks() &
+ QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN)) {
+ *static_cast<char**>(aValue) = ToNewCString(v);
+ }
+ return result;
+ }
+
+ case NPNVWindowNPObject: // Intentional fall-through
+ case NPNVPluginElementNPObject: {
+ NPObject* object;
+ NPError result = InternalGetNPObjectForValue(aVar, &object);
+ if (result == NPERR_NO_ERROR) {
+ *((NPObject**)aValue) = object;
+ }
+ return result;
+ }
+
+ case NPNVnetscapeWindow: {
+#ifdef XP_WIN
+ if (mWindow.type == NPWindowTypeDrawable) {
+ if (mCachedWinlessPluginHWND) {
+ *static_cast<HWND*>(aValue) = mCachedWinlessPluginHWND;
+ return NPERR_NO_ERROR;
+ }
+ NPError result;
+ if (!CallNPN_GetValue_NPNVnetscapeWindow(&mCachedWinlessPluginHWND, &result)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ *static_cast<HWND*>(aValue) = mCachedWinlessPluginHWND;
+ return result;
+ }
+ else {
+ *static_cast<HWND*>(aValue) = mPluginWindowHWND;
+ return NPERR_NO_ERROR;
+ }
+#elif defined(MOZ_X11)
+ NPError result;
+ CallNPN_GetValue_NPNVnetscapeWindow(static_cast<XID*>(aValue), &result);
+ return result;
+#else
+ return NPERR_GENERIC_ERROR;
+#endif
+ }
+
+ case NPNVsupportsAsyncBitmapSurfaceBool: {
+ bool value = false;
+ CallNPN_GetValue_SupportsAsyncBitmapSurface(&value);
+ *((NPBool*)aValue) = value;
+ return NPERR_NO_ERROR;
+ }
+
+#ifdef XP_WIN
+ case NPNVsupportsAsyncWindowsDXGISurfaceBool: {
+ bool value = false;
+ CallNPN_GetValue_SupportsAsyncDXGISurface(&value);
+ *((NPBool*)aValue) = value;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+#ifdef XP_WIN
+ case NPNVpreferredDXGIAdapter: {
+ DxgiAdapterDesc desc;
+ if (!CallNPN_GetValue_PreferredDXGIAdapter(&desc)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ *reinterpret_cast<DXGI_ADAPTER_DESC*>(aValue) = desc.ToDesc();
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+#ifdef XP_MACOSX
+ case NPNVsupportsCoreGraphicsBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCoreAnimationBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsInvalidatingCoreAnimationBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCompositingCoreAnimationPluginsBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCocoaBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+#ifndef NP_NO_CARBON
+ case NPNVsupportsCarbonBool: {
+ *((NPBool*)aValue) = false;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ case NPNVsupportsUpdatedCocoaTextInputBool: {
+ *static_cast<NPBool*>(aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+#ifndef NP_NO_QUICKDRAW
+ case NPNVsupportsQuickDrawBool: {
+ *((NPBool*)aValue) = false;
+ return NPERR_NO_ERROR;
+ }
+#endif /* NP_NO_QUICKDRAW */
+#endif /* XP_MACOSX */
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ case NPNVcontentsScaleFactor: {
+ *static_cast<double*>(aValue) = mContentsScaleFactor;
+ return NPERR_NO_ERROR;
+ }
+#endif /* defined(XP_MACOSX) || defined(XP_WIN) */
+
+ case NPNVCSSZoomFactor: {
+ *static_cast<double*>(aValue) = mCSSZoomFactor;
+ return NPERR_NO_ERROR;
+ }
+#ifdef DEBUG
+ case NPNVjavascriptEnabledBool:
+ case NPNVasdEnabledBool:
+ case NPNVisOfflineBool:
+ case NPNVSupportsXEmbedBool:
+ case NPNVSupportsWindowless:
+ NS_NOTREACHED("NPNVariable should be handled in PluginModuleChild.");
+ MOZ_FALLTHROUGH;
+#endif
+
+ default:
+ MOZ_LOG(GetPluginLog(), LogLevel::Warning,
+ ("In PluginInstanceChild::NPN_GetValue: Unhandled NPNVariable %i (%s)",
+ (int) aVar, NPNVariableToString(aVar)));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+#ifdef MOZ_WIDGET_COCOA
+#define DEFAULT_REFRESH_MS 20 // CoreAnimation: 50 FPS
+
+void
+CAUpdate(NPP npp, uint32_t timerID) {
+ static_cast<PluginInstanceChild*>(npp->ndata)->Invalidate();
+}
+
+void
+PluginInstanceChild::Invalidate()
+{
+ NPRect windowRect = {0, 0, uint16_t(mWindow.height),
+ uint16_t(mWindow.width)};
+
+ InvalidateRect(&windowRect);
+}
+#endif
+
+NPError
+PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
+{
+ MOZ_LOG(GetPluginLog(), LogLevel::Debug, ("%s (aVar=%i, aValue=%p)",
+ FULLFUNCTION, (int) aVar, aValue));
+
+ AssertPluginThread();
+
+ AutoStackHelper guard(this);
+
+ switch (aVar) {
+ case NPPVpluginWindowBool: {
+ NPError rv;
+ bool windowed = (NPBool) (intptr_t) aValue;
+
+ if (!CallNPN_SetValue_NPPVpluginWindow(windowed, &rv))
+ return NPERR_GENERIC_ERROR;
+
+ NPWindowType newWindowType = windowed ? NPWindowTypeWindow : NPWindowTypeDrawable;
+#ifdef MOZ_WIDGET_GTK
+ if (mWindow.type != newWindowType && mWsInfo.display) {
+ // plugin type has been changed but we already have a valid display
+ // so update it for the recent plugin mode
+ if (mXEmbed || !windowed) {
+ // Use default GTK display for XEmbed and windowless plugins
+ mWsInfo.display = DefaultXDisplay();
+ }
+ else {
+ mWsInfo.display = xt_client_get_display();
+ }
+ }
+#endif
+ mWindow.type = newWindowType;
+ return rv;
+ }
+
+ case NPPVpluginTransparentBool: {
+ NPError rv;
+ mIsTransparent = (!!aValue);
+
+ if (!CallNPN_SetValue_NPPVpluginTransparent(mIsTransparent, &rv))
+ return NPERR_GENERIC_ERROR;
+
+ return rv;
+ }
+
+ case NPPVpluginUsesDOMForCursorBool: {
+ NPError rv = NPERR_GENERIC_ERROR;
+ if (!CallNPN_SetValue_NPPVpluginUsesDOMForCursor((NPBool)(intptr_t)aValue, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return rv;
+ }
+
+ case NPPVpluginDrawingModel: {
+ NPError rv;
+ int drawingModel = (int16_t) (intptr_t) aValue;
+
+ if (!CallNPN_SetValue_NPPVpluginDrawingModel(drawingModel, &rv))
+ return NPERR_GENERIC_ERROR;
+
+ mDrawingModel = drawingModel;
+
+#ifdef XP_MACOSX
+ if (drawingModel == NPDrawingModelCoreAnimation) {
+ mCARefreshTimer = ScheduleTimer(DEFAULT_REFRESH_MS, true, CAUpdate);
+ }
+#endif
+
+ PLUGIN_LOG_DEBUG((" Plugin requested drawing model id #%i\n",
+ mDrawingModel));
+
+ return rv;
+ }
+
+#ifdef XP_MACOSX
+ case NPPVpluginEventModel: {
+ NPError rv;
+ int eventModel = (int16_t) (intptr_t) aValue;
+
+ if (!CallNPN_SetValue_NPPVpluginEventModel(eventModel, &rv))
+ return NPERR_GENERIC_ERROR;
+#if defined(__i386__)
+ mEventModel = static_cast<NPEventModel>(eventModel);
+#endif
+
+ PLUGIN_LOG_DEBUG((" Plugin requested event model id # %i\n",
+ eventModel));
+
+ return rv;
+ }
+#endif
+
+ case NPPVpluginIsPlayingAudio: {
+ NPError rv = NPERR_GENERIC_ERROR;
+ if (!CallNPN_SetValue_NPPVpluginIsPlayingAudio((NPBool)(intptr_t)aValue, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return rv;
+ }
+
+#ifdef XP_WIN
+ case NPPVpluginRequiresAudioDeviceChanges: {
+ // Many other NPN_SetValue variables are forwarded to our
+ // PluginInstanceParent, which runs on a content process. We
+ // instead forward this message to the PluginModuleParent, which runs
+ // on the chrome process. This is because our audio
+ // API calls should run the chrome proc, not content.
+ NPError rv = NPERR_GENERIC_ERROR;
+ PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+ if (chromeInstance) {
+ rv = chromeInstance->PluginRequiresAudioDeviceChanges(this,
+ (NPBool)(intptr_t)aValue);
+ }
+ return rv;
+ }
+#endif
+
+ default:
+ MOZ_LOG(GetPluginLog(), LogLevel::Warning,
+ ("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i (%s)",
+ (int) aVar, NPPVariableToString(aVar)));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+bool
+PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(
+ bool* wantsAllStreams, NPError* rv)
+{
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ uint32_t value = 0;
+ if (!mPluginIface->getvalue) {
+ *rv = NPERR_GENERIC_ERROR;
+ }
+ else {
+ *rv = mPluginIface->getvalue(GetNPP(), NPPVpluginWantsAllNetworkStreams,
+ &value);
+ }
+ *wantsAllStreams = value;
+ return true;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(
+ bool* needs, NPError* rv)
+{
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+#ifdef MOZ_X11
+ // 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 (!mPluginIface->getvalue) {
+ *rv = NPERR_GENERIC_ERROR;
+ }
+ else {
+ *rv = mPluginIface->getvalue(GetNPP(), NPPVpluginNeedsXEmbed,
+ &needsXEmbed);
+ }
+ *needs = needsXEmbed;
+ return true;
+
+#else
+
+ NS_RUNTIMEABORT("shouldn't be called on non-X11 platforms");
+ return false; // not reached
+
+#endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginScriptableNPObject(
+ PPluginScriptableObjectChild** aValue,
+ NPError* aResult)
+{
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ NPObject* object = nullptr;
+ NPError result = NPERR_GENERIC_ERROR;
+ if (mPluginIface->getvalue) {
+ result = mPluginIface->getvalue(GetNPP(), NPPVpluginScriptableNPObject,
+ &object);
+ }
+ if (result == NPERR_NO_ERROR && object) {
+ PluginScriptableObjectChild* actor = GetActorForNPObject(object);
+
+ // If we get an actor then it has retained. Otherwise we don't need it
+ // any longer.
+ PluginModuleChild::sBrowserFuncs.releaseobject(object);
+ if (actor) {
+ *aValue = actor;
+ *aResult = NPERR_NO_ERROR;
+ return true;
+ }
+
+ NS_ERROR("Failed to get actor!");
+ result = NPERR_GENERIC_ERROR;
+ }
+ else {
+ result = NPERR_GENERIC_ERROR;
+ }
+
+ *aValue = nullptr;
+ *aResult = result;
+ return true;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(
+ nsCString* aPlugId,
+ NPError* aResult)
+{
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+#if MOZ_ACCESSIBILITY_ATK
+
+ char* plugId = nullptr;
+ NPError result = NPERR_GENERIC_ERROR;
+ if (mPluginIface->getvalue) {
+ result = mPluginIface->getvalue(GetNPP(),
+ NPPVpluginNativeAccessibleAtkPlugId,
+ &plugId);
+ }
+
+ *aPlugId = nsCString(plugId);
+ *aResult = result;
+ return true;
+
+#else
+
+ NS_RUNTIMEABORT("shouldn't be called on non-ATK platforms");
+ return false;
+
+#endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value,
+ NPError* result)
+{
+ if (!mPluginIface->setvalue) {
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+ }
+
+ NPBool v = value;
+ *result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v);
+ return true;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_SetValue_NPNVCSSZoomFactor(const double& value,
+ NPError* result)
+{
+ if (!mPluginIface->setvalue) {
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+ }
+
+ mCSSZoomFactor = value;
+ double v = value;
+ *result = mPluginIface->setvalue(GetNPP(), NPNVCSSZoomFactor, &v);
+ return true;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value,
+ NPError* result)
+{
+ if (!mPluginIface->setvalue) {
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+ }
+
+ NPBool v = value;
+ *result = mPluginIface->setvalue(GetNPP(), NPNVmuteAudioBool, &v);
+ return true;
+}
+
+#if defined(XP_WIN)
+NPError
+PluginInstanceChild::DefaultAudioDeviceChanged(NPAudioDeviceChangeDetails& details)
+{
+ if (!mPluginIface->setvalue) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return mPluginIface->setvalue(GetNPP(), NPNVaudioDeviceChangeDetails, (void*)&details);
+}
+#endif
+
+
+bool
+PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
+ int16_t* handled)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+#if defined(MOZ_X11) && defined(DEBUG)
+ if (GraphicsExpose == event.event.type)
+ PLUGIN_LOG_DEBUG((" received drawable 0x%lx\n",
+ event.event.xgraphicsexpose.drawable));
+#endif
+
+#ifdef XP_MACOSX
+ // Mac OS X does not define an NPEvent structure. It defines more specific types.
+ NPCocoaEvent evcopy = event.event;
+
+ // Make sure we reset mCurrentEvent in case of an exception
+ AutoRestore<const NPCocoaEvent*> savePreviousEvent(mCurrentEvent);
+
+ // Track the current event for NPN_PopUpContextMenu.
+ mCurrentEvent = &event.event;
+#else
+ // Make a copy since we may modify values.
+ NPEvent evcopy = event.event;
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ // event.contentsScaleFactor <= 0 is a signal we shouldn't use it,
+ // for example when AnswerNPP_HandleEvent() is called from elsewhere
+ // in the child process (not via rpc code from the parent process).
+ if (event.contentsScaleFactor > 0) {
+ mContentsScaleFactor = event.contentsScaleFactor;
+ }
+#endif
+
+#ifdef OS_WIN
+ // FIXME/bug 567645: temporarily drop the "dummy event" on the floor
+ if (WM_NULL == evcopy.event)
+ return true;
+
+ *handled = WinlessHandleEvent(evcopy);
+ return true;
+#endif
+
+ // XXX A previous call to mPluginIface->event might block, e.g. right click
+ // for context menu. Still, we might get here again, calling into the plugin
+ // a second time while it's in the previous call.
+ if (!mPluginIface->event)
+ *handled = false;
+ else
+ *handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
+
+#ifdef XP_MACOSX
+ // Release any reference counted objects created in the child process.
+ if (evcopy.type == NPCocoaEventKeyDown ||
+ evcopy.type == NPCocoaEventKeyUp) {
+ ::CFRelease((CFStringRef)evcopy.data.key.characters);
+ ::CFRelease((CFStringRef)evcopy.data.key.charactersIgnoringModifiers);
+ }
+ else if (evcopy.type == NPCocoaEventTextInput) {
+ ::CFRelease((CFStringRef)evcopy.data.text.text);
+ }
+#endif
+
+#ifdef MOZ_X11
+ if (GraphicsExpose == event.event.type) {
+ // Make sure the X server completes the drawing before the parent
+ // draws on top and destroys the Drawable.
+ //
+ // XSync() waits for the X server to complete. Really this child
+ // process does not need to wait; the parent is the process that needs
+ // to wait. A possibly-slightly-better alternative would be to send
+ // an X event to the parent that the parent would wait for.
+ XSync(mWsInfo.display, False);
+ }
+#endif
+
+ return true;
+}
+
+#ifdef XP_MACOSX
+
+bool
+PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
+ Shmem&& mem,
+ int16_t* handled,
+ Shmem* rtnmem)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ PaintTracker pt;
+
+ NPCocoaEvent evcopy = event.event;
+ mContentsScaleFactor = event.contentsScaleFactor;
+
+ if (evcopy.type == NPCocoaEventDrawRect) {
+ int scaleFactor = ceil(mContentsScaleFactor);
+ if (!mShColorSpace) {
+ mShColorSpace = CreateSystemColorSpace();
+ if (!mShColorSpace) {
+ PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
+ *handled = false;
+ *rtnmem = mem;
+ return true;
+ }
+ }
+ if (!mShContext) {
+ void* cgContextByte = mem.get<char>();
+ mShContext = ::CGBitmapContextCreate(cgContextByte,
+ mWindow.width * scaleFactor,
+ mWindow.height * scaleFactor, 8,
+ mWindow.width * 4 * scaleFactor, mShColorSpace,
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host);
+
+ if (!mShContext) {
+ PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
+ *handled = false;
+ *rtnmem = mem;
+ return true;
+ }
+ }
+ CGRect clearRect = ::CGRectMake(0, 0, mWindow.width, mWindow.height);
+ ::CGContextClearRect(mShContext, clearRect);
+ evcopy.data.draw.context = mShContext;
+ } else {
+ PLUGIN_LOG_DEBUG(("Invalid event type for AnswerNNP_HandleEvent_Shmem."));
+ *handled = false;
+ *rtnmem = mem;
+ return true;
+ }
+
+ if (!mPluginIface->event) {
+ *handled = false;
+ } else {
+ ::CGContextSaveGState(evcopy.data.draw.context);
+ *handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
+ ::CGContextRestoreGState(evcopy.data.draw.context);
+ }
+
+ *rtnmem = mem;
+ return true;
+}
+
+#else
+bool
+PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
+ Shmem&& mem,
+ int16_t* handled,
+ Shmem* rtnmem)
+{
+ NS_RUNTIMEABORT("not reached.");
+ *rtnmem = mem;
+ return true;
+}
+#endif
+
+#ifdef XP_MACOSX
+
+void CallCGDraw(CGContextRef ref, void* aPluginInstance, nsIntRect aUpdateRect) {
+ PluginInstanceChild* pluginInstance = (PluginInstanceChild*)aPluginInstance;
+
+ pluginInstance->CGDraw(ref, aUpdateRect);
+}
+
+bool
+PluginInstanceChild::CGDraw(CGContextRef ref, nsIntRect aUpdateRect) {
+
+ NPCocoaEvent drawEvent;
+ drawEvent.type = NPCocoaEventDrawRect;
+ drawEvent.version = 0;
+ drawEvent.data.draw.x = aUpdateRect.x;
+ drawEvent.data.draw.y = aUpdateRect.y;
+ drawEvent.data.draw.width = aUpdateRect.width;
+ drawEvent.data.draw.height = aUpdateRect.height;
+ drawEvent.data.draw.context = ref;
+
+ NPRemoteEvent remoteDrawEvent = {drawEvent};
+ // Signal to AnswerNPP_HandleEvent() not to use this value
+ remoteDrawEvent.contentsScaleFactor = -1.0;
+
+ int16_t handled;
+ AnswerNPP_HandleEvent(remoteDrawEvent, &handled);
+ return handled == true;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
+ const uint32_t &surfaceid,
+ int16_t* handled)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ PaintTracker pt;
+
+ NPCocoaEvent evcopy = event.event;
+ mContentsScaleFactor = event.contentsScaleFactor;
+ RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(surfaceid,
+ mContentsScaleFactor);
+ if (!surf) {
+ NS_ERROR("Invalid IOSurface.");
+ *handled = false;
+ return false;
+ }
+
+ if (!mCARenderer) {
+ mCARenderer = new nsCARenderer();
+ }
+
+ if (evcopy.type == NPCocoaEventDrawRect) {
+ mCARenderer->AttachIOSurface(surf);
+ if (!mCARenderer->isInit()) {
+ void *caLayer = nullptr;
+ NPError result = mPluginIface->getvalue(GetNPP(),
+ NPPVpluginCoreAnimationLayer,
+ &caLayer);
+
+ if (result != NPERR_NO_ERROR || !caLayer) {
+ PLUGIN_LOG_DEBUG(("Plugin requested CoreAnimation but did not "
+ "provide CALayer."));
+ *handled = false;
+ return false;
+ }
+
+ mCARenderer->SetupRenderer(caLayer, mWindow.width, mWindow.height,
+ mContentsScaleFactor,
+ GetQuirks() & QUIRK_ALLOW_OFFLINE_RENDERER ?
+ ALLOW_OFFLINE_RENDERER : DISALLOW_OFFLINE_RENDERER);
+
+ // Flash needs to have the window set again after this step
+ if (mPluginIface->setwindow)
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+ }
+ } else {
+ PLUGIN_LOG_DEBUG(("Invalid event type for "
+ "AnswerNNP_HandleEvent_IOSurface."));
+ *handled = false;
+ return false;
+ }
+
+ mCARenderer->Render(mWindow.width, mWindow.height,
+ mContentsScaleFactor, nullptr);
+
+ return true;
+
+}
+
+#else
+bool
+PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
+ const uint32_t &surfaceid,
+ int16_t* handled)
+{
+ NS_RUNTIMEABORT("NPP_HandleEvent_IOSurface is a OSX-only message");
+ return false;
+}
+#endif
+
+bool
+PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event)
+{
+ NS_ASSERTION(!mLayersRendering && !mPendingPluginCall,
+ "Shouldn't be receiving WindowPosChanged with layer rendering");
+
+#ifdef OS_WIN
+ int16_t dontcare;
+ return AnswerNPP_HandleEvent(event, &dontcare);
+#else
+ NS_RUNTIMEABORT("WindowPosChanged is a windows-only message");
+ return false;
+#endif
+}
+
+bool
+PluginInstanceChild::RecvContentsScaleFactorChanged(const double& aContentsScaleFactor)
+{
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ mContentsScaleFactor = aContentsScaleFactor;
+#if defined(XP_MACOSX)
+ if (mShContext) {
+ // Release the shared context so that it is reallocated
+ // with the new size.
+ ::CGContextRelease(mShContext);
+ mShContext = nullptr;
+ }
+#endif
+ return true;
+#else
+ NS_RUNTIMEABORT("ContentsScaleFactorChanged is an Windows or OSX only message");
+ return false;
+#endif
+}
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+// Create a new window from NPWindow
+bool PluginInstanceChild::CreateWindow(const NPRemoteWindow& aWindow)
+{
+ PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
+ FULLFUNCTION,
+ aWindow.window,
+ aWindow.x, aWindow.y,
+ aWindow.width, aWindow.height));
+
+#ifdef MOZ_WIDGET_GTK
+ if (mXEmbed) {
+ mWindow.window = reinterpret_cast<void*>(aWindow.window);
+ }
+ else {
+ Window browserSocket = (Window)(aWindow.window);
+ xt_client_init(&mXtClient, mWsInfo.visual, mWsInfo.colormap, mWsInfo.depth);
+ xt_client_create(&mXtClient, browserSocket, mWindow.width, mWindow.height);
+ mWindow.window = (void *)XtWindow(mXtClient.child_widget);
+ }
+#else
+ mWindow.window = reinterpret_cast<void*>(aWindow.window);
+#endif
+
+ return true;
+}
+
+// Destroy window
+void PluginInstanceChild::DeleteWindow()
+{
+ PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
+ FULLFUNCTION,
+ mWindow.window,
+ mWindow.x, mWindow.y,
+ mWindow.width, mWindow.height));
+
+ if (!mWindow.window)
+ return;
+
+#ifdef MOZ_WIDGET_GTK
+ if (mXtClient.top_widget) {
+ xt_client_unrealize(&mXtClient);
+ xt_client_destroy(&mXtClient);
+ mXtClient.top_widget = nullptr;
+ }
+#endif
+
+ // We don't have to keep the plug-in window ID any longer.
+ mWindow.window = nullptr;
+}
+#endif
+
+bool
+PluginInstanceChild::AnswerCreateChildPluginWindow(NativeWindowHandle* aChildPluginWindow)
+{
+#if defined(XP_WIN)
+ MOZ_ASSERT(!mPluginWindowHWND);
+
+ if (!CreatePluginWindow()) {
+ return false;
+ }
+
+ MOZ_ASSERT(mPluginWindowHWND);
+
+ *aChildPluginWindow = mPluginWindowHWND;
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceChild::CreateChildPluginWindow not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginInstanceChild::RecvCreateChildPopupSurrogate(const NativeWindowHandle& aNetscapeWindow)
+{
+#if defined(XP_WIN)
+ mCachedWinlessPluginHWND = aNetscapeWindow;
+ CreateWinlessPopupSurrogate();
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceChild::CreateChildPluginWindow not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
+{
+ PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
+ FULLFUNCTION,
+ aWindow.window,
+ aWindow.x, aWindow.y,
+ aWindow.width, aWindow.height));
+ NS_ASSERTION(!mLayersRendering && !mPendingPluginCall,
+ "Shouldn't be receiving NPP_SetWindow with layer rendering");
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ NS_ASSERTION(mWsInfo.display, "We should have a valid display!");
+
+ // The minimum info is sent over IPC to allow this
+ // code to determine the rest.
+
+ mWindow.x = aWindow.x;
+ mWindow.y = aWindow.y;
+ mWindow.width = aWindow.width;
+ mWindow.height = aWindow.height;
+ mWindow.clipRect = aWindow.clipRect;
+ mWindow.type = aWindow.type;
+
+ mWsInfo.colormap = aWindow.colormap;
+ int depth;
+ FindVisualAndDepth(mWsInfo.display, aWindow.visualID,
+ &mWsInfo.visual, &depth);
+ mWsInfo.depth = depth;
+
+ if (!mWindow.window && mWindow.type == NPWindowTypeWindow) {
+ CreateWindow(aWindow);
+ }
+
+#ifdef MOZ_WIDGET_GTK
+ if (mXEmbed && gtk_check_version(2,18,7) != nullptr) { // older
+ if (aWindow.type == NPWindowTypeWindow) {
+ GdkWindow* socket_window = gdk_window_lookup(static_cast<GdkNativeWindow>(aWindow.window));
+ if (socket_window) {
+ // A GdkWindow for the socket already exists. Need to
+ // workaround https://bugzilla.gnome.org/show_bug.cgi?id=607061
+ // See wrap_gtk_plug_embedded in PluginModuleChild.cpp.
+ g_object_set_data(G_OBJECT(socket_window),
+ "moz-existed-before-set-window",
+ GUINT_TO_POINTER(1));
+ }
+ }
+
+ if (aWindow.visualID != X11None
+ && gtk_check_version(2, 12, 10) != nullptr) { // older
+ // Workaround for a bug in Gtk+ (prior to 2.12.10) where deleting
+ // a foreign GdkColormap will also free the XColormap.
+ // http://git.gnome.org/browse/gtk+/log/gdk/x11/gdkcolor-x11.c?id=GTK_2_12_10
+ GdkVisual *gdkvisual = gdkx_visual_get(aWindow.visualID);
+ GdkColormap *gdkcolor =
+ gdk_x11_colormap_foreign_new(gdkvisual, aWindow.colormap);
+
+ if (g_object_get_data(G_OBJECT(gdkcolor), "moz-have-extra-ref")) {
+ // We already have a ref to keep the object alive.
+ g_object_unref(gdkcolor);
+ } else {
+ // leak and mark as already leaked
+ g_object_set_data(G_OBJECT(gdkcolor),
+ "moz-have-extra-ref", GUINT_TO_POINTER(1));
+ }
+ }
+ }
+#endif
+
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceChild][%p] Answer_SetWindow w=<x=%d,y=%d, w=%d,h=%d>, clip=<l=%d,t=%d,r=%d,b=%d>",
+ this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
+ mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom));
+
+ if (mPluginIface->setwindow)
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+
+#elif defined(OS_WIN)
+ switch (aWindow.type) {
+ case NPWindowTypeWindow:
+ {
+ // This check is now done in PluginInstanceParent before this call, so
+ // we should never see it here.
+ MOZ_ASSERT(!(GetQuirks() & QUIRK_QUICKTIME_AVOID_SETWINDOW) ||
+ aWindow.width != 0 || aWindow.height != 0);
+
+ MOZ_ASSERT(mPluginWindowHWND,
+ "Child plugin window must exist before call to SetWindow");
+
+ HWND parentHWND = reinterpret_cast<HWND>(aWindow.window);
+ if (mPluginWindowHWND != parentHWND) {
+ mPluginParentHWND = parentHWND;
+ ShowWindow(mPluginWindowHWND, SW_SHOWNA);
+ }
+
+ SizePluginWindow(aWindow.width, aWindow.height);
+
+ mWindow.window = (void*)mPluginWindowHWND;
+ mWindow.x = aWindow.x;
+ mWindow.y = aWindow.y;
+ mWindow.width = aWindow.width;
+ mWindow.height = aWindow.height;
+ mWindow.type = aWindow.type;
+ mContentsScaleFactor = aWindow.contentsScaleFactor;
+
+ if (mPluginIface->setwindow) {
+ SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+ WNDPROC wndProc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC));
+ if (wndProc != PluginWindowProc) {
+ mPluginWndProc = reinterpret_cast<WNDPROC>(
+ SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(PluginWindowProc)));
+ NS_ASSERTION(mPluginWndProc != PluginWindowProc, "WTF?");
+ }
+ RemoveProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty);
+ HookSetWindowLongPtr();
+ }
+ }
+ break;
+
+ default:
+ NS_NOTREACHED("Bad plugin window type.");
+ return false;
+ break;
+ }
+
+#elif defined(XP_MACOSX)
+
+ mWindow.x = aWindow.x;
+ mWindow.y = aWindow.y;
+ mWindow.width = aWindow.width;
+ mWindow.height = aWindow.height;
+ mWindow.clipRect = aWindow.clipRect;
+ mWindow.type = aWindow.type;
+ mContentsScaleFactor = aWindow.contentsScaleFactor;
+
+ if (mShContext) {
+ // Release the shared context so that it is reallocated
+ // with the new size.
+ ::CGContextRelease(mShContext);
+ mShContext = nullptr;
+ }
+
+ if (mPluginIface->setwindow)
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+
+#elif defined(ANDROID)
+ // TODO: Need Android impl
+#elif defined(MOZ_WIDGET_UIKIT)
+ // Don't care
+#else
+# error Implement me for your OS
+#endif
+
+ return true;
+}
+
+bool
+PluginInstanceChild::Initialize()
+{
+#ifdef MOZ_WIDGET_GTK
+ NPError rv;
+
+ if (mWsInfo.display) {
+ // Already initialized
+ return false;
+ }
+
+ // Request for windowless plugins is set in newp(), before this call.
+ if (mWindow.type == NPWindowTypeWindow) {
+ AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(&mXEmbed, &rv);
+
+ // Set up Xt loop for windowed plugins without XEmbed support
+ if (!mXEmbed) {
+ xt_client_xloop_create();
+ }
+ }
+
+ // Use default GTK display for XEmbed and windowless plugins
+ if (mXEmbed || mWindow.type != NPWindowTypeWindow) {
+ mWsInfo.display = DefaultXDisplay();
+ }
+ else {
+ mWsInfo.display = xt_client_get_display();
+ }
+#endif
+
+ return true;
+}
+
+bool
+PluginInstanceChild::RecvHandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData,
+ const bool& aIsConsumed)
+{
+#if defined(OS_WIN)
+ const WinNativeKeyEventData* eventData =
+ static_cast<const WinNativeKeyEventData*>(aKeyEventData);
+ switch (eventData->mMessage) {
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ mLastKeyEventConsumed = aIsConsumed;
+ break;
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ // If preceding keydown or keyup event is consumed by the chrome
+ // process, we should consume WM_*CHAR messages too.
+ if (mLastKeyEventConsumed) {
+ return true;
+ }
+ default:
+ MOZ_CRASH("Needs to handle all messages posted to the parent");
+ }
+#endif // #if defined(OS_WIN)
+
+ // Unknown key input shouldn't be sent to plugin for security.
+ // XXX Is this possible if a plugin process which posted the message
+ // already crashed and this plugin process is recreated?
+ if (NS_WARN_IF(!mPostingKeyEvents && !mPostingKeyEventsOutdated)) {
+ return true;
+ }
+
+ // If there is outdated posting key events, we should consume the key
+ // events.
+ if (mPostingKeyEventsOutdated) {
+ mPostingKeyEventsOutdated--;
+ return true;
+ }
+
+ mPostingKeyEvents--;
+
+ // If composition has been started after posting the key event,
+ // we should discard the event since if we send the event to plugin,
+ // the plugin may be confused and the result may be broken because
+ // the event order is shuffled.
+ if (aIsConsumed || sIsIMEComposing) {
+ return true;
+ }
+
+#if defined(OS_WIN)
+ UINT message = 0;
+ switch (eventData->mMessage) {
+ case WM_KEYDOWN:
+ message = MOZ_WM_KEYDOWN;
+ break;
+ case WM_SYSKEYDOWN:
+ message = MOZ_WM_SYSKEYDOWN;
+ break;
+ case WM_KEYUP:
+ message = MOZ_WM_KEYUP;
+ break;
+ case WM_SYSKEYUP:
+ message = MOZ_WM_SYSKEYUP;
+ break;
+ case WM_CHAR:
+ message = MOZ_WM_CHAR;
+ break;
+ case WM_SYSCHAR:
+ message = MOZ_WM_SYSCHAR;
+ break;
+ case WM_DEADCHAR:
+ message = MOZ_WM_DEADCHAR;
+ break;
+ case WM_SYSDEADCHAR:
+ message = MOZ_WM_SYSDEADCHAR;
+ break;
+ default:
+ MOZ_CRASH("Needs to handle all messages posted to the parent");
+ }
+ PluginWindowProcInternal(mPluginWindowHWND, message,
+ eventData->mWParam, eventData->mLParam);
+#endif
+ return true;
+}
+
+#if defined(OS_WIN)
+
+static const TCHAR kWindowClassName[] = TEXT("GeckoPluginWindow");
+static const TCHAR kPluginInstanceChildProperty[] = TEXT("PluginInstanceChildProperty");
+static const TCHAR kFlashThrottleProperty[] = TEXT("MozillaFlashThrottleProperty");
+
+// static
+bool
+PluginInstanceChild::RegisterWindowClass()
+{
+ static bool alreadyRegistered = false;
+ if (alreadyRegistered)
+ return true;
+
+ alreadyRegistered = true;
+
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = DummyWindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(nullptr);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = kWindowClassName;
+ wcex.hIconSm = 0;
+
+ return RegisterClassEx(&wcex) ? true : false;
+}
+
+bool
+PluginInstanceChild::CreatePluginWindow()
+{
+ // already initialized
+ if (mPluginWindowHWND)
+ return true;
+
+ if (!RegisterWindowClass())
+ return false;
+
+ mPluginWindowHWND =
+ CreateWindowEx(WS_EX_LEFT | WS_EX_LTRREADING |
+ WS_EX_NOPARENTNOTIFY | // XXXbent Get rid of this!
+ WS_EX_RIGHTSCROLLBAR,
+ kWindowClassName, 0,
+ WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0,
+ 0, 0, nullptr, 0, GetModuleHandle(nullptr), 0);
+ if (!mPluginWindowHWND)
+ return false;
+ if (!SetProp(mPluginWindowHWND, kPluginInstanceChildProperty, this))
+ return false;
+
+ // Apparently some plugins require an ASCII WndProc.
+ SetWindowLongPtrA(mPluginWindowHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(DefWindowProcA));
+
+ return true;
+}
+
+void
+PluginInstanceChild::DestroyPluginWindow()
+{
+ if (mPluginWindowHWND) {
+ // Unsubclass the window.
+ WNDPROC wndProc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC));
+ // Removed prior to SetWindowLongPtr, see HookSetWindowLongPtr.
+ RemoveProp(mPluginWindowHWND, kPluginInstanceChildProperty);
+ if (wndProc == PluginWindowProc) {
+ NS_ASSERTION(mPluginWndProc, "Should have old proc here!");
+ SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(mPluginWndProc));
+ mPluginWndProc = 0;
+ }
+ DestroyWindow(mPluginWindowHWND);
+ mPluginWindowHWND = 0;
+ }
+}
+
+void
+PluginInstanceChild::SizePluginWindow(int width,
+ int height)
+{
+ if (mPluginWindowHWND) {
+ mPluginSize.x = width;
+ mPluginSize.y = height;
+ SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, width, height,
+ SWP_NOZORDER | SWP_NOREPOSITION);
+ }
+}
+
+// See chromium's webplugin_delegate_impl.cc for explanation of this function.
+// static
+LRESULT CALLBACK
+PluginInstanceChild::DummyWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ return CallWindowProc(DefWindowProc, hWnd, message, wParam, lParam);
+}
+
+// static
+LRESULT CALLBACK
+PluginInstanceChild::PluginWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ return mozilla::CallWindowProcCrashProtected(PluginWindowProcInternal, hWnd, message, wParam, lParam);
+}
+
+// static
+LRESULT CALLBACK
+PluginInstanceChild::PluginWindowProcInternal(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ NS_ASSERTION(!mozilla::ipc::MessageChannel::IsPumpingMessages(),
+ "Failed to prevent a nonqueued message from running!");
+ PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
+ GetProp(hWnd, kPluginInstanceChildProperty));
+ if (!self) {
+ NS_NOTREACHED("Badness!");
+ return 0;
+ }
+
+ NS_ASSERTION(self->mPluginWindowHWND == hWnd, "Wrong window!");
+ NS_ASSERTION(self->mPluginWndProc != PluginWindowProc, "Self-referential windowproc. Infinite recursion will happen soon.");
+
+ bool isIMECompositionMessage = false;
+ switch (message) {
+ // Adobe's shockwave positions the plugin window relative to the browser
+ // frame when it initializes. With oopp disabled, this wouldn't have an
+ // effect. With oopp, GeckoPluginWindow is a child of the parent plugin
+ // window, so the move offsets the child within the parent. Generally
+ // we don't want plugins moving or sizing our window, so we prevent
+ // these changes here.
+ case WM_WINDOWPOSCHANGING: {
+ WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lParam);
+ if (pos &&
+ (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE))) {
+ pos->x = pos->y = 0;
+ pos->cx = self->mPluginSize.x;
+ pos->cy = self->mPluginSize.y;
+ LRESULT res = CallWindowProc(self->mPluginWndProc,
+ hWnd, message, wParam, lParam);
+ pos->x = pos->y = 0;
+ pos->cx = self->mPluginSize.x;
+ pos->cy = self->mPluginSize.y;
+ return res;
+ }
+ break;
+ }
+
+ case WM_SETFOCUS:
+ // If this gets focus, ensure that there is no pending key events.
+ // Even if there were, we should ignore them for performance reason.
+ // Although, such case shouldn't occur.
+ NS_WARNING_ASSERTION(self->mPostingKeyEvents == 0,
+ "pending events");
+ self->mPostingKeyEvents = 0;
+ self->mLastKeyEventConsumed = false;
+ break;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ if (self->MaybePostKeyMessage(message, wParam, lParam)) {
+ // If PreHandleKeyMessage() posts the message to the parent
+ // process, we need to wait RecvOnKeyEventHandledBeforePlugin()
+ // to be called.
+ return 0; // Consume current message temporarily.
+ }
+ break;
+
+ case MOZ_WM_KEYDOWN:
+ message = WM_KEYDOWN;
+ break;
+ case MOZ_WM_SYSKEYDOWN:
+ message = WM_SYSKEYDOWN;
+ break;
+ case MOZ_WM_KEYUP:
+ message = WM_KEYUP;
+ break;
+ case MOZ_WM_SYSKEYUP:
+ message = WM_SYSKEYUP;
+ break;
+ case MOZ_WM_CHAR:
+ message = WM_CHAR;
+ break;
+ case MOZ_WM_SYSCHAR:
+ message = WM_SYSCHAR;
+ break;
+ case MOZ_WM_DEADCHAR:
+ message = WM_DEADCHAR;
+ break;
+ case MOZ_WM_SYSDEADCHAR:
+ message = WM_SYSDEADCHAR;
+ break;
+
+ case WM_IME_STARTCOMPOSITION:
+ isIMECompositionMessage = true;
+ sIsIMEComposing = true;
+ break;
+ case WM_IME_ENDCOMPOSITION:
+ isIMECompositionMessage = true;
+ sIsIMEComposing = false;
+ break;
+ case WM_IME_COMPOSITION:
+ isIMECompositionMessage = true;
+ // XXX Some old IME may not send WM_IME_COMPOSITION_START or
+ // WM_IME_COMPSOITION_END properly. So, we need to check
+ // WM_IME_COMPSOITION and if it includes commit string.
+ sIsIMEComposing = !(lParam & GCS_RESULTSTR);
+ break;
+
+ // The plugin received keyboard focus, let the parent know so the dom
+ // is up to date.
+ case WM_MOUSEACTIVATE:
+ self->CallPluginFocusChange(true);
+ break;
+ }
+
+ // When a composition is committed, there may be pending key
+ // events which were posted to the parent process before starting
+ // the composition. Then, we shouldn't handle it since they are
+ // now outdated.
+ if (isIMECompositionMessage && !sIsIMEComposing) {
+ self->mPostingKeyEventsOutdated += self->mPostingKeyEvents;
+ self->mPostingKeyEvents = 0;
+ }
+
+ // Prevent lockups due to plugins making rpc calls when the parent
+ // is making a synchronous SendMessage call to the child window. Add
+ // more messages as needed.
+ if ((InSendMessageEx(nullptr)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
+ switch(message) {
+ case WM_CHILDACTIVATE:
+ case WM_KILLFOCUS:
+ ReplyMessage(0);
+ break;
+ }
+ }
+
+ if (message == WM_KILLFOCUS) {
+ self->CallPluginFocusChange(false);
+ }
+
+ if (message == WM_USER+1 &&
+ (self->GetQuirks() & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)) {
+ self->FlashThrottleMessage(hWnd, message, wParam, lParam, true);
+ return 0;
+ }
+
+ NS_ASSERTION(self->mPluginWndProc != PluginWindowProc,
+ "Self-referential windowproc happened inside our hook proc. "
+ "Infinite recursion will happen soon.");
+
+ LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
+ lParam);
+
+ // Make sure capture is released by the child on mouse events. Fixes a
+ // problem with flash full screen mode mouse input. Appears to be
+ // caused by a bug in flash, since we are not setting the capture
+ // on the window.
+ if (message == WM_LBUTTONDOWN &&
+ self->GetQuirks() & QUIRK_FLASH_FIXUP_MOUSE_CAPTURE) {
+ wchar_t szClass[26];
+ HWND hwnd = GetForegroundWindow();
+ if (hwnd && GetClassNameW(hwnd, szClass,
+ sizeof(szClass)/sizeof(char16_t)) &&
+ !wcscmp(szClass, kFlashFullscreenClass)) {
+ ReleaseCapture();
+ SetFocus(hwnd);
+ }
+ }
+
+ if (message == WM_CLOSE) {
+ self->DestroyPluginWindow();
+ }
+
+ if (message == WM_NCDESTROY) {
+ RemoveProp(hWnd, kPluginInstanceChildProperty);
+ }
+
+ return res;
+}
+
+bool
+PluginInstanceChild::ShouldPostKeyMessage(UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ // If there is a composition, we shouldn't post the key message to the
+ // parent process because we cannot handle IME messages asynchronously.
+ // Therefore, if we posted key events to the parent process, the event
+ // order of the posted key events and IME events are shuffled.
+ if (sIsIMEComposing) {
+ return false;
+ }
+
+ // If there are some pending keyboard events which are not handled in
+ // the parent process, we should post the message for avoiding to shuffle
+ // the key event order.
+ if (mPostingKeyEvents) {
+ return true;
+ }
+
+ // If we are not waiting calls of RecvOnKeyEventHandledBeforePlugin(),
+ // we don't need to post WM_*CHAR messages.
+ switch (message) {
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_DEADCHAR:
+ case WM_SYSDEADCHAR:
+ return false;
+ }
+
+ // Otherwise, we should post key message which might match with a
+ // shortcut key.
+ ModifierKeyState modifierState;
+ if (!modifierState.MaybeMatchShortcutKey()) {
+ // For better UX, we shouldn't use IPC when user tries to
+ // input character(s).
+ return false;
+ }
+
+ // Ignore modifier key events and keys already handled by IME.
+ switch (wParam) {
+ case VK_SHIFT:
+ case VK_CONTROL:
+ case VK_MENU:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_CAPITAL:
+ case VK_NUMLOCK:
+ case VK_SCROLL:
+ // Following virtual keycodes shouldn't come with WM_(SYS)KEY* message
+ // but check it for avoiding unnecessary cross process communication.
+ case VK_LSHIFT:
+ case VK_RSHIFT:
+ case VK_LCONTROL:
+ case VK_RCONTROL:
+ case VK_LMENU:
+ case VK_RMENU:
+ case VK_PROCESSKEY:
+ case VK_PACKET:
+ case 0xFF: // 0xFF could be sent with unidentified key by the layout.
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool
+PluginInstanceChild::MaybePostKeyMessage(UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ if (!ShouldPostKeyMessage(message, wParam, lParam)) {
+ return false;
+ }
+
+ ModifierKeyState modifierState;
+ WinNativeKeyEventData winNativeKeyData(message, wParam, lParam,
+ modifierState);
+ NativeEventData nativeKeyData;
+ nativeKeyData.Copy(winNativeKeyData);
+ if (NS_WARN_IF(!SendOnWindowedPluginKeyEvent(nativeKeyData))) {
+ return false;
+ }
+
+ mPostingKeyEvents++;
+ return true;
+}
+
+/* set window long ptr hook for flash */
+
+/*
+ * 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.
+ */
+
+#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
+
+extern LRESULT CALLBACK
+NeuteredWindowProc(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam);
+
+const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
+
+// static
+bool
+PluginInstanceChild::SetWindowLongHookCheck(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong)
+{
+ // Let this go through if it's not a subclass
+ if (nIndex != GWLP_WNDPROC ||
+ // if it's not a subclassed plugin window
+ !GetProp(hWnd, kPluginInstanceChildProperty) ||
+ // if we're not disabled
+ GetProp(hWnd, kPluginIgnoreSubclassProperty) ||
+ // if the subclass is set to a known procedure
+ newLong == reinterpret_cast<LONG_PTR>(PluginWindowProc) ||
+ newLong == reinterpret_cast<LONG_PTR>(NeuteredWindowProc) ||
+ newLong == reinterpret_cast<LONG_PTR>(DefWindowProcA) ||
+ newLong == reinterpret_cast<LONG_PTR>(DefWindowProcW) ||
+ // if the subclass is a WindowsMessageLoop subclass restore
+ GetProp(hWnd, kOldWndProcProp))
+ return true;
+ // prevent the subclass
+ return false;
+}
+
+#ifdef _WIN64
+LONG_PTR WINAPI
+PluginInstanceChild::SetWindowLongPtrAHook(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong)
+#else
+LONG WINAPI
+PluginInstanceChild::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
+ PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
+ GetProp(hWnd, kPluginInstanceChildProperty));
+
+ // Hook our subclass back up, just like we do on setwindow.
+ WNDPROC currentProc =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
+ if (currentProc != PluginWindowProc) {
+ self->mPluginWndProc =
+ reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(hWnd, nIndex,
+ reinterpret_cast<LONG_PTR>(PluginWindowProc)));
+ NS_ASSERTION(self->mPluginWndProc != PluginWindowProc, "Infinite recursion coming up!");
+ }
+ return proc;
+}
+
+#ifdef _WIN64
+LONG_PTR WINAPI
+PluginInstanceChild::SetWindowLongPtrWHook(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong)
+#else
+LONG WINAPI
+PluginInstanceChild::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
+ PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
+ GetProp(hWnd, kPluginInstanceChildProperty));
+
+ // Hook our subclass back up, just like we do on setwindow.
+ WNDPROC currentProc =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
+ if (currentProc != PluginWindowProc) {
+ self->mPluginWndProc =
+ reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(hWnd, nIndex,
+ reinterpret_cast<LONG_PTR>(PluginWindowProc)));
+ NS_ASSERTION(self->mPluginWndProc != PluginWindowProc, "Infinite recursion coming up!");
+ }
+ return proc;
+}
+
+void
+PluginInstanceChild::HookSetWindowLongPtr()
+{
+ if (!(GetQuirks() & QUIRK_FLASH_HOOK_SETLONGPTR))
+ return;
+
+ 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
+}
+
+class SetCaptureHookData
+{
+public:
+ explicit SetCaptureHookData(HWND aHwnd)
+ : mHwnd(aHwnd)
+ , mHaveRect(false)
+ {
+ MOZ_ASSERT(aHwnd);
+ mHaveRect = !!GetClientRect(aHwnd, &mCaptureRect);
+ }
+
+ /**
+ * @return true if capture was released
+ */
+ bool HandleMouseMsg(const MSG& aMsg)
+ {
+ // If the window belongs to Unity, the mouse button is up, and the mouse
+ // has moved outside the client rect of the Unity window, release capture.
+ if (aMsg.hwnd != mHwnd || !mHaveRect) {
+ return false;
+ }
+ if (aMsg.message != WM_MOUSEMOVE && aMsg.message != WM_LBUTTONUP) {
+ return false;
+ }
+ if ((aMsg.message == WM_MOUSEMOVE && (aMsg.wParam & MK_LBUTTON))) {
+ return false;
+ }
+ POINT pt = { GET_X_LPARAM(aMsg.lParam), GET_Y_LPARAM(aMsg.lParam) };
+ if (PtInRect(&mCaptureRect, pt)) {
+ return false;
+ }
+ return !!ReleaseCapture();
+ }
+
+ bool IsUnityLosingCapture(const CWPSTRUCT& aInfo) const
+ {
+ return aInfo.message == WM_CAPTURECHANGED &&
+ aInfo.hwnd == mHwnd;
+ }
+
+private:
+ HWND mHwnd;
+ bool mHaveRect;
+ RECT mCaptureRect;
+};
+
+static StaticAutoPtr<SetCaptureHookData> sSetCaptureHookData;
+typedef HWND (WINAPI* User32SetCapture)(HWND);
+static User32SetCapture sUser32SetCaptureHookStub = nullptr;
+
+HWND WINAPI
+PluginInstanceChild::SetCaptureHook(HWND aHwnd)
+{
+ // Don't do anything unless aHwnd belongs to Unity
+ wchar_t className[256] = {0};
+ int numChars = GetClassNameW(aHwnd, className, ArrayLength(className));
+ NS_NAMED_LITERAL_STRING(unityClassName, "Unity.WebPlayer");
+ if (numChars == unityClassName.Length() && unityClassName == wwc(className)) {
+ sSetCaptureHookData = new SetCaptureHookData(aHwnd);
+ }
+ return sUser32SetCaptureHookStub(aHwnd);
+}
+
+void
+PluginInstanceChild::SetUnityHooks()
+{
+ if (!(GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE)) {
+ return;
+ }
+
+ sUser32Intercept.Init("user32.dll");
+ if (!sUser32SetCaptureHookStub) {
+ sUser32Intercept.AddHook("SetCapture",
+ reinterpret_cast<intptr_t>(SetCaptureHook),
+ (void**) &sUser32SetCaptureHookStub);
+ }
+ if (!mUnityGetMessageHook) {
+ mUnityGetMessageHook = SetWindowsHookEx(WH_GETMESSAGE,
+ &UnityGetMessageHookProc, NULL,
+ GetCurrentThreadId());
+ }
+ if (!mUnitySendMessageHook) {
+ mUnitySendMessageHook = SetWindowsHookEx(WH_CALLWNDPROC,
+ &UnitySendMessageHookProc,
+ NULL, GetCurrentThreadId());
+ }
+}
+
+void
+PluginInstanceChild::ClearUnityHooks()
+{
+ if (mUnityGetMessageHook) {
+ UnhookWindowsHookEx(mUnityGetMessageHook);
+ mUnityGetMessageHook = NULL;
+ }
+ if (mUnitySendMessageHook) {
+ UnhookWindowsHookEx(mUnitySendMessageHook);
+ mUnitySendMessageHook = NULL;
+ }
+ sSetCaptureHookData = nullptr;
+}
+
+LRESULT CALLBACK
+PluginInstanceChild::UnityGetMessageHookProc(int aCode, WPARAM aWparam,
+ LPARAM aLParam)
+{
+ if (aCode >= 0) {
+ MSG* info = reinterpret_cast<MSG*>(aLParam);
+ MOZ_ASSERT(info);
+ if (sSetCaptureHookData && sSetCaptureHookData->HandleMouseMsg(*info)) {
+ sSetCaptureHookData = nullptr;
+ }
+ }
+
+ return CallNextHookEx(0, aCode, aWparam, aLParam);
+}
+
+LRESULT CALLBACK
+PluginInstanceChild::UnitySendMessageHookProc(int aCode, WPARAM aWparam,
+ LPARAM aLParam)
+{
+ if (aCode >= 0) {
+ CWPSTRUCT* info = reinterpret_cast<CWPSTRUCT*>(aLParam);
+ MOZ_ASSERT(info);
+ if (sSetCaptureHookData &&
+ sSetCaptureHookData->IsUnityLosingCapture(*info)) {
+ sSetCaptureHookData = nullptr;
+ }
+ }
+
+ return CallNextHookEx(0, aCode, aWparam, aLParam);
+}
+
+/* windowless track popup menu helpers */
+
+BOOL
+WINAPI
+PluginInstanceChild::TrackPopupHookProc(HMENU hMenu,
+ UINT uFlags,
+ int x,
+ int y,
+ int nReserved,
+ HWND hWnd,
+ CONST RECT *prcRect)
+{
+ if (!sUser32TrackPopupMenuStub) {
+ NS_ERROR("TrackPopupMenu stub isn't set! Badness!");
+ return 0;
+ }
+
+ // Only change the parent when we know this is a context on the plugin
+ // surface within the browser. Prevents resetting the parent on child ui
+ // displayed by plugins that have working parent-child relationships.
+ wchar_t szClass[21];
+ bool haveClass = GetClassNameW(hWnd, szClass, ArrayLength(szClass));
+ if (!haveClass ||
+ (wcscmp(szClass, L"MozillaWindowClass") &&
+ wcscmp(szClass, L"SWFlash_Placeholder"))) {
+ // Unrecognized parent
+ return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
+ hWnd, prcRect);
+ }
+
+ // Called on an unexpected event, warn.
+ if (!sWinlessPopupSurrogateHWND) {
+ NS_WARNING(
+ "Untraced TrackPopupHookProc call! Menu might not work right!");
+ return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
+ hWnd, prcRect);
+ }
+
+ HWND surrogateHwnd = sWinlessPopupSurrogateHWND;
+ sWinlessPopupSurrogateHWND = nullptr;
+
+ // Popups that don't use TPM_RETURNCMD expect a final command message
+ // when an item is selected and the context closes. Since we replace
+ // the parent, we need to forward this back to the real parent so it
+ // can act on the menu item selected.
+ bool isRetCmdCall = (uFlags & TPM_RETURNCMD);
+
+ DWORD res = sUser32TrackPopupMenuStub(hMenu, uFlags|TPM_RETURNCMD, x, y,
+ nReserved, surrogateHwnd, prcRect);
+
+ if (!isRetCmdCall && res) {
+ SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(res, 0), 0);
+ }
+
+ return res;
+}
+
+void
+PluginInstanceChild::InitPopupMenuHook()
+{
+ if (!(GetQuirks() & QUIRK_WINLESS_TRACKPOPUP_HOOK) ||
+ sUser32TrackPopupMenuStub)
+ return;
+
+ // Note, once WindowsDllInterceptor is initialized for a module,
+ // it remains initialized for that particular module for it's
+ // lifetime. Additional instances are needed if other modules need
+ // to be hooked.
+ if (!sUser32TrackPopupMenuStub) {
+ sUser32Intercept.Init("user32.dll");
+ sUser32Intercept.AddHook("TrackPopupMenu", reinterpret_cast<intptr_t>(TrackPopupHookProc),
+ (void**) &sUser32TrackPopupMenuStub);
+ }
+}
+
+void
+PluginInstanceChild::CreateWinlessPopupSurrogate()
+{
+ // already initialized
+ if (mWinlessPopupSurrogateHWND)
+ return;
+
+ mWinlessPopupSurrogateHWND =
+ CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", nullptr, WS_POPUP,
+ 0, 0, 0, 0, nullptr, 0, GetModuleHandle(nullptr), 0);
+ if (!mWinlessPopupSurrogateHWND) {
+ NS_ERROR("CreateWindowEx failed for winless placeholder!");
+ return;
+ }
+
+ SendSetNetscapeWindowAsParent(mWinlessPopupSurrogateHWND);
+}
+
+// static
+HIMC
+PluginInstanceChild::ImmGetContextProc(HWND aWND)
+{
+ if (!sCurrentPluginInstance) {
+ return sImm32ImmGetContextStub(aWND);
+ }
+
+ wchar_t szClass[21];
+ int haveClass = GetClassNameW(aWND, szClass, ArrayLength(szClass));
+ if (!haveClass || wcscmp(szClass, L"SWFlash_PlaceholderX")) {
+ NS_WARNING("We cannot recongnize hooked window class");
+ return sImm32ImmGetContextStub(aWND);
+ }
+
+ return sHookIMC;
+}
+
+// static
+BOOL
+PluginInstanceChild::ImmReleaseContextProc(HWND aWND, HIMC aIMC)
+{
+ if (aIMC == sHookIMC) {
+ return TRUE;
+ }
+
+ return sImm32ImmReleaseContextStub(aWND, aIMC);
+}
+
+// static
+LONG
+PluginInstanceChild::ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex,
+ LPVOID aBuf, DWORD aLen)
+{
+ if (aIMC != sHookIMC) {
+ return sImm32ImmGetCompositionStringStub(aIMC, aIndex, aBuf, aLen);
+ }
+ if (!sCurrentPluginInstance) {
+ return IMM_ERROR_GENERAL;
+ }
+ AutoTArray<uint8_t, 16> dist;
+ int32_t length = 0; // IMM_ERROR_NODATA
+ sCurrentPluginInstance->SendGetCompositionString(aIndex, &dist, &length);
+ if (length == IMM_ERROR_NODATA || length == IMM_ERROR_GENERAL) {
+ return length;
+ }
+
+ if (aBuf && aLen >= static_cast<DWORD>(length)) {
+ memcpy(aBuf, dist.Elements(), length);
+ }
+ return length;
+}
+
+// staitc
+BOOL
+PluginInstanceChild::ImmSetCandidateWindowProc(HIMC aIMC, LPCANDIDATEFORM aForm)
+{
+ if (aIMC != sHookIMC) {
+ return sImm32ImmSetCandidateWindowStub(aIMC, aForm);
+ }
+
+ if (!sCurrentPluginInstance ||
+ aForm->dwIndex != 0) {
+ return FALSE;
+ }
+
+ CandidateWindowPosition position;
+ position.mPoint.x = aForm->ptCurrentPos.x;
+ position.mPoint.y = aForm->ptCurrentPos.y;
+ position.mExcludeRect = !!(aForm->dwStyle & CFS_EXCLUDE);
+ if (position.mExcludeRect) {
+ position.mRect.x = aForm->rcArea.left;
+ position.mRect.y = aForm->rcArea.top;
+ position.mRect.width = aForm->rcArea.right - aForm->rcArea.left;
+ position.mRect.height = aForm->rcArea.bottom - aForm->rcArea.top;
+ }
+
+ sCurrentPluginInstance->SendSetCandidateWindow(position);
+ return TRUE;
+}
+
+// static
+BOOL
+PluginInstanceChild::ImmNotifyIME(HIMC aIMC, DWORD aAction, DWORD aIndex,
+ DWORD aValue)
+{
+ if (aIMC != sHookIMC) {
+ return sImm32ImmNotifyIME(aIMC, aAction, aIndex, aValue);
+ }
+
+ // We only supports NI_COMPOSITIONSTR because Flash uses it only
+ if (!sCurrentPluginInstance ||
+ aAction != NI_COMPOSITIONSTR ||
+ (aIndex != CPS_COMPLETE && aIndex != CPS_CANCEL)) {
+ return FALSE;
+ }
+
+ sCurrentPluginInstance->SendRequestCommitOrCancel(aAction == CPS_COMPLETE);
+ return TRUE;
+}
+
+void
+PluginInstanceChild::InitImm32Hook()
+{
+ if (!(GetQuirks() & QUIRK_WINLESS_HOOK_IME)) {
+ return;
+ }
+
+ if (sImm32ImmGetContextStub) {
+ return;
+ }
+
+ // When using windowless plugin, IMM API won't work due ot OOP.
+
+ sImm32Intercept.Init("imm32.dll");
+ sImm32Intercept.AddHook(
+ "ImmGetContext",
+ reinterpret_cast<intptr_t>(ImmGetContextProc),
+ (void**)&sImm32ImmGetContextStub);
+ sImm32Intercept.AddHook(
+ "ImmReleaseContext",
+ reinterpret_cast<intptr_t>(ImmReleaseContextProc),
+ (void**)&sImm32ImmReleaseContextStub);
+ sImm32Intercept.AddHook(
+ "ImmGetCompositionStringW",
+ reinterpret_cast<intptr_t>(ImmGetCompositionStringProc),
+ (void**)&sImm32ImmGetCompositionStringStub);
+ sImm32Intercept.AddHook(
+ "ImmSetCandidateWindow",
+ reinterpret_cast<intptr_t>(ImmSetCandidateWindowProc),
+ (void**)&sImm32ImmSetCandidateWindowStub);
+ sImm32Intercept.AddHook(
+ "ImmNotifyIME",
+ reinterpret_cast<intptr_t>(ImmNotifyIME),
+ (void**)&sImm32ImmNotifyIME);
+}
+
+void
+PluginInstanceChild::DestroyWinlessPopupSurrogate()
+{
+ if (mWinlessPopupSurrogateHWND)
+ DestroyWindow(mWinlessPopupSurrogateHWND);
+ mWinlessPopupSurrogateHWND = nullptr;
+}
+
+int16_t
+PluginInstanceChild::WinlessHandleEvent(NPEvent& event)
+{
+ if (!mPluginIface->event)
+ return false;
+
+ // Events that might generate nested event dispatch loops need
+ // special handling during delivery.
+ int16_t handled;
+
+ HWND focusHwnd = nullptr;
+
+ // TrackPopupMenu will fail if the parent window is not associated with
+ // our ui thread. So we hook TrackPopupMenu so we can hand in a surrogate
+ // parent created in the child process.
+ if ((GetQuirks() & QUIRK_WINLESS_TRACKPOPUP_HOOK) && // XXX turn on by default?
+ (event.event == WM_RBUTTONDOWN || // flash
+ event.event == WM_RBUTTONUP)) { // silverlight
+ sWinlessPopupSurrogateHWND = mWinlessPopupSurrogateHWND;
+
+ // A little trick scrounged from chromium's code - set the focus
+ // to our surrogate parent so keyboard nav events go to the menu.
+ focusHwnd = SetFocus(mWinlessPopupSurrogateHWND);
+ }
+
+ AutoRestore<PluginInstanceChild *> pluginInstance(sCurrentPluginInstance);
+ if (event.event == WM_IME_STARTCOMPOSITION ||
+ event.event == WM_IME_COMPOSITION ||
+ event.event == WM_KILLFOCUS) {
+ sCurrentPluginInstance = this;
+ }
+
+ MessageLoop* loop = MessageLoop::current();
+ AutoRestore<bool> modalLoop(loop->os_modal_loop());
+
+ handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
+
+ sWinlessPopupSurrogateHWND = nullptr;
+
+ if (IsWindow(focusHwnd)) {
+ SetFocus(focusHwnd);
+ }
+
+ return handled;
+}
+
+/* flash msg throttling helpers */
+
+// Flash has the unfortunate habit of flooding dispatch loops with custom
+// windowing events they use for timing. We throttle these by dropping the
+// delivery priority below any other event, including pending ipc io
+// notifications. We do this for both windowed and windowless controls.
+// Note flash's windowless msg window can last longer than our instance,
+// so we try to unhook when the window is destroyed and in NPP_Destroy.
+
+void
+PluginInstanceChild::UnhookWinlessFlashThrottle()
+{
+ // We may have already unhooked
+ if (!mWinlessThrottleOldWndProc)
+ return;
+
+ WNDPROC tmpProc = mWinlessThrottleOldWndProc;
+ mWinlessThrottleOldWndProc = nullptr;
+
+ NS_ASSERTION(mWinlessHiddenMsgHWND,
+ "Missing mWinlessHiddenMsgHWND w/subclass set??");
+
+ // reset the subclass
+ SetWindowLongPtr(mWinlessHiddenMsgHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(tmpProc));
+
+ // Remove our instance prop
+ RemoveProp(mWinlessHiddenMsgHWND, kFlashThrottleProperty);
+ mWinlessHiddenMsgHWND = nullptr;
+}
+
+// static
+LRESULT CALLBACK
+PluginInstanceChild::WinlessHiddenFlashWndProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
+ GetProp(hWnd, kFlashThrottleProperty));
+ if (!self) {
+ NS_NOTREACHED("Badness!");
+ return 0;
+ }
+
+ NS_ASSERTION(self->mWinlessThrottleOldWndProc,
+ "Missing subclass procedure!!");
+
+ // Throttle
+ if (message == WM_USER+1) {
+ self->FlashThrottleMessage(hWnd, message, wParam, lParam, false);
+ return 0;
+ }
+
+ // Unhook
+ if (message == WM_CLOSE || message == WM_NCDESTROY) {
+ WNDPROC tmpProc = self->mWinlessThrottleOldWndProc;
+ self->UnhookWinlessFlashThrottle();
+ LRESULT res = CallWindowProc(tmpProc, hWnd, message, wParam, lParam);
+ return res;
+ }
+
+ return CallWindowProc(self->mWinlessThrottleOldWndProc,
+ hWnd, message, wParam, lParam);
+}
+
+// Enumerate all thread windows looking for flash's hidden message window.
+// Once we find it, sub class it so we can throttle user msgs.
+// static
+BOOL CALLBACK
+PluginInstanceChild::EnumThreadWindowsCallback(HWND hWnd,
+ LPARAM aParam)
+{
+ PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(aParam);
+ if (!self) {
+ NS_NOTREACHED("Enum befuddled!");
+ return FALSE;
+ }
+
+ wchar_t className[64];
+ if (!GetClassNameW(hWnd, className, sizeof(className)/sizeof(char16_t)))
+ return TRUE;
+
+ if (!wcscmp(className, L"SWFlash_PlaceholderX")) {
+ WNDPROC oldWndProc =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
+ // Only set this if we haven't already.
+ if (oldWndProc != WinlessHiddenFlashWndProc) {
+ if (self->mWinlessThrottleOldWndProc) {
+ NS_WARNING("mWinlessThrottleWndProc already set???");
+ return FALSE;
+ }
+ // Subsclass and store self as a property
+ self->mWinlessHiddenMsgHWND = hWnd;
+ self->mWinlessThrottleOldWndProc =
+ reinterpret_cast<WNDPROC>(SetWindowLongPtr(hWnd, GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(WinlessHiddenFlashWndProc)));
+ SetProp(hWnd, kFlashThrottleProperty, self);
+ NS_ASSERTION(self->mWinlessThrottleOldWndProc,
+ "SetWindowLongPtr failed?!");
+ }
+ // Return no matter what once we find the right window.
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+PluginInstanceChild::SetupFlashMsgThrottle()
+{
+ if (mWindow.type == NPWindowTypeDrawable) {
+ // Search for the flash hidden message window and subclass it. Only
+ // search for flash windows belonging to our ui thread!
+ if (mWinlessThrottleOldWndProc)
+ return;
+ EnumThreadWindows(GetCurrentThreadId(), EnumThreadWindowsCallback,
+ reinterpret_cast<LPARAM>(this));
+ }
+ else {
+ // Already setup through quirks and the subclass.
+ return;
+ }
+}
+
+WNDPROC
+PluginInstanceChild::FlashThrottleAsyncMsg::GetProc()
+{
+ if (mInstance) {
+ return mWindowed ? mInstance->mPluginWndProc :
+ mInstance->mWinlessThrottleOldWndProc;
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+PluginInstanceChild::FlashThrottleAsyncMsg::Run()
+{
+ RemoveFromAsyncList();
+
+ // GetProc() checks mInstance, and pulls the procedure from
+ // PluginInstanceChild. We don't transport sub-class procedure
+ // ptrs around in FlashThrottleAsyncMsg msgs.
+ if (!GetProc())
+ return NS_OK;
+
+ // deliver the event to flash
+ CallWindowProc(GetProc(), GetWnd(), GetMsg(), GetWParam(), GetLParam());
+ return NS_OK;
+}
+
+void
+PluginInstanceChild::FlashThrottleMessage(HWND aWnd,
+ UINT aMsg,
+ WPARAM aWParam,
+ LPARAM aLParam,
+ bool isWindowed)
+{
+ // We reuse ChildAsyncCall so we get the cancelation work
+ // that's done in Destroy.
+ RefPtr<FlashThrottleAsyncMsg> task =
+ new FlashThrottleAsyncMsg(this, aWnd, aMsg, aWParam,
+ aLParam, isWindowed);
+ {
+ MutexAutoLock lock(mAsyncCallMutex);
+ mPendingAsyncCalls.AppendElement(task);
+ }
+ MessageLoop::current()->PostDelayedTask(task.forget(),
+ kFlashWMUSERMessageThrottleDelayMs);
+}
+
+#endif // OS_WIN
+
+bool
+PluginInstanceChild::AnswerSetPluginFocus()
+{
+ MOZ_LOG(GetPluginLog(), LogLevel::Debug, ("%s", FULLFUNCTION));
+
+#if defined(OS_WIN)
+ // Parent is letting us know the dom set focus to the plugin. Note,
+ // focus can change during transit in certain edge cases, for example
+ // when a button click brings up a full screen window. Since we send
+ // this in response to a WM_SETFOCUS event on our parent, the parent
+ // should have focus when we receive this. If not, ignore the call.
+ if (::GetFocus() == mPluginWindowHWND ||
+ ((GetQuirks() & QUIRK_SILVERLIGHT_FOCUS_CHECK_PARENT) &&
+ (::GetFocus() != mPluginParentHWND)))
+ return true;
+ ::SetFocus(mPluginWindowHWND);
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceChild::AnswerSetPluginFocus not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginInstanceChild::AnswerUpdateWindow()
+{
+ MOZ_LOG(GetPluginLog(), LogLevel::Debug, ("%s", FULLFUNCTION));
+
+#if defined(OS_WIN)
+ if (mPluginWindowHWND) {
+ RECT rect;
+ if (GetUpdateRect(GetParent(mPluginWindowHWND), &rect, FALSE)) {
+ ::InvalidateRect(mPluginWindowHWND, &rect, FALSE);
+ }
+ UpdateWindow(mPluginWindowHWND);
+ }
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceChild::AnswerUpdateWindow not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginInstanceChild::RecvNPP_DidComposite()
+{
+ if (mPluginIface->didComposite) {
+ mPluginIface->didComposite(GetNPP());
+ }
+ return true;
+}
+
+PPluginScriptableObjectChild*
+PluginInstanceChild::AllocPPluginScriptableObjectChild()
+{
+ AssertPluginThread();
+ return new PluginScriptableObjectChild(Proxy);
+}
+
+bool
+PluginInstanceChild::DeallocPPluginScriptableObjectChild(
+ PPluginScriptableObjectChild* aObject)
+{
+ AssertPluginThread();
+ delete aObject;
+ return true;
+}
+
+bool
+PluginInstanceChild::RecvPPluginScriptableObjectConstructor(
+ PPluginScriptableObjectChild* aActor)
+{
+ AssertPluginThread();
+
+ // This is only called in response to the parent process requesting the
+ // creation of an actor. This actor will represent an NPObject that is
+ // created by the browser and returned to the plugin.
+ PluginScriptableObjectChild* actor =
+ static_cast<PluginScriptableObjectChild*>(aActor);
+ NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!");
+
+ actor->InitializeProxy();
+ NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
+
+ return true;
+}
+
+bool
+PluginInstanceChild::RecvPBrowserStreamConstructor(
+ PBrowserStreamChild* aActor,
+ const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyChild* notifyData,
+ const nsCString& headers)
+{
+ return true;
+}
+
+NPError
+PluginInstanceChild::DoNPP_NewStream(BrowserStreamChild* actor,
+ const nsCString& mimeType,
+ const bool& seekable,
+ uint16_t* stype)
+{
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+ NPError rv = actor->StreamConstructed(mimeType, seekable, stype);
+ return rv;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_NewStream(PBrowserStreamChild* actor,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t* stype)
+{
+ *rv = DoNPP_NewStream(static_cast<BrowserStreamChild*>(actor), mimeType,
+ seekable, stype);
+ return true;
+}
+
+class NewStreamAsyncCall : public ChildAsyncCall
+{
+public:
+ NewStreamAsyncCall(PluginInstanceChild* aInstance,
+ BrowserStreamChild* aBrowserStreamChild,
+ const nsCString& aMimeType,
+ const bool aSeekable)
+ : ChildAsyncCall(aInstance, nullptr, nullptr)
+ , mBrowserStreamChild(aBrowserStreamChild)
+ , mMimeType(aMimeType)
+ , mSeekable(aSeekable)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ RemoveFromAsyncList();
+
+ uint16_t stype = NP_NORMAL;
+ NPError rv = mInstance->DoNPP_NewStream(mBrowserStreamChild, mMimeType,
+ mSeekable, &stype);
+ DebugOnly<bool> sendOk =
+ mBrowserStreamChild->SendAsyncNPP_NewStreamResult(rv, stype);
+ MOZ_ASSERT(sendOk);
+ return NS_OK;
+ }
+
+private:
+ BrowserStreamChild* mBrowserStreamChild;
+ const nsCString mMimeType;
+ const bool mSeekable;
+};
+
+bool
+PluginInstanceChild::RecvAsyncNPP_NewStream(PBrowserStreamChild* actor,
+ const nsCString& mimeType,
+ const bool& seekable)
+{
+ // Reusing ChildAsyncCall so that the task is cancelled properly on Destroy
+ BrowserStreamChild* child = static_cast<BrowserStreamChild*>(actor);
+ RefPtr<NewStreamAsyncCall> task =
+ new NewStreamAsyncCall(this, child, mimeType, seekable);
+ PostChildAsyncCall(task.forget());
+ return true;
+}
+
+PBrowserStreamChild*
+PluginInstanceChild::AllocPBrowserStreamChild(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyChild* notifyData,
+ const nsCString& headers)
+{
+ AssertPluginThread();
+ return new BrowserStreamChild(this, url, length, lastmodified,
+ static_cast<StreamNotifyChild*>(notifyData),
+ headers);
+}
+
+bool
+PluginInstanceChild::DeallocPBrowserStreamChild(PBrowserStreamChild* stream)
+{
+ AssertPluginThread();
+ delete stream;
+ return true;
+}
+
+PPluginStreamChild*
+PluginInstanceChild::AllocPPluginStreamChild(const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result)
+{
+ NS_RUNTIMEABORT("not callable");
+ return nullptr;
+}
+
+bool
+PluginInstanceChild::DeallocPPluginStreamChild(PPluginStreamChild* stream)
+{
+ AssertPluginThread();
+ delete stream;
+ return true;
+}
+
+PStreamNotifyChild*
+PluginInstanceChild::AllocPStreamNotifyChild(const nsCString& url,
+ const nsCString& target,
+ const bool& post,
+ const nsCString& buffer,
+ const bool& file,
+ NPError* result)
+{
+ AssertPluginThread();
+ NS_RUNTIMEABORT("not reached");
+ return nullptr;
+}
+
+void
+StreamNotifyChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (AncestorDeletion == why && mBrowserStream) {
+ NS_ERROR("Pending NPP_URLNotify not called when closing an instance.");
+
+ // reclaim responsibility for deleting ourself
+ mBrowserStream->mStreamNotify = nullptr;
+ mBrowserStream = nullptr;
+ }
+}
+
+void
+StreamNotifyChild::SetAssociatedStream(BrowserStreamChild* bs)
+{
+ NS_ASSERTION(!mBrowserStream, "Two streams for one streamnotify?");
+
+ mBrowserStream = bs;
+}
+
+bool
+StreamNotifyChild::Recv__delete__(const NPReason& reason)
+{
+ AssertPluginThread();
+
+ if (mBrowserStream)
+ mBrowserStream->NotifyPending();
+ else
+ NPP_URLNotify(reason);
+
+ return true;
+}
+
+bool
+StreamNotifyChild::RecvRedirectNotify(const nsCString& url, const int32_t& status)
+{
+ // NPP_URLRedirectNotify requires a non-null closure. Since core logic
+ // assumes that all out-of-process notify streams have non-null closure
+ // data it will assume that the plugin was notified at this point and
+ // expect a response otherwise the redirect will hang indefinitely.
+ if (!mClosure) {
+ SendRedirectNotifyResponse(false);
+ }
+
+ PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(Manager());
+ if (instance->mPluginIface->urlredirectnotify)
+ instance->mPluginIface->urlredirectnotify(instance->GetNPP(), url.get(), status, mClosure);
+
+ return true;
+}
+
+void
+StreamNotifyChild::NPP_URLNotify(NPReason reason)
+{
+ PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(Manager());
+
+ if (mClosure)
+ instance->mPluginIface->urlnotify(instance->GetNPP(), mURL.get(),
+ reason, mClosure);
+}
+
+bool
+PluginInstanceChild::DeallocPStreamNotifyChild(PStreamNotifyChild* notifyData)
+{
+ AssertPluginThread();
+
+ if (!static_cast<StreamNotifyChild*>(notifyData)->mBrowserStream)
+ delete notifyData;
+ return true;
+}
+
+PluginScriptableObjectChild*
+PluginInstanceChild::GetActorForNPObject(NPObject* aObject)
+{
+ AssertPluginThread();
+ NS_ASSERTION(aObject, "Null pointer!");
+
+ if (aObject->_class == PluginScriptableObjectChild::GetClass()) {
+ // One of ours! It's a browser-provided object.
+ ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
+ NS_ASSERTION(object->parent, "Null actor!");
+ return object->parent;
+ }
+
+ PluginScriptableObjectChild* actor =
+ PluginScriptableObjectChild::GetActorForNPObject(aObject);
+ if (actor) {
+ // Plugin-provided object that we've previously wrapped.
+ return actor;
+ }
+
+ actor = new PluginScriptableObjectChild(LocalObject);
+ if (!SendPPluginScriptableObjectConstructor(actor)) {
+ NS_ERROR("Failed to send constructor message!");
+ return nullptr;
+ }
+
+ actor->InitializeLocal(aObject);
+ return actor;
+}
+
+NPError
+PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
+ NPStream** aStream)
+{
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ PluginStreamChild* ps = new PluginStreamChild();
+
+ NPError result;
+ CallPPluginStreamConstructor(ps, nsDependentCString(aMIMEType),
+ NullableString(aWindow), &result);
+ if (NPERR_NO_ERROR != result) {
+ *aStream = nullptr;
+ PPluginStreamChild::Call__delete__(ps, NPERR_GENERIC_ERROR, true);
+ return result;
+ }
+
+ *aStream = &ps->mStream;
+ return NPERR_NO_ERROR;
+}
+
+void
+PluginInstanceChild::NPN_URLRedirectResponse(void* notifyData, NPBool allow)
+{
+ if (!notifyData) {
+ return;
+ }
+
+ InfallibleTArray<PStreamNotifyChild*> notifyStreams;
+ ManagedPStreamNotifyChild(notifyStreams);
+ uint32_t notifyStreamCount = notifyStreams.Length();
+ for (uint32_t i = 0; i < notifyStreamCount; i++) {
+ StreamNotifyChild* sn = static_cast<StreamNotifyChild*>(notifyStreams[i]);
+ if (sn->mClosure == notifyData) {
+ sn->SendRedirectNotifyResponse(static_cast<bool>(allow));
+ return;
+ }
+ }
+ NS_ASSERTION(false, "Couldn't find stream for redirect response!");
+}
+
+bool
+PluginInstanceChild::IsUsingDirectDrawing()
+{
+ return IsDrawingModelDirect(mDrawingModel);
+}
+
+PluginInstanceChild::DirectBitmap::DirectBitmap(PluginInstanceChild* aOwner, const Shmem& shmem,
+ const IntSize& size, uint32_t stride, SurfaceFormat format)
+ : mOwner(aOwner),
+ mShmem(shmem),
+ mFormat(format),
+ mSize(size),
+ mStride(stride)
+{
+}
+
+PluginInstanceChild::DirectBitmap::~DirectBitmap()
+{
+ mOwner->DeallocShmem(mShmem);
+}
+
+static inline SurfaceFormat
+NPImageFormatToSurfaceFormat(NPImageFormat aFormat)
+{
+ switch (aFormat) {
+ case NPImageFormatBGRA32:
+ return SurfaceFormat::B8G8R8A8;
+ case NPImageFormatBGRX32:
+ return SurfaceFormat::B8G8R8X8;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown NPImageFormat");
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+static inline gfx::IntRect
+NPRectToIntRect(const NPRect& in)
+{
+ return IntRect(in.left, in.top, in.right - in.left, in.bottom - in.top);
+}
+
+NPError
+PluginInstanceChild::NPN_InitAsyncSurface(NPSize *size, NPImageFormat format,
+ void *initData, NPAsyncSurface *surface)
+{
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ if (!IsUsingDirectDrawing()) {
+ return NPERR_INVALID_PARAM;
+ }
+ if (format != NPImageFormatBGRA32 && format != NPImageFormatBGRX32) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ PodZero(surface);
+
+ // NPAPI guarantees that the SetCurrentAsyncSurface call will release the
+ // previous surface if it was different. However, no functionality exists
+ // within content to synchronize a non-shadow-layers transaction with the
+ // compositor.
+ //
+ // To get around this, we allocate two surfaces: a child copy, which we
+ // hand off to the plugin, and a parent copy, which we will hand off to
+ // the compositor. Each call to SetCurrentAsyncSurface will copy the
+ // invalid region from the child surface to its parent.
+ switch (mDrawingModel) {
+ case NPDrawingModelAsyncBitmapSurface: {
+ // Validate that the caller does not expect initial data to be set.
+ if (initData) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ // Validate that we're not double-allocating a surface.
+ RefPtr<DirectBitmap> holder;
+ if (mDirectBitmaps.Get(surface, getter_AddRefs(holder))) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ SurfaceFormat mozformat = NPImageFormatToSurfaceFormat(format);
+ int32_t bytesPerPixel = BytesPerPixel(mozformat);
+
+ if (size->width <= 0 || size->height <= 0) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ CheckedInt<uint32_t> nbytes = SafeBytesForBitmap(size->width, size->height, bytesPerPixel);
+ if (!nbytes.isValid()) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ Shmem shmem;
+ if (!AllocUnsafeShmem(nbytes.value(), SharedMemory::TYPE_BASIC, &shmem)) {
+ return NPERR_OUT_OF_MEMORY_ERROR;
+ }
+ MOZ_ASSERT(shmem.Size<uint8_t>() == nbytes.value());
+
+ surface->version = 0;
+ surface->size = *size;
+ surface->format = format;
+ surface->bitmap.data = shmem.get<unsigned char>();
+ surface->bitmap.stride = size->width * bytesPerPixel;
+
+ // Hold the shmem alive until Finalize() is called or this actor dies.
+ holder = new DirectBitmap(this, shmem,
+ IntSize(size->width, size->height),
+ surface->bitmap.stride, mozformat);
+ mDirectBitmaps.Put(surface, holder);
+ return NPERR_NO_ERROR;
+ }
+#if defined(XP_WIN)
+ case NPDrawingModelAsyncWindowsDXGISurface: {
+ // Validate that the caller does not expect initial data to be set.
+ if (initData) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ // Validate that we're not double-allocating a surface.
+ WindowsHandle handle = 0;
+ if (mDxgiSurfaces.Get(surface, &handle)) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ NPError error = NPERR_NO_ERROR;
+ SurfaceFormat mozformat = NPImageFormatToSurfaceFormat(format);
+ if (!SendInitDXGISurface(mozformat,
+ IntSize(size->width, size->height),
+ &handle,
+ &error))
+ {
+ return NPERR_GENERIC_ERROR;
+ }
+ if (error != NPERR_NO_ERROR) {
+ return error;
+ }
+
+ surface->version = 0;
+ surface->size = *size;
+ surface->format = format;
+ surface->sharedHandle = reinterpret_cast<HANDLE>(handle);
+
+ mDxgiSurfaces.Put(surface, handle);
+ return NPERR_NO_ERROR;
+ }
+#endif
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown drawing model");
+ }
+
+ return NPERR_INVALID_PARAM;
+}
+
+NPError
+PluginInstanceChild::NPN_FinalizeAsyncSurface(NPAsyncSurface *surface)
+{
+ AssertPluginThread();
+
+ if (!IsUsingDirectDrawing()) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // The API forbids this. If it becomes a problem we can revoke the current
+ // surface instead.
+ MOZ_ASSERT(!surface || mCurrentDirectSurface != surface);
+
+ switch (mDrawingModel) {
+ case NPDrawingModelAsyncBitmapSurface: {
+ RefPtr<DirectBitmap> bitmap;
+ if (!mDirectBitmaps.Get(surface, getter_AddRefs(bitmap))) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ PodZero(surface);
+ mDirectBitmaps.Remove(surface);
+ return NPERR_NO_ERROR;
+ }
+#if defined(XP_WIN)
+ case NPDrawingModelAsyncWindowsDXGISurface: {
+ WindowsHandle handle;
+ if (!mDxgiSurfaces.Get(surface, &handle)) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ SendFinalizeDXGISurface(handle);
+ mDxgiSurfaces.Remove(surface);
+ return NPERR_NO_ERROR;
+ }
+#endif
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown drawing model");
+ }
+
+ return NPERR_INVALID_PARAM;
+}
+
+void
+PluginInstanceChild::NPN_SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed)
+{
+ AssertPluginThread();
+
+ if (!IsUsingDirectDrawing()) {
+ return;
+ }
+
+ mCurrentDirectSurface = surface;
+
+ if (!surface) {
+ SendRevokeCurrentDirectSurface();
+ return;
+ }
+
+ switch (mDrawingModel) {
+ case NPDrawingModelAsyncBitmapSurface: {
+ RefPtr<DirectBitmap> bitmap;
+ if (!mDirectBitmaps.Get(surface, getter_AddRefs(bitmap))) {
+ return;
+ }
+
+ IntRect dirty = changed
+ ? NPRectToIntRect(*changed)
+ : IntRect(IntPoint(0, 0), bitmap->mSize);
+
+ // Need a holder since IPDL zaps the object for mysterious reasons.
+ Shmem shmemHolder = bitmap->mShmem;
+ SendShowDirectBitmap(shmemHolder, bitmap->mFormat, bitmap->mStride, bitmap->mSize, dirty);
+ break;
+ }
+#if defined(XP_WIN)
+ case NPDrawingModelAsyncWindowsDXGISurface: {
+ WindowsHandle handle;
+ if (!mDxgiSurfaces.Get(surface, &handle)) {
+ return;
+ }
+
+ IntRect dirty = changed
+ ? NPRectToIntRect(*changed)
+ : IntRect(IntPoint(0, 0), IntSize(surface->size.width, surface->size.height));
+
+ SendShowDirectDXGISurface(handle, dirty);
+ break;
+ }
+#endif
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown drawing model");
+ }
+}
+
+void
+PluginInstanceChild::DoAsyncRedraw()
+{
+ {
+ MutexAutoLock autoLock(mAsyncInvalidateMutex);
+ mAsyncInvalidateTask = nullptr;
+ }
+
+ SendRedrawPlugin();
+}
+
+bool
+PluginInstanceChild::RecvAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
+ const NPRemoteWindow& aWindow)
+{
+ AssertPluginThread();
+
+ AutoStackHelper guard(this);
+ NS_ASSERTION(!aWindow.window, "Remote window should be null.");
+
+ if (mCurrentAsyncSetWindowTask) {
+ mCurrentAsyncSetWindowTask->Cancel();
+ mCurrentAsyncSetWindowTask = nullptr;
+ }
+
+ // We shouldn't process this now because it may be received within a nested
+ // RPC call, and both Flash and Java don't expect to receive setwindow calls
+ // at arbitrary times.
+ mCurrentAsyncSetWindowTask =
+ NewNonOwningCancelableRunnableMethod<gfxSurfaceType, NPRemoteWindow, bool>
+ (this, &PluginInstanceChild::DoAsyncSetWindow, aSurfaceType, aWindow, true);
+ RefPtr<Runnable> addrefedTask = mCurrentAsyncSetWindowTask;
+ MessageLoop::current()->PostTask(addrefedTask.forget());
+
+ return true;
+}
+
+void
+PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
+ const NPRemoteWindow& aWindow,
+ bool aIsAsync)
+{
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceChild][%p] AsyncSetWindow to <x=%d,y=%d, w=%d,h=%d>",
+ this, aWindow.x, aWindow.y, aWindow.width, aWindow.height));
+
+ AssertPluginThread();
+ NS_ASSERTION(!aWindow.window, "Remote window should be null.");
+ NS_ASSERTION(!mPendingPluginCall, "Can't do SetWindow during plugin call!");
+
+ if (aIsAsync) {
+ if (!mCurrentAsyncSetWindowTask) {
+ return;
+ }
+ mCurrentAsyncSetWindowTask = nullptr;
+ }
+
+ mWindow.window = nullptr;
+ if (mWindow.width != aWindow.width || mWindow.height != aWindow.height ||
+ mWindow.clipRect.top != aWindow.clipRect.top ||
+ mWindow.clipRect.left != aWindow.clipRect.left ||
+ mWindow.clipRect.bottom != aWindow.clipRect.bottom ||
+ mWindow.clipRect.right != aWindow.clipRect.right)
+ mAccumulatedInvalidRect = nsIntRect(0, 0, aWindow.width, aWindow.height);
+
+ mWindow.x = aWindow.x;
+ mWindow.y = aWindow.y;
+ mWindow.width = aWindow.width;
+ mWindow.height = aWindow.height;
+ mWindow.clipRect = aWindow.clipRect;
+ mWindow.type = aWindow.type;
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ mContentsScaleFactor = aWindow.contentsScaleFactor;
+#endif
+
+ if (GetQuirks() & QUIRK_SILVERLIGHT_DEFAULT_TRANSPARENT)
+ mIsTransparent = true;
+
+ mLayersRendering = true;
+ mSurfaceType = aSurfaceType;
+ UpdateWindowAttributes(true);
+
+#ifdef XP_WIN
+ if (GetQuirks() & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)
+ SetupFlashMsgThrottle();
+#endif
+
+ if (!mAccumulatedInvalidRect.IsEmpty()) {
+ AsyncShowPluginFrame();
+ }
+}
+
+bool
+PluginInstanceChild::CreateOptSurface(void)
+{
+ MOZ_ASSERT(mSurfaceType != gfxSurfaceType::Max,
+ "Need a valid surface type here");
+ NS_ASSERTION(!mCurrentSurface, "mCurrentSurfaceActor can get out of sync.");
+
+ // Use an opaque surface unless we're transparent and *don't* have
+ // a background to source from.
+ gfxImageFormat format =
+ (mIsTransparent && !mBackground) ? SurfaceFormat::A8R8G8B8_UINT32 :
+ SurfaceFormat::X8R8G8B8_UINT32;
+
+#ifdef MOZ_X11
+ Display* dpy = mWsInfo.display;
+ Screen* screen = DefaultScreenOfDisplay(dpy);
+ if (format == SurfaceFormat::X8R8G8B8_UINT32 &&
+ DefaultDepth(dpy, DefaultScreen(dpy)) == 16) {
+ format = SurfaceFormat::R5G6B5_UINT16;
+ }
+
+ if (mSurfaceType == gfxSurfaceType::Xlib) {
+ if (!mIsTransparent || mBackground) {
+ Visual* defaultVisual = DefaultVisualOfScreen(screen);
+ mCurrentSurface =
+ gfxXlibSurface::Create(screen, defaultVisual,
+ IntSize(mWindow.width, mWindow.height));
+ return mCurrentSurface != nullptr;
+ }
+
+ XRenderPictFormat* xfmt = XRenderFindStandardFormat(dpy, PictStandardARGB32);
+ if (!xfmt) {
+ NS_ERROR("Need X falback surface, but FindRenderFormat failed");
+ return false;
+ }
+ mCurrentSurface =
+ gfxXlibSurface::Create(screen, xfmt,
+ IntSize(mWindow.width, mWindow.height));
+ return mCurrentSurface != nullptr;
+ }
+#endif
+
+#ifdef XP_WIN
+ if (mSurfaceType == gfxSurfaceType::Win32) {
+ bool willHaveTransparentPixels = mIsTransparent && !mBackground;
+
+ SharedDIBSurface* s = new SharedDIBSurface();
+ if (!s->Create(reinterpret_cast<HDC>(mWindow.window),
+ mWindow.width, mWindow.height,
+ willHaveTransparentPixels))
+ return false;
+
+ mCurrentSurface = s;
+ return true;
+ }
+
+ NS_RUNTIMEABORT("Shared-memory drawing not expected on Windows.");
+#endif
+
+ // Make common shmem implementation working for any platform
+ mCurrentSurface =
+ gfxSharedImageSurface::CreateUnsafe(this, IntSize(mWindow.width, mWindow.height), format);
+ return !!mCurrentSurface;
+}
+
+bool
+PluginInstanceChild::MaybeCreatePlatformHelperSurface(void)
+{
+ if (!mCurrentSurface) {
+ NS_ERROR("Cannot create helper surface without mCurrentSurface");
+ return false;
+ }
+
+#ifdef MOZ_X11
+ bool supportNonDefaultVisual = false;
+ Screen* screen = DefaultScreenOfDisplay(mWsInfo.display);
+ Visual* defaultVisual = DefaultVisualOfScreen(screen);
+ Visual* visual = nullptr;
+ Colormap colormap = 0;
+ mDoAlphaExtraction = false;
+ bool createHelperSurface = false;
+
+ if (mCurrentSurface->GetType() == gfxSurfaceType::Xlib) {
+ static_cast<gfxXlibSurface*>(mCurrentSurface.get())->
+ GetColormapAndVisual(&colormap, &visual);
+ // Create helper surface if layer surface visual not same as default
+ // and we don't support non-default visual rendering
+ if (!visual || (defaultVisual != visual && !supportNonDefaultVisual)) {
+ createHelperSurface = true;
+ visual = defaultVisual;
+ mDoAlphaExtraction = mIsTransparent;
+ }
+ } else if (mCurrentSurface->GetType() == gfxSurfaceType::Image) {
+ // For image layer surface we should always create helper surface
+ createHelperSurface = true;
+ // Check if we can create helper surface with non-default visual
+ visual = gfxXlibSurface::FindVisual(screen,
+ static_cast<gfxImageSurface*>(mCurrentSurface.get())->Format());
+ if (!visual || (defaultVisual != visual && !supportNonDefaultVisual)) {
+ visual = defaultVisual;
+ mDoAlphaExtraction = mIsTransparent;
+ }
+ }
+
+ if (createHelperSurface) {
+ if (!visual) {
+ NS_ERROR("Need X falback surface, but visual failed");
+ return false;
+ }
+ mHelperSurface =
+ gfxXlibSurface::Create(screen, visual,
+ mCurrentSurface->GetSize());
+ if (!mHelperSurface) {
+ NS_WARNING("Fail to create create helper surface");
+ return false;
+ }
+ }
+#elif defined(XP_WIN)
+ mDoAlphaExtraction = mIsTransparent && !mBackground;
+#endif
+
+ return true;
+}
+
+bool
+PluginInstanceChild::EnsureCurrentBuffer(void)
+{
+#ifndef XP_DARWIN
+ nsIntRect toInvalidate(0, 0, 0, 0);
+ IntSize winSize = IntSize(mWindow.width, mWindow.height);
+
+ if (mBackground && mBackground->GetSize() != winSize) {
+ // It would be nice to keep the old background here, but doing
+ // so can lead to cases in which we permanently keep the old
+ // background size.
+ mBackground = nullptr;
+ toInvalidate.UnionRect(toInvalidate,
+ nsIntRect(0, 0, winSize.width, winSize.height));
+ }
+
+ if (mCurrentSurface) {
+ IntSize surfSize = mCurrentSurface->GetSize();
+ if (winSize != surfSize ||
+ (mBackground && !CanPaintOnBackground()) ||
+ (mBackground &&
+ gfxContentType::COLOR != mCurrentSurface->GetContentType()) ||
+ (!mBackground && mIsTransparent &&
+ gfxContentType::COLOR == mCurrentSurface->GetContentType())) {
+ // Don't try to use an old, invalid DC.
+ mWindow.window = nullptr;
+ ClearCurrentSurface();
+ toInvalidate.UnionRect(toInvalidate,
+ nsIntRect(0, 0, winSize.width, winSize.height));
+ }
+ }
+
+ mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect, toInvalidate);
+
+ if (mCurrentSurface) {
+ return true;
+ }
+
+ if (!CreateOptSurface()) {
+ NS_ERROR("Cannot create optimized surface");
+ return false;
+ }
+
+ if (!MaybeCreatePlatformHelperSurface()) {
+ NS_ERROR("Cannot create helper surface");
+ return false;
+ }
+
+ return true;
+#elif defined(XP_MACOSX)
+
+ if (!mDoubleBufferCARenderer.HasCALayer()) {
+ void *caLayer = nullptr;
+ if (mDrawingModel == NPDrawingModelCoreGraphics) {
+ if (!mCGLayer) {
+ caLayer = mozilla::plugins::PluginUtilsOSX::GetCGLayer(CallCGDraw,
+ this,
+ mContentsScaleFactor);
+
+ if (!caLayer) {
+ PLUGIN_LOG_DEBUG(("GetCGLayer failed."));
+ return false;
+ }
+ }
+ mCGLayer = caLayer;
+ } else {
+ NPError result = mPluginIface->getvalue(GetNPP(),
+ NPPVpluginCoreAnimationLayer,
+ &caLayer);
+ if (result != NPERR_NO_ERROR || !caLayer) {
+ PLUGIN_LOG_DEBUG(("Plugin requested CoreAnimation but did not "
+ "provide CALayer."));
+ return false;
+ }
+ }
+ mDoubleBufferCARenderer.SetCALayer(caLayer);
+ }
+
+ if (mDoubleBufferCARenderer.HasFrontSurface() &&
+ (mDoubleBufferCARenderer.GetFrontSurfaceWidth() != mWindow.width ||
+ mDoubleBufferCARenderer.GetFrontSurfaceHeight() != mWindow.height ||
+ mDoubleBufferCARenderer.GetContentsScaleFactor() != mContentsScaleFactor)) {
+ mDoubleBufferCARenderer.ClearFrontSurface();
+ }
+
+ if (!mDoubleBufferCARenderer.HasFrontSurface()) {
+ bool allocSurface = mDoubleBufferCARenderer.InitFrontSurface(
+ mWindow.width, mWindow.height, mContentsScaleFactor,
+ GetQuirks() & QUIRK_ALLOW_OFFLINE_RENDERER ?
+ ALLOW_OFFLINE_RENDERER : DISALLOW_OFFLINE_RENDERER);
+ if (!allocSurface) {
+ PLUGIN_LOG_DEBUG(("Fail to allocate front IOSurface"));
+ return false;
+ }
+
+ if (mPluginIface->setwindow)
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+
+ nsIntRect toInvalidate(0, 0, mWindow.width, mWindow.height);
+ mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect, toInvalidate);
+ }
+#endif
+ return true;
+}
+
+void
+PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow)
+{
+#if defined(MOZ_X11) || defined(XP_WIN)
+ RefPtr<gfxASurface> curSurface = mHelperSurface ? mHelperSurface : mCurrentSurface;
+#endif // Only used within MOZ_X11 or XP_WIN blocks. Unused variable otherwise
+ bool needWindowUpdate = aForceSetWindow;
+#ifdef MOZ_X11
+ Visual* visual = nullptr;
+ Colormap colormap = 0;
+ if (curSurface && curSurface->GetType() == gfxSurfaceType::Xlib) {
+ static_cast<gfxXlibSurface*>(curSurface.get())->
+ GetColormapAndVisual(&colormap, &visual);
+ if (visual != mWsInfo.visual || colormap != mWsInfo.colormap) {
+ mWsInfo.visual = visual;
+ mWsInfo.colormap = colormap;
+ needWindowUpdate = true;
+ }
+ }
+#endif // MOZ_X11
+#ifdef XP_WIN
+ HDC dc = nullptr;
+
+ if (curSurface) {
+ if (!SharedDIBSurface::IsSharedDIBSurface(curSurface))
+ NS_RUNTIMEABORT("Expected SharedDIBSurface!");
+
+ SharedDIBSurface* dibsurf = static_cast<SharedDIBSurface*>(curSurface.get());
+ dc = dibsurf->GetHDC();
+ }
+ if (mWindow.window != dc) {
+ mWindow.window = dc;
+ needWindowUpdate = true;
+ }
+#endif // XP_WIN
+
+ if (!needWindowUpdate) {
+ return;
+ }
+
+#ifndef XP_MACOSX
+ // Adjusting the window isn't needed for OSX
+#ifndef XP_WIN
+ // On Windows, we translate the device context, in order for the window
+ // origin to be correct.
+ mWindow.x = mWindow.y = 0;
+#endif
+
+ if (IsVisible()) {
+ // The clip rect is relative to drawable top-left.
+ nsIntRect clipRect;
+
+ // Don't ask the plugin to draw outside the drawable. The clip rect
+ // is in plugin coordinates, not window coordinates.
+ // This also ensures that the unsigned clip rectangle offsets won't be -ve.
+ clipRect.SetRect(0, 0, mWindow.width, mWindow.height);
+
+ mWindow.clipRect.left = 0;
+ mWindow.clipRect.top = 0;
+ mWindow.clipRect.right = clipRect.XMost();
+ mWindow.clipRect.bottom = clipRect.YMost();
+ }
+#endif // XP_MACOSX
+
+#ifdef XP_WIN
+ // Windowless plugins on Windows need a WM_WINDOWPOSCHANGED event to update
+ // their location... or at least Flash does: Silverlight uses the
+ // window.x/y passed to NPP_SetWindow
+
+ if (mPluginIface->event) {
+ WINDOWPOS winpos = {
+ 0, 0,
+ mWindow.x, mWindow.y,
+ mWindow.width, mWindow.height,
+ 0
+ };
+ NPEvent pluginEvent = {
+ WM_WINDOWPOSCHANGED, 0,
+ (LPARAM) &winpos
+ };
+ mPluginIface->event(&mData, &pluginEvent);
+ }
+#endif
+
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceChild][%p] UpdateWindow w=<x=%d,y=%d, w=%d,h=%d>, clip=<l=%d,t=%d,r=%d,b=%d>",
+ this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
+ mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom));
+
+ if (mPluginIface->setwindow) {
+ mPluginIface->setwindow(&mData, &mWindow);
+ }
+}
+
+void
+PluginInstanceChild::PaintRectToPlatformSurface(const nsIntRect& aRect,
+ gfxASurface* aSurface)
+{
+ UpdateWindowAttributes();
+
+ // We should not send an async surface if we're using direct rendering.
+ MOZ_ASSERT(!IsUsingDirectDrawing());
+
+#ifdef MOZ_X11
+ {
+ NS_ASSERTION(aSurface->GetType() == gfxSurfaceType::Xlib,
+ "Non supported platform surface type");
+
+ NPEvent pluginEvent;
+ XGraphicsExposeEvent& exposeEvent = pluginEvent.xgraphicsexpose;
+ exposeEvent.type = GraphicsExpose;
+ exposeEvent.display = mWsInfo.display;
+ exposeEvent.drawable = static_cast<gfxXlibSurface*>(aSurface)->XDrawable();
+ exposeEvent.x = aRect.x;
+ exposeEvent.y = aRect.y;
+ exposeEvent.width = aRect.width;
+ exposeEvent.height = aRect.height;
+ exposeEvent.count = 0;
+ // information not set:
+ exposeEvent.serial = 0;
+ exposeEvent.send_event = False;
+ exposeEvent.major_code = 0;
+ exposeEvent.minor_code = 0;
+ mPluginIface->event(&mData, reinterpret_cast<void*>(&exposeEvent));
+ }
+#elif defined(XP_WIN)
+ NS_ASSERTION(SharedDIBSurface::IsSharedDIBSurface(aSurface),
+ "Expected (SharedDIB) image surface.");
+
+ // This rect is in the window coordinate space. aRect is in the plugin
+ // coordinate space.
+ RECT rect = {
+ mWindow.x + aRect.x,
+ mWindow.y + aRect.y,
+ mWindow.x + aRect.XMost(),
+ mWindow.y + aRect.YMost()
+ };
+ NPEvent paintEvent = {
+ WM_PAINT,
+ uintptr_t(mWindow.window),
+ uintptr_t(&rect)
+ };
+
+ ::SetViewportOrgEx((HDC) mWindow.window, -mWindow.x, -mWindow.y, nullptr);
+ ::SelectClipRgn((HDC) mWindow.window, nullptr);
+ ::IntersectClipRect((HDC) mWindow.window, rect.left, rect.top, rect.right, rect.bottom);
+ mPluginIface->event(&mData, reinterpret_cast<void*>(&paintEvent));
+#else
+ NS_RUNTIMEABORT("Surface type not implemented.");
+#endif
+}
+
+void
+PluginInstanceChild::PaintRectToSurface(const nsIntRect& aRect,
+ gfxASurface* aSurface,
+ const Color& aColor)
+{
+ // Render using temporary X surface, with copy to image surface
+ nsIntRect plPaintRect(aRect);
+ RefPtr<gfxASurface> renderSurface = aSurface;
+#ifdef MOZ_X11
+ if (mIsTransparent && (GetQuirks() & QUIRK_FLASH_EXPOSE_COORD_TRANSLATION)) {
+ // 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.), see bug 574583
+ plPaintRect.SetRect(0, 0, aRect.XMost(), aRect.YMost());
+ }
+ if (mHelperSurface) {
+ // On X11 we can paint to non Xlib surface only with HelperSurface
+ renderSurface = mHelperSurface;
+ }
+#endif
+
+ if (mIsTransparent && !CanPaintOnBackground()) {
+ RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(renderSurface);
+ gfx::Rect rect(plPaintRect.x, plPaintRect.y,
+ plPaintRect.width, plPaintRect.height);
+ // Moz2D treats OP_SOURCE operations as unbounded, so we need to
+ // clip to the rect that we want to fill:
+ dt->PushClipRect(rect);
+ dt->FillRect(rect, ColorPattern(aColor), // aColor is already a device color
+ DrawOptions(1.f, CompositionOp::OP_SOURCE));
+ dt->PopClip();
+ dt->Flush();
+ }
+
+ PaintRectToPlatformSurface(plPaintRect, renderSurface);
+
+ if (renderSurface != aSurface) {
+ RefPtr<DrawTarget> dt;
+ if (aSurface == mCurrentSurface &&
+ aSurface->GetType() == gfxSurfaceType::Image &&
+ aSurface->GetSurfaceFormat() == SurfaceFormat::B8G8R8X8) {
+ gfxImageSurface* imageSurface = static_cast<gfxImageSurface*>(aSurface);
+ // Bug 1196927 - Reinterpret target surface as BGRA to fill alpha with opaque.
+ // Certain backends (i.e. Skia) may not truly support BGRX formats, so they must
+ // be emulated by filling the alpha channel opaque as if it was BGRA data. Cairo
+ // leaves the alpha zeroed out for BGRX, so we cause Cairo to fill it as opaque
+ // by handling the copy target as a BGRA surface.
+ dt = Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ imageSurface->Data(),
+ imageSurface->GetSize(),
+ imageSurface->Stride(),
+ SurfaceFormat::B8G8R8A8);
+ } else {
+ // Copy helper surface content to target
+ dt = CreateDrawTargetForSurface(aSurface);
+ }
+ if (dt && dt->IsValid()) {
+ RefPtr<SourceSurface> surface =
+ gfxPlatform::GetSourceSurfaceForSurface(dt, renderSurface);
+ dt->CopySurface(surface, aRect, aRect.TopLeft());
+ } else {
+ gfxWarning() << "PluginInstanceChild::PaintRectToSurface failure";
+ }
+ }
+}
+
+void
+PluginInstanceChild::PaintRectWithAlphaExtraction(const nsIntRect& aRect,
+ gfxASurface* aSurface)
+{
+ MOZ_ASSERT(aSurface->GetContentType() == gfxContentType::COLOR_ALPHA,
+ "Refusing to pointlessly recover alpha");
+
+ nsIntRect rect(aRect);
+ // If |aSurface| can be used to paint and can have alpha values
+ // recovered directly to it, do that to save a tmp surface and
+ // copy.
+ bool useSurfaceSubimageForBlack = false;
+ if (gfxSurfaceType::Image == aSurface->GetType()) {
+ gfxImageSurface* surfaceAsImage =
+ static_cast<gfxImageSurface*>(aSurface);
+ useSurfaceSubimageForBlack =
+ (surfaceAsImage->Format() == SurfaceFormat::A8R8G8B8_UINT32);
+ // If we're going to use a subimage, nudge the rect so that we
+ // can use optimal alpha recovery. If we're not using a
+ // subimage, the temporaries should automatically get
+ // fast-path alpha recovery so we don't need to do anything.
+ if (useSurfaceSubimageForBlack) {
+ rect =
+ gfxAlphaRecovery::AlignRectForSubimageRecovery(aRect,
+ surfaceAsImage);
+ }
+ }
+
+ RefPtr<gfxImageSurface> whiteImage;
+ RefPtr<gfxImageSurface> blackImage;
+ gfxRect targetRect(rect.x, rect.y, rect.width, rect.height);
+ IntSize targetSize(rect.width, rect.height);
+ gfxPoint deviceOffset = -targetRect.TopLeft();
+
+ // We always use a temporary "white image"
+ whiteImage = new gfxImageSurface(targetSize, SurfaceFormat::X8R8G8B8_UINT32);
+ if (whiteImage->CairoStatus()) {
+ return;
+ }
+
+#ifdef XP_WIN
+ // On windows, we need an HDC and so can't paint directly to
+ // vanilla image surfaces. Bifurcate this painting code so that
+ // we don't accidentally attempt that.
+ if (!SharedDIBSurface::IsSharedDIBSurface(aSurface))
+ NS_RUNTIMEABORT("Expected SharedDIBSurface!");
+
+ // Paint the plugin directly onto the target, with a white
+ // background and copy the result
+ PaintRectToSurface(rect, aSurface, Color(1.f, 1.f, 1.f));
+ {
+ RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(whiteImage);
+ RefPtr<SourceSurface> surface =
+ gfxPlatform::GetSourceSurfaceForSurface(dt, aSurface);
+ dt->CopySurface(surface, rect, IntPoint());
+ }
+
+ // Paint the plugin directly onto the target, with a black
+ // background
+ PaintRectToSurface(rect, aSurface, Color(0.f, 0.f, 0.f));
+
+ // Don't copy the result, just extract a subimage so that we can
+ // recover alpha directly into the target
+ gfxImageSurface *image = static_cast<gfxImageSurface*>(aSurface);
+ blackImage = image->GetSubimage(targetRect);
+
+#else
+ // Paint onto white background
+ whiteImage->SetDeviceOffset(deviceOffset);
+ PaintRectToSurface(rect, whiteImage, Color(1.f, 1.f, 1.f));
+
+ if (useSurfaceSubimageForBlack) {
+ gfxImageSurface *surface = static_cast<gfxImageSurface*>(aSurface);
+ blackImage = surface->GetSubimage(targetRect);
+ } else {
+ blackImage = new gfxImageSurface(targetSize,
+ SurfaceFormat::A8R8G8B8_UINT32);
+ }
+
+ // Paint onto black background
+ blackImage->SetDeviceOffset(deviceOffset);
+ PaintRectToSurface(rect, blackImage, Color(0.f, 0.f, 0.f));
+#endif
+
+ MOZ_ASSERT(whiteImage && blackImage, "Didn't paint enough!");
+
+ // Extract alpha from black and white image and store to black
+ // image
+ if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) {
+ return;
+ }
+
+ // If we had to use a temporary black surface, copy the pixels
+ // with alpha back to the target
+ if (!useSurfaceSubimageForBlack) {
+ RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(aSurface);
+ RefPtr<SourceSurface> surface =
+ gfxPlatform::GetSourceSurfaceForSurface(dt, blackImage);
+ dt->CopySurface(surface,
+ IntRect(0, 0, rect.width, rect.height),
+ rect.TopLeft());
+ }
+}
+
+bool
+PluginInstanceChild::CanPaintOnBackground()
+{
+ return (mBackground &&
+ mCurrentSurface &&
+ mCurrentSurface->GetSize() == mBackground->GetSize());
+}
+
+bool
+PluginInstanceChild::ShowPluginFrame()
+{
+ // mLayersRendering can be false if we somehow get here without
+ // receiving AsyncSetWindow() first. mPendingPluginCall is our
+ // re-entrancy guard; we can't paint while nested inside another
+ // paint.
+ if (!mLayersRendering || mPendingPluginCall) {
+ return false;
+ }
+
+ // We should not attempt to asynchronously show the plugin if we're using
+ // direct rendering.
+ MOZ_ASSERT(!IsUsingDirectDrawing());
+
+ AutoRestore<bool> pending(mPendingPluginCall);
+ mPendingPluginCall = true;
+
+ bool temporarilyMakeVisible = !IsVisible() && !mHasPainted;
+ if (temporarilyMakeVisible && mWindow.width && mWindow.height) {
+ mWindow.clipRect.right = mWindow.width;
+ mWindow.clipRect.bottom = mWindow.height;
+ } else if (!IsVisible()) {
+ // If we're not visible, don't bother painting a <0,0,0,0>
+ // rect. If we're eventually made visible, the visibility
+ // change will invalidate our window.
+ ClearCurrentSurface();
+ return true;
+ }
+
+ if (!EnsureCurrentBuffer()) {
+ return false;
+ }
+
+#ifdef MOZ_WIDGET_COCOA
+ // We can't use the thebes code with CoreAnimation so we will
+ // take a different code path.
+ if (mDrawingModel == NPDrawingModelCoreAnimation ||
+ mDrawingModel == NPDrawingModelInvalidatingCoreAnimation ||
+ mDrawingModel == NPDrawingModelCoreGraphics) {
+
+ if (!IsVisible()) {
+ return true;
+ }
+
+ if (!mDoubleBufferCARenderer.HasFrontSurface()) {
+ NS_ERROR("CARenderer not initialized for rendering");
+ return false;
+ }
+
+ // Clear accRect here to be able to pass
+ // test_invalidate_during_plugin_paint test
+ nsIntRect rect = mAccumulatedInvalidRect;
+ mAccumulatedInvalidRect.SetEmpty();
+
+ // Fix up old invalidations that might have been made when our
+ // surface was a different size
+ rect.IntersectRect(rect,
+ nsIntRect(0, 0,
+ mDoubleBufferCARenderer.GetFrontSurfaceWidth(),
+ mDoubleBufferCARenderer.GetFrontSurfaceHeight()));
+
+ if (mDrawingModel == NPDrawingModelCoreGraphics) {
+ mozilla::plugins::PluginUtilsOSX::Repaint(mCGLayer, rect);
+ }
+
+ mDoubleBufferCARenderer.Render();
+
+ NPRect r = { (uint16_t)rect.y, (uint16_t)rect.x,
+ (uint16_t)rect.YMost(), (uint16_t)rect.XMost() };
+ SurfaceDescriptor currSurf;
+ currSurf = IOSurfaceDescriptor(mDoubleBufferCARenderer.GetFrontSurfaceID(),
+ mDoubleBufferCARenderer.GetContentsScaleFactor());
+
+ mHasPainted = true;
+
+ SurfaceDescriptor returnSurf;
+
+ if (!SendShow(r, currSurf, &returnSurf)) {
+ return false;
+ }
+
+ SwapSurfaces();
+ return true;
+ } else {
+ NS_ERROR("Unsupported drawing model for async layer rendering");
+ return false;
+ }
+#endif
+
+ NS_ASSERTION(mWindow.width == uint32_t(mWindow.clipRect.right - mWindow.clipRect.left) &&
+ mWindow.height == uint32_t(mWindow.clipRect.bottom - mWindow.clipRect.top),
+ "Clip rect should be same size as window when using layers");
+
+ // Clear accRect here to be able to pass
+ // test_invalidate_during_plugin_paint test
+ nsIntRect rect = mAccumulatedInvalidRect;
+ mAccumulatedInvalidRect.SetEmpty();
+
+ // Fix up old invalidations that might have been made when our
+ // surface was a different size
+ IntSize surfaceSize = mCurrentSurface->GetSize();
+ rect.IntersectRect(rect,
+ nsIntRect(0, 0, surfaceSize.width, surfaceSize.height));
+
+ if (!ReadbackDifferenceRect(rect)) {
+ // We couldn't read back the pixels that differ between the
+ // current surface and last, so we have to invalidate the
+ // entire window.
+ rect.SetRect(0, 0, mWindow.width, mWindow.height);
+ }
+
+ bool haveTransparentPixels =
+ gfxContentType::COLOR_ALPHA == mCurrentSurface->GetContentType();
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceChild][%p] Painting%s <x=%d,y=%d, w=%d,h=%d> on surface <w=%d,h=%d>",
+ this, haveTransparentPixels ? " with alpha" : "",
+ rect.x, rect.y, rect.width, rect.height,
+ mCurrentSurface->GetSize().width, mCurrentSurface->GetSize().height));
+
+ if (CanPaintOnBackground()) {
+ PLUGIN_LOG_DEBUG((" (on background)"));
+ // Source the background pixels ...
+ {
+ RefPtr<gfxASurface> surface =
+ mHelperSurface ? mHelperSurface : mCurrentSurface;
+ RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(surface);
+ RefPtr<SourceSurface> backgroundSurface =
+ gfxPlatform::GetSourceSurfaceForSurface(dt, mBackground);
+ dt->CopySurface(backgroundSurface, rect, rect.TopLeft());
+ }
+ // ... and hand off to the plugin
+ // BEWARE: mBackground may die during this call
+ PaintRectToSurface(rect, mCurrentSurface, Color());
+ } else if (!temporarilyMakeVisible && mDoAlphaExtraction) {
+ // We don't want to pay the expense of alpha extraction for
+ // phony paints.
+ PLUGIN_LOG_DEBUG((" (with alpha recovery)"));
+ PaintRectWithAlphaExtraction(rect, mCurrentSurface);
+ } else {
+ PLUGIN_LOG_DEBUG((" (onto opaque surface)"));
+
+ // If we're on a platform that needs helper surfaces for
+ // plugins, and we're forcing a throwaway paint of a
+ // wmode=transparent plugin, then make sure to use the helper
+ // surface here.
+ RefPtr<gfxASurface> target =
+ (temporarilyMakeVisible && mHelperSurface) ?
+ mHelperSurface : mCurrentSurface;
+
+ PaintRectToSurface(rect, target, Color());
+ }
+ mHasPainted = true;
+
+ if (temporarilyMakeVisible) {
+ mWindow.clipRect.right = mWindow.clipRect.bottom = 0;
+
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceChild][%p] Undoing temporary clipping w=<x=%d,y=%d, w=%d,h=%d>, clip=<l=%d,t=%d,r=%d,b=%d>",
+ this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
+ mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom));
+
+ if (mPluginIface->setwindow) {
+ mPluginIface->setwindow(&mData, &mWindow);
+ }
+
+ // Skip forwarding the results of the phony paint to the
+ // browser. We may have painted a transparent plugin using
+ // the opaque-plugin path, which can result in wrong pixels.
+ // We also don't want to pay the expense of forwarding the
+ // surface for plugins that might really be invisible.
+ mAccumulatedInvalidRect.SetRect(0, 0, mWindow.width, mWindow.height);
+ return true;
+ }
+
+ NPRect r = { (uint16_t)rect.y, (uint16_t)rect.x,
+ (uint16_t)rect.YMost(), (uint16_t)rect.XMost() };
+ SurfaceDescriptor currSurf;
+#ifdef MOZ_X11
+ if (mCurrentSurface->GetType() == gfxSurfaceType::Xlib) {
+ gfxXlibSurface *xsurf = static_cast<gfxXlibSurface*>(mCurrentSurface.get());
+ currSurf = SurfaceDescriptorX11(xsurf);
+ // Need to sync all pending x-paint requests
+ // before giving drawable to another process
+ XSync(mWsInfo.display, False);
+ } else
+#endif
+#ifdef XP_WIN
+ if (SharedDIBSurface::IsSharedDIBSurface(mCurrentSurface)) {
+ SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get());
+ if (!mCurrentSurfaceActor) {
+ base::SharedMemoryHandle handle = nullptr;
+ s->ShareToProcess(OtherPid(), &handle);
+
+ mCurrentSurfaceActor =
+ SendPPluginSurfaceConstructor(handle,
+ mCurrentSurface->GetSize(),
+ haveTransparentPixels);
+ }
+ currSurf = mCurrentSurfaceActor;
+ s->Flush();
+ } else
+#endif
+ if (gfxSharedImageSurface::IsSharedImage(mCurrentSurface)) {
+ currSurf = static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem();
+ } else {
+ NS_RUNTIMEABORT("Surface type is not remotable");
+ return false;
+ }
+
+ // Unused, except to possibly return a shmem to us
+ SurfaceDescriptor returnSurf;
+
+ if (!SendShow(r, currSurf, &returnSurf)) {
+ return false;
+ }
+
+ SwapSurfaces();
+ mSurfaceDifferenceRect = rect;
+ return true;
+}
+
+bool
+PluginInstanceChild::ReadbackDifferenceRect(const nsIntRect& rect)
+{
+ if (!mBackSurface)
+ return false;
+
+ // We can read safely from XSurface,SharedDIBSurface and Unsafe SharedMemory,
+ // because PluginHost is not able to modify that surface
+#if defined(MOZ_X11)
+ if (mBackSurface->GetType() != gfxSurfaceType::Xlib &&
+ !gfxSharedImageSurface::IsSharedImage(mBackSurface))
+ return false;
+#elif defined(XP_WIN)
+ if (!SharedDIBSurface::IsSharedDIBSurface(mBackSurface))
+ return false;
+#endif
+
+#if defined(MOZ_X11) || defined(XP_WIN)
+ if (mCurrentSurface->GetContentType() != mBackSurface->GetContentType())
+ return false;
+
+ if (mSurfaceDifferenceRect.IsEmpty())
+ return true;
+
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceChild][%p] Reading back part of <x=%d,y=%d, w=%d,h=%d>",
+ this, mSurfaceDifferenceRect.x, mSurfaceDifferenceRect.y,
+ mSurfaceDifferenceRect.width, mSurfaceDifferenceRect.height));
+
+ // Read back previous content
+ RefPtr<DrawTarget> dt = CreateDrawTargetForSurface(mCurrentSurface);
+ RefPtr<SourceSurface> source =
+ gfxPlatform::GetSourceSurfaceForSurface(dt, mBackSurface);
+ // Subtract from mSurfaceDifferenceRect area which is overlapping with rect
+ nsIntRegion result;
+ result.Sub(mSurfaceDifferenceRect, nsIntRegion(rect));
+ for (auto iter = result.RectIter(); !iter.Done(); iter.Next()) {
+ const nsIntRect& r = iter.Get();
+ dt->CopySurface(source, r, r.TopLeft());
+ }
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+void
+PluginInstanceChild::InvalidateRectDelayed(void)
+{
+ if (!mCurrentInvalidateTask) {
+ return;
+ }
+
+ mCurrentInvalidateTask = nullptr;
+ if (mAccumulatedInvalidRect.IsEmpty()) {
+ return;
+ }
+
+ if (!ShowPluginFrame()) {
+ AsyncShowPluginFrame();
+ }
+}
+
+void
+PluginInstanceChild::AsyncShowPluginFrame(void)
+{
+ if (mCurrentInvalidateTask) {
+ return;
+ }
+
+ // When the plugin is using direct surfaces to draw, it is not driving
+ // paints via paint events - it will drive painting via its own events
+ // and/or DidComposite callbacks.
+ if (IsUsingDirectDrawing()) {
+ return;
+ }
+
+ mCurrentInvalidateTask =
+ NewNonOwningCancelableRunnableMethod(this, &PluginInstanceChild::InvalidateRectDelayed);
+ RefPtr<Runnable> addrefedTask = mCurrentInvalidateTask;
+ MessageLoop::current()->PostTask(addrefedTask.forget());
+}
+
+void
+PluginInstanceChild::InvalidateRect(NPRect* aInvalidRect)
+{
+ NS_ASSERTION(aInvalidRect, "Null pointer!");
+
+#ifdef OS_WIN
+ // Invalidate and draw locally for windowed plugins.
+ if (mWindow.type == NPWindowTypeWindow) {
+ NS_ASSERTION(IsWindow(mPluginWindowHWND), "Bad window?!");
+ RECT rect = { aInvalidRect->left, aInvalidRect->top,
+ aInvalidRect->right, aInvalidRect->bottom };
+ ::InvalidateRect(mPluginWindowHWND, &rect, FALSE);
+ return;
+ }
+#endif
+
+ if (IsUsingDirectDrawing()) {
+ NS_ASSERTION(false, "Should not call InvalidateRect() in direct surface mode!");
+ return;
+ }
+
+ if (mLayersRendering) {
+ nsIntRect r(aInvalidRect->left, aInvalidRect->top,
+ aInvalidRect->right - aInvalidRect->left,
+ aInvalidRect->bottom - aInvalidRect->top);
+
+ mAccumulatedInvalidRect.UnionRect(r, mAccumulatedInvalidRect);
+ // If we are able to paint and invalidate sent, then reset
+ // accumulated rectangle
+ AsyncShowPluginFrame();
+ return;
+ }
+
+ // If we were going to use layers rendering but it's not set up
+ // yet, and the plugin happens to call this first, we'll forward
+ // the invalidation to the browser. It's unclear whether
+ // non-layers plugins need this rect forwarded when their window
+ // width or height is 0, which it would be for layers plugins
+ // before their first SetWindow().
+ SendNPN_InvalidateRect(*aInvalidRect);
+}
+
+bool
+PluginInstanceChild::RecvUpdateBackground(const SurfaceDescriptor& aBackground,
+ const nsIntRect& aRect)
+{
+ MOZ_ASSERT(mIsTransparent, "Only transparent plugins use backgrounds");
+
+ if (!mBackground) {
+ // XXX refactor me
+ switch (aBackground.type()) {
+#ifdef MOZ_X11
+ case SurfaceDescriptor::TSurfaceDescriptorX11: {
+ mBackground = aBackground.get_SurfaceDescriptorX11().OpenForeign();
+ break;
+ }
+#endif
+ case SurfaceDescriptor::TShmem: {
+ mBackground = gfxSharedImageSurface::Open(aBackground.get_Shmem());
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("Unexpected background surface descriptor");
+ }
+
+ if (!mBackground) {
+ return false;
+ }
+
+ IntSize bgSize = mBackground->GetSize();
+ mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect,
+ nsIntRect(0, 0, bgSize.width, bgSize.height));
+ AsyncShowPluginFrame();
+ return true;
+ }
+
+ // XXX refactor me
+ mAccumulatedInvalidRect.UnionRect(aRect, mAccumulatedInvalidRect);
+
+ // This must be asynchronous, because we may be nested within RPC messages
+ // which do not expect to receiving paint events.
+ AsyncShowPluginFrame();
+
+ return true;
+}
+
+PPluginBackgroundDestroyerChild*
+PluginInstanceChild::AllocPPluginBackgroundDestroyerChild()
+{
+ return new PluginBackgroundDestroyerChild();
+}
+
+bool
+PluginInstanceChild::RecvPPluginBackgroundDestroyerConstructor(
+ PPluginBackgroundDestroyerChild* aActor)
+{
+ // Our background changed, so we have to invalidate the area
+ // painted with the old background. If the background was
+ // destroyed because we have a new background, then we expect to
+ // be notified of that "soon", before processing the asynchronous
+ // invalidation here. If we're *not* getting a new background,
+ // our current front surface is stale and we want to repaint
+ // "soon" so that we can hand the browser back a surface with
+ // alpha values. (We should be notified of that invalidation soon
+ // too, but we don't assume that here.)
+ if (mBackground) {
+ IntSize bgsize = mBackground->GetSize();
+ mAccumulatedInvalidRect.UnionRect(
+ nsIntRect(0, 0, bgsize.width, bgsize.height), mAccumulatedInvalidRect);
+
+ // NB: we don't have to XSync here because only ShowPluginFrame()
+ // uses mBackground, and it always XSyncs after finishing.
+ mBackground = nullptr;
+ AsyncShowPluginFrame();
+ }
+
+ return PPluginBackgroundDestroyerChild::Send__delete__(aActor);
+}
+
+bool
+PluginInstanceChild::DeallocPPluginBackgroundDestroyerChild(
+ PPluginBackgroundDestroyerChild* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+uint32_t
+PluginInstanceChild::ScheduleTimer(uint32_t interval, bool repeat,
+ TimerFunc func)
+{
+ ChildTimer* t = new ChildTimer(this, interval, repeat, func);
+ if (0 == t->ID()) {
+ delete t;
+ return 0;
+ }
+
+ mTimers.AppendElement(t);
+ return t->ID();
+}
+
+void
+PluginInstanceChild::UnscheduleTimer(uint32_t id)
+{
+ if (0 == id)
+ return;
+
+ mTimers.RemoveElement(id, ChildTimer::IDComparator());
+}
+
+void
+PluginInstanceChild::AsyncCall(PluginThreadCallback aFunc, void* aUserData)
+{
+ RefPtr<ChildAsyncCall> task = new ChildAsyncCall(this, aFunc, aUserData);
+ PostChildAsyncCall(task.forget());
+}
+
+void
+PluginInstanceChild::PostChildAsyncCall(already_AddRefed<ChildAsyncCall> aTask)
+{
+ RefPtr<ChildAsyncCall> task = aTask;
+
+ {
+ MutexAutoLock lock(mAsyncCallMutex);
+ mPendingAsyncCalls.AppendElement(task);
+ }
+ ProcessChild::message_loop()->PostTask(task.forget());
+}
+
+void
+PluginInstanceChild::SwapSurfaces()
+{
+ RefPtr<gfxASurface> tmpsurf = mCurrentSurface;
+#ifdef XP_WIN
+ PPluginSurfaceChild* tmpactor = mCurrentSurfaceActor;
+#endif
+
+ mCurrentSurface = mBackSurface;
+#ifdef XP_WIN
+ mCurrentSurfaceActor = mBackSurfaceActor;
+#endif
+
+ mBackSurface = tmpsurf;
+#ifdef XP_WIN
+ mBackSurfaceActor = tmpactor;
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+ mDoubleBufferCARenderer.SwapSurfaces();
+
+ // Outdated back surface... not usable anymore due to changed plugin size.
+ // Dropping obsolete surface
+ if (mDoubleBufferCARenderer.HasFrontSurface() &&
+ mDoubleBufferCARenderer.HasBackSurface() &&
+ (mDoubleBufferCARenderer.GetFrontSurfaceWidth() !=
+ mDoubleBufferCARenderer.GetBackSurfaceWidth() ||
+ mDoubleBufferCARenderer.GetFrontSurfaceHeight() !=
+ mDoubleBufferCARenderer.GetBackSurfaceHeight() ||
+ mDoubleBufferCARenderer.GetFrontSurfaceContentsScaleFactor() !=
+ mDoubleBufferCARenderer.GetBackSurfaceContentsScaleFactor())) {
+
+ mDoubleBufferCARenderer.ClearFrontSurface();
+ }
+#else
+ if (mCurrentSurface && mBackSurface &&
+ (mCurrentSurface->GetSize() != mBackSurface->GetSize() ||
+ mCurrentSurface->GetContentType() != mBackSurface->GetContentType())) {
+ ClearCurrentSurface();
+ }
+#endif
+}
+
+void
+PluginInstanceChild::ClearCurrentSurface()
+{
+ mCurrentSurface = nullptr;
+#ifdef MOZ_WIDGET_COCOA
+ if (mDoubleBufferCARenderer.HasFrontSurface()) {
+ mDoubleBufferCARenderer.ClearFrontSurface();
+ }
+#endif
+#ifdef XP_WIN
+ if (mCurrentSurfaceActor) {
+ PPluginSurfaceChild::Send__delete__(mCurrentSurfaceActor);
+ mCurrentSurfaceActor = nullptr;
+ }
+#endif
+ mHelperSurface = nullptr;
+}
+
+void
+PluginInstanceChild::ClearAllSurfaces()
+{
+ if (mBackSurface) {
+ // Get last surface back, and drop it
+ SurfaceDescriptor temp = null_t();
+ NPRect r = { 0, 0, 1, 1 };
+ SendShow(r, temp, &temp);
+ }
+
+ if (gfxSharedImageSurface::IsSharedImage(mCurrentSurface))
+ DeallocShmem(static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem());
+ if (gfxSharedImageSurface::IsSharedImage(mBackSurface))
+ DeallocShmem(static_cast<gfxSharedImageSurface*>(mBackSurface.get())->GetShmem());
+ mCurrentSurface = nullptr;
+ mBackSurface = nullptr;
+
+#ifdef XP_WIN
+ if (mCurrentSurfaceActor) {
+ PPluginSurfaceChild::Send__delete__(mCurrentSurfaceActor);
+ mCurrentSurfaceActor = nullptr;
+ }
+ if (mBackSurfaceActor) {
+ PPluginSurfaceChild::Send__delete__(mBackSurfaceActor);
+ mBackSurfaceActor = nullptr;
+ }
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+ if (mDoubleBufferCARenderer.HasBackSurface()) {
+ // Get last surface back, and drop it
+ SurfaceDescriptor temp = null_t();
+ NPRect r = { 0, 0, 1, 1 };
+ SendShow(r, temp, &temp);
+ }
+
+ if (mCGLayer) {
+ mozilla::plugins::PluginUtilsOSX::ReleaseCGLayer(mCGLayer);
+ mCGLayer = nullptr;
+ }
+
+ mDoubleBufferCARenderer.ClearFrontSurface();
+ mDoubleBufferCARenderer.ClearBackSurface();
+#endif
+}
+
+static void
+InvalidateObjects(nsTHashtable<DeletingObjectEntry>& aEntries)
+{
+ for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
+ DeletingObjectEntry* e = iter.Get();
+ NPObject* o = e->GetKey();
+ if (!e->mDeleted && o->_class && o->_class->invalidate) {
+ o->_class->invalidate(o);
+ }
+ }
+}
+
+static void
+DeleteObjects(nsTHashtable<DeletingObjectEntry>& aEntries)
+{
+ for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
+ DeletingObjectEntry* e = iter.Get();
+ NPObject* o = e->GetKey();
+ if (!e->mDeleted) {
+ e->mDeleted = true;
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ {
+ int32_t refcnt = o->referenceCount;
+ while (refcnt) {
+ --refcnt;
+ NS_LOG_RELEASE(o, refcnt, "NPObject");
+ }
+ }
+#endif
+
+ PluginModuleChild::DeallocNPObject(o);
+ }
+ }
+}
+
+void
+PluginInstanceChild::Destroy()
+{
+ if (mDestroyed) {
+ return;
+ }
+ if (mStackDepth != 0) {
+ NS_RUNTIMEABORT("Destroying plugin instance on the stack.");
+ }
+ mDestroyed = true;
+
+#if defined(OS_WIN)
+ SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
+#endif
+
+ InfallibleTArray<PBrowserStreamChild*> streams;
+ ManagedPBrowserStreamChild(streams);
+
+ // First make sure none of these streams become deleted
+ for (uint32_t i = 0; i < streams.Length(); ) {
+ if (static_cast<BrowserStreamChild*>(streams[i])->InstanceDying())
+ ++i;
+ else
+ streams.RemoveElementAt(i);
+ }
+ for (uint32_t i = 0; i < streams.Length(); ++i)
+ static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery();
+
+ mTimers.Clear();
+
+ // NPP_Destroy() should be a synchronization point for plugin threads
+ // calling NPN_AsyncCall: after this function returns, they are no longer
+ // allowed to make async calls on this instance.
+ static_cast<PluginModuleChild *>(Manager())->NPP_Destroy(this);
+ mData.ndata = 0;
+
+ if (mCurrentInvalidateTask) {
+ mCurrentInvalidateTask->Cancel();
+ mCurrentInvalidateTask = nullptr;
+ }
+ if (mCurrentAsyncSetWindowTask) {
+ mCurrentAsyncSetWindowTask->Cancel();
+ mCurrentAsyncSetWindowTask = nullptr;
+ }
+ {
+ MutexAutoLock autoLock(mAsyncInvalidateMutex);
+ if (mAsyncInvalidateTask) {
+ mAsyncInvalidateTask->Cancel();
+ mAsyncInvalidateTask = nullptr;
+ }
+ }
+
+ ClearAllSurfaces();
+ mDirectBitmaps.Clear();
+
+ mDeletingHash = new nsTHashtable<DeletingObjectEntry>;
+ PluginScriptableObjectChild::NotifyOfInstanceShutdown(this);
+
+ InvalidateObjects(*mDeletingHash);
+ DeleteObjects(*mDeletingHash);
+
+ // Null out our cached actors as they should have been killed in the
+ // PluginInstanceDestroyed call above.
+ mCachedWindowActor = nullptr;
+ mCachedElementActor = nullptr;
+
+#if defined(OS_WIN)
+ DestroyWinlessPopupSurrogate();
+ UnhookWinlessFlashThrottle();
+ DestroyPluginWindow();
+#endif
+
+ // Pending async calls are discarded, not delivered. This matches the
+ // in-process behavior.
+ for (uint32_t i = 0; i < mPendingAsyncCalls.Length(); ++i)
+ mPendingAsyncCalls[i]->Cancel();
+
+ mPendingAsyncCalls.Clear();
+
+#ifdef MOZ_WIDGET_GTK
+ if (mWindow.type == NPWindowTypeWindow && !mXEmbed) {
+ xt_client_xloop_destroy();
+ }
+#endif
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ DeleteWindow();
+#endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+ *aResult = NPERR_NO_ERROR;
+
+ Destroy();
+
+ return true;
+}
+
+void
+PluginInstanceChild::ActorDestroy(ActorDestroyReason why)
+{
+#ifdef XP_WIN
+ // ClearAllSurfaces() should not try to send anything after ActorDestroy.
+ mCurrentSurfaceActor = nullptr;
+ mBackSurfaceActor = nullptr;
+#endif
+
+ Destroy();
+}
diff --git a/dom/plugins/ipc/PluginInstanceChild.h b/dom/plugins/ipc/PluginInstanceChild.h
new file mode 100644
index 000000000..0ad6e145d
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -0,0 +1,713 @@
+/* -*- 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 dom_plugins_PluginInstanceChild_h
+#define dom_plugins_PluginInstanceChild_h 1
+
+#include "mozilla/EventForwards.h"
+#include "mozilla/plugins/PPluginInstanceChild.h"
+#include "mozilla/plugins/PluginScriptableObjectChild.h"
+#include "mozilla/plugins/StreamNotifyChild.h"
+#include "mozilla/plugins/PPluginSurfaceChild.h"
+#include "mozilla/ipc/CrossProcessMutex.h"
+#include "nsRefPtrHashtable.h"
+#if defined(OS_WIN)
+#include "mozilla/gfx/SharedDIBWin.h"
+#elif defined(MOZ_WIDGET_COCOA)
+#include "PluginUtilsOSX.h"
+#include "mozilla/gfx/QuartzSupport.h"
+#include "base/timer.h"
+
+#endif
+
+#include "npfunctions.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+#include "ChildAsyncCall.h"
+#include "ChildTimer.h"
+#include "nsRect.h"
+#include "nsTHashtable.h"
+#include "mozilla/PaintTracker.h"
+#include "mozilla/gfx/Types.h"
+
+#include <map>
+
+#ifdef MOZ_WIDGET_GTK
+#include "gtk2xtbin.h"
+#endif
+
+class gfxASurface;
+
+namespace mozilla {
+namespace plugins {
+
+class PBrowserStreamChild;
+class BrowserStreamChild;
+class StreamNotifyChild;
+
+class PluginInstanceChild : public PPluginInstanceChild
+{
+ friend class BrowserStreamChild;
+ friend class PluginStreamChild;
+ friend class StreamNotifyChild;
+ friend class PluginScriptableObjectChild;
+
+#ifdef OS_WIN
+ friend LRESULT CALLBACK PluginWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+ static LRESULT CALLBACK PluginWindowProcInternal(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+#endif
+
+protected:
+ virtual bool
+ AnswerCreateChildPluginWindow(NativeWindowHandle* aChildPluginWindow) override;
+
+ virtual bool
+ RecvCreateChildPopupSurrogate(const NativeWindowHandle& aNetscapeWindow) override;
+
+ virtual bool
+ AnswerNPP_SetWindow(const NPRemoteWindow& window) override;
+
+ virtual bool
+ AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(bool* wantsAllStreams, NPError* rv) override;
+ virtual bool
+ AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(bool* needs, NPError* rv) override;
+ virtual bool
+ AnswerNPP_GetValue_NPPVpluginScriptableNPObject(PPluginScriptableObjectChild** value,
+ NPError* result) override;
+ virtual bool
+ AnswerNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(nsCString* aPlugId,
+ NPError* aResult) override;
+ virtual bool
+ AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value, NPError* result) override;
+ virtual bool
+ AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value, NPError* result) override;
+ virtual bool
+ AnswerNPP_SetValue_NPNVCSSZoomFactor(const double& value, NPError* result) override;
+
+ virtual bool
+ AnswerNPP_HandleEvent(const NPRemoteEvent& event, int16_t* handled) override;
+ virtual bool
+ AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
+ Shmem&& mem,
+ int16_t* handled,
+ Shmem* rtnmem) override;
+ virtual bool
+ AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
+ const uint32_t& surface,
+ int16_t* handled) override;
+
+ // Async rendering
+ virtual bool
+ RecvAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
+ const NPRemoteWindow& aWindow) override;
+
+ virtual void
+ DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
+ const NPRemoteWindow& aWindow,
+ bool aIsAsync);
+
+ virtual PPluginSurfaceChild*
+ AllocPPluginSurfaceChild(const WindowsSharedMemoryHandle&,
+ const gfx::IntSize&, const bool&) override {
+ return new PPluginSurfaceChild();
+ }
+
+ virtual bool DeallocPPluginSurfaceChild(PPluginSurfaceChild* s) override {
+ delete s;
+ return true;
+ }
+
+ virtual bool
+ AnswerPaint(const NPRemoteEvent& event, int16_t* handled) override
+ {
+ PaintTracker pt;
+ return AnswerNPP_HandleEvent(event, handled);
+ }
+
+ virtual bool
+ RecvWindowPosChanged(const NPRemoteEvent& event) override;
+
+ virtual bool
+ RecvContentsScaleFactorChanged(const double& aContentsScaleFactor) override;
+
+ virtual bool
+ AnswerNPP_Destroy(NPError* result) override;
+
+ virtual PPluginScriptableObjectChild*
+ AllocPPluginScriptableObjectChild() override;
+
+ virtual bool
+ DeallocPPluginScriptableObjectChild(PPluginScriptableObjectChild* aObject) override;
+
+ virtual bool
+ RecvPPluginScriptableObjectConstructor(PPluginScriptableObjectChild* aActor) override;
+
+ virtual bool
+ RecvPBrowserStreamConstructor(PBrowserStreamChild* aActor, const nsCString& aURL,
+ const uint32_t& aLength, const uint32_t& aLastmodified,
+ PStreamNotifyChild* aNotifyData, const nsCString& aHeaders) override;
+
+ virtual bool
+ AnswerNPP_NewStream(
+ PBrowserStreamChild* actor,
+ const nsCString& mimeType,
+ const bool& seekable,
+ NPError* rv,
+ uint16_t* stype) override;
+
+ virtual bool
+ RecvAsyncNPP_NewStream(
+ PBrowserStreamChild* actor,
+ const nsCString& mimeType,
+ const bool& seekable) override;
+
+ virtual PBrowserStreamChild*
+ AllocPBrowserStreamChild(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyChild* notifyData,
+ const nsCString& headers) override;
+
+ virtual bool
+ DeallocPBrowserStreamChild(PBrowserStreamChild* stream) override;
+
+ virtual PPluginStreamChild*
+ AllocPPluginStreamChild(const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result) override;
+
+ virtual bool
+ DeallocPPluginStreamChild(PPluginStreamChild* stream) override;
+
+ virtual PStreamNotifyChild*
+ AllocPStreamNotifyChild(const nsCString& url, const nsCString& target,
+ const bool& post, const nsCString& buffer,
+ const bool& file,
+ NPError* result) override;
+
+ virtual bool
+ DeallocPStreamNotifyChild(PStreamNotifyChild* notifyData) override;
+
+ virtual bool
+ AnswerSetPluginFocus() override;
+
+ virtual bool
+ AnswerUpdateWindow() override;
+
+ virtual bool
+ RecvNPP_DidComposite() override;
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ bool CreateWindow(const NPRemoteWindow& aWindow);
+ void DeleteWindow();
+#endif
+
+public:
+ PluginInstanceChild(const NPPluginFuncs* aPluginIface,
+ const nsCString& aMimeType,
+ const uint16_t& aMode,
+ const InfallibleTArray<nsCString>& aNames,
+ const InfallibleTArray<nsCString>& aValues);
+
+ virtual ~PluginInstanceChild();
+
+ NPError DoNPP_New();
+
+ // Common sync+async implementation of NPP_NewStream
+ NPError DoNPP_NewStream(BrowserStreamChild* actor,
+ const nsCString& mimeType,
+ const bool& seekable,
+ uint16_t* stype);
+
+ bool Initialize();
+
+ NPP GetNPP()
+ {
+ return &mData;
+ }
+
+ NPError
+ NPN_GetValue(NPNVariable aVariable, void* aValue);
+
+ NPError
+ NPN_SetValue(NPPVariable aVariable, void* aValue);
+
+ PluginScriptableObjectChild*
+ GetActorForNPObject(NPObject* aObject);
+
+ NPError
+ NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
+ NPStream** aStream);
+
+ void InvalidateRect(NPRect* aInvalidRect);
+
+#ifdef MOZ_WIDGET_COCOA
+ void Invalidate();
+#endif // definied(MOZ_WIDGET_COCOA)
+
+ uint32_t ScheduleTimer(uint32_t interval, bool repeat, TimerFunc func);
+ void UnscheduleTimer(uint32_t id);
+
+ void AsyncCall(PluginThreadCallback aFunc, void* aUserData);
+ // This function is a more general version of AsyncCall
+ void PostChildAsyncCall(already_AddRefed<ChildAsyncCall> aTask);
+
+ int GetQuirks();
+
+ void NPN_URLRedirectResponse(void* notifyData, NPBool allow);
+
+
+ NPError NPN_InitAsyncSurface(NPSize *size, NPImageFormat format,
+ void *initData, NPAsyncSurface *surface);
+ NPError NPN_FinalizeAsyncSurface(NPAsyncSurface *surface);
+
+ void NPN_SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed);
+
+ void DoAsyncRedraw();
+
+ virtual bool RecvHandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData,
+ const bool& aIsConsumed) override;
+
+#if defined(XP_WIN)
+ NPError DefaultAudioDeviceChanged(NPAudioDeviceChangeDetails& details);
+#endif
+
+private:
+ friend class PluginModuleChild;
+
+ NPError
+ InternalGetNPObjectForValue(NPNVariable aValue,
+ NPObject** aObject);
+
+ bool IsUsingDirectDrawing();
+
+ virtual bool RecvUpdateBackground(const SurfaceDescriptor& aBackground,
+ const nsIntRect& aRect) override;
+
+ virtual PPluginBackgroundDestroyerChild*
+ AllocPPluginBackgroundDestroyerChild() override;
+
+ virtual bool
+ RecvPPluginBackgroundDestroyerConstructor(PPluginBackgroundDestroyerChild* aActor) override;
+
+ virtual bool
+ DeallocPPluginBackgroundDestroyerChild(PPluginBackgroundDestroyerChild* aActor) override;
+
+#if defined(OS_WIN)
+ static bool RegisterWindowClass();
+ bool CreatePluginWindow();
+ void DestroyPluginWindow();
+ void SizePluginWindow(int width, int height);
+ int16_t WinlessHandleEvent(NPEvent& event);
+ void CreateWinlessPopupSurrogate();
+ void DestroyWinlessPopupSurrogate();
+ void InitPopupMenuHook();
+ void SetupFlashMsgThrottle();
+ void UnhookWinlessFlashThrottle();
+ void HookSetWindowLongPtr();
+ void SetUnityHooks();
+ void ClearUnityHooks();
+ void InitImm32Hook();
+ static inline bool SetWindowLongHookCheck(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong);
+ void FlashThrottleMessage(HWND, UINT, WPARAM, LPARAM, bool);
+ static LRESULT CALLBACK DummyWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+ static LRESULT CALLBACK PluginWindowProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+ static BOOL WINAPI TrackPopupHookProc(HMENU hMenu,
+ UINT uFlags,
+ int x,
+ int y,
+ int nReserved,
+ HWND hWnd,
+ CONST RECT *prcRect);
+ static HWND WINAPI SetCaptureHook(HWND aHwnd);
+ static LRESULT CALLBACK UnityGetMessageHookProc(int aCode, WPARAM aWparam, LPARAM aLParam);
+ static LRESULT CALLBACK UnitySendMessageHookProc(int aCode, WPARAM aWparam, LPARAM aLParam);
+ static BOOL CALLBACK EnumThreadWindowsCallback(HWND hWnd,
+ LPARAM aParam);
+ static LRESULT CALLBACK WinlessHiddenFlashWndProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+#ifdef _WIN64
+ static LONG_PTR WINAPI SetWindowLongPtrAHook(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong);
+ static LONG_PTR WINAPI SetWindowLongPtrWHook(HWND hWnd,
+ int nIndex,
+ LONG_PTR newLong);
+
+#else
+ static LONG WINAPI SetWindowLongAHook(HWND hWnd,
+ int nIndex,
+ LONG newLong);
+ static LONG WINAPI SetWindowLongWHook(HWND hWnd,
+ int nIndex,
+ LONG newLong);
+#endif
+
+ static HIMC WINAPI ImmGetContextProc(HWND aWND);
+ static BOOL WINAPI ImmReleaseContextProc(HWND aWND, HIMC aIMC);
+ static LONG WINAPI ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex,
+ LPVOID aBuf, DWORD aLen);
+ static BOOL WINAPI ImmSetCandidateWindowProc(HIMC hIMC,
+ LPCANDIDATEFORM plCandidate);
+ static BOOL WINAPI ImmNotifyIME(HIMC aIMC, DWORD aAction, DWORD aIndex,
+ DWORD aValue);
+
+ class FlashThrottleAsyncMsg : public ChildAsyncCall
+ {
+ public:
+ FlashThrottleAsyncMsg();
+ FlashThrottleAsyncMsg(PluginInstanceChild* aInst,
+ HWND aWnd, UINT aMsg,
+ WPARAM aWParam, LPARAM aLParam,
+ bool isWindowed)
+ : ChildAsyncCall(aInst, nullptr, nullptr),
+ mWnd(aWnd),
+ mMsg(aMsg),
+ mWParam(aWParam),
+ mLParam(aLParam),
+ mWindowed(isWindowed)
+ {}
+
+ NS_IMETHOD Run() override;
+
+ WNDPROC GetProc();
+ HWND GetWnd() { return mWnd; }
+ UINT GetMsg() { return mMsg; }
+ WPARAM GetWParam() { return mWParam; }
+ LPARAM GetLParam() { return mLParam; }
+
+ private:
+ HWND mWnd;
+ UINT mMsg;
+ WPARAM mWParam;
+ LPARAM mLParam;
+ bool mWindowed;
+ };
+
+ bool ShouldPostKeyMessage(UINT message, WPARAM wParam, LPARAM lParam);
+ bool MaybePostKeyMessage(UINT message, WPARAM wParam, LPARAM lParam);
+#endif // #if defined(OS_WIN)
+ const NPPluginFuncs* mPluginIface;
+ nsCString mMimeType;
+ uint16_t mMode;
+ InfallibleTArray<nsCString> mNames;
+ InfallibleTArray<nsCString> mValues;
+ NPP_t mData;
+ NPWindow mWindow;
+#if defined(XP_DARWIN) || defined(XP_WIN)
+ double mContentsScaleFactor;
+#endif
+ double mCSSZoomFactor;
+ uint32_t mPostingKeyEvents;
+ uint32_t mPostingKeyEventsOutdated;
+ int16_t mDrawingModel;
+
+ NPAsyncSurface* mCurrentDirectSurface;
+
+ // The surface hashtables below serve a few purposes. They let us verify
+ // and retain extra information about plugin surfaces, and they let us
+ // free shared memory that the plugin might forget to release.
+ struct DirectBitmap {
+ DirectBitmap(PluginInstanceChild* aOwner, const Shmem& shmem,
+ const gfx::IntSize& size, uint32_t stride, SurfaceFormat format);
+
+ private:
+ ~DirectBitmap();
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DirectBitmap);
+
+ PluginInstanceChild* mOwner;
+ Shmem mShmem;
+ gfx::SurfaceFormat mFormat;
+ gfx::IntSize mSize;
+ uint32_t mStride;
+ };
+ nsRefPtrHashtable<nsPtrHashKey<NPAsyncSurface>, DirectBitmap> mDirectBitmaps;
+
+#if defined(XP_WIN)
+ nsDataHashtable<nsPtrHashKey<NPAsyncSurface>, WindowsHandle> mDxgiSurfaces;
+#endif
+
+ mozilla::Mutex mAsyncInvalidateMutex;
+ CancelableRunnable *mAsyncInvalidateTask;
+
+ // Cached scriptable actors to avoid IPC churn
+ PluginScriptableObjectChild* mCachedWindowActor;
+ PluginScriptableObjectChild* mCachedElementActor;
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ NPSetWindowCallbackStruct mWsInfo;
+#ifdef MOZ_WIDGET_GTK
+ bool mXEmbed;
+ XtClient mXtClient;
+#endif
+#elif defined(OS_WIN)
+ HWND mPluginWindowHWND;
+ WNDPROC mPluginWndProc;
+ HWND mPluginParentHWND;
+ int mNestedEventLevelDepth;
+ HWND mCachedWinlessPluginHWND;
+ HWND mWinlessPopupSurrogateHWND;
+ nsIntPoint mPluginSize;
+ WNDPROC mWinlessThrottleOldWndProc;
+ HWND mWinlessHiddenMsgHWND;
+ HHOOK mUnityGetMessageHook;
+ HHOOK mUnitySendMessageHook;
+#endif
+
+ friend class ChildAsyncCall;
+
+ Mutex mAsyncCallMutex;
+ nsTArray<ChildAsyncCall*> mPendingAsyncCalls;
+ nsTArray<nsAutoPtr<ChildTimer> > mTimers;
+
+ /**
+ * During destruction we enumerate all remaining scriptable objects and
+ * invalidate/delete them. Enumeration can re-enter, so maintain a
+ * hash separate from PluginModuleChild.mObjectMap.
+ */
+ nsAutoPtr< nsTHashtable<DeletingObjectEntry> > mDeletingHash;
+
+#if defined(MOZ_WIDGET_COCOA)
+private:
+#if defined(__i386__)
+ NPEventModel mEventModel;
+#endif
+ CGColorSpaceRef mShColorSpace;
+ CGContextRef mShContext;
+ RefPtr<nsCARenderer> mCARenderer;
+ void *mCGLayer;
+
+ // Core Animation drawing model requires a refresh timer.
+ uint32_t mCARefreshTimer;
+
+public:
+ const NPCocoaEvent* getCurrentEvent() {
+ return mCurrentEvent;
+ }
+
+ bool CGDraw(CGContextRef ref, nsIntRect aUpdateRect);
+
+#if defined(__i386__)
+ NPEventModel EventModel() { return mEventModel; }
+#endif
+
+private:
+ const NPCocoaEvent *mCurrentEvent;
+#endif
+
+ bool CanPaintOnBackground();
+
+ bool IsVisible() {
+#ifdef XP_MACOSX
+ return mWindow.clipRect.top != mWindow.clipRect.bottom &&
+ mWindow.clipRect.left != mWindow.clipRect.right;
+#else
+ return mWindow.clipRect.top != 0 ||
+ mWindow.clipRect.left != 0 ||
+ mWindow.clipRect.bottom != 0 ||
+ mWindow.clipRect.right != 0;
+#endif
+ }
+
+ // ShowPluginFrame - in general does four things:
+ // 1) Create mCurrentSurface optimized for rendering to parent process
+ // 2) Updated mCurrentSurface to be a complete copy of mBackSurface
+ // 3) Draw the invalidated plugin area into mCurrentSurface
+ // 4) Send it to parent process.
+ bool ShowPluginFrame(void);
+
+ // If we can read back safely from mBackSurface, copy
+ // mSurfaceDifferenceRect from mBackSurface to mFrontSurface.
+ // @return Whether the back surface could be read.
+ bool ReadbackDifferenceRect(const nsIntRect& rect);
+
+ // Post ShowPluginFrame task
+ void AsyncShowPluginFrame(void);
+
+ // In the PaintRect functions, aSurface is the size of the full plugin
+ // window. Each PaintRect function renders into the subrectangle aRect of
+ // aSurface (possibly more if we're working around a Flash bug).
+
+ // Paint plugin content rectangle to surface with bg color filling
+ void PaintRectToSurface(const nsIntRect& aRect,
+ gfxASurface* aSurface,
+ const gfx::Color& aColor);
+
+ // Render plugin content to surface using
+ // white/black image alpha extraction algorithm
+ void PaintRectWithAlphaExtraction(const nsIntRect& aRect,
+ gfxASurface* aSurface);
+
+ // Call plugin NPAPI function to render plugin content to surface
+ // @param - aSurface - should be compatible with current platform plugin rendering
+ // @return - FALSE if plugin not painted to surface
+ void PaintRectToPlatformSurface(const nsIntRect& aRect,
+ gfxASurface* aSurface);
+
+ // Update NPWindow platform attributes and call plugin "setwindow"
+ // @param - aForceSetWindow - call setwindow even if platform attributes are the same
+ void UpdateWindowAttributes(bool aForceSetWindow = false);
+
+ // Create optimized mCurrentSurface for parent process rendering
+ // @return FALSE if optimized surface not created
+ bool CreateOptSurface(void);
+
+ // Create mHelperSurface if mCurrentSurface non compatible with plugins
+ // @return TRUE if helper surface created successfully, or not needed
+ bool MaybeCreatePlatformHelperSurface(void);
+
+ // Make sure that we have surface for rendering
+ bool EnsureCurrentBuffer(void);
+
+ // Helper function for delayed InvalidateRect call
+ // non null mCurrentInvalidateTask will call this function
+ void InvalidateRectDelayed(void);
+
+ // Clear mCurrentSurface/mCurrentSurfaceActor/mHelperSurface
+ void ClearCurrentSurface();
+
+ // Swap mCurrentSurface/mBackSurface and their associated actors
+ void SwapSurfaces();
+
+ // Clear all surfaces in response to NPP_Destroy
+ void ClearAllSurfaces();
+
+ void Destroy();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // Set as true when SetupLayer called
+ // and go with different path in InvalidateRect function
+ bool mLayersRendering;
+
+ // Current surface available for rendering
+ RefPtr<gfxASurface> mCurrentSurface;
+
+ // Back surface, just keeping reference to
+ // surface which is on ParentProcess side
+ RefPtr<gfxASurface> mBackSurface;
+
+#ifdef XP_MACOSX
+ // Current IOSurface available for rendering
+ // We can't use thebes gfxASurface like other platforms.
+ PluginUtilsOSX::nsDoubleBufferCARenderer mDoubleBufferCARenderer;
+#endif
+
+ // (Not to be confused with mBackSurface). This is a recent copy
+ // of the opaque pixels under our object frame, if
+ // |mIsTransparent|. We ask the plugin render directly onto a
+ // copy of the background pixels if available, and fall back on
+ // alpha recovery otherwise.
+ RefPtr<gfxASurface> mBackground;
+
+#ifdef XP_WIN
+ // These actors mirror mCurrentSurface/mBackSurface
+ PPluginSurfaceChild* mCurrentSurfaceActor;
+ PPluginSurfaceChild* mBackSurfaceActor;
+#endif
+
+ // Accumulated invalidate rect, while back buffer is not accessible,
+ // in plugin coordinates.
+ nsIntRect mAccumulatedInvalidRect;
+
+ // Plugin only call SetTransparent
+ // and does not remember their transparent state
+ // and p->getvalue return always false
+ bool mIsTransparent;
+
+ // Surface type optimized of parent process
+ gfxSurfaceType mSurfaceType;
+
+ // Keep InvalidateRect task pointer to be able Cancel it on Destroy
+ RefPtr<CancelableRunnable> mCurrentInvalidateTask;
+
+ // Keep AsyncSetWindow task pointer to be able to Cancel it on Destroy
+ RefPtr<CancelableRunnable> mCurrentAsyncSetWindowTask;
+
+ // True while plugin-child in plugin call
+ // Use to prevent plugin paint re-enter
+ bool mPendingPluginCall;
+
+ // On some platforms, plugins may not support rendering to a surface with
+ // alpha, or not support rendering to an image surface.
+ // In those cases we need to draw to a temporary platform surface; we cache
+ // that surface here.
+ RefPtr<gfxASurface> mHelperSurface;
+
+ // true when plugin does not support painting to ARGB32
+ // surface this is false if plugin supports
+ // NPPVpluginTransparentAlphaBool (which is not part of
+ // NPAPI yet)
+ bool mDoAlphaExtraction;
+
+ // true when the plugin has painted at least once. We use this to ensure
+ // that we ask a plugin to paint at least once even if it's invisible;
+ // some plugin (instances) rely on this in order to work properly.
+ bool mHasPainted;
+
+ // Cached rectangle rendered to previous surface(mBackSurface)
+ // Used for reading back to current surface and syncing data,
+ // in plugin coordinates.
+ nsIntRect mSurfaceDifferenceRect;
+
+ // Has this instance been destroyed, either by ActorDestroy or NPP_Destroy?
+ bool mDestroyed;
+
+#ifdef XP_WIN
+ // WM_*CHAR messages are never consumed by chrome process's widget.
+ // So, if preceding keydown or keyup event is consumed by reserved
+ // shortcut key in the chrome process, we shouldn't send the following
+ // WM_*CHAR messages to the plugin.
+ bool mLastKeyEventConsumed;
+#endif // #ifdef XP_WIN
+
+ // While IME in the process has composition, this is set to true.
+ // Otherwise, false.
+ static bool sIsIMEComposing;
+
+ // A counter is incremented by AutoStackHelper to indicate that there is an
+ // active plugin call which should be preventing shutdown.
+public:
+ class AutoStackHelper {
+ public:
+ explicit AutoStackHelper(PluginInstanceChild* instance)
+ : mInstance(instance)
+ {
+ ++mInstance->mStackDepth;
+ }
+ ~AutoStackHelper() {
+ --mInstance->mStackDepth;
+ }
+ private:
+ PluginInstanceChild *const mInstance;
+ };
+private:
+ int32_t mStackDepth;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // ifndef dom_plugins_PluginInstanceChild_h
diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp
new file mode 100644
index 000000000..02f0641f7
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -0,0 +1,2509 @@
+/* -*- 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/. */
+
+#include "mozilla/DebugOnly.h"
+#include <stdint.h> // for intptr_t
+
+#include "mozilla/BasicEvents.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
+#include "PluginInstanceParent.h"
+#include "BrowserStreamParent.h"
+#include "PluginAsyncSurrogate.h"
+#include "PluginBackgroundDestroyer.h"
+#include "PluginModuleParent.h"
+#include "PluginStreamParent.h"
+#include "StreamNotifyParent.h"
+#include "npfunctions.h"
+#include "nsAutoPtr.h"
+#include "gfxASurface.h"
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+#include "gfxSharedImageSurface.h"
+#include "nsNetUtil.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsFocusManager.h"
+#include "nsIDOMElement.h"
+#ifdef MOZ_X11
+#include "gfxXlibSurface.h"
+#endif
+#include "gfxContext.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "Layers.h"
+#include "ImageContainer.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "gfxPrefs.h"
+#include "LayersLogging.h"
+#include "mozilla/layers/TextureWrapperImage.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#if defined(XP_WIN)
+# include "mozilla/layers/D3D11ShareHandleImage.h"
+# include "mozilla/gfx/DeviceManagerDx.h"
+# include "mozilla/layers/TextureD3D11.h"
+#endif
+
+#ifdef XP_MACOSX
+#include "MacIOSurfaceImage.h"
+#endif
+
+#if defined(OS_WIN)
+#include <windowsx.h>
+#include "gfxWindowsPlatform.h"
+#include "mozilla/plugins/PluginSurfaceParent.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsIWidget.h"
+#include "nsPluginNativeWindow.h"
+#include "PluginQuirks.h"
+extern const wchar_t* kFlashFullscreenClass;
+#elif defined(MOZ_WIDGET_GTK)
+#include "mozilla/dom/ContentChild.h"
+#include <gdk/gdk.h>
+#elif defined(XP_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#endif // defined(XP_MACOSX)
+
+using namespace mozilla::plugins;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+void
+StreamNotifyParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Implement me! Bug 1005162
+}
+
+bool
+StreamNotifyParent::RecvRedirectNotifyResponse(const bool& allow)
+{
+ PluginInstanceParent* instance = static_cast<PluginInstanceParent*>(Manager());
+ instance->mNPNIface->urlredirectresponse(instance->mNPP, this, static_cast<NPBool>(allow));
+ return true;
+}
+
+#if defined(XP_WIN)
+namespace mozilla {
+namespace plugins {
+/**
+ * e10s specific, used in cross referencing hwnds with plugin instances so we
+ * can access methods here from PluginWidgetChild.
+ */
+static nsClassHashtable<nsVoidPtrHashKey, PluginInstanceParent>* sPluginInstanceList;
+
+// static
+PluginInstanceParent*
+PluginInstanceParent::LookupPluginInstanceByID(uintptr_t aId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (sPluginInstanceList) {
+ return sPluginInstanceList->Get((void*)aId);
+ }
+ return nullptr;
+}
+}
+}
+#endif
+
+PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
+ NPP npp,
+ const nsCString& aMimeType,
+ const NPNetscapeFuncs* npniface)
+ : mParent(parent)
+ , mSurrogate(PluginAsyncSurrogate::Cast(npp))
+ , mUseSurrogate(true)
+ , mNPP(npp)
+ , mNPNIface(npniface)
+ , mWindowType(NPWindowTypeWindow)
+ , mDrawingModel(kDefaultDrawingModel)
+ , mLastRecordedDrawingModel(-1)
+ , mFrameID(0)
+#if defined(OS_WIN)
+ , mPluginHWND(nullptr)
+ , mChildPluginHWND(nullptr)
+ , mChildPluginsParentHWND(nullptr)
+ , mPluginWndProc(nullptr)
+ , mNestedEventState(false)
+#endif // defined(XP_WIN)
+#if defined(XP_MACOSX)
+ , mShWidth(0)
+ , mShHeight(0)
+ , mShColorSpace(nullptr)
+#endif
+{
+#if defined(OS_WIN)
+ if (!sPluginInstanceList) {
+ sPluginInstanceList = new nsClassHashtable<nsVoidPtrHashKey, PluginInstanceParent>();
+ }
+#endif
+}
+
+PluginInstanceParent::~PluginInstanceParent()
+{
+ if (mNPP)
+ mNPP->pdata = nullptr;
+
+#if defined(OS_WIN)
+ NS_ASSERTION(!(mPluginHWND || mPluginWndProc),
+ "Subclass was not reset correctly before the dtor was reached!");
+#endif
+#if defined(MOZ_WIDGET_COCOA)
+ if (mShWidth != 0 && mShHeight != 0) {
+ DeallocShmem(mShSurface);
+ }
+ if (mShColorSpace)
+ ::CGColorSpaceRelease(mShColorSpace);
+#endif
+}
+
+bool
+PluginInstanceParent::InitMetadata(const nsACString& aMimeType,
+ const nsACString& aSrcAttribute)
+{
+ if (aSrcAttribute.IsEmpty()) {
+ return false;
+ }
+ // Ensure that the src attribute is absolute
+ RefPtr<nsPluginInstanceOwner> owner = GetOwner();
+ if (!owner) {
+ return false;
+ }
+ nsCOMPtr<nsIURI> baseUri(owner->GetBaseURI());
+ return NS_SUCCEEDED(NS_MakeAbsoluteURI(mSrcAttribute, aSrcAttribute, baseUri));
+}
+
+void
+PluginInstanceParent::ActorDestroy(ActorDestroyReason why)
+{
+#if defined(OS_WIN)
+ if (why == AbnormalShutdown) {
+ // If the plugin process crashes, this is the only
+ // chance we get to destroy resources.
+ UnsubclassPluginWindow();
+ }
+#endif
+ if (mFrontSurface) {
+ mFrontSurface = nullptr;
+ if (mImageContainer) {
+ mImageContainer->ClearAllImages();
+ }
+#ifdef MOZ_X11
+ FinishX(DefaultXDisplay());
+#endif
+ }
+ if (IsUsingDirectDrawing() && mImageContainer) {
+ mImageContainer->ClearAllImages();
+ }
+}
+
+NPError
+PluginInstanceParent::Destroy()
+{
+ NPError retval;
+ { // Scope for timer
+ Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_INSTANCE_DESTROY_MS>
+ timer(Module()->GetHistogramKey());
+ if (!CallNPP_Destroy(&retval)) {
+ retval = NPERR_GENERIC_ERROR;
+ }
+ }
+
+#if defined(OS_WIN)
+ UnsubclassPluginWindow();
+#endif
+
+ return retval;
+}
+
+bool
+PluginInstanceParent::IsUsingDirectDrawing()
+{
+ return IsDrawingModelDirect(mDrawingModel);
+}
+
+PBrowserStreamParent*
+PluginInstanceParent::AllocPBrowserStreamParent(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyParent* notifyData,
+ const nsCString& headers)
+{
+ NS_RUNTIMEABORT("Not reachable");
+ return nullptr;
+}
+
+bool
+PluginInstanceParent::DeallocPBrowserStreamParent(PBrowserStreamParent* stream)
+{
+ delete stream;
+ return true;
+}
+
+PPluginStreamParent*
+PluginInstanceParent::AllocPPluginStreamParent(const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result)
+{
+ return new PluginStreamParent(this, mimeType, target, result);
+}
+
+bool
+PluginInstanceParent::DeallocPPluginStreamParent(PPluginStreamParent* stream)
+{
+ delete stream;
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value,
+ NPError* result)
+{
+#ifdef XP_WIN
+ HWND id;
+#elif defined(MOZ_X11)
+ XID id;
+#elif defined(XP_DARWIN)
+ intptr_t id;
+#elif defined(ANDROID)
+ // TODO: Need Android impl
+ int id;
+#else
+#warning Implement me
+#endif
+
+ *result = mNPNIface->getvalue(mNPP, NPNVnetscapeWindow, &id);
+ *value = id;
+ return true;
+}
+
+bool
+PluginInstanceParent::InternalGetValueForNPObject(
+ NPNVariable aVariable,
+ PPluginScriptableObjectParent** aValue,
+ NPError* aResult)
+{
+ NPObject* npobject;
+ NPError result = mNPNIface->getvalue(mNPP, aVariable, (void*)&npobject);
+ if (result == NPERR_NO_ERROR) {
+ NS_ASSERTION(npobject, "Shouldn't return null and NPERR_NO_ERROR!");
+
+ PluginScriptableObjectParent* actor = GetActorForNPObject(npobject);
+ mNPNIface->releaseobject(npobject);
+ if (actor) {
+ *aValue = actor;
+ *aResult = NPERR_NO_ERROR;
+ return true;
+ }
+
+ NS_ERROR("Failed to get actor!");
+ result = NPERR_GENERIC_ERROR;
+ }
+
+ *aValue = nullptr;
+ *aResult = result;
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVWindowNPObject(
+ PPluginScriptableObjectParent** aValue,
+ NPError* aResult)
+{
+ return InternalGetValueForNPObject(NPNVWindowNPObject, aValue, aResult);
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVPluginElementNPObject(
+ PPluginScriptableObjectParent** aValue,
+ NPError* aResult)
+{
+ return InternalGetValueForNPObject(NPNVPluginElementNPObject, aValue,
+ aResult);
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVprivateModeBool(bool* value,
+ NPError* result)
+{
+ NPBool v;
+ *result = mNPNIface->getvalue(mNPP, NPNVprivateModeBool, &v);
+ *value = v;
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_DrawingModelSupport(const NPNVariable& model, bool* value)
+{
+ *value = false;
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_NPNVdocumentOrigin(nsCString* value,
+ NPError* result)
+{
+ void *v = nullptr;
+ *result = mNPNIface->getvalue(mNPP, NPNVdocumentOrigin, &v);
+ if (*result == NPERR_NO_ERROR && v) {
+ value->Adopt(static_cast<char*>(v));
+ }
+ return true;
+}
+
+static inline bool
+AllowDirectBitmapSurfaceDrawing()
+{
+ if (!gfxPrefs::PluginAsyncDrawingEnabled()) {
+ return false;
+ }
+ return gfxPlatform::GetPlatform()->SupportsPluginDirectBitmapDrawing();
+}
+
+static inline bool
+AllowDirectDXGISurfaceDrawing()
+{
+ if (!gfxPrefs::PluginAsyncDrawingEnabled()) {
+ return false;
+ }
+#if defined(XP_WIN)
+ return gfxWindowsPlatform::GetPlatform()->SupportsPluginDirectDXGIDrawing();
+#else
+ return false;
+#endif
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_SupportsAsyncBitmapSurface(bool* value)
+{
+ *value = AllowDirectBitmapSurfaceDrawing();
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_SupportsAsyncDXGISurface(bool* value)
+{
+ *value = AllowDirectDXGISurfaceDrawing();
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValue_PreferredDXGIAdapter(DxgiAdapterDesc* aOutDesc)
+{
+ PodZero(aOutDesc);
+#ifdef XP_WIN
+ if (!AllowDirectDXGISurfaceDrawing()) {
+ return false;
+ }
+
+ RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ return false;
+ }
+
+ RefPtr<IDXGIDevice> dxgi;
+ if (FAILED(device->QueryInterface(__uuidof(IDXGIDevice), getter_AddRefs(dxgi))) || !dxgi) {
+ return false;
+ }
+ RefPtr<IDXGIAdapter> adapter;
+ if (FAILED(dxgi->GetAdapter(getter_AddRefs(adapter))) || !adapter) {
+ return false;
+ }
+
+ DXGI_ADAPTER_DESC desc;
+ if (FAILED(adapter->GetDesc(&desc))) {
+ return false;
+ }
+
+ *aOutDesc = DxgiAdapterDesc::From(desc);
+#endif
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginWindow(
+ const bool& windowed, NPError* result)
+{
+ // Yes, we are passing a boolean as a void*. We have to cast to intptr_t
+ // first to avoid gcc warnings about casting to a pointer from a
+ // non-pointer-sized integer.
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginWindowBool,
+ (void*)(intptr_t)windowed);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginTransparent(
+ const bool& transparent, NPError* result)
+{
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginTransparentBool,
+ (void*)(intptr_t)transparent);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginUsesDOMForCursor(
+ const bool& useDOMForCursor, NPError* result)
+{
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginUsesDOMForCursorBool,
+ (void*)(intptr_t)useDOMForCursor);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginDrawingModel(
+ const int& drawingModel, NPError* result)
+{
+ bool allowed = false;
+
+ switch (drawingModel) {
+#if defined(XP_MACOSX)
+ case NPDrawingModelCoreAnimation:
+ case NPDrawingModelInvalidatingCoreAnimation:
+ case NPDrawingModelOpenGL:
+ case NPDrawingModelCoreGraphics:
+ allowed = true;
+ break;
+#elif defined(XP_WIN)
+ case NPDrawingModelSyncWin:
+ allowed = true;
+ break;
+ case NPDrawingModelAsyncWindowsDXGISurface:
+ allowed = AllowDirectDXGISurfaceDrawing();
+ break;
+#elif defined(MOZ_X11)
+ case NPDrawingModelSyncX:
+ allowed = true;
+ break;
+#endif
+ case NPDrawingModelAsyncBitmapSurface:
+ allowed = AllowDirectBitmapSurfaceDrawing();
+ break;
+ default:
+ allowed = false;
+ break;
+ }
+
+ if (!allowed) {
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+ }
+
+ mDrawingModel = drawingModel;
+
+ int requestModel = drawingModel;
+
+#ifdef XP_MACOSX
+ if (drawingModel == NPDrawingModelCoreAnimation ||
+ drawingModel == NPDrawingModelInvalidatingCoreAnimation) {
+ // We need to request CoreGraphics otherwise
+ // the nsPluginFrame will try to draw a CALayer
+ // that can not be shared across process.
+ requestModel = NPDrawingModelCoreGraphics;
+ }
+#endif
+
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginDrawingModel,
+ (void*)(intptr_t)requestModel);
+
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginEventModel(
+ const int& eventModel, NPError* result)
+{
+#ifdef XP_MACOSX
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginEventModel,
+ (void*)(intptr_t)eventModel);
+ return true;
+#else
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+#endif
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(
+ const bool& isAudioPlaying, NPError* result)
+{
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginIsPlayingAudio,
+ (void*)(intptr_t)isAudioPlaying);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetURL(const nsCString& url,
+ const nsCString& target,
+ NPError* result)
+{
+ *result = mNPNIface->geturl(mNPP,
+ NullableStringGet(url),
+ NullableStringGet(target));
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_PostURL(const nsCString& url,
+ const nsCString& target,
+ const nsCString& buffer,
+ const bool& file,
+ NPError* result)
+{
+ *result = mNPNIface->posturl(mNPP, url.get(), NullableStringGet(target),
+ buffer.Length(), buffer.get(), file);
+ return true;
+}
+
+PStreamNotifyParent*
+PluginInstanceParent::AllocPStreamNotifyParent(const nsCString& url,
+ const nsCString& target,
+ const bool& post,
+ const nsCString& buffer,
+ const bool& file,
+ NPError* result)
+{
+ return new StreamNotifyParent();
+}
+
+bool
+PluginInstanceParent::AnswerPStreamNotifyConstructor(PStreamNotifyParent* actor,
+ const nsCString& url,
+ const nsCString& target,
+ const bool& post,
+ const nsCString& buffer,
+ const bool& file,
+ NPError* result)
+{
+ bool streamDestroyed = false;
+ static_cast<StreamNotifyParent*>(actor)->
+ SetDestructionFlag(&streamDestroyed);
+
+ if (!post) {
+ *result = mNPNIface->geturlnotify(mNPP,
+ NullableStringGet(url),
+ NullableStringGet(target),
+ actor);
+ }
+ else {
+ *result = mNPNIface->posturlnotify(mNPP,
+ NullableStringGet(url),
+ NullableStringGet(target),
+ buffer.Length(),
+ NullableStringGet(buffer),
+ file, actor);
+ }
+
+ if (streamDestroyed) {
+ // If the stream was destroyed, we must return an error code in the
+ // constructor.
+ *result = NPERR_GENERIC_ERROR;
+ }
+ else {
+ static_cast<StreamNotifyParent*>(actor)->ClearDestructionFlag();
+ if (*result != NPERR_NO_ERROR)
+ return PStreamNotifyParent::Send__delete__(actor,
+ NPERR_GENERIC_ERROR);
+ }
+
+ return true;
+}
+
+bool
+PluginInstanceParent::DeallocPStreamNotifyParent(PStreamNotifyParent* notifyData)
+{
+ delete notifyData;
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvNPN_InvalidateRect(const NPRect& rect)
+{
+ mNPNIface->invalidaterect(mNPP, const_cast<NPRect*>(&rect));
+ return true;
+}
+
+static inline NPRect
+IntRectToNPRect(const gfx::IntRect& rect)
+{
+ NPRect r;
+ r.left = rect.x;
+ r.top = rect.y;
+ r.right = rect.x + rect.width;
+ r.bottom = rect.y + rect.height;
+ return r;
+}
+
+bool
+PluginInstanceParent::RecvRevokeCurrentDirectSurface()
+{
+ ImageContainer *container = GetImageContainer();
+ if (!container) {
+ return true;
+ }
+
+ container->ClearAllImages();
+
+ PLUGIN_LOG_DEBUG((" (RecvRevokeCurrentDirectSurface)"));
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvInitDXGISurface(const gfx::SurfaceFormat& format,
+ const gfx::IntSize& size,
+ WindowsHandle* outHandle,
+ NPError* outError)
+{
+ *outHandle = 0;
+ *outError = NPERR_GENERIC_ERROR;
+
+#if defined(XP_WIN)
+ if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
+ *outError = NPERR_INVALID_PARAM;
+ return true;
+ }
+ if (size.width <= 0 || size.height <= 0) {
+ *outError = NPERR_INVALID_PARAM;
+ return true;
+ }
+
+ ImageContainer *container = GetImageContainer();
+ if (!container) {
+ return true;
+ }
+
+ RefPtr<ImageBridgeChild> forwarder = ImageBridgeChild::GetSingleton();
+ if (!forwarder) {
+ return true;
+ }
+
+ RefPtr<ID3D11Device> d3d11 = DeviceManagerDx::Get()->GetContentDevice();
+ if (!d3d11) {
+ return true;
+ }
+
+ // Create the texture we'll give to the plugin process.
+ HANDLE sharedHandle = 0;
+ RefPtr<ID3D11Texture2D> back;
+ {
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, size.width, size.height, 1, 1);
+ desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
+ if (FAILED(d3d11->CreateTexture2D(&desc, nullptr, getter_AddRefs(back))) || !back) {
+ return true;
+ }
+
+ RefPtr<IDXGIResource> resource;
+ if (FAILED(back->QueryInterface(IID_IDXGIResource, getter_AddRefs(resource))) || !resource) {
+ return true;
+ }
+ if (FAILED(resource->GetSharedHandle(&sharedHandle) || !sharedHandle)) {
+ return true;
+ }
+ }
+
+ RefPtr<D3D11SurfaceHolder> holder = new D3D11SurfaceHolder(back, format, size);
+ mD3D11Surfaces.Put(reinterpret_cast<void*>(sharedHandle), holder);
+
+ *outHandle = reinterpret_cast<uintptr_t>(sharedHandle);
+ *outError = NPERR_NO_ERROR;
+#endif
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvFinalizeDXGISurface(const WindowsHandle& handle)
+{
+#if defined(XP_WIN)
+ mD3D11Surfaces.Remove(reinterpret_cast<void*>(handle));
+#endif
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvShowDirectBitmap(Shmem&& buffer,
+ const SurfaceFormat& format,
+ const uint32_t& stride,
+ const IntSize& size,
+ const IntRect& dirty)
+{
+ // Validate format.
+ if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
+ MOZ_ASSERT_UNREACHABLE("bad format type");
+ return false;
+ }
+ if (size.width <= 0 || size.height <= 0) {
+ MOZ_ASSERT_UNREACHABLE("bad image size");
+ return false;
+ }
+ if (mDrawingModel != NPDrawingModelAsyncBitmapSurface) {
+ MOZ_ASSERT_UNREACHABLE("plugin did not set a bitmap drawing model");
+ return false;
+ }
+
+ // Validate buffer and size.
+ CheckedInt<uint32_t> nbytes = CheckedInt<uint32_t>(uint32_t(size.height)) * stride;
+ if (!nbytes.isValid() || nbytes.value() != buffer.Size<uint8_t>()) {
+ MOZ_ASSERT_UNREACHABLE("bad shmem size");
+ return false;
+ }
+
+ ImageContainer* container = GetImageContainer();
+ if (!container) {
+ return false;
+ }
+
+ RefPtr<gfx::DataSourceSurface> source =
+ gfx::Factory::CreateWrappingDataSourceSurface(buffer.get<uint8_t>(), stride, size, format);
+ if (!source) {
+ return false;
+ }
+
+ // Allocate a texture for the compositor.
+ RefPtr<TextureClientRecycleAllocator> allocator = mParent->EnsureTextureAllocatorForDirectBitmap();
+ RefPtr<TextureClient> texture = allocator->CreateOrRecycle(
+ format, size, BackendSelector::Content,
+ TextureFlags::NO_FLAGS,
+ TextureAllocationFlags(ALLOC_FOR_OUT_OF_BAND_CONTENT | ALLOC_UPDATE_FROM_SURFACE));
+ if (!texture) {
+ NS_WARNING("Could not allocate a TextureClient for plugin!");
+ return false;
+ }
+
+ // Upload the plugin buffer.
+ {
+ TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return false;
+ }
+ texture->UpdateFromSurface(source);
+ }
+
+ // Wrap the texture in an image and ship it off.
+ RefPtr<TextureWrapperImage> image =
+ new TextureWrapperImage(texture, gfx::IntRect(gfx::IntPoint(0, 0), size));
+ SetCurrentImage(image);
+
+ PLUGIN_LOG_DEBUG((" (RecvShowDirectBitmap received shmem=%p stride=%d size=%s dirty=%s)",
+ buffer.get<unsigned char>(), stride, Stringify(size).c_str(), Stringify(dirty).c_str()));
+ return true;
+}
+
+void
+PluginInstanceParent::SetCurrentImage(Image* aImage)
+{
+ MOZ_ASSERT(IsUsingDirectDrawing());
+ ImageContainer::NonOwningImage holder(aImage);
+ holder.mFrameID = ++mFrameID;
+
+ AutoTArray<ImageContainer::NonOwningImage,1> imageList;
+ imageList.AppendElement(holder);
+ mImageContainer->SetCurrentImages(imageList);
+
+ // Invalidate our area in the page so the image gets flushed.
+ gfx::IntRect rect = aImage->GetPictureRect();
+ NPRect nprect = {uint16_t(rect.x), uint16_t(rect.y), uint16_t(rect.width), uint16_t(rect.height)};
+ RecvNPN_InvalidateRect(nprect);
+
+ RecordDrawingModel();
+}
+
+bool
+PluginInstanceParent::RecvShowDirectDXGISurface(const WindowsHandle& handle,
+ const gfx::IntRect& dirty)
+{
+#if defined(XP_WIN)
+ RefPtr<D3D11SurfaceHolder> surface;
+ if (!mD3D11Surfaces.Get(reinterpret_cast<void*>(handle), getter_AddRefs(surface))) {
+ return false;
+ }
+ if (!surface->IsValid()) {
+ return false;
+ }
+
+ ImageContainer* container = GetImageContainer();
+ if (!container) {
+ return false;
+ }
+
+ RefPtr<TextureClientRecycleAllocator> allocator = mParent->EnsureTextureAllocatorForDXGISurface();
+ RefPtr<TextureClient> texture = allocator->CreateOrRecycle(
+ surface->GetFormat(), surface->GetSize(),
+ BackendSelector::Content,
+ TextureFlags::NO_FLAGS,
+ ALLOC_FOR_OUT_OF_BAND_CONTENT);
+ if (!texture) {
+ NS_WARNING("Could not allocate a TextureClient for plugin!");
+ return false;
+ }
+
+ surface->CopyToTextureClient(texture);
+
+ gfx::IntSize size(surface->GetSize());
+ gfx::IntRect pictureRect(gfx::IntPoint(0, 0), size);
+
+ // Wrap the texture in an image and ship it off.
+ RefPtr<TextureWrapperImage> image = new TextureWrapperImage(texture, pictureRect);
+ SetCurrentImage(image);
+
+ PLUGIN_LOG_DEBUG((" (RecvShowDirect3D10Surface received handle=%p rect=%s)",
+ reinterpret_cast<void*>(handle), Stringify(dirty).c_str()));
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+PluginInstanceParent::RecvShow(const NPRect& updatedRect,
+ const SurfaceDescriptor& newSurface,
+ SurfaceDescriptor* prevSurface)
+{
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceParent][%p] RecvShow for <x=%d,y=%d, w=%d,h=%d>",
+ this, updatedRect.left, updatedRect.top,
+ updatedRect.right - updatedRect.left,
+ updatedRect.bottom - updatedRect.top));
+
+ MOZ_ASSERT(!IsUsingDirectDrawing());
+
+ // XXXjwatt rewrite to use Moz2D
+ RefPtr<gfxASurface> surface;
+ if (newSurface.type() == SurfaceDescriptor::TShmem) {
+ if (!newSurface.get_Shmem().IsReadable()) {
+ NS_WARNING("back surface not readable");
+ return false;
+ }
+ surface = gfxSharedImageSurface::Open(newSurface.get_Shmem());
+ }
+#ifdef XP_MACOSX
+ else if (newSurface.type() == SurfaceDescriptor::TIOSurfaceDescriptor) {
+ IOSurfaceDescriptor iodesc = newSurface.get_IOSurfaceDescriptor();
+
+ RefPtr<MacIOSurface> newIOSurface =
+ MacIOSurface::LookupSurface(iodesc.surfaceId(),
+ iodesc.contentsScaleFactor());
+
+ if (!newIOSurface) {
+ NS_WARNING("Got bad IOSurfaceDescriptor in RecvShow");
+ return false;
+ }
+
+ if (mFrontIOSurface)
+ *prevSurface = IOSurfaceDescriptor(mFrontIOSurface->GetIOSurfaceID(),
+ mFrontIOSurface->GetContentsScaleFactor());
+ else
+ *prevSurface = null_t();
+
+ mFrontIOSurface = newIOSurface;
+
+ RecvNPN_InvalidateRect(updatedRect);
+
+ PLUGIN_LOG_DEBUG((" (RecvShow invalidated for surface %p)",
+ mFrontSurface.get()));
+
+ return true;
+ }
+#endif
+#ifdef MOZ_X11
+ else if (newSurface.type() == SurfaceDescriptor::TSurfaceDescriptorX11) {
+ surface = newSurface.get_SurfaceDescriptorX11().OpenForeign();
+ }
+#endif
+#ifdef XP_WIN
+ else if (newSurface.type() == SurfaceDescriptor::TPPluginSurfaceParent) {
+ PluginSurfaceParent* s =
+ static_cast<PluginSurfaceParent*>(newSurface.get_PPluginSurfaceParent());
+ surface = s->Surface();
+ }
+#endif
+
+ if (mFrontSurface) {
+ // This is the "old front buffer" we're about to hand back to
+ // the plugin. We might still have drawing operations
+ // referencing it.
+#ifdef MOZ_X11
+ if (mFrontSurface->GetType() == gfxSurfaceType::Xlib) {
+ // Finish with the surface and XSync here to ensure the server has
+ // finished operations on the surface before the plugin starts
+ // scribbling on it again, or worse, destroys it.
+ mFrontSurface->Finish();
+ FinishX(DefaultXDisplay());
+ } else
+#endif
+ {
+ mFrontSurface->Flush();
+ }
+ }
+
+ if (mFrontSurface && gfxSharedImageSurface::IsSharedImage(mFrontSurface))
+ *prevSurface = static_cast<gfxSharedImageSurface*>(mFrontSurface.get())->GetShmem();
+ else
+ *prevSurface = null_t();
+
+ if (surface) {
+ // Notify the cairo backend that this surface has changed behind
+ // its back.
+ gfxRect ur(updatedRect.left, updatedRect.top,
+ updatedRect.right - updatedRect.left,
+ updatedRect.bottom - updatedRect.top);
+ surface->MarkDirty(ur);
+
+ bool isPlugin = true;
+ RefPtr<gfx::SourceSurface> sourceSurface =
+ gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, surface, isPlugin);
+ RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), sourceSurface);
+
+ AutoTArray<ImageContainer::NonOwningImage,1> imageList;
+ imageList.AppendElement(
+ ImageContainer::NonOwningImage(image));
+
+ ImageContainer *container = GetImageContainer();
+ container->SetCurrentImages(imageList);
+ }
+ else if (mImageContainer) {
+ mImageContainer->ClearAllImages();
+ }
+
+ mFrontSurface = surface;
+ RecvNPN_InvalidateRect(updatedRect);
+
+ PLUGIN_LOG_DEBUG((" (RecvShow invalidated for surface %p)",
+ mFrontSurface.get()));
+
+ RecordDrawingModel();
+ return true;
+}
+
+nsresult
+PluginInstanceParent::AsyncSetWindow(NPWindow* aWindow)
+{
+ NPRemoteWindow window;
+ mWindowType = aWindow->type;
+ window.window = reinterpret_cast<uint64_t>(aWindow->window);
+ window.x = aWindow->x;
+ window.y = aWindow->y;
+ window.width = aWindow->width;
+ window.height = aWindow->height;
+ window.clipRect = aWindow->clipRect;
+ window.type = aWindow->type;
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ double scaleFactor = 1.0;
+ mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &scaleFactor);
+ window.contentsScaleFactor = scaleFactor;
+#endif
+
+#if defined(OS_WIN)
+ MaybeCreateChildPopupSurrogate();
+#endif
+
+ if (!SendAsyncSetWindow(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType(),
+ window))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+nsresult
+PluginInstanceParent::GetImageContainer(ImageContainer** aContainer)
+{
+ if (IsUsingDirectDrawing()) {
+ // Use the image container created by the most recent direct surface
+ // call, if any. We don't create one if no surfaces were presented
+ // yet.
+ ImageContainer *container = mImageContainer;
+ NS_IF_ADDREF(container);
+ *aContainer = container;
+ return NS_OK;
+ }
+
+#ifdef XP_MACOSX
+ MacIOSurface* ioSurface = nullptr;
+
+ if (mFrontIOSurface) {
+ ioSurface = mFrontIOSurface;
+ } else if (mIOSurface) {
+ ioSurface = mIOSurface;
+ }
+
+ if (!mFrontSurface && !ioSurface)
+#else
+ if (!mFrontSurface)
+#endif
+ return NS_ERROR_NOT_AVAILABLE;
+
+ ImageContainer *container = GetImageContainer();
+
+ if (!container) {
+ return NS_ERROR_FAILURE;
+ }
+
+#ifdef XP_MACOSX
+ if (ioSurface) {
+ RefPtr<Image> image = new MacIOSurfaceImage(ioSurface);
+ container->SetCurrentImageInTransaction(image);
+
+ NS_IF_ADDREF(container);
+ *aContainer = container;
+ return NS_OK;
+ }
+#endif
+
+ NS_IF_ADDREF(container);
+ *aContainer = container;
+ return NS_OK;
+}
+
+nsresult
+PluginInstanceParent::GetImageSize(nsIntSize* aSize)
+{
+ if (IsUsingDirectDrawing()) {
+ if (!mImageContainer) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ *aSize = mImageContainer->GetCurrentSize();
+ return NS_OK;
+ }
+
+ if (mFrontSurface) {
+ mozilla::gfx::IntSize size = mFrontSurface->GetSize();
+ *aSize = nsIntSize(size.width, size.height);
+ return NS_OK;
+ }
+
+#ifdef XP_MACOSX
+ if (mFrontIOSurface) {
+ *aSize = nsIntSize(mFrontIOSurface->GetWidth(), mFrontIOSurface->GetHeight());
+ return NS_OK;
+ } else if (mIOSurface) {
+ *aSize = nsIntSize(mIOSurface->GetWidth(), mIOSurface->GetHeight());
+ return NS_OK;
+ }
+#endif
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+void
+PluginInstanceParent::DidComposite()
+{
+ if (!IsUsingDirectDrawing()) {
+ return;
+ }
+ Unused << SendNPP_DidComposite();
+}
+
+#ifdef XP_MACOSX
+nsresult
+PluginInstanceParent::IsRemoteDrawingCoreAnimation(bool *aDrawing)
+{
+ *aDrawing = (NPDrawingModelCoreAnimation == (NPDrawingModel)mDrawingModel ||
+ NPDrawingModelInvalidatingCoreAnimation == (NPDrawingModel)mDrawingModel);
+ return NS_OK;
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+nsresult
+PluginInstanceParent::ContentsScaleFactorChanged(double aContentsScaleFactor)
+{
+ bool rv = SendContentsScaleFactorChanged(aContentsScaleFactor);
+ return rv ? NS_OK : NS_ERROR_FAILURE;
+}
+#endif // #ifdef XP_MACOSX
+
+nsresult
+PluginInstanceParent::SetBackgroundUnknown()
+{
+ PLUGIN_LOG_DEBUG(("[InstanceParent][%p] SetBackgroundUnknown", this));
+
+ if (mBackground) {
+ DestroyBackground();
+ MOZ_ASSERT(!mBackground, "Background not destroyed");
+ }
+
+ return NS_OK;
+}
+
+nsresult
+PluginInstanceParent::BeginUpdateBackground(const nsIntRect& aRect,
+ DrawTarget** aDrawTarget)
+{
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceParent][%p] BeginUpdateBackground for <x=%d,y=%d, w=%d,h=%d>",
+ this, aRect.x, aRect.y, aRect.width, aRect.height));
+
+ if (!mBackground) {
+ // XXX if we failed to create a background surface on one
+ // update, there's no guarantee that later updates will be for
+ // the entire background area until successful. We might want
+ // to fix that eventually.
+ MOZ_ASSERT(aRect.TopLeft() == nsIntPoint(0, 0),
+ "Expecting rect for whole frame");
+ if (!CreateBackground(aRect.Size())) {
+ *aDrawTarget = nullptr;
+ return NS_OK;
+ }
+ }
+
+ mozilla::gfx::IntSize sz = mBackground->GetSize();
+#ifdef DEBUG
+ MOZ_ASSERT(nsIntRect(0, 0, sz.width, sz.height).Contains(aRect),
+ "Update outside of background area");
+#endif
+
+ RefPtr<gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
+ CreateDrawTargetForSurface(mBackground, gfx::IntSize(sz.width, sz.height));
+ dt.forget(aDrawTarget);
+
+ return NS_OK;
+}
+
+nsresult
+PluginInstanceParent::EndUpdateBackground(const nsIntRect& aRect)
+{
+ PLUGIN_LOG_DEBUG(
+ ("[InstanceParent][%p] EndUpdateBackground for <x=%d,y=%d, w=%d,h=%d>",
+ this, aRect.x, aRect.y, aRect.width, aRect.height));
+
+#ifdef MOZ_X11
+ // Have to XSync here to avoid the plugin trying to draw with this
+ // surface racing with its creation in the X server. We also want
+ // to avoid the plugin drawing onto stale pixels, then handing us
+ // back a front surface from those pixels that we might
+ // recomposite for "a while" until the next update. This XSync
+ // still doesn't guarantee that the plugin draws onto a consistent
+ // view of its background, but it does mean that the plugin is
+ // drawing onto pixels no older than those in the latest
+ // EndUpdateBackground().
+ XSync(DefaultXDisplay(), False);
+#endif
+
+ Unused << SendUpdateBackground(BackgroundDescriptor(), aRect);
+
+ return NS_OK;
+}
+
+#if defined(XP_WIN)
+nsresult
+PluginInstanceParent::SetScrollCaptureId(uint64_t aScrollCaptureId)
+{
+ if (aScrollCaptureId == ImageContainer::sInvalidAsyncContainerId) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mImageContainer = new ImageContainer(aScrollCaptureId);
+ return NS_OK;
+}
+
+nsresult
+PluginInstanceParent::GetScrollCaptureContainer(ImageContainer** aContainer)
+{
+ if (!aContainer || !mImageContainer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<ImageContainer> container = GetImageContainer();
+ container.forget(aContainer);
+
+ return NS_OK;
+}
+#endif // XP_WIN
+
+PluginAsyncSurrogate*
+PluginInstanceParent::GetAsyncSurrogate()
+{
+ return mSurrogate;
+}
+
+bool
+PluginInstanceParent::CreateBackground(const nsIntSize& aSize)
+{
+ MOZ_ASSERT(!mBackground, "Already have a background");
+
+ // XXX refactor me
+
+#if defined(MOZ_X11)
+ Screen* screen = DefaultScreenOfDisplay(DefaultXDisplay());
+ Visual* visual = DefaultVisualOfScreen(screen);
+ mBackground = gfxXlibSurface::Create(screen, visual,
+ mozilla::gfx::IntSize(aSize.width, aSize.height));
+ return !!mBackground;
+
+#elif defined(XP_WIN)
+ // We have chosen to create an unsafe surface in which the plugin
+ // can read from the region while we're writing to it.
+ mBackground =
+ gfxSharedImageSurface::CreateUnsafe(
+ this,
+ mozilla::gfx::IntSize(aSize.width, aSize.height),
+ mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32);
+ return !!mBackground;
+#else
+ return false;
+#endif
+}
+
+void
+PluginInstanceParent::DestroyBackground()
+{
+ if (!mBackground) {
+ return;
+ }
+
+ // Relinquish ownership of |mBackground| to its destroyer
+ PPluginBackgroundDestroyerParent* pbd =
+ new PluginBackgroundDestroyerParent(mBackground);
+ mBackground = nullptr;
+
+ // If this fails, there's no problem: |bd| will be destroyed along
+ // with the old background surface.
+ Unused << SendPPluginBackgroundDestroyerConstructor(pbd);
+}
+
+mozilla::plugins::SurfaceDescriptor
+PluginInstanceParent::BackgroundDescriptor()
+{
+ MOZ_ASSERT(mBackground, "Need a background here");
+
+ // XXX refactor me
+
+#ifdef MOZ_X11
+ gfxXlibSurface* xsurf = static_cast<gfxXlibSurface*>(mBackground.get());
+ return SurfaceDescriptorX11(xsurf);
+#endif
+
+#ifdef XP_WIN
+ MOZ_ASSERT(gfxSharedImageSurface::IsSharedImage(mBackground),
+ "Expected shared image surface");
+ gfxSharedImageSurface* shmem =
+ static_cast<gfxSharedImageSurface*>(mBackground.get());
+ return shmem->GetShmem();
+#endif
+
+ // If this is ever used, which it shouldn't be, it will trigger a
+ // hard assertion in IPDL-generated code.
+ return mozilla::plugins::SurfaceDescriptor();
+}
+
+ImageContainer*
+PluginInstanceParent::GetImageContainer()
+{
+ if (mImageContainer) {
+ return mImageContainer;
+ }
+
+ if (IsUsingDirectDrawing()) {
+ mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
+ } else {
+ mImageContainer = LayerManager::CreateImageContainer();
+ }
+ return mImageContainer;
+}
+
+PPluginBackgroundDestroyerParent*
+PluginInstanceParent::AllocPPluginBackgroundDestroyerParent()
+{
+ NS_RUNTIMEABORT("'Power-user' ctor is used exclusively");
+ return nullptr;
+}
+
+bool
+PluginInstanceParent::DeallocPPluginBackgroundDestroyerParent(
+ PPluginBackgroundDestroyerParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+NPError
+PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
+{
+ PLUGIN_LOG_DEBUG(("%s (aWindow=%p)", FULLFUNCTION, (void*) aWindow));
+
+ NS_ENSURE_TRUE(aWindow, NPERR_GENERIC_ERROR);
+
+ NPRemoteWindow window;
+ mWindowType = aWindow->type;
+
+#if defined(OS_WIN)
+ // On windowless controls, reset the shared memory surface as needed.
+ if (mWindowType == NPWindowTypeDrawable) {
+ MaybeCreateChildPopupSurrogate();
+ } else {
+ SubclassPluginWindow(reinterpret_cast<HWND>(aWindow->window));
+
+ // Skip SetWindow call for hidden QuickTime plugins.
+ if ((mParent->GetQuirks() & QUIRK_QUICKTIME_AVOID_SETWINDOW) &&
+ aWindow->width == 0 && aWindow->height == 0) {
+ return NPERR_NO_ERROR;
+ }
+
+ window.window = reinterpret_cast<uint64_t>(aWindow->window);
+ window.x = aWindow->x;
+ window.y = aWindow->y;
+ window.width = aWindow->width;
+ window.height = aWindow->height;
+ window.type = aWindow->type;
+
+ // On Windows we need to create and set the parent before we set the
+ // window on the plugin, or keyboard interaction will not work.
+ if (!MaybeCreateAndParentChildPluginWindow()) {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+#else
+ window.window = reinterpret_cast<uint64_t>(aWindow->window);
+ window.x = aWindow->x;
+ window.y = aWindow->y;
+ window.width = aWindow->width;
+ window.height = aWindow->height;
+ window.clipRect = aWindow->clipRect; // MacOS specific
+ window.type = aWindow->type;
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ double floatScaleFactor = 1.0;
+ mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &floatScaleFactor);
+ int scaleFactor = ceil(floatScaleFactor);
+ window.contentsScaleFactor = floatScaleFactor;
+#endif
+#if defined(XP_MACOSX)
+ if (mShWidth != window.width * scaleFactor || mShHeight != window.height * scaleFactor) {
+ if (mDrawingModel == NPDrawingModelCoreAnimation ||
+ mDrawingModel == NPDrawingModelInvalidatingCoreAnimation) {
+ mIOSurface = MacIOSurface::CreateIOSurface(window.width, window.height,
+ floatScaleFactor);
+ } else if (uint32_t(mShWidth * mShHeight) !=
+ window.width * scaleFactor * window.height * scaleFactor) {
+ if (mShWidth != 0 && mShHeight != 0) {
+ DeallocShmem(mShSurface);
+ mShWidth = 0;
+ mShHeight = 0;
+ }
+
+ if (window.width != 0 && window.height != 0) {
+ if (!AllocShmem(window.width * scaleFactor * window.height*4 * scaleFactor,
+ SharedMemory::TYPE_BASIC, &mShSurface)) {
+ PLUGIN_LOG_DEBUG(("Shared memory could not be allocated."));
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+ }
+ mShWidth = window.width * scaleFactor;
+ mShHeight = window.height * scaleFactor;
+ }
+#endif
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ const NPSetWindowCallbackStruct* ws_info =
+ static_cast<NPSetWindowCallbackStruct*>(aWindow->ws_info);
+ window.visualID = ws_info->visual ? ws_info->visual->visualid : 0;
+ window.colormap = ws_info->colormap;
+#endif
+
+ if (!CallNPP_SetWindow(window)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ RecordDrawingModel();
+ return NPERR_NO_ERROR;
+}
+
+NPError
+PluginInstanceParent::NPP_GetValue(NPPVariable aVariable,
+ void* _retval)
+{
+ switch (aVariable) {
+
+ case NPPVpluginWantsAllNetworkStreams: {
+ bool wantsAllStreams;
+ NPError rv;
+
+ if (!CallNPP_GetValue_NPPVpluginWantsAllNetworkStreams(&wantsAllStreams, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (NPERR_NO_ERROR != rv) {
+ return rv;
+ }
+
+ (*(NPBool*)_retval) = wantsAllStreams;
+ return NPERR_NO_ERROR;
+ }
+
+#ifdef MOZ_X11
+ case NPPVpluginNeedsXEmbed: {
+ bool needsXEmbed;
+ NPError rv;
+
+ if (!CallNPP_GetValue_NPPVpluginNeedsXEmbed(&needsXEmbed, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (NPERR_NO_ERROR != rv) {
+ return rv;
+ }
+
+ (*(NPBool*)_retval) = needsXEmbed;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ case NPPVpluginScriptableNPObject: {
+ PPluginScriptableObjectParent* actor;
+ NPError rv;
+ if (!CallNPP_GetValue_NPPVpluginScriptableNPObject(&actor, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (NPERR_NO_ERROR != rv) {
+ return rv;
+ }
+
+ if (!actor) {
+ NS_ERROR("NPPVpluginScriptableNPObject succeeded but null.");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs();
+ if (!npn) {
+ NS_WARNING("No netscape functions?!");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ NPObject* object =
+ static_cast<PluginScriptableObjectParent*>(actor)->GetObject(true);
+ NS_ASSERTION(object, "This shouldn't ever be null!");
+
+ (*(NPObject**)_retval) = npn->retainobject(object);
+ return NPERR_NO_ERROR;
+ }
+
+#ifdef MOZ_ACCESSIBILITY_ATK
+ case NPPVpluginNativeAccessibleAtkPlugId: {
+ nsCString plugId;
+ NPError rv;
+ if (!CallNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(&plugId, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (NPERR_NO_ERROR != rv) {
+ return rv;
+ }
+
+ (*(nsCString*)_retval) = plugId;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ default:
+ MOZ_LOG(GetPluginLog(), LogLevel::Warning,
+ ("In PluginInstanceParent::NPP_GetValue: Unhandled NPPVariable %i (%s)",
+ (int) aVariable, NPPVariableToString(aVariable)));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+NPError
+PluginInstanceParent::NPP_SetValue(NPNVariable variable, void* value)
+{
+ NPError result;
+ switch (variable) {
+ case NPNVprivateModeBool:
+ if (!CallNPP_SetValue_NPNVprivateModeBool(*static_cast<NPBool*>(value),
+ &result))
+ return NPERR_GENERIC_ERROR;
+
+ return result;
+
+ case NPNVmuteAudioBool:
+ if (!CallNPP_SetValue_NPNVmuteAudioBool(*static_cast<NPBool*>(value),
+ &result))
+ return NPERR_GENERIC_ERROR;
+
+ return result;
+
+ case NPNVCSSZoomFactor:
+ if (!CallNPP_SetValue_NPNVCSSZoomFactor(*static_cast<double*>(value),
+ &result))
+ return NPERR_GENERIC_ERROR;
+
+ return result;
+
+ default:
+ NS_ERROR("Unhandled NPNVariable in NPP_SetValue");
+ MOZ_LOG(GetPluginLog(), LogLevel::Warning,
+ ("In PluginInstanceParent::NPP_SetValue: Unhandled NPNVariable %i (%s)",
+ (int) variable, NPNVariableToString(variable)));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+void
+PluginInstanceParent::NPP_URLRedirectNotify(const char* url, int32_t status,
+ void* notifyData)
+{
+ if (!notifyData)
+ return;
+
+ PStreamNotifyParent* streamNotify = static_cast<PStreamNotifyParent*>(notifyData);
+ Unused << streamNotify->SendRedirectNotify(NullableString(url), status);
+}
+
+int16_t
+PluginInstanceParent::NPP_HandleEvent(void* event)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+#if defined(XP_MACOSX)
+ NPCocoaEvent* npevent = reinterpret_cast<NPCocoaEvent*>(event);
+#else
+ NPEvent* npevent = reinterpret_cast<NPEvent*>(event);
+#endif
+ NPRemoteEvent npremoteevent;
+ npremoteevent.event = *npevent;
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ double scaleFactor = 1.0;
+ mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &scaleFactor);
+ npremoteevent.contentsScaleFactor = scaleFactor;
+#endif
+ int16_t handled = 0;
+
+#if defined(OS_WIN)
+ if (mWindowType == NPWindowTypeDrawable) {
+ switch (npevent->event) {
+ case WM_KILLFOCUS:
+ {
+ // When the user selects fullscreen mode in Flash video players,
+ // WM_KILLFOCUS will be delayed by deferred event processing:
+ // WM_LBUTTONUP results in a call to CreateWindow within Flash,
+ // which fires WM_KILLFOCUS. Delayed delivery causes Flash to
+ // misinterpret the event, dropping back out of fullscreen. Trap
+ // this event and drop it.
+ wchar_t szClass[26];
+ HWND hwnd = GetForegroundWindow();
+ if (hwnd && hwnd != mPluginHWND &&
+ GetClassNameW(hwnd, szClass,
+ sizeof(szClass)/sizeof(char16_t)) &&
+ !wcscmp(szClass, kFlashFullscreenClass)) {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ // We send this in nsPluginFrame just before painting
+ return SendWindowPosChanged(npremoteevent);
+ }
+
+ case WM_IME_STARTCOMPOSITION:
+ case WM_IME_COMPOSITION:
+ case WM_IME_ENDCOMPOSITION:
+ if (!(mParent->GetQuirks() & QUIRK_WINLESS_HOOK_IME)) {
+ // IME message will be posted on allowed plugins only such as
+ // Flash. Because if we cannot know that plugin can handle
+ // IME correctly.
+ return 0;
+ }
+ break;
+ }
+ }
+#endif
+
+#if defined(MOZ_X11)
+ switch (npevent->type) {
+ case GraphicsExpose:
+ PLUGIN_LOG_DEBUG((" schlepping drawable 0x%lx across the pipe\n",
+ npevent->xgraphicsexpose.drawable));
+ // Make sure the X server has created the Drawable and completes any
+ // drawing before the plugin draws on top.
+ //
+ // XSync() waits for the X server to complete. Really this parent
+ // process does not need to wait; the child is the process that needs
+ // to wait. A possibly-slightly-better alternative would be to send
+ // an X event to the child that the child would wait for.
+ FinishX(DefaultXDisplay());
+
+ return CallPaint(npremoteevent, &handled) ? handled : 0;
+
+ case ButtonPress:
+ // Release any active pointer grab so that the plugin X client can
+ // grab the pointer if it wishes.
+ Display *dpy = DefaultXDisplay();
+# ifdef MOZ_WIDGET_GTK
+ // GDK attempts to (asynchronously) track whether there is an active
+ // grab so ungrab through GDK.
+ //
+ // This call needs to occur in the same process that receives the event in
+ // the first place (chrome process)
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+ cp->SendUngrabPointer(npevent->xbutton.time);
+ } else {
+ gdk_pointer_ungrab(npevent->xbutton.time);
+ }
+# else
+ XUngrabPointer(dpy, npevent->xbutton.time);
+# endif
+ // Wait for the ungrab to complete.
+ XSync(dpy, False);
+ break;
+ }
+#endif
+
+#ifdef XP_MACOSX
+ if (npevent->type == NPCocoaEventDrawRect) {
+ if (mDrawingModel == NPDrawingModelCoreAnimation ||
+ mDrawingModel == NPDrawingModelInvalidatingCoreAnimation) {
+ if (!mIOSurface) {
+ NS_ERROR("No IOSurface allocated.");
+ return false;
+ }
+ if (!CallNPP_HandleEvent_IOSurface(npremoteevent,
+ mIOSurface->GetIOSurfaceID(),
+ &handled))
+ return false; // no good way to handle errors here...
+
+ CGContextRef cgContext = npevent->data.draw.context;
+ if (!mShColorSpace) {
+ mShColorSpace = CreateSystemColorSpace();
+ }
+ if (!mShColorSpace) {
+ PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
+ return false;
+ }
+ if (cgContext) {
+ nsCARenderer::DrawSurfaceToCGContext(cgContext, mIOSurface,
+ mShColorSpace,
+ npevent->data.draw.x,
+ npevent->data.draw.y,
+ npevent->data.draw.width,
+ npevent->data.draw.height);
+ }
+ return true;
+ } else if (mFrontIOSurface) {
+ CGContextRef cgContext = npevent->data.draw.context;
+ if (!mShColorSpace) {
+ mShColorSpace = CreateSystemColorSpace();
+ }
+ if (!mShColorSpace) {
+ PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
+ return false;
+ }
+ if (cgContext) {
+ nsCARenderer::DrawSurfaceToCGContext(cgContext, mFrontIOSurface,
+ mShColorSpace,
+ npevent->data.draw.x,
+ npevent->data.draw.y,
+ npevent->data.draw.width,
+ npevent->data.draw.height);
+ }
+ return true;
+ } else {
+ if (mShWidth == 0 && mShHeight == 0) {
+ PLUGIN_LOG_DEBUG(("NPCocoaEventDrawRect on window of size 0."));
+ return false;
+ }
+ if (!mShSurface.IsReadable()) {
+ PLUGIN_LOG_DEBUG(("Shmem is not readable."));
+ return false;
+ }
+
+ if (!CallNPP_HandleEvent_Shmem(npremoteevent, mShSurface,
+ &handled, &mShSurface))
+ return false; // no good way to handle errors here...
+
+ if (!mShSurface.IsReadable()) {
+ PLUGIN_LOG_DEBUG(("Shmem not returned. Either the plugin crashed "
+ "or we have a bug."));
+ return false;
+ }
+
+ char* shContextByte = mShSurface.get<char>();
+
+ if (!mShColorSpace) {
+ mShColorSpace = CreateSystemColorSpace();
+ }
+ if (!mShColorSpace) {
+ PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
+ return false;
+ }
+ CGContextRef shContext = ::CGBitmapContextCreate(shContextByte,
+ mShWidth, mShHeight, 8,
+ mShWidth*4, mShColorSpace,
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host);
+ if (!shContext) {
+ PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
+ return false;
+ }
+
+ CGImageRef shImage = ::CGBitmapContextCreateImage(shContext);
+ if (shImage) {
+ CGContextRef cgContext = npevent->data.draw.context;
+
+ ::CGContextDrawImage(cgContext,
+ CGRectMake(0,0,mShWidth,mShHeight),
+ shImage);
+ ::CGImageRelease(shImage);
+ } else {
+ ::CGContextRelease(shContext);
+ return false;
+ }
+ ::CGContextRelease(shContext);
+ return true;
+ }
+ }
+#endif
+
+ if (!CallNPP_HandleEvent(npremoteevent, &handled))
+ return 0; // no good way to handle errors here...
+
+ return handled;
+}
+
+NPError
+PluginInstanceParent::NPP_NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16_t* stype)
+{
+ PLUGIN_LOG_DEBUG(("%s (type=%s, stream=%p, seekable=%i)",
+ FULLFUNCTION, (char*) type, (void*) stream, (int) seekable));
+
+ BrowserStreamParent* bs = new BrowserStreamParent(this, stream);
+
+ if (!SendPBrowserStreamConstructor(bs,
+ NullableString(stream->url),
+ stream->end,
+ stream->lastmodified,
+ static_cast<PStreamNotifyParent*>(stream->notifyData),
+ NullableString(stream->headers))) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_STREAM_INIT_MS>
+ timer(Module()->GetHistogramKey());
+
+ NPError err = NPERR_NO_ERROR;
+ if (mParent->IsStartingAsync()) {
+ MOZ_ASSERT(mSurrogate);
+ mSurrogate->AsyncCallDeparting();
+ if (SendAsyncNPP_NewStream(bs, NullableString(type), seekable)) {
+ *stype = nsPluginStreamListenerPeer::STREAM_TYPE_UNKNOWN;
+ } else {
+ err = NPERR_GENERIC_ERROR;
+ }
+ } else {
+ bs->SetAlive();
+ if (!CallNPP_NewStream(bs, NullableString(type), seekable, &err, stype)) {
+ err = NPERR_GENERIC_ERROR;
+ }
+ if (NPERR_NO_ERROR != err) {
+ Unused << PBrowserStreamParent::Send__delete__(bs);
+ }
+ }
+
+ return err;
+}
+
+NPError
+PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason)
+{
+ PLUGIN_LOG_DEBUG(("%s (stream=%p, reason=%i)",
+ FULLFUNCTION, (void*) stream, (int) reason));
+
+ AStream* s = static_cast<AStream*>(stream->pdata);
+ if (!s) {
+ // The stream has already been deleted by other means.
+ // With async plugin init this could happen if async NPP_NewStream
+ // returns an error code.
+ return NPERR_NO_ERROR;
+ }
+ if (s->IsBrowserStream()) {
+ BrowserStreamParent* sp =
+ static_cast<BrowserStreamParent*>(s);
+ if (sp->mNPP != this)
+ NS_RUNTIMEABORT("Mismatched plugin data");
+
+ sp->NPP_DestroyStream(reason);
+ return NPERR_NO_ERROR;
+ }
+ else {
+ PluginStreamParent* sp =
+ static_cast<PluginStreamParent*>(s);
+ if (sp->mInstance != this)
+ NS_RUNTIMEABORT("Mismatched plugin data");
+
+ return PPluginStreamParent::Call__delete__(sp, reason, false) ?
+ NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+ }
+}
+
+void
+PluginInstanceParent::NPP_Print(NPPrint* platformPrint)
+{
+ // TODO: implement me
+ NS_ERROR("Not implemented");
+}
+
+PPluginScriptableObjectParent*
+PluginInstanceParent::AllocPPluginScriptableObjectParent()
+{
+ return new PluginScriptableObjectParent(Proxy);
+}
+
+bool
+PluginInstanceParent::DeallocPPluginScriptableObjectParent(
+ PPluginScriptableObjectParent* aObject)
+{
+ PluginScriptableObjectParent* actor =
+ static_cast<PluginScriptableObjectParent*>(aObject);
+
+ NPObject* object = actor->GetObject(false);
+ if (object) {
+ NS_ASSERTION(mScriptableObjects.Get(object, nullptr),
+ "NPObject not in the hash!");
+ mScriptableObjects.Remove(object);
+ }
+#ifdef DEBUG
+ else {
+ for (auto iter = mScriptableObjects.Iter(); !iter.Done(); iter.Next()) {
+ NS_ASSERTION(actor != iter.UserData(),
+ "Actor in the hash with a null NPObject!");
+ }
+ }
+#endif
+
+ delete actor;
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvPPluginScriptableObjectConstructor(
+ PPluginScriptableObjectParent* aActor)
+{
+ // This is only called in response to the child process requesting the
+ // creation of an actor. This actor will represent an NPObject that is
+ // created by the plugin and returned to the browser.
+ PluginScriptableObjectParent* actor =
+ static_cast<PluginScriptableObjectParent*>(aActor);
+ NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!");
+
+ actor->InitializeProxy();
+ NS_ASSERTION(actor->GetObject(false), "Actor should have an object!");
+
+ return true;
+}
+
+void
+PluginInstanceParent::NPP_URLNotify(const char* url, NPReason reason,
+ void* notifyData)
+{
+ PLUGIN_LOG_DEBUG(("%s (%s, %i, %p)",
+ FULLFUNCTION, url, (int) reason, notifyData));
+
+ PStreamNotifyParent* streamNotify =
+ static_cast<PStreamNotifyParent*>(notifyData);
+ Unused << PStreamNotifyParent::Send__delete__(streamNotify, reason);
+}
+
+bool
+PluginInstanceParent::RegisterNPObjectForActor(
+ NPObject* aObject,
+ PluginScriptableObjectParent* aActor)
+{
+ NS_ASSERTION(aObject && aActor, "Null pointers!");
+ NS_ASSERTION(!mScriptableObjects.Get(aObject, nullptr), "Duplicate entry!");
+ mScriptableObjects.Put(aObject, aActor);
+ return true;
+}
+
+void
+PluginInstanceParent::UnregisterNPObject(NPObject* aObject)
+{
+ NS_ASSERTION(aObject, "Null pointer!");
+ NS_ASSERTION(mScriptableObjects.Get(aObject, nullptr), "Unknown entry!");
+ mScriptableObjects.Remove(aObject);
+}
+
+PluginScriptableObjectParent*
+PluginInstanceParent::GetActorForNPObject(NPObject* aObject)
+{
+ NS_ASSERTION(aObject, "Null pointer!");
+
+ if (aObject->_class == PluginScriptableObjectParent::GetClass()) {
+ // One of ours!
+ ParentNPObject* object = static_cast<ParentNPObject*>(aObject);
+ NS_ASSERTION(object->parent, "Null actor!");
+ return object->parent;
+ }
+
+ PluginScriptableObjectParent* actor;
+ if (mScriptableObjects.Get(aObject, &actor)) {
+ return actor;
+ }
+
+ actor = new PluginScriptableObjectParent(LocalObject);
+ if (!SendPPluginScriptableObjectConstructor(actor)) {
+ NS_WARNING("Failed to send constructor message!");
+ return nullptr;
+ }
+
+ actor->InitializeLocal(aObject);
+ return actor;
+}
+
+PPluginSurfaceParent*
+PluginInstanceParent::AllocPPluginSurfaceParent(const WindowsSharedMemoryHandle& handle,
+ const mozilla::gfx::IntSize& size,
+ const bool& transparent)
+{
+#ifdef XP_WIN
+ return new PluginSurfaceParent(handle, size, transparent);
+#else
+ NS_ERROR("This shouldn't be called!");
+ return nullptr;
+#endif
+}
+
+bool
+PluginInstanceParent::DeallocPPluginSurfaceParent(PPluginSurfaceParent* s)
+{
+#ifdef XP_WIN
+ delete s;
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+PluginInstanceParent::AnswerNPN_PushPopupsEnabledState(const bool& aState)
+{
+ mNPNIface->pushpopupsenabledstate(mNPP, aState ? 1 : 0);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_PopPopupsEnabledState()
+{
+ mNPNIface->poppopupsenabledstate(mNPP);
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetValueForURL(const NPNURLVariable& variable,
+ const nsCString& url,
+ nsCString* value,
+ NPError* result)
+{
+ char* v;
+ uint32_t len;
+
+ *result = mNPNIface->getvalueforurl(mNPP, (NPNURLVariable) variable,
+ url.get(), &v, &len);
+ if (NPERR_NO_ERROR == *result)
+ value->Adopt(v, len);
+
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_SetValueForURL(const NPNURLVariable& variable,
+ const nsCString& url,
+ const nsCString& value,
+ NPError* result)
+{
+ *result = mNPNIface->setvalueforurl(mNPP, (NPNURLVariable) variable,
+ url.get(), value.get(),
+ value.Length());
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_GetAuthenticationInfo(const nsCString& protocol,
+ const nsCString& host,
+ const int32_t& port,
+ const nsCString& scheme,
+ const nsCString& realm,
+ nsCString* username,
+ nsCString* password,
+ NPError* result)
+{
+ char* u;
+ uint32_t ulen;
+ char* p;
+ uint32_t plen;
+
+ *result = mNPNIface->getauthenticationinfo(mNPP, protocol.get(),
+ host.get(), port,
+ scheme.get(), realm.get(),
+ &u, &ulen, &p, &plen);
+ if (NPERR_NO_ERROR == *result) {
+ username->Adopt(u, ulen);
+ password->Adopt(p, plen);
+ }
+ return true;
+}
+
+bool
+PluginInstanceParent::AnswerNPN_ConvertPoint(const double& sourceX,
+ const bool& ignoreDestX,
+ const double& sourceY,
+ const bool& ignoreDestY,
+ const NPCoordinateSpace& sourceSpace,
+ const NPCoordinateSpace& destSpace,
+ double *destX,
+ double *destY,
+ bool *result)
+{
+ *result = mNPNIface->convertpoint(mNPP, sourceX, sourceY, sourceSpace,
+ ignoreDestX ? nullptr : destX,
+ ignoreDestY ? nullptr : destY,
+ destSpace);
+
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvRedrawPlugin()
+{
+ nsNPAPIPluginInstance *inst = static_cast<nsNPAPIPluginInstance*>(mNPP->ndata);
+ if (!inst) {
+ return false;
+ }
+
+ inst->RedrawPlugin();
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvNegotiatedCarbon()
+{
+ nsNPAPIPluginInstance *inst = static_cast<nsNPAPIPluginInstance*>(mNPP->ndata);
+ if (!inst) {
+ return false;
+ }
+ inst->CarbonNPAPIFailure();
+ return true;
+}
+
+nsPluginInstanceOwner*
+PluginInstanceParent::GetOwner()
+{
+ nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(mNPP->ndata);
+ if (!inst) {
+ return nullptr;
+ }
+ return inst->GetOwner();
+}
+
+bool
+PluginInstanceParent::RecvAsyncNPP_NewResult(const NPError& aResult)
+{
+ // NB: mUseSurrogate must be cleared before doing anything else, especially
+ // calling NPP_SetWindow!
+ mUseSurrogate = false;
+
+ mSurrogate->AsyncCallArriving();
+ if (aResult == NPERR_NO_ERROR) {
+ mSurrogate->SetAcceptingCalls(true);
+ }
+
+ // It is possible for a plugin instance to outlive its owner (eg. When a
+ // PluginDestructionGuard was on the stack at the time the owner was being
+ // destroyed). We need to handle that case.
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (!owner) {
+ // We can't do anything at this point, just return. Any pending browser
+ // streams will be cleaned up when the plugin instance is destroyed.
+ return true;
+ }
+
+ if (aResult != NPERR_NO_ERROR) {
+ mSurrogate->NotifyAsyncInitFailed();
+ return true;
+ }
+
+ // Now we need to do a bunch of exciting post-NPP_New housekeeping.
+ owner->NotifyHostCreateWidget();
+
+ MOZ_ASSERT(mSurrogate);
+ mSurrogate->OnInstanceCreated(this);
+
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvSetNetscapeWindowAsParent(const NativeWindowHandle& childWindow)
+{
+#if defined(XP_WIN)
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (!owner || NS_FAILED(owner->SetNetscapeWindowAsParent(childWindow))) {
+ NS_WARNING("Failed to set Netscape window as parent.");
+ }
+
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceParent::RecvSetNetscapeWindowAsParent not implemented!");
+ return false;
+#endif
+}
+
+#if defined(OS_WIN)
+
+/*
+ plugin focus changes between processes
+
+ focus from dom -> child:
+ Focus manager calls on widget to set the focus on the window.
+ We pick up the resulting wm_setfocus event here, and forward
+ that over ipc to the child which calls set focus on itself.
+
+ focus from child -> focus manager:
+ Child picks up the local wm_setfocus and sends it via ipc over
+ here. We then post a custom event to widget/windows/nswindow
+ which fires off a gui event letting the browser know.
+*/
+
+static const wchar_t kPluginInstanceParentProperty[] =
+ L"PluginInstanceParentProperty";
+
+// static
+LRESULT CALLBACK
+PluginInstanceParent::PluginWindowHookProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ PluginInstanceParent* self = reinterpret_cast<PluginInstanceParent*>(
+ ::GetPropW(hWnd, kPluginInstanceParentProperty));
+ if (!self) {
+ NS_NOTREACHED("PluginInstanceParent::PluginWindowHookProc null this ptr!");
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+
+ NS_ASSERTION(self->mPluginHWND == hWnd, "Wrong window!");
+
+ switch (message) {
+ case WM_SETFOCUS:
+ // Let the child plugin window know it should take focus.
+ Unused << self->CallSetPluginFocus();
+ break;
+
+ case WM_CLOSE:
+ self->UnsubclassPluginWindow();
+ break;
+ }
+
+ if (self->mPluginWndProc == PluginWindowHookProc) {
+ NS_NOTREACHED(
+ "PluginWindowHookProc invoking mPluginWndProc w/"
+ "mPluginWndProc == PluginWindowHookProc????");
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return ::CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
+ lParam);
+}
+
+void
+PluginInstanceParent::SubclassPluginWindow(HWND aWnd)
+{
+ if ((aWnd && mPluginHWND == aWnd) || (!aWnd && mPluginHWND)) {
+ return;
+ }
+
+ if (XRE_IsContentProcess()) {
+ if (!aWnd) {
+ NS_WARNING("PluginInstanceParent::SubclassPluginWindow unexpected null window");
+ return;
+ }
+ mPluginHWND = aWnd; // now a remote window, we can't subclass this
+ mPluginWndProc = nullptr;
+ // Note sPluginInstanceList wil delete 'this' if we do not remove
+ // it on shutdown.
+ sPluginInstanceList->Put((void*)mPluginHWND, this);
+ return;
+ }
+
+ NS_ASSERTION(!(mPluginHWND && aWnd != mPluginHWND),
+ "PluginInstanceParent::SubclassPluginWindow hwnd is not our window!");
+
+ mPluginHWND = aWnd;
+ mPluginWndProc =
+ (WNDPROC)::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(PluginWindowHookProc));
+ DebugOnly<bool> bRes = ::SetPropW(mPluginHWND, kPluginInstanceParentProperty, this);
+ NS_ASSERTION(mPluginWndProc,
+ "PluginInstanceParent::SubclassPluginWindow failed to set subclass!");
+ NS_ASSERTION(bRes,
+ "PluginInstanceParent::SubclassPluginWindow failed to set prop!");
+}
+
+void
+PluginInstanceParent::UnsubclassPluginWindow()
+{
+ if (XRE_IsContentProcess()) {
+ if (mPluginHWND) {
+ // Remove 'this' from the plugin list safely
+ nsAutoPtr<PluginInstanceParent> tmp;
+ MOZ_ASSERT(sPluginInstanceList);
+ sPluginInstanceList->RemoveAndForget((void*)mPluginHWND, tmp);
+ tmp.forget();
+ if (!sPluginInstanceList->Count()) {
+ delete sPluginInstanceList;
+ sPluginInstanceList = nullptr;
+ }
+ }
+ mPluginHWND = nullptr;
+ return;
+ }
+
+ if (mPluginHWND && mPluginWndProc) {
+ ::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(mPluginWndProc));
+
+ ::RemovePropW(mPluginHWND, kPluginInstanceParentProperty);
+
+ mPluginWndProc = nullptr;
+ mPluginHWND = nullptr;
+ }
+}
+
+/* windowless drawing helpers */
+
+/*
+ * Origin info:
+ *
+ * windowless, offscreen:
+ *
+ * WM_WINDOWPOSCHANGED: origin is relative to container
+ * setwindow: origin is 0,0
+ * WM_PAINT: origin is 0,0
+ *
+ * windowless, native:
+ *
+ * WM_WINDOWPOSCHANGED: origin is relative to container
+ * setwindow: origin is relative to container
+ * WM_PAINT: origin is relative to container
+ *
+ * PluginInstanceParent:
+ *
+ * painting: mPluginPort (nsIntRect, saved in SetWindow)
+ */
+
+bool
+PluginInstanceParent::MaybeCreateAndParentChildPluginWindow()
+{
+ // On Windows we need to create and set the parent before we set the
+ // window on the plugin, or keyboard interaction will not work.
+ if (!mChildPluginHWND) {
+ if (!CallCreateChildPluginWindow(&mChildPluginHWND) ||
+ !mChildPluginHWND) {
+ return false;
+ }
+ }
+
+ // It's not clear if the parent window would ever change, but when this
+ // was done in the NPAPI child it used to allow for this.
+ if (mPluginHWND == mChildPluginsParentHWND) {
+ return true;
+ }
+
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (!owner) {
+ // We can't reparent without an owner, the plugin is probably shutting
+ // down, just return true to allow any calls to continue.
+ return true;
+ }
+
+ // Note that this call will probably cause a sync native message to the
+ // process that owns the child window.
+ owner->SetWidgetWindowAsParent(mChildPluginHWND);
+ mChildPluginsParentHWND = mPluginHWND;
+ return true;
+}
+
+void
+PluginInstanceParent::MaybeCreateChildPopupSurrogate()
+{
+ // Already created or not required for this plugin.
+ if (mChildPluginHWND || mWindowType != NPWindowTypeDrawable ||
+ !(mParent->GetQuirks() & QUIRK_WINLESS_TRACKPOPUP_HOOK)) {
+ return;
+ }
+
+ // We need to pass the netscape window down to be cached as part of the call
+ // to create the surrogate, because the reparenting of the surrogate in the
+ // main process can cause sync Windows messages to the plugin process, which
+ // then cause sync messages from the plugin child for the netscape window
+ // which causes a deadlock.
+ NativeWindowHandle netscapeWindow;
+ NPError result = mNPNIface->getvalue(mNPP, NPNVnetscapeWindow,
+ &netscapeWindow);
+ if (NPERR_NO_ERROR != result) {
+ NS_WARNING("Can't get netscape window to pass to plugin child.");
+ return;
+ }
+
+ if (!SendCreateChildPopupSurrogate(netscapeWindow)) {
+ NS_WARNING("Failed to create popup surrogate in child.");
+ }
+}
+
+#endif // defined(OS_WIN)
+
+bool
+PluginInstanceParent::AnswerPluginFocusChange(const bool& gotFocus)
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+
+ // Currently only in use on windows - an event we receive from the child
+ // when it's plugin window (or one of it's children) receives keyboard
+ // focus. We detect this and forward a notification here so we can update
+ // focus.
+#if defined(OS_WIN)
+ if (gotFocus) {
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (owner) {
+ nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+ nsCOMPtr<nsIDOMElement> element;
+ owner->GetDOMElement(getter_AddRefs(element));
+ if (fm && element) {
+ fm->SetFocus(element, 0);
+ }
+ }
+ }
+ return true;
+#else
+ NS_NOTREACHED("PluginInstanceParent::AnswerPluginFocusChange not implemented!");
+ return false;
+#endif
+}
+
+PluginInstanceParent*
+PluginInstanceParent::Cast(NPP aInstance, PluginAsyncSurrogate** aSurrogate)
+{
+ PluginDataResolver* resolver =
+ static_cast<PluginDataResolver*>(aInstance->pdata);
+
+ // If the plugin crashed and the PluginInstanceParent was deleted,
+ // aInstance->pdata will be nullptr.
+ if (!resolver) {
+ return nullptr;
+ }
+
+ PluginInstanceParent* instancePtr = resolver->GetInstance();
+
+ if (instancePtr && aInstance != instancePtr->mNPP) {
+ NS_RUNTIMEABORT("Corrupted plugin data.");
+ }
+
+ if (aSurrogate) {
+ *aSurrogate = resolver->GetAsyncSurrogate();
+ }
+
+ return instancePtr;
+}
+
+bool
+PluginInstanceParent::RecvGetCompositionString(const uint32_t& aIndex,
+ nsTArray<uint8_t>* aDist,
+ int32_t* aLength)
+{
+#if defined(OS_WIN)
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (!owner) {
+ *aLength = IMM_ERROR_GENERAL;
+ return true;
+ }
+
+ if (!owner->GetCompositionString(aIndex, aDist, aLength)) {
+ *aLength = IMM_ERROR_NODATA;
+ }
+#endif
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvSetCandidateWindow(
+ const mozilla::widget::CandidateWindowPosition& aPosition)
+{
+#if defined(OS_WIN)
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (owner) {
+ owner->SetCandidateWindow(aPosition);
+ }
+#endif
+ return true;
+}
+
+bool
+PluginInstanceParent::RecvRequestCommitOrCancel(const bool& aCommitted)
+{
+#if defined(OS_WIN)
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (owner) {
+ owner->RequestCommitOrCancel(aCommitted);
+ }
+#endif
+ return true;
+}
+
+nsresult
+PluginInstanceParent::HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData,
+ bool aIsConsumed)
+{
+ if (NS_WARN_IF(!SendHandledWindowedPluginKeyEvent(aKeyEventData,
+ aIsConsumed))) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+bool
+PluginInstanceParent::RecvOnWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData)
+{
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (NS_WARN_IF(!owner)) {
+ // Notifies the plugin process of the key event being not consumed
+ // by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return true;
+ }
+ owner->OnWindowedPluginKeyEvent(aKeyEventData);
+ return true;
+}
+
+void
+PluginInstanceParent::RecordDrawingModel()
+{
+ int mode = -1;
+ switch (mWindowType) {
+ case NPWindowTypeWindow:
+ // We use 0=windowed since there is no specific NPDrawingModel value.
+ mode = 0;
+ break;
+ case NPWindowTypeDrawable:
+ mode = mDrawingModel + 1;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("bad window type");
+ return;
+ }
+
+ if (mode == mLastRecordedDrawingModel) {
+ return;
+ }
+ MOZ_ASSERT(mode >= 0);
+
+ Telemetry::Accumulate(Telemetry::PLUGIN_DRAWING_MODEL, mode);
+ mLastRecordedDrawingModel = mode;
+}
diff --git a/dom/plugins/ipc/PluginInstanceParent.h b/dom/plugins/ipc/PluginInstanceParent.h
new file mode 100644
index 000000000..b2feafacc
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -0,0 +1,475 @@
+/* -*- 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 dom_plugins_PluginInstanceParent_h
+#define dom_plugins_PluginInstanceParent_h 1
+
+#include "mozilla/plugins/PPluginInstanceParent.h"
+#include "mozilla/plugins/PluginScriptableObjectParent.h"
+#if defined(OS_WIN)
+#include "mozilla/gfx/SharedDIBWin.h"
+#include <d3d10_1.h>
+#include "nsRefPtrHashtable.h"
+#elif defined(MOZ_WIDGET_COCOA)
+#include "mozilla/gfx/QuartzSupport.h"
+#endif
+
+#include "npfunctions.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsRect.h"
+#include "PluginDataResolver.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/EventForwards.h"
+
+class gfxASurface;
+class gfxContext;
+class nsPluginInstanceOwner;
+
+namespace mozilla {
+namespace layers {
+class Image;
+class ImageContainer;
+class TextureClientRecycleAllocator;
+} // namespace layers
+namespace plugins {
+
+class PBrowserStreamParent;
+class PluginModuleParent;
+class D3D11SurfaceHolder;
+
+class PluginInstanceParent : public PPluginInstanceParent
+ , public PluginDataResolver
+{
+ friend class PluginModuleParent;
+ friend class BrowserStreamParent;
+ friend class PluginStreamParent;
+ friend class StreamNotifyParent;
+
+#if defined(XP_WIN)
+public:
+ /**
+ * Helper method for looking up instances based on a supplied id.
+ */
+ static PluginInstanceParent*
+ LookupPluginInstanceByID(uintptr_t aId);
+#endif // defined(XP_WIN)
+
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ PluginInstanceParent(PluginModuleParent* parent,
+ NPP npp,
+ const nsCString& mimeType,
+ const NPNetscapeFuncs* npniface);
+
+ virtual ~PluginInstanceParent();
+
+ bool InitMetadata(const nsACString& aMimeType,
+ const nsACString& aSrcAttribute);
+ NPError Destroy();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ virtual PPluginScriptableObjectParent*
+ AllocPPluginScriptableObjectParent() override;
+
+ virtual bool
+ RecvPPluginScriptableObjectConstructor(PPluginScriptableObjectParent* aActor) override;
+
+ virtual bool
+ DeallocPPluginScriptableObjectParent(PPluginScriptableObjectParent* aObject) override;
+ virtual PBrowserStreamParent*
+ AllocPBrowserStreamParent(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyParent* notifyData,
+ const nsCString& headers) override;
+ virtual bool
+ DeallocPBrowserStreamParent(PBrowserStreamParent* stream) override;
+
+ virtual PPluginStreamParent*
+ AllocPPluginStreamParent(const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result) override;
+ virtual bool
+ DeallocPPluginStreamParent(PPluginStreamParent* stream) override;
+
+ virtual bool
+ AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value,
+ NPError* result) override;
+ virtual bool
+ AnswerNPN_GetValue_NPNVWindowNPObject(
+ PPluginScriptableObjectParent** value,
+ NPError* result) override;
+ virtual bool
+ AnswerNPN_GetValue_NPNVPluginElementNPObject(
+ PPluginScriptableObjectParent** value,
+ NPError* result) override;
+ virtual bool
+ AnswerNPN_GetValue_NPNVprivateModeBool(bool* value, NPError* result) override;
+
+ virtual bool
+ AnswerNPN_GetValue_DrawingModelSupport(const NPNVariable& model, bool* value) override;
+
+ virtual bool
+ AnswerNPN_GetValue_NPNVdocumentOrigin(nsCString* value, NPError* result) override;
+
+ virtual bool
+ AnswerNPN_GetValue_SupportsAsyncBitmapSurface(bool* value) override;
+
+ virtual bool
+ AnswerNPN_GetValue_SupportsAsyncDXGISurface(bool* value) override;
+
+ virtual bool
+ AnswerNPN_GetValue_PreferredDXGIAdapter(DxgiAdapterDesc* desc) override;
+
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginWindow(const bool& windowed, NPError* result) override;
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginTransparent(const bool& transparent,
+ NPError* result) override;
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginUsesDOMForCursor(const bool& useDOMForCursor,
+ NPError* result) override;
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginDrawingModel(const int& drawingModel,
+ NPError* result) override;
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginEventModel(const int& eventModel,
+ NPError* result) override;
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(const bool& isAudioPlaying,
+ NPError* result) override;
+
+ virtual bool
+ AnswerNPN_GetURL(const nsCString& url, const nsCString& target,
+ NPError *result) override;
+
+ virtual bool
+ AnswerNPN_PostURL(const nsCString& url, const nsCString& target,
+ const nsCString& buffer, const bool& file,
+ NPError* result) override;
+
+ virtual PStreamNotifyParent*
+ AllocPStreamNotifyParent(const nsCString& url, const nsCString& target,
+ const bool& post, const nsCString& buffer,
+ const bool& file,
+ NPError* result) override;
+
+ virtual bool
+ AnswerPStreamNotifyConstructor(PStreamNotifyParent* actor,
+ const nsCString& url,
+ const nsCString& target,
+ const bool& post, const nsCString& buffer,
+ const bool& file,
+ NPError* result) override;
+
+ virtual bool
+ DeallocPStreamNotifyParent(PStreamNotifyParent* notifyData) override;
+
+ virtual bool
+ RecvNPN_InvalidateRect(const NPRect& rect) override;
+
+ virtual bool
+ RecvRevokeCurrentDirectSurface() override;
+
+ virtual bool
+ RecvInitDXGISurface(const gfx::SurfaceFormat& format,
+ const gfx::IntSize& size,
+ WindowsHandle* outHandle,
+ NPError* outError) override;
+ virtual bool
+ RecvFinalizeDXGISurface(const WindowsHandle& handle) override;
+
+ virtual bool
+ RecvShowDirectBitmap(Shmem&& buffer,
+ const gfx::SurfaceFormat& format,
+ const uint32_t& stride,
+ const gfx::IntSize& size,
+ const gfx::IntRect& dirty) override;
+
+ virtual bool
+ RecvShowDirectDXGISurface(const WindowsHandle& handle,
+ const gfx::IntRect& rect) override;
+
+ // Async rendering
+ virtual bool
+ RecvShow(const NPRect& updatedRect,
+ const SurfaceDescriptor& newSurface,
+ SurfaceDescriptor* prevSurface) override;
+
+ virtual PPluginSurfaceParent*
+ AllocPPluginSurfaceParent(const WindowsSharedMemoryHandle& handle,
+ const mozilla::gfx::IntSize& size,
+ const bool& transparent) override;
+
+ virtual bool
+ DeallocPPluginSurfaceParent(PPluginSurfaceParent* s) override;
+
+ virtual bool
+ AnswerNPN_PushPopupsEnabledState(const bool& aState) override;
+
+ virtual bool
+ AnswerNPN_PopPopupsEnabledState() override;
+
+ virtual bool
+ AnswerNPN_GetValueForURL(const NPNURLVariable& variable,
+ const nsCString& url,
+ nsCString* value, NPError* result) override;
+
+ virtual bool
+ AnswerNPN_SetValueForURL(const NPNURLVariable& variable,
+ const nsCString& url,
+ const nsCString& value, NPError* result) override;
+
+ virtual bool
+ AnswerNPN_GetAuthenticationInfo(const nsCString& protocol,
+ const nsCString& host,
+ const int32_t& port,
+ const nsCString& scheme,
+ const nsCString& realm,
+ nsCString* username,
+ nsCString* password,
+ NPError* result) override;
+
+ virtual bool
+ AnswerNPN_ConvertPoint(const double& sourceX,
+ const bool& ignoreDestX,
+ const double& sourceY,
+ const bool& ignoreDestY,
+ const NPCoordinateSpace& sourceSpace,
+ const NPCoordinateSpace& destSpace,
+ double *destX,
+ double *destY,
+ bool *result) override;
+
+ virtual bool
+ RecvRedrawPlugin() override;
+
+ virtual bool
+ RecvNegotiatedCarbon() override;
+
+ virtual bool
+ RecvAsyncNPP_NewResult(const NPError& aResult) override;
+
+ virtual bool
+ RecvSetNetscapeWindowAsParent(const NativeWindowHandle& childWindow) override;
+
+ NPError NPP_SetWindow(const NPWindow* aWindow);
+
+ NPError NPP_GetValue(NPPVariable variable, void* retval);
+ NPError NPP_SetValue(NPNVariable variable, void* value);
+
+ void NPP_URLRedirectNotify(const char* url, int32_t status,
+ void* notifyData);
+
+ NPError NPP_NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16_t* stype);
+ NPError NPP_DestroyStream(NPStream* stream, NPReason reason);
+
+ void NPP_Print(NPPrint* platformPrint);
+
+ int16_t NPP_HandleEvent(void* event);
+
+ void NPP_URLNotify(const char* url, NPReason reason, void* notifyData);
+
+ PluginModuleParent* Module()
+ {
+ return mParent;
+ }
+
+ const NPNetscapeFuncs* GetNPNIface()
+ {
+ return mNPNIface;
+ }
+
+ bool
+ RegisterNPObjectForActor(NPObject* aObject,
+ PluginScriptableObjectParent* aActor);
+
+ void
+ UnregisterNPObject(NPObject* aObject);
+
+ PluginScriptableObjectParent*
+ GetActorForNPObject(NPObject* aObject);
+
+ NPP
+ GetNPP()
+ {
+ return mNPP;
+ }
+
+ bool
+ UseSurrogate() const
+ {
+ return mUseSurrogate;
+ }
+
+ void
+ GetSrcAttribute(nsACString& aOutput) const
+ {
+ aOutput = mSrcAttribute;
+ }
+
+ virtual bool
+ AnswerPluginFocusChange(const bool& gotFocus) override;
+
+ nsresult AsyncSetWindow(NPWindow* window);
+ nsresult GetImageContainer(mozilla::layers::ImageContainer** aContainer);
+ nsresult GetImageSize(nsIntSize* aSize);
+#ifdef XP_MACOSX
+ nsresult IsRemoteDrawingCoreAnimation(bool *aDrawing);
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
+#endif
+ nsresult SetBackgroundUnknown();
+ nsresult BeginUpdateBackground(const nsIntRect& aRect,
+ DrawTarget** aDrawTarget);
+ nsresult EndUpdateBackground(const nsIntRect& aRect);
+#if defined(XP_WIN)
+ nsresult SetScrollCaptureId(uint64_t aScrollCaptureId);
+ nsresult GetScrollCaptureContainer(mozilla::layers::ImageContainer** aContainer);
+#endif
+ void DidComposite();
+
+ bool IsUsingDirectDrawing();
+
+ virtual PluginAsyncSurrogate* GetAsyncSurrogate() override;
+
+ virtual PluginInstanceParent* GetInstance() override { return this; }
+
+ static PluginInstanceParent* Cast(NPP instance,
+ PluginAsyncSurrogate** aSurrogate = nullptr);
+
+ // for IME hook
+ virtual bool
+ RecvGetCompositionString(const uint32_t& aIndex,
+ nsTArray<uint8_t>* aBuffer,
+ int32_t* aLength) override;
+ virtual bool
+ RecvSetCandidateWindow(
+ const mozilla::widget::CandidateWindowPosition& aPosition) override;
+ virtual bool
+ RecvRequestCommitOrCancel(const bool& aCommitted) override;
+
+ // for reserved shortcut key handling with windowed plugin on Windows
+ nsresult HandledWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData,
+ bool aIsConsumed);
+ virtual bool
+ RecvOnWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData) override;
+
+private:
+ // Create an appropriate platform surface for a background of size
+ // |aSize|. Return true if successful.
+ bool CreateBackground(const nsIntSize& aSize);
+ void DestroyBackground();
+ SurfaceDescriptor BackgroundDescriptor() /*const*/;
+
+ typedef mozilla::layers::ImageContainer ImageContainer;
+ ImageContainer *GetImageContainer();
+
+ virtual PPluginBackgroundDestroyerParent*
+ AllocPPluginBackgroundDestroyerParent() override;
+
+ virtual bool
+ DeallocPPluginBackgroundDestroyerParent(PPluginBackgroundDestroyerParent* aActor) override;
+
+ bool InternalGetValueForNPObject(NPNVariable aVariable,
+ PPluginScriptableObjectParent** aValue,
+ NPError* aResult);
+
+ nsPluginInstanceOwner* GetOwner();
+
+ void SetCurrentImage(layers::Image* aImage);
+
+ // Update Telemetry with the current drawing model.
+ void RecordDrawingModel();
+
+private:
+ PluginModuleParent* mParent;
+ RefPtr<PluginAsyncSurrogate> mSurrogate;
+ bool mUseSurrogate;
+ NPP mNPP;
+ const NPNetscapeFuncs* mNPNIface;
+ nsCString mSrcAttribute;
+ NPWindowType mWindowType;
+ int16_t mDrawingModel;
+
+ // Since plugins may request different drawing models to find a compatible
+ // one, we only record the drawing model after a SetWindow call and if the
+ // drawing model has changed.
+ int mLastRecordedDrawingModel;
+
+ nsDataHashtable<nsPtrHashKey<NPObject>, PluginScriptableObjectParent*> mScriptableObjects;
+
+ // This is used to tell the compositor that it should invalidate the ImageLayer.
+ uint32_t mFrameID;
+
+#if defined(XP_WIN)
+ // Note: DXGI 1.1 surface handles are global across all processes, and are not
+ // marshaled. As long as we haven't freed a texture its handle should be valid
+ // as a unique cross-process identifier for the texture.
+ nsRefPtrHashtable<nsPtrHashKey<void>, D3D11SurfaceHolder> mD3D11Surfaces;
+#endif
+
+#if defined(OS_WIN)
+private:
+ // Used in handling parent/child forwarding of events.
+ static LRESULT CALLBACK PluginWindowHookProc(HWND hWnd, UINT message,
+ WPARAM wParam, LPARAM lParam);
+ void SubclassPluginWindow(HWND aWnd);
+ void UnsubclassPluginWindow();
+
+ bool MaybeCreateAndParentChildPluginWindow();
+ void MaybeCreateChildPopupSurrogate();
+
+private:
+ nsIntRect mPluginPort;
+ nsIntRect mSharedSize;
+ HWND mPluginHWND;
+ // This is used for the normal child plugin HWND for windowed plugins and,
+ // if needed, also the child popup surrogate HWND for windowless plugins.
+ HWND mChildPluginHWND;
+ HWND mChildPluginsParentHWND;
+ WNDPROC mPluginWndProc;
+ bool mNestedEventState;
+#endif // defined(XP_WIN)
+#if defined(MOZ_WIDGET_COCOA)
+private:
+ Shmem mShSurface;
+ uint16_t mShWidth;
+ uint16_t mShHeight;
+ CGColorSpaceRef mShColorSpace;
+ RefPtr<MacIOSurface> mIOSurface;
+ RefPtr<MacIOSurface> mFrontIOSurface;
+#endif // definied(MOZ_WIDGET_COCOA)
+
+ // ObjectFrame layer wrapper
+ RefPtr<gfxASurface> mFrontSurface;
+ // For windowless+transparent instances, this surface contains a
+ // "pretty recent" copy of the pixels under its <object> frame.
+ // On the plugin side, we use this surface to avoid doing alpha
+ // recovery when possible. This surface is created and owned by
+ // the browser, but a "read-only" reference is sent to the plugin.
+ //
+ // We have explicitly chosen not to provide any guarantees about
+ // the consistency of the pixels in |mBackground|. A plugin may
+ // be able to observe partial updates to the background.
+ RefPtr<gfxASurface> mBackground;
+
+ RefPtr<ImageContainer> mImageContainer;
+};
+
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // ifndef dom_plugins_PluginInstanceParent_h
diff --git a/dom/plugins/ipc/PluginInterposeOSX.h b/dom/plugins/ipc/PluginInterposeOSX.h
new file mode 100644
index 000000000..2a742b8aa
--- /dev/null
+++ b/dom/plugins/ipc/PluginInterposeOSX.h
@@ -0,0 +1,137 @@
+// vim:set ts=2 sts=2 sw=2 et cin:
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+//
+// 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 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 DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H
+#define DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H
+
+#include "base/basictypes.h"
+#include "nsPoint.h"
+#include "npapi.h"
+
+// Make this includable from non-Objective-C code.
+#ifndef __OBJC__
+class NSCursor;
+#else
+#import <Cocoa/Cocoa.h>
+#endif
+
+// The header file QuickdrawAPI.h is missing on OS X 10.7 and up (though the
+// QuickDraw APIs defined in it are still present) -- so we need to supply the
+// relevant parts of its contents here. It's likely that Apple will eventually
+// remove the APIs themselves (probably in OS X 10.8), so we need to make them
+// weak imports, and test for their presence before using them.
+#if !defined(__QUICKDRAWAPI__)
+
+typedef short Bits16[16];
+struct Cursor {
+ Bits16 data;
+ Bits16 mask;
+ Point hotSpot;
+};
+typedef struct Cursor Cursor;
+
+#endif /* __QUICKDRAWAPI__ */
+
+namespace mac_plugin_interposing {
+
+// Class used to serialize NSCursor objects over IPC between processes.
+class NSCursorInfo {
+public:
+ enum Type {
+ TypeCustom,
+ TypeArrow,
+ TypeClosedHand,
+ TypeContextualMenu, // Only supported on OS X 10.6 and up
+ TypeCrosshair,
+ TypeDisappearingItem,
+ TypeDragCopy, // Only supported on OS X 10.6 and up
+ TypeDragLink, // Only supported on OS X 10.6 and up
+ TypeIBeam,
+ TypeNotAllowed, // Only supported on OS X 10.6 and up
+ TypeOpenHand,
+ TypePointingHand,
+ TypeResizeDown,
+ TypeResizeLeft,
+ TypeResizeLeftRight,
+ TypeResizeRight,
+ TypeResizeUp,
+ TypeResizeUpDown,
+ TypeTransparent // Special type
+ };
+
+ NSCursorInfo();
+ explicit NSCursorInfo(NSCursor* aCursor);
+ explicit NSCursorInfo(const Cursor* aCursor);
+ ~NSCursorInfo();
+
+ NSCursor* GetNSCursor() const;
+ Type GetType() const;
+ const char* GetTypeName() const;
+ nsPoint GetHotSpot() const;
+ uint8_t* GetCustomImageData() const;
+ uint32_t GetCustomImageDataLength() const;
+
+ void SetType(Type aType);
+ void SetHotSpot(nsPoint aHotSpot);
+ void SetCustomImageData(uint8_t* aData, uint32_t aDataLength);
+
+ static bool GetNativeCursorsSupported();
+
+private:
+ NSCursor* GetTransparentCursor() const;
+
+ Type mType;
+ // The hot spot's coordinate system is the cursor's coordinate system, and
+ // has an upper-left origin (in both Cocoa and pre-Cocoa systems).
+ nsPoint mHotSpot;
+ uint8_t* mCustomImageData;
+ uint32_t mCustomImageDataLength;
+ static int32_t mNativeCursorsSupported;
+};
+
+namespace parent {
+
+void OnPluginShowWindow(uint32_t window_id, CGRect window_bounds, bool modal);
+void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid);
+void OnSetCursor(const NSCursorInfo& cursorInfo);
+void OnShowCursor(bool show);
+void OnPushCursor(const NSCursorInfo& cursorInfo);
+void OnPopCursor();
+
+} // namespace parent
+
+namespace child {
+
+void SetUpCocoaInterposing();
+
+} // namespace child
+
+} // namespace mac_plugin_interposing
+
+#endif /* DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H */
diff --git a/dom/plugins/ipc/PluginInterposeOSX.mm b/dom/plugins/ipc/PluginInterposeOSX.mm
new file mode 100644
index 000000000..bf24d2b0d
--- /dev/null
+++ b/dom/plugins/ipc/PluginInterposeOSX.mm
@@ -0,0 +1,1158 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+//
+// 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 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.
+
+#include "base/basictypes.h"
+#include "nsCocoaUtils.h"
+#include "PluginModuleChild.h"
+#include "nsDebug.h"
+#include "PluginInterposeOSX.h"
+#include <set>
+#import <AppKit/AppKit.h>
+#import <objc/runtime.h>
+#import <Carbon/Carbon.h>
+
+using namespace mozilla::plugins;
+
+namespace mac_plugin_interposing {
+
+int32_t NSCursorInfo::mNativeCursorsSupported = -1;
+
+// This constructor may be called from the browser process or the plugin
+// process.
+NSCursorInfo::NSCursorInfo()
+ : mType(TypeArrow)
+ , mHotSpot(nsPoint(0, 0))
+ , mCustomImageData(NULL)
+ , mCustomImageDataLength(0)
+{
+}
+
+NSCursorInfo::NSCursorInfo(NSCursor* aCursor)
+ : mType(TypeArrow)
+ , mHotSpot(nsPoint(0, 0))
+ , mCustomImageData(NULL)
+ , mCustomImageDataLength(0)
+{
+ // This constructor is only ever called from the plugin process, so the
+ // following is safe.
+ if (!GetNativeCursorsSupported()) {
+ return;
+ }
+
+ NSPoint hotSpotCocoa = [aCursor hotSpot];
+ mHotSpot = nsPoint(hotSpotCocoa.x, hotSpotCocoa.y);
+
+ Class nsCursorClass = [NSCursor class];
+ if ([aCursor isEqual:[NSCursor arrowCursor]]) {
+ mType = TypeArrow;
+ } else if ([aCursor isEqual:[NSCursor closedHandCursor]]) {
+ mType = TypeClosedHand;
+ } else if ([aCursor isEqual:[NSCursor crosshairCursor]]) {
+ mType = TypeCrosshair;
+ } else if ([aCursor isEqual:[NSCursor disappearingItemCursor]]) {
+ mType = TypeDisappearingItem;
+ } else if ([aCursor isEqual:[NSCursor IBeamCursor]]) {
+ mType = TypeIBeam;
+ } else if ([aCursor isEqual:[NSCursor openHandCursor]]) {
+ mType = TypeOpenHand;
+ } else if ([aCursor isEqual:[NSCursor pointingHandCursor]]) {
+ mType = TypePointingHand;
+ } else if ([aCursor isEqual:[NSCursor resizeDownCursor]]) {
+ mType = TypeResizeDown;
+ } else if ([aCursor isEqual:[NSCursor resizeLeftCursor]]) {
+ mType = TypeResizeLeft;
+ } else if ([aCursor isEqual:[NSCursor resizeLeftRightCursor]]) {
+ mType = TypeResizeLeftRight;
+ } else if ([aCursor isEqual:[NSCursor resizeRightCursor]]) {
+ mType = TypeResizeRight;
+ } else if ([aCursor isEqual:[NSCursor resizeUpCursor]]) {
+ mType = TypeResizeUp;
+ } else if ([aCursor isEqual:[NSCursor resizeUpDownCursor]]) {
+ mType = TypeResizeUpDown;
+ // The following cursor types are only supported on OS X 10.6 and up.
+ } else if ([nsCursorClass respondsToSelector:@selector(contextualMenuCursor)] &&
+ [aCursor isEqual:[nsCursorClass performSelector:@selector(contextualMenuCursor)]]) {
+ mType = TypeContextualMenu;
+ } else if ([nsCursorClass respondsToSelector:@selector(dragCopyCursor)] &&
+ [aCursor isEqual:[nsCursorClass performSelector:@selector(dragCopyCursor)]]) {
+ mType = TypeDragCopy;
+ } else if ([nsCursorClass respondsToSelector:@selector(dragLinkCursor)] &&
+ [aCursor isEqual:[nsCursorClass performSelector:@selector(dragLinkCursor)]]) {
+ mType = TypeDragLink;
+ } else if ([nsCursorClass respondsToSelector:@selector(operationNotAllowedCursor)] &&
+ [aCursor isEqual:[nsCursorClass performSelector:@selector(operationNotAllowedCursor)]]) {
+ mType = TypeNotAllowed;
+ } else {
+ NSImage* image = [aCursor image];
+ NSArray* reps = image ? [image representations] : nil;
+ NSUInteger repsCount = reps ? [reps count] : 0;
+ if (!repsCount) {
+ // If we have a custom cursor with no image representations, assume we
+ // need a transparent cursor.
+ mType = TypeTransparent;
+ } else {
+ CGImageRef cgImage = nil;
+ // XXX We don't know how to deal with a cursor that doesn't have a
+ // bitmap image representation. For now we fall back to an arrow
+ // cursor.
+ for (NSUInteger i = 0; i < repsCount; ++i) {
+ id rep = [reps objectAtIndex:i];
+ if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
+ cgImage = [(NSBitmapImageRep*)rep CGImage];
+ break;
+ }
+ }
+ if (cgImage) {
+ CFMutableDataRef data = ::CFDataCreateMutable(kCFAllocatorDefault, 0);
+ if (data) {
+ CGImageDestinationRef dest = ::CGImageDestinationCreateWithData(data,
+ kUTTypePNG,
+ 1,
+ NULL);
+ if (dest) {
+ ::CGImageDestinationAddImage(dest, cgImage, NULL);
+ if (::CGImageDestinationFinalize(dest)) {
+ uint32_t dataLength = (uint32_t) ::CFDataGetLength(data);
+ mCustomImageData = (uint8_t*) moz_xmalloc(dataLength);
+ ::CFDataGetBytes(data, ::CFRangeMake(0, dataLength), mCustomImageData);
+ mCustomImageDataLength = dataLength;
+ mType = TypeCustom;
+ }
+ ::CFRelease(dest);
+ }
+ ::CFRelease(data);
+ }
+ }
+ if (!mCustomImageData) {
+ mType = TypeArrow;
+ }
+ }
+ }
+}
+
+NSCursorInfo::NSCursorInfo(const Cursor* aCursor)
+ : mType(TypeArrow)
+ , mHotSpot(nsPoint(0, 0))
+ , mCustomImageData(NULL)
+ , mCustomImageDataLength(0)
+{
+ // This constructor is only ever called from the plugin process, so the
+ // following is safe.
+ if (!GetNativeCursorsSupported()) {
+ return;
+ }
+
+ mHotSpot = nsPoint(aCursor->hotSpot.h, aCursor->hotSpot.v);
+
+ int width = 16, height = 16;
+ int bytesPerPixel = 4;
+ int rowBytes = width * bytesPerPixel;
+ int bitmapSize = height * rowBytes;
+
+ bool isTransparent = true;
+
+ uint8_t* bitmap = (uint8_t*) moz_xmalloc(bitmapSize);
+ // The way we create 'bitmap' is largely "borrowed" from Chrome's
+ // WebCursor::InitFromCursor().
+ for (int y = 0; y < height; ++y) {
+ unsigned short data = aCursor->data[y];
+ unsigned short mask = aCursor->mask[y];
+ // Change 'data' and 'mask' from big-endian to little-endian, but output
+ // big-endian data below.
+ data = ((data << 8) & 0xFF00) | ((data >> 8) & 0x00FF);
+ mask = ((mask << 8) & 0xFF00) | ((mask >> 8) & 0x00FF);
+ // It'd be nice to use a gray-scale bitmap. But
+ // CGBitmapContextCreateImage() (used below) won't work with one that also
+ // has alpha values.
+ for (int x = 0; x < width; ++x) {
+ int offset = (y * rowBytes) + (x * bytesPerPixel);
+ // Color value
+ if (data & 0x8000) {
+ bitmap[offset] = 0x0;
+ bitmap[offset + 1] = 0x0;
+ bitmap[offset + 2] = 0x0;
+ } else {
+ bitmap[offset] = 0xFF;
+ bitmap[offset + 1] = 0xFF;
+ bitmap[offset + 2] = 0xFF;
+ }
+ // Mask value
+ if (mask & 0x8000) {
+ bitmap[offset + 3] = 0xFF;
+ isTransparent = false;
+ } else {
+ bitmap[offset + 3] = 0x0;
+ }
+ data <<= 1;
+ mask <<= 1;
+ }
+ }
+
+ if (isTransparent) {
+ // If aCursor is transparent, we don't need to serialize custom cursor
+ // data over IPC.
+ mType = TypeTransparent;
+ } else {
+ CGColorSpaceRef color = ::CGColorSpaceCreateDeviceRGB();
+ if (color) {
+ CGContextRef context =
+ ::CGBitmapContextCreate(bitmap,
+ width,
+ height,
+ 8,
+ rowBytes,
+ color,
+ kCGImageAlphaPremultipliedLast |
+ kCGBitmapByteOrder32Big);
+ if (context) {
+ CGImageRef image = ::CGBitmapContextCreateImage(context);
+ if (image) {
+ ::CFMutableDataRef data = ::CFDataCreateMutable(kCFAllocatorDefault, 0);
+ if (data) {
+ CGImageDestinationRef dest =
+ ::CGImageDestinationCreateWithData(data,
+ kUTTypePNG,
+ 1,
+ NULL);
+ if (dest) {
+ ::CGImageDestinationAddImage(dest, image, NULL);
+ if (::CGImageDestinationFinalize(dest)) {
+ uint32_t dataLength = (uint32_t) ::CFDataGetLength(data);
+ mCustomImageData = (uint8_t*) moz_xmalloc(dataLength);
+ ::CFDataGetBytes(data,
+ ::CFRangeMake(0, dataLength),
+ mCustomImageData);
+ mCustomImageDataLength = dataLength;
+ mType = TypeCustom;
+ }
+ ::CFRelease(dest);
+ }
+ ::CFRelease(data);
+ }
+ ::CGImageRelease(image);
+ }
+ ::CGContextRelease(context);
+ }
+ ::CGColorSpaceRelease(color);
+ }
+ }
+
+ free(bitmap);
+}
+
+NSCursorInfo::~NSCursorInfo()
+{
+ if (mCustomImageData) {
+ free(mCustomImageData);
+ }
+}
+
+NSCursor* NSCursorInfo::GetNSCursor() const
+{
+ NSCursor* retval = nil;
+
+ Class nsCursorClass = [NSCursor class];
+ switch(mType) {
+ case TypeArrow:
+ retval = [NSCursor arrowCursor];
+ break;
+ case TypeClosedHand:
+ retval = [NSCursor closedHandCursor];
+ break;
+ case TypeCrosshair:
+ retval = [NSCursor crosshairCursor];
+ break;
+ case TypeDisappearingItem:
+ retval = [NSCursor disappearingItemCursor];
+ break;
+ case TypeIBeam:
+ retval = [NSCursor IBeamCursor];
+ break;
+ case TypeOpenHand:
+ retval = [NSCursor openHandCursor];
+ break;
+ case TypePointingHand:
+ retval = [NSCursor pointingHandCursor];
+ break;
+ case TypeResizeDown:
+ retval = [NSCursor resizeDownCursor];
+ break;
+ case TypeResizeLeft:
+ retval = [NSCursor resizeLeftCursor];
+ break;
+ case TypeResizeLeftRight:
+ retval = [NSCursor resizeLeftRightCursor];
+ break;
+ case TypeResizeRight:
+ retval = [NSCursor resizeRightCursor];
+ break;
+ case TypeResizeUp:
+ retval = [NSCursor resizeUpCursor];
+ break;
+ case TypeResizeUpDown:
+ retval = [NSCursor resizeUpDownCursor];
+ break;
+ // The following four cursor types are only supported on OS X 10.6 and up.
+ case TypeContextualMenu: {
+ if ([nsCursorClass respondsToSelector:@selector(contextualMenuCursor)]) {
+ retval = [nsCursorClass performSelector:@selector(contextualMenuCursor)];
+ }
+ break;
+ }
+ case TypeDragCopy: {
+ if ([nsCursorClass respondsToSelector:@selector(dragCopyCursor)]) {
+ retval = [nsCursorClass performSelector:@selector(dragCopyCursor)];
+ }
+ break;
+ }
+ case TypeDragLink: {
+ if ([nsCursorClass respondsToSelector:@selector(dragLinkCursor)]) {
+ retval = [nsCursorClass performSelector:@selector(dragLinkCursor)];
+ }
+ break;
+ }
+ case TypeNotAllowed: {
+ if ([nsCursorClass respondsToSelector:@selector(operationNotAllowedCursor)]) {
+ retval = [nsCursorClass performSelector:@selector(operationNotAllowedCursor)];
+ }
+ break;
+ }
+ case TypeTransparent:
+ retval = GetTransparentCursor();
+ break;
+ default:
+ break;
+ }
+
+ if (!retval && mCustomImageData && mCustomImageDataLength) {
+ CGDataProviderRef provider = ::CGDataProviderCreateWithData(NULL,
+ (const void*)mCustomImageData,
+ mCustomImageDataLength,
+ NULL);
+ if (provider) {
+ CGImageRef cgImage = ::CGImageCreateWithPNGDataProvider(provider,
+ NULL,
+ false,
+ kCGRenderingIntentDefault);
+ if (cgImage) {
+ NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
+ if (rep) {
+ NSImage* image = [[NSImage alloc] init];
+ if (image) {
+ [image addRepresentation:rep];
+ retval = [[[NSCursor alloc] initWithImage:image
+ hotSpot:NSMakePoint(mHotSpot.x, mHotSpot.y)]
+ autorelease];
+ [image release];
+ }
+ [rep release];
+ }
+ ::CGImageRelease(cgImage);
+ }
+ ::CFRelease(provider);
+ }
+ }
+
+ // Fall back to an arrow cursor if need be.
+ if (!retval) {
+ retval = [NSCursor arrowCursor];
+ }
+
+ return retval;
+}
+
+// Get a transparent cursor with the appropriate hot spot. We need one if
+// (for example) we have a custom cursor with no image data.
+NSCursor* NSCursorInfo::GetTransparentCursor() const
+{
+ NSCursor* retval = nil;
+
+ int width = 16, height = 16;
+ int bytesPerPixel = 2;
+ int rowBytes = width * bytesPerPixel;
+ int dataSize = height * rowBytes;
+
+ uint8_t* data = (uint8_t*) moz_xmalloc(dataSize);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ int offset = (y * rowBytes) + (x * bytesPerPixel);
+ data[offset] = 0x7E; // Arbitrary gray-scale value
+ data[offset + 1] = 0; // Alpha value to make us transparent
+ }
+ }
+
+ NSBitmapImageRep* imageRep =
+ [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
+ pixelsWide:width
+ pixelsHigh:height
+ bitsPerSample:8
+ samplesPerPixel:2
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSCalibratedWhiteColorSpace
+ bytesPerRow:rowBytes
+ bitsPerPixel:16]
+ autorelease];
+ if (imageRep) {
+ uint8_t* repDataPtr = [imageRep bitmapData];
+ if (repDataPtr) {
+ memcpy(repDataPtr, data, dataSize);
+ NSImage *image =
+ [[[NSImage alloc] initWithSize:NSMakeSize(width, height)]
+ autorelease];
+ if (image) {
+ [image addRepresentation:imageRep];
+ retval =
+ [[[NSCursor alloc] initWithImage:image
+ hotSpot:NSMakePoint(mHotSpot.x, mHotSpot.y)]
+ autorelease];
+ }
+ }
+ }
+
+ free(data);
+
+ // Fall back to an arrow cursor if (for some reason) the above code failed.
+ if (!retval) {
+ retval = [NSCursor arrowCursor];
+ }
+
+ return retval;
+}
+
+NSCursorInfo::Type NSCursorInfo::GetType() const
+{
+ return mType;
+}
+
+const char* NSCursorInfo::GetTypeName() const
+{
+ switch(mType) {
+ case TypeCustom:
+ return "TypeCustom";
+ case TypeArrow:
+ return "TypeArrow";
+ case TypeClosedHand:
+ return "TypeClosedHand";
+ case TypeContextualMenu:
+ return "TypeContextualMenu";
+ case TypeCrosshair:
+ return "TypeCrosshair";
+ case TypeDisappearingItem:
+ return "TypeDisappearingItem";
+ case TypeDragCopy:
+ return "TypeDragCopy";
+ case TypeDragLink:
+ return "TypeDragLink";
+ case TypeIBeam:
+ return "TypeIBeam";
+ case TypeNotAllowed:
+ return "TypeNotAllowed";
+ case TypeOpenHand:
+ return "TypeOpenHand";
+ case TypePointingHand:
+ return "TypePointingHand";
+ case TypeResizeDown:
+ return "TypeResizeDown";
+ case TypeResizeLeft:
+ return "TypeResizeLeft";
+ case TypeResizeLeftRight:
+ return "TypeResizeLeftRight";
+ case TypeResizeRight:
+ return "TypeResizeRight";
+ case TypeResizeUp:
+ return "TypeResizeUp";
+ case TypeResizeUpDown:
+ return "TypeResizeUpDown";
+ case TypeTransparent:
+ return "TypeTransparent";
+ default:
+ break;
+ }
+ return "TypeUnknown";
+}
+
+nsPoint NSCursorInfo::GetHotSpot() const
+{
+ return mHotSpot;
+}
+
+uint8_t* NSCursorInfo::GetCustomImageData() const
+{
+ return mCustomImageData;
+}
+
+uint32_t NSCursorInfo::GetCustomImageDataLength() const
+{
+ return mCustomImageDataLength;
+}
+
+void NSCursorInfo::SetType(Type aType)
+{
+ mType = aType;
+}
+
+void NSCursorInfo::SetHotSpot(nsPoint aHotSpot)
+{
+ mHotSpot = aHotSpot;
+}
+
+void NSCursorInfo::SetCustomImageData(uint8_t* aData, uint32_t aDataLength)
+{
+ if (mCustomImageData) {
+ free(mCustomImageData);
+ }
+ if (aDataLength) {
+ mCustomImageData = (uint8_t*) moz_xmalloc(aDataLength);
+ memcpy(mCustomImageData, aData, aDataLength);
+ } else {
+ mCustomImageData = NULL;
+ }
+ mCustomImageDataLength = aDataLength;
+}
+
+// This should never be called from the browser process -- only from the
+// plugin process.
+bool NSCursorInfo::GetNativeCursorsSupported()
+{
+ if (mNativeCursorsSupported == -1) {
+ ENSURE_PLUGIN_THREAD(false);
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ bool result = pmc->GetNativeCursorsSupported();
+ if (result) {
+ mNativeCursorsSupported = 1;
+ } else {
+ mNativeCursorsSupported = 0;
+ }
+ }
+ }
+ return (mNativeCursorsSupported == 1);
+}
+
+} // namespace mac_plugin_interposing
+
+namespace mac_plugin_interposing {
+namespace parent {
+
+// Tracks plugin windows currently visible.
+std::set<uint32_t> plugin_visible_windows_set_;
+// Tracks full screen windows currently visible.
+std::set<uint32_t> plugin_fullscreen_windows_set_;
+// Tracks modal windows currently visible.
+std::set<uint32_t> plugin_modal_windows_set_;
+
+void OnPluginShowWindow(uint32_t window_id,
+ CGRect window_bounds,
+ bool modal) {
+ plugin_visible_windows_set_.insert(window_id);
+
+ if (modal)
+ plugin_modal_windows_set_.insert(window_id);
+
+ CGRect main_display_bounds = ::CGDisplayBounds(CGMainDisplayID());
+
+ if (CGRectEqualToRect(window_bounds, main_display_bounds) &&
+ (plugin_fullscreen_windows_set_.find(window_id) ==
+ plugin_fullscreen_windows_set_.end())) {
+ plugin_fullscreen_windows_set_.insert(window_id);
+
+ nsCocoaUtils::HideOSChromeOnScreen(true);
+ }
+}
+
+static void ActivateProcess(pid_t pid) {
+ ProcessSerialNumber process;
+ OSStatus status = ::GetProcessForPID(pid, &process);
+
+ if (status == noErr) {
+ SetFrontProcess(&process);
+ } else {
+ NS_WARNING("Unable to get process for pid.");
+ }
+}
+
+// Must be called on the UI thread.
+// If plugin_pid is -1, the browser will be the active process on return,
+// otherwise that process will be given focus back before this function returns.
+static void ReleasePluginFullScreen(pid_t plugin_pid) {
+ // Releasing full screen only works if we are the frontmost process; grab
+ // focus, but give it back to the plugin process if requested.
+ ActivateProcess(base::GetCurrentProcId());
+
+ nsCocoaUtils::HideOSChromeOnScreen(false);
+
+ if (plugin_pid != -1) {
+ ActivateProcess(plugin_pid);
+ }
+}
+
+void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) {
+ bool had_windows = !plugin_visible_windows_set_.empty();
+ plugin_visible_windows_set_.erase(window_id);
+ bool browser_needs_activation = had_windows &&
+ plugin_visible_windows_set_.empty();
+
+ plugin_modal_windows_set_.erase(window_id);
+ if (plugin_fullscreen_windows_set_.find(window_id) !=
+ plugin_fullscreen_windows_set_.end()) {
+ plugin_fullscreen_windows_set_.erase(window_id);
+ pid_t plugin_pid = browser_needs_activation ? -1 : aPluginPid;
+ browser_needs_activation = false;
+ ReleasePluginFullScreen(plugin_pid);
+ }
+
+ if (browser_needs_activation) {
+ ActivateProcess(getpid());
+ }
+}
+
+void OnSetCursor(const NSCursorInfo& cursorInfo)
+{
+ NSCursor* aCursor = cursorInfo.GetNSCursor();
+ if (aCursor) {
+ [aCursor set];
+ }
+}
+
+void OnShowCursor(bool show)
+{
+ if (show) {
+ [NSCursor unhide];
+ } else {
+ [NSCursor hide];
+ }
+}
+
+void OnPushCursor(const NSCursorInfo& cursorInfo)
+{
+ NSCursor* aCursor = cursorInfo.GetNSCursor();
+ if (aCursor) {
+ [aCursor push];
+ }
+}
+
+void OnPopCursor()
+{
+ [NSCursor pop];
+}
+
+} // namespace parent
+} // namespace mac_plugin_interposing
+
+namespace mac_plugin_interposing {
+namespace child {
+
+// TODO(stuartmorgan): Make this an IPC to order the plugin process above the
+// browser process only if the browser is current frontmost.
+void FocusPluginProcess() {
+ ProcessSerialNumber this_process, front_process;
+ if ((GetCurrentProcess(&this_process) != noErr) ||
+ (GetFrontProcess(&front_process) != noErr)) {
+ return;
+ }
+
+ Boolean matched = false;
+ if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
+ !matched) {
+ SetFrontProcess(&this_process);
+ }
+}
+
+void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
+ bool modal) {
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc)
+ pmc->PluginShowWindow(window_id, modal, bounds);
+}
+
+void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc)
+ pmc->PluginHideWindow(window_id);
+}
+
+void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo)
+{
+ ENSURE_PLUGIN_THREAD_VOID();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ pmc->SetCursor(aCursorInfo);
+ }
+}
+
+void NotifyBrowserOfShowCursor(bool show)
+{
+ ENSURE_PLUGIN_THREAD_VOID();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ pmc->ShowCursor(show);
+ }
+}
+
+void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo)
+{
+ ENSURE_PLUGIN_THREAD_VOID();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ pmc->PushCursor(aCursorInfo);
+ }
+}
+
+void NotifyBrowserOfPopCursor()
+{
+ ENSURE_PLUGIN_THREAD_VOID();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ pmc->PopCursor();
+ }
+}
+
+struct WindowInfo {
+ uint32_t window_id;
+ CGRect bounds;
+ explicit WindowInfo(NSWindow* aWindow) {
+ NSInteger window_num = [aWindow windowNumber];
+ window_id = window_num > 0 ? window_num : 0;
+ bounds = NSRectToCGRect([aWindow frame]);
+ }
+};
+
+static void OnPluginWindowClosed(const WindowInfo& window_info) {
+ if (window_info.window_id == 0)
+ return;
+ mac_plugin_interposing::child::NotifyBrowserOfPluginHideWindow(window_info.window_id,
+ window_info.bounds);
+}
+
+static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
+ // The window id is 0 if it has never been shown (including while it is the
+ // process of being shown for the first time); when that happens, we'll catch
+ // it in _setWindowNumber instead.
+ static BOOL s_pending_display_is_modal = NO;
+ if (window_info.window_id == 0) {
+ if (is_modal)
+ s_pending_display_is_modal = YES;
+ return;
+ }
+ if (s_pending_display_is_modal) {
+ is_modal = YES;
+ s_pending_display_is_modal = NO;
+ }
+ mac_plugin_interposing::child::NotifyBrowserOfPluginShowWindow(
+ window_info.window_id, window_info.bounds, is_modal);
+}
+
+static BOOL OnSetCursor(NSCursorInfo &aInfo)
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfSetCursor(aInfo);
+ return YES;
+ }
+ return NO;
+}
+
+static BOOL OnHideCursor()
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfShowCursor(NO);
+ return YES;
+ }
+ return NO;
+}
+
+static BOOL OnUnhideCursor()
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfShowCursor(YES);
+ return YES;
+ }
+ return NO;
+}
+
+static BOOL OnPushCursor(NSCursorInfo &aInfo)
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfPushCursor(aInfo);
+ return YES;
+ }
+ return NO;
+}
+
+static BOOL OnPopCursor()
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfPopCursor();
+ return YES;
+ }
+ return NO;
+}
+
+} // namespace child
+} // namespace mac_plugin_interposing
+
+using namespace mac_plugin_interposing::child;
+
+@interface NSWindow (PluginInterposing)
+- (void)pluginInterpose_orderOut:(id)sender;
+- (void)pluginInterpose_orderFront:(id)sender;
+- (void)pluginInterpose_makeKeyAndOrderFront:(id)sender;
+- (void)pluginInterpose_setWindowNumber:(NSInteger)num;
+@end
+
+@implementation NSWindow (PluginInterposing)
+
+- (void)pluginInterpose_orderOut:(id)sender {
+ WindowInfo window_info(self);
+ [self pluginInterpose_orderOut:sender];
+ OnPluginWindowClosed(window_info);
+}
+
+- (void)pluginInterpose_orderFront:(id)sender {
+ mac_plugin_interposing::child::FocusPluginProcess();
+ [self pluginInterpose_orderFront:sender];
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+- (void)pluginInterpose_makeKeyAndOrderFront:(id)sender {
+ mac_plugin_interposing::child::FocusPluginProcess();
+ [self pluginInterpose_makeKeyAndOrderFront:sender];
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+- (void)pluginInterpose_setWindowNumber:(NSInteger)num {
+ if (num > 0)
+ mac_plugin_interposing::child::FocusPluginProcess();
+ [self pluginInterpose_setWindowNumber:num];
+ if (num > 0)
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+@end
+
+@interface NSApplication (PluginInterposing)
+- (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window;
+@end
+
+@implementation NSApplication (PluginInterposing)
+
+- (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window {
+ mac_plugin_interposing::child::FocusPluginProcess();
+ // This is out-of-order relative to the other calls, but runModalForWindow:
+ // won't return until the window closes, and the order only matters for
+ // full-screen windows.
+ OnPluginWindowShown(WindowInfo(window), YES);
+ return [self pluginInterpose_runModalForWindow:window];
+}
+
+@end
+
+// Hook commands to manipulate the current cursor, so that they can be passed
+// from the child process to the parent process. These commands have no
+// effect unless they're performed in the parent process.
+@interface NSCursor (PluginInterposing)
+- (void)pluginInterpose_set;
+- (void)pluginInterpose_push;
+- (void)pluginInterpose_pop;
++ (NSCursor*)pluginInterpose_currentCursor;
++ (void)pluginInterpose_hide;
++ (void)pluginInterpose_unhide;
++ (void)pluginInterpose_pop;
+@end
+
+// Cache the results of [NSCursor set], [NSCursor push] and [NSCursor pop].
+// The last element is always the current cursor.
+static NSMutableArray* gCursorStack = nil;
+
+static BOOL initCursorStack()
+{
+ if (!gCursorStack) {
+ gCursorStack = [[NSMutableArray arrayWithCapacity:5] retain];
+ }
+ return (gCursorStack != NULL);
+}
+
+static NSCursor* currentCursorFromCache()
+{
+ if (!initCursorStack())
+ return nil;
+ return (NSCursor*) [gCursorStack lastObject];
+}
+
+static void setCursorInCache(NSCursor* aCursor)
+{
+ if (!initCursorStack() || !aCursor)
+ return;
+ NSUInteger count = [gCursorStack count];
+ if (count) {
+ [gCursorStack replaceObjectAtIndex:count - 1 withObject:aCursor];
+ } else {
+ [gCursorStack addObject:aCursor];
+ }
+}
+
+static void pushCursorInCache(NSCursor* aCursor)
+{
+ if (!initCursorStack() || !aCursor)
+ return;
+ [gCursorStack addObject:aCursor];
+}
+
+static void popCursorInCache()
+{
+ if (!initCursorStack())
+ return;
+ // Apple's doc on the +[NSCursor pop] method says: "If the current cursor
+ // is the only cursor on the stack, this method does nothing."
+ if ([gCursorStack count] > 1) {
+ [gCursorStack removeLastObject];
+ }
+}
+
+@implementation NSCursor (PluginInterposing)
+
+- (void)pluginInterpose_set
+{
+ NSCursorInfo info(self);
+ OnSetCursor(info);
+ setCursorInCache(self);
+ [self pluginInterpose_set];
+}
+
+- (void)pluginInterpose_push
+{
+ NSCursorInfo info(self);
+ OnPushCursor(info);
+ pushCursorInCache(self);
+ [self pluginInterpose_push];
+}
+
+- (void)pluginInterpose_pop
+{
+ OnPopCursor();
+ popCursorInCache();
+ [self pluginInterpose_pop];
+}
+
+// The currentCursor method always returns nil when running in a background
+// process. But this may confuse plugins (notably Flash, see bug 621117). So
+// if we get a nil return from the "call to super", we return a cursor that's
+// been cached by previous calls to set or push. According to Apple's docs,
+// currentCursor "only returns the cursor set by your application using
+// NSCursor methods". So we don't need to worry about changes to the cursor
+// made by other methods like SetThemeCursor().
++ (NSCursor*)pluginInterpose_currentCursor
+{
+ NSCursor* retval = [self pluginInterpose_currentCursor];
+ if (!retval) {
+ retval = currentCursorFromCache();
+ }
+ return retval;
+}
+
++ (void)pluginInterpose_hide
+{
+ OnHideCursor();
+ [self pluginInterpose_hide];
+}
+
++ (void)pluginInterpose_unhide
+{
+ OnUnhideCursor();
+ [self pluginInterpose_unhide];
+}
+
++ (void)pluginInterpose_pop
+{
+ OnPopCursor();
+ popCursorInCache();
+ [self pluginInterpose_pop];
+}
+
+@end
+
+static void ExchangeMethods(Class target_class,
+ BOOL class_method,
+ SEL original,
+ SEL replacement) {
+ Method m1;
+ Method m2;
+ if (class_method) {
+ m1 = class_getClassMethod(target_class, original);
+ m2 = class_getClassMethod(target_class, replacement);
+ } else {
+ m1 = class_getInstanceMethod(target_class, original);
+ m2 = class_getInstanceMethod(target_class, replacement);
+ }
+
+ if (m1 == m2)
+ return;
+
+ if (m1 && m2)
+ method_exchangeImplementations(m1, m2);
+ else
+ NS_NOTREACHED("Cocoa swizzling failed");
+}
+
+namespace mac_plugin_interposing {
+namespace child {
+
+void SetUpCocoaInterposing() {
+ Class nswindow_class = [NSWindow class];
+ ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
+ @selector(pluginInterpose_orderOut:));
+ ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
+ @selector(pluginInterpose_orderFront:));
+ ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
+ @selector(pluginInterpose_makeKeyAndOrderFront:));
+ ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
+ @selector(pluginInterpose_setWindowNumber:));
+
+ ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
+ @selector(pluginInterpose_runModalForWindow:));
+
+ Class nscursor_class = [NSCursor class];
+ ExchangeMethods(nscursor_class, NO, @selector(set),
+ @selector(pluginInterpose_set));
+ ExchangeMethods(nscursor_class, NO, @selector(push),
+ @selector(pluginInterpose_push));
+ ExchangeMethods(nscursor_class, NO, @selector(pop),
+ @selector(pluginInterpose_pop));
+ ExchangeMethods(nscursor_class, YES, @selector(currentCursor),
+ @selector(pluginInterpose_currentCursor));
+ ExchangeMethods(nscursor_class, YES, @selector(hide),
+ @selector(pluginInterpose_hide));
+ ExchangeMethods(nscursor_class, YES, @selector(unhide),
+ @selector(pluginInterpose_unhide));
+ ExchangeMethods(nscursor_class, YES, @selector(pop),
+ @selector(pluginInterpose_pop));
+}
+
+} // namespace child
+} // namespace mac_plugin_interposing
+
+// Called from plugin_child_interpose.mm, which hooks calls to
+// SetCursor() (the QuickDraw call) from the plugin child process.
+extern "C" NS_VISIBILITY_DEFAULT BOOL
+mac_plugin_interposing_child_OnSetCursor(const Cursor* cursor)
+{
+ NSCursorInfo info(cursor);
+ return OnSetCursor(info);
+}
+
+// Called from plugin_child_interpose.mm, which hooks calls to
+// SetThemeCursor() (the Appearance Manager call) from the plugin child
+// process.
+extern "C" NS_VISIBILITY_DEFAULT BOOL
+mac_plugin_interposing_child_OnSetThemeCursor(ThemeCursor cursor)
+{
+ NSCursorInfo info;
+ switch (cursor) {
+ case kThemeArrowCursor:
+ info.SetType(NSCursorInfo::TypeArrow);
+ break;
+ case kThemeCopyArrowCursor:
+ info.SetType(NSCursorInfo::TypeDragCopy);
+ break;
+ case kThemeAliasArrowCursor:
+ info.SetType(NSCursorInfo::TypeDragLink);
+ break;
+ case kThemeContextualMenuArrowCursor:
+ info.SetType(NSCursorInfo::TypeContextualMenu);
+ break;
+ case kThemeIBeamCursor:
+ info.SetType(NSCursorInfo::TypeIBeam);
+ break;
+ case kThemeCrossCursor:
+ case kThemePlusCursor:
+ info.SetType(NSCursorInfo::TypeCrosshair);
+ break;
+ case kThemeWatchCursor:
+ case kThemeSpinningCursor:
+ info.SetType(NSCursorInfo::TypeArrow);
+ break;
+ case kThemeClosedHandCursor:
+ info.SetType(NSCursorInfo::TypeClosedHand);
+ break;
+ case kThemeOpenHandCursor:
+ info.SetType(NSCursorInfo::TypeOpenHand);
+ break;
+ case kThemePointingHandCursor:
+ case kThemeCountingUpHandCursor:
+ case kThemeCountingDownHandCursor:
+ case kThemeCountingUpAndDownHandCursor:
+ info.SetType(NSCursorInfo::TypePointingHand);
+ break;
+ case kThemeResizeLeftCursor:
+ info.SetType(NSCursorInfo::TypeResizeLeft);
+ break;
+ case kThemeResizeRightCursor:
+ info.SetType(NSCursorInfo::TypeResizeRight);
+ break;
+ case kThemeResizeLeftRightCursor:
+ info.SetType(NSCursorInfo::TypeResizeLeftRight);
+ break;
+ case kThemeNotAllowedCursor:
+ info.SetType(NSCursorInfo::TypeNotAllowed);
+ break;
+ case kThemeResizeUpCursor:
+ info.SetType(NSCursorInfo::TypeResizeUp);
+ break;
+ case kThemeResizeDownCursor:
+ info.SetType(NSCursorInfo::TypeResizeDown);
+ break;
+ case kThemeResizeUpDownCursor:
+ info.SetType(NSCursorInfo::TypeResizeUpDown);
+ break;
+ case kThemePoofCursor:
+ info.SetType(NSCursorInfo::TypeDisappearingItem);
+ break;
+ default:
+ info.SetType(NSCursorInfo::TypeArrow);
+ break;
+ }
+ return OnSetCursor(info);
+}
+
+extern "C" NS_VISIBILITY_DEFAULT BOOL
+mac_plugin_interposing_child_OnHideCursor()
+{
+ return OnHideCursor();
+}
+
+extern "C" NS_VISIBILITY_DEFAULT BOOL
+mac_plugin_interposing_child_OnShowCursor()
+{
+ return OnUnhideCursor();
+}
diff --git a/dom/plugins/ipc/PluginLibrary.h b/dom/plugins/ipc/PluginLibrary.h
new file mode 100644
index 000000000..c9499ee0d
--- /dev/null
+++ b/dom/plugins/ipc/PluginLibrary.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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 mozilla_PluginLibrary_h
+#define mozilla_PluginLibrary_h 1
+
+#include "prlink.h"
+#include "npapi.h"
+#include "npfunctions.h"
+#include "nscore.h"
+#include "nsTArray.h"
+#include "nsError.h"
+#include "mozilla/EventForwards.h"
+#include "nsSize.h"
+#include "nsRect.h"
+
+class nsCString;
+class nsNPAPIPlugin;
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+}
+namespace layers {
+class Image;
+class ImageContainer;
+} // namespace layers
+} // namespace mozilla
+
+class nsIClearSiteDataCallback;
+
+#define nsIGetSitesWithDataCallback_CID {0xd0028b83, 0xfdf9, 0x4c53, {0xb7, 0xbb, 0x47, 0x46, 0x0f, 0x6b, 0x83, 0x6c}}
+class nsIGetSitesWithDataCallback : public nsISupports {
+public:
+ NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& result) = 0;
+ NS_DECLARE_STATIC_IID_ACCESSOR(nsIGetSitesWithDataCallback_CID)
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIGetSitesWithDataCallback, nsIGetSitesWithDataCallback_CID)
+
+namespace mozilla {
+
+class PluginLibrary
+{
+public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ virtual ~PluginLibrary() { }
+
+ /**
+ * Inform this library about the nsNPAPIPlugin which owns it. This
+ * object will hold a weak pointer to the plugin.
+ */
+ virtual void SetPlugin(nsNPAPIPlugin* plugin) = 0;
+
+ virtual bool HasRequiredFunctions() = 0;
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) = 0;
+#else
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) = 0;
+#endif
+ virtual nsresult NP_Shutdown(NPError* error) = 0;
+ virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) = 0;
+ virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
+ void *aValue, NPError* error) = 0;
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) = 0;
+#endif
+ virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
+ uint16_t mode, int16_t argc, char* argn[],
+ char* argv[], NPSavedData* saved,
+ NPError* error) = 0;
+
+ virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
+ uint64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback) = 0;
+ virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) = 0;
+
+ virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window) = 0;
+ virtual nsresult GetImageContainer(NPP instance, mozilla::layers::ImageContainer** aContainer) = 0;
+ virtual nsresult GetImageSize(NPP instance, nsIntSize* aSize) = 0;
+ virtual void DidComposite(NPP instance) = 0;
+ virtual bool IsOOP() = 0;
+#if defined(XP_MACOSX)
+ virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) = 0;
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) = 0;
+#endif
+#if defined(XP_WIN)
+ virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) = 0;
+#endif
+ virtual nsresult HandledWindowedPluginKeyEvent(
+ NPP aInstance,
+ const mozilla::NativeEventData& aNativeKeyData,
+ bool aIsCOnsumed) = 0;
+
+ /**
+ * The next three methods are the third leg in the trip to
+ * PluginInstanceParent. They approximately follow the ReadbackSink
+ * API.
+ */
+ virtual nsresult SetBackgroundUnknown(NPP instance) = 0;
+ virtual nsresult BeginUpdateBackground(NPP instance,
+ const nsIntRect&, DrawTarget**) = 0;
+ virtual nsresult EndUpdateBackground(NPP instance, const nsIntRect&) = 0;
+ virtual nsresult GetRunID(uint32_t* aRunID) = 0;
+ virtual void SetHasLocalInstance() = 0;
+};
+
+} // namespace mozilla
+
+#endif // ifndef mozilla_PluginLibrary_h
diff --git a/dom/plugins/ipc/PluginMessageUtils.cpp b/dom/plugins/ipc/PluginMessageUtils.cpp
new file mode 100644
index 000000000..47653fe6e
--- /dev/null
+++ b/dom/plugins/ipc/PluginMessageUtils.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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 "PluginMessageUtils.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+
+#include "PluginInstanceParent.h"
+#include "PluginInstanceChild.h"
+#include "PluginScriptableObjectParent.h"
+#include "PluginScriptableObjectChild.h"
+
+using std::string;
+
+using mozilla::ipc::MessageChannel;
+
+namespace {
+
+class DeferNPObjectReleaseRunnable : public mozilla::Runnable
+{
+public:
+ DeferNPObjectReleaseRunnable(const NPNetscapeFuncs* f, NPObject* o)
+ : mFuncs(f)
+ , mObject(o)
+ {
+ NS_ASSERTION(o, "no release null objects");
+ }
+
+ NS_IMETHOD Run();
+
+private:
+ const NPNetscapeFuncs* mFuncs;
+ NPObject* mObject;
+};
+
+NS_IMETHODIMP
+DeferNPObjectReleaseRunnable::Run()
+{
+ mFuncs->releaseobject(mObject);
+ return NS_OK;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace plugins {
+
+NPRemoteWindow::NPRemoteWindow() :
+ window(0), x(0), y(0), width(0), height(0), type(NPWindowTypeDrawable)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ , visualID(0)
+ , colormap(0)
+#endif /* XP_UNIX */
+#if defined(XP_MACOSX)
+ ,contentsScaleFactor(1.0)
+#endif
+{
+ clipRect.top = 0;
+ clipRect.left = 0;
+ clipRect.bottom = 0;
+ clipRect.right = 0;
+}
+
+ipc::RacyInterruptPolicy
+MediateRace(const MessageChannel::MessageInfo& parent,
+ const MessageChannel::MessageInfo& child)
+{
+ switch (parent.type()) {
+ case PPluginInstance::Msg_Paint__ID:
+ case PPluginInstance::Msg_NPP_SetWindow__ID:
+ case PPluginInstance::Msg_NPP_HandleEvent_Shmem__ID:
+ case PPluginInstance::Msg_NPP_HandleEvent_IOSurface__ID:
+ // our code relies on the frame list not changing during paints and
+ // reflows
+ return ipc::RIPParentWins;
+
+ default:
+ return ipc::RIPChildWins;
+ }
+}
+
+#if defined(OS_LINUX)
+static string
+ReplaceAll(const string& haystack, const string& needle, const string& with)
+{
+ string munged = haystack;
+ string::size_type i = 0;
+
+ while (string::npos != (i = munged.find(needle, i))) {
+ munged.replace(i, needle.length(), with);
+ i += with.length();
+ }
+
+ return munged;
+}
+#endif
+
+string
+MungePluginDsoPath(const string& path)
+{
+#if defined(OS_LINUX)
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=519601
+ return ReplaceAll(path, "netscape", "netsc@pe");
+#else
+ return path;
+#endif
+}
+
+string
+UnmungePluginDsoPath(const string& munged)
+{
+#if defined(OS_LINUX)
+ return ReplaceAll(munged, "netsc@pe", "netscape");
+#else
+ return munged;
+#endif
+}
+
+
+LogModule*
+GetPluginLog()
+{
+ static LazyLogModule sLog("IPCPlugins");
+ return sLog;
+}
+
+void
+DeferNPObjectLastRelease(const NPNetscapeFuncs* f, NPObject* o)
+{
+ if (!o)
+ return;
+
+ if (o->referenceCount > 1) {
+ f->releaseobject(o);
+ return;
+ }
+
+ NS_DispatchToCurrentThread(new DeferNPObjectReleaseRunnable(f, o));
+}
+
+void DeferNPVariantLastRelease(const NPNetscapeFuncs* f, NPVariant* v)
+{
+ if (!NPVARIANT_IS_OBJECT(*v)) {
+ f->releasevariantvalue(v);
+ return;
+ }
+ DeferNPObjectLastRelease(f, v->value.objectValue);
+ VOID_TO_NPVARIANT(*v);
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h
new file mode 100644
index 000000000..55be59d62
--- /dev/null
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -0,0 +1,750 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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 DOM_PLUGINS_PLUGINMESSAGEUTILS_H
+#define DOM_PLUGINS_PLUGINMESSAGEUTILS_H
+
+#include "ipc/IPCMessageUtils.h"
+#include "base/message_loop.h"
+
+#include "mozilla/ipc/CrossProcessMutex.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "gfxipc/ShadowLayerUtils.h"
+
+#include "npapi.h"
+#include "npruntime.h"
+#include "npfunctions.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/Logging.h"
+#include "nsHashKeys.h"
+#ifdef MOZ_CRASHREPORTER
+# include "nsExceptionHandler.h"
+#endif
+#ifdef XP_MACOSX
+#include "PluginInterposeOSX.h"
+#else
+namespace mac_plugin_interposing { class NSCursorInfo { }; }
+#endif
+using mac_plugin_interposing::NSCursorInfo;
+
+namespace mozilla {
+namespace plugins {
+
+using layers::SurfaceDescriptorX11;
+
+enum ScriptableObjectType
+{
+ LocalObject,
+ Proxy
+};
+
+mozilla::ipc::RacyInterruptPolicy
+MediateRace(const mozilla::ipc::MessageChannel::MessageInfo& parent,
+ const mozilla::ipc::MessageChannel::MessageInfo& child);
+
+std::string
+MungePluginDsoPath(const std::string& path);
+std::string
+UnmungePluginDsoPath(const std::string& munged);
+
+extern mozilla::LogModule* GetPluginLog();
+
+#if defined(_MSC_VER)
+#define FULLFUNCTION __FUNCSIG__
+#elif defined(__GNUC__)
+#define FULLFUNCTION __PRETTY_FUNCTION__
+#else
+#define FULLFUNCTION __FUNCTION__
+#endif
+
+#define PLUGIN_LOG_DEBUG(args) MOZ_LOG(GetPluginLog(), mozilla::LogLevel::Debug, args)
+#define PLUGIN_LOG_DEBUG_FUNCTION MOZ_LOG(GetPluginLog(), mozilla::LogLevel::Debug, ("%s", FULLFUNCTION))
+#define PLUGIN_LOG_DEBUG_METHOD MOZ_LOG(GetPluginLog(), mozilla::LogLevel::Debug, ("%s [%p]", FULLFUNCTION, (void*) this))
+
+/**
+ * This is NPByteRange without the linked list.
+ */
+struct IPCByteRange
+{
+ int32_t offset;
+ uint32_t length;
+};
+
+typedef nsTArray<IPCByteRange> IPCByteRanges;
+
+typedef nsCString Buffer;
+
+struct NPRemoteWindow
+{
+ NPRemoteWindow();
+ uint64_t window;
+ int32_t x;
+ int32_t y;
+ uint32_t width;
+ uint32_t height;
+ NPRect clipRect;
+ NPWindowType type;
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ VisualID visualID;
+ Colormap colormap;
+#endif /* XP_UNIX */
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ double contentsScaleFactor;
+#endif
+};
+
+// This struct is like NPAudioDeviceChangeDetails, only it uses a
+// std::wstring instead of a const wchar_t* for the defaultDevice.
+// This gives us the necessary memory-ownership semantics without
+// requiring C++ objects in npapi.h.
+struct NPAudioDeviceChangeDetailsIPC
+{
+ int32_t flow;
+ int32_t role;
+ std::wstring defaultDevice;
+};
+
+#ifdef XP_WIN
+typedef HWND NativeWindowHandle;
+#elif defined(MOZ_X11)
+typedef XID NativeWindowHandle;
+#elif defined(XP_DARWIN) || defined(ANDROID)
+typedef intptr_t NativeWindowHandle; // never actually used, will always be 0
+#else
+#error Need NativeWindowHandle for this platform
+#endif
+
+#ifdef XP_WIN
+typedef base::SharedMemoryHandle WindowsSharedMemoryHandle;
+typedef HANDLE DXGISharedSurfaceHandle;
+#else
+typedef mozilla::null_t WindowsSharedMemoryHandle;
+typedef mozilla::null_t DXGISharedSurfaceHandle;
+#endif
+
+// XXX maybe not the best place for these. better one?
+
+#define VARSTR(v_) case v_: return #v_
+inline const char*
+NPPVariableToString(NPPVariable aVar)
+{
+ switch (aVar) {
+ VARSTR(NPPVpluginNameString);
+ VARSTR(NPPVpluginDescriptionString);
+ VARSTR(NPPVpluginWindowBool);
+ VARSTR(NPPVpluginTransparentBool);
+ VARSTR(NPPVjavaClass);
+ VARSTR(NPPVpluginWindowSize);
+ VARSTR(NPPVpluginTimerInterval);
+
+ VARSTR(NPPVpluginScriptableInstance);
+ VARSTR(NPPVpluginScriptableIID);
+
+ VARSTR(NPPVjavascriptPushCallerBool);
+
+ VARSTR(NPPVpluginKeepLibraryInMemory);
+ VARSTR(NPPVpluginNeedsXEmbed);
+
+ VARSTR(NPPVpluginScriptableNPObject);
+
+ VARSTR(NPPVformValue);
+
+ VARSTR(NPPVpluginUrlRequestsDisplayedBool);
+
+ VARSTR(NPPVpluginWantsAllNetworkStreams);
+
+#ifdef XP_MACOSX
+ VARSTR(NPPVpluginDrawingModel);
+ VARSTR(NPPVpluginEventModel);
+#endif
+
+#ifdef XP_WIN
+ VARSTR(NPPVpluginRequiresAudioDeviceChanges);
+#endif
+
+ default: return "???";
+ }
+}
+
+inline const char*
+NPNVariableToString(NPNVariable aVar)
+{
+ switch(aVar) {
+ VARSTR(NPNVxDisplay);
+ VARSTR(NPNVxtAppContext);
+ VARSTR(NPNVnetscapeWindow);
+ VARSTR(NPNVjavascriptEnabledBool);
+ VARSTR(NPNVasdEnabledBool);
+ VARSTR(NPNVisOfflineBool);
+
+ VARSTR(NPNVserviceManager);
+ VARSTR(NPNVDOMElement);
+ VARSTR(NPNVDOMWindow);
+ VARSTR(NPNVToolkit);
+ VARSTR(NPNVSupportsXEmbedBool);
+
+ VARSTR(NPNVWindowNPObject);
+
+ VARSTR(NPNVPluginElementNPObject);
+
+ VARSTR(NPNVSupportsWindowless);
+
+ VARSTR(NPNVprivateModeBool);
+ VARSTR(NPNVdocumentOrigin);
+
+#ifdef XP_WIN
+ VARSTR(NPNVaudioDeviceChangeDetails);
+#endif
+
+ default: return "???";
+ }
+}
+#undef VARSTR
+
+inline bool IsPluginThread()
+{
+ MessageLoop* loop = MessageLoop::current();
+ if (!loop)
+ return false;
+ return (loop->type() == MessageLoop::TYPE_UI);
+}
+
+inline void AssertPluginThread()
+{
+ MOZ_RELEASE_ASSERT(IsPluginThread(), "Should be on the plugin's main thread!");
+}
+
+#define ENSURE_PLUGIN_THREAD(retval) \
+ PR_BEGIN_MACRO \
+ if (!IsPluginThread()) { \
+ NS_WARNING("Not running on the plugin's main thread!"); \
+ return (retval); \
+ } \
+ PR_END_MACRO
+
+#define ENSURE_PLUGIN_THREAD_VOID() \
+ PR_BEGIN_MACRO \
+ if (!IsPluginThread()) { \
+ NS_WARNING("Not running on the plugin's main thread!"); \
+ return; \
+ } \
+ PR_END_MACRO
+
+void DeferNPObjectLastRelease(const NPNetscapeFuncs* f, NPObject* o);
+void DeferNPVariantLastRelease(const NPNetscapeFuncs* f, NPVariant* v);
+
+inline bool IsDrawingModelDirect(int16_t aModel)
+{
+ return aModel == NPDrawingModelAsyncBitmapSurface
+#if defined(XP_WIN)
+ || aModel == NPDrawingModelAsyncWindowsDXGISurface
+#endif
+ ;
+}
+
+// in NPAPI, char* == nullptr is sometimes meaningful. the following is
+// helper code for dealing with nullable nsCString's
+inline nsCString
+NullableString(const char* aString)
+{
+ if (!aString) {
+ return NullCString();
+ }
+ return nsCString(aString);
+}
+
+inline const char*
+NullableStringGet(const nsCString& str)
+{
+ if (str.IsVoid())
+ return nullptr;
+
+ return str.get();
+}
+
+struct DeletingObjectEntry : public nsPtrHashKey<NPObject>
+{
+ explicit DeletingObjectEntry(const NPObject* key)
+ : nsPtrHashKey<NPObject>(key)
+ , mDeleted(false)
+ { }
+
+ bool mDeleted;
+};
+
+} /* namespace plugins */
+
+} /* namespace mozilla */
+
+namespace IPC {
+
+template <>
+struct ParamTraits<NPRect>
+{
+ typedef NPRect paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.top);
+ WriteParam(aMsg, aParam.left);
+ WriteParam(aMsg, aParam.bottom);
+ WriteParam(aMsg, aParam.right);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ uint16_t top, left, bottom, right;
+ if (ReadParam(aMsg, aIter, &top) &&
+ ReadParam(aMsg, aIter, &left) &&
+ ReadParam(aMsg, aIter, &bottom) &&
+ ReadParam(aMsg, aIter, &right)) {
+ aResult->top = top;
+ aResult->left = left;
+ aResult->bottom = bottom;
+ aResult->right = right;
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(StringPrintf(L"[%u, %u, %u, %u]", aParam.top, aParam.left,
+ aParam.bottom, aParam.right));
+ }
+};
+
+template <>
+struct ParamTraits<NPWindowType>
+{
+ typedef NPWindowType paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteInt16(int16_t(aParam));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ int16_t result;
+ if (aMsg->ReadInt16(aIter, &result)) {
+ *aResult = paramType(result);
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(StringPrintf(L"%d", int16_t(aParam)));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteWindow>
+{
+ typedef mozilla::plugins::NPRemoteWindow paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteUInt64(aParam.window);
+ WriteParam(aMsg, aParam.x);
+ WriteParam(aMsg, aParam.y);
+ WriteParam(aMsg, aParam.width);
+ WriteParam(aMsg, aParam.height);
+ WriteParam(aMsg, aParam.clipRect);
+ WriteParam(aMsg, aParam.type);
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ aMsg->WriteULong(aParam.visualID);
+ aMsg->WriteULong(aParam.colormap);
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ aMsg->WriteDouble(aParam.contentsScaleFactor);
+#endif
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ uint64_t window;
+ int32_t x, y;
+ uint32_t width, height;
+ NPRect clipRect;
+ NPWindowType type;
+ if (!(aMsg->ReadUInt64(aIter, &window) &&
+ ReadParam(aMsg, aIter, &x) &&
+ ReadParam(aMsg, aIter, &y) &&
+ ReadParam(aMsg, aIter, &width) &&
+ ReadParam(aMsg, aIter, &height) &&
+ ReadParam(aMsg, aIter, &clipRect) &&
+ ReadParam(aMsg, aIter, &type)))
+ return false;
+
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ unsigned long visualID;
+ unsigned long colormap;
+ if (!(aMsg->ReadULong(aIter, &visualID) &&
+ aMsg->ReadULong(aIter, &colormap)))
+ return false;
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ double contentsScaleFactor;
+ if (!aMsg->ReadDouble(aIter, &contentsScaleFactor))
+ return false;
+#endif
+
+ aResult->window = window;
+ aResult->x = x;
+ aResult->y = y;
+ aResult->width = width;
+ aResult->height = height;
+ aResult->clipRect = clipRect;
+ aResult->type = type;
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
+ aResult->visualID = visualID;
+ aResult->colormap = colormap;
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ aResult->contentsScaleFactor = contentsScaleFactor;
+#endif
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(StringPrintf(L"[%u, %d, %d, %u, %u, %d",
+ (unsigned long)aParam.window,
+ aParam.x, aParam.y, aParam.width,
+ aParam.height, (long)aParam.type));
+ }
+};
+
+#ifdef XP_MACOSX
+template <>
+struct ParamTraits<NPNSString*>
+{
+ typedef NPNSString* paramType;
+
+ // Empty string writes a length of 0 and no buffer.
+ // We don't write a nullptr terminating character in buffers.
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ CFStringRef cfString = (CFStringRef)aParam;
+
+ // Write true if we have a string, false represents nullptr.
+ aMsg->WriteBool(!!cfString);
+ if (!cfString) {
+ return;
+ }
+
+ long length = ::CFStringGetLength(cfString);
+ WriteParam(aMsg, length);
+ if (length == 0) {
+ return;
+ }
+
+ // Attempt to get characters without any allocation/conversion.
+ if (::CFStringGetCharactersPtr(cfString)) {
+ aMsg->WriteBytes(::CFStringGetCharactersPtr(cfString), length * sizeof(UniChar));
+ } else {
+ UniChar *buffer = (UniChar*)moz_xmalloc(length * sizeof(UniChar));
+ ::CFStringGetCharacters(cfString, ::CFRangeMake(0, length), buffer);
+ aMsg->WriteBytes(buffer, length * sizeof(UniChar));
+ free(buffer);
+ }
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ bool haveString = false;
+ if (!aMsg->ReadBool(aIter, &haveString)) {
+ return false;
+ }
+ if (!haveString) {
+ *aResult = nullptr;
+ return true;
+ }
+
+ long length;
+ if (!ReadParam(aMsg, aIter, &length)) {
+ return false;
+ }
+
+ // Avoid integer multiplication overflow.
+ if (length > INT_MAX / static_cast<long>(sizeof(UniChar))) {
+ return false;
+ }
+
+ auto chars = mozilla::MakeUnique<UniChar[]>(length);
+ if (length != 0) {
+ if (!aMsg->ReadBytesInto(aIter, chars.get(), length * sizeof(UniChar))) {
+ return false;
+ }
+ }
+
+ *aResult = (NPNSString*)::CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)chars.get(),
+ length * sizeof(UniChar),
+ kCFStringEncodingUTF16, false);
+ if (!*aResult) {
+ return false;
+ }
+
+ return true;
+ }
+};
+#endif
+
+#ifdef XP_MACOSX
+template <>
+struct ParamTraits<NSCursorInfo>
+{
+ typedef NSCursorInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ NSCursorInfo::Type type = aParam.GetType();
+
+ aMsg->WriteInt(type);
+
+ nsPoint hotSpot = aParam.GetHotSpot();
+ WriteParam(aMsg, hotSpot.x);
+ WriteParam(aMsg, hotSpot.y);
+
+ uint32_t dataLength = aParam.GetCustomImageDataLength();
+ WriteParam(aMsg, dataLength);
+ if (dataLength == 0) {
+ return;
+ }
+
+ uint8_t* buffer = (uint8_t*)moz_xmalloc(dataLength);
+ memcpy(buffer, aParam.GetCustomImageData(), dataLength);
+ aMsg->WriteBytes(buffer, dataLength);
+ free(buffer);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ NSCursorInfo::Type type;
+ if (!aMsg->ReadInt(aIter, (int*)&type)) {
+ return false;
+ }
+
+ nscoord hotSpotX, hotSpotY;
+ if (!ReadParam(aMsg, aIter, &hotSpotX) ||
+ !ReadParam(aMsg, aIter, &hotSpotY)) {
+ return false;
+ }
+
+ uint32_t dataLength;
+ if (!ReadParam(aMsg, aIter, &dataLength)) {
+ return false;
+ }
+
+ auto data = mozilla::MakeUnique<uint8_t[]>(dataLength);
+ if (dataLength != 0) {
+ if (!aMsg->ReadBytesInto(aIter, data.get(), dataLength)) {
+ return false;
+ }
+ }
+
+ aResult->SetType(type);
+ aResult->SetHotSpot(nsPoint(hotSpotX, hotSpotY));
+ aResult->SetCustomImageData(data.get(), dataLength);
+
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ const char* typeName = aParam.GetTypeName();
+ nsPoint hotSpot = aParam.GetHotSpot();
+ int hotSpotX, hotSpotY;
+#ifdef NS_COORD_IS_FLOAT
+ hotSpotX = rint(hotSpot.x);
+ hotSpotY = rint(hotSpot.y);
+#else
+ hotSpotX = hotSpot.x;
+ hotSpotY = hotSpot.y;
+#endif
+ uint32_t dataLength = aParam.GetCustomImageDataLength();
+ uint8_t* data = aParam.GetCustomImageData();
+
+ aLog->append(StringPrintf(L"[%s, (%i %i), %u, %p]",
+ typeName, hotSpotX, hotSpotY, dataLength, data));
+ }
+};
+#else
+template<>
+struct ParamTraits<NSCursorInfo>
+{
+ typedef NSCursorInfo paramType;
+ static void Write(Message* aMsg, const paramType& aParam) {
+ NS_RUNTIMEABORT("NSCursorInfo isn't meaningful on this platform");
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+ NS_RUNTIMEABORT("NSCursorInfo isn't meaningful on this platform");
+ return false;
+ }
+};
+#endif // #ifdef XP_MACOSX
+
+template <>
+struct ParamTraits<mozilla::plugins::IPCByteRange>
+{
+ typedef mozilla::plugins::IPCByteRange paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.offset);
+ WriteParam(aMsg, aParam.length);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ paramType p;
+ if (ReadParam(aMsg, aIter, &p.offset) &&
+ ReadParam(aMsg, aIter, &p.length)) {
+ *aResult = p;
+ return true;
+ }
+ return false;
+ }
+};
+
+template <>
+struct ParamTraits<NPNVariable>
+{
+ typedef NPNVariable paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, int(aParam));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ int intval;
+ if (ReadParam(aMsg, aIter, &intval)) {
+ *aResult = paramType(intval);
+ return true;
+ }
+ return false;
+ }
+};
+
+template<>
+struct ParamTraits<NPNURLVariable>
+{
+ typedef NPNURLVariable paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, int(aParam));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ int intval;
+ if (ReadParam(aMsg, aIter, &intval)) {
+ switch (intval) {
+ case NPNURLVCookie:
+ case NPNURLVProxy:
+ *aResult = paramType(intval);
+ return true;
+ }
+ }
+ return false;
+ }
+};
+
+
+template<>
+struct ParamTraits<NPCoordinateSpace>
+{
+ typedef NPCoordinateSpace paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, int32_t(aParam));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ int32_t intval;
+ if (ReadParam(aMsg, aIter, &intval)) {
+ switch (intval) {
+ case NPCoordinateSpacePlugin:
+ case NPCoordinateSpaceWindow:
+ case NPCoordinateSpaceFlippedWindow:
+ case NPCoordinateSpaceScreen:
+ case NPCoordinateSpaceFlippedScreen:
+ *aResult = paramType(intval);
+ return true;
+ }
+ }
+ return false;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::plugins::NPAudioDeviceChangeDetailsIPC>
+{
+ typedef mozilla::plugins::NPAudioDeviceChangeDetailsIPC paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.flow);
+ WriteParam(aMsg, aParam.role);
+ WriteParam(aMsg, aParam.defaultDevice);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ int32_t flow, role;
+ std::wstring defaultDevice;
+ if (ReadParam(aMsg, aIter, &flow) &&
+ ReadParam(aMsg, aIter, &role) &&
+ ReadParam(aMsg, aIter, &defaultDevice)) {
+ aResult->flow = flow;
+ aResult->role = role;
+ aResult->defaultDevice = defaultDevice;
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(StringPrintf(L"[%d, %d, %S]", aParam.flow, aParam.role,
+ aParam.defaultDevice.c_str()));
+ }
+};
+
+} /* namespace IPC */
+
+
+// Serializing NPEvents is completely platform-specific and can be rather
+// intricate depending on the platform. So for readability we split it
+// into separate files and have the only macro crud live here.
+//
+// NB: these guards are based on those where struct NPEvent is defined
+// in npapi.h. They should be kept in sync.
+#if defined(XP_MACOSX)
+# include "mozilla/plugins/NPEventOSX.h"
+#elif defined(XP_WIN)
+# include "mozilla/plugins/NPEventWindows.h"
+#elif defined(ANDROID)
+# include "mozilla/plugins/NPEventAndroid.h"
+#elif defined(XP_UNIX)
+# include "mozilla/plugins/NPEventUnix.h"
+#else
+# error Unsupported platform
+#endif
+
+#endif /* DOM_PLUGINS_PLUGINMESSAGEUTILS_H */
diff --git a/dom/plugins/ipc/PluginModuleChild.cpp b/dom/plugins/ipc/PluginModuleChild.cpp
new file mode 100644
index 000000000..84dc7c71f
--- /dev/null
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -0,0 +1,2681 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set 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/. */
+
+#include "mozilla/plugins/PluginModuleChild.h"
+
+/* This must occur *after* plugins/PluginModuleChild.h to avoid typedefs conflicts. */
+#include "mozilla/ArrayUtils.h"
+
+#include "mozilla/ipc/MessageChannel.h"
+
+#ifdef MOZ_WIDGET_GTK
+#include <gtk/gtk.h>
+#endif
+
+#include "nsIFile.h"
+
+#include "pratom.h"
+#include "nsDebug.h"
+#include "nsCOMPtr.h"
+#include "nsPluginsDir.h"
+#include "nsXULAppAPI.h"
+
+#ifdef MOZ_X11
+# include "nsX11ErrorHandler.h"
+# include "mozilla/X11Util.h"
+#endif
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/plugins/PluginInstanceChild.h"
+#include "mozilla/plugins/StreamNotifyChild.h"
+#include "mozilla/plugins/BrowserStreamChild.h"
+#include "mozilla/plugins/PluginStreamChild.h"
+#include "mozilla/dom/CrashReporterChild.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
+
+#include "nsNPAPIPlugin.h"
+
+#ifdef XP_WIN
+#include "nsWindowsDllInterceptor.h"
+#include "mozilla/widget/AudioSession.h"
+#include "WinUtils.h"
+#include <knownfolders.h>
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include "PluginInterposeOSX.h"
+#include "PluginUtilsOSX.h"
+#endif
+
+#include "GeckoProfiler.h"
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+using namespace mozilla::plugins;
+using namespace mozilla::widget;
+using mozilla::dom::CrashReporterChild;
+using mozilla::dom::PCrashReporterChild;
+
+#if defined(XP_WIN)
+const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
+const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
+#endif
+
+namespace {
+// see PluginModuleChild::GetChrome()
+PluginModuleChild* gChromeInstance = nullptr;
+} // namespace
+
+#ifdef XP_WIN
+// Hooking CreateFileW for protected-mode magic
+static WindowsDllInterceptor sKernel32Intercept;
+typedef HANDLE (WINAPI *CreateFileWPtr)(LPCWSTR fname, DWORD access,
+ DWORD share,
+ LPSECURITY_ATTRIBUTES security,
+ DWORD creation, DWORD flags,
+ HANDLE ftemplate);
+static CreateFileWPtr sCreateFileWStub = nullptr;
+typedef HANDLE (WINAPI *CreateFileAPtr)(LPCSTR fname, DWORD access,
+ DWORD share,
+ LPSECURITY_ATTRIBUTES security,
+ DWORD creation, DWORD flags,
+ HANDLE ftemplate);
+static CreateFileAPtr sCreateFileAStub = nullptr;
+
+// Used with fix for flash fullscreen window loosing focus.
+static bool gDelayFlashFocusReplyUntilEval = false;
+// Used to fix GetWindowInfo problems with internal flash settings dialogs
+static WindowsDllInterceptor sUser32Intercept;
+typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
+static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
+static HWND sBrowserHwnd = nullptr;
+// sandbox process doesn't get current key states. So we need get it on chrome.
+typedef SHORT (WINAPI *GetKeyStatePtr)(int);
+static GetKeyStatePtr sGetKeyStatePtrStub = nullptr;
+#endif
+
+/* static */
+PluginModuleChild*
+PluginModuleChild::CreateForContentProcess(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherPid)
+{
+ PluginModuleChild* child = new PluginModuleChild(false);
+
+ if (!child->InitForContent(aOtherPid, XRE_GetIOMessageLoop(), aTransport)) {
+ return nullptr;
+ }
+
+ return child;
+}
+
+PluginModuleChild::PluginModuleChild(bool aIsChrome)
+ : mLibrary(0)
+ , mPluginFilename("")
+ , mQuirks(QUIRKS_NOT_INITIALIZED)
+ , mIsChrome(aIsChrome)
+ , mHasShutdown(false)
+ , mTransport(nullptr)
+ , mShutdownFunc(0)
+ , mInitializeFunc(0)
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ , mGetEntryPointsFunc(0)
+#elif defined(MOZ_WIDGET_GTK)
+ , mNestedLoopTimerId(0)
+#endif
+#ifdef OS_WIN
+ , mNestedEventHook(nullptr)
+ , mGlobalCallWndProcHook(nullptr)
+ , mAsyncRenderSupport(false)
+#endif
+{
+ memset(&mFunctions, 0, sizeof(mFunctions));
+ if (mIsChrome) {
+ MOZ_ASSERT(!gChromeInstance);
+ gChromeInstance = this;
+ }
+
+#ifdef XP_MACOSX
+ if (aIsChrome) {
+ mac_plugin_interposing::child::SetUpCocoaInterposing();
+ }
+#endif
+}
+
+PluginModuleChild::~PluginModuleChild()
+{
+ if (mIsChrome) {
+ MOZ_ASSERT(gChromeInstance == this);
+
+ // We don't unload the plugin library in case it uses atexit handlers or
+ // other similar hooks.
+
+ DeinitGraphics();
+ PluginScriptableObjectChild::ClearIdentifiers();
+
+ gChromeInstance = nullptr;
+ }
+}
+
+// static
+PluginModuleChild*
+PluginModuleChild::GetChrome()
+{
+ // A special PluginModuleChild instance that talks to the chrome process
+ // during startup and shutdown. Synchronous messages to or from this actor
+ // should be avoided because they may lead to hangs.
+ MOZ_ASSERT(gChromeInstance);
+ return gChromeInstance;
+}
+
+bool
+PluginModuleChild::CommonInit(base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ // Request Windows message deferral behavior on our channel. This
+ // applies to the top level and all sub plugin protocols since they
+ // all share the same channel.
+ // Bug 1090573 - Don't do this for connections to content processes.
+ GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
+ if (!Open(aChannel, aParentPid, aIOLoop)) {
+ return false;
+ }
+
+ memset((void*) &mFunctions, 0, sizeof(mFunctions));
+ mFunctions.size = sizeof(mFunctions);
+ mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+
+ return true;
+}
+
+bool
+PluginModuleChild::InitForContent(base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel)
+{
+ if (!CommonInit(aParentPid, aIOLoop, aChannel)) {
+ return false;
+ }
+
+ mTransport = aChannel;
+
+ mLibrary = GetChrome()->mLibrary;
+ mFunctions = GetChrome()->mFunctions;
+
+ return true;
+}
+
+bool
+PluginModuleChild::RecvDisableFlashProtectedMode()
+{
+ MOZ_ASSERT(mIsChrome);
+#ifdef XP_WIN
+ HookProtectedMode();
+#else
+ MOZ_ASSERT(false, "Should not be called");
+#endif
+ return true;
+}
+
+bool
+PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
+ base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel)
+{
+ NS_ASSERTION(aChannel, "need a channel");
+
+ if (!InitGraphics())
+ return false;
+
+ mPluginFilename = aPluginFilename.c_str();
+ nsCOMPtr<nsIFile> localFile;
+ NS_NewLocalFile(NS_ConvertUTF8toUTF16(mPluginFilename),
+ true,
+ getter_AddRefs(localFile));
+
+ if (!localFile)
+ return false;
+
+ bool exists;
+ localFile->Exists(&exists);
+ NS_ASSERTION(exists, "plugin file ain't there");
+
+ nsPluginFile pluginFile(localFile);
+
+ nsPluginInfo info = nsPluginInfo();
+ if (NS_FAILED(pluginFile.GetPluginInfo(info, &mLibrary))) {
+ return false;
+ }
+
+#if defined(XP_WIN)
+ // XXX quirks isn't initialized yet
+ mAsyncRenderSupport = info.fSupportsAsyncRender;
+#endif
+#if defined(MOZ_X11)
+ NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10.");
+ if (StringBeginsWith(nsDependentCString(info.fDescription), flash10Head)) {
+ AddQuirk(QUIRK_FLASH_EXPOSE_COORD_TRANSLATION);
+ }
+#endif
+#if defined(XP_MACOSX)
+ const char* namePrefix = "Plugin Content";
+ char nameBuffer[80];
+ SprintfLiteral(nameBuffer, "%s (%s)", namePrefix, info.fName);
+ mozilla::plugins::PluginUtilsOSX::SetProcessName(nameBuffer);
+#endif
+ pluginFile.FreePluginInfo(info);
+#if defined(MOZ_X11) || defined(XP_MACOSX)
+ if (!mLibrary)
+#endif
+ {
+ nsresult rv = pluginFile.LoadPlugin(&mLibrary);
+ if (NS_FAILED(rv))
+ return false;
+ }
+ NS_ASSERTION(mLibrary, "couldn't open shared object");
+
+ if (!CommonInit(aParentPid, aIOLoop, aChannel)) {
+ return false;
+ }
+
+ GetIPCChannel()->SetAbortOnError(true);
+
+ // TODO: use PluginPRLibrary here
+
+#if defined(OS_LINUX) || defined(OS_BSD)
+ mShutdownFunc =
+ (NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
+
+ // create the new plugin handler
+
+ mInitializeFunc =
+ (NP_PLUGINUNIXINIT) PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
+ NS_ASSERTION(mInitializeFunc, "couldn't find NP_Initialize()");
+
+#elif defined(OS_WIN) || defined(OS_MACOSX)
+ mShutdownFunc =
+ (NP_PLUGINSHUTDOWN)PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
+
+ mGetEntryPointsFunc =
+ (NP_GETENTRYPOINTS)PR_FindSymbol(mLibrary, "NP_GetEntryPoints");
+ NS_ENSURE_TRUE(mGetEntryPointsFunc, false);
+
+ mInitializeFunc =
+ (NP_PLUGININIT)PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
+ NS_ENSURE_TRUE(mInitializeFunc, false);
+#else
+
+# error Please copy the initialization code from nsNPAPIPlugin.cpp
+
+#endif
+
+ return true;
+}
+
+#if defined(MOZ_WIDGET_GTK)
+
+typedef void (*GObjectDisposeFn)(GObject*);
+typedef gboolean (*GtkWidgetScrollEventFn)(GtkWidget*, GdkEventScroll*);
+typedef void (*GtkPlugEmbeddedFn)(GtkPlug*);
+
+static GObjectDisposeFn real_gtk_plug_dispose;
+static GtkPlugEmbeddedFn real_gtk_plug_embedded;
+
+static void
+undo_bogus_unref(gpointer data, GObject* object, gboolean is_last_ref) {
+ if (!is_last_ref) // recursion in g_object_ref
+ return;
+
+ g_object_ref(object);
+}
+
+static void
+wrap_gtk_plug_dispose(GObject* object) {
+ // Work around Flash Player bug described in bug 538914.
+ //
+ // This function is called during gtk_widget_destroy and/or before
+ // the object's last reference is removed. A reference to the
+ // object is held during the call so the ref count should not drop
+ // to zero. However, Flash Player tries to destroy the GtkPlug
+ // using g_object_unref instead of gtk_widget_destroy. The
+ // reference that Flash is removing actually belongs to the
+ // GtkPlug. During real_gtk_plug_dispose, the GtkPlug removes its
+ // reference.
+ //
+ // A toggle ref is added to prevent premature deletion of the object
+ // caused by Flash Player's extra unref, and to detect when there are
+ // unexpectedly no other references.
+ g_object_add_toggle_ref(object, undo_bogus_unref, nullptr);
+ (*real_gtk_plug_dispose)(object);
+ g_object_remove_toggle_ref(object, undo_bogus_unref, nullptr);
+}
+
+static gboolean
+gtk_plug_scroll_event(GtkWidget *widget, GdkEventScroll *gdk_event)
+{
+ if (!gtk_widget_is_toplevel(widget)) // in same process as its GtkSocket
+ return FALSE; // event not handled; propagate to GtkSocket
+
+ GdkWindow* socket_window = gtk_plug_get_socket_window(GTK_PLUG(widget));
+ if (!socket_window)
+ return FALSE;
+
+ // Propagate the event to the embedder.
+ GdkScreen* screen = gdk_window_get_screen(socket_window);
+ GdkWindow* plug_window = gtk_widget_get_window(widget);
+ GdkWindow* event_window = gdk_event->window;
+ gint x = gdk_event->x;
+ gint y = gdk_event->y;
+ unsigned int button;
+ unsigned int button_mask = 0;
+ XEvent xevent;
+ Display* dpy = GDK_WINDOW_XDISPLAY(socket_window);
+
+ /* Translate the event coordinates to the plug window,
+ * which should be aligned with the socket window.
+ */
+ while (event_window != plug_window)
+ {
+ gint dx, dy;
+
+ gdk_window_get_position(event_window, &dx, &dy);
+ x += dx;
+ y += dy;
+
+ event_window = gdk_window_get_parent(event_window);
+ if (!event_window)
+ return FALSE;
+ }
+
+ switch (gdk_event->direction) {
+ case GDK_SCROLL_UP:
+ button = 4;
+ button_mask = Button4Mask;
+ break;
+ case GDK_SCROLL_DOWN:
+ button = 5;
+ button_mask = Button5Mask;
+ break;
+ case GDK_SCROLL_LEFT:
+ button = 6;
+ break;
+ case GDK_SCROLL_RIGHT:
+ button = 7;
+ break;
+ default:
+ return FALSE; // unknown GdkScrollDirection
+ }
+
+ memset(&xevent, 0, sizeof(xevent));
+ xevent.xbutton.type = ButtonPress;
+ xevent.xbutton.window = gdk_x11_window_get_xid(socket_window);
+ xevent.xbutton.root = gdk_x11_window_get_xid(gdk_screen_get_root_window(screen));
+ xevent.xbutton.subwindow = gdk_x11_window_get_xid(plug_window);
+ xevent.xbutton.time = gdk_event->time;
+ xevent.xbutton.x = x;
+ xevent.xbutton.y = y;
+ xevent.xbutton.x_root = gdk_event->x_root;
+ xevent.xbutton.y_root = gdk_event->y_root;
+ xevent.xbutton.state = gdk_event->state;
+ xevent.xbutton.button = button;
+ xevent.xbutton.same_screen = True;
+
+ gdk_error_trap_push();
+
+ XSendEvent(dpy, xevent.xbutton.window,
+ True, ButtonPressMask, &xevent);
+
+ xevent.xbutton.type = ButtonRelease;
+ xevent.xbutton.state |= button_mask;
+ XSendEvent(dpy, xevent.xbutton.window,
+ True, ButtonReleaseMask, &xevent);
+
+ gdk_display_sync(gdk_screen_get_display(screen));
+ gdk_error_trap_pop();
+
+ return TRUE; // event handled
+}
+
+static void
+wrap_gtk_plug_embedded(GtkPlug* plug) {
+ GdkWindow* socket_window = gtk_plug_get_socket_window(plug);
+ if (socket_window) {
+ if (gtk_check_version(2,18,7) != nullptr // older
+ && g_object_get_data(G_OBJECT(socket_window),
+ "moz-existed-before-set-window")) {
+ // Add missing reference for
+ // https://bugzilla.gnome.org/show_bug.cgi?id=607061
+ g_object_ref(socket_window);
+ }
+
+ // Ensure the window exists to make this GtkPlug behave like an
+ // in-process GtkPlug for Flash Player. (Bugs 561308 and 539138).
+ gtk_widget_realize(GTK_WIDGET(plug));
+ }
+
+ if (*real_gtk_plug_embedded) {
+ (*real_gtk_plug_embedded)(plug);
+ }
+}
+
+//
+// The next four constants are knobs that can be tuned. They trade
+// off potential UI lag from delayed event processing with CPU time.
+//
+static const gint kNestedLoopDetectorPriority = G_PRIORITY_HIGH_IDLE;
+// 90ms so that we can hopefully break livelocks before the user
+// notices UI lag (100ms)
+static const guint kNestedLoopDetectorIntervalMs = 90;
+
+static const gint kBrowserEventPriority = G_PRIORITY_HIGH_IDLE;
+static const guint kBrowserEventIntervalMs = 10;
+
+// static
+gboolean
+PluginModuleChild::DetectNestedEventLoop(gpointer data)
+{
+ PluginModuleChild* pmc = static_cast<PluginModuleChild*>(data);
+
+ MOZ_ASSERT(0 != pmc->mNestedLoopTimerId,
+ "callback after descheduling");
+ MOZ_ASSERT(pmc->mTopLoopDepth < g_main_depth(),
+ "not canceled before returning to main event loop!");
+
+ PLUGIN_LOG_DEBUG(("Detected nested glib event loop"));
+
+ // just detected a nested loop; start a timer that will
+ // periodically rpc-call back into the browser and process some
+ // events
+ pmc->mNestedLoopTimerId =
+ g_timeout_add_full(kBrowserEventPriority,
+ kBrowserEventIntervalMs,
+ PluginModuleChild::ProcessBrowserEvents,
+ data,
+ nullptr);
+ // cancel the nested-loop detection timer
+ return FALSE;
+}
+
+// static
+gboolean
+PluginModuleChild::ProcessBrowserEvents(gpointer data)
+{
+ PluginModuleChild* pmc = static_cast<PluginModuleChild*>(data);
+
+ MOZ_ASSERT(pmc->mTopLoopDepth < g_main_depth(),
+ "not canceled before returning to main event loop!");
+
+ pmc->CallProcessSomeEvents();
+
+ return TRUE;
+}
+
+void
+PluginModuleChild::EnteredCxxStack()
+{
+ MOZ_ASSERT(0 == mNestedLoopTimerId,
+ "previous timer not descheduled");
+
+ mNestedLoopTimerId =
+ g_timeout_add_full(kNestedLoopDetectorPriority,
+ kNestedLoopDetectorIntervalMs,
+ PluginModuleChild::DetectNestedEventLoop,
+ this,
+ nullptr);
+
+#ifdef DEBUG
+ mTopLoopDepth = g_main_depth();
+#endif
+}
+
+void
+PluginModuleChild::ExitedCxxStack()
+{
+ MOZ_ASSERT(0 < mNestedLoopTimerId,
+ "nested loop timeout not scheduled");
+
+ g_source_remove(mNestedLoopTimerId);
+ mNestedLoopTimerId = 0;
+}
+
+#endif
+
+bool
+PluginModuleChild::RecvSetParentHangTimeout(const uint32_t& aSeconds)
+{
+#ifdef XP_WIN
+ SetReplyTimeoutMs(((aSeconds > 0) ? (1000 * aSeconds) : 0));
+#endif
+ return true;
+}
+
+bool
+PluginModuleChild::ShouldContinueFromReplyTimeout()
+{
+#ifdef XP_WIN
+ NS_RUNTIMEABORT("terminating child process");
+#endif
+ return true;
+}
+
+bool
+PluginModuleChild::InitGraphics()
+{
+#if defined(MOZ_WIDGET_GTK)
+ // Work around plugins that don't interact well with GDK
+ // client-side windows.
+ PR_SetEnv("GDK_NATIVE_WINDOWS=1");
+
+ gtk_init(0, 0);
+
+ // GtkPlug is a static class so will leak anyway but this ref makes sure.
+ gpointer gtk_plug_class = g_type_class_ref(GTK_TYPE_PLUG);
+
+ // The dispose method is a good place to hook into the destruction process
+ // because the reference count should be 1 the last time dispose is
+ // called. (Toggle references wouldn't detect if the reference count
+ // might be higher.)
+ GObjectDisposeFn* dispose = &G_OBJECT_CLASS(gtk_plug_class)->dispose;
+ MOZ_ASSERT(*dispose != wrap_gtk_plug_dispose,
+ "InitGraphics called twice");
+ real_gtk_plug_dispose = *dispose;
+ *dispose = wrap_gtk_plug_dispose;
+
+ // If we ever stop setting GDK_NATIVE_WINDOWS, we'll also need to
+ // gtk_widget_add_events GDK_SCROLL_MASK or GDK client-side windows will
+ // not tell us about the scroll events that it intercepts. With native
+ // windows, this is called when GDK intercepts the events; if GDK doesn't
+ // intercept the events, then the X server will instead send them directly
+ // to an ancestor (embedder) window.
+ GtkWidgetScrollEventFn* scroll_event =
+ &GTK_WIDGET_CLASS(gtk_plug_class)->scroll_event;
+ if (!*scroll_event) {
+ *scroll_event = gtk_plug_scroll_event;
+ }
+
+ GtkPlugEmbeddedFn* embedded = &GTK_PLUG_CLASS(gtk_plug_class)->embedded;
+ real_gtk_plug_embedded = *embedded;
+ *embedded = wrap_gtk_plug_embedded;
+
+#else
+ // may not be necessary on all platforms
+#endif
+#ifdef MOZ_X11
+ // Do this after initializing GDK, or GDK will install its own handler.
+ InstallX11ErrorHandler();
+#endif
+ return true;
+}
+
+void
+PluginModuleChild::DeinitGraphics()
+{
+#if defined(MOZ_X11) && defined(NS_FREE_PERMANENT_DATA)
+ // We free some data off of XDisplay close hooks, ensure they're
+ // run. Closing the display is pretty scary, so we only do it to
+ // silence leak checkers.
+ XCloseDisplay(DefaultXDisplay());
+#endif
+}
+
+NPError
+PluginModuleChild::NP_Shutdown()
+{
+ AssertPluginThread();
+ MOZ_ASSERT(mIsChrome);
+
+ if (mHasShutdown) {
+ return NPERR_NO_ERROR;
+ }
+
+#if defined XP_WIN
+ mozilla::widget::StopAudioSession();
+#endif
+
+ // the PluginModuleParent shuts down this process after this interrupt
+ // call pops off its stack
+
+ NPError rv = mShutdownFunc ? mShutdownFunc() : NPERR_NO_ERROR;
+
+ // weakly guard against re-entry after NP_Shutdown
+ memset(&mFunctions, 0, sizeof(mFunctions));
+
+#ifdef OS_WIN
+ ResetEventHooks();
+#endif
+
+ GetIPCChannel()->SetAbortOnError(false);
+
+ mHasShutdown = true;
+
+ return rv;
+}
+
+bool
+PluginModuleChild::AnswerNP_Shutdown(NPError *rv)
+{
+ *rv = NP_Shutdown();
+ return true;
+}
+
+bool
+PluginModuleChild::AnswerOptionalFunctionsSupported(bool *aURLRedirectNotify,
+ bool *aClearSiteData,
+ bool *aGetSitesWithData)
+{
+ *aURLRedirectNotify = !!mFunctions.urlredirectnotify;
+ *aClearSiteData = !!mFunctions.clearsitedata;
+ *aGetSitesWithData = !!mFunctions.getsiteswithdata;
+ return true;
+}
+
+bool
+PluginModuleChild::RecvNPP_ClearSiteData(const nsCString& aSite,
+ const uint64_t& aFlags,
+ const uint64_t& aMaxAge,
+ const uint64_t& aCallbackId)
+{
+ NPError result =
+ mFunctions.clearsitedata(NullableStringGet(aSite), aFlags, aMaxAge);
+ SendReturnClearSiteData(result, aCallbackId);
+ return true;
+}
+
+bool
+PluginModuleChild::RecvNPP_GetSitesWithData(const uint64_t& aCallbackId)
+{
+ char** result = mFunctions.getsiteswithdata();
+ InfallibleTArray<nsCString> array;
+ if (!result) {
+ SendReturnSitesWithData(array, aCallbackId);
+ return true;
+ }
+ char** iterator = result;
+ while (*iterator) {
+ array.AppendElement(*iterator);
+ free(*iterator);
+ ++iterator;
+ }
+ SendReturnSitesWithData(array, aCallbackId);
+ free(result);
+ return true;
+}
+
+bool
+PluginModuleChild::RecvSetAudioSessionData(const nsID& aId,
+ const nsString& aDisplayName,
+ const nsString& aIconPath)
+{
+#if !defined XP_WIN
+ NS_RUNTIMEABORT("Not Reached!");
+ return false;
+#else
+ nsresult rv = mozilla::widget::RecvAudioSessionData(aId, aDisplayName, aIconPath);
+ NS_ENSURE_SUCCESS(rv, true); // Bail early if this fails
+
+ // Ignore failures here; we can't really do anything about them
+ mozilla::widget::StartAudioSession();
+ return true;
+#endif
+}
+
+PPluginModuleChild*
+PluginModuleChild::AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherPid)
+{
+ return PluginModuleChild::CreateForContentProcess(aTransport, aOtherPid);
+}
+
+PCrashReporterChild*
+PluginModuleChild::AllocPCrashReporterChild(mozilla::dom::NativeThreadId* id,
+ uint32_t* processType)
+{
+ return new CrashReporterChild();
+}
+
+bool
+PluginModuleChild::DeallocPCrashReporterChild(PCrashReporterChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+bool
+PluginModuleChild::AnswerPCrashReporterConstructor(
+ PCrashReporterChild* actor,
+ mozilla::dom::NativeThreadId* id,
+ uint32_t* processType)
+{
+#ifdef MOZ_CRASHREPORTER
+ *id = CrashReporter::CurrentThreadId();
+ *processType = XRE_GetProcessType();
+#endif
+ return true;
+}
+
+void
+PluginModuleChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (!mIsChrome) {
+ PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+ if (chromeInstance) {
+ chromeInstance->SendNotifyContentModuleDestroyed();
+ }
+
+ // Destroy ourselves once we finish other teardown activities.
+ RefPtr<DeleteTask<PluginModuleChild>> task =
+ new DeleteTask<PluginModuleChild>(this);
+ MessageLoop::current()->PostTask(task.forget());
+ return;
+ }
+
+ if (AbnormalShutdown == why) {
+ NS_WARNING("shutting down early because of crash!");
+ ProcessChild::QuickExit();
+ }
+
+ if (!mHasShutdown) {
+ MOZ_ASSERT(gChromeInstance == this);
+ NP_Shutdown();
+ }
+
+ // doesn't matter why we're being destroyed; it's up to us to
+ // initiate (clean) shutdown
+ XRE_ShutdownChildProcess();
+}
+
+void
+PluginModuleChild::CleanUp()
+{
+}
+
+const char*
+PluginModuleChild::GetUserAgent()
+{
+ return NullableStringGet(Settings().userAgent());
+}
+
+//-----------------------------------------------------------------------------
+// FIXME/cjones: just getting this out of the way for the moment ...
+
+namespace mozilla {
+namespace plugins {
+namespace child {
+
+static NPError
+_requestread(NPStream *pstream, NPByteRange *rangeList);
+
+static NPError
+_geturlnotify(NPP aNPP, const char* relativeURL, const char* target,
+ void* notifyData);
+
+static NPError
+_getvalue(NPP aNPP, NPNVariable variable, void *r_value);
+
+static NPError
+_setvalue(NPP aNPP, NPPVariable variable, void *r_value);
+
+static NPError
+_geturl(NPP aNPP, const char* relativeURL, const char* target);
+
+static NPError
+_posturlnotify(NPP aNPP, const char* relativeURL, const char *target,
+ uint32_t len, const char *buf, NPBool file, void* notifyData);
+
+static NPError
+_posturl(NPP aNPP, const char* relativeURL, const char *target, uint32_t len,
+ const char *buf, NPBool file);
+
+static NPError
+_newstream(NPP aNPP, NPMIMEType type, const char* window, NPStream** pstream);
+
+static int32_t
+_write(NPP aNPP, NPStream *pstream, int32_t len, void *buffer);
+
+static NPError
+_destroystream(NPP aNPP, NPStream *pstream, NPError reason);
+
+static void
+_status(NPP aNPP, const char *message);
+
+static void
+_memfree (void *ptr);
+
+static uint32_t
+_memflush(uint32_t size);
+
+static void
+_reloadplugins(NPBool reloadPages);
+
+static void
+_invalidaterect(NPP aNPP, NPRect *invalidRect);
+
+static void
+_invalidateregion(NPP aNPP, NPRegion invalidRegion);
+
+static void
+_forceredraw(NPP aNPP);
+
+static const char*
+_useragent(NPP aNPP);
+
+static void*
+_memalloc (uint32_t size);
+
+// Deprecated entry points for the old Java plugin.
+static void* /* OJI type: JRIEnv* */
+_getjavaenv(void);
+
+// Deprecated entry points for the old Java plugin.
+static void* /* OJI type: jref */
+_getjavapeer(NPP aNPP);
+
+static bool
+_invoke(NPP aNPP, NPObject* npobj, NPIdentifier method, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+
+static bool
+_invokedefault(NPP aNPP, NPObject* npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+
+static bool
+_evaluate(NPP aNPP, NPObject* npobj, NPString *script, NPVariant *result);
+
+static bool
+_getproperty(NPP aNPP, NPObject* npobj, NPIdentifier property,
+ NPVariant *result);
+
+static bool
+_setproperty(NPP aNPP, NPObject* npobj, NPIdentifier property,
+ const NPVariant *value);
+
+static bool
+_removeproperty(NPP aNPP, NPObject* npobj, NPIdentifier property);
+
+static bool
+_hasproperty(NPP aNPP, NPObject* npobj, NPIdentifier propertyName);
+
+static bool
+_hasmethod(NPP aNPP, NPObject* npobj, NPIdentifier methodName);
+
+static bool
+_enumerate(NPP aNPP, NPObject *npobj, NPIdentifier **identifier,
+ uint32_t *count);
+
+static bool
+_construct(NPP aNPP, NPObject* npobj, const NPVariant *args,
+ uint32_t argCount, NPVariant *result);
+
+static void
+_releasevariantvalue(NPVariant *variant);
+
+static void
+_setexception(NPObject* npobj, const NPUTF8 *message);
+
+static void
+_pushpopupsenabledstate(NPP aNPP, NPBool enabled);
+
+static void
+_poppopupsenabledstate(NPP aNPP);
+
+static void
+_pluginthreadasynccall(NPP instance, PluginThreadCallback func,
+ void *userData);
+
+static NPError
+_getvalueforurl(NPP npp, NPNURLVariable variable, const char *url,
+ char **value, uint32_t *len);
+
+static NPError
+_setvalueforurl(NPP npp, NPNURLVariable variable, const char *url,
+ const char *value, uint32_t len);
+
+static NPError
+_getauthenticationinfo(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);
+
+static uint32_t
+_scheduletimer(NPP instance, uint32_t interval, NPBool repeat,
+ void (*timerFunc)(NPP npp, uint32_t timerID));
+
+static void
+_unscheduletimer(NPP instance, uint32_t timerID);
+
+static NPError
+_popupcontextmenu(NPP instance, NPMenu* menu);
+
+static NPBool
+_convertpoint(NPP instance,
+ double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace);
+
+static void
+_urlredirectresponse(NPP instance, void* notifyData, NPBool allow);
+
+static NPError
+_initasyncsurface(NPP instance, NPSize *size,
+ NPImageFormat format, void *initData,
+ NPAsyncSurface *surface);
+
+static NPError
+_finalizeasyncsurface(NPP instance, NPAsyncSurface *surface);
+
+static void
+_setcurrentasyncsurface(NPP instance, NPAsyncSurface *surface, NPRect *changed);
+
+} /* namespace child */
+} /* namespace plugins */
+} /* namespace mozilla */
+
+const NPNetscapeFuncs PluginModuleChild::sBrowserFuncs = {
+ sizeof(sBrowserFuncs),
+ (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR,
+ mozilla::plugins::child::_geturl,
+ mozilla::plugins::child::_posturl,
+ mozilla::plugins::child::_requestread,
+ mozilla::plugins::child::_newstream,
+ mozilla::plugins::child::_write,
+ mozilla::plugins::child::_destroystream,
+ mozilla::plugins::child::_status,
+ mozilla::plugins::child::_useragent,
+ mozilla::plugins::child::_memalloc,
+ mozilla::plugins::child::_memfree,
+ mozilla::plugins::child::_memflush,
+ mozilla::plugins::child::_reloadplugins,
+ mozilla::plugins::child::_getjavaenv,
+ mozilla::plugins::child::_getjavapeer,
+ mozilla::plugins::child::_geturlnotify,
+ mozilla::plugins::child::_posturlnotify,
+ mozilla::plugins::child::_getvalue,
+ mozilla::plugins::child::_setvalue,
+ mozilla::plugins::child::_invalidaterect,
+ mozilla::plugins::child::_invalidateregion,
+ mozilla::plugins::child::_forceredraw,
+ PluginModuleChild::NPN_GetStringIdentifier,
+ PluginModuleChild::NPN_GetStringIdentifiers,
+ PluginModuleChild::NPN_GetIntIdentifier,
+ PluginModuleChild::NPN_IdentifierIsString,
+ PluginModuleChild::NPN_UTF8FromIdentifier,
+ PluginModuleChild::NPN_IntFromIdentifier,
+ PluginModuleChild::NPN_CreateObject,
+ PluginModuleChild::NPN_RetainObject,
+ PluginModuleChild::NPN_ReleaseObject,
+ mozilla::plugins::child::_invoke,
+ mozilla::plugins::child::_invokedefault,
+ mozilla::plugins::child::_evaluate,
+ mozilla::plugins::child::_getproperty,
+ mozilla::plugins::child::_setproperty,
+ mozilla::plugins::child::_removeproperty,
+ mozilla::plugins::child::_hasproperty,
+ mozilla::plugins::child::_hasmethod,
+ mozilla::plugins::child::_releasevariantvalue,
+ mozilla::plugins::child::_setexception,
+ mozilla::plugins::child::_pushpopupsenabledstate,
+ mozilla::plugins::child::_poppopupsenabledstate,
+ mozilla::plugins::child::_enumerate,
+ mozilla::plugins::child::_pluginthreadasynccall,
+ mozilla::plugins::child::_construct,
+ mozilla::plugins::child::_getvalueforurl,
+ mozilla::plugins::child::_setvalueforurl,
+ mozilla::plugins::child::_getauthenticationinfo,
+ mozilla::plugins::child::_scheduletimer,
+ mozilla::plugins::child::_unscheduletimer,
+ mozilla::plugins::child::_popupcontextmenu,
+ mozilla::plugins::child::_convertpoint,
+ nullptr, // handleevent, unimplemented
+ nullptr, // unfocusinstance, unimplemented
+ mozilla::plugins::child::_urlredirectresponse,
+ mozilla::plugins::child::_initasyncsurface,
+ mozilla::plugins::child::_finalizeasyncsurface,
+ mozilla::plugins::child::_setcurrentasyncsurface,
+};
+
+PluginInstanceChild*
+InstCast(NPP aNPP)
+{
+ MOZ_ASSERT(!!(aNPP->ndata), "nil instance");
+ return static_cast<PluginInstanceChild*>(aNPP->ndata);
+}
+
+namespace mozilla {
+namespace plugins {
+namespace child {
+
+NPError
+_requestread(NPStream* aStream,
+ NPByteRange* aRangeList)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+
+ BrowserStreamChild* bs =
+ static_cast<BrowserStreamChild*>(static_cast<AStream*>(aStream->ndata));
+ bs->EnsureCorrectStream(aStream);
+ return bs->NPN_RequestRead(aRangeList);
+}
+
+NPError
+_geturlnotify(NPP aNPP,
+ const char* aRelativeURL,
+ const char* aTarget,
+ void* aNotifyData)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+
+ if (!aNPP) // nullptr check for nspluginwrapper (bug 561690)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ nsCString url = NullableString(aRelativeURL);
+ StreamNotifyChild* sn = new StreamNotifyChild(url);
+
+ NPError err;
+ InstCast(aNPP)->CallPStreamNotifyConstructor(
+ sn, url, NullableString(aTarget), false, nsCString(), false, &err);
+
+ if (NPERR_NO_ERROR == err) {
+ // If NPN_PostURLNotify fails, the parent will immediately send us
+ // a PStreamNotifyDestructor, which should not call NPP_URLNotify.
+ sn->SetValid(aNotifyData);
+ }
+
+ return err;
+}
+
+NPError
+_getvalue(NPP aNPP,
+ NPNVariable aVariable,
+ void* aValue)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+
+ switch (aVariable) {
+ // Copied from nsNPAPIPlugin.cpp
+ case NPNVToolkit:
+#if defined(MOZ_WIDGET_GTK)
+ *static_cast<NPNToolkitType*>(aValue) = NPNVGtk2;
+ return NPERR_NO_ERROR;
+#endif
+ return NPERR_GENERIC_ERROR;
+
+ case NPNVjavascriptEnabledBool:
+ *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().javascriptEnabled();
+ return NPERR_NO_ERROR;
+ case NPNVasdEnabledBool:
+ *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().asdEnabled();
+ return NPERR_NO_ERROR;
+ case NPNVisOfflineBool:
+ *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().isOffline();
+ return NPERR_NO_ERROR;
+ case NPNVSupportsXEmbedBool:
+ *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().supportsXembed();
+ return NPERR_NO_ERROR;
+ case NPNVSupportsWindowless:
+ *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().supportsWindowless();
+ return NPERR_NO_ERROR;
+#if defined(MOZ_WIDGET_GTK)
+ case NPNVxDisplay: {
+ if (aNPP) {
+ return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
+ }
+ else {
+ *(void **)aValue = xt_client_get_display();
+ }
+ return NPERR_NO_ERROR;
+ }
+ case NPNVxtAppContext:
+ return NPERR_GENERIC_ERROR;
+#endif
+ default: {
+ if (aNPP) {
+ return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
+ }
+
+ NS_WARNING("Null NPP!");
+ return NPERR_INVALID_INSTANCE_ERROR;
+ }
+ }
+
+ NS_NOTREACHED("Shouldn't get here!");
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError
+_setvalue(NPP aNPP,
+ NPPVariable aVariable,
+ void* aValue)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+ return InstCast(aNPP)->NPN_SetValue(aVariable, aValue);
+}
+
+NPError
+_geturl(NPP aNPP,
+ const char* aRelativeURL,
+ const char* aTarget)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+
+ NPError err;
+ InstCast(aNPP)->CallNPN_GetURL(NullableString(aRelativeURL),
+ NullableString(aTarget), &err);
+ return err;
+}
+
+NPError
+_posturlnotify(NPP aNPP,
+ const char* aRelativeURL,
+ const char* aTarget,
+ uint32_t aLength,
+ const char* aBuffer,
+ NPBool aIsFile,
+ void* aNotifyData)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+
+ if (!aBuffer)
+ return NPERR_INVALID_PARAM;
+
+ nsCString url = NullableString(aRelativeURL);
+ StreamNotifyChild* sn = new StreamNotifyChild(url);
+
+ NPError err;
+ InstCast(aNPP)->CallPStreamNotifyConstructor(
+ sn, url, NullableString(aTarget), true,
+ nsCString(aBuffer, aLength), aIsFile, &err);
+
+ if (NPERR_NO_ERROR == err) {
+ // If NPN_PostURLNotify fails, the parent will immediately send us
+ // a PStreamNotifyDestructor, which should not call NPP_URLNotify.
+ sn->SetValid(aNotifyData);
+ }
+
+ return err;
+}
+
+NPError
+_posturl(NPP aNPP,
+ const char* aRelativeURL,
+ const char* aTarget,
+ uint32_t aLength,
+ const char* aBuffer,
+ NPBool aIsFile)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+
+ NPError err;
+ // FIXME what should happen when |aBuffer| is null?
+ InstCast(aNPP)->CallNPN_PostURL(NullableString(aRelativeURL),
+ NullableString(aTarget),
+ nsDependentCString(aBuffer, aLength),
+ aIsFile, &err);
+ return err;
+}
+
+NPError
+_newstream(NPP aNPP,
+ NPMIMEType aMIMEType,
+ const char* aWindow,
+ NPStream** aStream)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+ return InstCast(aNPP)->NPN_NewStream(aMIMEType, aWindow, aStream);
+}
+
+int32_t
+_write(NPP aNPP,
+ NPStream* aStream,
+ int32_t aLength,
+ void* aBuffer)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(0);
+
+ PluginStreamChild* ps =
+ static_cast<PluginStreamChild*>(static_cast<AStream*>(aStream->ndata));
+ ps->EnsureCorrectInstance(InstCast(aNPP));
+ ps->EnsureCorrectStream(aStream);
+ return ps->NPN_Write(aLength, aBuffer);
+}
+
+NPError
+_destroystream(NPP aNPP,
+ NPStream* aStream,
+ NPError aReason)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
+
+ PluginInstanceChild* p = InstCast(aNPP);
+ AStream* s = static_cast<AStream*>(aStream->ndata);
+ if (s->IsBrowserStream()) {
+ BrowserStreamChild* bs = static_cast<BrowserStreamChild*>(s);
+ bs->EnsureCorrectInstance(p);
+ bs->NPN_DestroyStream(aReason);
+ }
+ else {
+ PluginStreamChild* ps = static_cast<PluginStreamChild*>(s);
+ ps->EnsureCorrectInstance(p);
+ PPluginStreamChild::Call__delete__(ps, aReason, false);
+ }
+ return NPERR_NO_ERROR;
+}
+
+void
+_status(NPP aNPP,
+ const char* aMessage)
+{
+ // NPN_Status is no longer supported.
+}
+
+void
+_memfree(void* aPtr)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ free(aPtr);
+}
+
+uint32_t
+_memflush(uint32_t aSize)
+{
+ return 0;
+}
+
+void
+_reloadplugins(NPBool aReloadPages)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ // Send the reload message to all modules. Chrome will need to reload from
+ // disk and content will need to request a new list of plugin tags from
+ // chrome.
+ PluginModuleChild::GetChrome()->SendNPN_ReloadPlugins(!!aReloadPages);
+}
+
+void
+_invalidaterect(NPP aNPP,
+ NPRect* aInvalidRect)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD_VOID();
+ // nullptr check for nspluginwrapper (bug 548434)
+ if (aNPP) {
+ InstCast(aNPP)->InvalidateRect(aInvalidRect);
+ }
+}
+
+void
+_invalidateregion(NPP aNPP,
+ NPRegion aInvalidRegion)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD_VOID();
+ NS_WARNING("Not yet implemented!");
+}
+
+void
+_forceredraw(NPP aNPP)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ // We ignore calls to NPN_ForceRedraw. Such calls should
+ // never be necessary.
+}
+
+const char*
+_useragent(NPP aNPP)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(nullptr);
+ return PluginModuleChild::GetChrome()->GetUserAgent();
+}
+
+void*
+_memalloc(uint32_t aSize)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ return moz_xmalloc(aSize);
+}
+
+// Deprecated entry points for the old Java plugin.
+void* /* OJI type: JRIEnv* */
+_getjavaenv(void)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ return 0;
+}
+
+void* /* OJI type: jref */
+_getjavapeer(NPP aNPP)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ return 0;
+}
+
+bool
+_invoke(NPP aNPP,
+ NPObject* aNPObj,
+ NPIdentifier aMethod,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->invoke)
+ return false;
+
+ return aNPObj->_class->invoke(aNPObj, aMethod, aArgs, aArgCount, aResult);
+}
+
+bool
+_invokedefault(NPP aNPP,
+ NPObject* aNPObj,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->invokeDefault)
+ return false;
+
+ return aNPObj->_class->invokeDefault(aNPObj, aArgs, aArgCount, aResult);
+}
+
+bool
+_evaluate(NPP aNPP,
+ NPObject* aObject,
+ NPString* aScript,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!(aNPP && aObject && aScript && aResult)) {
+ NS_ERROR("Bad arguments!");
+ return false;
+ }
+
+ PluginScriptableObjectChild* actor =
+ InstCast(aNPP)->GetActorForNPObject(aObject);
+ if (!actor) {
+ NS_ERROR("Failed to create actor?!");
+ return false;
+ }
+
+#ifdef XP_WIN
+ if (gDelayFlashFocusReplyUntilEval) {
+ ReplyMessage(0);
+ gDelayFlashFocusReplyUntilEval = false;
+ }
+#endif
+
+ return actor->Evaluate(aScript, aResult);
+}
+
+bool
+_getproperty(NPP aNPP,
+ NPObject* aNPObj,
+ NPIdentifier aPropertyName,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->getProperty)
+ return false;
+
+ return aNPObj->_class->getProperty(aNPObj, aPropertyName, aResult);
+}
+
+bool
+_setproperty(NPP aNPP,
+ NPObject* aNPObj,
+ NPIdentifier aPropertyName,
+ const NPVariant* aValue)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->setProperty)
+ return false;
+
+ return aNPObj->_class->setProperty(aNPObj, aPropertyName, aValue);
+}
+
+bool
+_removeproperty(NPP aNPP,
+ NPObject* aNPObj,
+ NPIdentifier aPropertyName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->removeProperty)
+ return false;
+
+ return aNPObj->_class->removeProperty(aNPObj, aPropertyName);
+}
+
+bool
+_hasproperty(NPP aNPP,
+ NPObject* aNPObj,
+ NPIdentifier aPropertyName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->hasProperty)
+ return false;
+
+ return aNPObj->_class->hasProperty(aNPObj, aPropertyName);
+}
+
+bool
+_hasmethod(NPP aNPP,
+ NPObject* aNPObj,
+ NPIdentifier aMethodName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->hasMethod)
+ return false;
+
+ return aNPObj->_class->hasMethod(aNPObj, aMethodName);
+}
+
+bool
+_enumerate(NPP aNPP,
+ NPObject* aNPObj,
+ NPIdentifier** aIdentifiers,
+ uint32_t* aCount)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class)
+ return false;
+
+ if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(aNPObj->_class) ||
+ !aNPObj->_class->enumerate) {
+ *aIdentifiers = 0;
+ *aCount = 0;
+ return true;
+ }
+
+ return aNPObj->_class->enumerate(aNPObj, aIdentifiers, aCount);
+}
+
+bool
+_construct(NPP aNPP,
+ NPObject* aNPObj,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(false);
+
+ if (!aNPP || !aNPObj || !aNPObj->_class ||
+ !NP_CLASS_STRUCT_VERSION_HAS_CTOR(aNPObj->_class) ||
+ !aNPObj->_class->construct) {
+ return false;
+ }
+
+ return aNPObj->_class->construct(aNPObj, aArgs, aArgCount, aResult);
+}
+
+void
+_releasevariantvalue(NPVariant* aVariant)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ // Only assert plugin thread here for consistency with in-process plugins.
+ AssertPluginThread();
+
+ if (NPVARIANT_IS_STRING(*aVariant)) {
+ NPString str = NPVARIANT_TO_STRING(*aVariant);
+ free(const_cast<NPUTF8*>(str.UTF8Characters));
+ }
+ else if (NPVARIANT_IS_OBJECT(*aVariant)) {
+ NPObject* object = NPVARIANT_TO_OBJECT(*aVariant);
+ if (object) {
+ PluginModuleChild::NPN_ReleaseObject(object);
+ }
+ }
+ VOID_TO_NPVARIANT(*aVariant);
+}
+
+void
+_setexception(NPObject* aNPObj,
+ const NPUTF8* aMessage)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ // Do nothing. We no longer support this API.
+}
+
+void
+_pushpopupsenabledstate(NPP aNPP,
+ NPBool aEnabled)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ InstCast(aNPP)->CallNPN_PushPopupsEnabledState(aEnabled ? true : false);
+}
+
+void
+_poppopupsenabledstate(NPP aNPP)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ InstCast(aNPP)->CallNPN_PopPopupsEnabledState();
+}
+
+void
+_pluginthreadasynccall(NPP aNPP,
+ PluginThreadCallback aFunc,
+ void* aUserData)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (!aFunc)
+ return;
+
+ InstCast(aNPP)->AsyncCall(aFunc, aUserData);
+}
+
+NPError
+_getvalueforurl(NPP npp, NPNURLVariable variable, const char *url,
+ char **value, uint32_t *len)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+
+ if (!url)
+ return NPERR_INVALID_URL;
+
+ if (!npp || !value || !len)
+ return NPERR_INVALID_PARAM;
+
+ switch (variable) {
+ case NPNURLVCookie:
+ case NPNURLVProxy:
+ nsCString v;
+ NPError result;
+ InstCast(npp)->
+ CallNPN_GetValueForURL(variable, nsCString(url), &v, &result);
+ if (NPERR_NO_ERROR == result) {
+ *value = ToNewCString(v);
+ *len = v.Length();
+ }
+ return result;
+ }
+
+ return NPERR_INVALID_PARAM;
+}
+
+NPError
+_setvalueforurl(NPP npp, NPNURLVariable variable, const char *url,
+ const char *value, uint32_t len)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+
+ if (!value)
+ return NPERR_INVALID_PARAM;
+
+ if (!url)
+ return NPERR_INVALID_URL;
+
+ switch (variable) {
+ case NPNURLVCookie:
+ case NPNURLVProxy:
+ NPError result;
+ InstCast(npp)->CallNPN_SetValueForURL(variable, nsCString(url),
+ nsDependentCString(value, len),
+ &result);
+ return result;
+ }
+
+ return NPERR_INVALID_PARAM;
+}
+
+NPError
+_getauthenticationinfo(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)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+
+ if (!protocol || !host || !scheme || !realm || !username || !ulen ||
+ !password || !plen)
+ return NPERR_INVALID_PARAM;
+
+ nsCString u;
+ nsCString p;
+ NPError result;
+ InstCast(npp)->
+ CallNPN_GetAuthenticationInfo(nsDependentCString(protocol),
+ nsDependentCString(host),
+ port,
+ nsDependentCString(scheme),
+ nsDependentCString(realm),
+ &u, &p, &result);
+ if (NPERR_NO_ERROR == result) {
+ *username = ToNewCString(u);
+ *ulen = u.Length();
+ *password = ToNewCString(p);
+ *plen = p.Length();
+ }
+ return result;
+}
+
+uint32_t
+_scheduletimer(NPP npp, uint32_t interval, NPBool repeat,
+ void (*timerFunc)(NPP npp, uint32_t timerID))
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+ return InstCast(npp)->ScheduleTimer(interval, repeat, timerFunc);
+}
+
+void
+_unscheduletimer(NPP npp, uint32_t timerID)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+ InstCast(npp)->UnscheduleTimer(timerID);
+}
+
+
+#ifdef OS_MACOSX
+static void ProcessBrowserEvents(void* pluginModule) {
+ PluginModuleChild* pmc = static_cast<PluginModuleChild*>(pluginModule);
+
+ if (!pmc)
+ return;
+
+ pmc->CallProcessSomeEvents();
+}
+#endif
+
+NPError
+_popupcontextmenu(NPP instance, NPMenu* menu)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+
+#ifdef MOZ_WIDGET_COCOA
+ double pluginX, pluginY;
+ double screenX, screenY;
+
+ const NPCocoaEvent* currentEvent = InstCast(instance)->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,
+ InstCast(instance)->Manager(),
+ ProcessBrowserEvents);
+ } 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)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ if (!IsPluginThread()) {
+ NS_WARNING("Not running on the plugin's main thread!");
+ return false;
+ }
+
+ double rDestX = 0;
+ bool ignoreDestX = !destX;
+ double rDestY = 0;
+ bool ignoreDestY = !destY;
+ bool result = false;
+ InstCast(instance)->CallNPN_ConvertPoint(sourceX, ignoreDestX, sourceY, ignoreDestY, sourceSpace, destSpace,
+ &rDestX, &rDestY, &result);
+ if (result) {
+ if (destX)
+ *destX = rDestX;
+ if (destY)
+ *destY = rDestY;
+ }
+
+ return result;
+}
+
+void
+_urlredirectresponse(NPP instance, void* notifyData, NPBool allow)
+{
+ InstCast(instance)->NPN_URLRedirectResponse(notifyData, allow);
+}
+
+NPError
+_initasyncsurface(NPP instance, NPSize *size,
+ NPImageFormat format, void *initData,
+ NPAsyncSurface *surface)
+{
+ return InstCast(instance)->NPN_InitAsyncSurface(size, format, initData, surface);
+}
+
+NPError
+_finalizeasyncsurface(NPP instance, NPAsyncSurface *surface)
+{
+ return InstCast(instance)->NPN_FinalizeAsyncSurface(surface);
+}
+
+void
+_setcurrentasyncsurface(NPP instance, NPAsyncSurface *surface, NPRect *changed)
+{
+ InstCast(instance)->NPN_SetCurrentAsyncSurface(surface, changed);
+}
+
+} /* namespace child */
+} /* namespace plugins */
+} /* namespace mozilla */
+
+//-----------------------------------------------------------------------------
+
+bool
+PluginModuleChild::RecvSettingChanged(const PluginSettings& aSettings)
+{
+ mCachedSettings = aSettings;
+ return true;
+}
+
+bool
+PluginModuleChild::AnswerNP_GetEntryPoints(NPError* _retval)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+ MOZ_ASSERT(mIsChrome);
+
+#if defined(OS_LINUX) || defined(OS_BSD)
+ return true;
+#elif defined(OS_WIN) || defined(OS_MACOSX)
+ *_retval = mGetEntryPointsFunc(&mFunctions);
+ return true;
+#else
+# error Please implement me for your platform
+#endif
+}
+
+bool
+PluginModuleChild::AnswerNP_Initialize(const PluginSettings& aSettings, NPError* rv)
+{
+ *rv = DoNP_Initialize(aSettings);
+ return true;
+}
+
+bool
+PluginModuleChild::RecvAsyncNP_Initialize(const PluginSettings& aSettings)
+{
+ NPError error = DoNP_Initialize(aSettings);
+ return SendNP_InitializeResult(error);
+}
+
+NPError
+PluginModuleChild::DoNP_Initialize(const PluginSettings& aSettings)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+ MOZ_ASSERT(mIsChrome);
+
+ mCachedSettings = aSettings;
+
+#ifdef OS_WIN
+ SetEventHooks();
+#endif
+
+#ifdef MOZ_X11
+ // Send the parent our X socket to act as a proxy reference for our X
+ // resources.
+ int xSocketFd = ConnectionNumber(DefaultXDisplay());
+ SendBackUpXResources(FileDescriptor(xSocketFd));
+#endif
+
+ NPError result;
+#if defined(OS_LINUX) || defined(OS_BSD)
+ result = mInitializeFunc(&sBrowserFuncs, &mFunctions);
+#elif defined(OS_WIN) || defined(OS_MACOSX)
+ result = mInitializeFunc(&sBrowserFuncs);
+#else
+# error Please implement me for your platform
+#endif
+
+ return result;
+}
+
+#if defined(XP_WIN)
+
+// Windows 8 RTM (kernelbase's version is 6.2.9200.16384) doesn't call
+// CreateFileW from CreateFileA.
+// So we hook CreateFileA too to use CreateFileW hook.
+
+static HANDLE WINAPI
+CreateFileAHookFn(LPCSTR fname, DWORD access, DWORD share,
+ LPSECURITY_ATTRIBUTES security, DWORD creation, DWORD flags,
+ HANDLE ftemplate)
+{
+ while (true) { // goto out
+ // Our hook is for mms.cfg into \Windows\System32\Macromed\Flash
+ // We don't requrie supporting too long path.
+ WCHAR unicodeName[MAX_PATH];
+ size_t len = strlen(fname);
+
+ if (len >= MAX_PATH) {
+ break;
+ }
+
+ // We call to CreateFileW for workaround of Windows 8 RTM
+ int newLen = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, fname,
+ len, unicodeName, MAX_PATH);
+ if (newLen == 0 || newLen >= MAX_PATH) {
+ break;
+ }
+ unicodeName[newLen] = '\0';
+
+ return CreateFileW(unicodeName, access, share, security, creation, flags, ftemplate);
+ }
+
+ return sCreateFileAStub(fname, access, share, security, creation, flags,
+ ftemplate);
+}
+
+static bool
+GetLocalLowTempPath(size_t aLen, LPWSTR aPath)
+{
+ NS_NAMED_LITERAL_STRING(tempname, "\\Temp");
+ LPWSTR path;
+ if (SUCCEEDED(WinUtils::SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0,
+ nullptr, &path))) {
+ if (wcslen(path) + tempname.Length() < aLen) {
+ wcscpy(aPath, path);
+ wcscat(aPath, tempname.get());
+ ::CoTaskMemFree(path);
+ return true;
+ }
+ ::CoTaskMemFree(path);
+ }
+
+ // XP doesn't support SHGetKnownFolderPath and LocalLow
+ if (!GetTempPathW(aLen, aPath)) {
+ return false;
+ }
+ return true;
+}
+
+HANDLE WINAPI
+CreateFileWHookFn(LPCWSTR fname, DWORD access, DWORD share,
+ LPSECURITY_ATTRIBUTES security, DWORD creation, DWORD flags,
+ HANDLE ftemplate)
+{
+ static const WCHAR kConfigFile[] = L"mms.cfg";
+ static const size_t kConfigLength = ArrayLength(kConfigFile) - 1;
+
+ while (true) { // goto out, in sheep's clothing
+ size_t len = wcslen(fname);
+ if (len < kConfigLength) {
+ break;
+ }
+ if (wcscmp(fname + len - kConfigLength, kConfigFile) != 0) {
+ break;
+ }
+
+ // This is the config file we want to rewrite
+ WCHAR tempPath[MAX_PATH+1];
+ if (GetLocalLowTempPath(MAX_PATH, tempPath) == 0) {
+ break;
+ }
+ WCHAR tempFile[MAX_PATH+1];
+ if (GetTempFileNameW(tempPath, L"fx", 0, tempFile) == 0) {
+ break;
+ }
+ HANDLE replacement =
+ sCreateFileWStub(tempFile, GENERIC_READ | GENERIC_WRITE, share,
+ security, TRUNCATE_EXISTING,
+ FILE_ATTRIBUTE_TEMPORARY |
+ FILE_FLAG_DELETE_ON_CLOSE,
+ NULL);
+ if (replacement == INVALID_HANDLE_VALUE) {
+ break;
+ }
+
+ HANDLE original = sCreateFileWStub(fname, access, share, security,
+ creation, flags, ftemplate);
+ if (original != INVALID_HANDLE_VALUE) {
+ // copy original to replacement
+ static const size_t kBufferSize = 1024;
+ char buffer[kBufferSize];
+ DWORD bytes;
+ while (ReadFile(original, buffer, kBufferSize, &bytes, NULL)) {
+ if (bytes == 0) {
+ break;
+ }
+ DWORD wbytes;
+ WriteFile(replacement, buffer, bytes, &wbytes, NULL);
+ if (bytes < kBufferSize) {
+ break;
+ }
+ }
+ CloseHandle(original);
+ }
+ static const char kSettingString[] = "\nProtectedMode=0\n";
+ DWORD wbytes;
+ WriteFile(replacement, static_cast<const void*>(kSettingString),
+ sizeof(kSettingString) - 1, &wbytes, NULL);
+ SetFilePointer(replacement, 0, NULL, FILE_BEGIN);
+ return replacement;
+ }
+ return sCreateFileWStub(fname, access, share, security, creation, flags,
+ ftemplate);
+}
+
+void
+PluginModuleChild::HookProtectedMode()
+{
+ sKernel32Intercept.Init("kernel32.dll");
+ sKernel32Intercept.AddHook("CreateFileW",
+ reinterpret_cast<intptr_t>(CreateFileWHookFn),
+ (void**) &sCreateFileWStub);
+ sKernel32Intercept.AddHook("CreateFileA",
+ reinterpret_cast<intptr_t>(CreateFileAHookFn),
+ (void**) &sCreateFileAStub);
+}
+
+BOOL WINAPI
+PMCGetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi)
+{
+ if (!pwi)
+ return FALSE;
+
+ if (!sGetWindowInfoPtrStub) {
+ NS_ASSERTION(FALSE, "Something is horribly wrong in PMCGetWindowInfoHook!");
+ return FALSE;
+ }
+
+ if (!sBrowserHwnd) {
+ wchar_t szClass[20];
+ if (GetClassNameW(hWnd, szClass, ArrayLength(szClass)) &&
+ !wcscmp(szClass, kMozillaWindowClass)) {
+ sBrowserHwnd = hWnd;
+ }
+ }
+ // Oddity: flash does strange rect comparisons for mouse input destined for
+ // it's internal settings window. Post removing sub widgets for tabs, touch
+ // this up so they get the rect they expect.
+ // XXX potentially tie this to a specific major version?
+ BOOL result = sGetWindowInfoPtrStub(hWnd, pwi);
+ if (sBrowserHwnd && sBrowserHwnd == hWnd)
+ pwi->rcWindow = pwi->rcClient;
+ return result;
+}
+
+SHORT WINAPI PMCGetKeyState(int aVirtKey);
+
+// Runnable that performs GetKeyState on the main thread so that it can be
+// synchronously run on the PluginModuleParent via IPC.
+// The task alerts the given semaphore when it is finished.
+class GetKeyStateTask : public Runnable
+{
+ SHORT* mKeyState;
+ int mVirtKey;
+ HANDLE mSemaphore;
+
+public:
+ explicit GetKeyStateTask(int aVirtKey, HANDLE aSemaphore, SHORT* aKeyState) :
+ mVirtKey(aVirtKey), mSemaphore(aSemaphore), mKeyState(aKeyState)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+ *mKeyState = PMCGetKeyState(mVirtKey);
+ if (!ReleaseSemaphore(mSemaphore, 1, nullptr)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+ }
+};
+
+// static
+SHORT WINAPI
+PMCGetKeyState(int aVirtKey)
+{
+ if (!IsPluginThread()) {
+ // synchronously request the key state from the main thread
+
+ // Start a semaphore at 0. We Release the semaphore (bringing its count to 1)
+ // when the synchronous call is done.
+ HANDLE semaphore = CreateSemaphore(NULL, 0, 1, NULL);
+ if (semaphore == nullptr) {
+ MOZ_ASSERT(semaphore != nullptr);
+ return 0;
+ }
+
+ SHORT keyState;
+ RefPtr<GetKeyStateTask> task = new GetKeyStateTask(aVirtKey, semaphore, &keyState);
+ ProcessChild::message_loop()->PostTask(task.forget());
+ DWORD err = WaitForSingleObject(semaphore, INFINITE);
+ if (err != WAIT_FAILED) {
+ CloseHandle(semaphore);
+ return keyState;
+ }
+ PLUGIN_LOG_DEBUG(("Error while waiting for GetKeyState semaphore: %d",
+ GetLastError()));
+ MOZ_ASSERT(err != WAIT_FAILED);
+ CloseHandle(semaphore);
+ return 0;
+ }
+ PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+ if (chromeInstance) {
+ int16_t ret = 0;
+ if (chromeInstance->CallGetKeyState(aVirtKey, &ret)) {
+ return ret;
+ }
+ }
+ return sGetKeyStatePtrStub(aVirtKey);
+}
+#endif
+
+PPluginInstanceChild*
+PluginModuleChild::AllocPPluginInstanceChild(const nsCString& aMimeType,
+ const uint16_t& aMode,
+ const InfallibleTArray<nsCString>& aNames,
+ const InfallibleTArray<nsCString>& aValues)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+
+ // In e10s, gChromeInstance hands out quirks to instances, but never
+ // allocates an instance on its own. Make sure it gets the latest copy
+ // of quirks once we have them. Also note, with process-per-tab, we may
+ // have multiple PluginModuleChilds in the same plugin process, so only
+ // initialize this once in gChromeInstance, which is a singleton.
+ GetChrome()->InitQuirksModes(aMimeType);
+ mQuirks = GetChrome()->mQuirks;
+
+#ifdef XP_WIN
+ sUser32Intercept.Init("user32.dll");
+ if ((mQuirks & QUIRK_FLASH_HOOK_GETWINDOWINFO) &&
+ !sGetWindowInfoPtrStub) {
+ sUser32Intercept.AddHook("GetWindowInfo", reinterpret_cast<intptr_t>(PMCGetWindowInfoHook),
+ (void**) &sGetWindowInfoPtrStub);
+ }
+
+ if ((mQuirks & QUIRK_FLASH_HOOK_GETKEYSTATE) &&
+ !sGetKeyStatePtrStub) {
+ sUser32Intercept.AddHook("GetKeyState", reinterpret_cast<intptr_t>(PMCGetKeyState),
+ (void**) &sGetKeyStatePtrStub);
+ }
+#endif
+
+ return new PluginInstanceChild(&mFunctions, aMimeType, aMode, aNames,
+ aValues);
+}
+
+void
+PluginModuleChild::InitQuirksModes(const nsCString& aMimeType)
+{
+ if (mQuirks != QUIRKS_NOT_INITIALIZED) {
+ return;
+ }
+
+ mQuirks = GetQuirksFromMimeTypeAndFilename(aMimeType, mPluginFilename);
+}
+
+bool
+PluginModuleChild::AnswerModuleSupportsAsyncRender(bool* aResult)
+{
+#if defined(XP_WIN)
+ *aResult = gChromeInstance->mAsyncRenderSupport;
+ return true;
+#else
+ NS_NOTREACHED("Shouldn't get here!");
+ return false;
+#endif
+}
+
+bool
+PluginModuleChild::RecvPPluginInstanceConstructor(PPluginInstanceChild* aActor,
+ const nsCString& aMimeType,
+ const uint16_t& aMode,
+ InfallibleTArray<nsCString>&& aNames,
+ InfallibleTArray<nsCString>&& aValues)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+
+ NS_ASSERTION(aActor, "Null actor!");
+ return true;
+}
+
+bool
+PluginModuleChild::AnswerSyncNPP_New(PPluginInstanceChild* aActor, NPError* rv)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ PluginInstanceChild* childInstance =
+ reinterpret_cast<PluginInstanceChild*>(aActor);
+ AssertPluginThread();
+ *rv = childInstance->DoNPP_New();
+ return true;
+}
+
+class AsyncNewResultSender : public ChildAsyncCall
+{
+public:
+ AsyncNewResultSender(PluginInstanceChild* aInstance, NPError aResult)
+ : ChildAsyncCall(aInstance, nullptr, nullptr)
+ , mResult(aResult)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ RemoveFromAsyncList();
+ DebugOnly<bool> sendOk = mInstance->SendAsyncNPP_NewResult(mResult);
+ MOZ_ASSERT(sendOk);
+ return NS_OK;
+ }
+
+private:
+ NPError mResult;
+};
+
+static void
+RunAsyncNPP_New(void* aChildInstance)
+{
+ MOZ_ASSERT(aChildInstance);
+ PluginInstanceChild* childInstance =
+ static_cast<PluginInstanceChild*>(aChildInstance);
+ NPError rv = childInstance->DoNPP_New();
+ RefPtr<AsyncNewResultSender> task =
+ new AsyncNewResultSender(childInstance, rv);
+ childInstance->PostChildAsyncCall(task.forget());
+}
+
+bool
+PluginModuleChild::RecvAsyncNPP_New(PPluginInstanceChild* aActor)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ PluginInstanceChild* childInstance =
+ reinterpret_cast<PluginInstanceChild*>(aActor);
+ AssertPluginThread();
+ // We don't want to run NPP_New async from within nested calls
+ childInstance->AsyncCall(&RunAsyncNPP_New, childInstance);
+ return true;
+}
+
+bool
+PluginModuleChild::DeallocPPluginInstanceChild(PPluginInstanceChild* aActor)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+
+ delete aActor;
+
+ return true;
+}
+
+NPObject*
+PluginModuleChild::NPN_CreateObject(NPP aNPP, NPClass* aClass)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ ENSURE_PLUGIN_THREAD(nullptr);
+
+ PluginInstanceChild* i = InstCast(aNPP);
+ if (i->mDeletingHash) {
+ NS_ERROR("Plugin used NPP after NPP_Destroy");
+ return nullptr;
+ }
+
+ NPObject* newObject;
+ if (aClass && aClass->allocate) {
+ newObject = aClass->allocate(aNPP, aClass);
+ }
+ else {
+ newObject = reinterpret_cast<NPObject*>(child::_memalloc(sizeof(NPObject)));
+ }
+
+ if (newObject) {
+ newObject->_class = aClass;
+ newObject->referenceCount = 1;
+ NS_LOG_ADDREF(newObject, 1, "NPObject", sizeof(NPObject));
+ }
+
+ PluginScriptableObjectChild::RegisterObject(newObject, i);
+
+ return newObject;
+}
+
+NPObject*
+PluginModuleChild::NPN_RetainObject(NPObject* aNPObj)
+{
+ AssertPluginThread();
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ int32_t refCnt =
+#endif
+ PR_ATOMIC_INCREMENT((int32_t*)&aNPObj->referenceCount);
+ NS_LOG_ADDREF(aNPObj, refCnt, "NPObject", sizeof(NPObject));
+
+ return aNPObj;
+}
+
+void
+PluginModuleChild::NPN_ReleaseObject(NPObject* aNPObj)
+{
+ AssertPluginThread();
+
+ PluginInstanceChild* instance = PluginScriptableObjectChild::GetInstanceForNPObject(aNPObj);
+ if (!instance) {
+ NS_ERROR("Releasing object not in mObjectMap?");
+ return;
+ }
+
+ DeletingObjectEntry* doe = nullptr;
+ if (instance->mDeletingHash) {
+ doe = instance->mDeletingHash->GetEntry(aNPObj);
+ if (!doe) {
+ NS_ERROR("An object for a destroyed instance isn't in the instance deletion hash");
+ return;
+ }
+ if (doe->mDeleted)
+ return;
+ }
+
+ int32_t refCnt = PR_ATOMIC_DECREMENT((int32_t*)&aNPObj->referenceCount);
+ NS_LOG_RELEASE(aNPObj, refCnt, "NPObject");
+
+ if (refCnt == 0) {
+ DeallocNPObject(aNPObj);
+ if (doe)
+ doe->mDeleted = true;
+ }
+ return;
+}
+
+void
+PluginModuleChild::DeallocNPObject(NPObject* aNPObj)
+{
+ if (aNPObj->_class && aNPObj->_class->deallocate) {
+ aNPObj->_class->deallocate(aNPObj);
+ } else {
+ child::_memfree(aNPObj);
+ }
+
+ PluginScriptableObjectChild* actor = PluginScriptableObjectChild::GetActorForNPObject(aNPObj);
+ if (actor)
+ actor->NPObjectDestroyed();
+
+ PluginScriptableObjectChild::UnregisterObject(aNPObj);
+}
+
+NPIdentifier
+PluginModuleChild::NPN_GetStringIdentifier(const NPUTF8* aName)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+
+ if (!aName)
+ return 0;
+
+ nsDependentCString name(aName);
+ PluginIdentifier ident(name);
+ PluginScriptableObjectChild::StackIdentifier stackID(ident);
+ stackID.MakePermanent();
+ return stackID.ToNPIdentifier();
+}
+
+void
+PluginModuleChild::NPN_GetStringIdentifiers(const NPUTF8** aNames,
+ int32_t aNameCount,
+ NPIdentifier* aIdentifiers)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+
+ if (!(aNames && aNameCount > 0 && aIdentifiers)) {
+ NS_RUNTIMEABORT("Bad input! Headed for a crash!");
+ }
+
+ for (int32_t index = 0; index < aNameCount; ++index) {
+ if (!aNames[index]) {
+ aIdentifiers[index] = 0;
+ continue;
+ }
+ nsDependentCString name(aNames[index]);
+ PluginIdentifier ident(name);
+ PluginScriptableObjectChild::StackIdentifier stackID(ident);
+ stackID.MakePermanent();
+ aIdentifiers[index] = stackID.ToNPIdentifier();
+ }
+}
+
+bool
+PluginModuleChild::NPN_IdentifierIsString(NPIdentifier aIdentifier)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ PluginScriptableObjectChild::StackIdentifier stack(aIdentifier);
+ return stack.IsString();
+}
+
+NPIdentifier
+PluginModuleChild::NPN_GetIntIdentifier(int32_t aIntId)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+
+ PluginIdentifier ident(aIntId);
+ PluginScriptableObjectChild::StackIdentifier stackID(ident);
+ stackID.MakePermanent();
+ return stackID.ToNPIdentifier();
+}
+
+NPUTF8*
+PluginModuleChild::NPN_UTF8FromIdentifier(NPIdentifier aIdentifier)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ PluginScriptableObjectChild::StackIdentifier stackID(aIdentifier);
+ if (stackID.IsString()) {
+ return ToNewCString(stackID.GetString());
+ }
+ return nullptr;
+}
+
+int32_t
+PluginModuleChild::NPN_IntFromIdentifier(NPIdentifier aIdentifier)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ PluginScriptableObjectChild::StackIdentifier stackID(aIdentifier);
+ if (!stackID.IsString()) {
+ return stackID.GetInt();
+ }
+ return INT32_MIN;
+}
+
+#ifdef OS_WIN
+void
+PluginModuleChild::EnteredCall()
+{
+ mIncallPumpingStack.AppendElement();
+}
+
+void
+PluginModuleChild::ExitedCall()
+{
+ NS_ASSERTION(mIncallPumpingStack.Length(), "mismatched entered/exited");
+ uint32_t len = mIncallPumpingStack.Length();
+ const IncallFrame& f = mIncallPumpingStack[len - 1];
+ if (f._spinning)
+ MessageLoop::current()->SetNestableTasksAllowed(f._savedNestableTasksAllowed);
+
+ mIncallPumpingStack.TruncateLength(len - 1);
+}
+
+LRESULT CALLBACK
+PluginModuleChild::CallWindowProcHook(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ // Trap and reply to anything we recognize as the source of a
+ // potential send message deadlock.
+ if (nCode >= 0 &&
+ (InSendMessageEx(nullptr)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
+ CWPSTRUCT* pCwp = reinterpret_cast<CWPSTRUCT*>(lParam);
+ if (pCwp->message == WM_KILLFOCUS) {
+ // Fix for flash fullscreen window loosing focus. On single
+ // core systems, sync killfocus events need to be handled
+ // after the flash fullscreen window procedure processes this
+ // message, otherwise fullscreen focus will not work correctly.
+ wchar_t szClass[26];
+ if (GetClassNameW(pCwp->hwnd, szClass,
+ sizeof(szClass)/sizeof(char16_t)) &&
+ !wcscmp(szClass, kFlashFullscreenClass)) {
+ gDelayFlashFocusReplyUntilEval = true;
+ }
+ }
+ }
+
+ return CallNextHookEx(nullptr, nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK
+PluginModuleChild::NestedInputEventHook(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ PluginModuleChild* self = GetChrome();
+ uint32_t len = self->mIncallPumpingStack.Length();
+ if (nCode >= 0 && len && !self->mIncallPumpingStack[len - 1]._spinning) {
+ MessageLoop* loop = MessageLoop::current();
+ self->SendProcessNativeEventsInInterruptCall();
+ IncallFrame& f = self->mIncallPumpingStack[len - 1];
+ f._spinning = true;
+ f._savedNestableTasksAllowed = loop->NestableTasksAllowed();
+ loop->SetNestableTasksAllowed(true);
+ loop->set_os_modal_loop(true);
+ }
+
+ return CallNextHookEx(nullptr, nCode, wParam, lParam);
+}
+
+void
+PluginModuleChild::SetEventHooks()
+{
+ NS_ASSERTION(!mNestedEventHook,
+ "mNestedEventHook already setup in call to SetNestedInputEventHook?");
+ NS_ASSERTION(!mGlobalCallWndProcHook,
+ "mGlobalCallWndProcHook already setup in call to CallWindowProcHook?");
+
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+
+ // WH_MSGFILTER event hook for detecting modal loops in the child.
+ mNestedEventHook = SetWindowsHookEx(WH_MSGFILTER,
+ NestedInputEventHook,
+ nullptr,
+ GetCurrentThreadId());
+
+ // WH_CALLWNDPROC event hook for trapping sync messages sent from
+ // parent that can cause deadlocks.
+ mGlobalCallWndProcHook = SetWindowsHookEx(WH_CALLWNDPROC,
+ CallWindowProcHook,
+ nullptr,
+ GetCurrentThreadId());
+}
+
+void
+PluginModuleChild::ResetEventHooks()
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+ if (mNestedEventHook)
+ UnhookWindowsHookEx(mNestedEventHook);
+ mNestedEventHook = nullptr;
+ if (mGlobalCallWndProcHook)
+ UnhookWindowsHookEx(mGlobalCallWndProcHook);
+ mGlobalCallWndProcHook = nullptr;
+}
+#endif
+
+bool
+PluginModuleChild::RecvProcessNativeEventsInInterruptCall()
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(OS_WIN)
+ ProcessNativeEventsInInterruptCall();
+ return true;
+#else
+ NS_RUNTIMEABORT(
+ "PluginModuleChild::RecvProcessNativeEventsInInterruptCall not implemented!");
+ return false;
+#endif
+}
+
+#ifdef MOZ_WIDGET_COCOA
+void
+PluginModuleChild::ProcessNativeEvents() {
+ CallProcessSomeEvents();
+}
+#endif
+
+bool
+PluginModuleChild::RecvStartProfiler(const ProfilerInitParams& params)
+{
+ nsTArray<const char*> featureArray;
+ for (size_t i = 0; i < params.features().Length(); ++i) {
+ featureArray.AppendElement(params.features()[i].get());
+ }
+
+ nsTArray<const char*> threadNameFilterArray;
+ for (size_t i = 0; i < params.threadFilters().Length(); ++i) {
+ threadNameFilterArray.AppendElement(params.threadFilters()[i].get());
+ }
+
+ profiler_start(params.entries(), params.interval(),
+ featureArray.Elements(), featureArray.Length(),
+ threadNameFilterArray.Elements(), threadNameFilterArray.Length());
+
+ return true;
+}
+
+bool
+PluginModuleChild::RecvStopProfiler()
+{
+ profiler_stop();
+ return true;
+}
+
+bool
+PluginModuleChild::RecvGatherProfile()
+{
+ nsCString profileCString;
+ UniquePtr<char[]> profile = profiler_get_profile();
+ if (profile != nullptr) {
+ profileCString = nsCString(profile.get(), strlen(profile.get()));
+ } else {
+ profileCString = nsCString("", 0);
+ }
+
+ Unused << SendProfile(profileCString);
+ return true;
+}
+
+NPError
+PluginModuleChild::PluginRequiresAudioDeviceChanges(
+ PluginInstanceChild* aInstance,
+ NPBool aShouldRegister)
+{
+#ifdef XP_WIN
+ // Maintain a set of PluginInstanceChildren that we need to tell when the
+ // default audio device has changed.
+ NPError rv = NPERR_NO_ERROR;
+ if (aShouldRegister) {
+ if (mAudioNotificationSet.IsEmpty()) {
+ // We are registering the first plugin. Notify the PluginModuleParent
+ // that it needs to start sending us audio device notifications.
+ if (!CallNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
+ aShouldRegister, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+ if (rv == NPERR_NO_ERROR) {
+ mAudioNotificationSet.PutEntry(aInstance);
+ }
+ }
+ else if (!mAudioNotificationSet.IsEmpty()) {
+ mAudioNotificationSet.RemoveEntry(aInstance);
+ if (mAudioNotificationSet.IsEmpty()) {
+ // We released the last plugin. Unregister from the PluginModuleParent.
+ if (!CallNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
+ aShouldRegister, &rv)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+ }
+ return rv;
+#else
+ NS_RUNTIMEABORT("PluginRequiresAudioDeviceChanges is not available on this platform.");
+ return NPERR_GENERIC_ERROR;
+#endif // XP_WIN
+}
+
+bool
+PluginModuleChild::RecvNPP_SetValue_NPNVaudioDeviceChangeDetails(
+ const NPAudioDeviceChangeDetailsIPC& detailsIPC)
+{
+#if defined(XP_WIN)
+ NPAudioDeviceChangeDetails details;
+ details.flow = detailsIPC.flow;
+ details.role = detailsIPC.role;
+ details.defaultDevice = detailsIPC.defaultDevice.c_str();
+ for (auto iter = mAudioNotificationSet.ConstIter(); !iter.Done(); iter.Next()) {
+ PluginInstanceChild* pluginInst = iter.Get()->GetKey();
+ pluginInst->DefaultAudioDeviceChanged(details);
+ }
+ return true;
+#else
+ NS_RUNTIMEABORT("NPP_SetValue_NPNVaudioDeviceChangeDetails is a Windows-only message");
+ return false;
+#endif
+}
diff --git a/dom/plugins/ipc/PluginModuleChild.h b/dom/plugins/ipc/PluginModuleChild.h
new file mode 100644
index 000000000..233a95369
--- /dev/null
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -0,0 +1,387 @@
+/* -*- 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 dom_plugins_PluginModuleChild_h
+#define dom_plugins_PluginModuleChild_h 1
+
+#include "mozilla/Attributes.h"
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+#include "prlink.h"
+
+#include "npapi.h"
+#include "npfunctions.h"
+
+#include "nsDataHashtable.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+
+#ifdef MOZ_WIDGET_COCOA
+#include "PluginInterposeOSX.h"
+#endif
+
+#include "mozilla/plugins/PPluginModuleChild.h"
+#include "mozilla/plugins/PluginInstanceChild.h"
+#include "mozilla/plugins/PluginMessageUtils.h"
+#include "mozilla/plugins/PluginQuirks.h"
+
+// NOTE: stolen from nsNPAPIPlugin.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) (void);
+
+namespace mozilla {
+namespace dom {
+class PCrashReporterChild;
+} // namespace dom
+
+namespace plugins {
+
+class PluginInstanceChild;
+
+class PluginModuleChild : public PPluginModuleChild
+{
+ typedef mozilla::dom::PCrashReporterChild PCrashReporterChild;
+protected:
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override
+ {
+ return MediateRace(parent, child);
+ }
+
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+ virtual bool RecvSettingChanged(const PluginSettings& aSettings) override;
+
+ // Implement the PPluginModuleChild interface
+ virtual bool RecvDisableFlashProtectedMode() override;
+ virtual bool AnswerNP_GetEntryPoints(NPError* rv) override;
+ virtual bool AnswerNP_Initialize(const PluginSettings& aSettings, NPError* rv) override;
+ virtual bool RecvAsyncNP_Initialize(const PluginSettings& aSettings) override;
+ virtual bool AnswerSyncNPP_New(PPluginInstanceChild* aActor, NPError* rv)
+ override;
+ virtual bool RecvAsyncNPP_New(PPluginInstanceChild* aActor) override;
+
+ virtual PPluginModuleChild*
+ AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess) override;
+
+ virtual PPluginInstanceChild*
+ AllocPPluginInstanceChild(const nsCString& aMimeType,
+ const uint16_t& aMode,
+ const InfallibleTArray<nsCString>& aNames,
+ const InfallibleTArray<nsCString>& aValues)
+ override;
+
+ virtual bool
+ DeallocPPluginInstanceChild(PPluginInstanceChild* aActor) override;
+
+ virtual bool
+ RecvPPluginInstanceConstructor(PPluginInstanceChild* aActor,
+ const nsCString& aMimeType,
+ const uint16_t& aMode,
+ InfallibleTArray<nsCString>&& aNames,
+ InfallibleTArray<nsCString>&& aValues)
+ override;
+ virtual bool
+ AnswerNP_Shutdown(NPError *rv) override;
+
+ virtual bool
+ AnswerOptionalFunctionsSupported(bool *aURLRedirectNotify,
+ bool *aClearSiteData,
+ bool *aGetSitesWithData) override;
+
+ virtual bool
+ RecvNPP_ClearSiteData(const nsCString& aSite,
+ const uint64_t& aFlags,
+ const uint64_t& aMaxAge,
+ const uint64_t& aCallbackId) override;
+
+ virtual bool
+ RecvNPP_GetSitesWithData(const uint64_t& aCallbackId) override;
+
+ virtual bool
+ RecvSetAudioSessionData(const nsID& aId,
+ const nsString& aDisplayName,
+ const nsString& aIconPath) override;
+
+ virtual bool
+ RecvSetParentHangTimeout(const uint32_t& aSeconds) override;
+
+ virtual PCrashReporterChild*
+ AllocPCrashReporterChild(mozilla::dom::NativeThreadId* id,
+ uint32_t* processType) override;
+ virtual bool
+ DeallocPCrashReporterChild(PCrashReporterChild* actor) override;
+ virtual bool
+ AnswerPCrashReporterConstructor(PCrashReporterChild* actor,
+ mozilla::dom::NativeThreadId* id,
+ uint32_t* processType) override;
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+ virtual bool
+ RecvProcessNativeEventsInInterruptCall() override;
+
+ virtual bool RecvStartProfiler(const ProfilerInitParams& params) override;
+ virtual bool RecvStopProfiler() override;
+ virtual bool RecvGatherProfile() override;
+
+ virtual bool
+ AnswerModuleSupportsAsyncRender(bool* aResult) override;
+public:
+ explicit PluginModuleChild(bool aIsChrome);
+ virtual ~PluginModuleChild();
+
+ bool CommonInit(base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel);
+
+ // aPluginFilename is UTF8, not native-charset!
+ bool InitForChrome(const std::string& aPluginFilename,
+ base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel);
+
+ bool InitForContent(base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel);
+
+ static PluginModuleChild*
+ CreateForContentProcess(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess);
+
+ void CleanUp();
+
+ NPError NP_Shutdown();
+
+ const char* GetUserAgent();
+
+ static const NPNetscapeFuncs sBrowserFuncs;
+
+ static PluginModuleChild* GetChrome();
+
+ /**
+ * The child implementation of NPN_CreateObject.
+ */
+ static NPObject* NPN_CreateObject(NPP aNPP, NPClass* aClass);
+ /**
+ * The child implementation of NPN_RetainObject.
+ */
+ static NPObject* NPN_RetainObject(NPObject* aNPObj);
+ /**
+ * The child implementation of NPN_ReleaseObject.
+ */
+ static void NPN_ReleaseObject(NPObject* aNPObj);
+
+ /**
+ * The child implementations of NPIdentifier-related functions.
+ */
+ static NPIdentifier NPN_GetStringIdentifier(const NPUTF8* aName);
+ static void NPN_GetStringIdentifiers(const NPUTF8** aNames,
+ int32_t aNameCount,
+ NPIdentifier* aIdentifiers);
+ static NPIdentifier NPN_GetIntIdentifier(int32_t aIntId);
+ static bool NPN_IdentifierIsString(NPIdentifier aIdentifier);
+ static NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier aIdentifier);
+ static int32_t NPN_IntFromIdentifier(NPIdentifier aIdentifier);
+
+#ifdef MOZ_WIDGET_COCOA
+ void ProcessNativeEvents();
+
+ void PluginShowWindow(uint32_t window_id, bool modal, CGRect r) {
+ SendPluginShowWindow(window_id, modal, r.origin.x, r.origin.y, r.size.width, r.size.height);
+ }
+
+ void PluginHideWindow(uint32_t window_id) {
+ SendPluginHideWindow(window_id);
+ }
+
+ void SetCursor(NSCursorInfo& cursorInfo) {
+ SendSetCursor(cursorInfo);
+ }
+
+ void ShowCursor(bool show) {
+ SendShowCursor(show);
+ }
+
+ void PushCursor(NSCursorInfo& cursorInfo) {
+ SendPushCursor(cursorInfo);
+ }
+
+ void PopCursor() {
+ SendPopCursor();
+ }
+
+ bool GetNativeCursorsSupported() {
+ return Settings().nativeCursorsSupported();
+ }
+#endif
+
+ int GetQuirks() { return mQuirks; }
+
+ const PluginSettings& Settings() const { return mCachedSettings; }
+
+ NPError PluginRequiresAudioDeviceChanges(PluginInstanceChild* aInstance,
+ NPBool aShouldRegister);
+ bool RecvNPP_SetValue_NPNVaudioDeviceChangeDetails(
+ const NPAudioDeviceChangeDetailsIPC& detailsIPC) override;
+
+private:
+ NPError DoNP_Initialize(const PluginSettings& aSettings);
+ void AddQuirk(PluginQuirks quirk) {
+ if (mQuirks == QUIRKS_NOT_INITIALIZED)
+ mQuirks = 0;
+ mQuirks |= quirk;
+ }
+ void InitQuirksModes(const nsCString& aMimeType);
+ bool InitGraphics();
+ void DeinitGraphics();
+
+#if defined(OS_WIN)
+ void HookProtectedMode();
+#endif
+
+#if defined(MOZ_WIDGET_GTK)
+ static gboolean DetectNestedEventLoop(gpointer data);
+ static gboolean ProcessBrowserEvents(gpointer data);
+
+ virtual void EnteredCxxStack() override;
+ virtual void ExitedCxxStack() override;
+#endif
+
+ PRLibrary* mLibrary;
+ nsCString mPluginFilename; // UTF8
+ int mQuirks;
+
+ bool mIsChrome;
+ bool mHasShutdown; // true if NP_Shutdown has run
+ Transport* mTransport;
+
+ // we get this from the plugin
+ NP_PLUGINSHUTDOWN mShutdownFunc;
+#if defined(OS_LINUX) || defined(OS_BSD)
+ NP_PLUGINUNIXINIT mInitializeFunc;
+#elif defined(OS_WIN) || defined(OS_MACOSX)
+ NP_PLUGININIT mInitializeFunc;
+ NP_GETENTRYPOINTS mGetEntryPointsFunc;
+#endif
+
+ NPPluginFuncs mFunctions;
+
+ PluginSettings mCachedSettings;
+
+#if defined(MOZ_WIDGET_GTK)
+ // If a plugin spins a nested glib event loop in response to a
+ // synchronous IPC message from the browser, the loop might break
+ // only after the browser responds to a request sent by the
+ // plugin. This can happen if a plugin uses gtk's synchronous
+ // copy/paste, for example. But because the browser is blocked on
+ // a condvar, it can't respond to the request. This situation
+ // isn't technically a deadlock, but the symptoms are basically
+ // the same from the user's perspective.
+ //
+ // We take two steps to prevent this
+ //
+ // (1) Detect nested event loops spun by the plugin. This is
+ // done by scheduling a glib timer event in the plugin
+ // process whenever the browser might block on the plugin.
+ // If the plugin indeed spins a nested loop, this timer event
+ // will fire "soon" thereafter.
+ //
+ // (2) When a nested loop is detected, deschedule the
+ // nested-loop-detection timer and in its place, schedule
+ // another timer that periodically calls back into the
+ // browser and spins a mini event loop. This mini event loop
+ // processes a handful of pending native events.
+ //
+ // Because only timer (1) or (2) (or neither) may be active at any
+ // point in time, we use the same member variable
+ // |mNestedLoopTimerId| to refer to both.
+ //
+ // When the browser no longer might be blocked on a plugin's IPC
+ // response, we deschedule whichever of (1) or (2) is active.
+ guint mNestedLoopTimerId;
+# ifdef DEBUG
+ // Depth of the stack of calls to g_main_context_dispatch before any
+ // nested loops are run. This is 1 when IPC calls are dispatched from
+ // g_main_context_iteration, or 0 when dispatched directly from
+ // MessagePumpForUI.
+ int mTopLoopDepth;
+# endif
+#endif
+
+#if defined(XP_WIN)
+ typedef nsTHashtable<nsPtrHashKey<PluginInstanceChild>> PluginInstanceSet;
+ // Set of plugins that have registered to be notified when the audio device
+ // changes.
+ PluginInstanceSet mAudioNotificationSet;
+#endif
+
+public: // called by PluginInstanceChild
+ /**
+ * Dealloc an NPObject after last-release or when the associated instance
+ * is destroyed. This function will remove the object from mObjectMap.
+ */
+ static void DeallocNPObject(NPObject* o);
+
+ NPError NPP_Destroy(PluginInstanceChild* instance) {
+ return mFunctions.destroy(instance->GetNPP(), 0);
+ }
+
+private:
+#if defined(OS_WIN)
+ virtual void EnteredCall() override;
+ virtual void ExitedCall() override;
+
+ // Entered/ExitedCall notifications keep track of whether the plugin has
+ // entered a nested event loop within this interrupt call.
+ struct IncallFrame
+ {
+ IncallFrame()
+ : _spinning(false)
+ , _savedNestableTasksAllowed(false)
+ { }
+
+ bool _spinning;
+ bool _savedNestableTasksAllowed;
+ };
+
+ AutoTArray<IncallFrame, 8> mIncallPumpingStack;
+
+ static LRESULT CALLBACK NestedInputEventHook(int code,
+ WPARAM wParam,
+ LPARAM lParam);
+ static LRESULT CALLBACK CallWindowProcHook(int code,
+ WPARAM wParam,
+ LPARAM lParam);
+ void SetEventHooks();
+ void ResetEventHooks();
+ HHOOK mNestedEventHook;
+ HHOOK mGlobalCallWndProcHook;
+public:
+ bool mAsyncRenderSupport;
+#endif
+};
+
+} /* namespace plugins */
+} /* namespace mozilla */
+
+#endif // ifndef dom_plugins_PluginModuleChild_h
diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp
new file mode 100755
index 000000000..b85a3e94b
--- /dev/null
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -0,0 +1,3384 @@
+/* -*- 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/. */
+
+#include "mozilla/plugins/PluginModuleParent.h"
+
+#include "base/process_util.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PCrashReporterParent.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/plugins/BrowserStreamParent.h"
+#include "mozilla/plugins/PluginAsyncSurrogate.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/plugins/PluginInstanceParent.h"
+#include "mozilla/Preferences.h"
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "mozilla/ProfileGatherer.h"
+#endif
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/Services.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "nsAutoPtr.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsIObserverService.h"
+#include "nsIXULRuntime.h"
+#include "nsNPAPIPlugin.h"
+#include "nsPrintfCString.h"
+#include "prsystem.h"
+#include "PluginQuirks.h"
+#include "GeckoProfiler.h"
+#include "nsPluginTags.h"
+#include "nsUnicharUtils.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+
+#ifdef XP_WIN
+#include "mozilla/plugins/PluginSurfaceParent.h"
+#include "mozilla/widget/AudioSession.h"
+#include "PluginHangUIParent.h"
+#include "PluginUtilsWin.h"
+#endif
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "nsIProfiler.h"
+#include "nsIProfileSaveEvent.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+#include <glib.h>
+#elif XP_MACOSX
+#include "PluginInterposeOSX.h"
+#include "PluginUtilsOSX.h"
+#endif
+
+using base::KillProcess;
+
+using mozilla::PluginLibrary;
+#ifdef MOZ_ENABLE_PROFILER_SPS
+using mozilla::ProfileGatherer;
+#endif
+using mozilla::ipc::MessageChannel;
+using mozilla::ipc::GeckoChildProcessHost;
+using mozilla::dom::PCrashReporterParent;
+using mozilla::dom::CrashReporterParent;
+
+using namespace mozilla;
+using namespace mozilla::plugins;
+using namespace mozilla::plugins::parent;
+
+#ifdef MOZ_CRASHREPORTER
+#include "mozilla/dom/CrashReporterParent.h"
+
+using namespace CrashReporter;
+#endif
+
+static const char kContentTimeoutPref[] = "dom.ipc.plugins.contentTimeoutSecs";
+static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs";
+static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs";
+static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs";
+static const char kAsyncInitPref[] = "dom.ipc.plugins.asyncInit.enabled";
+#ifdef XP_WIN
+static const char kHangUITimeoutPref[] = "dom.ipc.plugins.hangUITimeoutSecs";
+static const char kHangUIMinDisplayPref[] = "dom.ipc.plugins.hangUIMinDisplaySecs";
+#define CHILD_TIMEOUT_PREF kHangUITimeoutPref
+#else
+#define CHILD_TIMEOUT_PREF kChildTimeoutPref
+#endif
+
+bool
+mozilla::plugins::SetupBridge(uint32_t aPluginId,
+ dom::ContentParent* aContentParent,
+ bool aForceBridgeNow,
+ nsresult* rv,
+ uint32_t* runID)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+ if (NS_WARN_IF(!rv) || NS_WARN_IF(!runID)) {
+ return false;
+ }
+
+ PluginModuleChromeParent::ClearInstantiationFlag();
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ RefPtr<nsNPAPIPlugin> plugin;
+ *rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin));
+ if (NS_FAILED(*rv)) {
+ return true;
+ }
+ PluginModuleChromeParent* chromeParent = static_cast<PluginModuleChromeParent*>(plugin->GetLibrary());
+ /*
+ * We can't accumulate BLOCKED_ON_PLUGIN_MODULE_INIT_MS until here because
+ * its histogram key is not available until *after* NP_Initialize.
+ */
+ chromeParent->AccumulateModuleInitBlockedTime();
+ *rv = chromeParent->GetRunID(runID);
+ if (NS_FAILED(*rv)) {
+ return true;
+ }
+ if (chromeParent->IsStartingAsync()) {
+ chromeParent->SetContentParent(aContentParent);
+ }
+ if (!aForceBridgeNow && chromeParent->IsStartingAsync() &&
+ PluginModuleChromeParent::DidInstantiate()) {
+ // We'll handle the bridging asynchronously
+ return true;
+ }
+ *rv = PPluginModule::Bridge(aContentParent, chromeParent);
+ return true;
+}
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+
+/**
+ * Use for executing CreateToolhelp32Snapshot off main thread
+ */
+class mozilla::plugins::FinishInjectorInitTask : public mozilla::CancelableRunnable
+{
+public:
+ FinishInjectorInitTask()
+ : mMutex("FlashInjectorInitTask::mMutex")
+ , mParent(nullptr)
+ , mMainThreadMsgLoop(MessageLoop::current())
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ void Init(PluginModuleChromeParent* aParent)
+ {
+ MOZ_ASSERT(aParent);
+ mParent = aParent;
+ }
+
+ void PostToMainThread()
+ {
+ RefPtr<Runnable> self = this;
+ mSnapshot.own(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+ { // Scope for lock
+ mozilla::MutexAutoLock lock(mMutex);
+ if (mMainThreadMsgLoop) {
+ mMainThreadMsgLoop->PostTask(self.forget());
+ }
+ }
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mParent->DoInjection(mSnapshot);
+ // We don't need to hold this lock during DoInjection, but we do need
+ // to obtain it before returning from Run() to ensure that
+ // PostToMainThread has completed before we return.
+ mozilla::MutexAutoLock lock(mMutex);
+ return NS_OK;
+ }
+
+ nsresult Cancel() override
+ {
+ mozilla::MutexAutoLock lock(mMutex);
+ mMainThreadMsgLoop = nullptr;
+ return NS_OK;
+ }
+
+private:
+ mozilla::Mutex mMutex;
+ nsAutoHandle mSnapshot;
+ PluginModuleChromeParent* mParent;
+ MessageLoop* mMainThreadMsgLoop;
+};
+
+#endif // MOZ_CRASHREPORTER_INJECTOR
+
+namespace {
+
+/**
+ * Objects of this class remain linked until either an error occurs in the
+ * plugin initialization sequence, or until
+ * PluginModuleContentParent::OnLoadPluginResult has completed executing.
+ */
+class PluginModuleMapping : public PRCList
+{
+public:
+ explicit PluginModuleMapping(uint32_t aPluginId, bool aAllowAsyncInit)
+ : mPluginId(aPluginId)
+ , mAllowAsyncInit(aAllowAsyncInit)
+ , mProcessIdValid(false)
+ , mModule(nullptr)
+ , mChannelOpened(false)
+ {
+ MOZ_COUNT_CTOR(PluginModuleMapping);
+ PR_INIT_CLIST(this);
+ PR_APPEND_LINK(this, &sModuleListHead);
+ }
+
+ ~PluginModuleMapping()
+ {
+ PR_REMOVE_LINK(this);
+ MOZ_COUNT_DTOR(PluginModuleMapping);
+ }
+
+ bool
+ IsChannelOpened() const
+ {
+ return mChannelOpened;
+ }
+
+ void
+ SetChannelOpened()
+ {
+ mChannelOpened = true;
+ }
+
+ PluginModuleContentParent*
+ GetModule()
+ {
+ if (!mModule) {
+ mModule = new PluginModuleContentParent(mAllowAsyncInit);
+ }
+ return mModule;
+ }
+
+ static PluginModuleMapping*
+ AssociateWithProcessId(uint32_t aPluginId, base::ProcessId aProcessId)
+ {
+ PluginModuleMapping* mapping =
+ static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
+ while (mapping != &sModuleListHead) {
+ if (mapping->mPluginId == aPluginId) {
+ mapping->AssociateWithProcessId(aProcessId);
+ return mapping;
+ }
+ mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
+ }
+ return nullptr;
+ }
+
+ static PluginModuleMapping*
+ Resolve(base::ProcessId aProcessId)
+ {
+ PluginModuleMapping* mapping = nullptr;
+
+ if (sIsLoadModuleOnStack) {
+ // Special case: If loading synchronously, we just need to access
+ // the tail entry of the list.
+ mapping =
+ static_cast<PluginModuleMapping*>(PR_LIST_TAIL(&sModuleListHead));
+ MOZ_ASSERT(mapping);
+ return mapping;
+ }
+
+ mapping =
+ static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
+ while (mapping != &sModuleListHead) {
+ if (mapping->mProcessIdValid && mapping->mProcessId == aProcessId) {
+ return mapping;
+ }
+ mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
+ }
+ return nullptr;
+ }
+
+ static PluginModuleMapping*
+ FindModuleByPluginId(uint32_t aPluginId)
+ {
+ PluginModuleMapping* mapping =
+ static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
+ while (mapping != &sModuleListHead) {
+ if (mapping->mPluginId == aPluginId) {
+ return mapping;
+ }
+ mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
+ }
+ return nullptr;
+ }
+
+ static bool
+ IsLoadModuleOnStack()
+ {
+ return sIsLoadModuleOnStack;
+ }
+
+ class MOZ_RAII NotifyLoadingModule
+ {
+ public:
+ explicit NotifyLoadingModule(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ PluginModuleMapping::sIsLoadModuleOnStack = true;
+ }
+
+ ~NotifyLoadingModule()
+ {
+ PluginModuleMapping::sIsLoadModuleOnStack = false;
+ }
+
+ private:
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+ };
+
+private:
+ void
+ AssociateWithProcessId(base::ProcessId aProcessId)
+ {
+ MOZ_ASSERT(!mProcessIdValid);
+ mProcessId = aProcessId;
+ mProcessIdValid = true;
+ }
+
+ uint32_t mPluginId;
+ bool mAllowAsyncInit;
+ bool mProcessIdValid;
+ base::ProcessId mProcessId;
+ PluginModuleContentParent* mModule;
+ bool mChannelOpened;
+
+ friend class NotifyLoadingModule;
+
+ static PRCList sModuleListHead;
+ static bool sIsLoadModuleOnStack;
+};
+
+PRCList PluginModuleMapping::sModuleListHead =
+ PR_INIT_STATIC_CLIST(&PluginModuleMapping::sModuleListHead);
+
+bool PluginModuleMapping::sIsLoadModuleOnStack = false;
+
+} // namespace
+
+static PluginModuleChromeParent*
+PluginModuleChromeParentForId(const uint32_t aPluginId)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ nsPluginTag* pluginTag = host->PluginWithId(aPluginId);
+ if (!pluginTag || !pluginTag->mPlugin) {
+ return nullptr;
+ }
+ RefPtr<nsNPAPIPlugin> plugin = pluginTag->mPlugin;
+
+ return static_cast<PluginModuleChromeParent*>(plugin->GetLibrary());
+}
+
+void
+mozilla::plugins::TakeFullMinidump(uint32_t aPluginId,
+ base::ProcessId aContentProcessId,
+ const nsAString& aBrowserDumpId,
+ nsString& aDumpId)
+{
+ PluginModuleChromeParent* chromeParent =
+ PluginModuleChromeParentForId(aPluginId);
+
+ if (chromeParent) {
+ chromeParent->TakeFullMinidump(aContentProcessId, aBrowserDumpId, aDumpId);
+ }
+}
+
+void
+mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
+ base::ProcessId aContentProcessId,
+ const nsCString& aMonitorDescription,
+ const nsAString& aDumpId)
+{
+ PluginModuleChromeParent* chromeParent =
+ PluginModuleChromeParentForId(aPluginId);
+
+ if (chromeParent) {
+ chromeParent->TerminateChildProcess(MessageLoop::current(),
+ aContentProcessId,
+ aMonitorDescription,
+ aDumpId);
+ }
+}
+
+/* static */ PluginLibrary*
+PluginModuleContentParent::LoadModule(uint32_t aPluginId,
+ nsPluginTag* aPluginTag)
+{
+ PluginModuleMapping::NotifyLoadingModule loadingModule;
+ nsAutoPtr<PluginModuleMapping> mapping(
+ new PluginModuleMapping(aPluginId, aPluginTag->mSupportsAsyncInit));
+
+ MOZ_ASSERT(XRE_IsContentProcess());
+
+ /*
+ * We send a LoadPlugin message to the chrome process using an intr
+ * message. Before it sends its response, it sends a message to create
+ * PluginModuleParent instance. That message is handled by
+ * PluginModuleContentParent::Initialize, which saves the instance in
+ * its module mapping. We fetch it from there after LoadPlugin finishes.
+ */
+ dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+ nsresult rv;
+ uint32_t runID;
+ TimeStamp sendLoadPluginStart = TimeStamp::Now();
+ if (!cp->SendLoadPlugin(aPluginId, &rv, &runID) ||
+ NS_FAILED(rv)) {
+ return nullptr;
+ }
+ TimeStamp sendLoadPluginEnd = TimeStamp::Now();
+
+ PluginModuleContentParent* parent = mapping->GetModule();
+ MOZ_ASSERT(parent);
+ parent->mTimeBlocked += (sendLoadPluginEnd - sendLoadPluginStart);
+
+ if (!mapping->IsChannelOpened()) {
+ // mapping is linked into PluginModuleMapping::sModuleListHead and is
+ // needed later, so since this function is returning successfully we
+ // forget it here.
+ mapping.forget();
+ }
+
+ parent->mPluginId = aPluginId;
+ parent->mRunID = runID;
+
+ return parent;
+}
+
+/* static */ void
+PluginModuleContentParent::AssociatePluginId(uint32_t aPluginId,
+ base::ProcessId aOtherPid)
+{
+ DebugOnly<PluginModuleMapping*> mapping =
+ PluginModuleMapping::AssociateWithProcessId(aPluginId, aOtherPid);
+ MOZ_ASSERT(mapping);
+}
+
+/* static */ PluginModuleContentParent*
+PluginModuleContentParent::Initialize(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherPid)
+{
+ nsAutoPtr<PluginModuleMapping> moduleMapping(
+ PluginModuleMapping::Resolve(aOtherPid));
+ MOZ_ASSERT(moduleMapping);
+ PluginModuleContentParent* parent = moduleMapping->GetModule();
+ MOZ_ASSERT(parent);
+
+ DebugOnly<bool> ok = parent->Open(aTransport, aOtherPid,
+ XRE_GetIOMessageLoop(),
+ mozilla::ipc::ParentSide);
+ MOZ_ASSERT(ok);
+
+ moduleMapping->SetChannelOpened();
+
+ // Request Windows message deferral behavior on our channel. This
+ // applies to the top level and all sub plugin protocols since they
+ // all share the same channel.
+ parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
+ TimeoutChanged(kContentTimeoutPref, parent);
+
+ // moduleMapping is linked into PluginModuleMapping::sModuleListHead and is
+ // needed later, so since this function is returning successfully we
+ // forget it here.
+ moduleMapping.forget();
+ return parent;
+}
+
+/* static */ void
+PluginModuleContentParent::OnLoadPluginResult(const uint32_t& aPluginId,
+ const bool& aResult)
+{
+ nsAutoPtr<PluginModuleMapping> moduleMapping(
+ PluginModuleMapping::FindModuleByPluginId(aPluginId));
+ MOZ_ASSERT(moduleMapping);
+ PluginModuleContentParent* parent = moduleMapping->GetModule();
+ MOZ_ASSERT(parent);
+ parent->RecvNP_InitializeResult(aResult ? NPERR_NO_ERROR
+ : NPERR_GENERIC_ERROR);
+}
+
+void
+PluginModuleChromeParent::SetContentParent(dom::ContentParent* aContentParent)
+{
+ // mContentParent is to be used ONLY during async plugin init!
+ MOZ_ASSERT(aContentParent && mIsStartingAsync);
+ mContentParent = aContentParent;
+}
+
+bool
+PluginModuleChromeParent::SendAssociatePluginId()
+{
+ MOZ_ASSERT(mContentParent);
+ return mContentParent->SendAssociatePluginId(mPluginId, OtherPid());
+}
+
+// static
+PluginLibrary*
+PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId,
+ nsPluginTag* aPluginTag)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ nsAutoPtr<PluginModuleChromeParent> parent(
+ new PluginModuleChromeParent(aFilePath, aPluginId,
+ aPluginTag->mSandboxLevel,
+ aPluginTag->mSupportsAsyncInit));
+ UniquePtr<LaunchCompleteTask> onLaunchedRunnable(new LaunchedTask(parent));
+ parent->mSubprocess->SetCallRunnableImmediately(!parent->mIsStartingAsync);
+ TimeStamp launchStart = TimeStamp::Now();
+ bool launched = parent->mSubprocess->Launch(Move(onLaunchedRunnable),
+ aPluginTag->mSandboxLevel);
+ if (!launched) {
+ // We never reached open
+ parent->mShutdown = true;
+ return nullptr;
+ }
+ parent->mIsFlashPlugin = aPluginTag->mIsFlashPlugin;
+ uint32_t blocklistState;
+ nsresult rv = aPluginTag->GetBlocklistState(&blocklistState);
+ parent->mIsBlocklisted = NS_FAILED(rv) || blocklistState != 0;
+ if (!parent->mIsStartingAsync) {
+ int32_t launchTimeoutSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
+ if (!parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000)) {
+ parent->mShutdown = true;
+ return nullptr;
+ }
+ }
+ TimeStamp launchEnd = TimeStamp::Now();
+ parent->mTimeBlocked = (launchEnd - launchStart);
+ return parent.forget();
+}
+
+void
+PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded)
+{
+ if (!aSucceeded) {
+ mShutdown = true;
+ OnInitFailure();
+ return;
+ }
+ // We may have already been initialized by another call that was waiting
+ // for process connect. If so, this function doesn't need to run.
+ if (mAsyncInitRv != NS_ERROR_NOT_INITIALIZED || mShutdown) {
+ return;
+ }
+
+ Open(mSubprocess->GetChannel(),
+ base::GetProcId(mSubprocess->GetChildProcessHandle()));
+
+ // Request Windows message deferral behavior on our channel. This
+ // applies to the top level and all sub plugin protocols since they
+ // all share the same channel.
+ GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
+ TimeoutChanged(CHILD_TIMEOUT_PREF, this);
+
+ Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
+ Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
+#ifdef XP_WIN
+ Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
+ Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
+#endif
+
+ RegisterSettingsCallbacks();
+
+#ifdef MOZ_CRASHREPORTER
+ // If this fails, we're having IPC troubles, and we're doomed anyways.
+ if (!CrashReporterParent::CreateCrashReporter(this)) {
+ mShutdown = true;
+ Close();
+ OnInitFailure();
+ return;
+ }
+ CrashReporterParent* crashReporter = CrashReporter();
+ if (crashReporter) {
+ crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("AsyncPluginInit"),
+ mIsStartingAsync ?
+ NS_LITERAL_CSTRING("1") :
+ NS_LITERAL_CSTRING("0"));
+ }
+#ifdef XP_WIN
+ { // Scope for lock
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+ mCrashReporter = CrashReporter();
+ }
+#endif
+#endif
+
+#if defined(XP_WIN) && defined(_X86_)
+ // Protected mode only applies to Windows and only to x86.
+ if (!mIsBlocklisted && mIsFlashPlugin &&
+ (Preferences::GetBool("dom.ipc.plugins.flash.disable-protected-mode", false) ||
+ mSandboxLevel >= 2)) {
+ SendDisableFlashProtectedMode();
+ }
+#endif
+
+ if (mInitOnAsyncConnect) {
+ mInitOnAsyncConnect = false;
+#if defined(XP_WIN)
+ mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
+ &mAsyncInitError);
+ if (NS_SUCCEEDED(mAsyncInitRv))
+#endif
+ {
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+ mAsyncInitRv = NP_Initialize(mNPNIface,
+ mNPPIface,
+ &mAsyncInitError);
+#else
+ mAsyncInitRv = NP_Initialize(mNPNIface,
+ &mAsyncInitError);
+#endif
+ }
+
+#if defined(XP_MACOSX)
+ if (NS_SUCCEEDED(mAsyncInitRv)) {
+ mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
+ &mAsyncInitError);
+ }
+#endif
+ }
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+ bool profilerActive = false;
+ DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ if (profilerActive) {
+ nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
+ rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsISupports> gatherer;
+ rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+
+ StartProfiler(currentProfilerParams);
+ }
+#endif
+}
+
+bool
+PluginModuleChromeParent::WaitForIPCConnection()
+{
+ PluginProcessParent* process = Process();
+ MOZ_ASSERT(process);
+ process->SetCallRunnableImmediately(true);
+ if (!process->WaitUntilConnected()) {
+ return false;
+ }
+ return true;
+}
+
+PluginModuleParent::PluginModuleParent(bool aIsChrome, bool aAllowAsyncInit)
+ : mQuirks(QUIRKS_NOT_INITIALIZED)
+ , mIsChrome(aIsChrome)
+ , mShutdown(false)
+ , mHadLocalInstance(false)
+ , mClearSiteDataSupported(false)
+ , mGetSitesWithDataSupported(false)
+ , mNPNIface(nullptr)
+ , mNPPIface(nullptr)
+ , mPlugin(nullptr)
+ , mTaskFactory(this)
+ , mSandboxLevel(0)
+ , mIsFlashPlugin(false)
+ , mIsStartingAsync(false)
+ , mNPInitialized(false)
+ , mIsNPShutdownPending(false)
+ , mAsyncNewRv(NS_ERROR_NOT_INITIALIZED)
+{
+#if defined(MOZ_CRASHREPORTER)
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AsyncPluginInit"),
+ mIsStartingAsync ?
+ NS_LITERAL_CSTRING("1") :
+ NS_LITERAL_CSTRING("0"));
+#endif
+}
+
+PluginModuleParent::~PluginModuleParent()
+{
+ if (!OkToCleanup()) {
+ NS_RUNTIMEABORT("unsafe destruction");
+ }
+
+ if (!mShutdown) {
+ NS_WARNING("Plugin host deleted the module without shutting down.");
+ NPError err;
+ NP_Shutdown(&err);
+ }
+}
+
+PluginModuleContentParent::PluginModuleContentParent(bool aAllowAsyncInit)
+ : PluginModuleParent(false, aAllowAsyncInit)
+{
+ Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this);
+}
+
+PluginModuleContentParent::~PluginModuleContentParent()
+{
+ Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
+}
+
+bool PluginModuleChromeParent::sInstantiated = false;
+
+PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
+ uint32_t aPluginId,
+ int32_t aSandboxLevel,
+ bool aAllowAsyncInit)
+ : PluginModuleParent(true, aAllowAsyncInit)
+ , mSubprocess(new PluginProcessParent(aFilePath))
+ , mPluginId(aPluginId)
+ , mChromeTaskFactory(this)
+ , mHangAnnotationFlags(0)
+#ifdef XP_WIN
+ , mPluginCpuUsageOnHang()
+ , mHangUIParent(nullptr)
+ , mHangUIEnabled(true)
+ , mIsTimerReset(true)
+#ifdef MOZ_CRASHREPORTER
+ , mCrashReporterMutex("PluginModuleChromeParent::mCrashReporterMutex")
+ , mCrashReporter(nullptr)
+#endif
+#endif
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ , mFlashProcess1(0)
+ , mFlashProcess2(0)
+ , mFinishInitTask(nullptr)
+#endif
+ , mInitOnAsyncConnect(false)
+ , mAsyncInitRv(NS_ERROR_NOT_INITIALIZED)
+ , mAsyncInitError(NPERR_NO_ERROR)
+ , mContentParent(nullptr)
+{
+ NS_ASSERTION(mSubprocess, "Out of memory!");
+ sInstantiated = true;
+ mSandboxLevel = aSandboxLevel;
+ mRunID = GeckoChildProcessHost::GetUniqueID();
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ InitPluginProfiling();
+#endif
+
+ mozilla::HangMonitor::RegisterAnnotator(*this);
+}
+
+PluginModuleChromeParent::~PluginModuleChromeParent()
+{
+ if (!OkToCleanup()) {
+ NS_RUNTIMEABORT("unsafe destruction");
+ }
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ ShutdownPluginProfiling();
+#endif
+
+#ifdef XP_WIN
+ // If we registered for audio notifications, stop.
+ mozilla::plugins::PluginUtilsWin::RegisterForAudioDeviceChanges(this,
+ false);
+#endif
+
+ if (!mShutdown) {
+ NS_WARNING("Plugin host deleted the module without shutting down.");
+ NPError err;
+ NP_Shutdown(&err);
+ }
+
+ NS_ASSERTION(mShutdown, "NP_Shutdown didn't");
+
+ if (mSubprocess) {
+ mSubprocess->Delete();
+ mSubprocess = nullptr;
+ }
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ if (mFlashProcess1)
+ UnregisterInjectorCallback(mFlashProcess1);
+ if (mFlashProcess2)
+ UnregisterInjectorCallback(mFlashProcess2);
+ if (mFinishInitTask) {
+ // mFinishInitTask will be deleted by the main thread message_loop
+ mFinishInitTask->Cancel();
+ }
+#endif
+
+ UnregisterSettingsCallbacks();
+
+ Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this);
+ Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this);
+#ifdef XP_WIN
+ Preferences::UnregisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
+ Preferences::UnregisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
+
+ if (mHangUIParent) {
+ delete mHangUIParent;
+ mHangUIParent = nullptr;
+ }
+#endif
+
+ mozilla::HangMonitor::UnregisterAnnotator(*this);
+}
+
+#ifdef MOZ_CRASHREPORTER
+void
+PluginModuleChromeParent::WriteExtraDataForMinidump(AnnotationTable& notes)
+{
+#ifdef XP_WIN
+ // mCrashReporterMutex is already held by the caller
+ mCrashReporterMutex.AssertCurrentThreadOwns();
+#endif
+ typedef nsDependentCString CS;
+
+ // Get the plugin filename, try to get just the file leafname
+ const std::string& pluginFile = mSubprocess->GetPluginFilePath();
+ size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR);
+ if (filePos == std::string::npos)
+ filePos = 0;
+ else
+ filePos++;
+ notes.Put(NS_LITERAL_CSTRING("PluginFilename"), CS(pluginFile.substr(filePos).c_str()));
+
+ notes.Put(NS_LITERAL_CSTRING("PluginName"), mPluginName);
+ notes.Put(NS_LITERAL_CSTRING("PluginVersion"), mPluginVersion);
+
+ CrashReporterParent* crashReporter = CrashReporter();
+ if (crashReporter) {
+#ifdef XP_WIN
+ if (mPluginCpuUsageOnHang.Length() > 0) {
+ notes.Put(NS_LITERAL_CSTRING("NumberOfProcessors"),
+ nsPrintfCString("%d", PR_GetNumberOfProcessors()));
+
+ nsCString cpuUsageStr;
+ cpuUsageStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[0] * 100) / 100);
+ notes.Put(NS_LITERAL_CSTRING("PluginCpuUsage"), cpuUsageStr);
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ for (uint32_t i=1; i<mPluginCpuUsageOnHang.Length(); ++i) {
+ nsCString tempStr;
+ tempStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[i] * 100) / 100);
+ notes.Put(nsPrintfCString("CpuUsageFlashProcess%d", i), tempStr);
+ }
+#endif
+ }
+#endif
+ }
+}
+#endif // MOZ_CRASHREPORTER
+
+void
+PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
+{
+ int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
+ MessageChannel::kNoTimeout;
+ SetReplyTimeoutMs(timeoutMs);
+}
+
+void
+PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
+{
+ PluginModuleParent* module = static_cast<PluginModuleParent*>(aModule);
+
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+#ifndef XP_WIN
+ if (!strcmp(aPref, kChildTimeoutPref)) {
+ MOZ_ASSERT(module->IsChrome());
+ // The timeout value used by the parent for children
+ int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
+ module->SetChildTimeout(timeoutSecs);
+#else
+ if (!strcmp(aPref, kChildTimeoutPref) ||
+ !strcmp(aPref, kHangUIMinDisplayPref) ||
+ !strcmp(aPref, kHangUITimeoutPref)) {
+ MOZ_ASSERT(module->IsChrome());
+ static_cast<PluginModuleChromeParent*>(module)->EvaluateHangUIState(true);
+#endif // XP_WIN
+ } else if (!strcmp(aPref, kParentTimeoutPref)) {
+ // The timeout value used by the child for its parent
+ MOZ_ASSERT(module->IsChrome());
+ int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
+ Unused << static_cast<PluginModuleChromeParent*>(module)->SendSetParentHangTimeout(timeoutSecs);
+ } else if (!strcmp(aPref, kContentTimeoutPref)) {
+ MOZ_ASSERT(!module->IsChrome());
+ int32_t timeoutSecs = Preferences::GetInt(kContentTimeoutPref, 0);
+ module->SetChildTimeout(timeoutSecs);
+ }
+}
+
+void
+PluginModuleChromeParent::CleanupFromTimeout(const bool aFromHangUI)
+{
+ if (mShutdown) {
+ return;
+ }
+
+ if (!OkToCleanup()) {
+ // there's still plugin code on the C++ stack, try again
+ MessageLoop::current()->PostDelayedTask(
+ mChromeTaskFactory.NewRunnableMethod(
+ &PluginModuleChromeParent::CleanupFromTimeout, aFromHangUI), 10);
+ return;
+ }
+
+ /* If the plugin container was terminated by the Plugin Hang UI,
+ then either the I/O thread detects a channel error, or the
+ main thread must set the error (whomever gets there first).
+ OTOH, if we terminate and return false from
+ ShouldContinueFromReplyTimeout, then the channel state has
+ already been set to ChannelTimeout and we should call the
+ regular Close function. */
+ if (aFromHangUI) {
+ GetIPCChannel()->CloseWithError();
+ } else {
+ Close();
+ }
+}
+
+#ifdef XP_WIN
+namespace {
+
+uint64_t
+FileTimeToUTC(const FILETIME& ftime)
+{
+ ULARGE_INTEGER li;
+ li.LowPart = ftime.dwLowDateTime;
+ li.HighPart = ftime.dwHighDateTime;
+ return li.QuadPart;
+}
+
+struct CpuUsageSamples
+{
+ uint64_t sampleTimes[2];
+ uint64_t cpuTimes[2];
+};
+
+bool
+GetProcessCpuUsage(const InfallibleTArray<base::ProcessHandle>& processHandles, InfallibleTArray<float>& cpuUsage)
+{
+ InfallibleTArray<CpuUsageSamples> samples(processHandles.Length());
+ FILETIME creationTime, exitTime, kernelTime, userTime, currentTime;
+ BOOL res;
+
+ for (uint32_t i = 0; i < processHandles.Length(); ++i) {
+ ::GetSystemTimeAsFileTime(&currentTime);
+ res = ::GetProcessTimes(processHandles[i], &creationTime, &exitTime, &kernelTime, &userTime);
+ if (!res) {
+ NS_WARNING("failed to get process times");
+ return false;
+ }
+
+ CpuUsageSamples s;
+ s.sampleTimes[0] = FileTimeToUTC(currentTime);
+ s.cpuTimes[0] = FileTimeToUTC(kernelTime) + FileTimeToUTC(userTime);
+ samples.AppendElement(s);
+ }
+
+ // we already hung for a while, a little bit longer won't matter
+ ::Sleep(50);
+
+ const int32_t numberOfProcessors = PR_GetNumberOfProcessors();
+
+ for (uint32_t i = 0; i < processHandles.Length(); ++i) {
+ ::GetSystemTimeAsFileTime(&currentTime);
+ res = ::GetProcessTimes(processHandles[i], &creationTime, &exitTime, &kernelTime, &userTime);
+ if (!res) {
+ NS_WARNING("failed to get process times");
+ return false;
+ }
+
+ samples[i].sampleTimes[1] = FileTimeToUTC(currentTime);
+ samples[i].cpuTimes[1] = FileTimeToUTC(kernelTime) + FileTimeToUTC(userTime);
+
+ const uint64_t deltaSampleTime = samples[i].sampleTimes[1] - samples[i].sampleTimes[0];
+ const uint64_t deltaCpuTime = samples[i].cpuTimes[1] - samples[i].cpuTimes[0];
+ const float usage = 100.f * (float(deltaCpuTime) / deltaSampleTime) / numberOfProcessors;
+ cpuUsage.AppendElement(usage);
+ }
+
+ return true;
+}
+
+} // namespace
+
+#endif // #ifdef XP_WIN
+
+/**
+ * This function converts the topmost routing id on the call stack (as recorded
+ * by the MessageChannel) into a pointer to a IProtocol object.
+ */
+mozilla::ipc::IProtocol*
+PluginModuleChromeParent::GetInvokingProtocol()
+{
+ int32_t routingId = GetIPCChannel()->GetTopmostMessageRoutingId();
+ // Nothing being routed. No protocol. Just return nullptr.
+ if (routingId == MSG_ROUTING_NONE) {
+ return nullptr;
+ }
+ // If routingId is MSG_ROUTING_CONTROL then we're dealing with control
+ // messages that were initiated by the topmost managing protocol, ie. this.
+ if (routingId == MSG_ROUTING_CONTROL) {
+ return this;
+ }
+ // Otherwise we can look up the protocol object by the routing id.
+ mozilla::ipc::IProtocol* protocol = Lookup(routingId);
+ return protocol;
+}
+
+/**
+ * This function examines the IProtocol object parameter and converts it into
+ * the PluginInstanceParent object that is associated with that protocol, if
+ * any. Since PluginInstanceParent manages subprotocols, this function needs
+ * to determine whether |aProtocol| is a subprotocol, and if so it needs to
+ * obtain the protocol's manager.
+ *
+ * This function needs to be updated if the subprotocols are modified in
+ * PPluginInstance.ipdl.
+ */
+PluginInstanceParent*
+PluginModuleChromeParent::GetManagingInstance(mozilla::ipc::IProtocol* aProtocol)
+{
+ MOZ_ASSERT(aProtocol);
+ mozilla::ipc::IProtocol* listener = aProtocol;
+ switch (listener->GetProtocolTypeId()) {
+ case PPluginInstanceMsgStart:
+ // In this case, aProtocol is the instance itself. Just cast it.
+ return static_cast<PluginInstanceParent*>(aProtocol);
+ case PPluginBackgroundDestroyerMsgStart: {
+ PPluginBackgroundDestroyerParent* actor =
+ static_cast<PPluginBackgroundDestroyerParent*>(aProtocol);
+ return static_cast<PluginInstanceParent*>(actor->Manager());
+ }
+ case PPluginScriptableObjectMsgStart: {
+ PPluginScriptableObjectParent* actor =
+ static_cast<PPluginScriptableObjectParent*>(aProtocol);
+ return static_cast<PluginInstanceParent*>(actor->Manager());
+ }
+ case PBrowserStreamMsgStart: {
+ PBrowserStreamParent* actor =
+ static_cast<PBrowserStreamParent*>(aProtocol);
+ return static_cast<PluginInstanceParent*>(actor->Manager());
+ }
+ case PPluginStreamMsgStart: {
+ PPluginStreamParent* actor =
+ static_cast<PPluginStreamParent*>(aProtocol);
+ return static_cast<PluginInstanceParent*>(actor->Manager());
+ }
+ case PStreamNotifyMsgStart: {
+ PStreamNotifyParent* actor =
+ static_cast<PStreamNotifyParent*>(aProtocol);
+ return static_cast<PluginInstanceParent*>(actor->Manager());
+ }
+#ifdef XP_WIN
+ case PPluginSurfaceMsgStart: {
+ PPluginSurfaceParent* actor =
+ static_cast<PPluginSurfaceParent*>(aProtocol);
+ return static_cast<PluginInstanceParent*>(actor->Manager());
+ }
+#endif
+ default:
+ return nullptr;
+ }
+}
+
+void
+PluginModuleChromeParent::EnteredCxxStack()
+{
+ mHangAnnotationFlags |= kInPluginCall;
+}
+
+void
+PluginModuleChromeParent::ExitedCxxStack()
+{
+ mHangAnnotationFlags = 0;
+#ifdef XP_WIN
+ FinishHangUI();
+#endif
+}
+
+/**
+ * This function is always called by the HangMonitor thread.
+ */
+void
+PluginModuleChromeParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
+{
+ uint32_t flags = mHangAnnotationFlags;
+ if (flags) {
+ /* We don't actually annotate anything specifically for kInPluginCall;
+ we use it to determine whether to annotate other things. It will
+ be pretty obvious from the ChromeHang stack that we're in a plugin
+ call when the hang occurred. */
+ if (flags & kHangUIShown) {
+ aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIShown"),
+ true);
+ }
+ if (flags & kHangUIContinued) {
+ aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIContinued"),
+ true);
+ }
+ if (flags & kHangUIDontShow) {
+ aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIDontShow"),
+ true);
+ }
+ aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginName"), mPluginName);
+ aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginVersion"),
+ mPluginVersion);
+ }
+}
+
+#ifdef MOZ_CRASHREPORTER
+static bool
+CreatePluginMinidump(base::ProcessId processId, ThreadId childThread,
+ nsIFile* parentMinidump, const nsACString& name)
+{
+ mozilla::ipc::ScopedProcessHandle handle;
+ if (processId == 0 ||
+ !base::OpenPrivilegedProcessHandle(processId, &handle.rwget())) {
+ return false;
+ }
+ return CreateAdditionalChildMinidump(handle, 0, parentMinidump, name);
+}
+#endif
+
+bool
+PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
+{
+ if (mIsFlashPlugin) {
+ MessageLoop::current()->PostTask(
+ mTaskFactory.NewRunnableMethod(
+ &PluginModuleChromeParent::NotifyFlashHang));
+ }
+
+#ifdef XP_WIN
+ if (LaunchHangUI()) {
+ return true;
+ }
+ // If LaunchHangUI returned false then we should proceed with the
+ // original plugin hang behaviour and kill the plugin container.
+ FinishHangUI();
+#endif // XP_WIN
+ TerminateChildProcess(MessageLoop::current(),
+ mozilla::ipc::kInvalidProcessId,
+ NS_LITERAL_CSTRING("ModalHangUI"),
+ EmptyString());
+ GetIPCChannel()->CloseWithTimeout();
+ return false;
+}
+
+bool
+PluginModuleContentParent::ShouldContinueFromReplyTimeout()
+{
+ RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
+ if (!monitor) {
+ return true;
+ }
+ monitor->NotifyPluginHang(mPluginId);
+ return true;
+}
+
+void
+PluginModuleContentParent::OnExitedSyncSend()
+{
+ ProcessHangMonitor::ClearHang();
+}
+
+void
+PluginModuleChromeParent::TakeFullMinidump(base::ProcessId aContentPid,
+ const nsAString& aBrowserDumpId,
+ nsString& aDumpId)
+{
+#ifdef MOZ_CRASHREPORTER
+#ifdef XP_WIN
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+#endif // XP_WIN
+
+ CrashReporterParent* crashReporter = CrashReporter();
+ if (!crashReporter) {
+ return;
+ }
+
+ bool reportsReady = false;
+
+ // Check to see if we already have a browser dump id - with e10s plugin
+ // hangs we take this earlier (see ProcessHangMonitor) from a background
+ // thread. We do this before we message the main thread about the hang
+ // since the posted message will trash our browser stack state.
+ bool exists;
+ nsCOMPtr<nsIFile> browserDumpFile;
+ if (!aBrowserDumpId.IsEmpty() &&
+ CrashReporter::GetMinidumpForID(aBrowserDumpId, getter_AddRefs(browserDumpFile)) &&
+ browserDumpFile &&
+ NS_SUCCEEDED(browserDumpFile->Exists(&exists)) && exists)
+ {
+ // We have a single browser report, generate a new plugin process parent
+ // report and pair it up with the browser report handed in.
+ reportsReady = crashReporter->GenerateMinidumpAndPair(this, browserDumpFile,
+ NS_LITERAL_CSTRING("browser"));
+ if (!reportsReady) {
+ browserDumpFile = nullptr;
+ CrashReporter::DeleteMinidumpFilesForID(aBrowserDumpId);
+ }
+ }
+
+ // Generate crash report including plugin and browser process minidumps.
+ // The plugin process is the parent report with additional dumps including
+ // the browser process, content process when running under e10s, and
+ // various flash subprocesses if we're the flash module.
+ if (!reportsReady) {
+ reportsReady = crashReporter->GeneratePairedMinidump(this);
+ }
+
+ if (reportsReady) {
+ // Important to set this here, it tells the ActorDestroy handler
+ // that we have an existing crash report that needs to be finalized.
+ mPluginDumpID = crashReporter->ChildDumpID();
+ aDumpId = mPluginDumpID;
+ PLUGIN_LOG_DEBUG(
+ ("generated paired browser/plugin minidumps: %s)",
+ NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
+ nsAutoCString additionalDumps("browser");
+ nsCOMPtr<nsIFile> pluginDumpFile;
+ if (GetMinidumpForID(mPluginDumpID, getter_AddRefs(pluginDumpFile)) &&
+ pluginDumpFile) {
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ // If we have handles to the flash sandbox processes on Windows,
+ // include those minidumps as well.
+ if (CreatePluginMinidump(mFlashProcess1, 0, pluginDumpFile,
+ NS_LITERAL_CSTRING("flash1"))) {
+ additionalDumps.AppendLiteral(",flash1");
+ }
+ if (CreatePluginMinidump(mFlashProcess2, 0, pluginDumpFile,
+ NS_LITERAL_CSTRING("flash2"))) {
+ additionalDumps.AppendLiteral(",flash2");
+ }
+#endif // MOZ_CRASHREPORTER_INJECTOR
+ if (aContentPid != mozilla::ipc::kInvalidProcessId) {
+ // Include the content process minidump
+ if (CreatePluginMinidump(aContentPid, 0,
+ pluginDumpFile,
+ NS_LITERAL_CSTRING("content"))) {
+ additionalDumps.AppendLiteral(",content");
+ }
+ }
+ }
+ crashReporter->AnnotateCrashReport(
+ NS_LITERAL_CSTRING("additional_minidumps"),
+ additionalDumps);
+ } else {
+ NS_WARNING("failed to capture paired minidumps from hang");
+ }
+#endif // MOZ_CRASHREPORTER
+}
+
+void
+PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
+ base::ProcessId aContentPid,
+ const nsCString& aMonitorDescription,
+ const nsAString& aDumpId)
+{
+#ifdef MOZ_CRASHREPORTER
+ // Start by taking a full minidump if necessary, this is done early
+ // because it also needs to lock the mCrashReporterMutex and Mutex doesn't
+ // support recrusive locking.
+ nsAutoString dumpId;
+ if (aDumpId.IsEmpty()) {
+ TakeFullMinidump(aContentPid, EmptyString(), dumpId);
+ }
+
+#ifdef XP_WIN
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+ CrashReporterParent* crashReporter = mCrashReporter;
+ if (!crashReporter) {
+ // If mCrashReporter is null then the hang has ended, the plugin module
+ // is shutting down. There's nothing to do here.
+ return;
+ }
+#else
+ CrashReporterParent* crashReporter = CrashReporter();
+#endif // XP_WIN
+ crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("PluginHang"),
+ NS_LITERAL_CSTRING("1"));
+ crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("HangMonitorDescription"),
+ aMonitorDescription);
+#ifdef XP_WIN
+ if (mHangUIParent) {
+ unsigned int hangUIDuration = mHangUIParent->LastShowDurationMs();
+ if (hangUIDuration) {
+ nsPrintfCString strHangUIDuration("%u", hangUIDuration);
+ crashReporter->AnnotateCrashReport(
+ NS_LITERAL_CSTRING("PluginHangUIDuration"),
+ strHangUIDuration);
+ }
+ }
+#endif // XP_WIN
+#endif // MOZ_CRASHREPORTER
+
+ mozilla::ipc::ScopedProcessHandle geckoChildProcess;
+ bool childOpened = base::OpenProcessHandle(OtherPid(),
+ &geckoChildProcess.rwget());
+
+#ifdef XP_WIN
+ // collect cpu usage for plugin processes
+
+ InfallibleTArray<base::ProcessHandle> processHandles;
+
+ if (childOpened) {
+ processHandles.AppendElement(geckoChildProcess);
+ }
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ mozilla::ipc::ScopedProcessHandle flashBrokerProcess;
+ if (mFlashProcess1 &&
+ base::OpenProcessHandle(mFlashProcess1, &flashBrokerProcess.rwget())) {
+ processHandles.AppendElement(flashBrokerProcess);
+ }
+ mozilla::ipc::ScopedProcessHandle flashSandboxProcess;
+ if (mFlashProcess2 &&
+ base::OpenProcessHandle(mFlashProcess2, &flashSandboxProcess.rwget())) {
+ processHandles.AppendElement(flashSandboxProcess);
+ }
+#endif
+
+ if (!GetProcessCpuUsage(processHandles, mPluginCpuUsageOnHang)) {
+ mPluginCpuUsageOnHang.Clear();
+ }
+#endif
+
+ // this must run before the error notification from the channel,
+ // or not at all
+ bool isFromHangUI = aMsgLoop != MessageLoop::current();
+ aMsgLoop->PostTask(
+ mChromeTaskFactory.NewRunnableMethod(
+ &PluginModuleChromeParent::CleanupFromTimeout, isFromHangUI));
+
+ if (!childOpened || !KillProcess(geckoChildProcess, 1, false)) {
+ NS_WARNING("failed to kill subprocess!");
+ }
+}
+
+bool
+PluginModuleParent::GetPluginDetails()
+{
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (!host) {
+ return false;
+ }
+ nsPluginTag* pluginTag = host->TagForPlugin(mPlugin);
+ if (!pluginTag) {
+ return false;
+ }
+ mPluginName = pluginTag->Name();
+ mPluginVersion = pluginTag->Version();
+ mPluginFilename = pluginTag->FileName();
+ mIsFlashPlugin = pluginTag->mIsFlashPlugin;
+ mSandboxLevel = pluginTag->mSandboxLevel;
+ return true;
+}
+
+void
+PluginModuleParent::InitQuirksModes(const nsCString& aMimeType)
+{
+ if (mQuirks != QUIRKS_NOT_INITIALIZED) {
+ return;
+ }
+
+ mQuirks = GetQuirksFromMimeTypeAndFilename(aMimeType, mPluginFilename);
+}
+
+#ifdef XP_WIN
+void
+PluginModuleChromeParent::EvaluateHangUIState(const bool aReset)
+{
+ int32_t minDispSecs = Preferences::GetInt(kHangUIMinDisplayPref, 10);
+ int32_t autoStopSecs = Preferences::GetInt(kChildTimeoutPref, 0);
+ int32_t timeoutSecs = 0;
+ if (autoStopSecs > 0 && autoStopSecs < minDispSecs) {
+ /* If we're going to automatically terminate the plugin within a
+ time frame shorter than minDispSecs, there's no point in
+ showing the hang UI; it would just flash briefly on the screen. */
+ mHangUIEnabled = false;
+ } else {
+ timeoutSecs = Preferences::GetInt(kHangUITimeoutPref, 0);
+ mHangUIEnabled = timeoutSecs > 0;
+ }
+ if (mHangUIEnabled) {
+ if (aReset) {
+ mIsTimerReset = true;
+ SetChildTimeout(timeoutSecs);
+ return;
+ } else if (mIsTimerReset) {
+ /* The Hang UI is being shown, so now we're setting the
+ timeout to kChildTimeoutPref while we wait for a user
+ response. ShouldContinueFromReplyTimeout will fire
+ after (reply timeout / 2) seconds, which is not what
+ we want. Doubling the timeout value here so that we get
+ the right result. */
+ autoStopSecs *= 2;
+ }
+ }
+ mIsTimerReset = false;
+ SetChildTimeout(autoStopSecs);
+}
+
+bool
+PluginModuleChromeParent::LaunchHangUI()
+{
+ if (!mHangUIEnabled) {
+ return false;
+ }
+ if (mHangUIParent) {
+ if (mHangUIParent->IsShowing()) {
+ // We've already shown the UI but the timeout has expired again.
+ return false;
+ }
+ if (mHangUIParent->DontShowAgain()) {
+ mHangAnnotationFlags |= kHangUIDontShow;
+ bool wasLastHangStopped = mHangUIParent->WasLastHangStopped();
+ if (!wasLastHangStopped) {
+ mHangAnnotationFlags |= kHangUIContinued;
+ }
+ return !wasLastHangStopped;
+ }
+ delete mHangUIParent;
+ mHangUIParent = nullptr;
+ }
+ mHangUIParent = new PluginHangUIParent(this,
+ Preferences::GetInt(kHangUITimeoutPref, 0),
+ Preferences::GetInt(kChildTimeoutPref, 0));
+ bool retval = mHangUIParent->Init(NS_ConvertUTF8toUTF16(mPluginName));
+ if (retval) {
+ mHangAnnotationFlags |= kHangUIShown;
+ /* Once the UI is shown we switch the timeout over to use
+ kChildTimeoutPref, allowing us to terminate a hung plugin
+ after kChildTimeoutPref seconds if the user doesn't respond to
+ the hang UI. */
+ EvaluateHangUIState(false);
+ }
+ return retval;
+}
+
+void
+PluginModuleChromeParent::FinishHangUI()
+{
+ if (mHangUIEnabled && mHangUIParent) {
+ bool needsCancel = mHangUIParent->IsShowing();
+ // If we're still showing, send a Cancel notification
+ if (needsCancel) {
+ mHangUIParent->Cancel();
+ }
+ /* If we cancelled the UI or if the user issued a response,
+ we need to reset the child process timeout. */
+ if (needsCancel ||
+ (!mIsTimerReset && mHangUIParent->WasShown())) {
+ /* We changed the timeout to kChildTimeoutPref when the plugin hang
+ UI was displayed. Now that we're finishing the UI, we need to
+ switch it back to kHangUITimeoutPref. */
+ EvaluateHangUIState(true);
+ }
+ }
+}
+
+void
+PluginModuleChromeParent::OnHangUIContinue()
+{
+ mHangAnnotationFlags |= kHangUIContinued;
+}
+#endif // XP_WIN
+
+#ifdef MOZ_CRASHREPORTER
+CrashReporterParent*
+PluginModuleChromeParent::CrashReporter()
+{
+ return static_cast<CrashReporterParent*>(LoneManagedOrNullAsserts(ManagedPCrashReporterParent()));
+}
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+static void
+RemoveMinidump(nsIFile* minidump)
+{
+ if (!minidump)
+ return;
+
+ minidump->Remove(false);
+ nsCOMPtr<nsIFile> extraFile;
+ if (GetExtraFileForMinidump(minidump,
+ getter_AddRefs(extraFile))) {
+ extraFile->Remove(true);
+ }
+}
+#endif // MOZ_CRASHREPORTER_INJECTOR
+
+void
+PluginModuleChromeParent::ProcessFirstMinidump()
+{
+#ifdef XP_WIN
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+#endif
+ CrashReporterParent* crashReporter = CrashReporter();
+ if (!crashReporter)
+ return;
+
+ AnnotationTable notes(4);
+ WriteExtraDataForMinidump(notes);
+
+ if (!mPluginDumpID.IsEmpty()) {
+ // mPluginDumpID may be set in TerminateChildProcess, which means the
+ // process hang monitor has already collected a 3-way browser, plugin,
+ // content crash report. If so, update the existing report with our
+ // annotations and finalize it. If not, fall through for standard
+ // plugin crash report handling.
+ crashReporter->GenerateChildData(&notes);
+ crashReporter->FinalizeChildData();
+ return;
+ }
+
+ uint32_t sequence = UINT32_MAX;
+ nsCOMPtr<nsIFile> dumpFile;
+ nsAutoCString flashProcessType;
+ TakeMinidump(getter_AddRefs(dumpFile), &sequence);
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ nsCOMPtr<nsIFile> childDumpFile;
+ uint32_t childSequence;
+
+ if (mFlashProcess1 &&
+ TakeMinidumpForChild(mFlashProcess1,
+ getter_AddRefs(childDumpFile),
+ &childSequence)) {
+ if (childSequence < sequence) {
+ RemoveMinidump(dumpFile);
+ dumpFile = childDumpFile;
+ sequence = childSequence;
+ flashProcessType.AssignLiteral("Broker");
+ }
+ else {
+ RemoveMinidump(childDumpFile);
+ }
+ }
+ if (mFlashProcess2 &&
+ TakeMinidumpForChild(mFlashProcess2,
+ getter_AddRefs(childDumpFile),
+ &childSequence)) {
+ if (childSequence < sequence) {
+ RemoveMinidump(dumpFile);
+ dumpFile = childDumpFile;
+ sequence = childSequence;
+ flashProcessType.AssignLiteral("Sandbox");
+ }
+ else {
+ RemoveMinidump(childDumpFile);
+ }
+ }
+#endif
+
+ if (!dumpFile) {
+ NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!");
+ return;
+ }
+
+ PLUGIN_LOG_DEBUG(("got child minidump: %s",
+ NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
+
+ GetIDFromMinidump(dumpFile, mPluginDumpID);
+ if (!flashProcessType.IsEmpty()) {
+ notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"), flashProcessType);
+ }
+ crashReporter->GenerateCrashReportForMinidump(dumpFile, &notes);
+}
+#endif
+
+void
+PluginModuleParent::ActorDestroy(ActorDestroyReason why)
+{
+ switch (why) {
+ case AbnormalShutdown: {
+ mShutdown = true;
+ // Defer the PluginCrashed method so that we don't re-enter
+ // and potentially modify the actor child list while enumerating it.
+ if (mPlugin)
+ MessageLoop::current()->PostTask(
+ mTaskFactory.NewRunnableMethod(
+ &PluginModuleParent::NotifyPluginCrashed));
+ break;
+ }
+ case NormalShutdown:
+ mShutdown = true;
+ break;
+
+ default:
+ NS_RUNTIMEABORT("Unexpected shutdown reason for toplevel actor.");
+ }
+}
+
+nsresult
+PluginModuleParent::GetRunID(uint32_t* aRunID)
+{
+ if (NS_WARN_IF(!aRunID)) {
+ return NS_ERROR_INVALID_POINTER;
+ }
+ *aRunID = mRunID;
+ return NS_OK;
+}
+
+void
+PluginModuleChromeParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (why == AbnormalShutdown) {
+#ifdef MOZ_CRASHREPORTER
+ ProcessFirstMinidump();
+#endif
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
+ NS_LITERAL_CSTRING("plugin"), 1);
+ }
+
+ // We can't broadcast settings changes anymore.
+ UnregisterSettingsCallbacks();
+
+ PluginModuleParent::ActorDestroy(why);
+}
+
+void
+PluginModuleParent::NotifyFlashHang()
+{
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "flash-plugin-hang", nullptr);
+ }
+}
+
+void
+PluginModuleParent::NotifyPluginCrashed()
+{
+ if (!OkToCleanup()) {
+ // there's still plugin code on the C++ stack. try again
+ MessageLoop::current()->PostDelayedTask(
+ mTaskFactory.NewRunnableMethod(
+ &PluginModuleParent::NotifyPluginCrashed), 10);
+ return;
+ }
+
+ if (mPlugin)
+ mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID);
+}
+
+PPluginInstanceParent*
+PluginModuleParent::AllocPPluginInstanceParent(const nsCString& aMimeType,
+ const uint16_t& aMode,
+ const InfallibleTArray<nsCString>& aNames,
+ const InfallibleTArray<nsCString>& aValues)
+{
+ NS_ERROR("Not reachable!");
+ return nullptr;
+}
+
+bool
+PluginModuleParent::DeallocPPluginInstanceParent(PPluginInstanceParent* aActor)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ delete aActor;
+ return true;
+}
+
+void
+PluginModuleParent::SetPluginFuncs(NPPluginFuncs* aFuncs)
+{
+ MOZ_ASSERT(aFuncs);
+
+ aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+ aFuncs->javaClass = nullptr;
+
+ // Gecko should always call these functions through a PluginLibrary object.
+ aFuncs->newp = nullptr;
+ aFuncs->clearsitedata = nullptr;
+ aFuncs->getsiteswithdata = nullptr;
+
+ aFuncs->destroy = NPP_Destroy;
+ aFuncs->setwindow = NPP_SetWindow;
+ aFuncs->newstream = NPP_NewStream;
+ aFuncs->destroystream = NPP_DestroyStream;
+ aFuncs->asfile = NPP_StreamAsFile;
+ aFuncs->writeready = NPP_WriteReady;
+ aFuncs->write = NPP_Write;
+ aFuncs->print = NPP_Print;
+ aFuncs->event = NPP_HandleEvent;
+ aFuncs->urlnotify = NPP_URLNotify;
+ aFuncs->getvalue = NPP_GetValue;
+ aFuncs->setvalue = NPP_SetValue;
+ aFuncs->gotfocus = nullptr;
+ aFuncs->lostfocus = nullptr;
+ aFuncs->urlredirectnotify = nullptr;
+
+ // Provide 'NPP_URLRedirectNotify', 'NPP_ClearSiteData', and
+ // 'NPP_GetSitesWithData' functionality if it is supported by the plugin.
+ bool urlRedirectSupported = false;
+ Unused << CallOptionalFunctionsSupported(&urlRedirectSupported,
+ &mClearSiteDataSupported,
+ &mGetSitesWithDataSupported);
+ if (urlRedirectSupported) {
+ aFuncs->urlredirectnotify = NPP_URLRedirectNotify;
+ }
+}
+
+#define RESOLVE_AND_CALL(instance, func) \
+NP_BEGIN_MACRO \
+ PluginAsyncSurrogate* surrogate = nullptr; \
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);\
+ if (surrogate && (!i || i->UseSurrogate())) { \
+ return surrogate->func; \
+ } \
+ if (!i) { \
+ return NPERR_GENERIC_ERROR; \
+ } \
+ return i->func; \
+NP_END_MACRO
+
+NPError
+PluginModuleParent::NPP_Destroy(NPP instance,
+ NPSavedData** saved)
+{
+ // FIXME/cjones:
+ // (1) send a "destroy" message to the child
+ // (2) the child shuts down its instance
+ // (3) remove both parent and child IDs from map
+ // (4) free parent
+
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ PluginAsyncSurrogate* surrogate = nullptr;
+ PluginInstanceParent* parentInstance =
+ PluginInstanceParent::Cast(instance, &surrogate);
+ if (surrogate && (!parentInstance || parentInstance->UseSurrogate())) {
+ return surrogate->NPP_Destroy(saved);
+ }
+
+ if (!parentInstance)
+ return NPERR_NO_ERROR;
+
+ NPError retval = parentInstance->Destroy();
+ instance->pdata = nullptr;
+
+ Unused << PluginInstanceParent::Call__delete__(parentInstance);
+ return retval;
+}
+
+NPError
+PluginModuleParent::NPP_NewStream(NPP instance, NPMIMEType type,
+ NPStream* stream, NPBool seekable,
+ uint16_t* stype)
+{
+ PROFILER_LABEL("PluginModuleParent", "NPP_NewStream",
+ js::ProfileEntry::Category::OTHER);
+ RESOLVE_AND_CALL(instance, NPP_NewStream(type, stream, seekable, stype));
+}
+
+NPError
+PluginModuleParent::NPP_SetWindow(NPP instance, NPWindow* window)
+{
+ RESOLVE_AND_CALL(instance, NPP_SetWindow(window));
+}
+
+NPError
+PluginModuleParent::NPP_DestroyStream(NPP instance,
+ NPStream* stream,
+ NPReason reason)
+{
+ RESOLVE_AND_CALL(instance, NPP_DestroyStream(stream, reason));
+}
+
+int32_t
+PluginModuleParent::NPP_WriteReady(NPP instance,
+ NPStream* stream)
+{
+ PluginAsyncSurrogate* surrogate = nullptr;
+ BrowserStreamParent* s = StreamCast(instance, stream, &surrogate);
+ if (!s) {
+ if (surrogate) {
+ return surrogate->NPP_WriteReady(stream);
+ }
+ return -1;
+ }
+
+ return s->WriteReady();
+}
+
+int32_t
+PluginModuleParent::NPP_Write(NPP instance,
+ NPStream* stream,
+ int32_t offset,
+ int32_t len,
+ void* buffer)
+{
+ BrowserStreamParent* s = StreamCast(instance, stream);
+ if (!s)
+ return -1;
+
+ return s->Write(offset, len, buffer);
+}
+
+void
+PluginModuleParent::NPP_StreamAsFile(NPP instance,
+ NPStream* stream,
+ const char* fname)
+{
+ BrowserStreamParent* s = StreamCast(instance, stream);
+ if (!s)
+ return;
+
+ s->StreamAsFile(fname);
+}
+
+void
+PluginModuleParent::NPP_Print(NPP instance, NPPrint* platformPrint)
+{
+
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ i->NPP_Print(platformPrint);
+}
+
+int16_t
+PluginModuleParent::NPP_HandleEvent(NPP instance, void* event)
+{
+ RESOLVE_AND_CALL(instance, NPP_HandleEvent(event));
+}
+
+void
+PluginModuleParent::NPP_URLNotify(NPP instance, const char* url,
+ NPReason reason, void* notifyData)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ if (!i)
+ return;
+
+ i->NPP_URLNotify(url, reason, notifyData);
+}
+
+NPError
+PluginModuleParent::NPP_GetValue(NPP instance,
+ NPPVariable variable, void *ret_value)
+{
+ // The rules are slightly different for this function.
+ // If there is a surrogate, we *always* use it.
+ PluginAsyncSurrogate* surrogate = nullptr;
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);
+ if (surrogate) {
+ return surrogate->NPP_GetValue(variable, ret_value);
+ }
+ if (!i) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return i->NPP_GetValue(variable, ret_value);
+}
+
+NPError
+PluginModuleParent::NPP_SetValue(NPP instance, NPNVariable variable,
+ void *value)
+{
+ RESOLVE_AND_CALL(instance, NPP_SetValue(variable, value));
+}
+
+bool
+PluginModuleChromeParent::AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
+ const bool& shouldRegister, NPError* result)
+{
+#ifdef XP_WIN
+ *result = NPERR_NO_ERROR;
+ nsresult err =
+ mozilla::plugins::PluginUtilsWin::RegisterForAudioDeviceChanges(this,
+ shouldRegister);
+ if (err != NS_OK) {
+ *result = NPERR_GENERIC_ERROR;
+ }
+ return true;
+#else
+ NS_RUNTIMEABORT("NPPVpluginRequiresAudioDeviceChanges is not valid on this platform.");
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+#endif
+}
+
+bool
+PluginModuleParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
+{
+#ifndef MOZ_X11
+ NS_RUNTIMEABORT("This message only makes sense on X11 platforms");
+#else
+ MOZ_ASSERT(0 > mPluginXSocketFdDup.get(),
+ "Already backed up X resources??");
+ if (aXSocketFd.IsValid()) {
+ auto rawFD = aXSocketFd.ClonePlatformHandle();
+ mPluginXSocketFdDup.reset(rawFD.release());
+ }
+#endif
+ return true;
+}
+
+void
+PluginModuleParent::NPP_URLRedirectNotify(NPP instance, const char* url,
+ int32_t status, void* notifyData)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ if (!i)
+ return;
+
+ i->NPP_URLRedirectNotify(url, status, notifyData);
+}
+
+BrowserStreamParent*
+PluginModuleParent::StreamCast(NPP instance, NPStream* s,
+ PluginAsyncSurrogate** aSurrogate)
+{
+ PluginInstanceParent* ip = PluginInstanceParent::Cast(instance, aSurrogate);
+ if (!ip || (aSurrogate && *aSurrogate && ip->UseSurrogate())) {
+ return nullptr;
+ }
+
+ BrowserStreamParent* sp =
+ static_cast<BrowserStreamParent*>(static_cast<AStream*>(s->pdata));
+ if (sp && (sp->mNPP != ip || s != sp->mStream)) {
+ NS_RUNTIMEABORT("Corrupted plugin stream data.");
+ }
+ return sp;
+}
+
+bool
+PluginModuleParent::HasRequiredFunctions()
+{
+ return true;
+}
+
+nsresult
+PluginModuleParent::AsyncSetWindow(NPP instance, NPWindow* window)
+{
+ PluginAsyncSurrogate* surrogate = nullptr;
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);
+ if (surrogate && (!i || i->UseSurrogate())) {
+ return surrogate->AsyncSetWindow(window);
+ } else if (!i) {
+ return NS_ERROR_FAILURE;
+ }
+ return i->AsyncSetWindow(window);
+}
+
+nsresult
+PluginModuleParent::GetImageContainer(NPP instance,
+ mozilla::layers::ImageContainer** aContainer)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ return !i ? NS_ERROR_FAILURE : i->GetImageContainer(aContainer);
+}
+
+nsresult
+PluginModuleParent::GetImageSize(NPP instance,
+ nsIntSize* aSize)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ return !i ? NS_ERROR_FAILURE : i->GetImageSize(aSize);
+}
+
+void
+PluginModuleParent::DidComposite(NPP aInstance)
+{
+ if (PluginInstanceParent* i = PluginInstanceParent::Cast(aInstance)) {
+ i->DidComposite();
+ }
+}
+
+nsresult
+PluginModuleParent::SetBackgroundUnknown(NPP instance)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ if (!i)
+ return NS_ERROR_FAILURE;
+
+ return i->SetBackgroundUnknown();
+}
+
+nsresult
+PluginModuleParent::BeginUpdateBackground(NPP instance,
+ const nsIntRect& aRect,
+ DrawTarget** aDrawTarget)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ if (!i)
+ return NS_ERROR_FAILURE;
+
+ return i->BeginUpdateBackground(aRect, aDrawTarget);
+}
+
+nsresult
+PluginModuleParent::EndUpdateBackground(NPP instance, const nsIntRect& aRect)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ if (!i)
+ return NS_ERROR_FAILURE;
+
+ return i->EndUpdateBackground(aRect);
+}
+
+#if defined(XP_WIN)
+nsresult
+PluginModuleParent::GetScrollCaptureContainer(NPP aInstance,
+ mozilla::layers::ImageContainer** aContainer)
+{
+ PluginInstanceParent* inst = PluginInstanceParent::Cast(aInstance);
+ return !inst ? NS_ERROR_FAILURE : inst->GetScrollCaptureContainer(aContainer);
+}
+#endif
+
+nsresult
+PluginModuleParent::HandledWindowedPluginKeyEvent(
+ NPP aInstance,
+ const NativeEventData& aNativeKeyData,
+ bool aIsConsumed)
+{
+ PluginInstanceParent* parent = PluginInstanceParent::Cast(aInstance);
+ if (NS_WARN_IF(!parent)) {
+ return NS_ERROR_FAILURE;
+ }
+ return parent->HandledWindowedPluginKeyEvent(aNativeKeyData, aIsConsumed);
+}
+
+void
+PluginModuleParent::OnInitFailure()
+{
+ if (GetIPCChannel()->CanSend()) {
+ Close();
+ }
+
+ mShutdown = true;
+
+ if (mIsStartingAsync) {
+ /* If we've failed then we need to enumerate any pending NPP_New calls
+ and clean them up. */
+ uint32_t len = mSurrogateInstances.Length();
+ for (uint32_t i = 0; i < len; ++i) {
+ mSurrogateInstances[i]->NotifyAsyncInitFailed();
+ }
+ mSurrogateInstances.Clear();
+ }
+}
+
+class PluginOfflineObserver final : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit PluginOfflineObserver(PluginModuleChromeParent* pmp)
+ : mPmp(pmp)
+ {}
+
+private:
+ ~PluginOfflineObserver() {}
+ PluginModuleChromeParent* mPmp;
+};
+
+NS_IMPL_ISUPPORTS(PluginOfflineObserver, nsIObserver)
+
+NS_IMETHODIMP
+PluginOfflineObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ MOZ_ASSERT(!strcmp(aTopic, "ipc:network:set-offline"));
+ mPmp->CachedSettingChanged();
+ return NS_OK;
+}
+
+static const char* kSettingsPrefs[] =
+ {"javascript.enabled",
+ "dom.ipc.plugins.nativeCursorSupport"};
+
+void
+PluginModuleChromeParent::RegisterSettingsCallbacks()
+{
+ for (size_t i = 0; i < ArrayLength(kSettingsPrefs); i++) {
+ Preferences::RegisterCallback(CachedSettingChanged, kSettingsPrefs[i], this);
+ }
+
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ if (observerService) {
+ mPluginOfflineObserver = new PluginOfflineObserver(this);
+ observerService->AddObserver(mPluginOfflineObserver, "ipc:network:set-offline", false);
+ }
+}
+
+void
+PluginModuleChromeParent::UnregisterSettingsCallbacks()
+{
+ for (size_t i = 0; i < ArrayLength(kSettingsPrefs); i++) {
+ Preferences::UnregisterCallback(CachedSettingChanged, kSettingsPrefs[i], this);
+ }
+
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->RemoveObserver(mPluginOfflineObserver, "ipc:network:set-offline");
+ mPluginOfflineObserver = nullptr;
+ }
+}
+
+bool
+PluginModuleParent::GetSetting(NPNVariable aVariable)
+{
+ NPBool boolVal = false;
+ mozilla::plugins::parent::_getvalue(nullptr, aVariable, &boolVal);
+ return boolVal;
+}
+
+void
+PluginModuleParent::GetSettings(PluginSettings* aSettings)
+{
+ aSettings->javascriptEnabled() = GetSetting(NPNVjavascriptEnabledBool);
+ aSettings->asdEnabled() = GetSetting(NPNVasdEnabledBool);
+ aSettings->isOffline() = GetSetting(NPNVisOfflineBool);
+ aSettings->supportsXembed() = GetSetting(NPNVSupportsXEmbedBool);
+ aSettings->supportsWindowless() = GetSetting(NPNVSupportsWindowless);
+ aSettings->userAgent() = NullableString(mNPNIface->uagent(nullptr));
+
+#if defined(XP_MACOSX)
+ aSettings->nativeCursorsSupported() =
+ Preferences::GetBool("dom.ipc.plugins.nativeCursorSupport", false);
+#else
+ // Need to initialize this to satisfy IPDL.
+ aSettings->nativeCursorsSupported() = false;
+#endif
+}
+
+void
+PluginModuleChromeParent::CachedSettingChanged()
+{
+ PluginSettings settings;
+ GetSettings(&settings);
+ Unused << SendSettingChanged(settings);
+}
+
+/* static */ void
+PluginModuleChromeParent::CachedSettingChanged(const char* aPref, void* aModule)
+{
+ PluginModuleChromeParent *module = static_cast<PluginModuleChromeParent*>(aModule);
+ module->CachedSettingChanged();
+}
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+nsresult
+PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ mNPNIface = bFuncs;
+ mNPPIface = pFuncs;
+
+ if (mShutdown) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ *error = NPERR_NO_ERROR;
+ if (mIsStartingAsync) {
+ if (GetIPCChannel()->CanSend()) {
+ // We're already connected, so we may call this immediately.
+ RecvNP_InitializeResult(*error);
+ } else {
+ PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+ }
+ } else {
+ SetPluginFuncs(pFuncs);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ if (mShutdown) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ *error = NPERR_NO_ERROR;
+
+ mNPNIface = bFuncs;
+ mNPPIface = pFuncs;
+
+ // NB: This *MUST* be set prior to checking whether the subprocess has
+ // been connected!
+ if (mIsStartingAsync) {
+ PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+ }
+
+ if (!mSubprocess->IsConnected()) {
+ // The subprocess isn't connected yet. Defer NP_Initialize until
+ // OnProcessLaunched is invoked.
+ mInitOnAsyncConnect = true;
+ return NS_OK;
+ }
+
+ PluginSettings settings;
+ GetSettings(&settings);
+
+ TimeStamp callNpInitStart = TimeStamp::Now();
+ // Asynchronous case
+ if (mIsStartingAsync) {
+ if (!SendAsyncNP_Initialize(settings)) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+ TimeStamp callNpInitEnd = TimeStamp::Now();
+ mTimeBlocked += (callNpInitEnd - callNpInitStart);
+ return NS_OK;
+ }
+
+ // Synchronous case
+ if (!CallNP_Initialize(settings, error)) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+ else if (*error != NPERR_NO_ERROR) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+ TimeStamp callNpInitEnd = TimeStamp::Now();
+ mTimeBlocked += (callNpInitEnd - callNpInitStart);
+
+ RecvNP_InitializeResult(*error);
+
+ return NS_OK;
+}
+
+bool
+PluginModuleParent::RecvNP_InitializeResult(const NPError& aError)
+{
+ if (aError != NPERR_NO_ERROR) {
+ OnInitFailure();
+ return true;
+ }
+
+ SetPluginFuncs(mNPPIface);
+ if (mIsStartingAsync) {
+ InitAsyncSurrogates();
+ }
+
+ mNPInitialized = true;
+ return true;
+}
+
+bool
+PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
+{
+ if (!mContentParent) {
+ return PluginModuleParent::RecvNP_InitializeResult(aError);
+ }
+ bool initOk = aError == NPERR_NO_ERROR;
+ if (initOk) {
+ SetPluginFuncs(mNPPIface);
+ if (mIsStartingAsync && !SendAssociatePluginId()) {
+ initOk = false;
+ }
+ }
+ mNPInitialized = initOk;
+ bool result = mContentParent->SendLoadPluginResult(mPluginId, initOk);
+ mContentParent = nullptr;
+ return result;
+}
+
+#else
+
+nsresult
+PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ mNPNIface = bFuncs;
+
+ if (mShutdown) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+}
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+
+nsresult
+PluginModuleContentParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
+ if (mIsStartingAsync && GetIPCChannel()->CanSend()) {
+ // We're already connected, so we may call this immediately.
+ RecvNP_InitializeResult(*error);
+ }
+ return rv;
+}
+
+#endif
+
+nsresult
+PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
+{
+ nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
+ if (NS_FAILED(rv))
+ return rv;
+
+#if defined(XP_MACOSX)
+ if (!mSubprocess->IsConnected()) {
+ // The subprocess isn't connected yet. Defer NP_Initialize until
+ // OnProcessLaunched is invoked.
+ mInitOnAsyncConnect = true;
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+ }
+#else
+ if (mInitOnAsyncConnect) {
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+ }
+#endif
+
+ PluginSettings settings;
+ GetSettings(&settings);
+
+ TimeStamp callNpInitStart = TimeStamp::Now();
+ if (mIsStartingAsync) {
+ if (!SendAsyncNP_Initialize(settings)) {
+ return NS_ERROR_FAILURE;
+ }
+ TimeStamp callNpInitEnd = TimeStamp::Now();
+ mTimeBlocked += (callNpInitEnd - callNpInitStart);
+ return NS_OK;
+ }
+
+ if (!CallNP_Initialize(settings, error)) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+ TimeStamp callNpInitEnd = TimeStamp::Now();
+ mTimeBlocked += (callNpInitEnd - callNpInitStart);
+ RecvNP_InitializeResult(*error);
+ return NS_OK;
+}
+
+bool
+PluginModuleParent::RecvNP_InitializeResult(const NPError& aError)
+{
+ if (aError != NPERR_NO_ERROR) {
+ OnInitFailure();
+ return true;
+ }
+
+ if (mIsStartingAsync && mNPPIface) {
+ SetPluginFuncs(mNPPIface);
+ InitAsyncSurrogates();
+ }
+
+ mNPInitialized = true;
+ return true;
+}
+
+bool
+PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
+{
+ bool ok = true;
+ if (mContentParent) {
+ if ((ok = SendAssociatePluginId())) {
+ ok = mContentParent->SendLoadPluginResult(mPluginId,
+ aError == NPERR_NO_ERROR);
+ mContentParent = nullptr;
+ }
+ } else if (aError == NPERR_NO_ERROR) {
+ // Initialization steps for (e10s && !asyncInit) || !e10s
+#if defined XP_WIN
+ if (mIsStartingAsync) {
+ SetPluginFuncs(mNPPIface);
+ }
+
+ // Send the info needed to join the browser process's audio session to the
+ // plugin process.
+ nsID id;
+ nsString sessionName;
+ nsString iconPath;
+
+ if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
+ iconPath))) {
+ Unused << SendSetAudioSessionData(id, sessionName, iconPath);
+ }
+#endif
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ InitializeInjector();
+#endif
+ }
+
+ return PluginModuleParent::RecvNP_InitializeResult(aError) && ok;
+}
+
+#endif
+
+void
+PluginModuleParent::InitAsyncSurrogates()
+{
+ if (MaybeRunDeferredShutdown()) {
+ // We've shut down, so the surrogates are no longer valid. Clear
+ // mSurrogateInstances to ensure that these aren't used.
+ mSurrogateInstances.Clear();
+ return;
+ }
+
+ uint32_t len = mSurrogateInstances.Length();
+ for (uint32_t i = 0; i < len; ++i) {
+ NPError err;
+ mAsyncNewRv = mSurrogateInstances[i]->NPP_New(&err);
+ if (NS_FAILED(mAsyncNewRv)) {
+ mSurrogateInstances[i]->NotifyAsyncInitFailed();
+ continue;
+ }
+ }
+ mSurrogateInstances.Clear();
+}
+
+bool
+PluginModuleParent::RemovePendingSurrogate(
+ const RefPtr<PluginAsyncSurrogate>& aSurrogate)
+{
+ return mSurrogateInstances.RemoveElement(aSurrogate);
+}
+
+bool
+PluginModuleParent::MaybeRunDeferredShutdown()
+{
+ if (!mIsStartingAsync || !mIsNPShutdownPending) {
+ return false;
+ }
+ MOZ_ASSERT(!mShutdown);
+ NPError error;
+ if (!DoShutdown(&error)) {
+ return false;
+ }
+ mIsNPShutdownPending = false;
+ return true;
+}
+
+nsresult
+PluginModuleParent::NP_Shutdown(NPError* error)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ if (mShutdown) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ /* If we're still running an async NP_Initialize then we need to defer
+ shutdown until we've received the result of the NP_Initialize call. */
+ if (mIsStartingAsync && !mNPInitialized) {
+ mIsNPShutdownPending = true;
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+ }
+
+ if (!DoShutdown(error)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+bool
+PluginModuleParent::DoShutdown(NPError* error)
+{
+ bool ok = true;
+ if (IsChrome() && mHadLocalInstance) {
+ // We synchronously call NP_Shutdown if the chrome process was using
+ // plugins itself. That way we can service any requests the plugin
+ // makes. If we're in e10s, though, the content processes will have
+ // already shut down and there's no one to talk to. So we shut down
+ // asynchronously in PluginModuleChild::ActorDestroy.
+ ok = CallNP_Shutdown(error);
+ }
+
+ // if NP_Shutdown() is nested within another interrupt call, this will
+ // break things. but lord help us if we're doing that anyway; the
+ // plugin dso will have been unloaded on the other side by the
+ // CallNP_Shutdown() message
+ Close();
+
+ // mShutdown should either be initialized to false, or be transitiong from
+ // false to true. It is never ok to go from true to false. Using OR for
+ // the following assignment to ensure this.
+ mShutdown |= ok;
+ if (!ok) {
+ *error = NPERR_GENERIC_ERROR;
+ }
+ return ok;
+}
+
+nsresult
+PluginModuleParent::NP_GetMIMEDescription(const char** mimeDesc)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ *mimeDesc = "application/x-foobar";
+ return NS_OK;
+}
+
+nsresult
+PluginModuleParent::NP_GetValue(void *future, NPPVariable aVariable,
+ void *aValue, NPError* error)
+{
+ MOZ_LOG(GetPluginLog(), LogLevel::Warning, ("%s Not implemented, requested variable %i", __FUNCTION__,
+ (int) aVariable));
+
+ //TODO: implement this correctly
+ *error = NPERR_GENERIC_ERROR;
+ return NS_OK;
+}
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+nsresult
+PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
+{
+ NS_ASSERTION(pFuncs, "Null pointer!");
+
+ *error = NPERR_NO_ERROR;
+ if (mIsStartingAsync && !IsChrome()) {
+ mNPPIface = pFuncs;
+#if defined(XP_MACOSX)
+ if (mNPInitialized) {
+ SetPluginFuncs(pFuncs);
+ InitAsyncSurrogates();
+ } else {
+ PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+ }
+#else
+ PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+#endif
+ } else {
+ SetPluginFuncs(pFuncs);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+PluginModuleChromeParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
+{
+#if defined(XP_MACOSX)
+ if (mInitOnAsyncConnect) {
+ PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+ mNPPIface = pFuncs;
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+ }
+#else
+ if (mIsStartingAsync) {
+ PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+ }
+ if (!mSubprocess->IsConnected()) {
+ mNPPIface = pFuncs;
+ mInitOnAsyncConnect = true;
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+ }
+#endif
+
+ // We need to have the plugin process update its function table here by
+ // actually calling NP_GetEntryPoints. The parent's function table will
+ // reflect nullptr entries in the child's table once SetPluginFuncs is
+ // called.
+
+ if (!CallNP_GetEntryPoints(error)) {
+ return NS_ERROR_FAILURE;
+ }
+ else if (*error != NPERR_NO_ERROR) {
+ return NS_OK;
+ }
+
+ return PluginModuleParent::NP_GetEntryPoints(pFuncs, error);
+}
+
+#endif
+
+nsresult
+PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
+ uint16_t mode, int16_t argc, char* argn[],
+ char* argv[], NPSavedData* saved,
+ NPError* error)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ if (mShutdown) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mIsStartingAsync) {
+ if (!PluginAsyncSurrogate::Create(this, pluginType, instance, mode,
+ argc, argn, argv)) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mNPInitialized) {
+ RefPtr<PluginAsyncSurrogate> surrogate =
+ PluginAsyncSurrogate::Cast(instance);
+ mSurrogateInstances.AppendElement(surrogate);
+ *error = NPERR_NO_ERROR;
+ return NS_PLUGIN_INIT_PENDING;
+ }
+ }
+
+ // create the instance on the other side
+ InfallibleTArray<nsCString> names;
+ InfallibleTArray<nsCString> values;
+
+ for (int i = 0; i < argc; ++i) {
+ names.AppendElement(NullableString(argn[i]));
+ values.AppendElement(NullableString(argv[i]));
+ }
+
+ nsresult rv = NPP_NewInternal(pluginType, instance, mode, names, values,
+ saved, error);
+ if (NS_FAILED(rv) || !mIsStartingAsync) {
+ return rv;
+ }
+ return NS_PLUGIN_INIT_PENDING;
+}
+
+class nsCaseInsensitiveUTF8StringArrayComparator
+{
+public:
+ template<class A, class B>
+ bool Equals(const A& a, const B& b) const {
+ return a.Equals(b.get(), nsCaseInsensitiveUTF8StringComparator());
+ }
+};
+
+void
+PluginModuleParent::AccumulateModuleInitBlockedTime()
+{
+ if (mPluginName.IsEmpty()) {
+ GetPluginDetails();
+ }
+ Telemetry::Accumulate(Telemetry::BLOCKED_ON_PLUGIN_MODULE_INIT_MS,
+ GetHistogramKey(),
+ static_cast<uint32_t>(mTimeBlocked.ToMilliseconds()));
+ mTimeBlocked = TimeDuration();
+}
+
+nsresult
+PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
+ uint16_t mode,
+ InfallibleTArray<nsCString>& names,
+ InfallibleTArray<nsCString>& values,
+ NPSavedData* saved, NPError* error)
+{
+ MOZ_ASSERT(names.Length() == values.Length());
+ if (mPluginName.IsEmpty()) {
+ GetPluginDetails();
+ InitQuirksModes(nsDependentCString(pluginType));
+ /** mTimeBlocked measures the time that the main thread has been blocked
+ * on plugin module initialization. As implemented, this is the sum of
+ * plugin-container launch + toolhelp32 snapshot + NP_Initialize.
+ * We don't accumulate its value until here because the plugin info
+ * for its histogram key is not available until *after* NP_Initialize.
+ */
+ AccumulateModuleInitBlockedTime();
+ }
+
+ nsCaseInsensitiveUTF8StringArrayComparator comparator;
+ NS_NAMED_LITERAL_CSTRING(srcAttributeName, "src");
+ auto srcAttributeIndex = names.IndexOf(srcAttributeName, 0, comparator);
+ nsAutoCString srcAttribute;
+ if (srcAttributeIndex != names.NoIndex) {
+ srcAttribute = values[srcAttributeIndex];
+ }
+
+ nsDependentCString strPluginType(pluginType);
+ PluginInstanceParent* parentInstance =
+ new PluginInstanceParent(this, instance, strPluginType, mNPNIface);
+
+ if (mIsFlashPlugin) {
+ parentInstance->InitMetadata(strPluginType, srcAttribute);
+#ifdef XP_WIN
+ bool supportsAsyncRender =
+ Preferences::GetBool("dom.ipc.plugins.asyncdrawing.enabled", false);
+ if (supportsAsyncRender) {
+ // Prefs indicates we want async plugin rendering, make sure
+ // the flash module has support.
+ CallModuleSupportsAsyncRender(&supportsAsyncRender);
+ }
+#ifdef _WIN64
+ // For 64-bit builds force windowless if the flash library doesn't support
+ // async rendering regardless of sandbox level.
+ if (!supportsAsyncRender) {
+#else
+ // For 32-bit builds force windowless if the flash library doesn't support
+ // async rendering and the sandbox level is 2 or greater.
+ if (!supportsAsyncRender && mSandboxLevel >= 2) {
+#endif
+ NS_NAMED_LITERAL_CSTRING(wmodeAttributeName, "wmode");
+ NS_NAMED_LITERAL_CSTRING(opaqueAttributeValue, "opaque");
+ auto wmodeAttributeIndex =
+ names.IndexOf(wmodeAttributeName, 0, comparator);
+ if (wmodeAttributeIndex != names.NoIndex) {
+ if (!values[wmodeAttributeIndex].EqualsLiteral("transparent")) {
+ values[wmodeAttributeIndex].Assign(opaqueAttributeValue);
+ }
+ } else {
+ names.AppendElement(wmodeAttributeName);
+ values.AppendElement(opaqueAttributeValue);
+ }
+ }
+#endif
+ }
+
+ // Release the surrogate reference that was in pdata
+ RefPtr<PluginAsyncSurrogate> surrogate(
+ dont_AddRef(PluginAsyncSurrogate::Cast(instance)));
+ // Now replace it with the instance
+ instance->pdata = static_cast<PluginDataResolver*>(parentInstance);
+
+ if (!SendPPluginInstanceConstructor(parentInstance,
+ nsDependentCString(pluginType), mode,
+ names, values)) {
+ // |parentInstance| is automatically deleted.
+ instance->pdata = nullptr;
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ { // Scope for timer
+ Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_INSTANCE_INIT_MS>
+ timer(GetHistogramKey());
+ if (mIsStartingAsync) {
+ MOZ_ASSERT(surrogate);
+ surrogate->AsyncCallDeparting();
+ if (!SendAsyncNPP_New(parentInstance)) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+ *error = NPERR_NO_ERROR;
+ } else {
+ if (!CallSyncNPP_New(parentInstance, error)) {
+ // if IPC is down, we'll get an immediate "failed" return, but
+ // without *error being set. So make sure that the error
+ // condition is signaled to nsNPAPIPluginInstance
+ if (NPERR_NO_ERROR == *error) {
+ *error = NPERR_GENERIC_ERROR;
+ }
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ if (*error != NPERR_NO_ERROR) {
+ if (!mIsStartingAsync) {
+ NPP_Destroy(instance, 0);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ UpdatePluginTimeout();
+
+ return NS_OK;
+}
+
+void
+PluginModuleChromeParent::UpdatePluginTimeout()
+{
+ TimeoutChanged(kParentTimeoutPref, this);
+}
+
+nsresult
+PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge,
+ nsCOMPtr<nsIClearSiteDataCallback> callback)
+{
+ if (!mClearSiteDataSupported)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ static uint64_t callbackId = 0;
+ callbackId++;
+ mClearSiteDataCallbacks[callbackId] = callback;
+
+ if (!SendNPP_ClearSiteData(NullableString(site), flags, maxAge, callbackId)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+
+nsresult
+PluginModuleParent::NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback)
+{
+ if (!mGetSitesWithDataSupported)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ static uint64_t callbackId = 0;
+ callbackId++;
+ mSitesWithDataCallbacks[callbackId] = callback;
+
+ if (!SendNPP_GetSitesWithData(callbackId))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+#if defined(XP_MACOSX)
+nsresult
+PluginModuleParent::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ if (!i)
+ return NS_ERROR_FAILURE;
+
+ return i->IsRemoteDrawingCoreAnimation(aDrawing);
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+nsresult
+PluginModuleParent::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ if (!i)
+ return NS_ERROR_FAILURE;
+
+ return i->ContentsScaleFactorChanged(aContentsScaleFactor);
+}
+#endif // #if defined(XP_MACOSX)
+
+#if defined(XP_MACOSX)
+bool
+PluginModuleParent::AnswerProcessSomeEvents()
+{
+ mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop();
+ return true;
+}
+
+#elif !defined(MOZ_WIDGET_GTK)
+bool
+PluginModuleParent::AnswerProcessSomeEvents()
+{
+ NS_RUNTIMEABORT("unreached");
+ return false;
+}
+
+#else
+static const int kMaxChancesToProcessEvents = 20;
+
+bool
+PluginModuleParent::AnswerProcessSomeEvents()
+{
+ PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
+
+ int i = 0;
+ for (; i < kMaxChancesToProcessEvents; ++i)
+ if (!g_main_context_iteration(nullptr, FALSE))
+ break;
+
+ PLUGIN_LOG_DEBUG(("... quitting mini nested loop; processed %i tasks", i));
+
+ return true;
+}
+#endif
+
+bool
+PluginModuleParent::RecvProcessNativeEventsInInterruptCall()
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(OS_WIN)
+ ProcessNativeEventsInInterruptCall();
+ return true;
+#else
+ NS_NOTREACHED(
+ "PluginModuleParent::RecvProcessNativeEventsInInterruptCall not implemented!");
+ return false;
+#endif
+}
+
+void
+PluginModuleParent::ProcessRemoteNativeEventsInInterruptCall()
+{
+#if defined(OS_WIN)
+ Unused << SendProcessNativeEventsInInterruptCall();
+ return;
+#endif
+ NS_NOTREACHED(
+ "PluginModuleParent::ProcessRemoteNativeEventsInInterruptCall not implemented!");
+}
+
+bool
+PluginModuleParent::RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
+ const int32_t& aX, const int32_t& aY,
+ const size_t& aWidth, const size_t& aHeight)
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ CGRect windowBound = ::CGRectMake(aX, aY, aWidth, aHeight);
+ mac_plugin_interposing::parent::OnPluginShowWindow(aWindowId, windowBound, aModal);
+ return true;
+#else
+ NS_NOTREACHED(
+ "PluginInstanceParent::RecvPluginShowWindow not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginModuleParent::RecvPluginHideWindow(const uint32_t& aWindowId)
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPluginHideWindow(aWindowId, OtherPid());
+ return true;
+#else
+ NS_NOTREACHED(
+ "PluginInstanceParent::RecvPluginHideWindow not implemented!");
+ return false;
+#endif
+}
+
+PCrashReporterParent*
+PluginModuleParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+ uint32_t* processType)
+{
+ MOZ_CRASH("unreachable");
+}
+
+bool
+PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
+{
+ MOZ_CRASH("unreachable");
+}
+
+PCrashReporterParent*
+PluginModuleChromeParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+ uint32_t* processType)
+{
+#ifdef MOZ_CRASHREPORTER
+ return new CrashReporterParent();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+PluginModuleChromeParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
+{
+#ifdef MOZ_CRASHREPORTER
+#ifdef XP_WIN
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+ if (actor == static_cast<PCrashReporterParent*>(mCrashReporter)) {
+ mCrashReporter = nullptr;
+ }
+#endif
+#endif
+ delete actor;
+ return true;
+}
+
+bool
+PluginModuleParent::RecvSetCursor(const NSCursorInfo& aCursorInfo)
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnSetCursor(aCursorInfo);
+ return true;
+#else
+ NS_NOTREACHED(
+ "PluginInstanceParent::RecvSetCursor not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginModuleParent::RecvShowCursor(const bool& aShow)
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnShowCursor(aShow);
+ return true;
+#else
+ NS_NOTREACHED(
+ "PluginInstanceParent::RecvShowCursor not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginModuleParent::RecvPushCursor(const NSCursorInfo& aCursorInfo)
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPushCursor(aCursorInfo);
+ return true;
+#else
+ NS_NOTREACHED(
+ "PluginInstanceParent::RecvPushCursor not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginModuleParent::RecvPopCursor()
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPopCursor();
+ return true;
+#else
+ NS_NOTREACHED(
+ "PluginInstanceParent::RecvPopCursor not implemented!");
+ return false;
+#endif
+}
+
+bool
+PluginModuleParent::RecvNPN_SetException(const nsCString& aMessage)
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+
+ // This function ignores its first argument.
+ mozilla::plugins::parent::_setexception(nullptr, NullableStringGet(aMessage));
+ return true;
+}
+
+bool
+PluginModuleParent::RecvNPN_ReloadPlugins(const bool& aReloadPages)
+{
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+
+ mozilla::plugins::parent::_reloadplugins(aReloadPages);
+ return true;
+}
+
+bool
+PluginModuleChromeParent::RecvNotifyContentModuleDestroyed()
+{
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (host) {
+ host->NotifyContentModuleDestroyed(mPluginId);
+ }
+ return true;
+}
+
+bool
+PluginModuleParent::RecvReturnClearSiteData(const NPError& aRv,
+ const uint64_t& aCallbackId)
+{
+ if (mClearSiteDataCallbacks.find(aCallbackId) == mClearSiteDataCallbacks.end()) {
+ return true;
+ }
+ if (!!mClearSiteDataCallbacks[aCallbackId]) {
+ nsresult rv;
+ switch (aRv) {
+ 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;
+ }
+ mClearSiteDataCallbacks[aCallbackId]->Callback(rv);
+ }
+ mClearSiteDataCallbacks.erase(aCallbackId);
+ return true;
+}
+
+bool
+PluginModuleParent::RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
+ const uint64_t& aCallbackId)
+{
+ if (mSitesWithDataCallbacks.find(aCallbackId) == mSitesWithDataCallbacks.end()) {
+ return true;
+ }
+
+ if (!!mSitesWithDataCallbacks[aCallbackId]) {
+ mSitesWithDataCallbacks[aCallbackId]->SitesWithData(aSites);
+ }
+ mSitesWithDataCallbacks.erase(aCallbackId);
+ return true;
+}
+
+layers::TextureClientRecycleAllocator*
+PluginModuleParent::EnsureTextureAllocatorForDirectBitmap()
+{
+ if (!mTextureAllocatorForDirectBitmap) {
+ mTextureAllocatorForDirectBitmap = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton().get());
+ }
+ return mTextureAllocatorForDirectBitmap;
+}
+
+layers::TextureClientRecycleAllocator*
+PluginModuleParent::EnsureTextureAllocatorForDXGISurface()
+{
+ if (!mTextureAllocatorForDXGISurface) {
+ mTextureAllocatorForDXGISurface = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton().get());
+ }
+ return mTextureAllocatorForDXGISurface;
+}
+
+
+bool
+PluginModuleParent::AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
+ const bool& shouldRegister,
+ NPError* result) {
+ NS_RUNTIMEABORT("SetValue_NPPVpluginRequiresAudioDeviceChanges is only valid "
+ "with PluginModuleChromeParent");
+ *result = NPERR_GENERIC_ERROR;
+ return true;
+}
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+
+// We only add the crash reporter to subprocess which have the filename
+// FlashPlayerPlugin*
+#define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN"
+
+static DWORD
+GetFlashChildOfPID(DWORD pid, HANDLE snapshot)
+{
+ PROCESSENTRY32 entry = {
+ sizeof(entry)
+ };
+ for (BOOL ok = Process32First(snapshot, &entry);
+ ok;
+ ok = Process32Next(snapshot, &entry)) {
+ if (entry.th32ParentProcessID == pid) {
+ nsString name(entry.szExeFile);
+ ToUpperCase(name);
+ if (StringBeginsWith(name, NS_LITERAL_STRING(FLASH_PROCESS_PREFIX))) {
+ return entry.th32ProcessID;
+ }
+ }
+ }
+ return 0;
+}
+
+// We only look for child processes of the Flash plugin, NPSWF*
+#define FLASH_PLUGIN_PREFIX "NPSWF"
+
+void
+PluginModuleChromeParent::InitializeInjector()
+{
+ if (!Preferences::GetBool("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", false))
+ return;
+
+ nsCString path(Process()->GetPluginFilePath().c_str());
+ ToUpperCase(path);
+ int32_t lastSlash = path.RFindCharInSet("\\/");
+ if (kNotFound == lastSlash)
+ return;
+
+ if (!StringBeginsWith(Substring(path, lastSlash + 1),
+ NS_LITERAL_CSTRING(FLASH_PLUGIN_PREFIX)))
+ return;
+
+ TimeStamp th32Start = TimeStamp::Now();
+ mFinishInitTask = mChromeTaskFactory.NewTask<FinishInjectorInitTask>();
+ mFinishInitTask->Init(this);
+ if (!::QueueUserWorkItem(&PluginModuleChromeParent::GetToolhelpSnapshot,
+ mFinishInitTask, WT_EXECUTEDEFAULT)) {
+ mFinishInitTask = nullptr;
+ return;
+ }
+ TimeStamp th32End = TimeStamp::Now();
+ mTimeBlocked += (th32End - th32Start);
+}
+
+void
+PluginModuleChromeParent::DoInjection(const nsAutoHandle& aSnapshot)
+{
+ DWORD pluginProcessPID = GetProcessId(Process()->GetChildProcessHandle());
+ mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, aSnapshot);
+ if (mFlashProcess1) {
+ InjectCrashReporterIntoProcess(mFlashProcess1, this);
+
+ mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, aSnapshot);
+ if (mFlashProcess2) {
+ InjectCrashReporterIntoProcess(mFlashProcess2, this);
+ }
+ }
+ mFinishInitTask = nullptr;
+}
+
+DWORD WINAPI
+PluginModuleChromeParent::GetToolhelpSnapshot(LPVOID aContext)
+{
+ FinishInjectorInitTask* task = static_cast<FinishInjectorInitTask*>(aContext);
+ MOZ_ASSERT(task);
+ task->PostToMainThread();
+ return 0;
+}
+
+void
+PluginModuleChromeParent::OnCrash(DWORD processID)
+{
+ if (!mShutdown) {
+ GetIPCChannel()->CloseWithError();
+ mozilla::ipc::ScopedProcessHandle geckoPluginChild;
+ if (base::OpenProcessHandle(OtherPid(), &geckoPluginChild.rwget())) {
+ if (!base::KillProcess(geckoPluginChild,
+ base::PROCESS_END_KILLED_BY_USER, false)) {
+ NS_ERROR("May have failed to kill child process.");
+ }
+ } else {
+ NS_ERROR("Failed to open child process when attempting kill.");
+ }
+ }
+}
+
+#endif // MOZ_CRASHREPORTER_INJECTOR
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+class PluginProfilerObserver final : public nsIObserver,
+ public nsSupportsWeakReference
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit PluginProfilerObserver(PluginModuleChromeParent* pmp)
+ : mPmp(pmp)
+ {}
+
+private:
+ ~PluginProfilerObserver() {}
+ PluginModuleChromeParent* mPmp;
+};
+
+NS_IMPL_ISUPPORTS(PluginProfilerObserver, nsIObserver, nsISupportsWeakReference)
+
+NS_IMETHODIMP
+PluginProfilerObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ if (!strcmp(aTopic, "profiler-started")) {
+ nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
+ mPmp->StartProfiler(params);
+ } else if (!strcmp(aTopic, "profiler-stopped")) {
+ mPmp->StopProfiler();
+ } else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
+ mPmp->GatherAsyncProfile();
+ } else if (!strcmp(aTopic, "profiler-subprocess")) {
+ nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
+ mPmp->GatheredAsyncProfile(pse);
+ }
+ return NS_OK;
+}
+
+void
+PluginModuleChromeParent::InitPluginProfiling()
+{
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ if (observerService) {
+ mProfilerObserver = new PluginProfilerObserver(this);
+ observerService->AddObserver(mProfilerObserver, "profiler-started", false);
+ observerService->AddObserver(mProfilerObserver, "profiler-stopped", false);
+ observerService->AddObserver(mProfilerObserver, "profiler-subprocess-gather", false);
+ observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false);
+ }
+}
+
+void
+PluginModuleChromeParent::ShutdownPluginProfiling()
+{
+ nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->RemoveObserver(mProfilerObserver, "profiler-started");
+ observerService->RemoveObserver(mProfilerObserver, "profiler-stopped");
+ observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess-gather");
+ observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
+ }
+}
+
+void
+PluginModuleChromeParent::StartProfiler(nsIProfilerStartParams* aParams)
+{
+ if (NS_WARN_IF(!aParams)) {
+ return;
+ }
+
+ ProfilerInitParams ipcParams;
+
+ ipcParams.enabled() = true;
+ aParams->GetEntries(&ipcParams.entries());
+ aParams->GetInterval(&ipcParams.interval());
+ ipcParams.features() = aParams->GetFeatures();
+ ipcParams.threadFilters() = aParams->GetThreadFilterNames();
+
+ Unused << SendStartProfiler(ipcParams);
+
+ nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+ if (NS_WARN_IF(!profiler)) {
+ return;
+ }
+ nsCOMPtr<nsISupports> gatherer;
+ profiler->GetProfileGatherer(getter_AddRefs(gatherer));
+ mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+}
+
+void
+PluginModuleChromeParent::StopProfiler()
+{
+ mGatherer = nullptr;
+ Unused << SendStopProfiler();
+}
+
+void
+PluginModuleChromeParent::GatherAsyncProfile()
+{
+ if (NS_WARN_IF(!mGatherer)) {
+ return;
+ }
+ mGatherer->WillGatherOOPProfile();
+ Unused << SendGatherProfile();
+}
+
+void
+PluginModuleChromeParent::GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent)
+{
+ if (aSaveEvent && !mProfile.IsEmpty()) {
+ aSaveEvent->AddSubProfile(mProfile.get());
+ mProfile.Truncate();
+ }
+}
+#endif // MOZ_ENABLE_PROFILER_SPS
+
+bool
+PluginModuleChromeParent::RecvProfile(const nsCString& aProfile)
+{
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ if (NS_WARN_IF(!mGatherer)) {
+ return true;
+ }
+
+ mProfile = aProfile;
+ mGatherer->GatheredOOPProfile();
+#endif
+ return true;
+}
+
+bool
+PluginModuleParent::AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet)
+{
+ return false;
+}
+
+bool
+PluginModuleChromeParent::AnswerGetKeyState(const int32_t& aVirtKey,
+ int16_t* aRet)
+{
+#if defined(XP_WIN)
+ *aRet = ::GetKeyState(aVirtKey);
+ return true;
+#else
+ return PluginModuleParent::AnswerGetKeyState(aVirtKey, aRet);
+#endif
+}
diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h
new file mode 100644
index 000000000..cc24d6ed2
--- /dev/null
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -0,0 +1,686 @@
+/* -*- 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 mozilla_plugins_PluginModuleParent_h
+#define mozilla_plugins_PluginModuleParent_h
+
+#include "base/process.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/HangAnnotations.h"
+#include "mozilla/PluginLibrary.h"
+#include "mozilla/plugins/PluginProcessParent.h"
+#include "mozilla/plugins/PPluginModuleParent.h"
+#include "mozilla/plugins/PluginMessageUtils.h"
+#include "mozilla/plugins/PluginTypes.h"
+#include "mozilla/ipc/TaskFactory.h"
+#include "mozilla/TimeStamp.h"
+#include "npapi.h"
+#include "npfunctions.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsIObserver.h"
+#ifdef XP_WIN
+#include "nsWindowsHelpers.h"
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+class nsIProfileSaveEvent;
+class nsPluginTag;
+
+namespace mozilla {
+#ifdef MOZ_ENABLE_PROFILER_SPS
+class ProfileGatherer;
+#endif
+namespace dom {
+class PCrashReporterParent;
+class CrashReporterParent;
+} // namespace dom
+
+namespace layers {
+class TextureClientRecycleAllocator;
+} // namespace layers
+
+namespace plugins {
+//-----------------------------------------------------------------------------
+
+class BrowserStreamParent;
+class PluginAsyncSurrogate;
+class PluginInstanceParent;
+
+#ifdef XP_WIN
+class PluginHangUIParent;
+#endif
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+class FinishInjectorInitTask;
+#endif
+
+/**
+ * PluginModuleParent
+ *
+ * This class implements the NPP API from the perspective of the rest
+ * of Gecko, forwarding NPP calls along to the child process that is
+ * actually running the plugin.
+ *
+ * This class /also/ implements a version of the NPN API, because the
+ * child process needs to make these calls back into Gecko proper.
+ * This class is responsible for "actually" making those function calls.
+ *
+ * If a plugin is running, there will always be one PluginModuleParent for it in
+ * the chrome process. In addition, any content process using the plugin will
+ * have its own PluginModuleParent. The subclasses PluginModuleChromeParent and
+ * PluginModuleContentParent implement functionality that is specific to one
+ * case or the other.
+ */
+class PluginModuleParent
+ : public PPluginModuleParent
+ , public PluginLibrary
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ , public CrashReporter::InjectorCrashCallback
+#endif
+{
+protected:
+ typedef mozilla::PluginLibrary PluginLibrary;
+ typedef mozilla::dom::PCrashReporterParent PCrashReporterParent;
+ typedef mozilla::dom::CrashReporterParent CrashReporterParent;
+
+ PPluginInstanceParent*
+ AllocPPluginInstanceParent(const nsCString& aMimeType,
+ const uint16_t& aMode,
+ const InfallibleTArray<nsCString>& aNames,
+ const InfallibleTArray<nsCString>& aValues)
+ override;
+
+ virtual bool
+ DeallocPPluginInstanceParent(PPluginInstanceParent* aActor) override;
+
+public:
+ explicit PluginModuleParent(bool aIsChrome, bool aAllowAsyncInit);
+ virtual ~PluginModuleParent();
+
+ bool RemovePendingSurrogate(const RefPtr<PluginAsyncSurrogate>& aSurrogate);
+
+ /** @return the state of the pref that controls async plugin init */
+ bool IsStartingAsync() const { return mIsStartingAsync; }
+ /** @return whether this modules NP_Initialize has successfully completed
+ executing */
+ bool IsInitialized() const { return mNPInitialized; }
+ bool IsChrome() const { return mIsChrome; }
+
+ virtual void SetPlugin(nsNPAPIPlugin* plugin) override
+ {
+ mPlugin = plugin;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ const NPNetscapeFuncs* GetNetscapeFuncs() {
+ return mNPNIface;
+ }
+
+ bool OkToCleanup() const {
+ return !IsOnCxxStack();
+ }
+
+ void ProcessRemoteNativeEventsInInterruptCall() override;
+
+ virtual bool WaitForIPCConnection() { return true; }
+
+ nsCString GetHistogramKey() const {
+ return mPluginName + mPluginVersion;
+ }
+
+ void AccumulateModuleInitBlockedTime();
+
+ virtual nsresult GetRunID(uint32_t* aRunID) override;
+ virtual void SetHasLocalInstance() override {
+ mHadLocalInstance = true;
+ }
+
+ int GetQuirks() { return mQuirks; }
+
+protected:
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override
+ {
+ return MediateRace(parent, child);
+ }
+
+ virtual bool
+ RecvBackUpXResources(const FileDescriptor& aXSocketFd) override;
+
+ virtual bool AnswerProcessSomeEvents() override;
+
+ virtual bool
+ RecvProcessNativeEventsInInterruptCall() override;
+
+ virtual bool
+ RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
+ const int32_t& aX, const int32_t& aY,
+ const size_t& aWidth, const size_t& aHeight) override;
+
+ virtual bool
+ RecvPluginHideWindow(const uint32_t& aWindowId) override;
+
+ virtual PCrashReporterParent*
+ AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+ uint32_t* processType) override;
+ virtual bool
+ DeallocPCrashReporterParent(PCrashReporterParent* actor) override;
+
+ virtual bool
+ RecvSetCursor(const NSCursorInfo& aCursorInfo) override;
+
+ virtual bool
+ RecvShowCursor(const bool& aShow) override;
+
+ virtual bool
+ RecvPushCursor(const NSCursorInfo& aCursorInfo) override;
+
+ virtual bool
+ RecvPopCursor() override;
+
+ virtual bool
+ RecvNPN_SetException(const nsCString& aMessage) override;
+
+ virtual bool
+ RecvNPN_ReloadPlugins(const bool& aReloadPages) override;
+
+ virtual bool
+ RecvNP_InitializeResult(const NPError& aError) override;
+
+ static BrowserStreamParent* StreamCast(NPP instance, NPStream* s,
+ PluginAsyncSurrogate** aSurrogate = nullptr);
+
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
+ const bool& shouldRegister,
+ NPError* result) override;
+
+protected:
+ void SetChildTimeout(const int32_t aChildTimeout);
+ static void TimeoutChanged(const char* aPref, void* aModule);
+
+ virtual void UpdatePluginTimeout() {}
+
+ virtual bool RecvNotifyContentModuleDestroyed() override { return true; }
+
+ virtual bool RecvProfile(const nsCString& aProfile) override { return true; }
+
+ virtual bool AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
+
+ virtual bool RecvReturnClearSiteData(const NPError& aRv,
+ const uint64_t& aCallbackId) override;
+
+ virtual bool RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
+ const uint64_t& aCallbackId) override;
+
+ void SetPluginFuncs(NPPluginFuncs* aFuncs);
+
+ nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance, uint16_t mode,
+ InfallibleTArray<nsCString>& names,
+ InfallibleTArray<nsCString>& values,
+ NPSavedData* saved, NPError* error);
+
+ // NPP-like API that Gecko calls are trampolined into. These
+ // messages then get forwarded along to the plugin instance,
+ // and then eventually the child process.
+
+ static NPError NPP_Destroy(NPP instance, NPSavedData** save);
+
+ static NPError NPP_SetWindow(NPP instance, NPWindow* window);
+ static NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16_t* stype);
+ static NPError NPP_DestroyStream(NPP instance,
+ NPStream* stream, NPReason reason);
+ static int32_t NPP_WriteReady(NPP instance, NPStream* stream);
+ static int32_t NPP_Write(NPP instance, NPStream* stream,
+ int32_t offset, int32_t len, void* buffer);
+ static void NPP_StreamAsFile(NPP instance,
+ NPStream* stream, const char* fname);
+ static void NPP_Print(NPP instance, NPPrint* platformPrint);
+ static int16_t NPP_HandleEvent(NPP instance, void* event);
+ static void NPP_URLNotify(NPP instance, const char* url,
+ NPReason reason, void* notifyData);
+ static NPError NPP_GetValue(NPP instance,
+ NPPVariable variable, void *ret_value);
+ static NPError NPP_SetValue(NPP instance, NPNVariable variable,
+ void *value);
+ static void NPP_URLRedirectNotify(NPP instance, const char* url,
+ int32_t status, void* notifyData);
+
+ virtual bool HasRequiredFunctions() 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 void DidComposite(NPP aInstance) override;
+ virtual bool IsOOP() override { return true; }
+ virtual nsresult SetBackgroundUnknown(NPP instance) override;
+ virtual nsresult BeginUpdateBackground(NPP instance,
+ const nsIntRect& aRect,
+ DrawTarget** aDrawTarget) override;
+ virtual nsresult EndUpdateBackground(NPP instance,
+ const nsIntRect& aRect) 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;
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) override;
+#else
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override;
+#endif
+ virtual nsresult NP_Shutdown(NPError* error) override;
+
+ virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) override;
+ virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
+ void *aValue, NPError* error) override;
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) override;
+#endif
+ virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
+ uint16_t mode, int16_t argc, char* argn[],
+ char* argv[], NPSavedData* saved,
+ NPError* error) override;
+ virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge,
+ nsCOMPtr<nsIClearSiteDataCallback> callback) override;
+ virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) override;
+
+private:
+ std::map<uint64_t, nsCOMPtr<nsIClearSiteDataCallback>> mClearSiteDataCallbacks;
+ std::map<uint64_t, nsCOMPtr<nsIGetSitesWithDataCallback>> mSitesWithDataCallbacks;
+
+ nsCString mPluginFilename;
+ int mQuirks;
+ void InitQuirksModes(const nsCString& aMimeType);
+
+public:
+
+#if defined(XP_MACOSX)
+ virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) override;
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) override;
+#endif
+
+ void InitAsyncSurrogates();
+
+ layers::TextureClientRecycleAllocator* EnsureTextureAllocatorForDirectBitmap();
+ layers::TextureClientRecycleAllocator* EnsureTextureAllocatorForDXGISurface();
+
+protected:
+ void NotifyFlashHang();
+ void NotifyPluginCrashed();
+ void OnInitFailure();
+ bool MaybeRunDeferredShutdown();
+ bool DoShutdown(NPError* error);
+
+ bool GetSetting(NPNVariable aVariable);
+ void GetSettings(PluginSettings* aSettings);
+
+ bool mIsChrome;
+ bool mShutdown;
+ bool mHadLocalInstance;
+ bool mClearSiteDataSupported;
+ bool mGetSitesWithDataSupported;
+ NPNetscapeFuncs* mNPNIface;
+ NPPluginFuncs* mNPPIface;
+ nsNPAPIPlugin* mPlugin;
+ ipc::TaskFactory<PluginModuleParent> mTaskFactory;
+ nsString mPluginDumpID;
+ nsString mBrowserDumpID;
+ nsString mHangID;
+ RefPtr<nsIObserver> mProfilerObserver;
+ TimeDuration mTimeBlocked;
+ nsCString mPluginName;
+ nsCString mPluginVersion;
+ int32_t mSandboxLevel;
+ bool mIsFlashPlugin;
+
+#ifdef MOZ_X11
+ // Dup of plugin's X socket, used to scope its resources to this
+ // object instead of the plugin process's lifetime
+ ScopedClose mPluginXSocketFdDup;
+#endif
+
+ bool
+ GetPluginDetails();
+
+ friend class mozilla::dom::CrashReporterParent;
+ friend class mozilla::plugins::PluginAsyncSurrogate;
+
+ bool mIsStartingAsync;
+ bool mNPInitialized;
+ bool mIsNPShutdownPending;
+ nsTArray<RefPtr<PluginAsyncSurrogate>> mSurrogateInstances;
+ nsresult mAsyncNewRv;
+ uint32_t mRunID;
+
+ RefPtr<layers::TextureClientRecycleAllocator> mTextureAllocatorForDirectBitmap;
+ RefPtr<layers::TextureClientRecycleAllocator> mTextureAllocatorForDXGISurface;
+};
+
+class PluginModuleContentParent : public PluginModuleParent
+{
+ public:
+ explicit PluginModuleContentParent(bool aAllowAsyncInit);
+
+ static PluginLibrary* LoadModule(uint32_t aPluginId, nsPluginTag* aPluginTag);
+
+ static PluginModuleContentParent* Initialize(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess);
+
+ static void OnLoadPluginResult(const uint32_t& aPluginId, const bool& aResult);
+ static void AssociatePluginId(uint32_t aPluginId, base::ProcessId aProcessId);
+
+ virtual ~PluginModuleContentParent();
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override;
+#endif
+
+ private:
+ virtual bool ShouldContinueFromReplyTimeout() override;
+ virtual void OnExitedSyncSend() override;
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ void OnCrash(DWORD processID) override {}
+#endif
+
+ static PluginModuleContentParent* sSavedModuleParent;
+
+ uint32_t mPluginId;
+};
+
+class PluginModuleChromeParent
+ : public PluginModuleParent
+ , public mozilla::HangMonitor::Annotator
+{
+ public:
+ /**
+ * LoadModule
+ *
+ * This may or may not launch a plugin child process,
+ * and may or may not be very expensive.
+ */
+ static PluginLibrary* LoadModule(const char* aFilePath, uint32_t aPluginId,
+ nsPluginTag* aPluginTag);
+
+ /**
+ * The following two functions are called by SetupBridge to determine
+ * whether an existing plugin module was reused, or whether a new module
+ * was instantiated by the plugin host.
+ */
+ static void ClearInstantiationFlag() { sInstantiated = false; }
+ static bool DidInstantiate() { return sInstantiated; }
+
+ virtual ~PluginModuleChromeParent();
+
+ /*
+ * Takes a full multi-process dump including the plugin process and the
+ * content process. If aBrowserDumpId is not empty then the browser dump
+ * associated with it will be paired to the resulting minidump.
+ * Takes ownership of the file associated with aBrowserDumpId.
+ *
+ * @param aContentPid PID of the e10s content process from which a hang was
+ * reported. May be kInvalidProcessId if not applicable.
+ * @param aBrowserDumpId (optional) previously taken browser dump id. If
+ * provided TakeFullMinidump will use this dump file instead of
+ * generating a new one. If not provided a browser dump will be taken at
+ * the time of this call.
+ * @param aDumpId Returns the ID of the newly generated crash dump. Left
+ * untouched upon failure.
+ */
+ void TakeFullMinidump(base::ProcessId aContentPid,
+ const nsAString& aBrowserDumpId,
+ nsString& aDumpId);
+
+ /*
+ * Terminates the plugin process associated with this plugin module. Also
+ * generates appropriate crash reports unless an existing one is provided.
+ * Takes ownership of the file associated with aDumpId on success.
+ *
+ * @param aMsgLoop the main message pump associated with the module
+ * protocol.
+ * @param aContentPid PID of the e10s content process from which a hang was
+ * reported. May be kInvalidProcessId if not applicable.
+ * @param aMonitorDescription a string describing the hang monitor that
+ * is making this call. This string is added to the crash reporter
+ * annotations for the plugin process.
+ * @param aDumpId (optional) previously taken dump id. If provided
+ * TerminateChildProcess will use this dump file instead of generating a
+ * multi-process crash report. If not provided a multi-process dump will
+ * be taken at the time of this call.
+ */
+ void TerminateChildProcess(MessageLoop* aMsgLoop,
+ base::ProcessId aContentPid,
+ const nsCString& aMonitorDescription,
+ const nsAString& aDumpId);
+
+#ifdef XP_WIN
+ /**
+ * Called by Plugin Hang UI to notify that the user has clicked continue.
+ * Used for chrome hang annotations.
+ */
+ void
+ OnHangUIContinue();
+
+ void
+ EvaluateHangUIState(const bool aReset);
+#endif // XP_WIN
+
+ virtual bool WaitForIPCConnection() override;
+
+ virtual bool
+ RecvNP_InitializeResult(const NPError& aError) override;
+
+ void
+ SetContentParent(dom::ContentParent* aContentParent);
+
+ bool
+ SendAssociatePluginId();
+
+ void CachedSettingChanged();
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ void GatherAsyncProfile();
+ void GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent);
+ void StartProfiler(nsIProfilerStartParams* aParams);
+ void StopProfiler();
+#endif
+
+ virtual bool
+ RecvProfile(const nsCString& aProfile) override;
+
+ virtual bool
+ AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
+
+private:
+ virtual void
+ EnteredCxxStack() override;
+
+ void
+ ExitedCxxStack() override;
+
+ mozilla::ipc::IProtocol* GetInvokingProtocol();
+ PluginInstanceParent* GetManagingInstance(mozilla::ipc::IProtocol* aProtocol);
+
+ virtual void
+ AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) override;
+
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+#ifdef MOZ_CRASHREPORTER
+ void ProcessFirstMinidump();
+ void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
+#endif
+
+ virtual PCrashReporterParent*
+ AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+ uint32_t* processType) override;
+ virtual bool
+ DeallocPCrashReporterParent(PCrashReporterParent* actor) override;
+
+ PluginProcessParent* Process() const { return mSubprocess; }
+ base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) override;
+#else
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override;
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) override;
+#endif
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ // aFilePath is UTF8, not native!
+ explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId,
+ int32_t aSandboxLevel,
+ bool aAllowAsyncInit);
+
+ CrashReporterParent* CrashReporter();
+
+ void CleanupFromTimeout(const bool aByHangUI);
+
+ virtual void UpdatePluginTimeout() override;
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ void InitPluginProfiling();
+ void ShutdownPluginProfiling();
+#endif
+
+ void RegisterSettingsCallbacks();
+ void UnregisterSettingsCallbacks();
+
+ virtual bool RecvNotifyContentModuleDestroyed() override;
+
+ static void CachedSettingChanged(const char* aPref, void* aModule);
+
+ virtual bool
+ AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
+ const bool& shouldRegister,
+ NPError* result) override;
+
+ PluginProcessParent* mSubprocess;
+ uint32_t mPluginId;
+
+ ipc::TaskFactory<PluginModuleChromeParent> mChromeTaskFactory;
+
+ enum HangAnnotationFlags
+ {
+ kInPluginCall = (1u << 0),
+ kHangUIShown = (1u << 1),
+ kHangUIContinued = (1u << 2),
+ kHangUIDontShow = (1u << 3)
+ };
+ Atomic<uint32_t> mHangAnnotationFlags;
+#ifdef XP_WIN
+ InfallibleTArray<float> mPluginCpuUsageOnHang;
+ PluginHangUIParent *mHangUIParent;
+ bool mHangUIEnabled;
+ bool mIsTimerReset;
+#ifdef MOZ_CRASHREPORTER
+ /**
+ * This mutex protects the crash reporter when the Plugin Hang UI event
+ * handler is executing off main thread. It is intended to protect both
+ * the mCrashReporter variable in addition to the CrashReporterParent object
+ * that mCrashReporter refers to.
+ */
+ mozilla::Mutex mCrashReporterMutex;
+ CrashReporterParent* mCrashReporter;
+#endif // MOZ_CRASHREPORTER
+
+
+ /**
+ * Launches the Plugin Hang UI.
+ *
+ * @return true if plugin-hang-ui.exe has been successfully launched.
+ * false if the Plugin Hang UI is disabled, already showing,
+ * or the launch failed.
+ */
+ bool
+ LaunchHangUI();
+
+ /**
+ * Finishes the Plugin Hang UI and cancels if it is being shown to the user.
+ */
+ void
+ FinishHangUI();
+#endif
+
+ friend class mozilla::dom::CrashReporterParent;
+ friend class mozilla::plugins::PluginAsyncSurrogate;
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ friend class mozilla::plugins::FinishInjectorInitTask;
+
+ void InitializeInjector();
+ void DoInjection(const nsAutoHandle& aSnapshot);
+ static DWORD WINAPI GetToolhelpSnapshot(LPVOID aContext);
+
+ void OnCrash(DWORD processID) override;
+
+ DWORD mFlashProcess1;
+ DWORD mFlashProcess2;
+ RefPtr<mozilla::plugins::FinishInjectorInitTask> mFinishInitTask;
+#endif
+
+ void OnProcessLaunched(const bool aSucceeded);
+
+ class LaunchedTask : public LaunchCompleteTask
+ {
+ public:
+ explicit LaunchedTask(PluginModuleChromeParent* aModule)
+ : mModule(aModule)
+ {
+ MOZ_ASSERT(aModule);
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mModule->OnProcessLaunched(mLaunchSucceeded);
+ return NS_OK;
+ }
+
+ private:
+ PluginModuleChromeParent* mModule;
+ };
+
+ friend class LaunchedTask;
+
+ bool mInitOnAsyncConnect;
+ nsresult mAsyncInitRv;
+ NPError mAsyncInitError;
+ // mContentParent is to be used ONLY during the IPC dance that occurs
+ // when ContentParent::RecvLoadPlugin is called under async plugin init!
+ // In other contexts it is *unsafe*, as there might be multiple content
+ // processes in existence!
+ dom::ContentParent* mContentParent;
+ nsCOMPtr<nsIObserver> mPluginOfflineObserver;
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ RefPtr<mozilla::ProfileGatherer> mGatherer;
+#endif
+ nsCString mProfile;
+ bool mIsBlocklisted;
+ static bool sInstantiated;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginModuleParent_h
diff --git a/dom/plugins/ipc/PluginProcessChild.cpp b/dom/plugins/ipc/PluginProcessChild.cpp
new file mode 100644
index 000000000..eb698e8af
--- /dev/null
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -0,0 +1,149 @@
+/* -*- 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/. */
+
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/plugins/PluginProcessChild.h"
+
+#include "prlink.h"
+
+#include "base/command_line.h"
+#include "base/string_util.h"
+#include "nsDebugImpl.h"
+
+#if defined(XP_MACOSX)
+#include "nsCocoaFeatures.h"
+// An undocumented CoreGraphics framework method, present in the same form
+// since at least OS X 10.5.
+extern "C" CGError CGSSetDebugOptions(int options);
+#endif
+
+#ifdef XP_WIN
+bool ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath);
+#if defined(MOZ_SANDBOX)
+#define TARGET_SANDBOX_EXPORTS
+#include "mozilla/sandboxTarget.h"
+#endif
+#endif
+
+using mozilla::ipc::IOThreadChild;
+
+#ifdef OS_WIN
+#include "nsSetDllDirectory.h"
+#include <algorithm>
+#endif
+
+namespace mozilla {
+namespace plugins {
+
+
+bool
+PluginProcessChild::Init()
+{
+ nsDebugImpl::SetMultiprocessMode("NPAPI");
+
+#if defined(XP_MACOSX)
+ // Remove the trigger for "dyld interposing" that we added in
+ // GeckoChildProcessHost::PerformAsyncLaunchInternal(), in the host
+ // process just before we were launched. Dyld interposing will still
+ // happen in our process (the plugin child process). But we don't want
+ // it to happen in any processes that the plugin might launch from our
+ // process.
+ nsCString interpose(PR_GetEnv("DYLD_INSERT_LIBRARIES"));
+ if (!interpose.IsEmpty()) {
+ // If we added the path to libplugin_child_interpose.dylib to an
+ // existing DYLD_INSERT_LIBRARIES, we appended it to the end, after a
+ // ":" path seperator.
+ int32_t lastSeparatorPos = interpose.RFind(":");
+ int32_t lastTriggerPos = interpose.RFind("libplugin_child_interpose.dylib");
+ bool needsReset = false;
+ if (lastTriggerPos != -1) {
+ if (lastSeparatorPos == -1) {
+ interpose.Truncate();
+ needsReset = true;
+ } else if (lastTriggerPos > lastSeparatorPos) {
+ interpose.SetLength(lastSeparatorPos);
+ needsReset = true;
+ }
+ }
+ if (needsReset) {
+ nsCString setInterpose("DYLD_INSERT_LIBRARIES=");
+ if (!interpose.IsEmpty()) {
+ setInterpose.Append(interpose);
+ }
+ // Values passed to PR_SetEnv() must be seperately allocated.
+ char* setInterposePtr = strdup(setInterpose.get());
+ PR_SetEnv(setInterposePtr);
+ }
+ }
+#endif
+
+ // Certain plugins, such as flash, steal the unhandled exception filter
+ // thus we never get crash reports when they fault. This call fixes it.
+ message_loop()->set_exception_restoration(true);
+
+ std::string pluginFilename;
+
+#if defined(OS_POSIX)
+ // NB: need to be very careful in ensuring that the first arg
+ // (after the binary name) here is indeed the plugin module path.
+ // Keep in sync with dom/plugins/PluginModuleParent.
+ std::vector<std::string> values = CommandLine::ForCurrentProcess()->argv();
+ MOZ_ASSERT(values.size() >= 2, "not enough args");
+
+ pluginFilename = UnmungePluginDsoPath(values[1]);
+
+#elif defined(OS_WIN)
+ std::vector<std::wstring> values =
+ CommandLine::ForCurrentProcess()->GetLooseValues();
+ MOZ_ASSERT(values.size() >= 1, "not enough loose args");
+
+ if (ShouldProtectPluginCurrentDirectory(values[0].c_str())) {
+ SanitizeEnvironmentVariables();
+ SetDllDirectory(L"");
+ }
+
+ pluginFilename = WideToUTF8(values[0]);
+
+#if defined(MOZ_SANDBOX)
+ // This is probably the earliest we would want to start the sandbox.
+ // As we attempt to tighten the sandbox, we may need to consider moving this
+ // to later in the plugin initialization.
+ mozilla::SandboxTarget::Instance()->StartSandbox();
+#endif
+#else
+# error Sorry
+#endif
+
+ if (NS_FAILED(nsRegion::InitStatic())) {
+ NS_ERROR("Could not initialize nsRegion");
+ return false;
+ }
+
+ bool retval = mPlugin.InitForChrome(pluginFilename, ParentPid(),
+ IOThreadChild::message_loop(),
+ IOThreadChild::channel());
+#if defined(XP_MACOSX)
+ if (nsCocoaFeatures::OnYosemiteOrLater()) {
+ // Explicitly turn off CGEvent logging. This works around bug 1092855.
+ // If there are already CGEvents in the log, turning off logging also
+ // causes those events to be written to disk. But at this point no
+ // CGEvents have yet been processed. CGEvents are events (usually
+ // input events) pulled from the WindowServer. An option of 0x80000008
+ // turns on CGEvent logging.
+ CGSSetDebugOptions(0x80000007);
+ }
+#endif
+ return retval;
+}
+
+void
+PluginProcessChild::CleanUp()
+{
+ nsRegion::ShutdownStatic();
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginProcessChild.h b/dom/plugins/ipc/PluginProcessChild.h
new file mode 100644
index 000000000..4a077ed2f
--- /dev/null
+++ b/dom/plugins/ipc/PluginProcessChild.h
@@ -0,0 +1,55 @@
+/* -*- 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 dom_plugins_PluginProcessChild_h
+#define dom_plugins_PluginProcessChild_h 1
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/plugins/PluginModuleChild.h"
+
+#if defined(XP_WIN)
+#include "mozilla/mscom/MainThreadRuntime.h"
+#endif
+
+namespace mozilla {
+namespace plugins {
+//-----------------------------------------------------------------------------
+
+class PluginProcessChild : public mozilla::ipc::ProcessChild {
+protected:
+ typedef mozilla::ipc::ProcessChild ProcessChild;
+
+public:
+ explicit PluginProcessChild(ProcessId aParentPid)
+ : ProcessChild(aParentPid), mPlugin(true)
+ { }
+
+ virtual ~PluginProcessChild()
+ { }
+
+ virtual bool Init() override;
+ virtual void CleanUp() override;
+
+protected:
+ static PluginProcessChild* current() {
+ return static_cast<PluginProcessChild*>(ProcessChild::current());
+ }
+
+private:
+#if defined(XP_WIN)
+ /* Drag-and-drop and Silverlight depend on the host initializing COM.
+ * This object initializes and configures COM. */
+ mozilla::mscom::MainThreadRuntime mCOMRuntime;
+#endif
+ PluginModuleChild mPlugin;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PluginProcessChild);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // ifndef dom_plugins_PluginProcessChild_h
diff --git a/dom/plugins/ipc/PluginProcessParent.cpp b/dom/plugins/ipc/PluginProcessParent.cpp
new file mode 100644
index 000000000..2a73bce51
--- /dev/null
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -0,0 +1,257 @@
+/* -*- 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/. */
+
+#include "mozilla/plugins/PluginProcessParent.h"
+
+#include "base/string_util.h"
+#include "base/process_util.h"
+
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/plugins/PluginMessageUtils.h"
+#include "mozilla/Telemetry.h"
+#include "nsThreadUtils.h"
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+#include "nsDirectoryServiceDefs.h"
+#endif
+
+using std::vector;
+using std::string;
+
+using mozilla::ipc::BrowserProcessSubThread;
+using mozilla::ipc::GeckoChildProcessHost;
+using mozilla::plugins::LaunchCompleteTask;
+using mozilla::plugins::PluginProcessParent;
+using base::ProcessArchitecture;
+
+PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath) :
+ GeckoChildProcessHost(GeckoProcessType_Plugin),
+ mPluginFilePath(aPluginFilePath),
+ mTaskFactory(this),
+ mMainMsgLoop(MessageLoop::current()),
+ mRunCompleteTaskImmediately(false)
+{
+}
+
+PluginProcessParent::~PluginProcessParent()
+{
+}
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+static void
+AddSandboxAllowedFile(vector<std::wstring>& aAllowedFiles, nsIProperties* aDirSvc,
+ const char* aDir, const nsAString& aSuffix = EmptyString())
+{
+ nsCOMPtr<nsIFile> userDir;
+ nsresult rv = aDirSvc->Get(aDir, NS_GET_IID(nsIFile), getter_AddRefs(userDir));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsAutoString userDirPath;
+ rv = userDir->GetPath(userDirPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ if (!aSuffix.IsEmpty()) {
+ userDirPath.Append(aSuffix);
+ }
+ aAllowedFiles.push_back(std::wstring(userDirPath.get()));
+ return;
+}
+
+static void
+AddSandboxAllowedFiles(int32_t aSandboxLevel,
+ vector<std::wstring>& aAllowedFilesRead,
+ vector<std::wstring>& aAllowedFilesReadWrite,
+ vector<std::wstring>& aAllowedDirectories)
+{
+ if (aSandboxLevel < 2) {
+ return;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIProperties> dirSvc =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // Higher than level 2 currently removes the users own rights.
+ if (aSandboxLevel > 2) {
+ AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR);
+ AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR,
+ NS_LITERAL_STRING("\\*"));
+ }
+
+ // Level 2 and above is now using low integrity, so we need to give write
+ // access to the Flash directories.
+ // This should be made Flash specific (Bug 1171396).
+ AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
+ AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_LOCAL_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
+ AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Adobe\\Flash Player\\*"));
+
+ // Access also has to be given to create the parent directories as they may
+ // not exist.
+ AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Macromedia"));
+ AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Macromedia\\Flash Player"));
+ AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_LOCAL_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Macromedia"));
+ AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_LOCAL_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Macromedia\\Flash Player"));
+ AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Adobe"));
+ AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
+ NS_LITERAL_STRING("\\Adobe\\Flash Player"));
+}
+#endif
+
+bool
+PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchCompleteTask,
+ int32_t aSandboxLevel)
+{
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ mSandboxLevel = aSandboxLevel;
+ AddSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead,
+ mAllowedFilesReadWrite, mAllowedDirectories);
+#else
+ if (aSandboxLevel != 0) {
+ MOZ_ASSERT(false,
+ "Can't enable an NPAPI process sandbox for platform/build.");
+ }
+#endif
+
+ ProcessArchitecture currentArchitecture = base::GetCurrentProcessArchitecture();
+ uint32_t containerArchitectures = GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
+
+ uint32_t pluginLibArchitectures = currentArchitecture;
+#ifdef XP_MACOSX
+ nsresult rv = GetArchitecturesForBinary(mPluginFilePath.c_str(), &pluginLibArchitectures);
+ if (NS_FAILED(rv)) {
+ // If the call failed just assume that we want the current architecture.
+ pluginLibArchitectures = currentArchitecture;
+ }
+#endif
+
+ ProcessArchitecture selectedArchitecture = currentArchitecture;
+ if (!(pluginLibArchitectures & containerArchitectures & currentArchitecture)) {
+ // Prefererence in order: x86_64, i386, PPC. The only particularly important thing
+ // about this order is that we'll prefer 64-bit architectures first.
+ if (base::PROCESS_ARCH_X86_64 & pluginLibArchitectures & containerArchitectures) {
+ selectedArchitecture = base::PROCESS_ARCH_X86_64;
+ }
+ else if (base::PROCESS_ARCH_I386 & pluginLibArchitectures & containerArchitectures) {
+ selectedArchitecture = base::PROCESS_ARCH_I386;
+ }
+ else if (base::PROCESS_ARCH_PPC & pluginLibArchitectures & containerArchitectures) {
+ selectedArchitecture = base::PROCESS_ARCH_PPC;
+ }
+ else if (base::PROCESS_ARCH_ARM & pluginLibArchitectures & containerArchitectures) {
+ selectedArchitecture = base::PROCESS_ARCH_ARM;
+ }
+ else if (base::PROCESS_ARCH_MIPS & pluginLibArchitectures & containerArchitectures) {
+ selectedArchitecture = base::PROCESS_ARCH_MIPS;
+ }
+ else {
+ return false;
+ }
+ }
+
+ mLaunchCompleteTask = mozilla::Move(aLaunchCompleteTask);
+
+ vector<string> args;
+ args.push_back(MungePluginDsoPath(mPluginFilePath));
+
+ bool result = AsyncLaunch(args, selectedArchitecture);
+ if (!result) {
+ mLaunchCompleteTask = nullptr;
+ }
+ return result;
+}
+
+void
+PluginProcessParent::Delete()
+{
+ MessageLoop* currentLoop = MessageLoop::current();
+ MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+
+ if (currentLoop == ioLoop) {
+ delete this;
+ return;
+ }
+
+ ioLoop->PostTask(NewNonOwningRunnableMethod(this, &PluginProcessParent::Delete));
+}
+
+void
+PluginProcessParent::SetCallRunnableImmediately(bool aCallImmediately)
+{
+ mRunCompleteTaskImmediately = aCallImmediately;
+}
+
+/**
+ * This function exists so that we may provide an additional level of
+ * indirection between the task being posted to main event loop (a
+ * RunnableMethod) and the launch complete task itself. This is needed
+ * for cases when both WaitUntilConnected or OnChannel* race to invoke the
+ * task.
+ */
+void
+PluginProcessParent::RunLaunchCompleteTask()
+{
+ if (mLaunchCompleteTask) {
+ mLaunchCompleteTask->Run();
+ mLaunchCompleteTask = nullptr;
+ }
+}
+
+bool
+PluginProcessParent::WaitUntilConnected(int32_t aTimeoutMs)
+{
+ bool result = GeckoChildProcessHost::WaitUntilConnected(aTimeoutMs);
+ if (mRunCompleteTaskImmediately && mLaunchCompleteTask) {
+ if (result) {
+ mLaunchCompleteTask->SetLaunchSucceeded();
+ }
+ RunLaunchCompleteTask();
+ }
+ return result;
+}
+
+void
+PluginProcessParent::OnChannelConnected(int32_t peer_pid)
+{
+ GeckoChildProcessHost::OnChannelConnected(peer_pid);
+ if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
+ mLaunchCompleteTask->SetLaunchSucceeded();
+ mMainMsgLoop->PostTask(mTaskFactory.NewRunnableMethod(
+ &PluginProcessParent::RunLaunchCompleteTask));
+ }
+}
+
+void
+PluginProcessParent::OnChannelError()
+{
+ GeckoChildProcessHost::OnChannelError();
+ if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
+ mMainMsgLoop->PostTask(mTaskFactory.NewRunnableMethod(
+ &PluginProcessParent::RunLaunchCompleteTask));
+ }
+}
+
+bool
+PluginProcessParent::IsConnected()
+{
+ mozilla::MonitorAutoLock lock(mMonitor);
+ return mProcessState == PROCESS_CONNECTED;
+}
+
diff --git a/dom/plugins/ipc/PluginProcessParent.h b/dom/plugins/ipc/PluginProcessParent.h
new file mode 100644
index 000000000..b93e14bf6
--- /dev/null
+++ b/dom/plugins/ipc/PluginProcessParent.h
@@ -0,0 +1,94 @@
+/* -*- 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 dom_plugins_PluginProcessParent_h
+#define dom_plugins_PluginProcessParent_h 1
+
+#include "mozilla/Attributes.h"
+#include "base/basictypes.h"
+
+#include "base/file_path.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "chrome/common/child_process_host.h"
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/TaskFactory.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsIRunnable.h"
+
+namespace mozilla {
+namespace plugins {
+
+class LaunchCompleteTask : public Runnable
+{
+public:
+ LaunchCompleteTask()
+ : mLaunchSucceeded(false)
+ {
+ }
+
+ void SetLaunchSucceeded() { mLaunchSucceeded = true; }
+
+protected:
+ bool mLaunchSucceeded;
+};
+
+class PluginProcessParent : public mozilla::ipc::GeckoChildProcessHost
+{
+public:
+ explicit PluginProcessParent(const std::string& aPluginFilePath);
+ ~PluginProcessParent();
+
+ /**
+ * Launch the plugin process. If the process fails to launch,
+ * this method will return false.
+ *
+ * @param aLaunchCompleteTask Task that is executed on the main
+ * thread once the asynchonous launch has completed.
+ * @param aSandboxLevel Determines the strength of the sandbox.
+ * <= 0 means no sandbox.
+ */
+ bool Launch(UniquePtr<LaunchCompleteTask> aLaunchCompleteTask = UniquePtr<LaunchCompleteTask>(),
+ int32_t aSandboxLevel = 0);
+
+ void Delete();
+
+ virtual bool CanShutdown() override
+ {
+ return true;
+ }
+
+ const std::string& GetPluginFilePath() { return mPluginFilePath; }
+
+ using mozilla::ipc::GeckoChildProcessHost::GetChannel;
+
+ void SetCallRunnableImmediately(bool aCallImmediately);
+ virtual bool WaitUntilConnected(int32_t aTimeoutMs = 0) override;
+
+ virtual void OnChannelConnected(int32_t peer_pid) override;
+ virtual void OnChannelError() override;
+
+ bool IsConnected();
+
+private:
+ void RunLaunchCompleteTask();
+
+ std::string mPluginFilePath;
+ ipc::TaskFactory<PluginProcessParent> mTaskFactory;
+ UniquePtr<LaunchCompleteTask> mLaunchCompleteTask;
+ MessageLoop* mMainMsgLoop;
+ bool mRunCompleteTaskImmediately;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PluginProcessParent);
+};
+
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // ifndef dom_plugins_PluginProcessParent_h
diff --git a/dom/plugins/ipc/PluginQuirks.cpp b/dom/plugins/ipc/PluginQuirks.cpp
new file mode 100644
index 000000000..c60cf8836
--- /dev/null
+++ b/dom/plugins/ipc/PluginQuirks.cpp
@@ -0,0 +1,78 @@
+/* -*- 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/. */
+
+#include "PluginQuirks.h"
+
+#include "nsPluginHost.h"
+
+namespace mozilla {
+namespace plugins {
+
+int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
+ const nsCString& aPluginFilename)
+{
+ int quirks = 0;
+
+ nsPluginHost::SpecialType specialType = nsPluginHost::GetSpecialType(aMimeType);
+
+ if (specialType == nsPluginHost::eSpecialType_Silverlight) {
+ quirks |= QUIRK_SILVERLIGHT_DEFAULT_TRANSPARENT;
+#ifdef OS_WIN
+ quirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
+ quirks |= QUIRK_SILVERLIGHT_FOCUS_CHECK_PARENT;
+#endif
+ }
+
+ if (specialType == nsPluginHost::eSpecialType_Flash) {
+ quirks |= QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN;
+#ifdef OS_WIN
+ quirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
+ quirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS;
+ quirks |= QUIRK_FLASH_HOOK_SETLONGPTR;
+ quirks |= QUIRK_FLASH_HOOK_GETWINDOWINFO;
+ quirks |= QUIRK_FLASH_FIXUP_MOUSE_CAPTURE;
+ quirks |= QUIRK_WINLESS_HOOK_IME;
+#if defined(_M_X64) || defined(__x86_64__)
+ quirks |= QUIRK_FLASH_HOOK_GETKEYSTATE;
+#endif
+#endif
+ }
+
+#ifdef OS_WIN
+ // QuickTime plugin usually loaded with audio/mpeg mimetype
+ NS_NAMED_LITERAL_CSTRING(quicktime, "npqtplugin");
+ if (FindInReadable(quicktime, aPluginFilename)) {
+ quirks |= QUIRK_QUICKTIME_AVOID_SETWINDOW;
+ }
+#endif
+
+#ifdef XP_MACOSX
+ // Whitelist Flash and Quicktime to support offline renderer
+ NS_NAMED_LITERAL_CSTRING(quicktime, "QuickTime Plugin.plugin");
+ if (specialType == nsPluginHost::eSpecialType_Flash) {
+ quirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
+ } else if (FindInReadable(quicktime, aPluginFilename)) {
+ quirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
+ }
+#endif
+
+#ifdef OS_WIN
+ if (specialType == nsPluginHost::eSpecialType_Unity) {
+ quirks |= QUIRK_UNITY_FIXUP_MOUSE_CAPTURE;
+ }
+#endif
+
+#ifdef OS_WIN
+ if (specialType == nsPluginHost::eSpecialType_Test) {
+ quirks |= QUIRK_WINLESS_HOOK_IME;
+ }
+#endif
+
+ return quirks;
+}
+
+} /* namespace plugins */
+} /* namespace mozilla */
diff --git a/dom/plugins/ipc/PluginQuirks.h b/dom/plugins/ipc/PluginQuirks.h
new file mode 100644
index 000000000..f0a6b6a30
--- /dev/null
+++ b/dom/plugins/ipc/PluginQuirks.h
@@ -0,0 +1,68 @@
+/* -*- 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 dom_plugins_PluginQuirks_h
+#define dom_plugins_PluginQuirks_h
+
+namespace mozilla {
+namespace plugins {
+
+// Quirks mode support for various plugin mime types
+enum PluginQuirks {
+ QUIRKS_NOT_INITIALIZED = 0,
+ // Silverlight assumes it is transparent in windowless mode. This quirk
+ // matches the logic in nsNPAPIPluginInstance::SetWindowless.
+ QUIRK_SILVERLIGHT_DEFAULT_TRANSPARENT = 1 << 0,
+ // Win32: Hook TrackPopupMenu api so that we can swap out parent
+ // hwnds. The api will fail with parents not associated with our
+ // child ui thread. See WinlessHandleEvent for details.
+ QUIRK_WINLESS_TRACKPOPUP_HOOK = 1 << 1,
+ // Win32: Throttle flash WM_USER+1 heart beat messages to prevent
+ // flooding chromium's dispatch loop, which can cause ipc traffic
+ // processing lag.
+ QUIRK_FLASH_THROTTLE_WMUSER_EVENTS = 1 << 2,
+ // Win32: Catch resets on our subclass by hooking SetWindowLong.
+ QUIRK_FLASH_HOOK_SETLONGPTR = 1 << 3,
+ // X11: 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.
+ QUIRK_FLASH_EXPOSE_COORD_TRANSLATION = 1 << 4,
+ // Win32: Catch get window info calls on the browser and tweak the
+ // results so mouse input works when flash is displaying it's settings
+ // window.
+ QUIRK_FLASH_HOOK_GETWINDOWINFO = 1 << 5,
+ // Win: Addresses a flash bug with mouse capture and full screen
+ // windows.
+ QUIRK_FLASH_FIXUP_MOUSE_CAPTURE = 1 << 6,
+ // Win: QuickTime steals focus on SetWindow calls even if it's hidden.
+ // Avoid calling SetWindow in that case.
+ QUIRK_QUICKTIME_AVOID_SETWINDOW = 1 << 7,
+ // Win: Check to make sure the parent window has focus before calling
+ // set focus on the child. Addresses a full screen dialog prompt
+ // problem in Silverlight.
+ QUIRK_SILVERLIGHT_FOCUS_CHECK_PARENT = 1 << 8,
+ // Mac: Allow the plugin to use offline renderer mode.
+ // Use this only if the plugin is certified the support the offline renderer.
+ QUIRK_ALLOW_OFFLINE_RENDERER = 1 << 9,
+ // Work around a Flash bug where it fails to check the error code of a
+ // NPN_GetValue(NPNVdocumentOrigin) call before trying to dereference
+ // its char* output.
+ QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN = 1 << 10,
+ // Win: Addresses a Unity bug with mouse capture.
+ QUIRK_UNITY_FIXUP_MOUSE_CAPTURE = 1 << 11,
+ // Win: Hook IMM32 API to handle IME event on windowless plugin
+ QUIRK_WINLESS_HOOK_IME = 1 << 12,
+ // Win: Hook GetKeyState to get keyboard state on sandbox process
+ QUIRK_FLASH_HOOK_GETKEYSTATE = 1 << 13,
+};
+
+int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
+ const nsCString& aPluginFilename);
+
+} /* namespace plugins */
+} /* namespace mozilla */
+
+#endif // ifndef dom_plugins_PluginQuirks_h
diff --git a/dom/plugins/ipc/PluginScriptableObjectChild.cpp b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
new file mode 100644
index 000000000..371189170
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -0,0 +1,1298 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 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 "PluginScriptableObjectChild.h"
+#include "PluginScriptableObjectUtils.h"
+#include "mozilla/plugins/PluginTypes.h"
+
+using namespace mozilla::plugins;
+
+/**
+ * NPIdentifiers in the plugin process use a tagged representation. The low bit
+ * stores the tag. If it's zero, the identifier is a string, and the value is a
+ * pointer to a StoredIdentifier. If the tag bit is 1, then the rest of the
+ * NPIdentifier value is the integer itself. Like the JSAPI, we require that all
+ * integers stored in NPIdentifier be non-negative.
+ *
+ * String identifiers are stored in the sIdentifiers hashtable to ensure
+ * uniqueness. The lifetime of these identifiers is only as long as the incoming
+ * IPC call from the chrome process. If the plugin wants to retain an
+ * identifier, it needs to call NPN_GetStringIdentifier, which causes the
+ * mPermanent flag to be set on the identifier. When this flag is set, the
+ * identifier is saved until the plugin process exits.
+ *
+ * The StackIdentifier RAII class is used to manage ownership of
+ * identifiers. Any identifier obtained from this class should not be used
+ * outside its scope, except when the MakePermanent() method has been called on
+ * it.
+ *
+ * The lifetime of an NPIdentifier in the plugin process is totally divorced
+ * from the lifetime of an NPIdentifier in the chrome process (where an
+ * NPIdentifier is stored as a jsid). The JS GC in the chrome process is able to
+ * trace through the entire heap, unlike in the plugin process, so there is no
+ * reason to retain identifiers there.
+ */
+
+PluginScriptableObjectChild::IdentifierTable PluginScriptableObjectChild::sIdentifiers;
+
+/* static */ PluginScriptableObjectChild::StoredIdentifier*
+PluginScriptableObjectChild::HashIdentifier(const nsCString& aIdentifier)
+{
+ StoredIdentifier* stored = sIdentifiers.Get(aIdentifier).get();
+ if (stored) {
+ return stored;
+ }
+
+ stored = new StoredIdentifier(aIdentifier);
+ sIdentifiers.Put(aIdentifier, stored);
+ return stored;
+}
+
+/* static */ void
+PluginScriptableObjectChild::UnhashIdentifier(StoredIdentifier* aStored)
+{
+ MOZ_ASSERT(sIdentifiers.Get(aStored->mIdentifier));
+ sIdentifiers.Remove(aStored->mIdentifier);
+}
+
+/* static */ void
+PluginScriptableObjectChild::ClearIdentifiers()
+{
+ sIdentifiers.Clear();
+}
+
+PluginScriptableObjectChild::StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier)
+: mIdentifier(aIdentifier),
+ mStored(nullptr)
+{
+ if (aIdentifier.type() == PluginIdentifier::TnsCString) {
+ mStored = PluginScriptableObjectChild::HashIdentifier(mIdentifier.get_nsCString());
+ }
+}
+
+PluginScriptableObjectChild::StackIdentifier::StackIdentifier(NPIdentifier aIdentifier)
+: mStored(nullptr)
+{
+ uintptr_t bits = reinterpret_cast<uintptr_t>(aIdentifier);
+ if (bits & 1) {
+ int32_t num = int32_t(bits >> 1);
+ mIdentifier = PluginIdentifier(num);
+ } else {
+ mStored = static_cast<StoredIdentifier*>(aIdentifier);
+ mIdentifier = mStored->mIdentifier;
+ }
+}
+
+PluginScriptableObjectChild::StackIdentifier::~StackIdentifier()
+{
+ if (!mStored) {
+ return;
+ }
+
+ // Each StackIdentifier owns one reference to its StoredIdentifier. In
+ // addition, the sIdentifiers table owns a reference. If mPermanent is false
+ // and sIdentifiers has the last reference, then we want to remove the
+ // StoredIdentifier from the table (and destroy it).
+ StoredIdentifier *stored = mStored;
+ mStored = nullptr;
+ if (stored->mRefCnt == 1 && !stored->mPermanent) {
+ PluginScriptableObjectChild::UnhashIdentifier(stored);
+ }
+}
+
+NPIdentifier
+PluginScriptableObjectChild::StackIdentifier::ToNPIdentifier() const
+{
+ if (mStored) {
+ MOZ_ASSERT(mIdentifier.type() == PluginIdentifier::TnsCString);
+ MOZ_ASSERT((reinterpret_cast<uintptr_t>(mStored.get()) & 1) == 0);
+ return mStored;
+ }
+
+ int32_t num = mIdentifier.get_int32_t();
+ // The JS engine imposes this condition on int32s in jsids, so we assume it.
+ MOZ_ASSERT(num >= 0);
+ return reinterpret_cast<NPIdentifier>((num << 1) | 1);
+}
+
+static PluginIdentifier
+FromNPIdentifier(NPIdentifier aIdentifier)
+{
+ PluginScriptableObjectChild::StackIdentifier stack(aIdentifier);
+ return stack.GetIdentifier();
+}
+
+// static
+NPObject*
+PluginScriptableObjectChild::ScriptableAllocate(NPP aInstance,
+ NPClass* aClass)
+{
+ AssertPluginThread();
+
+ if (aClass != GetClass()) {
+ NS_RUNTIMEABORT("Huh?! Wrong class!");
+ }
+
+ return new ChildNPObject();
+}
+
+// static
+void
+PluginScriptableObjectChild::ScriptableInvalidate(NPObject* aObject)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ // This can happen more than once, and is just fine.
+ return;
+ }
+
+ object->invalidated = true;
+}
+
+// static
+void
+PluginScriptableObjectChild::ScriptableDeallocate(NPObject* aObject)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ PluginScriptableObjectChild* actor = object->parent;
+ if (actor) {
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+ actor->DropNPObject();
+ }
+
+ delete object;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableHasMethod(NPObject* aObject,
+ NPIdentifier aName)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ bool result;
+ actor->CallHasMethod(FromNPIdentifier(aName), &result);
+
+ return result;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableInvoke(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+ if (!args.IsOk()) {
+ NS_ERROR("Failed to convert arguments!");
+ return false;
+ }
+
+ Variant remoteResult;
+ bool success;
+ actor->CallInvoke(FromNPIdentifier(aName), args,
+ &remoteResult, &success);
+
+ if (!success) {
+ return false;
+ }
+
+ ConvertToVariant(remoteResult, *aResult);
+ return true;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableInvokeDefault(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+ if (!args.IsOk()) {
+ NS_ERROR("Failed to convert arguments!");
+ return false;
+ }
+
+ Variant remoteResult;
+ bool success;
+ actor->CallInvokeDefault(args, &remoteResult, &success);
+
+ if (!success) {
+ return false;
+ }
+
+ ConvertToVariant(remoteResult, *aResult);
+ return true;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableHasProperty(NPObject* aObject,
+ NPIdentifier aName)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ bool result;
+ actor->CallHasProperty(FromNPIdentifier(aName), &result);
+
+ return result;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableGetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ NPVariant* aResult)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ PluginInstanceChild::AutoStackHelper guard(actor->mInstance);
+
+ Variant result;
+ bool success;
+ actor->CallGetParentProperty(FromNPIdentifier(aName),
+ &result, &success);
+
+ if (!success) {
+ return false;
+ }
+
+ ConvertToVariant(result, *aResult);
+ return true;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableSetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aValue)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ ProtectedVariant value(*aValue, actor->GetInstance());
+ if (!value.IsOk()) {
+ NS_WARNING("Failed to convert variant!");
+ return false;
+ }
+
+ bool success;
+ actor->CallSetProperty(FromNPIdentifier(aName), value,
+ &success);
+
+ return success;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableRemoveProperty(NPObject* aObject,
+ NPIdentifier aName)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ bool success;
+ actor->CallRemoveProperty(FromNPIdentifier(aName),
+ &success);
+
+ return success;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableEnumerate(NPObject* aObject,
+ NPIdentifier** aIdentifiers,
+ uint32_t* aCount)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ AutoTArray<PluginIdentifier, 10> identifiers;
+ bool success;
+ actor->CallEnumerate(&identifiers, &success);
+
+ if (!success) {
+ return false;
+ }
+
+ *aCount = identifiers.Length();
+ if (!*aCount) {
+ *aIdentifiers = nullptr;
+ return true;
+ }
+
+ *aIdentifiers = reinterpret_cast<NPIdentifier*>(
+ PluginModuleChild::sBrowserFuncs.memalloc(*aCount * sizeof(NPIdentifier)));
+ if (!*aIdentifiers) {
+ NS_ERROR("Out of memory!");
+ return false;
+ }
+
+ for (uint32_t index = 0; index < *aCount; index++) {
+ StackIdentifier id(identifiers[index]);
+ // Make the id permanent in case the plugin retains it.
+ id.MakePermanent();
+ (*aIdentifiers)[index] = id.ToNPIdentifier();
+ }
+ return true;
+}
+
+// static
+bool
+PluginScriptableObjectChild::ScriptableConstruct(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ NS_RUNTIMEABORT("Don't know what kind of object this is!");
+ }
+
+ ChildNPObject* object = reinterpret_cast<ChildNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
+ NS_ASSERTION(actor, "This shouldn't ever be null!");
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+ if (!args.IsOk()) {
+ NS_ERROR("Failed to convert arguments!");
+ return false;
+ }
+
+ Variant remoteResult;
+ bool success;
+ actor->CallConstruct(args, &remoteResult, &success);
+
+ if (!success) {
+ return false;
+ }
+
+ ConvertToVariant(remoteResult, *aResult);
+ return true;
+}
+
+const NPClass PluginScriptableObjectChild::sNPClass = {
+ NP_CLASS_STRUCT_VERSION,
+ PluginScriptableObjectChild::ScriptableAllocate,
+ PluginScriptableObjectChild::ScriptableDeallocate,
+ PluginScriptableObjectChild::ScriptableInvalidate,
+ PluginScriptableObjectChild::ScriptableHasMethod,
+ PluginScriptableObjectChild::ScriptableInvoke,
+ PluginScriptableObjectChild::ScriptableInvokeDefault,
+ PluginScriptableObjectChild::ScriptableHasProperty,
+ PluginScriptableObjectChild::ScriptableGetProperty,
+ PluginScriptableObjectChild::ScriptableSetProperty,
+ PluginScriptableObjectChild::ScriptableRemoveProperty,
+ PluginScriptableObjectChild::ScriptableEnumerate,
+ PluginScriptableObjectChild::ScriptableConstruct
+};
+
+PluginScriptableObjectChild::PluginScriptableObjectChild(
+ ScriptableObjectType aType)
+: mInstance(nullptr),
+ mObject(nullptr),
+ mInvalidated(false),
+ mProtectCount(0),
+ mType(aType)
+{
+ AssertPluginThread();
+}
+
+PluginScriptableObjectChild::~PluginScriptableObjectChild()
+{
+ AssertPluginThread();
+
+ if (mObject) {
+ UnregisterActor(mObject);
+
+ if (mObject->_class == GetClass()) {
+ NS_ASSERTION(mType == Proxy, "Wrong type!");
+ static_cast<ChildNPObject*>(mObject)->parent = nullptr;
+ }
+ else {
+ NS_ASSERTION(mType == LocalObject, "Wrong type!");
+ PluginModuleChild::sBrowserFuncs.releaseobject(mObject);
+ }
+ }
+}
+
+bool
+PluginScriptableObjectChild::InitializeProxy()
+{
+ AssertPluginThread();
+ NS_ASSERTION(mType == Proxy, "Bad type!");
+ NS_ASSERTION(!mObject, "Calling Initialize more than once!");
+ NS_ASSERTION(!mInvalidated, "Already invalidated?!");
+
+ mInstance = static_cast<PluginInstanceChild*>(Manager());
+ NS_ASSERTION(mInstance, "Null manager?!");
+
+ NPObject* object = CreateProxyObject();
+ if (!object) {
+ NS_ERROR("Failed to create object!");
+ return false;
+ }
+
+ if (!RegisterActor(object)) {
+ NS_ERROR("RegisterActor failed");
+ return false;
+ }
+
+ mObject = object;
+ return true;
+}
+
+void
+PluginScriptableObjectChild::InitializeLocal(NPObject* aObject)
+{
+ AssertPluginThread();
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+ NS_ASSERTION(!mObject, "Calling Initialize more than once!");
+ NS_ASSERTION(!mInvalidated, "Already invalidated?!");
+
+ mInstance = static_cast<PluginInstanceChild*>(Manager());
+ NS_ASSERTION(mInstance, "Null manager?!");
+
+ PluginModuleChild::sBrowserFuncs.retainobject(aObject);
+
+ NS_ASSERTION(!mProtectCount, "Should be zero!");
+ mProtectCount++;
+
+ if (!RegisterActor(aObject)) {
+ NS_ERROR("RegisterActor failed");
+ }
+
+ mObject = aObject;
+}
+
+NPObject*
+PluginScriptableObjectChild::CreateProxyObject()
+{
+ NS_ASSERTION(mInstance, "Must have an instance!");
+ NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
+
+ NPClass* proxyClass = const_cast<NPClass*>(GetClass());
+ NPObject* npobject =
+ PluginModuleChild::sBrowserFuncs.createobject(mInstance->GetNPP(),
+ proxyClass);
+ NS_ASSERTION(npobject, "Failed to create object?!");
+ NS_ASSERTION(npobject->_class == GetClass(), "Wrong kind of object!");
+ NS_ASSERTION(npobject->referenceCount == 1, "Some kind of live object!");
+
+ ChildNPObject* object = static_cast<ChildNPObject*>(npobject);
+ NS_ASSERTION(!object->invalidated, "Bad object!");
+ NS_ASSERTION(!object->parent, "Bad object!");
+
+ // We don't want to have the actor own this object but rather let the object
+ // own this actor. Set the reference count to 0 here so that when the object
+ // dies we will send the destructor message to the child.
+ object->referenceCount = 0;
+ NS_LOG_RELEASE(object, 0, "NPObject");
+
+ object->parent = const_cast<PluginScriptableObjectChild*>(this);
+ return object;
+}
+
+bool
+PluginScriptableObjectChild::ResurrectProxyObject()
+{
+ NS_ASSERTION(mInstance, "Must have an instance already!");
+ NS_ASSERTION(!mObject, "Should not have an object already!");
+ NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
+
+ if (!InitializeProxy()) {
+ NS_ERROR("Initialize failed!");
+ return false;
+ }
+
+ SendProtect();
+ return true;
+}
+
+NPObject*
+PluginScriptableObjectChild::GetObject(bool aCanResurrect)
+{
+ if (!mObject && aCanResurrect && !ResurrectProxyObject()) {
+ NS_ERROR("Null object!");
+ return nullptr;
+ }
+ return mObject;
+}
+
+void
+PluginScriptableObjectChild::Protect()
+{
+ NS_ASSERTION(mObject, "No object!");
+ NS_ASSERTION(mProtectCount >= 0, "Negative retain count?!");
+
+ if (mType == LocalObject) {
+ ++mProtectCount;
+ }
+}
+
+void
+PluginScriptableObjectChild::Unprotect()
+{
+ NS_ASSERTION(mObject, "Bad state!");
+ NS_ASSERTION(mProtectCount >= 0, "Negative retain count?!");
+
+ if (mType == LocalObject) {
+ if (--mProtectCount == 0) {
+ PluginScriptableObjectChild::Send__delete__(this);
+ }
+ }
+}
+
+void
+PluginScriptableObjectChild::DropNPObject()
+{
+ NS_ASSERTION(mObject, "Invalidated object!");
+ NS_ASSERTION(mObject->_class == GetClass(), "Wrong type of object!");
+ NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
+
+ // We think we're about to be deleted, but we could be racing with the other
+ // process.
+ UnregisterActor(mObject);
+ mObject = nullptr;
+
+ SendUnprotect();
+}
+
+void
+PluginScriptableObjectChild::NPObjectDestroyed()
+{
+ NS_ASSERTION(LocalObject == mType,
+ "ScriptableDeallocate should have handled this for proxies");
+ mInvalidated = true;
+ mObject = nullptr;
+}
+
+bool
+PluginScriptableObjectChild::AnswerInvalidate()
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ return true;
+ }
+
+ mInvalidated = true;
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (mObject->_class && mObject->_class->invalidate) {
+ mObject->_class->invalidate(mObject);
+ }
+
+ Unprotect();
+
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerHasMethod(const PluginIdentifier& aId,
+ bool* aHasMethod)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerHasMethod with an invalidated object!");
+ *aHasMethod = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->hasMethod)) {
+ *aHasMethod = false;
+ return true;
+ }
+
+ StackIdentifier id(aId);
+ *aHasMethod = mObject->_class->hasMethod(mObject, id.ToNPIdentifier());
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerInvoke(const PluginIdentifier& aId,
+ InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerInvoke with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->invoke)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, mozilla::fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ ConvertToVariant(aArgs[index], convertedArgs[index]);
+ }
+
+ NPVariant result;
+ VOID_TO_NPVARIANT(result);
+ StackIdentifier id(aId);
+ bool success = mObject->_class->invoke(mObject, id.ToNPIdentifier(),
+ convertedArgs.Elements(), argCount,
+ &result);
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]);
+ }
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, GetInstance(),
+ false);
+
+ DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ *aSuccess = true;
+ *aResult = convertedResult;
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerInvokeDefault(InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerInvokeDefault with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->invokeDefault)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, mozilla::fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ ConvertToVariant(aArgs[index], convertedArgs[index]);
+ }
+
+ NPVariant result;
+ VOID_TO_NPVARIANT(result);
+ bool success = mObject->_class->invokeDefault(mObject,
+ convertedArgs.Elements(),
+ argCount, &result);
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]);
+ }
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, GetInstance(),
+ false);
+
+ DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ *aResult = convertedResult;
+ *aSuccess = true;
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerHasProperty(const PluginIdentifier& aId,
+ bool* aHasProperty)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerHasProperty with an invalidated object!");
+ *aHasProperty = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->hasProperty)) {
+ *aHasProperty = false;
+ return true;
+ }
+
+ StackIdentifier id(aId);
+ *aHasProperty = mObject->_class->hasProperty(mObject, id.ToNPIdentifier());
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerGetChildProperty(const PluginIdentifier& aId,
+ bool* aHasProperty,
+ bool* aHasMethod,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ *aHasProperty = *aHasMethod = *aSuccess = false;
+ *aResult = void_t();
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerGetProperty with an invalidated object!");
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->hasProperty &&
+ mObject->_class->hasMethod && mObject->_class->getProperty)) {
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ NPIdentifier id = stackID.ToNPIdentifier();
+
+ *aHasProperty = mObject->_class->hasProperty(mObject, id);
+ *aHasMethod = mObject->_class->hasMethod(mObject, id);
+
+ if (*aHasProperty) {
+ NPVariant result;
+ VOID_TO_NPVARIANT(result);
+
+ if (!mObject->_class->getProperty(mObject, id, &result)) {
+ return true;
+ }
+
+ Variant converted;
+ if ((*aSuccess = ConvertToRemoteVariant(result, converted, GetInstance(),
+ false))) {
+ DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
+ *aResult = converted;
+ }
+ }
+
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerSetProperty(const PluginIdentifier& aId,
+ const Variant& aValue,
+ bool* aSuccess)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerSetProperty with an invalidated object!");
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->hasProperty &&
+ mObject->_class->setProperty)) {
+ *aSuccess = false;
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ NPIdentifier id = stackID.ToNPIdentifier();
+
+ if (!mObject->_class->hasProperty(mObject, id)) {
+ *aSuccess = false;
+ return true;
+ }
+
+ NPVariant converted;
+ ConvertToVariant(aValue, converted);
+
+ if ((*aSuccess = mObject->_class->setProperty(mObject, id, &converted))) {
+ PluginModuleChild::sBrowserFuncs.releasevariantvalue(&converted);
+ }
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerRemoveProperty(const PluginIdentifier& aId,
+ bool* aSuccess)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!");
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->hasProperty &&
+ mObject->_class->removeProperty)) {
+ *aSuccess = false;
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ NPIdentifier id = stackID.ToNPIdentifier();
+ *aSuccess = mObject->_class->hasProperty(mObject, id) ?
+ mObject->_class->removeProperty(mObject, id) :
+ true;
+
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
+ bool* aSuccess)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->enumerate)) {
+ *aSuccess = false;
+ return true;
+ }
+
+ NPIdentifier* ids;
+ uint32_t idCount;
+ if (!mObject->_class->enumerate(mObject, &ids, &idCount)) {
+ *aSuccess = false;
+ return true;
+ }
+
+ aProperties->SetCapacity(idCount);
+
+ for (uint32_t index = 0; index < idCount; index++) {
+ aProperties->AppendElement(FromNPIdentifier(ids[index]));
+ }
+
+ PluginModuleChild::sBrowserFuncs.memfree(ids);
+ *aSuccess = true;
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::AnswerConstruct(InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerConstruct with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->construct)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, mozilla::fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ ConvertToVariant(aArgs[index], convertedArgs[index]);
+ }
+
+ NPVariant result;
+ VOID_TO_NPVARIANT(result);
+ bool success = mObject->_class->construct(mObject, convertedArgs.Elements(),
+ argCount, &result);
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]);
+ }
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, GetInstance(),
+ false);
+
+ DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ *aResult = convertedResult;
+ *aSuccess = true;
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::RecvProtect()
+{
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ Protect();
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::RecvUnprotect()
+{
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ Unprotect();
+ return true;
+}
+
+bool
+PluginScriptableObjectChild::Evaluate(NPString* aScript,
+ NPVariant* aResult)
+{
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ nsDependentCString script("");
+ if (aScript->UTF8Characters && aScript->UTF8Length) {
+ script.Rebind(aScript->UTF8Characters, aScript->UTF8Length);
+ }
+
+ bool success;
+ Variant result;
+ CallNPN_Evaluate(script, &result, &success);
+
+ if (!success) {
+ return false;
+ }
+
+ ConvertToVariant(result, *aResult);
+ return true;
+}
+
+nsTHashtable<PluginScriptableObjectChild::NPObjectData>* PluginScriptableObjectChild::sObjectMap;
+
+bool
+PluginScriptableObjectChild::RegisterActor(NPObject* aObject)
+{
+ AssertPluginThread();
+ MOZ_ASSERT(aObject, "Null pointer!");
+
+ NPObjectData* d = sObjectMap->GetEntry(aObject);
+ if (!d) {
+ NS_ERROR("NPObject not in object table");
+ return false;
+ }
+
+ d->actor = this;
+ return true;
+}
+
+void
+PluginScriptableObjectChild::UnregisterActor(NPObject* aObject)
+{
+ AssertPluginThread();
+ MOZ_ASSERT(aObject, "Null pointer!");
+
+ NPObjectData* d = sObjectMap->GetEntry(aObject);
+ MOZ_ASSERT(d, "NPObject not in object table");
+ if (d) {
+ d->actor = nullptr;
+ }
+}
+
+/* static */ PluginScriptableObjectChild*
+PluginScriptableObjectChild::GetActorForNPObject(NPObject* aObject)
+{
+ AssertPluginThread();
+ MOZ_ASSERT(aObject, "Null pointer!");
+
+ NPObjectData* d = sObjectMap->GetEntry(aObject);
+ if (!d) {
+ NS_ERROR("Plugin using object not created with NPN_CreateObject?");
+ return nullptr;
+ }
+
+ return d->actor;
+}
+
+/* static */ void
+PluginScriptableObjectChild::RegisterObject(NPObject* aObject, PluginInstanceChild* aInstance)
+{
+ AssertPluginThread();
+
+ if (!sObjectMap) {
+ sObjectMap = new nsTHashtable<PluginScriptableObjectChild::NPObjectData>();
+ }
+
+ NPObjectData* d = sObjectMap->PutEntry(aObject);
+ MOZ_ASSERT(!d->instance, "New NPObject already mapped?");
+ d->instance = aInstance;
+}
+
+/* static */ void
+PluginScriptableObjectChild::UnregisterObject(NPObject* aObject)
+{
+ AssertPluginThread();
+
+ sObjectMap->RemoveEntry(aObject);
+
+ if (!sObjectMap->Count()) {
+ delete sObjectMap;
+ sObjectMap = nullptr;
+ }
+}
+
+/* static */ PluginInstanceChild*
+PluginScriptableObjectChild::GetInstanceForNPObject(NPObject* aObject)
+{
+ AssertPluginThread();
+ NPObjectData* d = sObjectMap->GetEntry(aObject);
+ if (!d) {
+ return nullptr;
+ }
+ return d->instance;
+}
+
+/* static */ void
+PluginScriptableObjectChild::NotifyOfInstanceShutdown(PluginInstanceChild* aInstance)
+{
+ AssertPluginThread();
+ if (!sObjectMap) {
+ return;
+ }
+
+ for (auto iter = sObjectMap->Iter(); !iter.Done(); iter.Next()) {
+ NPObjectData* d = iter.Get();
+ if (d->instance == aInstance) {
+ NPObject* o = d->GetKey();
+ aInstance->mDeletingHash->PutEntry(o);
+ }
+ }
+}
diff --git a/dom/plugins/ipc/PluginScriptableObjectChild.h b/dom/plugins/ipc/PluginScriptableObjectChild.h
new file mode 100644
index 000000000..2d2e061de
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.h
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 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 dom_plugins_PluginScriptableObjectChild_h
+#define dom_plugins_PluginScriptableObjectChild_h 1
+
+#include "mozilla/plugins/PPluginScriptableObjectChild.h"
+#include "mozilla/plugins/PluginMessageUtils.h"
+#include "mozilla/plugins/PluginTypes.h"
+
+#include "npruntime.h"
+#include "nsDataHashtable.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceChild;
+class PluginScriptableObjectChild;
+
+struct ChildNPObject : NPObject
+{
+ ChildNPObject()
+ : NPObject(), parent(nullptr), invalidated(false)
+ {
+ MOZ_COUNT_CTOR(ChildNPObject);
+ }
+
+ ~ChildNPObject()
+ {
+ MOZ_COUNT_DTOR(ChildNPObject);
+ }
+
+ // |parent| is always valid as long as the actor is alive. Once the actor is
+ // destroyed this will be set to null.
+ PluginScriptableObjectChild* parent;
+ bool invalidated;
+};
+
+class PluginScriptableObjectChild : public PPluginScriptableObjectChild
+{
+ friend class PluginInstanceChild;
+
+public:
+ explicit PluginScriptableObjectChild(ScriptableObjectType aType);
+ virtual ~PluginScriptableObjectChild();
+
+ bool
+ InitializeProxy();
+
+ void
+ InitializeLocal(NPObject* aObject);
+
+
+ virtual bool
+ AnswerInvalidate() override;
+
+ virtual bool
+ AnswerHasMethod(const PluginIdentifier& aId,
+ bool* aHasMethod) override;
+
+ virtual bool
+ AnswerInvoke(const PluginIdentifier& aId,
+ InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerInvokeDefault(InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerHasProperty(const PluginIdentifier& aId,
+ bool* aHasProperty) override;
+
+ virtual bool
+ AnswerGetChildProperty(const PluginIdentifier& aId,
+ bool* aHasProperty,
+ bool* aHasMethod,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerSetProperty(const PluginIdentifier& aId,
+ const Variant& aValue,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerRemoveProperty(const PluginIdentifier& aId,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerConstruct(InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ RecvProtect() override;
+
+ virtual bool
+ RecvUnprotect() override;
+
+ NPObject*
+ GetObject(bool aCanResurrect);
+
+ static const NPClass*
+ GetClass()
+ {
+ return &sNPClass;
+ }
+
+ PluginInstanceChild*
+ GetInstance() const
+ {
+ return mInstance;
+ }
+
+ // Protect only affects LocalObject actors. It is called by the
+ // ProtectedVariant/Actor helper classes before the actor is used as an
+ // argument to an IPC call and when the parent process resurrects a
+ // proxy object to the NPObject associated with this actor.
+ void Protect();
+
+ // Unprotect only affects LocalObject actors. It is called by the
+ // ProtectedVariant/Actor helper classes after the actor is used as an
+ // argument to an IPC call and when the parent process is no longer using
+ // this actor.
+ void Unprotect();
+
+ // DropNPObject is only used for Proxy actors and is called when the child
+ // process is no longer using the NPObject associated with this actor. The
+ // parent process may subsequently use this actor again in which case a new
+ // NPObject will be created and associated with this actor (see
+ // ResurrectProxyObject).
+ void DropNPObject();
+
+ /**
+ * After NPP_Destroy, all NPObjects associated with an instance are
+ * destroyed. We are informed of this destruction. This should only be called
+ * on Local actors.
+ */
+ void NPObjectDestroyed();
+
+ bool
+ Evaluate(NPString* aScript,
+ NPVariant* aResult);
+
+ ScriptableObjectType
+ Type() const {
+ return mType;
+ }
+
+private:
+ struct StoredIdentifier
+ {
+ nsCString mIdentifier;
+ nsAutoRefCnt mRefCnt;
+ bool mPermanent;
+
+ nsrefcnt AddRef() {
+ ++mRefCnt;
+ return mRefCnt;
+ }
+
+ nsrefcnt Release() {
+ --mRefCnt;
+ if (mRefCnt == 0) {
+ delete this;
+ return 0;
+ }
+ return mRefCnt;
+ }
+
+ explicit StoredIdentifier(const nsCString& aIdentifier)
+ : mIdentifier(aIdentifier), mRefCnt(), mPermanent(false)
+ { MOZ_COUNT_CTOR(StoredIdentifier); }
+
+ ~StoredIdentifier() { MOZ_COUNT_DTOR(StoredIdentifier); }
+ };
+
+public:
+ class MOZ_STACK_CLASS StackIdentifier
+ {
+ public:
+ explicit StackIdentifier(const PluginIdentifier& aIdentifier);
+ explicit StackIdentifier(NPIdentifier aIdentifier);
+ ~StackIdentifier();
+
+ void MakePermanent()
+ {
+ if (mStored) {
+ mStored->mPermanent = true;
+ }
+ }
+ NPIdentifier ToNPIdentifier() const;
+
+ bool IsString() const { return mIdentifier.type() == PluginIdentifier::TnsCString; }
+ const nsCString& GetString() const { return mIdentifier.get_nsCString(); }
+
+ int32_t GetInt() const { return mIdentifier.get_int32_t(); }
+
+ PluginIdentifier GetIdentifier() const { return mIdentifier; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackIdentifier);
+
+ PluginIdentifier mIdentifier;
+ RefPtr<StoredIdentifier> mStored;
+ };
+
+ static void ClearIdentifiers();
+
+ bool RegisterActor(NPObject* aObject);
+ void UnregisterActor(NPObject* aObject);
+
+ static PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject);
+
+ static void RegisterObject(NPObject* aObject, PluginInstanceChild* aInstance);
+ static void UnregisterObject(NPObject* aObject);
+
+ static PluginInstanceChild* GetInstanceForNPObject(NPObject* aObject);
+
+ /**
+ * Fill PluginInstanceChild.mDeletingHash with all the remaining NPObjects
+ * associated with that instance.
+ */
+ static void NotifyOfInstanceShutdown(PluginInstanceChild* aInstance);
+
+private:
+ static NPObject*
+ ScriptableAllocate(NPP aInstance,
+ NPClass* aClass);
+
+ static void
+ ScriptableInvalidate(NPObject* aObject);
+
+ static void
+ ScriptableDeallocate(NPObject* aObject);
+
+ static bool
+ ScriptableHasMethod(NPObject* aObject,
+ NPIdentifier aName);
+
+ static bool
+ ScriptableInvoke(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult);
+
+ static bool
+ ScriptableInvokeDefault(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult);
+
+ static bool
+ ScriptableHasProperty(NPObject* aObject,
+ NPIdentifier aName);
+
+ static bool
+ ScriptableGetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ NPVariant* aResult);
+
+ static bool
+ ScriptableSetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aValue);
+
+ static bool
+ ScriptableRemoveProperty(NPObject* aObject,
+ NPIdentifier aName);
+
+ static bool
+ ScriptableEnumerate(NPObject* aObject,
+ NPIdentifier** aIdentifiers,
+ uint32_t* aCount);
+
+ static bool
+ ScriptableConstruct(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult);
+
+ NPObject*
+ CreateProxyObject();
+
+ // ResurrectProxyObject is only used with Proxy actors. It is called when the
+ // parent process uses an actor whose NPObject was deleted by the child
+ // process.
+ bool ResurrectProxyObject();
+
+private:
+ PluginInstanceChild* mInstance;
+ NPObject* mObject;
+ bool mInvalidated;
+ int mProtectCount;
+
+ ScriptableObjectType mType;
+
+ static const NPClass sNPClass;
+
+ static StoredIdentifier* HashIdentifier(const nsCString& aIdentifier);
+ static void UnhashIdentifier(StoredIdentifier* aIdentifier);
+
+ typedef nsDataHashtable<nsCStringHashKey, RefPtr<StoredIdentifier>> IdentifierTable;
+ static IdentifierTable sIdentifiers;
+
+ struct NPObjectData : public nsPtrHashKey<NPObject>
+ {
+ explicit NPObjectData(const NPObject* key)
+ : nsPtrHashKey<NPObject>(key),
+ instance(nullptr),
+ actor(nullptr)
+ { }
+
+ // never nullptr
+ PluginInstanceChild* instance;
+
+ // sometimes nullptr (no actor associated with an NPObject)
+ PluginScriptableObjectChild* actor;
+ };
+
+ /**
+ * mObjectMap contains all the currently active NPObjects (from NPN_CreateObject until the
+ * final release/dealloc, whether or not an actor is currently associated with the object.
+ */
+ static nsTHashtable<NPObjectData>* sObjectMap;
+};
+
+} /* namespace plugins */
+} /* namespace mozilla */
+
+#endif /* dom_plugins_PluginScriptableObjectChild_h */
diff --git a/dom/plugins/ipc/PluginScriptableObjectParent.cpp b/dom/plugins/ipc/PluginScriptableObjectParent.cpp
new file mode 100644
index 000000000..6e385b98c
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.cpp
@@ -0,0 +1,1393 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 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 "PluginScriptableObjectParent.h"
+
+#include "jsapi.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/plugins/PluginTypes.h"
+#include "mozilla/Unused.h"
+#include "nsNPAPIPlugin.h"
+#include "PluginAsyncSurrogate.h"
+#include "PluginScriptableObjectUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::plugins;
+using namespace mozilla::plugins::parent;
+
+/**
+ * NPIdentifiers in the chrome process are stored as jsids. The difficulty is in
+ * ensuring that string identifiers are rooted without pinning them all. We
+ * assume that all NPIdentifiers passed into nsJSNPRuntime will not be used
+ * outside the scope of the NPAPI call (i.e., they won't be stored in the
+ * heap). Rooting is done using the StackIdentifier class, which roots the
+ * identifier via RootedId.
+ *
+ * This system does not allow jsids to be moved, as would be needed for
+ * generational or compacting GC. When Firefox implements a moving GC for
+ * strings, we will need to ensure that no movement happens while NPAPI code is
+ * on the stack: although StackIdentifier roots all identifiers used, the GC has
+ * no way to know that a jsid cast to an NPIdentifier needs to be fixed up if it
+ * is moved.
+ */
+
+class MOZ_STACK_CLASS StackIdentifier
+{
+public:
+ explicit StackIdentifier(const PluginIdentifier& aIdentifier,
+ bool aAtomizeAndPin = false);
+
+ bool Failed() const { return mFailed; }
+ NPIdentifier ToNPIdentifier() const { return mIdentifier; }
+
+private:
+ bool mFailed;
+ NPIdentifier mIdentifier;
+ AutoSafeJSContext mCx;
+ JS::RootedId mId;
+};
+
+StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier, bool aAtomizeAndPin)
+: mFailed(false),
+ mId(mCx)
+{
+ if (aIdentifier.type() == PluginIdentifier::TnsCString) {
+ // We don't call _getstringidentifier because we may not want to intern the string.
+ NS_ConvertUTF8toUTF16 utf16name(aIdentifier.get_nsCString());
+ JS::RootedString str(mCx, JS_NewUCStringCopyN(mCx, utf16name.get(), utf16name.Length()));
+ if (!str) {
+ NS_ERROR("Id can't be allocated");
+ mFailed = true;
+ return;
+ }
+ if (aAtomizeAndPin) {
+ str = JS_AtomizeAndPinJSString(mCx, str);
+ if (!str) {
+ NS_ERROR("Id can't be allocated");
+ mFailed = true;
+ return;
+ }
+ }
+ if (!JS_StringToId(mCx, str, &mId)) {
+ NS_ERROR("Id can't be allocated");
+ mFailed = true;
+ return;
+ }
+ mIdentifier = JSIdToNPIdentifier(mId);
+ return;
+ }
+
+ mIdentifier = mozilla::plugins::parent::_getintidentifier(aIdentifier.get_int32_t());
+}
+
+static bool
+FromNPIdentifier(NPIdentifier aIdentifier, PluginIdentifier* aResult)
+{
+ if (mozilla::plugins::parent::_identifierisstring(aIdentifier)) {
+ nsCString string;
+ NPUTF8* chars =
+ mozilla::plugins::parent::_utf8fromidentifier(aIdentifier);
+ if (!chars) {
+ return false;
+ }
+ string.Adopt(chars);
+ *aResult = PluginIdentifier(string);
+ return true;
+ }
+ else {
+ int32_t intval = mozilla::plugins::parent::_intfromidentifier(aIdentifier);
+ *aResult = PluginIdentifier(intval);
+ return true;
+ }
+}
+
+namespace {
+
+inline void
+ReleaseVariant(NPVariant& aVariant,
+ PluginInstanceParent* aInstance)
+{
+ PushSurrogateAcceptCalls acceptCalls(aInstance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance);
+ if (npn) {
+ npn->releasevariantvalue(&aVariant);
+ }
+}
+
+} // namespace
+
+// static
+NPObject*
+PluginScriptableObjectParent::ScriptableAllocate(NPP aInstance,
+ NPClass* aClass)
+{
+ if (aClass != GetClass()) {
+ NS_ERROR("Huh?! Wrong class!");
+ return nullptr;
+ }
+
+ return new ParentNPObject();
+}
+
+// static
+void
+PluginScriptableObjectParent::ScriptableInvalidate(NPObject* aObject)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ // This can happen more than once, and is just fine.
+ return;
+ }
+
+ object->invalidated = true;
+
+ // |object->parent| may be null already if the instance has gone away.
+ if (object->parent && !object->parent->CallInvalidate()) {
+ NS_ERROR("Failed to send message!");
+ }
+}
+
+// static
+void
+PluginScriptableObjectParent::ScriptableDeallocate(NPObject* aObject)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+
+ if (object->asyncWrapperCount > 0) {
+ // In this case we should just drop the refcount to the asyncWrapperCount
+ // instead of deallocating because there are still some async wrappers
+ // out there that are referencing this object.
+ object->referenceCount = object->asyncWrapperCount;
+ return;
+ }
+
+ PluginScriptableObjectParent* actor = object->parent;
+ if (actor) {
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+ actor->DropNPObject();
+ }
+
+ delete object;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableHasMethod(NPObject* aObject,
+ NPIdentifier aName)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+ if (!actor) {
+ return false;
+ }
+
+ PluginIdentifier identifier;
+ if (!FromNPIdentifier(aName, &identifier)) {
+ return false;
+ }
+
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ bool result;
+ if (!actor->CallHasMethod(identifier, &result)) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ return result;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableInvoke(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+ if (!actor) {
+ return false;
+ }
+
+ PluginIdentifier identifier;
+ if (!FromNPIdentifier(aName, &identifier)) {
+ return false;
+ }
+
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+ if (!args.IsOk()) {
+ NS_ERROR("Failed to convert arguments!");
+ return false;
+ }
+
+ Variant remoteResult;
+ bool success;
+ if (!actor->CallInvoke(identifier, args, &remoteResult,
+ &success)) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ if (!success) {
+ return false;
+ }
+
+ if (!ConvertToVariant(remoteResult, *aResult, actor->GetInstance())) {
+ NS_WARNING("Failed to convert result!");
+ return false;
+ }
+ return true;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableInvokeDefault(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+ if (!actor) {
+ return false;
+ }
+
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+ if (!args.IsOk()) {
+ NS_ERROR("Failed to convert arguments!");
+ return false;
+ }
+
+ Variant remoteResult;
+ bool success;
+ if (!actor->CallInvokeDefault(args, &remoteResult, &success)) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ if (!success) {
+ return false;
+ }
+
+ if (!ConvertToVariant(remoteResult, *aResult, actor->GetInstance())) {
+ NS_WARNING("Failed to convert result!");
+ return false;
+ }
+ return true;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableHasProperty(NPObject* aObject,
+ NPIdentifier aName)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+ if (!actor) {
+ return false;
+ }
+
+ PluginIdentifier identifier;
+ if (!FromNPIdentifier(aName, &identifier)) {
+ return false;
+ }
+
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ bool result;
+ if (!actor->CallHasProperty(identifier, &result)) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ return result;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableGetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ NPVariant* aResult)
+{
+ // See GetPropertyHelper below.
+ NS_NOTREACHED("Shouldn't ever call this directly!");
+ return false;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableSetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aValue)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+ if (!actor) {
+ return false;
+ }
+
+ PluginIdentifier identifier;
+ if (!FromNPIdentifier(aName, &identifier)) {
+ return false;
+ }
+
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ ProtectedVariant value(*aValue, actor->GetInstance());
+ if (!value.IsOk()) {
+ NS_WARNING("Failed to convert variant!");
+ return false;
+ }
+
+ bool success;
+ if (!actor->CallSetProperty(identifier, value, &success)) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ return success;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableRemoveProperty(NPObject* aObject,
+ NPIdentifier aName)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+ if (!actor) {
+ return false;
+ }
+
+ PluginIdentifier identifier;
+ if (!FromNPIdentifier(aName, &identifier)) {
+ return false;
+ }
+
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ bool success;
+ if (!actor->CallRemoveProperty(identifier, &success)) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ return success;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableEnumerate(NPObject* aObject,
+ NPIdentifier** aIdentifiers,
+ uint32_t* aCount)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+ if (!actor) {
+ return false;
+ }
+
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(aObject);
+ if (!npn) {
+ NS_ERROR("No netscape funcs!");
+ return false;
+ }
+
+ AutoTArray<PluginIdentifier, 10> identifiers;
+ bool success;
+ if (!actor->CallEnumerate(&identifiers, &success)) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ if (!success) {
+ return false;
+ }
+
+ *aCount = identifiers.Length();
+ if (!*aCount) {
+ *aIdentifiers = nullptr;
+ return true;
+ }
+
+ *aIdentifiers = (NPIdentifier*)npn->memalloc(*aCount * sizeof(NPIdentifier));
+ if (!*aIdentifiers) {
+ NS_ERROR("Out of memory!");
+ return false;
+ }
+
+ for (uint32_t index = 0; index < *aCount; index++) {
+ // We pin the ID to avoid a GC hazard here. This could probably be fixed
+ // if the interface with nsJSNPRuntime were smarter.
+ StackIdentifier stackID(identifiers[index], true /* aAtomizeAndPin */);
+ if (stackID.Failed()) {
+ return false;
+ }
+ (*aIdentifiers)[index] = stackID.ToNPIdentifier();
+ }
+ return true;
+}
+
+// static
+bool
+PluginScriptableObjectParent::ScriptableConstruct(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult)
+{
+ if (aObject->_class != GetClass()) {
+ NS_ERROR("Don't know what kind of object this is!");
+ return false;
+ }
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
+ if (!actor) {
+ return false;
+ }
+
+ NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
+
+ ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
+ if (!args.IsOk()) {
+ NS_ERROR("Failed to convert arguments!");
+ return false;
+ }
+
+ Variant remoteResult;
+ bool success;
+ if (!actor->CallConstruct(args, &remoteResult, &success)) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ if (!success) {
+ return false;
+ }
+
+ if (!ConvertToVariant(remoteResult, *aResult, actor->GetInstance())) {
+ NS_WARNING("Failed to convert result!");
+ return false;
+ }
+ return true;
+}
+
+const NPClass PluginScriptableObjectParent::sNPClass = {
+ NP_CLASS_STRUCT_VERSION,
+ PluginScriptableObjectParent::ScriptableAllocate,
+ PluginScriptableObjectParent::ScriptableDeallocate,
+ PluginScriptableObjectParent::ScriptableInvalidate,
+ PluginScriptableObjectParent::ScriptableHasMethod,
+ PluginScriptableObjectParent::ScriptableInvoke,
+ PluginScriptableObjectParent::ScriptableInvokeDefault,
+ PluginScriptableObjectParent::ScriptableHasProperty,
+ PluginScriptableObjectParent::ScriptableGetProperty,
+ PluginScriptableObjectParent::ScriptableSetProperty,
+ PluginScriptableObjectParent::ScriptableRemoveProperty,
+ PluginScriptableObjectParent::ScriptableEnumerate,
+ PluginScriptableObjectParent::ScriptableConstruct
+};
+
+PluginScriptableObjectParent::PluginScriptableObjectParent(
+ ScriptableObjectType aType)
+: mInstance(nullptr),
+ mObject(nullptr),
+ mProtectCount(0),
+ mType(aType)
+{
+}
+
+PluginScriptableObjectParent::~PluginScriptableObjectParent()
+{
+ if (mObject) {
+ if (mObject->_class == GetClass()) {
+ NS_ASSERTION(mType == Proxy, "Wrong type!");
+ static_cast<ParentNPObject*>(mObject)->parent = nullptr;
+ }
+ else {
+ NS_ASSERTION(mType == LocalObject, "Wrong type!");
+ GetInstance()->GetNPNIface()->releaseobject(mObject);
+ }
+ }
+}
+
+void
+PluginScriptableObjectParent::InitializeProxy()
+{
+ NS_ASSERTION(mType == Proxy, "Bad type!");
+ NS_ASSERTION(!mObject, "Calling Initialize more than once!");
+
+ mInstance = static_cast<PluginInstanceParent*>(Manager());
+ NS_ASSERTION(mInstance, "Null manager?!");
+
+ NPObject* object = CreateProxyObject();
+ NS_ASSERTION(object, "Failed to create object!");
+
+ if (!mInstance->RegisterNPObjectForActor(object, this)) {
+ NS_ERROR("Out of memory?");
+ }
+
+ mObject = object;
+}
+
+void
+PluginScriptableObjectParent::InitializeLocal(NPObject* aObject)
+{
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+ NS_ASSERTION(!(mInstance && mObject), "Calling Initialize more than once!");
+
+ mInstance = static_cast<PluginInstanceParent*>(Manager());
+ NS_ASSERTION(mInstance, "Null manager?!");
+
+ mInstance->GetNPNIface()->retainobject(aObject);
+
+ NS_ASSERTION(!mProtectCount, "Should be zero!");
+ mProtectCount++;
+
+ if (!mInstance->RegisterNPObjectForActor(aObject, this)) {
+ NS_ERROR("Out of memory?");
+ }
+
+ mObject = aObject;
+}
+
+NPObject*
+PluginScriptableObjectParent::CreateProxyObject()
+{
+ NS_ASSERTION(mInstance, "Must have an instance!");
+ NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
+
+ PushSurrogateAcceptCalls acceptCalls(mInstance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(mInstance);
+
+ NPObject* npobject = npn->createobject(mInstance->GetNPP(),
+ const_cast<NPClass*>(GetClass()));
+ NS_ASSERTION(npobject, "Failed to create object?!");
+ NS_ASSERTION(npobject->_class == GetClass(), "Wrong kind of object!");
+ NS_ASSERTION(npobject->referenceCount == 1, "Some kind of live object!");
+
+ ParentNPObject* object = static_cast<ParentNPObject*>(npobject);
+ NS_ASSERTION(!object->invalidated, "Bad object!");
+ NS_ASSERTION(!object->parent, "Bad object!");
+
+ // We don't want to have the actor own this object but rather let the object
+ // own this actor. Set the reference count to 0 here so that when the object
+ // dies we will send the destructor message to the child.
+ object->referenceCount = 0;
+ NS_LOG_RELEASE(object, 0, "BrowserNPObject");
+
+ object->parent = const_cast<PluginScriptableObjectParent*>(this);
+ return object;
+}
+
+bool
+PluginScriptableObjectParent::ResurrectProxyObject()
+{
+ NS_ASSERTION(mInstance, "Must have an instance already!");
+ NS_ASSERTION(!mObject, "Should not have an object already!");
+ NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
+
+ InitializeProxy();
+ NS_ASSERTION(mObject, "Initialize failed!");
+
+ if (!SendProtect()) {
+ NS_WARNING("Failed to send message!");
+ return false;
+ }
+
+ return true;
+}
+
+NPObject*
+PluginScriptableObjectParent::GetObject(bool aCanResurrect)
+{
+ if (!mObject && aCanResurrect && !ResurrectProxyObject()) {
+ NS_ERROR("Null object!");
+ return nullptr;
+ }
+ return mObject;
+}
+
+void
+PluginScriptableObjectParent::Protect()
+{
+ NS_ASSERTION(mObject, "No object!");
+ NS_ASSERTION(mProtectCount >= 0, "Negative protect count?!");
+
+ if (mType == LocalObject) {
+ ++mProtectCount;
+ }
+}
+
+void
+PluginScriptableObjectParent::Unprotect()
+{
+ NS_ASSERTION(mObject, "No object!");
+ NS_ASSERTION(mProtectCount >= 0, "Negative protect count?!");
+
+ if (mType == LocalObject) {
+ if (--mProtectCount == 0) {
+ Unused << PluginScriptableObjectParent::Send__delete__(this);
+ }
+ }
+}
+
+void
+PluginScriptableObjectParent::DropNPObject()
+{
+ NS_ASSERTION(mObject, "Invalidated object!");
+ NS_ASSERTION(mObject->_class == GetClass(), "Wrong type of object!");
+ NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
+
+ // We think we're about to be deleted, but we could be racing with the other
+ // process.
+ PluginInstanceParent* instance = GetInstance();
+ NS_ASSERTION(instance, "Must have an instance!");
+
+ instance->UnregisterNPObject(mObject);
+ mObject = nullptr;
+
+ Unused << SendUnprotect();
+}
+
+void
+PluginScriptableObjectParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Implement me! Bug 1005163
+}
+
+bool
+PluginScriptableObjectParent::AnswerHasMethod(const PluginIdentifier& aId,
+ bool* aHasMethod)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerHasMethod with an invalidated object!");
+ *aHasMethod = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aHasMethod = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aHasMethod = false;
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aHasMethod = false;
+ return true;
+ }
+ *aHasMethod = npn->hasmethod(instance->GetNPP(), mObject, stackID.ToNPIdentifier());
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerInvoke(const PluginIdentifier& aId,
+ InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerInvoke with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ if (!ConvertToVariant(aArgs[index], convertedArgs[index], instance)) {
+ // Don't leak things we've already converted!
+ while (index-- > 0) {
+ ReleaseVariant(convertedArgs[index], instance);
+ }
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+ }
+
+ NPVariant result;
+ bool success = npn->invoke(instance->GetNPP(), mObject, stackID.ToNPIdentifier(),
+ convertedArgs.Elements(), argCount, &result);
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ ReleaseVariant(convertedArgs[index], instance);
+ }
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
+
+ DeferNPVariantLastRelease(npn, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ *aResult = convertedResult;
+ *aSuccess = true;
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerInvokeDefault(InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerInvoke with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ if (!ConvertToVariant(aArgs[index], convertedArgs[index], instance)) {
+ // Don't leak things we've already converted!
+ while (index-- > 0) {
+ ReleaseVariant(convertedArgs[index], instance);
+ }
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+ }
+
+ NPVariant result;
+ bool success = npn->invokeDefault(instance->GetNPP(), mObject,
+ convertedArgs.Elements(), argCount,
+ &result);
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ ReleaseVariant(convertedArgs[index], instance);
+ }
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
+
+ DeferNPVariantLastRelease(npn, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ *aResult = convertedResult;
+ *aSuccess = true;
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerHasProperty(const PluginIdentifier& aId,
+ bool* aHasProperty)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerHasProperty with an invalidated object!");
+ *aHasProperty = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aHasProperty = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aHasProperty = false;
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aHasProperty = false;
+ return true;
+ }
+
+ *aHasProperty = npn->hasproperty(instance->GetNPP(), mObject,
+ stackID.ToNPIdentifier());
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerGetParentProperty(
+ const PluginIdentifier& aId,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerGetProperty with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NPVariant result;
+ if (!npn->getproperty(instance->GetNPP(), mObject, stackID.ToNPIdentifier(),
+ &result)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ Variant converted;
+ if ((*aSuccess = ConvertToRemoteVariant(result, converted, instance))) {
+ DeferNPVariantLastRelease(npn, &result);
+ *aResult = converted;
+ }
+ else {
+ *aResult = void_t();
+ }
+
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerSetProperty(const PluginIdentifier& aId,
+ const Variant& aValue,
+ bool* aSuccess)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerSetProperty with an invalidated object!");
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aSuccess = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aSuccess = false;
+ return true;
+ }
+
+ NPVariant converted;
+ if (!ConvertToVariant(aValue, converted, instance)) {
+ *aSuccess = false;
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aSuccess = false;
+ return true;
+ }
+
+ if ((*aSuccess = npn->setproperty(instance->GetNPP(), mObject,
+ stackID.ToNPIdentifier(), &converted))) {
+ ReleaseVariant(converted, instance);
+ }
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerRemoveProperty(const PluginIdentifier& aId,
+ bool* aSuccess)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!");
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aSuccess = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aSuccess = false;
+ return true;
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aSuccess = false;
+ return true;
+ }
+
+ *aSuccess = npn->removeproperty(instance->GetNPP(), mObject,
+ stackID.ToNPIdentifier());
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
+ bool* aSuccess)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aSuccess = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_WARNING("No netscape funcs?!");
+ *aSuccess = false;
+ return true;
+ }
+
+ NPIdentifier* ids;
+ uint32_t idCount;
+ if (!npn->enumerate(instance->GetNPP(), mObject, &ids, &idCount)) {
+ *aSuccess = false;
+ return true;
+ }
+
+ aProperties->SetCapacity(idCount);
+
+ for (uint32_t index = 0; index < idCount; index++) {
+ PluginIdentifier id;
+ if (!FromNPIdentifier(ids[index], &id)) {
+ return false;
+ }
+ aProperties->AppendElement(id);
+ }
+
+ npn->memfree(ids);
+ *aSuccess = true;
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerConstruct(InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ if (!mObject) {
+ NS_WARNING("Calling AnswerConstruct with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ if (!ConvertToVariant(aArgs[index], convertedArgs[index], instance)) {
+ // Don't leak things we've already converted!
+ while (index-- > 0) {
+ ReleaseVariant(convertedArgs[index], instance);
+ }
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+ }
+
+ NPVariant result;
+ bool success = npn->construct(instance->GetNPP(), mObject,
+ convertedArgs.Elements(), argCount, &result);
+
+ for (uint32_t index = 0; index < argCount; index++) {
+ ReleaseVariant(convertedArgs[index], instance);
+ }
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, instance);
+
+ DeferNPVariantLastRelease(npn, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ *aSuccess = true;
+ *aResult = convertedResult;
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::RecvProtect()
+{
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ Protect();
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::RecvUnprotect()
+{
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ Unprotect();
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::AnswerNPN_Evaluate(const nsCString& aScript,
+ Variant* aResult,
+ bool* aSuccess)
+{
+ PluginInstanceParent* instance = GetInstance();
+ if (!instance) {
+ NS_ERROR("No instance?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ PushSurrogateAcceptCalls acceptCalls(instance);
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ NPString script = { aScript.get(), aScript.Length() };
+
+ NPVariant result;
+ bool success = npn->evaluate(instance->GetNPP(), mObject, &script, &result);
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, instance);
+
+ DeferNPVariantLastRelease(npn, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return true;
+ }
+
+ *aSuccess = true;
+ *aResult = convertedResult;
+ return true;
+}
+
+bool
+PluginScriptableObjectParent::GetPropertyHelper(NPIdentifier aName,
+ bool* aHasProperty,
+ bool* aHasMethod,
+ NPVariant* aResult)
+{
+ NS_ASSERTION(Type() == Proxy, "Bad type!");
+
+ ParentNPObject* object = static_cast<ParentNPObject*>(mObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return false;
+ }
+
+ PluginIdentifier identifier;
+ if (!FromNPIdentifier(aName, &identifier)) {
+ return false;
+ }
+
+ bool hasProperty, hasMethod, success;
+ Variant result;
+ if (!CallGetChildProperty(identifier, &hasProperty, &hasMethod, &result,
+ &success)) {
+ return false;
+ }
+
+ if (!success) {
+ return false;
+ }
+
+ if (!ConvertToVariant(result, *aResult, GetInstance())) {
+ NS_WARNING("Failed to convert result!");
+ return false;
+ }
+
+ *aHasProperty = hasProperty;
+ *aHasMethod = hasMethod;
+ return true;
+}
diff --git a/dom/plugins/ipc/PluginScriptableObjectParent.h b/dom/plugins/ipc/PluginScriptableObjectParent.h
new file mode 100644
index 000000000..8a1cdc028
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.h
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 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 dom_plugins_PluginScriptableObjectParent_h
+#define dom_plugins_PluginScriptableObjectParent_h 1
+
+#include "mozilla/plugins/PPluginScriptableObjectParent.h"
+#include "mozilla/plugins/PluginMessageUtils.h"
+
+#include "npfunctions.h"
+#include "npruntime.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceParent;
+class PluginScriptableObjectParent;
+
+struct ParentNPObject : NPObject
+{
+ ParentNPObject()
+ : NPObject()
+ , parent(nullptr)
+ , invalidated(false)
+ , asyncWrapperCount(0)
+ {}
+
+ // |parent| is always valid as long as the actor is alive. Once the actor is
+ // destroyed this will be set to null.
+ PluginScriptableObjectParent* parent;
+ bool invalidated;
+ int32_t asyncWrapperCount;
+};
+
+class PluginScriptableObjectParent : public PPluginScriptableObjectParent
+{
+ friend class PluginInstanceParent;
+
+public:
+ explicit PluginScriptableObjectParent(ScriptableObjectType aType);
+ virtual ~PluginScriptableObjectParent();
+
+ void
+ InitializeProxy();
+
+ void
+ InitializeLocal(NPObject* aObject);
+
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool
+ AnswerHasMethod(const PluginIdentifier& aId,
+ bool* aHasMethod) override;
+
+ virtual bool
+ AnswerInvoke(const PluginIdentifier& aId,
+ InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerInvokeDefault(InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerHasProperty(const PluginIdentifier& aId,
+ bool* aHasProperty) override;
+
+ virtual bool
+ AnswerGetParentProperty(const PluginIdentifier& aId,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerSetProperty(const PluginIdentifier& aId,
+ const Variant& aValue,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerRemoveProperty(const PluginIdentifier& aId,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerConstruct(InfallibleTArray<Variant>&& aArgs,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ AnswerNPN_Evaluate(const nsCString& aScript,
+ Variant* aResult,
+ bool* aSuccess) override;
+
+ virtual bool
+ RecvProtect() override;
+
+ virtual bool
+ RecvUnprotect() override;
+
+ static const NPClass*
+ GetClass()
+ {
+ return &sNPClass;
+ }
+
+ PluginInstanceParent*
+ GetInstance() const
+ {
+ return mInstance;
+ }
+
+ NPObject*
+ GetObject(bool aCanResurrect);
+
+ // Protect only affects LocalObject actors. It is called by the
+ // ProtectedVariant/Actor helper classes before the actor is used as an
+ // argument to an IPC call and when the child process resurrects a
+ // proxy object to the NPObject associated with this actor.
+ void Protect();
+
+ // Unprotect only affects LocalObject actors. It is called by the
+ // ProtectedVariant/Actor helper classes after the actor is used as an
+ // argument to an IPC call and when the child process is no longer using this
+ // actor.
+ void Unprotect();
+
+ // DropNPObject is only used for Proxy actors and is called when the parent
+ // process is no longer using the NPObject associated with this actor. The
+ // child process may subsequently use this actor again in which case a new
+ // NPObject will be created and associated with this actor (see
+ // ResurrectProxyObject).
+ void DropNPObject();
+
+ ScriptableObjectType
+ Type() const {
+ return mType;
+ }
+
+ bool GetPropertyHelper(NPIdentifier aName,
+ bool* aHasProperty,
+ bool* aHasMethod,
+ NPVariant* aResult);
+
+private:
+ static NPObject*
+ ScriptableAllocate(NPP aInstance,
+ NPClass* aClass);
+
+ static void
+ ScriptableInvalidate(NPObject* aObject);
+
+ static void
+ ScriptableDeallocate(NPObject* aObject);
+
+ static bool
+ ScriptableHasMethod(NPObject* aObject,
+ NPIdentifier aName);
+
+ static bool
+ ScriptableInvoke(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult);
+
+ static bool
+ ScriptableInvokeDefault(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult);
+
+ static bool
+ ScriptableHasProperty(NPObject* aObject,
+ NPIdentifier aName);
+
+ static bool
+ ScriptableGetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ NPVariant* aResult);
+
+ static bool
+ ScriptableSetProperty(NPObject* aObject,
+ NPIdentifier aName,
+ const NPVariant* aValue);
+
+ static bool
+ ScriptableRemoveProperty(NPObject* aObject,
+ NPIdentifier aName);
+
+ static bool
+ ScriptableEnumerate(NPObject* aObject,
+ NPIdentifier** aIdentifiers,
+ uint32_t* aCount);
+
+ static bool
+ ScriptableConstruct(NPObject* aObject,
+ const NPVariant* aArgs,
+ uint32_t aArgCount,
+ NPVariant* aResult);
+
+ NPObject*
+ CreateProxyObject();
+
+ // ResurrectProxyObject is only used with Proxy actors. It is called when the
+ // child process uses an actor whose NPObject was deleted by the parent
+ // process.
+ bool ResurrectProxyObject();
+
+private:
+ PluginInstanceParent* mInstance;
+
+ // This may be a ParentNPObject or some other kind depending on who created
+ // it. Have to check its class to find out.
+ NPObject* mObject;
+ int mProtectCount;
+
+ ScriptableObjectType mType;
+
+ static const NPClass sNPClass;
+};
+
+} /* namespace plugins */
+} /* namespace mozilla */
+
+#endif /* dom_plugins_PluginScriptableObjectParent_h */
diff --git a/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h b/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
new file mode 100644
index 000000000..fef663f4c
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 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 "PluginScriptableObjectUtils.h"
+
+namespace {
+
+template<class InstanceType>
+class VariantTraits;
+
+template<>
+class VariantTraits<mozilla::plugins::PluginInstanceParent>
+{
+public:
+ typedef mozilla::plugins::PluginScriptableObjectParent ScriptableObjectType;
+};
+
+template<>
+class VariantTraits<mozilla::plugins::PluginInstanceChild>
+{
+public:
+ typedef mozilla::plugins::PluginScriptableObjectChild ScriptableObjectType;
+};
+
+} /* anonymous namespace */
+
+inline bool
+mozilla::plugins::ConvertToVariant(const Variant& aRemoteVariant,
+ NPVariant& aVariant,
+ PluginInstanceParent* aInstance)
+{
+ switch (aRemoteVariant.type()) {
+ case Variant::Tvoid_t: {
+ VOID_TO_NPVARIANT(aVariant);
+ break;
+ }
+
+ case Variant::Tnull_t: {
+ NULL_TO_NPVARIANT(aVariant);
+ break;
+ }
+
+ case Variant::Tbool: {
+ BOOLEAN_TO_NPVARIANT(aRemoteVariant.get_bool(), aVariant);
+ break;
+ }
+
+ case Variant::Tint: {
+ INT32_TO_NPVARIANT(aRemoteVariant.get_int(), aVariant);
+ break;
+ }
+
+ case Variant::Tdouble: {
+ DOUBLE_TO_NPVARIANT(aRemoteVariant.get_double(), aVariant);
+ break;
+ }
+
+ case Variant::TnsCString: {
+ const nsCString& string = aRemoteVariant.get_nsCString();
+ const size_t length = string.Length();
+ NPUTF8* buffer = static_cast<NPUTF8*>(::malloc(sizeof(NPUTF8) * (length + 1)));
+ if (!buffer) {
+ NS_ERROR("Out of memory!");
+ return false;
+ }
+
+ std::copy(string.get(), string.get() + length, buffer);
+ buffer[length] = '\0';
+ STRINGN_TO_NPVARIANT(buffer, length, aVariant);
+ break;
+ }
+
+ case Variant::TPPluginScriptableObjectParent: {
+ NS_ASSERTION(aInstance, "Must have an instance!");
+ NPObject* object = NPObjectFromVariant(aRemoteVariant);
+ if (!object) {
+ NS_ERROR("Er, this shouldn't fail!");
+ return false;
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance);
+ if (!npn) {
+ NS_ERROR("Null netscape funcs!");
+ return false;
+ }
+
+ npn->retainobject(object);
+ OBJECT_TO_NPVARIANT(object, aVariant);
+ break;
+ }
+
+ case Variant::TPPluginScriptableObjectChild: {
+ NS_ASSERTION(!aInstance, "No instance should be given!");
+ NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin,
+ "Should be running on child only!");
+
+ NPObject* object = NPObjectFromVariant(aRemoteVariant);
+ NS_ASSERTION(object, "Null object?!");
+
+ PluginModuleChild::sBrowserFuncs.retainobject(object);
+ OBJECT_TO_NPVARIANT(object, aVariant);
+ break;
+ }
+
+ default:
+ NS_NOTREACHED("Shouldn't get here!");
+ return false;
+ }
+
+ return true;
+}
+
+template <class InstanceType>
+bool
+mozilla::plugins::ConvertToRemoteVariant(const NPVariant& aVariant,
+ Variant& aRemoteVariant,
+ InstanceType* aInstance,
+ bool aProtectActors)
+{
+ if (NPVARIANT_IS_VOID(aVariant)) {
+ aRemoteVariant = mozilla::void_t();
+ }
+ else if (NPVARIANT_IS_NULL(aVariant)) {
+ aRemoteVariant = mozilla::null_t();
+ }
+ else if (NPVARIANT_IS_BOOLEAN(aVariant)) {
+ aRemoteVariant = NPVARIANT_TO_BOOLEAN(aVariant);
+ }
+ else if (NPVARIANT_IS_INT32(aVariant)) {
+ aRemoteVariant = NPVARIANT_TO_INT32(aVariant);
+ }
+ else if (NPVARIANT_IS_DOUBLE(aVariant)) {
+ aRemoteVariant = NPVARIANT_TO_DOUBLE(aVariant);
+ }
+ else if (NPVARIANT_IS_STRING(aVariant)) {
+ NPString str = NPVARIANT_TO_STRING(aVariant);
+ nsCString string(str.UTF8Characters, str.UTF8Length);
+ aRemoteVariant = string;
+ }
+ else if (NPVARIANT_IS_OBJECT(aVariant)) {
+ NPObject* object = NPVARIANT_TO_OBJECT(aVariant);
+
+ typename VariantTraits<InstanceType>::ScriptableObjectType* actor =
+ aInstance->GetActorForNPObject(object);
+
+ if (!actor) {
+ NS_ERROR("Null actor!");
+ return false;
+ }
+
+ if (aProtectActors) {
+ actor->Protect();
+ }
+
+ aRemoteVariant = actor;
+ }
+ else {
+ NS_NOTREACHED("Shouldn't get here!");
+ return false;
+ }
+
+ return true;
+}
diff --git a/dom/plugins/ipc/PluginScriptableObjectUtils.h b/dom/plugins/ipc/PluginScriptableObjectUtils.h
new file mode 100644
index 000000000..bef2113c7
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils.h
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 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 dom_plugins_PluginScriptableObjectUtils_h
+#define dom_plugins_PluginScriptableObjectUtils_h
+
+#include "PluginModuleParent.h"
+#include "PluginModuleChild.h"
+#include "PluginInstanceParent.h"
+#include "PluginInstanceChild.h"
+#include "PluginScriptableObjectParent.h"
+#include "PluginScriptableObjectChild.h"
+
+#include "npapi.h"
+#include "npfunctions.h"
+#include "npruntime.h"
+
+#include "nsDebug.h"
+
+namespace mozilla {
+namespace plugins {
+
+inline PluginInstanceParent*
+GetInstance(NPObject* aObject)
+{
+ NS_ASSERTION(aObject->_class == PluginScriptableObjectParent::GetClass(),
+ "Bad class!");
+
+ ParentNPObject* object = reinterpret_cast<ParentNPObject*>(aObject);
+ if (object->invalidated) {
+ NS_WARNING("Calling method on an invalidated object!");
+ return nullptr;
+ }
+ if (!object->parent) {
+ return nullptr;
+ }
+ return object->parent->GetInstance();
+}
+
+inline NPObject*
+NPObjectFromVariant(const Variant& aRemoteVariant)
+{
+ switch (aRemoteVariant.type()) {
+ case Variant::TPPluginScriptableObjectParent: {
+ PluginScriptableObjectParent* actor =
+ const_cast<PluginScriptableObjectParent*>(
+ reinterpret_cast<const PluginScriptableObjectParent*>(
+ aRemoteVariant.get_PPluginScriptableObjectParent()));
+ return actor->GetObject(true);
+ }
+
+ case Variant::TPPluginScriptableObjectChild: {
+ PluginScriptableObjectChild* actor =
+ const_cast<PluginScriptableObjectChild*>(
+ reinterpret_cast<const PluginScriptableObjectChild*>(
+ aRemoteVariant.get_PPluginScriptableObjectChild()));
+ return actor->GetObject(true);
+ }
+
+ default:
+ NS_NOTREACHED("Shouldn't get here!");
+ return nullptr;
+ }
+}
+
+inline NPObject*
+NPObjectFromVariant(const NPVariant& aVariant)
+{
+ NS_ASSERTION(NPVARIANT_IS_OBJECT(aVariant), "Wrong variant type!");
+ return NPVARIANT_TO_OBJECT(aVariant);
+}
+
+inline const NPNetscapeFuncs*
+GetNetscapeFuncs(PluginInstanceParent* aInstance)
+{
+ PluginModuleParent* module = aInstance->Module();
+ if (!module) {
+ NS_WARNING("Null module?!");
+ return nullptr;
+ }
+ return module->GetNetscapeFuncs();
+}
+
+inline const NPNetscapeFuncs*
+GetNetscapeFuncs(NPObject* aObject)
+{
+ NS_ASSERTION(aObject->_class == PluginScriptableObjectParent::GetClass(),
+ "Bad class!");
+
+ PluginInstanceParent* instance = GetInstance(aObject);
+ if (!instance) {
+ return nullptr;
+ }
+
+ return GetNetscapeFuncs(instance);
+}
+
+inline void
+ReleaseRemoteVariant(Variant& aVariant)
+{
+ switch (aVariant.type()) {
+ case Variant::TPPluginScriptableObjectParent: {
+ PluginScriptableObjectParent* actor =
+ const_cast<PluginScriptableObjectParent*>(
+ reinterpret_cast<const PluginScriptableObjectParent*>(
+ aVariant.get_PPluginScriptableObjectParent()));
+ actor->Unprotect();
+ break;
+ }
+
+ case Variant::TPPluginScriptableObjectChild: {
+ NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin,
+ "Should only be running in the child!");
+ PluginScriptableObjectChild* actor =
+ const_cast<PluginScriptableObjectChild*>(
+ reinterpret_cast<const PluginScriptableObjectChild*>(
+ aVariant.get_PPluginScriptableObjectChild()));
+ actor->Unprotect();
+ break;
+ }
+
+ default:
+ break; // Intentional fall-through for other variant types.
+ }
+
+ aVariant = mozilla::void_t();
+}
+
+bool
+ConvertToVariant(const Variant& aRemoteVariant,
+ NPVariant& aVariant,
+ PluginInstanceParent* aInstance = nullptr);
+
+template <class InstanceType>
+bool
+ConvertToRemoteVariant(const NPVariant& aVariant,
+ Variant& aRemoteVariant,
+ InstanceType* aInstance,
+ bool aProtectActors = false);
+
+class ProtectedVariant
+{
+public:
+ ProtectedVariant(const NPVariant& aVariant,
+ PluginInstanceParent* aInstance)
+ {
+ mOk = ConvertToRemoteVariant(aVariant, mVariant, aInstance, true);
+ }
+
+ ProtectedVariant(const NPVariant& aVariant,
+ PluginInstanceChild* aInstance)
+ {
+ mOk = ConvertToRemoteVariant(aVariant, mVariant, aInstance, true);
+ }
+
+ ~ProtectedVariant() {
+ ReleaseRemoteVariant(mVariant);
+ }
+
+ bool IsOk() {
+ return mOk;
+ }
+
+ operator const Variant&() {
+ return mVariant;
+ }
+
+private:
+ Variant mVariant;
+ bool mOk;
+};
+
+class ProtectedVariantArray
+{
+public:
+ ProtectedVariantArray(const NPVariant* aArgs,
+ uint32_t aCount,
+ PluginInstanceParent* aInstance)
+ : mUsingShadowArray(false)
+ {
+ for (uint32_t index = 0; index < aCount; index++) {
+ Variant* remoteVariant = mArray.AppendElement();
+ if (!(remoteVariant &&
+ ConvertToRemoteVariant(aArgs[index], *remoteVariant, aInstance,
+ true))) {
+ mOk = false;
+ return;
+ }
+ }
+ mOk = true;
+ }
+
+ ProtectedVariantArray(const NPVariant* aArgs,
+ uint32_t aCount,
+ PluginInstanceChild* aInstance)
+ : mUsingShadowArray(false)
+ {
+ for (uint32_t index = 0; index < aCount; index++) {
+ Variant* remoteVariant = mArray.AppendElement();
+ if (!(remoteVariant &&
+ ConvertToRemoteVariant(aArgs[index], *remoteVariant, aInstance,
+ true))) {
+ mOk = false;
+ return;
+ }
+ }
+ mOk = true;
+ }
+
+ ~ProtectedVariantArray()
+ {
+ InfallibleTArray<Variant>& vars = EnsureAndGetShadowArray();
+ uint32_t count = vars.Length();
+ for (uint32_t index = 0; index < count; index++) {
+ ReleaseRemoteVariant(vars[index]);
+ }
+ }
+
+ operator const InfallibleTArray<Variant>&()
+ {
+ return EnsureAndGetShadowArray();
+ }
+
+ bool IsOk()
+ {
+ return mOk;
+ }
+
+private:
+ InfallibleTArray<Variant>&
+ EnsureAndGetShadowArray()
+ {
+ if (!mUsingShadowArray) {
+ mShadowArray.SwapElements(mArray);
+ mUsingShadowArray = true;
+ }
+ return mShadowArray;
+ }
+
+ // We convert the variants fallibly, but pass them to Call*()
+ // methods as an infallible array
+ nsTArray<Variant> mArray;
+ InfallibleTArray<Variant> mShadowArray;
+ bool mOk;
+ bool mUsingShadowArray;
+};
+
+template<class ActorType>
+struct ProtectedActorTraits
+{
+ static bool Nullable();
+};
+
+template<class ActorType, class Traits=ProtectedActorTraits<ActorType> >
+class ProtectedActor
+{
+public:
+ explicit ProtectedActor(ActorType* aActor) : mActor(aActor)
+ {
+ if (!Traits::Nullable()) {
+ NS_ASSERTION(mActor, "This should never be null!");
+ }
+ }
+
+ ~ProtectedActor()
+ {
+ if (Traits::Nullable() && !mActor)
+ return;
+ mActor->Unprotect();
+ }
+
+ ActorType* operator->()
+ {
+ return mActor;
+ }
+
+ explicit operator bool()
+ {
+ return !!mActor;
+ }
+
+private:
+ ActorType* mActor;
+};
+
+template<>
+struct ProtectedActorTraits<PluginScriptableObjectParent>
+{
+ static bool Nullable() { return true; }
+};
+
+template<>
+struct ProtectedActorTraits<PluginScriptableObjectChild>
+{
+ static bool Nullable() { return false; }
+};
+
+} /* namespace plugins */
+} /* namespace mozilla */
+
+#include "PluginScriptableObjectUtils-inl.h"
+
+#endif /* dom_plugins_PluginScriptableObjectUtils_h */
diff --git a/dom/plugins/ipc/PluginStreamChild.cpp b/dom/plugins/ipc/PluginStreamChild.cpp
new file mode 100644
index 000000000..7fbcd9f33
--- /dev/null
+++ b/dom/plugins/ipc/PluginStreamChild.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "PluginStreamChild.h"
+#include "mozilla/plugins/PluginInstanceChild.h"
+
+namespace mozilla {
+namespace plugins {
+
+PluginStreamChild::PluginStreamChild()
+ : mClosed(false)
+{
+ memset(&mStream, 0, sizeof(mStream));
+ mStream.ndata = static_cast<AStream*>(this);
+}
+
+bool
+PluginStreamChild::Answer__delete__(const NPReason& reason,
+ const bool& artificial)
+{
+ AssertPluginThread();
+ if (!artificial)
+ NPP_DestroyStream(reason);
+ return true;
+}
+
+int32_t
+PluginStreamChild::NPN_Write(int32_t length, void* buffer)
+{
+ AssertPluginThread();
+
+ int32_t written = 0;
+ CallNPN_Write(nsCString(static_cast<char*>(buffer), length),
+ &written);
+ if (written < 0)
+ PPluginStreamChild::Call__delete__(this, NPERR_GENERIC_ERROR, true);
+ // careful after here! |this| just got deleted
+
+ return written;
+}
+
+void
+PluginStreamChild::NPP_DestroyStream(NPError reason)
+{
+ AssertPluginThread();
+
+ if (mClosed)
+ return;
+
+ mClosed = true;
+ Instance()->mPluginIface->destroystream(
+ &Instance()->mData, &mStream, reason);
+}
+
+PluginInstanceChild*
+PluginStreamChild::Instance()
+{
+ return static_cast<PluginInstanceChild*>(Manager());
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginStreamChild.h b/dom/plugins/ipc/PluginStreamChild.h
new file mode 100644
index 000000000..b133f754e
--- /dev/null
+++ b/dom/plugins/ipc/PluginStreamChild.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 mozilla_plugins_PluginStreamChild_h
+#define mozilla_plugins_PluginStreamChild_h
+
+#include "mozilla/plugins/PPluginStreamChild.h"
+#include "mozilla/plugins/AStream.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceChild;
+
+class PluginStreamChild : public PPluginStreamChild, public AStream
+{
+ friend class PluginInstanceChild;
+
+public:
+ PluginStreamChild();
+ virtual ~PluginStreamChild() { }
+
+ virtual bool IsBrowserStream() override { return false; }
+
+ virtual bool Answer__delete__(const NPReason& reason,
+ const bool& artificial) override;
+
+ int32_t NPN_Write(int32_t length, void* buffer);
+ void NPP_DestroyStream(NPError reason);
+
+ void EnsureCorrectInstance(PluginInstanceChild* i)
+ {
+ if (i != Instance())
+ NS_RUNTIMEABORT("Incorrect stream instance");
+ }
+ void EnsureCorrectStream(NPStream* s)
+ {
+ if (s != &mStream)
+ NS_RUNTIMEABORT("Incorrect stream data");
+ }
+
+private:
+ PluginInstanceChild* Instance();
+
+ NPStream mStream;
+ bool mClosed;
+};
+
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
diff --git a/dom/plugins/ipc/PluginStreamParent.cpp b/dom/plugins/ipc/PluginStreamParent.cpp
new file mode 100644
index 000000000..1b4cfeb02
--- /dev/null
+++ b/dom/plugins/ipc/PluginStreamParent.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "PluginStreamParent.h"
+#include "PluginInstanceParent.h"
+
+namespace mozilla {
+namespace plugins {
+
+PluginStreamParent::PluginStreamParent(PluginInstanceParent* npp,
+ const nsCString& mimeType,
+ const nsCString& target,
+ NPError* result)
+ : mInstance(npp)
+ , mClosed(false)
+{
+ *result = mInstance->mNPNIface->newstream(mInstance->mNPP,
+ const_cast<char*>(mimeType.get()),
+ NullableStringGet(target),
+ &mStream);
+ if (*result == NPERR_NO_ERROR)
+ mStream->pdata = static_cast<AStream*>(this);
+ else
+ mStream = nullptr;
+}
+
+void
+PluginStreamParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Implement me! Bug 1005166
+}
+
+bool
+PluginStreamParent::AnswerNPN_Write(const Buffer& data, int32_t* written)
+{
+ if (mClosed) {
+ *written = -1;
+ return true;
+ }
+
+ *written = mInstance->mNPNIface->write(mInstance->mNPP, mStream,
+ data.Length(),
+ const_cast<char*>(data.get()));
+ if (*written < 0)
+ mClosed = true;
+
+ return true;
+}
+
+bool
+PluginStreamParent::Answer__delete__(const NPError& reason,
+ const bool& artificial)
+{
+ if (!artificial)
+ this->NPN_DestroyStream(reason);
+ return true;
+}
+
+void
+PluginStreamParent::NPN_DestroyStream(NPReason reason)
+{
+ if (mClosed)
+ return;
+
+ mInstance->mNPNIface->destroystream(mInstance->mNPP, mStream, reason);
+ mClosed = true;
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginStreamParent.h b/dom/plugins/ipc/PluginStreamParent.h
new file mode 100644
index 000000000..a361b72af
--- /dev/null
+++ b/dom/plugins/ipc/PluginStreamParent.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 mozilla_plugins_PluginStreamParent_h
+#define mozilla_plugins_PluginStreamParent_h
+
+#include "mozilla/plugins/PPluginStreamParent.h"
+#include "mozilla/plugins/AStream.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginInstanceParent;
+
+class PluginStreamParent : public PPluginStreamParent, public AStream
+{
+ friend class PluginModuleParent;
+ friend class PluginInstanceParent;
+
+public:
+ PluginStreamParent(PluginInstanceParent* npp, const nsCString& mimeType,
+ const nsCString& target, NPError* result);
+ virtual ~PluginStreamParent() { }
+
+ virtual bool IsBrowserStream() override { return false; }
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool AnswerNPN_Write(const Buffer& data, int32_t* written) override;
+
+ virtual bool Answer__delete__(const NPError& reason, const bool& artificial) override;
+
+private:
+ void NPN_DestroyStream(NPReason reason);
+
+ PluginInstanceParent* mInstance;
+ NPStream* mStream;
+ bool mClosed;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
diff --git a/dom/plugins/ipc/PluginSurfaceParent.cpp b/dom/plugins/ipc/PluginSurfaceParent.cpp
new file mode 100644
index 000000000..1d5e19ad6
--- /dev/null
+++ b/dom/plugins/ipc/PluginSurfaceParent.cpp
@@ -0,0 +1,34 @@
+/* -*- 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/. */
+
+#include "mozilla/plugins/PluginSurfaceParent.h"
+#include "mozilla/gfx/SharedDIBSurface.h"
+
+using mozilla::gfx::SharedDIBSurface;
+
+namespace mozilla {
+namespace plugins {
+
+PluginSurfaceParent::PluginSurfaceParent(const WindowsSharedMemoryHandle& handle,
+ const gfx::IntSize& size,
+ bool transparent)
+{
+ SharedDIBSurface* dibsurf = new SharedDIBSurface();
+ if (dibsurf->Attach(handle, size.width, size.height, transparent))
+ mSurface = dibsurf;
+}
+
+PluginSurfaceParent::~PluginSurfaceParent()
+{
+}
+
+void
+PluginSurfaceParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Implement me! Bug 1005167
+}
+
+}
+}
diff --git a/dom/plugins/ipc/PluginSurfaceParent.h b/dom/plugins/ipc/PluginSurfaceParent.h
new file mode 100644
index 000000000..037547d66
--- /dev/null
+++ b/dom/plugins/ipc/PluginSurfaceParent.h
@@ -0,0 +1,40 @@
+/* -*- 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/. */
+
+#ifndef dom_plugins_PluginSurfaceParent_h
+#define dom_plugins_PluginSurfaceParent_h
+
+#include "mozilla/plugins/PPluginSurfaceParent.h"
+#include "mozilla/plugins/PluginMessageUtils.h"
+
+#ifndef XP_WIN
+#error "This header is for Windows only."
+#endif
+
+class gfxASurface;
+
+namespace mozilla {
+namespace plugins {
+
+class PluginSurfaceParent : public PPluginSurfaceParent
+{
+public:
+ PluginSurfaceParent(const WindowsSharedMemoryHandle& handle,
+ const gfx::IntSize& size,
+ const bool transparent);
+ ~PluginSurfaceParent();
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ gfxASurface* Surface() { return mSurface; }
+
+private:
+ RefPtr<gfxASurface> mSurface;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugin_PluginSurfaceParent_h
diff --git a/dom/plugins/ipc/PluginTypes.ipdlh b/dom/plugins/ipc/PluginTypes.ipdlh
new file mode 100644
index 000000000..ed5ae0c71
--- /dev/null
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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/. */
+
+namespace mozilla {
+namespace plugins {
+
+struct PluginTag
+{
+ uint32_t id;
+ nsCString name;
+ nsCString description;
+ nsCString[] mimeTypes;
+ nsCString[] mimeDescriptions;
+ nsCString[] extensions;
+ bool isJavaPlugin;
+ bool isFlashPlugin;
+ bool supportsAsyncInit;
+ bool supportsAsyncRender; // flash specific
+ nsCString filename;
+ nsCString version;
+ int64_t lastModifiedTime;
+ bool isFromExtension;
+ int32_t sandboxLevel;
+};
+
+union PluginIdentifier
+{
+ nsCString;
+ int32_t;
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginUtilsOSX.h b/dom/plugins/ipc/PluginUtilsOSX.h
new file mode 100644
index 000000000..c201782c0
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsOSX.h
@@ -0,0 +1,92 @@
+/* -*- 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 dom_plugins_PluginUtilsOSX_h
+#define dom_plugins_PluginUtilsOSX_h 1
+
+#include "npapi.h"
+#include "mozilla/gfx/QuartzSupport.h"
+#include "nsRect.h"
+
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsOSX {
+
+// Need to call back into the browser's message loop to process event.
+typedef void (*RemoteProcessEvents) (void*);
+
+NPError ShowCocoaContextMenu(void* aMenu, int aX, int aY, void* pluginModule, RemoteProcessEvents remoteEvent);
+
+void InvokeNativeEventLoop();
+
+// Need to call back and send a cocoa draw event to the plugin.
+typedef void (*DrawPluginFunc) (CGContextRef, void*, nsIntRect aUpdateRect);
+
+void* GetCGLayer(DrawPluginFunc aFunc, void* aPluginInstance, double aContentsScaleFactor);
+void ReleaseCGLayer(void* cgLayer);
+void Repaint(void* cgLayer, nsIntRect aRect);
+
+bool SetProcessName(const char* aProcessName);
+
+/*
+ * Provides a wrapper around nsCARenderer to manage double buffering
+ * without having to unbind nsCARenderer on every surface swaps.
+ *
+ * The double buffer renderer begins with no initialize surfaces.
+ * The buffers can be initialized and cleared individually.
+ * Swapping still occurs regardless if the buffers are initialized.
+ */
+class nsDoubleBufferCARenderer {
+public:
+ nsDoubleBufferCARenderer() : mCALayer(nullptr), mContentsScaleFactor(1.0) {}
+ // Returns width in "display pixels". A "display pixel" is the smallest
+ // fully addressable part of a display. But in HiDPI modes each "display
+ // pixel" corresponds to more than one device pixel. Multiply display pixels
+ // by mContentsScaleFactor to get device pixels.
+ size_t GetFrontSurfaceWidth();
+ // Returns height in "display pixels". Multiply by
+ // mContentsScaleFactor to get device pixels.
+ size_t GetFrontSurfaceHeight();
+ double GetFrontSurfaceContentsScaleFactor();
+ // Returns width in "display pixels". Multiply by
+ // mContentsScaleFactor to get device pixels.
+ size_t GetBackSurfaceWidth();
+ // Returns height in "display pixels". Multiply by
+ // mContentsScaleFactor to get device pixels.
+ size_t GetBackSurfaceHeight();
+ double GetBackSurfaceContentsScaleFactor();
+ IOSurfaceID GetFrontSurfaceID();
+
+ bool HasBackSurface();
+ bool HasFrontSurface();
+ bool HasCALayer();
+
+ void SetCALayer(void *aCALayer);
+ // aWidth and aHeight are in "display pixels". Multiply by
+ // aContentsScaleFactor to get device pixels.
+ bool InitFrontSurface(size_t aWidth, size_t aHeight,
+ double aContentsScaleFactor,
+ AllowOfflineRendererEnum aAllowOfflineRenderer);
+ void Render();
+ void SwapSurfaces();
+ void ClearFrontSurface();
+ void ClearBackSurface();
+
+ double GetContentsScaleFactor() { return mContentsScaleFactor; }
+
+private:
+ void *mCALayer;
+ RefPtr<nsCARenderer> mCARenderer;
+ RefPtr<MacIOSurface> mFrontSurface;
+ RefPtr<MacIOSurface> mBackSurface;
+ double mContentsScaleFactor;
+};
+
+} // namespace PluginUtilsOSX
+} // namespace plugins
+} // namespace mozilla
+
+#endif //dom_plugins_PluginUtilsOSX_h
diff --git a/dom/plugins/ipc/PluginUtilsOSX.mm b/dom/plugins/ipc/PluginUtilsOSX.mm
new file mode 100644
index 000000000..2a920f7f6
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsOSX.mm
@@ -0,0 +1,462 @@
+/* -*- 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/. */
+
+#include <dlfcn.h>
+#import <AppKit/AppKit.h>
+#import <QuartzCore/QuartzCore.h>
+#include "PluginUtilsOSX.h"
+
+// Remove definitions for try/catch interfering with ObjCException macros.
+#include "nsObjCExceptions.h"
+#include "nsCocoaUtils.h"
+
+#include "nsDebug.h"
+
+#include "mozilla/Sprintf.h"
+
+@interface CALayer (ContentsScale)
+- (double)contentsScale;
+- (void)setContentsScale:(double)scale;
+@end
+
+using namespace mozilla::plugins::PluginUtilsOSX;
+
+@interface CGBridgeLayer : CALayer {
+ DrawPluginFunc mDrawFunc;
+ void* mPluginInstance;
+ nsIntRect mUpdateRect;
+}
+- (void)setDrawFunc:(DrawPluginFunc)aFunc
+ pluginInstance:(void*)aPluginInstance;
+- (void)updateRect:(nsIntRect)aRect;
+
+@end
+
+// CGBitmapContextSetData() is an undocumented function present (with
+// the same signature) since at least OS X 10.5. As the name suggests,
+// it's used to replace the "data" in a bitmap context that was
+// originally specified in a call to CGBitmapContextCreate() or
+// CGBitmapContextCreateWithData().
+typedef void (*CGBitmapContextSetDataFunc) (CGContextRef c,
+ size_t x,
+ size_t y,
+ size_t width,
+ size_t height,
+ void* data,
+ size_t bitsPerComponent,
+ size_t bitsPerPixel,
+ size_t bytesPerRow);
+CGBitmapContextSetDataFunc CGBitmapContextSetDataPtr = NULL;
+
+@implementation CGBridgeLayer
+- (void) updateRect:(nsIntRect)aRect
+{
+ mUpdateRect.UnionRect(mUpdateRect, aRect);
+}
+
+- (void) setDrawFunc:(DrawPluginFunc)aFunc
+ pluginInstance:(void*)aPluginInstance
+{
+ mDrawFunc = aFunc;
+ mPluginInstance = aPluginInstance;
+}
+
+- (void)drawInContext:(CGContextRef)aCGContext
+{
+ ::CGContextSaveGState(aCGContext);
+ ::CGContextTranslateCTM(aCGContext, 0, self.bounds.size.height);
+ ::CGContextScaleCTM(aCGContext, (CGFloat) 1, (CGFloat) -1);
+
+ mUpdateRect = nsIntRect::Truncate(0, 0, self.bounds.size.width, self.bounds.size.height);
+
+ mDrawFunc(aCGContext, mPluginInstance, mUpdateRect);
+
+ ::CGContextRestoreGState(aCGContext);
+
+ mUpdateRect.SetEmpty();
+}
+
+@end
+
+void* mozilla::plugins::PluginUtilsOSX::GetCGLayer(DrawPluginFunc aFunc,
+ void* aPluginInstance,
+ double aContentsScaleFactor) {
+ CGBridgeLayer *bridgeLayer = [[CGBridgeLayer alloc] init];
+
+ // We need to make bridgeLayer behave properly when its superlayer changes
+ // size (in nsCARenderer::SetBounds()).
+ bridgeLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
+ bridgeLayer.needsDisplayOnBoundsChange = YES;
+ NSNull *nullValue = [NSNull null];
+ NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
+ nullValue, @"bounds",
+ nullValue, @"contents",
+ nullValue, @"contentsRect",
+ nullValue, @"position",
+ nil];
+ [bridgeLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
+
+ // For reasons that aren't clear (perhaps one or more OS bugs), we can only
+ // use full HiDPI resolution here if the tree is built with the 10.7 SDK or
+ // up. If we build with the 10.6 SDK, changing the contentsScale property
+ // of bridgeLayer (even to the same value) causes it to stop working (go
+ // blank). This doesn't happen with objects that are members of the CALayer
+ // class (as opposed to one of its subclasses).
+#if defined(MAC_OS_X_VERSION_10_7) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+ if ([bridgeLayer respondsToSelector:@selector(setContentsScale:)]) {
+ bridgeLayer.contentsScale = aContentsScaleFactor;
+ }
+#endif
+
+ [bridgeLayer setDrawFunc:aFunc
+ pluginInstance:aPluginInstance];
+ return bridgeLayer;
+}
+
+void mozilla::plugins::PluginUtilsOSX::ReleaseCGLayer(void *cgLayer) {
+ CGBridgeLayer *bridgeLayer = (CGBridgeLayer*)cgLayer;
+ [bridgeLayer release];
+}
+
+void mozilla::plugins::PluginUtilsOSX::Repaint(void *caLayer, nsIntRect aRect) {
+ CGBridgeLayer *bridgeLayer = (CGBridgeLayer*)caLayer;
+ [CATransaction begin];
+ [bridgeLayer updateRect:aRect];
+ [bridgeLayer setNeedsDisplay];
+ [bridgeLayer displayIfNeeded];
+ [CATransaction commit];
+}
+
+@interface EventProcessor : NSObject {
+ RemoteProcessEvents aRemoteEvents;
+ void *aPluginModule;
+}
+- (void)setRemoteEvents:(RemoteProcessEvents) remoteEvents pluginModule:(void*) pluginModule;
+- (void)onTick;
+@end
+
+@implementation EventProcessor
+- (void) onTick
+{
+ aRemoteEvents(aPluginModule);
+}
+
+- (void)setRemoteEvents:(RemoteProcessEvents) remoteEvents pluginModule:(void*) pluginModule
+{
+ aRemoteEvents = remoteEvents;
+ aPluginModule = pluginModule;
+}
+@end
+
+#define EVENT_PROCESS_DELAY 0.05 // 50 ms
+
+NPError mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(void* aMenu, int aX, int aY, void* pluginModule, RemoteProcessEvents remoteEvent)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ // Set the native cursor to the OS default (an arrow) before displaying the
+ // context menu. Otherwise (if the plugin has changed the cursor) it may
+ // stay as the plugin has set it -- which means it may be invisible. We
+ // need to do this because we display the context menu without making the
+ // plugin process the foreground process. If we did, the cursor would
+ // change to an arrow cursor automatically -- as it does in Chrome.
+ [[NSCursor arrowCursor] set];
+
+ EventProcessor* eventProcessor = nullptr;
+ NSTimer *eventTimer = nullptr;
+ if (pluginModule) {
+ // Create a timer to process browser events while waiting
+ // on the menu. This prevents the browser from hanging
+ // during the lifetime of the menu.
+ eventProcessor = [[EventProcessor alloc] init];
+ [eventProcessor setRemoteEvents:remoteEvent pluginModule:pluginModule];
+ eventTimer = [NSTimer timerWithTimeInterval:EVENT_PROCESS_DELAY
+ target:eventProcessor selector:@selector(onTick)
+ userInfo:nil repeats:TRUE];
+ // Use NSEventTrackingRunLoopMode otherwise the timer will
+ // not fire during the right click menu.
+ [[NSRunLoop currentRunLoop] addTimer:eventTimer
+ forMode:NSEventTrackingRunLoopMode];
+ }
+
+ NSMenu* nsmenu = reinterpret_cast<NSMenu*>(aMenu);
+ NSPoint screen_point = ::NSMakePoint(aX, aY);
+
+ [nsmenu popUpMenuPositioningItem:nil atLocation:screen_point inView:nil];
+
+ if (pluginModule) {
+ [eventTimer invalidate];
+ [eventProcessor release];
+ }
+
+ return NPERR_NO_ERROR;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NPERR_GENERIC_ERROR);
+}
+
+void mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+ ::CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+
+#define UNDOCUMENTED_SESSION_CONSTANT ((int)-2)
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsOSX {
+ static void *sApplicationASN = NULL;
+ static void *sApplicationInfoItem = NULL;
+} // namespace PluginUtilsOSX
+} // namespace plugins
+} // namespace mozilla
+
+bool mozilla::plugins::PluginUtilsOSX::SetProcessName(const char* aProcessName) {
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+ nsAutoreleasePool localPool;
+
+ if (!aProcessName || strcmp(aProcessName, "") == 0) {
+ return false;
+ }
+
+ NSString *currentName = [[[NSBundle mainBundle] localizedInfoDictionary]
+ objectForKey:(NSString *)kCFBundleNameKey];
+
+ char formattedName[1024];
+ SprintfLiteral(formattedName, "%s %s", [currentName UTF8String], aProcessName);
+
+ aProcessName = formattedName;
+
+ // This function is based on Chrome/Webkit's and relies on potentially dangerous SPI.
+ typedef CFTypeRef (*LSGetASNType)();
+ typedef OSStatus (*LSSetInformationItemType)(int, CFTypeRef,
+ CFStringRef,
+ CFStringRef,
+ CFDictionaryRef*);
+
+ CFBundleRef launchServices = ::CFBundleGetBundleWithIdentifier(
+ CFSTR("com.apple.LaunchServices"));
+ if (!launchServices) {
+ NS_WARNING("Failed to set process name: Could not open LaunchServices bundle");
+ return false;
+ }
+
+ if (!sApplicationASN) {
+ sApplicationASN = ::CFBundleGetFunctionPointerForName(launchServices,
+ CFSTR("_LSGetCurrentApplicationASN"));
+ }
+
+ LSGetASNType getASNFunc = reinterpret_cast<LSGetASNType>
+ (sApplicationASN);
+
+ if (!sApplicationInfoItem) {
+ sApplicationInfoItem = ::CFBundleGetFunctionPointerForName(launchServices,
+ CFSTR("_LSSetApplicationInformationItem"));
+ }
+
+ LSSetInformationItemType setInformationItemFunc
+ = reinterpret_cast<LSSetInformationItemType>
+ (sApplicationInfoItem);
+
+ void * displayNameKeyAddr = ::CFBundleGetDataPointerForName(launchServices,
+ CFSTR("_kLSDisplayNameKey"));
+
+ CFStringRef displayNameKey = nil;
+ if (displayNameKeyAddr) {
+ displayNameKey = reinterpret_cast<CFStringRef>(*(CFStringRef*)displayNameKeyAddr);
+ }
+
+ // Rename will fail without this
+ ProcessSerialNumber psn;
+ if (::GetCurrentProcess(&psn) != noErr) {
+ return false;
+ }
+
+ CFTypeRef currentAsn = getASNFunc();
+
+ if (!getASNFunc || !setInformationItemFunc ||
+ !displayNameKey || !currentAsn) {
+ NS_WARNING("Failed to set process name: Accessing launchServices failed");
+ return false;
+ }
+
+ CFStringRef processName = ::CFStringCreateWithCString(nil,
+ aProcessName,
+ kCFStringEncodingASCII);
+ if (!processName) {
+ NS_WARNING("Failed to set process name: Could not create CFStringRef");
+ return false;
+ }
+
+ OSErr err = setInformationItemFunc(UNDOCUMENTED_SESSION_CONSTANT, currentAsn,
+ displayNameKey, processName,
+ nil); // Optional out param
+ ::CFRelease(processName);
+ if (err != noErr) {
+ NS_WARNING("Failed to set process name: LSSetInformationItemType err");
+ return false;
+ }
+
+ return true;
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
+}
+
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsOSX {
+
+size_t nsDoubleBufferCARenderer::GetFrontSurfaceWidth() {
+ if (!HasFrontSurface()) {
+ return 0;
+ }
+
+ return mFrontSurface->GetWidth();
+}
+
+size_t nsDoubleBufferCARenderer::GetFrontSurfaceHeight() {
+ if (!HasFrontSurface()) {
+ return 0;
+ }
+
+ return mFrontSurface->GetHeight();
+}
+
+double nsDoubleBufferCARenderer::GetFrontSurfaceContentsScaleFactor() {
+ if (!HasFrontSurface()) {
+ return 1.0;
+ }
+
+ return mFrontSurface->GetContentsScaleFactor();
+}
+
+size_t nsDoubleBufferCARenderer::GetBackSurfaceWidth() {
+ if (!HasBackSurface()) {
+ return 0;
+ }
+
+ return mBackSurface->GetWidth();
+}
+
+size_t nsDoubleBufferCARenderer::GetBackSurfaceHeight() {
+ if (!HasBackSurface()) {
+ return 0;
+ }
+
+ return mBackSurface->GetHeight();
+}
+
+double nsDoubleBufferCARenderer::GetBackSurfaceContentsScaleFactor() {
+ if (!HasBackSurface()) {
+ return 1.0;
+ }
+
+ return mBackSurface->GetContentsScaleFactor();
+}
+
+IOSurfaceID nsDoubleBufferCARenderer::GetFrontSurfaceID() {
+ if (!HasFrontSurface()) {
+ return 0;
+ }
+
+ return mFrontSurface->GetIOSurfaceID();
+}
+
+bool nsDoubleBufferCARenderer::HasBackSurface() {
+ return !!mBackSurface;
+}
+
+bool nsDoubleBufferCARenderer::HasFrontSurface() {
+ return !!mFrontSurface;
+}
+
+bool nsDoubleBufferCARenderer::HasCALayer() {
+ return !!mCALayer;
+}
+
+void nsDoubleBufferCARenderer::SetCALayer(void *aCALayer) {
+ mCALayer = aCALayer;
+}
+
+bool nsDoubleBufferCARenderer::InitFrontSurface(size_t aWidth, size_t aHeight,
+ double aContentsScaleFactor,
+ AllowOfflineRendererEnum aAllowOfflineRenderer) {
+ if (!mCALayer) {
+ return false;
+ }
+
+ mContentsScaleFactor = aContentsScaleFactor;
+ mFrontSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight, mContentsScaleFactor);
+ if (!mFrontSurface) {
+ mCARenderer = nullptr;
+ return false;
+ }
+
+ if (!mCARenderer) {
+ mCARenderer = new nsCARenderer();
+ if (!mCARenderer) {
+ mFrontSurface = nullptr;
+ return false;
+ }
+
+ mCARenderer->AttachIOSurface(mFrontSurface);
+
+ nsresult result = mCARenderer->SetupRenderer(mCALayer,
+ mFrontSurface->GetWidth(),
+ mFrontSurface->GetHeight(),
+ mContentsScaleFactor,
+ aAllowOfflineRenderer);
+
+ if (result != NS_OK) {
+ mCARenderer = nullptr;
+ mFrontSurface = nullptr;
+ return false;
+ }
+ } else {
+ mCARenderer->AttachIOSurface(mFrontSurface);
+ }
+
+ return true;
+}
+
+void nsDoubleBufferCARenderer::Render() {
+ if (!HasFrontSurface() || !mCARenderer) {
+ return;
+ }
+
+ mCARenderer->Render(GetFrontSurfaceWidth(), GetFrontSurfaceHeight(),
+ mContentsScaleFactor, nullptr);
+}
+
+void nsDoubleBufferCARenderer::SwapSurfaces() {
+ RefPtr<MacIOSurface> prevFrontSurface = mFrontSurface;
+ mFrontSurface = mBackSurface;
+ mBackSurface = prevFrontSurface;
+
+ if (mFrontSurface) {
+ mCARenderer->AttachIOSurface(mFrontSurface);
+ }
+}
+
+void nsDoubleBufferCARenderer::ClearFrontSurface() {
+ mFrontSurface = nullptr;
+ if (!mFrontSurface && !mBackSurface) {
+ mCARenderer = nullptr;
+ }
+}
+
+void nsDoubleBufferCARenderer::ClearBackSurface() {
+ mBackSurface = nullptr;
+ if (!mFrontSurface && !mBackSurface) {
+ mCARenderer = nullptr;
+ }
+}
+
+} // namespace PluginUtilsOSX
+} // namespace plugins
+} // namespace mozilla
+
diff --git a/dom/plugins/ipc/PluginUtilsWin.cpp b/dom/plugins/ipc/PluginUtilsWin.cpp
new file mode 100644
index 000000000..db6387a51
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsWin.cpp
@@ -0,0 +1,237 @@
+/* -*- 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/. */
+
+/* PluginUtilsWin.cpp - top-level Windows plugin management code */
+
+#include <mmdeviceapi.h>
+#include "PluginUtilsWin.h"
+#include "PluginModuleParent.h"
+#include "mozilla/StaticMutex.h"
+
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsWin {
+
+typedef nsTHashtable<nsPtrHashKey<PluginModuleParent>> PluginModuleSet;
+StaticMutex sMutex;
+
+class AudioDeviceChangedRunnable : public Runnable
+{
+public:
+ explicit AudioDeviceChangedRunnable(const PluginModuleSet* aAudioNotificationSet,
+ NPAudioDeviceChangeDetailsIPC aChangeDetails) :
+ mChangeDetails(aChangeDetails)
+ , mAudioNotificationSet(aAudioNotificationSet)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ StaticMutexAutoLock lock(sMutex);
+ PLUGIN_LOG_DEBUG(("Notifying %d plugins of audio device change.",
+ mAudioNotificationSet->Count()));
+
+ for (auto iter = mAudioNotificationSet->ConstIter(); !iter.Done(); iter.Next()) {
+ PluginModuleParent* pluginModule = iter.Get()->GetKey();
+ pluginModule->SendNPP_SetValue_NPNVaudioDeviceChangeDetails(mChangeDetails);
+ }
+ return NS_OK;
+ }
+
+protected:
+ NPAudioDeviceChangeDetailsIPC mChangeDetails;
+ const PluginModuleSet* mAudioNotificationSet;
+};
+
+class AudioNotification : public IMMNotificationClient
+{
+public:
+ AudioNotification() :
+ mRefCt(1)
+ , mIsRegistered(false)
+ {
+ HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&mDeviceEnum));
+ if (FAILED(hr)) {
+ mDeviceEnum = nullptr;
+ return;
+ }
+
+ hr = mDeviceEnum->RegisterEndpointNotificationCallback(this);
+ if (FAILED(hr)) {
+ mDeviceEnum->Release();
+ mDeviceEnum = nullptr;
+ return;
+ }
+
+ mIsRegistered = true;
+ }
+
+ ~AudioNotification()
+ {
+ MOZ_ASSERT(!mIsRegistered,
+ "Destroying AudioNotification without first calling Unregister");
+ if (mDeviceEnum) {
+ mDeviceEnum->Release();
+ }
+ }
+
+ // IMMNotificationClient Implementation
+ HRESULT STDMETHODCALLTYPE
+ OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id) override
+ {
+ NPAudioDeviceChangeDetailsIPC changeDetails;
+ changeDetails.flow = (int32_t)flow;
+ changeDetails.role = (int32_t)role;
+ changeDetails.defaultDevice = device_id ? std::wstring(device_id) : L"";
+
+ // Make sure that plugin is notified on the main thread.
+ RefPtr<AudioDeviceChangedRunnable> runnable =
+ new AudioDeviceChangedRunnable(&mAudioNotificationSet, changeDetails);
+ NS_DispatchToMainThread(runnable);
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ OnDeviceAdded(LPCWSTR device_id) override
+ {
+ return S_OK;
+ };
+
+ HRESULT STDMETHODCALLTYPE
+ OnDeviceRemoved(LPCWSTR device_id) override
+ {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) override
+ {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) override
+ {
+ return S_OK;
+ }
+
+ // IUnknown Implementation
+ ULONG STDMETHODCALLTYPE
+ AddRef() override
+ {
+ return InterlockedIncrement(&mRefCt);
+ }
+
+ ULONG STDMETHODCALLTYPE
+ Release() override
+ {
+ ULONG ulRef = InterlockedDecrement(&mRefCt);
+ if (0 == ulRef) {
+ delete this;
+ }
+ return ulRef;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ QueryInterface(REFIID riid, VOID **ppvInterface) override
+ {
+ if (__uuidof(IUnknown) == riid) {
+ AddRef();
+ *ppvInterface = (IUnknown*)this;
+ } else if (__uuidof(IMMNotificationClient) == riid) {
+ AddRef();
+ *ppvInterface = (IMMNotificationClient*)this;
+ } else {
+ *ppvInterface = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+ }
+
+ /*
+ * A Valid instance must be Unregistered before Releasing it.
+ */
+ void Unregister()
+ {
+ if (mDeviceEnum) {
+ mDeviceEnum->UnregisterEndpointNotificationCallback(this);
+ }
+ mIsRegistered = false;
+ }
+
+ /*
+ * True whenever the notification server is set to report events to this object.
+ */
+ bool IsRegistered() {
+ return mIsRegistered;
+ }
+
+ void AddModule(PluginModuleParent* aModule) {
+ StaticMutexAutoLock lock(sMutex);
+ mAudioNotificationSet.PutEntry(aModule);
+ }
+
+ void RemoveModule(PluginModuleParent* aModule) {
+ StaticMutexAutoLock lock(sMutex);
+ mAudioNotificationSet.RemoveEntry(aModule);
+ }
+
+ /*
+ * Are any modules registered for audio notifications?
+ */
+ bool HasModules() {
+ return !mAudioNotificationSet.IsEmpty();
+ }
+
+private:
+ bool mIsRegistered; // only used to make sure that Unregister is called before destroying a Valid instance.
+ LONG mRefCt;
+ IMMDeviceEnumerator* mDeviceEnum;
+
+ // Set of plugin modules that have registered to be notified when the audio device
+ // changes.
+ PluginModuleSet mAudioNotificationSet;
+}; // class AudioNotification
+
+// callback that gets notified of audio device events, or NULL
+AudioNotification* sAudioNotification = nullptr;
+
+nsresult
+RegisterForAudioDeviceChanges(PluginModuleParent* aModuleParent, bool aShouldRegister)
+{
+ // Hold the AudioNotification singleton iff there are PluginModuleParents
+ // that are subscribed to it.
+ if (aShouldRegister) {
+ if (!sAudioNotification) {
+ // We are registering the first module. Create the singleton.
+ sAudioNotification = new AudioNotification();
+ if (!sAudioNotification->IsRegistered()) {
+ PLUGIN_LOG_DEBUG(("Registered for plugin audio device notification failed."));
+ sAudioNotification->Release();
+ sAudioNotification = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+ PLUGIN_LOG_DEBUG(("Registered for plugin audio device notification."));
+ }
+ sAudioNotification->AddModule(aModuleParent);
+ }
+ else if (!aShouldRegister && sAudioNotification) {
+ sAudioNotification->RemoveModule(aModuleParent);
+ if (!sAudioNotification->HasModules()) {
+ // We have removed the last module from the notification mechanism
+ // so we can destroy the singleton.
+ PLUGIN_LOG_DEBUG(("Unregistering for plugin audio device notification."));
+ sAudioNotification->Unregister();
+ sAudioNotification->Release();
+ sAudioNotification = nullptr;
+ }
+ }
+ return NS_OK;
+}
+
+} // namespace PluginUtilsWin
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginUtilsWin.h b/dom/plugins/ipc/PluginUtilsWin.h
new file mode 100644
index 000000000..097ae5262
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsWin.h
@@ -0,0 +1,23 @@
+/* -*- 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 dom_plugins_PluginUtilsWin_h
+#define dom_plugins_PluginUtilsWin_h 1
+
+#include "npapi.h"
+
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsWin {
+
+nsresult RegisterForAudioDeviceChanges(PluginModuleParent* aModuleParent,
+ bool aShouldRegister);
+
+} // namespace PluginUtilsWin
+} // namespace plugins
+} // namespace mozilla
+
+#endif //dom_plugins_PluginUtilsWin_h
diff --git a/dom/plugins/ipc/PluginWidgetChild.cpp b/dom/plugins/ipc/PluginWidgetChild.cpp
new file mode 100644
index 000000000..263082981
--- /dev/null
+++ b/dom/plugins/ipc/PluginWidgetChild.cpp
@@ -0,0 +1,71 @@
+/* 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/plugins/PluginWidgetChild.h"
+
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/plugins/PluginWidgetParent.h"
+#include "PluginWidgetProxy.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDebug.h"
+
+#if defined(XP_WIN)
+#include "mozilla/plugins/PluginInstanceParent.h"
+#endif
+
+#define PWLOG(...)
+//#define PWLOG(...) printf_stderr(__VA_ARGS__)
+
+namespace mozilla {
+namespace plugins {
+
+PluginWidgetChild::PluginWidgetChild() :
+ mWidget(nullptr)
+{
+ PWLOG("PluginWidgetChild::PluginWidgetChild()\n");
+ MOZ_COUNT_CTOR(PluginWidgetChild);
+}
+
+PluginWidgetChild::~PluginWidgetChild()
+{
+ PWLOG("PluginWidgetChild::~PluginWidgetChild()\n");
+ MOZ_COUNT_DTOR(PluginWidgetChild);
+}
+
+// Called by the proxy widget when it is destroyed by layout. Only gets
+// called once.
+void
+PluginWidgetChild::ProxyShutdown()
+{
+ PWLOG("PluginWidgetChild::ProxyShutdown()\n");
+ if (mWidget) {
+ mWidget = nullptr;
+ auto tab = static_cast<mozilla::dom::TabChild*>(Manager());
+ if (!tab->IsDestroyed()) {
+ Unused << Send__delete__(this);
+ }
+ }
+}
+
+void
+PluginWidgetChild::KillWidget()
+{
+ PWLOG("PluginWidgetChild::KillWidget()\n");
+ if (mWidget) {
+ mWidget->ChannelDestroyed();
+ }
+ mWidget = nullptr;
+}
+
+void
+PluginWidgetChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ PWLOG("PluginWidgetChild::ActorDestroy(%d)\n", aWhy);
+ KillWidget();
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginWidgetChild.h b/dom/plugins/ipc/PluginWidgetChild.h
new file mode 100644
index 000000000..cfb5a4327
--- /dev/null
+++ b/dom/plugins/ipc/PluginWidgetChild.h
@@ -0,0 +1,39 @@
+/* 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 mozilla_plugins_PluginWidgetChild_h
+#define mozilla_plugins_PluginWidgetChild_h
+
+#include "mozilla/plugins/PPluginWidgetChild.h"
+
+namespace mozilla {
+namespace widget {
+class PluginWidgetProxy;
+} // namespace widget
+namespace plugins {
+
+class PluginWidgetChild : public PPluginWidgetChild
+{
+public:
+ PluginWidgetChild();
+ virtual ~PluginWidgetChild();
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void SetWidget(mozilla::widget::PluginWidgetProxy* aWidget) {
+ mWidget = aWidget;
+ }
+ void ProxyShutdown();
+
+private:
+ void KillWidget();
+
+ mozilla::widget::PluginWidgetProxy* mWidget;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginWidgetChild_h
+
diff --git a/dom/plugins/ipc/PluginWidgetParent.cpp b/dom/plugins/ipc/PluginWidgetParent.cpp
new file mode 100644
index 000000000..3c9a95b52
--- /dev/null
+++ b/dom/plugins/ipc/PluginWidgetParent.cpp
@@ -0,0 +1,237 @@
+/* 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 "PluginWidgetParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsWidgetsCID.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDebug.h"
+
+#if defined(MOZ_WIDGET_GTK)
+#include "nsPluginNativeWindowGtk.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::widget;
+
+#define PWLOG(...)
+//#define PWLOG(...) printf_stderr(__VA_ARGS__)
+
+#if defined(XP_WIN)
+namespace mozilla {
+namespace dom {
+// For nsWindow
+const wchar_t* kPluginWidgetContentParentProperty =
+ L"kPluginWidgetParentProperty";
+} }
+#endif
+
+namespace mozilla {
+namespace plugins {
+
+static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID);
+
+// This macro returns true to prevent an abort in the child process when
+// ipc message delivery fails.
+#define ENSURE_CHANNEL { \
+ if (!mWidget) { \
+ NS_WARNING("called on an invalid remote widget."); \
+ return true; \
+ } \
+}
+
+PluginWidgetParent::PluginWidgetParent()
+{
+ PWLOG("PluginWidgetParent::PluginWidgetParent()\n");
+ MOZ_COUNT_CTOR(PluginWidgetParent);
+}
+
+PluginWidgetParent::~PluginWidgetParent()
+{
+ PWLOG("PluginWidgetParent::~PluginWidgetParent()\n");
+ MOZ_COUNT_DTOR(PluginWidgetParent);
+ // A destroy call can actually get skipped if a widget is associated
+ // with the last out-of-process page, make sure and cleanup any left
+ // over widgets if we have them.
+ KillWidget();
+}
+
+mozilla::dom::TabParent*
+PluginWidgetParent::GetTabParent()
+{
+ return static_cast<mozilla::dom::TabParent*>(Manager());
+}
+
+void
+PluginWidgetParent::SetParent(nsIWidget* aParent)
+{
+ // This will trigger sync send messages to the plugin process window
+ // procedure and a cascade of events to that window related to focus
+ // and activation.
+ if (mWidget && aParent) {
+ mWidget->SetParent(aParent);
+ }
+}
+
+// When plugins run in chrome, nsPluginNativeWindow(Plat) implements platform
+// specific functionality that wraps plugin widgets. With e10s we currently
+// bypass this code on Window, and reuse a bit of it on Linux. Content still
+// makes use of some of the utility functions as well.
+
+bool
+PluginWidgetParent::RecvCreate(nsresult* aResult, uint64_t* aScrollCaptureId,
+ uintptr_t* aPluginInstanceId)
+{
+ PWLOG("PluginWidgetParent::RecvCreate()\n");
+
+ *aScrollCaptureId = 0;
+ *aPluginInstanceId = 0;
+
+ mWidget = do_CreateInstance(kWidgetCID, aResult);
+ NS_ASSERTION(NS_SUCCEEDED(*aResult), "widget create failure");
+
+#if defined(MOZ_WIDGET_GTK)
+ // We need this currently just for GTK in setting up a socket widget
+ // we can send over to content -> plugin.
+ PLUG_NewPluginNativeWindow((nsPluginNativeWindow**)&mWrapper);
+ if (!mWrapper) {
+ KillWidget();
+ return false;
+ }
+ // Give a copy of this to the widget, which handles some update
+ // work for us.
+ mWidget->SetNativeData(NS_NATIVE_PLUGIN_OBJECT_PTR, (uintptr_t)mWrapper.get());
+#endif
+
+ // This returns the top level window widget
+ nsCOMPtr<nsIWidget> parentWidget = GetTabParent()->GetWidget();
+ // If this fails, bail.
+ if (!parentWidget) {
+ *aResult = NS_ERROR_NOT_AVAILABLE;
+ KillWidget();
+ return true;
+ }
+
+ nsWidgetInitData initData;
+ initData.mWindowType = eWindowType_plugin_ipc_chrome;
+ initData.mUnicode = false;
+ initData.clipChildren = true;
+ initData.clipSiblings = true;
+ *aResult = mWidget->Create(parentWidget.get(), nullptr,
+ LayoutDeviceIntRect(0, 0, 0, 0), &initData);
+ if (NS_FAILED(*aResult)) {
+ KillWidget();
+ // This should never fail, abort.
+ return false;
+ }
+
+ mWidget->EnableDragDrop(true);
+
+#if defined(MOZ_WIDGET_GTK)
+ // For setup, initially GTK code expects 'window' to hold the parent.
+ mWrapper->window = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
+ DebugOnly<nsresult> drv = mWrapper->CreateXEmbedWindow(false);
+ NS_ASSERTION(NS_SUCCEEDED(drv), "widget call failure");
+ mWrapper->SetAllocation();
+ PWLOG("Plugin XID=%p\n", (void*)mWrapper->window);
+#elif defined(XP_WIN)
+ DebugOnly<DWORD> winres =
+ ::SetPropW((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW),
+ mozilla::dom::kPluginWidgetContentParentProperty,
+ GetTabParent()->Manager()->AsContentParent());
+ NS_ASSERTION(winres, "SetPropW call failure");
+
+ *aScrollCaptureId = mWidget->CreateScrollCaptureContainer();
+ *aPluginInstanceId =
+ reinterpret_cast<uintptr_t>(mWidget->GetNativeData(NS_NATIVE_PLUGIN_ID));
+#endif
+
+ // This is a special call we make to nsBaseWidget to register this
+ // window as a remote plugin window which is expected to receive
+ // visibility updates from the compositor, which ships this data
+ // over with corresponding layer updates.
+ mWidget->RegisterPluginWindowForRemoteUpdates();
+
+ return true;
+}
+
+void
+PluginWidgetParent::KillWidget()
+{
+ PWLOG("PluginWidgetParent::KillWidget() widget=%p\n", (void*)mWidget.get());
+ if (mWidget) {
+ mWidget->UnregisterPluginWindowForRemoteUpdates();
+ mWidget->Destroy();
+#if defined(MOZ_WIDGET_GTK)
+ mWidget->SetNativeData(NS_NATIVE_PLUGIN_OBJECT_PTR, (uintptr_t)0);
+ mWrapper = nullptr;
+#elif defined(XP_WIN)
+ ::RemovePropW((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW),
+ mozilla::dom::kPluginWidgetContentParentProperty);
+#endif
+ mWidget = nullptr;
+ }
+}
+
+void
+PluginWidgetParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ PWLOG("PluginWidgetParent::ActorDestroy(%d)\n", aWhy);
+ KillWidget();
+}
+
+// Called by TabParent's Destroy() in response to an early tear down (Early
+// in that this is happening before layout in the child has had a chance
+// to destroy the child widget.) when the tab is closing.
+void
+PluginWidgetParent::ParentDestroy()
+{
+ PWLOG("PluginWidgetParent::ParentDestroy()\n");
+}
+
+bool
+PluginWidgetParent::RecvSetFocus(const bool& aRaise)
+{
+ ENSURE_CHANNEL;
+ PWLOG("PluginWidgetParent::RecvSetFocus(%d)\n", aRaise);
+ mWidget->SetFocus(aRaise);
+ return true;
+}
+
+bool
+PluginWidgetParent::RecvGetNativePluginPort(uintptr_t* value)
+{
+ ENSURE_CHANNEL;
+#if defined(MOZ_WIDGET_GTK)
+ *value = (uintptr_t)mWrapper->window;
+ NS_ASSERTION(*value, "no xid??");
+#else
+ *value = (uintptr_t)mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
+ NS_ASSERTION(*value, "no native port??");
+#endif
+ PWLOG("PluginWidgetParent::RecvGetNativeData() %p\n", (void*)*value);
+ return true;
+}
+
+bool
+PluginWidgetParent::RecvSetNativeChildWindow(const uintptr_t& aChildWindow)
+{
+#if defined(XP_WIN)
+ ENSURE_CHANNEL;
+ PWLOG("PluginWidgetParent::RecvSetNativeChildWindow(%p)\n",
+ (void*)aChildWindow);
+ mWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW, aChildWindow);
+ return true;
+#else
+ NS_NOTREACHED("PluginWidgetParent::RecvSetNativeChildWindow not implemented!");
+ return false;
+#endif
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginWidgetParent.h b/dom/plugins/ipc/PluginWidgetParent.h
new file mode 100644
index 000000000..17778002b
--- /dev/null
+++ b/dom/plugins/ipc/PluginWidgetParent.h
@@ -0,0 +1,65 @@
+/* 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 mozilla_plugins_PluginWidgetParent_h
+#define mozilla_plugins_PluginWidgetParent_h
+
+#include "mozilla/plugins/PPluginWidgetParent.h"
+#include "nsAutoPtr.h"
+#include "nsIWidget.h"
+#include "nsCOMPtr.h"
+
+#if defined(MOZ_WIDGET_GTK)
+class nsPluginNativeWindowGtk;
+#endif
+
+namespace mozilla {
+
+namespace dom {
+class TabParent;
+} // namespace dom
+
+namespace plugins {
+
+class PluginWidgetParent : public PPluginWidgetParent
+{
+public:
+ PluginWidgetParent();
+ virtual ~PluginWidgetParent();
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ virtual bool RecvCreate(nsresult* aResult, uint64_t* aScrollCaptureId,
+ uintptr_t* aPluginInstanceId) override;
+ virtual bool RecvSetFocus(const bool& aRaise) override;
+ virtual bool RecvGetNativePluginPort(uintptr_t* value) override;
+ bool RecvSetNativeChildWindow(const uintptr_t& aChildWindow) override;
+
+ // Helper for compositor checks on the channel
+ bool ActorDestroyed() { return !mWidget; }
+
+ // Called by PBrowser when it receives a Destroy() call from the child.
+ void ParentDestroy();
+
+ // Sets mWidget's parent
+ void SetParent(nsIWidget* aParent);
+
+private:
+ // The tab our connection is associated with.
+ mozilla::dom::TabParent* GetTabParent();
+
+private:
+ void KillWidget();
+
+ // The chrome side native widget.
+ nsCOMPtr<nsIWidget> mWidget;
+#if defined(MOZ_WIDGET_GTK)
+ nsAutoPtr<nsPluginNativeWindowGtk> mWrapper;
+#endif
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginWidgetParent_h
+
diff --git a/dom/plugins/ipc/StreamNotifyChild.h b/dom/plugins/ipc/StreamNotifyChild.h
new file mode 100644
index 000000000..b43a76111
--- /dev/null
+++ b/dom/plugins/ipc/StreamNotifyChild.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=2 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 mozilla_plugins_StreamNotifyChild_h
+#define mozilla_plugins_StreamNotifyChild_h
+
+#include "mozilla/plugins/PStreamNotifyChild.h"
+
+namespace mozilla {
+namespace plugins {
+
+class BrowserStreamChild;
+
+class StreamNotifyChild : public PStreamNotifyChild
+{
+ friend class PluginInstanceChild;
+ friend class BrowserStreamChild;
+
+public:
+ explicit StreamNotifyChild(const nsCString& aURL)
+ : mURL(aURL)
+ , mClosure(nullptr)
+ , mBrowserStream(nullptr)
+ { }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ void SetValid(void* aClosure) {
+ mClosure = aClosure;
+ }
+
+ void NPP_URLNotify(NPReason reason);
+
+private:
+ virtual bool Recv__delete__(const NPReason& reason) override;
+
+ bool RecvRedirectNotify(const nsCString& url, const int32_t& status) override;
+
+ /**
+ * If a stream is created for this this URLNotify, we associate the objects
+ * so that the NPP_URLNotify call is not fired before the stream data is
+ * completely delivered. The BrowserStreamChild takes responsibility for
+ * calling NPP_URLNotify and deleting this object.
+ */
+ void SetAssociatedStream(BrowserStreamChild* bs);
+
+ nsCString mURL;
+ void* mClosure;
+
+ /**
+ * If mBrowserStream is true, it is responsible for deleting this C++ object
+ * and DeallocPStreamNotify is not, so that the delayed delivery of
+ * NPP_URLNotify is possible.
+ */
+ BrowserStreamChild* mBrowserStream;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
diff --git a/dom/plugins/ipc/StreamNotifyParent.h b/dom/plugins/ipc/StreamNotifyParent.h
new file mode 100644
index 000000000..dac5f1c28
--- /dev/null
+++ b/dom/plugins/ipc/StreamNotifyParent.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=2 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 mozilla_plugins_StreamNotifyParent_h
+#define mozilla_plugins_StreamNotifyParent_h
+
+#include "mozilla/plugins/PStreamNotifyParent.h"
+
+namespace mozilla {
+namespace plugins {
+
+class StreamNotifyParent : public PStreamNotifyParent
+{
+ friend class PluginInstanceParent;
+
+ StreamNotifyParent()
+ : mDestructionFlag(nullptr)
+ { }
+ ~StreamNotifyParent() {
+ if (mDestructionFlag)
+ *mDestructionFlag = true;
+ }
+
+public:
+ // If we are destroyed within the call to NPN_GetURLNotify, notify the caller
+ // so that we aren't destroyed again. see bug 536437.
+ void SetDestructionFlag(bool* flag) {
+ mDestructionFlag = flag;
+ }
+ void ClearDestructionFlag() {
+ mDestructionFlag = nullptr;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+ bool RecvRedirectNotifyResponse(const bool& allow) override;
+
+ bool* mDestructionFlag;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
diff --git a/dom/plugins/ipc/hangui/HangUIDlg.h b/dom/plugins/ipc/hangui/HangUIDlg.h
new file mode 100644
index 000000000..47339acc2
--- /dev/null
+++ b/dom/plugins/ipc/hangui/HangUIDlg.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 mozilla_plugins_HangUIDlg_h
+#define mozilla_plugins_HangUIDlg_h
+
+#define IDD_HANGUIDLG 102
+#define IDC_MSG 1000
+#define IDC_CONTINUE 1001
+#define IDC_STOP 1002
+#define IDC_NOFUTURE 1003
+#define IDC_DLGICON 1004
+
+#endif // mozilla_plugins_HangUIDlg_h
+
diff --git a/dom/plugins/ipc/hangui/HangUIDlg.rc b/dom/plugins/ipc/hangui/HangUIDlg.rc
new file mode 100644
index 000000000..62e98ca24
--- /dev/null
+++ b/dom/plugins/ipc/hangui/HangUIDlg.rc
@@ -0,0 +1,26 @@
+/* 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 "HangUIDlg.h"
+#include <windows.h>
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_HANGUIDLG DIALOGEX 0, 0, 400, 75
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "Continue",IDC_CONTINUE,283,51,50,18
+ PUSHBUTTON "Stop",IDC_STOP,341,51,50,18
+ CONTROL "Check1",IDC_NOFUTURE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,37,32,354,10
+ LTEXT "Static",IDC_MSG,37,7,353,24
+ ICON "",IDC_DLGICON,7,7,20,20
+END
+
diff --git a/dom/plugins/ipc/hangui/MiniShmBase.h b/dom/plugins/ipc/hangui/MiniShmBase.h
new file mode 100644
index 000000000..0ac8840dd
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmBase.h
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 mozilla_plugins_MiniShmBase_h
+#define mozilla_plugins_MiniShmBase_h
+
+#include "base/basictypes.h"
+
+#include "nsDebug.h"
+
+#include <windows.h>
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This class is used to provide RAII semantics for mapped views.
+ * @see ScopedHandle
+ */
+class ScopedMappedFileView
+{
+public:
+ explicit
+ ScopedMappedFileView(LPVOID aView)
+ : mView(aView)
+ {
+ }
+
+ ~ScopedMappedFileView()
+ {
+ Close();
+ }
+
+ void
+ Close()
+ {
+ if (mView) {
+ ::UnmapViewOfFile(mView);
+ mView = nullptr;
+ }
+ }
+
+ void
+ Set(LPVOID aView)
+ {
+ Close();
+ mView = aView;
+ }
+
+ LPVOID
+ Get() const
+ {
+ return mView;
+ }
+
+ LPVOID
+ Take()
+ {
+ LPVOID result = mView;
+ mView = nullptr;
+ return result;
+ }
+
+ operator LPVOID()
+ {
+ return mView;
+ }
+
+ bool
+ IsValid() const
+ {
+ return (mView);
+ }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedMappedFileView);
+
+ LPVOID mView;
+};
+
+class MiniShmBase;
+
+class MiniShmObserver
+{
+public:
+ /**
+ * This function is called whenever there is a new shared memory request.
+ * @param aMiniShmObj MiniShmBase object that may be used to read and
+ * write from shared memory.
+ */
+ virtual void OnMiniShmEvent(MiniShmBase *aMiniShmObj) = 0;
+ /**
+ * This function is called once when a MiniShmParent and a MiniShmChild
+ * object have successfully negotiated a connection.
+ *
+ * @param aMiniShmObj MiniShmBase object that may be used to read and
+ * write from shared memory.
+ */
+ virtual void OnMiniShmConnect(MiniShmBase *aMiniShmObj) { }
+};
+
+/**
+ * Base class for MiniShm connections. This class defines the common
+ * interfaces and code between parent and child.
+ */
+class MiniShmBase
+{
+public:
+ /**
+ * Obtains a writable pointer into shared memory of type T.
+ * typename T must be plain-old-data and contain an unsigned integral
+ * member T::identifier that uniquely identifies T with respect to
+ * other types used by the protocol being implemented.
+ *
+ * @param aPtr Pointer to receive the shared memory address.
+ * This value is set if and only if the function
+ * succeeded.
+ * @return NS_OK if and only if aPtr was successfully obtained.
+ * NS_ERROR_ILLEGAL_VALUE if type T is not valid for MiniShm.
+ * NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
+ * NS_ERROR_NOT_AVAILABLE if the memory is not safe to write.
+ */
+ template<typename T> nsresult
+ GetWritePtr(T*& aPtr)
+ {
+ if (!mWriteHeader || !mGuard) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (sizeof(T) > mPayloadMaxLen ||
+ T::identifier <= RESERVED_CODE_LAST) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (::WaitForSingleObject(mGuard, mTimeout) != WAIT_OBJECT_0) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ mWriteHeader->mId = T::identifier;
+ mWriteHeader->mPayloadLen = sizeof(T);
+ aPtr = reinterpret_cast<T*>(mWriteHeader + 1);
+ return NS_OK;
+ }
+
+ /**
+ * Obtains a readable pointer into shared memory of type T.
+ * typename T must be plain-old-data and contain an unsigned integral
+ * member T::identifier that uniquely identifies T with respect to
+ * other types used by the protocol being implemented.
+ *
+ * @param aPtr Pointer to receive the shared memory address.
+ * This value is set if and only if the function
+ * succeeded.
+ * @return NS_OK if and only if aPtr was successfully obtained.
+ * NS_ERROR_ILLEGAL_VALUE if type T is not valid for MiniShm or if
+ * type T does not match the type of the data
+ * stored in shared memory.
+ * NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
+ */
+ template<typename T> nsresult
+ GetReadPtr(const T*& aPtr)
+ {
+ if (!mReadHeader) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (mReadHeader->mId != T::identifier ||
+ sizeof(T) != mReadHeader->mPayloadLen) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aPtr = reinterpret_cast<const T*>(mReadHeader + 1);
+ return NS_OK;
+ }
+
+ /**
+ * Fires the peer's event causing its request handler to execute.
+ *
+ * @return Should return NS_OK if the send was successful.
+ */
+ virtual nsresult
+ Send() = 0;
+
+protected:
+ /**
+ * MiniShm reserves some identifier codes for its own use. Any
+ * identifiers used by MiniShm protocol implementations must be
+ * greater than RESERVED_CODE_LAST.
+ */
+ enum ReservedCodes
+ {
+ RESERVED_CODE_INIT = 0,
+ RESERVED_CODE_INIT_COMPLETE = 1,
+ RESERVED_CODE_LAST = RESERVED_CODE_INIT_COMPLETE
+ };
+
+ struct MiniShmHeader
+ {
+ unsigned int mId;
+ unsigned int mPayloadLen;
+ };
+
+ struct MiniShmInit
+ {
+ enum identifier_t
+ {
+ identifier = RESERVED_CODE_INIT
+ };
+ HANDLE mParentEvent;
+ HANDLE mParentGuard;
+ HANDLE mChildEvent;
+ HANDLE mChildGuard;
+ };
+
+ struct MiniShmInitComplete
+ {
+ enum identifier_t
+ {
+ identifier = RESERVED_CODE_INIT_COMPLETE
+ };
+ bool mSucceeded;
+ };
+
+ MiniShmBase()
+ : mObserver(nullptr),
+ mWriteHeader(nullptr),
+ mReadHeader(nullptr),
+ mPayloadMaxLen(0),
+ mGuard(nullptr),
+ mTimeout(INFINITE)
+ {
+ }
+ virtual ~MiniShmBase()
+ { }
+
+ virtual void
+ OnEvent()
+ {
+ if (mObserver) {
+ mObserver->OnMiniShmEvent(this);
+ }
+ }
+
+ virtual void
+ OnConnect()
+ {
+ if (mObserver) {
+ mObserver->OnMiniShmConnect(this);
+ }
+ }
+
+ nsresult
+ SetView(LPVOID aView, const unsigned int aSize, bool aIsChild)
+ {
+ if (!aView || aSize <= 2 * sizeof(MiniShmHeader)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ // Divide the region into halves for parent and child
+ if (aIsChild) {
+ mReadHeader = static_cast<MiniShmHeader*>(aView);
+ mWriteHeader = reinterpret_cast<MiniShmHeader*>(static_cast<char*>(aView)
+ + aSize / 2U);
+ } else {
+ mWriteHeader = static_cast<MiniShmHeader*>(aView);
+ mReadHeader = reinterpret_cast<MiniShmHeader*>(static_cast<char*>(aView)
+ + aSize / 2U);
+ }
+ mPayloadMaxLen = aSize / 2U - sizeof(MiniShmHeader);
+ return NS_OK;
+ }
+
+ nsresult
+ SetGuard(HANDLE aGuard, DWORD aTimeout)
+ {
+ if (!aGuard || !aTimeout) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ mGuard = aGuard;
+ mTimeout = aTimeout;
+ return NS_OK;
+ }
+
+ inline void
+ SetObserver(MiniShmObserver *aObserver) { mObserver = aObserver; }
+
+ /**
+ * Obtains a writable pointer into shared memory of type T. This version
+ * differs from GetWritePtr in that it allows typename T to be one of
+ * the private data structures declared in MiniShmBase.
+ *
+ * @param aPtr Pointer to receive the shared memory address.
+ * This value is set if and only if the function
+ * succeeded.
+ * @return NS_OK if and only if aPtr was successfully obtained.
+ * NS_ERROR_ILLEGAL_VALUE if type T not an internal MiniShm struct.
+ * NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
+ */
+ template<typename T> nsresult
+ GetWritePtrInternal(T*& aPtr)
+ {
+ if (!mWriteHeader) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (sizeof(T) > mPayloadMaxLen ||
+ T::identifier > RESERVED_CODE_LAST) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ mWriteHeader->mId = T::identifier;
+ mWriteHeader->mPayloadLen = sizeof(T);
+ aPtr = reinterpret_cast<T*>(mWriteHeader + 1);
+ return NS_OK;
+ }
+
+ static VOID CALLBACK
+ SOnEvent(PVOID aContext, BOOLEAN aIsTimer)
+ {
+ MiniShmBase* object = static_cast<MiniShmBase*>(aContext);
+ object->OnEvent();
+ }
+
+private:
+ MiniShmObserver* mObserver;
+ MiniShmHeader* mWriteHeader;
+ MiniShmHeader* mReadHeader;
+ unsigned int mPayloadMaxLen;
+ HANDLE mGuard;
+ DWORD mTimeout;
+
+ DISALLOW_COPY_AND_ASSIGN(MiniShmBase);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_MiniShmBase_h
+
diff --git a/dom/plugins/ipc/hangui/MiniShmChild.cpp b/dom/plugins/ipc/hangui/MiniShmChild.cpp
new file mode 100644
index 000000000..0683340a1
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmChild.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "MiniShmChild.h"
+
+#include <limits>
+#include <sstream>
+
+namespace mozilla {
+namespace plugins {
+
+MiniShmChild::MiniShmChild()
+ : mParentEvent(nullptr),
+ mParentGuard(nullptr),
+ mChildEvent(nullptr),
+ mChildGuard(nullptr),
+ mFileMapping(nullptr),
+ mRegWait(nullptr),
+ mView(nullptr),
+ mTimeout(INFINITE)
+{}
+
+MiniShmChild::~MiniShmChild()
+{
+ if (mRegWait) {
+ ::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
+ }
+ if (mParentGuard) {
+ // Try to avoid shutting down while the parent's event handler is running.
+ ::WaitForSingleObject(mParentGuard, mTimeout);
+ ::CloseHandle(mParentGuard);
+ }
+ if (mParentEvent) {
+ ::CloseHandle(mParentEvent);
+ }
+ if (mChildEvent) {
+ ::CloseHandle(mChildEvent);
+ }
+ if (mChildGuard) {
+ ::CloseHandle(mChildGuard);
+ }
+ if (mView) {
+ ::UnmapViewOfFile(mView);
+ }
+ if (mFileMapping) {
+ ::CloseHandle(mFileMapping);
+ }
+}
+
+nsresult
+MiniShmChild::Init(MiniShmObserver* aObserver, const std::wstring& aCookie,
+ const DWORD aTimeout)
+{
+ if (aCookie.empty() || !aTimeout) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (mFileMapping) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+ std::wistringstream iss(aCookie);
+ HANDLE mapHandle = nullptr;
+ iss >> mapHandle;
+ if (!iss) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ ScopedMappedFileView view(::MapViewOfFile(mapHandle,
+ FILE_MAP_WRITE,
+ 0, 0, 0));
+ if (!view.IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+ MEMORY_BASIC_INFORMATION memInfo = {0};
+ SIZE_T querySize = ::VirtualQuery(view, &memInfo, sizeof(memInfo));
+ unsigned int mappingSize = 0;
+ if (querySize) {
+ if (memInfo.RegionSize <= std::numeric_limits<unsigned int>::max()) {
+ mappingSize = static_cast<unsigned int>(memInfo.RegionSize);
+ }
+ }
+ if (!querySize || !mappingSize) {
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv = SetView(view, mappingSize, true);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ const MiniShmInit* initStruct = nullptr;
+ rv = GetReadPtr(initStruct);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!initStruct->mParentEvent || !initStruct->mParentGuard ||
+ !initStruct->mChildEvent || !initStruct->mChildGuard) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = SetGuard(initStruct->mParentGuard, aTimeout);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!::RegisterWaitForSingleObject(&mRegWait,
+ initStruct->mChildEvent,
+ &SOnEvent,
+ this,
+ INFINITE,
+ WT_EXECUTEDEFAULT)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ MiniShmInitComplete* initCompleteStruct = nullptr;
+ rv = GetWritePtrInternal(initCompleteStruct);
+ if (NS_FAILED(rv)) {
+ ::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
+ mRegWait = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ initCompleteStruct->mSucceeded = true;
+
+ // We must set the member variables before we signal the event
+ mFileMapping = mapHandle;
+ mView = view.Take();
+ mParentEvent = initStruct->mParentEvent;
+ mParentGuard = initStruct->mParentGuard;
+ mChildEvent = initStruct->mChildEvent;
+ mChildGuard = initStruct->mChildGuard;
+ SetObserver(aObserver);
+ mTimeout = aTimeout;
+
+ rv = Send();
+ if (NS_FAILED(rv)) {
+ initCompleteStruct->mSucceeded = false;
+ mFileMapping = nullptr;
+ view.Set(mView);
+ mView = nullptr;
+ mParentEvent = nullptr;
+ mParentGuard = nullptr;
+ mChildEvent = nullptr;
+ mChildGuard = nullptr;
+ ::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
+ mRegWait = nullptr;
+ return rv;
+ }
+
+ OnConnect();
+ return NS_OK;
+}
+
+nsresult
+MiniShmChild::Send()
+{
+ if (!mParentEvent) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (!::SetEvent(mParentEvent)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+void
+MiniShmChild::OnEvent()
+{
+ MiniShmBase::OnEvent();
+ ::SetEvent(mChildGuard);
+}
+
+} // namespace plugins
+} // namespace mozilla
+
diff --git a/dom/plugins/ipc/hangui/MiniShmChild.h b/dom/plugins/ipc/hangui/MiniShmChild.h
new file mode 100644
index 000000000..19c9deea7
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmChild.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 mozilla_plugins_MiniShmChild_h
+#define mozilla_plugins_MiniShmChild_h
+
+#include "MiniShmBase.h"
+
+#include <string>
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This class provides a lightweight shared memory interface for a child
+ * process in Win32.
+ * This code assumes that there is a parent-child relationship between
+ * processes, as it inherits handles from the parent process.
+ * Note that this class is *not* an IPDL actor.
+ *
+ * @see MiniShmParent
+ */
+class MiniShmChild : public MiniShmBase
+{
+public:
+ MiniShmChild();
+ virtual ~MiniShmChild();
+
+ /**
+ * Initialize shared memory on the child side.
+ *
+ * @param aObserver A MiniShmObserver object to receive event notifications.
+ * @param aCookie Cookie obtained from MiniShmParent::GetCookie
+ * @param aTimeout Timeout in milliseconds.
+ * @return nsresult error code
+ */
+ nsresult
+ Init(MiniShmObserver* aObserver, const std::wstring& aCookie,
+ const DWORD aTimeout);
+
+ virtual nsresult
+ Send() override;
+
+protected:
+ void
+ OnEvent() override;
+
+private:
+ HANDLE mParentEvent;
+ HANDLE mParentGuard;
+ HANDLE mChildEvent;
+ HANDLE mChildGuard;
+ HANDLE mFileMapping;
+ HANDLE mRegWait;
+ LPVOID mView;
+ DWORD mTimeout;
+
+ DISALLOW_COPY_AND_ASSIGN(MiniShmChild);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_MiniShmChild_h
+
diff --git a/dom/plugins/ipc/hangui/PluginHangUI.h b/dom/plugins/ipc/hangui/PluginHangUI.h
new file mode 100644
index 000000000..2c6df78bb
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUI.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 mozilla_plugins_PluginHangUI_h
+#define mozilla_plugins_PluginHangUI_h
+
+namespace mozilla {
+namespace plugins {
+
+enum HangUIUserResponse
+{
+ HANGUI_USER_RESPONSE_CANCEL = 1,
+ HANGUI_USER_RESPONSE_CONTINUE = 2,
+ HANGUI_USER_RESPONSE_STOP = 4,
+ HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN = 8
+};
+
+enum PluginHangUIStructID
+{
+ PLUGIN_HANGUI_COMMAND = 0x10,
+ PLUGIN_HANGUI_RESULT
+};
+
+struct PluginHangUICommand
+{
+ enum
+ {
+ identifier = PLUGIN_HANGUI_COMMAND
+ };
+ enum CmdCode
+ {
+ HANGUI_CMD_SHOW = 1,
+ HANGUI_CMD_CANCEL = 2
+ };
+ CmdCode mCode;
+};
+
+struct PluginHangUIResponse
+{
+ enum
+ {
+ identifier = PLUGIN_HANGUI_RESULT
+ };
+ unsigned int mResponseBits;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginHangUI_h
+
diff --git a/dom/plugins/ipc/hangui/PluginHangUIChild.cpp b/dom/plugins/ipc/hangui/PluginHangUIChild.cpp
new file mode 100644
index 000000000..c1730a207
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUIChild.cpp
@@ -0,0 +1,425 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "PluginHangUI.h"
+
+#include "PluginHangUIChild.h"
+#include "HangUIDlg.h"
+
+#include <assert.h>
+#include <commctrl.h>
+#include <windowsx.h>
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+namespace mozilla {
+namespace plugins {
+
+struct WinInfo
+{
+ WinInfo(HWND aHwnd, POINT& aPos, SIZE& aSize)
+ :hwnd(aHwnd)
+ {
+ pos.x = aPos.x;
+ pos.y = aPos.y;
+ size.cx = aSize.cx;
+ size.cy = aSize.cy;
+ }
+ HWND hwnd;
+ POINT pos;
+ SIZE size;
+};
+typedef std::vector<WinInfo> WinInfoVec;
+
+PluginHangUIChild* PluginHangUIChild::sSelf = nullptr;
+const int PluginHangUIChild::kExpectedMinimumArgc = 10;
+
+PluginHangUIChild::PluginHangUIChild()
+ : mResponseBits(0),
+ mParentWindow(nullptr),
+ mDlgHandle(nullptr),
+ mMainThread(nullptr),
+ mParentProcess(nullptr),
+ mRegWaitProcess(nullptr),
+ mIPCTimeoutMs(0)
+{
+}
+
+PluginHangUIChild::~PluginHangUIChild()
+{
+ if (mMainThread) {
+ CloseHandle(mMainThread);
+ }
+ if (mRegWaitProcess) {
+ UnregisterWaitEx(mRegWaitProcess, INVALID_HANDLE_VALUE);
+ }
+ if (mParentProcess) {
+ CloseHandle(mParentProcess);
+ }
+ sSelf = nullptr;
+}
+
+bool
+PluginHangUIChild::Init(int aArgc, wchar_t* aArgv[])
+{
+ if (aArgc < kExpectedMinimumArgc) {
+ return false;
+ }
+ unsigned int i = 1;
+ mMessageText = aArgv[i];
+ mWindowTitle = aArgv[++i];
+ mWaitBtnText = aArgv[++i];
+ mKillBtnText = aArgv[++i];
+ mNoFutureText = aArgv[++i];
+ std::wistringstream issHwnd(aArgv[++i]);
+ issHwnd >> reinterpret_cast<HANDLE&>(mParentWindow);
+ if (!issHwnd) {
+ return false;
+ }
+ std::wistringstream issProc(aArgv[++i]);
+ issProc >> mParentProcess;
+ if (!issProc) {
+ return false;
+ }
+ // Only set the App User Model ID if it's present in the args
+ if (wcscmp(aArgv[++i], L"-")) {
+ HMODULE shell32 = LoadLibrary(L"shell32.dll");
+ if (shell32) {
+ SETAPPUSERMODELID fSetAppUserModelID = (SETAPPUSERMODELID)
+ GetProcAddress(shell32, "SetCurrentProcessExplicitAppUserModelID");
+ if (fSetAppUserModelID) {
+ fSetAppUserModelID(aArgv[i]);
+ }
+ FreeLibrary(shell32);
+ }
+ }
+ std::wistringstream issTimeout(aArgv[++i]);
+ issTimeout >> mIPCTimeoutMs;
+ if (!issTimeout) {
+ return false;
+ }
+
+ nsresult rv = mMiniShm.Init(this,
+ std::wstring(aArgv[++i]),
+ IsDebuggerPresent() ? INFINITE : mIPCTimeoutMs);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ sSelf = this;
+ return true;
+}
+
+void
+PluginHangUIChild::OnMiniShmEvent(MiniShmBase* aMiniShmObj)
+{
+ const PluginHangUICommand* cmd = nullptr;
+ nsresult rv = aMiniShmObj->GetReadPtr(cmd);
+ assert(NS_SUCCEEDED(rv));
+ bool returnStatus = false;
+ if (NS_SUCCEEDED(rv)) {
+ switch (cmd->mCode) {
+ case PluginHangUICommand::HANGUI_CMD_SHOW:
+ returnStatus = RecvShow();
+ break;
+ case PluginHangUICommand::HANGUI_CMD_CANCEL:
+ returnStatus = RecvCancel();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// static
+INT_PTR CALLBACK
+PluginHangUIChild::SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode,
+ WPARAM aWParam, LPARAM aLParam)
+{
+ PluginHangUIChild *self = PluginHangUIChild::sSelf;
+ if (self) {
+ return self->HangUIDlgProc(aDlgHandle, aMsgCode, aWParam, aLParam);
+ }
+ return FALSE;
+}
+
+void
+PluginHangUIChild::ResizeButtons()
+{
+ // Control IDs are specified right-to-left as they appear in the dialog
+ UINT ids[] = { IDC_STOP, IDC_CONTINUE };
+ UINT numIds = sizeof(ids)/sizeof(ids[0]);
+
+ // Pass 1: Compute the ideal size
+ bool needResizing = false;
+ SIZE idealSize = {0};
+ WinInfoVec winInfo;
+ for (UINT i = 0; i < numIds; ++i) {
+ HWND wnd = GetDlgItem(mDlgHandle, ids[i]);
+ if (!wnd) {
+ return;
+ }
+
+ // Get the button's dimensions in screen coordinates
+ RECT curRect;
+ if (!GetWindowRect(wnd, &curRect)) {
+ return;
+ }
+
+ // Get (x,y) position of the button in client coordinates
+ POINT pt;
+ pt.x = curRect.left;
+ pt.y = curRect.top;
+ if (!ScreenToClient(mDlgHandle, &pt)) {
+ return;
+ }
+
+ // Request the button's text margins
+ RECT margins;
+ if (!Button_GetTextMargin(wnd, &margins)) {
+ return;
+ }
+
+ // Compute the button's width and height
+ SIZE curSize;
+ curSize.cx = curRect.right - curRect.left;
+ curSize.cy = curRect.bottom - curRect.top;
+
+ // Request the button's ideal width and height and add in the margins
+ SIZE size = {0};
+ if (!Button_GetIdealSize(wnd, &size)) {
+ return;
+ }
+ size.cx += margins.left + margins.right;
+ size.cy += margins.top + margins.bottom;
+
+ // Size all buttons to be the same width as the longest button encountered
+ idealSize.cx = std::max(idealSize.cx, size.cx);
+ idealSize.cy = std::max(idealSize.cy, size.cy);
+
+ // We won't bother resizing unless we need extra space
+ if (idealSize.cx > curSize.cx) {
+ needResizing = true;
+ }
+
+ // Save the relevant info for the resize, if any. We do this even if
+ // needResizing is false because another button may trigger a resize later.
+ winInfo.push_back(WinInfo(wnd, pt, curSize));
+ }
+
+ if (!needResizing) {
+ return;
+ }
+
+ // Pass 2: Resize the windows
+ int deltaX = 0;
+ HDWP hwp = BeginDeferWindowPos((int) winInfo.size());
+ if (!hwp) {
+ return;
+ }
+ for (WinInfoVec::const_iterator itr = winInfo.begin();
+ itr != winInfo.end(); ++itr) {
+ // deltaX accumulates the size changes so that each button's x coordinate
+ // can compensate for the width increases
+ deltaX += idealSize.cx - itr->size.cx;
+ hwp = DeferWindowPos(hwp, itr->hwnd, nullptr,
+ itr->pos.x - deltaX, itr->pos.y,
+ idealSize.cx, itr->size.cy,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ if (!hwp) {
+ return;
+ }
+ }
+ EndDeferWindowPos(hwp);
+}
+
+INT_PTR
+PluginHangUIChild::HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
+ LPARAM aLParam)
+{
+ mDlgHandle = aDlgHandle;
+ switch (aMsgCode) {
+ case WM_INITDIALOG: {
+ // Register a wait on the Firefox process so that we will be informed
+ // if it dies while the dialog is showing
+ RegisterWaitForSingleObject(&mRegWaitProcess,
+ mParentProcess,
+ &SOnParentProcessExit,
+ this,
+ INFINITE,
+ WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE);
+ SetWindowText(aDlgHandle, mWindowTitle);
+ SetDlgItemText(aDlgHandle, IDC_MSG, mMessageText);
+ SetDlgItemText(aDlgHandle, IDC_NOFUTURE, mNoFutureText);
+ SetDlgItemText(aDlgHandle, IDC_CONTINUE, mWaitBtnText);
+ SetDlgItemText(aDlgHandle, IDC_STOP, mKillBtnText);
+ ResizeButtons();
+ HANDLE icon = LoadImage(nullptr, IDI_QUESTION, IMAGE_ICON, 0, 0,
+ LR_DEFAULTSIZE | LR_SHARED);
+ if (icon) {
+ SendDlgItemMessage(aDlgHandle, IDC_DLGICON, STM_SETICON, (WPARAM)icon, 0);
+ }
+ EnableWindow(mParentWindow, FALSE);
+ return TRUE;
+ }
+ case WM_CLOSE: {
+ mResponseBits |= HANGUI_USER_RESPONSE_CANCEL;
+ EndDialog(aDlgHandle, 0);
+ SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+ case WM_COMMAND: {
+ switch (LOWORD(aWParam)) {
+ case IDC_CONTINUE:
+ if (HIWORD(aWParam) == BN_CLICKED) {
+ mResponseBits |= HANGUI_USER_RESPONSE_CONTINUE;
+ EndDialog(aDlgHandle, 1);
+ SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+ break;
+ case IDC_STOP:
+ if (HIWORD(aWParam) == BN_CLICKED) {
+ mResponseBits |= HANGUI_USER_RESPONSE_STOP;
+ EndDialog(aDlgHandle, 1);
+ SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+ break;
+ case IDC_NOFUTURE:
+ if (HIWORD(aWParam) == BN_CLICKED) {
+ if (Button_GetCheck(GetDlgItem(aDlgHandle,
+ IDC_NOFUTURE)) == BST_CHECKED) {
+ mResponseBits |= HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN;
+ } else {
+ mResponseBits &=
+ ~static_cast<DWORD>(HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN);
+ }
+ SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case WM_DESTROY: {
+ EnableWindow(mParentWindow, TRUE);
+ SetForegroundWindow(mParentWindow);
+ break;
+ }
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+// static
+VOID CALLBACK
+PluginHangUIChild::SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer)
+{
+ // Simulate a cancel if the parent process died
+ PluginHangUIChild* object = static_cast<PluginHangUIChild*>(aObject);
+ object->RecvCancel();
+}
+
+bool
+PluginHangUIChild::RecvShow()
+{
+ return (QueueUserAPC(&ShowAPC,
+ mMainThread,
+ reinterpret_cast<ULONG_PTR>(this)));
+}
+
+bool
+PluginHangUIChild::Show()
+{
+ INT_PTR dlgResult = DialogBox(GetModuleHandle(nullptr),
+ MAKEINTRESOURCE(IDD_HANGUIDLG),
+ nullptr,
+ &SHangUIDlgProc);
+ mDlgHandle = nullptr;
+ assert(dlgResult != -1);
+ bool result = false;
+ if (dlgResult != -1) {
+ PluginHangUIResponse* response = nullptr;
+ nsresult rv = mMiniShm.GetWritePtr(response);
+ if (NS_SUCCEEDED(rv)) {
+ response->mResponseBits = mResponseBits;
+ result = NS_SUCCEEDED(mMiniShm.Send());
+ }
+ }
+ return result;
+}
+
+// static
+VOID CALLBACK
+PluginHangUIChild::ShowAPC(ULONG_PTR aContext)
+{
+ PluginHangUIChild* object = reinterpret_cast<PluginHangUIChild*>(aContext);
+ object->Show();
+}
+
+bool
+PluginHangUIChild::RecvCancel()
+{
+ if (mDlgHandle) {
+ PostMessage(mDlgHandle, WM_CLOSE, 0, 0);
+ }
+ return true;
+}
+
+bool
+PluginHangUIChild::WaitForDismissal()
+{
+ if (!SetMainThread()) {
+ return false;
+ }
+ DWORD waitResult = WaitForSingleObjectEx(mParentProcess,
+ mIPCTimeoutMs,
+ TRUE);
+ return waitResult == WAIT_OBJECT_0 ||
+ waitResult == WAIT_IO_COMPLETION;
+}
+
+bool
+PluginHangUIChild::SetMainThread()
+{
+ if (mMainThread) {
+ CloseHandle(mMainThread);
+ mMainThread = nullptr;
+ }
+ mMainThread = OpenThread(THREAD_SET_CONTEXT,
+ FALSE,
+ GetCurrentThreadId());
+ return !(!mMainThread);
+}
+
+} // namespace plugins
+} // namespace mozilla
+
+#ifdef __MINGW32__
+extern "C"
+#endif
+int
+wmain(int argc, wchar_t *argv[])
+{
+ INITCOMMONCONTROLSEX icc = { sizeof(INITCOMMONCONTROLSEX),
+ ICC_STANDARD_CLASSES };
+ if (!InitCommonControlsEx(&icc)) {
+ return 1;
+ }
+ mozilla::plugins::PluginHangUIChild hangui;
+ if (!hangui.Init(argc, argv)) {
+ return 1;
+ }
+ if (!hangui.WaitForDismissal()) {
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/dom/plugins/ipc/hangui/PluginHangUIChild.h b/dom/plugins/ipc/hangui/PluginHangUIChild.h
new file mode 100644
index 000000000..000e003ec
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUIChild.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 mozilla_plugins_PluginHangUIChild_h
+#define mozilla_plugins_PluginHangUIChild_h
+
+#include "MiniShmChild.h"
+
+#include <string>
+
+#include <windows.h>
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This class implements the plugin-hang-ui.
+ *
+ * NOTE: PluginHangUIChild is *not* an IPDL actor! In this case, "Child"
+ * is describing the fact that plugin-hang-ui is a child process to the
+ * firefox process, which is the PluginHangUIParent.
+ * PluginHangUIParent and PluginHangUIChild are a matched pair.
+ * @see PluginHangUIParent
+ */
+class PluginHangUIChild : public MiniShmObserver
+{
+public:
+ PluginHangUIChild();
+ virtual ~PluginHangUIChild();
+
+ bool
+ Init(int aArgc, wchar_t* aArgv[]);
+
+ /**
+ * Displays the Plugin Hang UI and does not return until the UI has
+ * been dismissed.
+ *
+ * @return true if the UI was displayed and the user response was
+ * successfully sent back to the parent. Otherwise false.
+ */
+ bool
+ Show();
+
+ /**
+ * Causes the calling thread to wait either for the Hang UI to be
+ * dismissed or for the parent process to terminate. This should
+ * be called by the main thread.
+ *
+ * @return true unless there was an error initiating the wait
+ */
+ bool
+ WaitForDismissal();
+
+ virtual void
+ OnMiniShmEvent(MiniShmBase* aMiniShmObj) override;
+
+private:
+ bool
+ RecvShow();
+
+ bool
+ RecvCancel();
+
+ bool
+ SetMainThread();
+
+ void
+ ResizeButtons();
+
+ INT_PTR
+ HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam, LPARAM aLParam);
+
+ static VOID CALLBACK
+ ShowAPC(ULONG_PTR aContext);
+
+ static INT_PTR CALLBACK
+ SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
+ LPARAM aLParam);
+
+ static VOID CALLBACK
+ SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer);
+
+ static PluginHangUIChild *sSelf;
+
+ const wchar_t* mMessageText;
+ const wchar_t* mWindowTitle;
+ const wchar_t* mWaitBtnText;
+ const wchar_t* mKillBtnText;
+ const wchar_t* mNoFutureText;
+ unsigned int mResponseBits;
+ HWND mParentWindow;
+ HWND mDlgHandle;
+ HANDLE mMainThread;
+ HANDLE mParentProcess;
+ HANDLE mRegWaitProcess;
+ DWORD mIPCTimeoutMs;
+ MiniShmChild mMiniShm;
+
+ static const int kExpectedMinimumArgc;
+
+ typedef HRESULT (WINAPI *SETAPPUSERMODELID)(PCWSTR);
+
+ DISALLOW_COPY_AND_ASSIGN(PluginHangUIChild);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginHangUIChild_h
+
diff --git a/dom/plugins/ipc/hangui/module.ver b/dom/plugins/ipc/hangui/module.ver
new file mode 100644
index 000000000..d11506f4a
--- /dev/null
+++ b/dom/plugins/ipc/hangui/module.ver
@@ -0,0 +1,6 @@
+WIN32_MODULE_COMPANYNAME=Mozilla Corporation
+WIN32_MODULE_PRODUCTVERSION=@MOZ_APP_WINVERSION@
+WIN32_MODULE_PRODUCTVERSION_STRING=@MOZ_APP_VERSION@
+WIN32_MODULE_DESCRIPTION=Plugin Hang UI for @MOZ_APP_DISPLAYNAME@
+WIN32_MODULE_PRODUCTNAME=@MOZ_APP_DISPLAYNAME@
+WIN32_MODULE_NAME=@MOZ_APP_DISPLAYNAME@
diff --git a/dom/plugins/ipc/hangui/moz.build b/dom/plugins/ipc/hangui/moz.build
new file mode 100644
index 000000000..0b84cfb8f
--- /dev/null
+++ b/dom/plugins/ipc/hangui/moz.build
@@ -0,0 +1,27 @@
+# -*- 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/.
+
+Program('plugin-hang-ui')
+
+UNIFIED_SOURCES += [
+ 'MiniShmChild.cpp',
+ 'PluginHangUIChild.cpp',
+]
+include('/ipc/chromium/chromium-config.mozbuild')
+
+DEFINES['NS_NO_XPCOM'] = True
+DEFINES['_HAS_EXCEPTIONS'] = 0
+
+DISABLE_STL_WRAPPING = True
+
+if CONFIG['GNU_CC']:
+ WIN32_EXE_LDFLAGS += ['-municode']
+
+RCINCLUDE = 'HangUIDlg.rc'
+
+OS_LIBS += [
+ 'comctl32',
+]
diff --git a/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest b/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest
new file mode 100644
index 000000000..8d6149c58
--- /dev/null
+++ b/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="*"
+ name="plugin-hang-ui"
+ type="win32"
+/>
+<description>Firefox Plugin Hang User Interface</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+ <ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+ <ms_asmv3:security>
+ <ms_asmv3:requestedPrivileges>
+ <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </ms_asmv3:requestedPrivileges>
+ </ms_asmv3:security>
+ </ms_asmv3:trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/dom/plugins/ipc/interpose/moz.build b/dom/plugins/ipc/interpose/moz.build
new file mode 100644
index 000000000..8bd8ee651
--- /dev/null
+++ b/dom/plugins/ipc/interpose/moz.build
@@ -0,0 +1,13 @@
+# -*- 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/.
+
+SharedLibrary('plugin_child_interpose')
+
+UNIFIED_SOURCES += [ "%s.mm" % (LIBRARY_NAME) ]
+
+OS_LIBS += ['-framework Carbon']
+
+DIST_INSTALL = True
diff --git a/dom/plugins/ipc/interpose/plugin_child_interpose.mm b/dom/plugins/ipc/interpose/plugin_child_interpose.mm
new file mode 100644
index 000000000..58c39b0a1
--- /dev/null
+++ b/dom/plugins/ipc/interpose/plugin_child_interpose.mm
@@ -0,0 +1,134 @@
+/* -*- 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/. */
+
+// Use "dyld interposing" to hook methods imported from other libraries in the
+// plugin child process. The basic technique is described at
+// http://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73&lpg=PA73&dq=__interpose&source=bl&ots=OJnnXZYpZC&sig=o7I3lXvoduUi13SrPfOON7o3do4&hl=en&ei=AoehS9brCYGQNrvsmeUM&sa=X&oi=book_result&ct=result&resnum=6&ved=0CBsQ6AEwBQ#v=onepage&q=__interpose&f=false.
+// The idea of doing it for the plugin child process comes from Chromium code,
+// particularly from plugin_carbon_interpose_mac.cc
+// (http://codesearch.google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/chrome/browser/plugin_carbon_interpose_mac.cc&q=nscursor&exact_package=chromium&d=1&l=168)
+// and from PluginProcessHost::Init() in plugin_process_host.cc
+// (http://codesearch.google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/content/browser/plugin_process_host.cc&q=nscursor&exact_package=chromium&d=1&l=222).
+
+// These hooks are needed to make certain OS calls work from the child process
+// (a background process) that would normally only work when called in the
+// parent process (the foreground process). They allow us to serialize
+// information from the child process to the parent process, so that the same
+// (or equivalent) calls can be made from the parent process.
+
+// This file lives in a seperate module (libplugin_child_interpose.dylib),
+// which will get loaded by the OS before any other modules when the plugin
+// child process is launched (from GeckoChildProcessHost::
+// PerformAsyncLaunchInternal()). For this reason it shouldn't link in other
+// browser modules when loaded. Instead it should use dlsym() to load
+// pointers to the methods it wants to call in other modules.
+
+#if !defined(__LP64__)
+
+#include <dlfcn.h>
+#import <Carbon/Carbon.h>
+
+// The header file QuickdrawAPI.h is missing on OS X 10.7 and up (though the
+// QuickDraw APIs defined in it are still present) -- so we need to supply the
+// relevant parts of its contents here. It's likely that Apple will eventually
+// remove the APIs themselves (probably in OS X 10.8), so we need to make them
+// weak imports, and test for their presence before using them.
+#if !defined(__QUICKDRAWAPI__)
+
+struct Cursor;
+extern "C" void SetCursor(const Cursor * crsr) __attribute__((weak_import));
+
+#endif /* __QUICKDRAWAPI__ */
+
+BOOL (*OnSetThemeCursorPtr) (ThemeCursor) = NULL;
+BOOL (*OnSetCursorPtr) (const Cursor*) = NULL;
+BOOL (*OnHideCursorPtr) () = NULL;
+BOOL (*OnShowCursorPtr) () = NULL;
+
+static BOOL loadXULPtrs()
+{
+ if (!OnSetThemeCursorPtr) {
+ // mac_plugin_interposing_child_OnSetThemeCursor(ThemeCursor cursor) is in
+ // PluginInterposeOSX.mm
+ OnSetThemeCursorPtr = (BOOL(*)(ThemeCursor))
+ dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnSetThemeCursor");
+ }
+ if (!OnSetCursorPtr) {
+ // mac_plugin_interposing_child_OnSetCursor(const Cursor* cursor) is in
+ // PluginInterposeOSX.mm
+ OnSetCursorPtr = (BOOL(*)(const Cursor*))
+ dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnSetCursor");
+ }
+ if (!OnHideCursorPtr) {
+ // mac_plugin_interposing_child_OnHideCursor() is in PluginInterposeOSX.mm
+ OnHideCursorPtr = (BOOL(*)())
+ dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnHideCursor");
+ }
+ if (!OnShowCursorPtr) {
+ // mac_plugin_interposing_child_OnShowCursor() is in PluginInterposeOSX.mm
+ OnShowCursorPtr = (BOOL(*)())
+ dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnShowCursor");
+ }
+ return (OnSetCursorPtr && OnSetThemeCursorPtr && OnHideCursorPtr && OnShowCursorPtr);
+}
+
+static OSStatus MacPluginChildSetThemeCursor(ThemeCursor cursor)
+{
+ if (loadXULPtrs()) {
+ OnSetThemeCursorPtr(cursor);
+ }
+ return ::SetThemeCursor(cursor);
+}
+
+static void MacPluginChildSetCursor(const Cursor* cursor)
+{
+ if (::SetCursor) {
+ if (loadXULPtrs()) {
+ OnSetCursorPtr(cursor);
+ }
+ ::SetCursor(cursor);
+ }
+}
+
+static CGError MacPluginChildCGDisplayHideCursor(CGDirectDisplayID display)
+{
+ if (loadXULPtrs()) {
+ OnHideCursorPtr();
+ }
+ return ::CGDisplayHideCursor(display);
+}
+
+static CGError MacPluginChildCGDisplayShowCursor(CGDirectDisplayID display)
+{
+ if (loadXULPtrs()) {
+ OnShowCursorPtr();
+ }
+ return ::CGDisplayShowCursor(display);
+}
+
+#pragma mark -
+
+struct interpose_substitution {
+ const void* replacement;
+ const void* original;
+};
+
+#define INTERPOSE_FUNCTION(function) \
+ { reinterpret_cast<const void*>(MacPluginChild##function), \
+ reinterpret_cast<const void*>(function) }
+
+__attribute__((used)) static const interpose_substitution substitutions[]
+ __attribute__((section("__DATA, __interpose"))) = {
+ INTERPOSE_FUNCTION(SetThemeCursor),
+ INTERPOSE_FUNCTION(CGDisplayHideCursor),
+ INTERPOSE_FUNCTION(CGDisplayShowCursor),
+ // SetCursor() and other QuickDraw APIs will probably be removed in OS X
+ // 10.8. But this will make 'SetCursor' NULL, which will just stop the OS
+ // from interposing it (tested using an INTERPOSE_FUNCTION_BROKEN macro
+ // that just sets the second address of each tuple to NULL).
+ INTERPOSE_FUNCTION(SetCursor),
+};
+
+#endif // !__LP64__
diff --git a/dom/plugins/ipc/moz.build b/dom/plugins/ipc/moz.build
new file mode 100644
index 000000000..b569aeb4c
--- /dev/null
+++ b/dom/plugins/ipc/moz.build
@@ -0,0 +1,153 @@
+# -*- 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'] == 'cocoa':
+ DIRS += ['interpose']
+
+EXPORTS.mozilla += [
+ 'PluginLibrary.h',
+]
+
+EXPORTS.mozilla.plugins += [
+ 'AStream.h',
+ 'BrowserStreamChild.h',
+ 'BrowserStreamParent.h',
+ 'ChildAsyncCall.h',
+ 'ChildTimer.h',
+ 'NPEventAndroid.h',
+ 'NPEventOSX.h',
+ 'NPEventUnix.h',
+ 'NPEventWindows.h',
+ 'PluginAsyncSurrogate.h',
+ 'PluginBridge.h',
+ 'PluginDataResolver.h',
+ 'PluginInstanceChild.h',
+ 'PluginInstanceParent.h',
+ 'PluginMessageUtils.h',
+ 'PluginModuleChild.h',
+ 'PluginModuleParent.h',
+ 'PluginProcessChild.h',
+ 'PluginProcessParent.h',
+ 'PluginQuirks.h',
+ 'PluginScriptableObjectChild.h',
+ 'PluginScriptableObjectParent.h',
+ 'PluginScriptableObjectUtils-inl.h',
+ 'PluginScriptableObjectUtils.h',
+ 'PluginStreamChild.h',
+ 'PluginStreamParent.h',
+ 'PluginUtilsOSX.h',
+ 'PluginWidgetChild.h',
+ 'PluginWidgetParent.h',
+ 'StreamNotifyChild.h',
+ 'StreamNotifyParent.h',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ EXPORTS.mozilla.plugins += [
+ 'PluginSurfaceParent.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'PluginHangUIParent.cpp',
+ 'PluginSurfaceParent.cpp',
+ ]
+ SOURCES += [
+ 'MiniShmParent.cpp', # Issues with CreateEvent
+ ]
+ DEFINES['MOZ_HANGUI_PROCESS_NAME'] = '"plugin-hang-ui%s"' % CONFIG['BIN_SUFFIX']
+ LOCAL_INCLUDES += [
+ '/widget',
+ 'hangui',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ EXPORTS.mozilla.plugins += [
+ 'PluginInterposeOSX.h',
+ ]
+
+UNIFIED_SOURCES += [
+ 'BrowserStreamChild.cpp',
+ 'BrowserStreamParent.cpp',
+ 'ChildAsyncCall.cpp',
+ 'ChildTimer.cpp',
+ 'PluginAsyncSurrogate.cpp',
+ 'PluginBackgroundDestroyer.cpp',
+ 'PluginInstanceParent.cpp',
+ 'PluginMessageUtils.cpp',
+ 'PluginModuleParent.cpp',
+ 'PluginProcessChild.cpp',
+ 'PluginProcessParent.cpp',
+ 'PluginQuirks.cpp',
+ 'PluginScriptableObjectChild.cpp',
+ 'PluginScriptableObjectParent.cpp',
+ 'PluginStreamChild.cpp',
+ 'PluginStreamParent.cpp',
+]
+
+SOURCES += [
+ 'PluginInstanceChild.cpp', # 'PluginThreadCallback' : ambiguous symbol
+ 'PluginModuleChild.cpp', # Redefinition of mozilla::WindowsDllInterceptor sUser32Intercept
+ 'PluginWidgetChild.cpp',
+ 'PluginWidgetParent.cpp',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'PluginInterposeOSX.mm',
+ 'PluginUtilsOSX.mm',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ UNIFIED_SOURCES += [
+ 'D3D11SurfaceHolder.cpp',
+ 'PluginUtilsWin.cpp'
+ ]
+
+IPDL_SOURCES += [
+ 'PBrowserStream.ipdl',
+ 'PluginTypes.ipdlh',
+ 'PPluginBackgroundDestroyer.ipdl',
+ 'PPluginInstance.ipdl',
+ 'PPluginModule.ipdl',
+ 'PPluginScriptableObject.ipdl',
+ 'PPluginStream.ipdl',
+ 'PPluginSurface.ipdl',
+ 'PStreamNotify.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+ '../base',
+ '/xpcom/base/',
+]
+
+if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
+ LOCAL_INCLUDES += [
+ '/security/sandbox/chromium',
+ '/security/sandbox/chromium-shim',
+ ]
+
+DEFINES['FORCE_PR_LOG'] = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gtk3':
+ CXXFLAGS += CONFIG['TK_CFLAGS']
+else:
+ # Force build against gtk+2 for struct offsets and such.
+ CXXFLAGS += CONFIG['MOZ_GTK2_CFLAGS']
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+if CONFIG['_MSC_VER']:
+ # This is intended as a temporary hack to support building with VS2015.
+ # conversion from 'X' to 'Y' requires a narrowing conversion
+ CXXFLAGS += ['-wd4838']
+
+ # 'type cast': conversion from 'unsigned int' to 'HIMC' of greater size
+ CXXFLAGS += ['-wd4312']