summaryrefslogtreecommitdiffstats
path: root/dom/plugins/ipc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/plugins/ipc
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--dom/plugins/ipc/AStream.h26
-rw-r--r--dom/plugins/ipc/BrowserStreamChild.cpp217
-rw-r--r--dom/plugins/ipc/BrowserStreamChild.h150
-rw-r--r--dom/plugins/ipc/BrowserStreamParent.cpp85
-rw-r--r--dom/plugins/ipc/BrowserStreamParent.h58
-rw-r--r--dom/plugins/ipc/ChildTimer.cpp31
-rw-r--r--dom/plugins/ipc/ChildTimer.h55
-rw-r--r--dom/plugins/ipc/D3D11SurfaceHolder.cpp81
-rw-r--r--dom/plugins/ipc/D3D11SurfaceHolder.h46
-rw-r--r--dom/plugins/ipc/FunctionBroker.cpp1429
-rw-r--r--dom/plugins/ipc/FunctionBroker.h1452
-rw-r--r--dom/plugins/ipc/FunctionBrokerChild.cpp111
-rw-r--r--dom/plugins/ipc/FunctionBrokerChild.h51
-rw-r--r--dom/plugins/ipc/FunctionBrokerIPCUtils.cpp334
-rw-r--r--dom/plugins/ipc/FunctionBrokerIPCUtils.h436
-rw-r--r--dom/plugins/ipc/FunctionBrokerParent.cpp139
-rw-r--r--dom/plugins/ipc/FunctionBrokerParent.h67
-rw-r--r--dom/plugins/ipc/FunctionBrokerThread.h52
-rw-r--r--dom/plugins/ipc/FunctionHook.cpp359
-rw-r--r--dom/plugins/ipc/FunctionHook.h206
-rw-r--r--dom/plugins/ipc/IpdlTuple.h186
-rw-r--r--dom/plugins/ipc/MiniShmParent.cpp178
-rw-r--r--dom/plugins/ipc/MiniShmParent.h87
-rw-r--r--dom/plugins/ipc/NPEventAndroid.h50
-rw-r--r--dom/plugins/ipc/NPEventOSX.h198
-rw-r--r--dom/plugins/ipc/NPEventUnix.h90
-rw-r--r--dom/plugins/ipc/NPEventWindows.h158
-rw-r--r--dom/plugins/ipc/PBrowserStream.ipdl42
-rw-r--r--dom/plugins/ipc/PFunctionBroker.ipdl23
-rw-r--r--dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl34
-rw-r--r--dom/plugins/ipc/PPluginInstance.ipdl294
-rw-r--r--dom/plugins/ipc/PPluginModule.ipdl155
-rw-r--r--dom/plugins/ipc/PPluginScriptableObject.ipdl102
-rw-r--r--dom/plugins/ipc/PPluginSurface.ipdl18
-rw-r--r--dom/plugins/ipc/PStreamNotify.ipdl39
-rw-r--r--dom/plugins/ipc/PluginBackgroundDestroyer.cpp35
-rw-r--r--dom/plugins/ipc/PluginBackgroundDestroyer.h56
-rw-r--r--dom/plugins/ipc/PluginBridge.h41
-rw-r--r--dom/plugins/ipc/PluginHangUIParent.cpp400
-rw-r--r--dom/plugins/ipc/PluginHangUIParent.h142
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.cpp4045
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.h613
-rw-r--r--dom/plugins/ipc/PluginInstanceParent.cpp2326
-rw-r--r--dom/plugins/ipc/PluginInstanceParent.h403
-rw-r--r--dom/plugins/ipc/PluginInterposeOSX.h137
-rw-r--r--dom/plugins/ipc/PluginInterposeOSX.mm1040
-rw-r--r--dom/plugins/ipc/PluginLibrary.h125
-rw-r--r--dom/plugins/ipc/PluginMessageUtils.cpp141
-rw-r--r--dom/plugins/ipc/PluginMessageUtils.h662
-rw-r--r--dom/plugins/ipc/PluginModuleChild.cpp1984
-rw-r--r--dom/plugins/ipc/PluginModuleChild.h345
-rw-r--r--dom/plugins/ipc/PluginModuleParent.cpp2541
-rw-r--r--dom/plugins/ipc/PluginModuleParent.h545
-rw-r--r--dom/plugins/ipc/PluginProcessChild.cpp186
-rw-r--r--dom/plugins/ipc/PluginProcessChild.h53
-rw-r--r--dom/plugins/ipc/PluginProcessParent.cpp191
-rw-r--r--dom/plugins/ipc/PluginProcessParent.h95
-rw-r--r--dom/plugins/ipc/PluginQuirks.cpp54
-rw-r--r--dom/plugins/ipc/PluginQuirks.h64
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectChild.cpp1205
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectChild.h276
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectParent.cpp1289
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectParent.h169
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectUtils-inl.h154
-rw-r--r--dom/plugins/ipc/PluginScriptableObjectUtils.h253
-rw-r--r--dom/plugins/ipc/PluginSurfaceParent.cpp29
-rw-r--r--dom/plugins/ipc/PluginSurfaceParent.h38
-rw-r--r--dom/plugins/ipc/PluginTypes.ipdlh48
-rw-r--r--dom/plugins/ipc/PluginUtilsOSX.h94
-rw-r--r--dom/plugins/ipc/PluginUtilsOSX.mm429
-rw-r--r--dom/plugins/ipc/PluginUtilsWin.cpp272
-rw-r--r--dom/plugins/ipc/PluginUtilsWin.h27
-rw-r--r--dom/plugins/ipc/PluginWidgetChild.cpp60
-rw-r--r--dom/plugins/ipc/PluginWidgetChild.h41
-rw-r--r--dom/plugins/ipc/PluginWidgetParent.cpp168
-rw-r--r--dom/plugins/ipc/PluginWidgetParent.h64
-rw-r--r--dom/plugins/ipc/StreamNotifyChild.h60
-rw-r--r--dom/plugins/ipc/StreamNotifyParent.h41
-rw-r--r--dom/plugins/ipc/hangui/HangUIDlg.h17
-rw-r--r--dom/plugins/ipc/hangui/HangUIDlg.rc26
-rw-r--r--dom/plugins/ipc/hangui/MiniShmBase.h280
-rw-r--r--dom/plugins/ipc/hangui/MiniShmChild.cpp160
-rw-r--r--dom/plugins/ipc/hangui/MiniShmChild.h63
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUI.h39
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUIChild.cpp386
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUIChild.h100
-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.manifest37
-rw-r--r--dom/plugins/ipc/interpose/moz.build13
-rw-r--r--dom/plugins/ipc/interpose/plugin_child_interpose.mm129
-rw-r--r--dom/plugins/ipc/moz.build149
92 files changed, 29412 insertions, 0 deletions
diff --git a/dom/plugins/ipc/AStream.h b/dom/plugins/ipc/AStream.h
new file mode 100644
index 0000000000..06ac822320
--- /dev/null
+++ b/dom/plugins/ipc/AStream.h
@@ -0,0 +1,26 @@
+/* -*- 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 0000000000..21e6706ba3
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamChild.cpp
@@ -0,0 +1,217 @@
+/* -*- 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::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),
+ 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);
+
+ // NP_NORMAL is the only permissible stream type
+ if (*stype != NP_NORMAL) {
+ rv = NPERR_INVALID_PARAM;
+ // The plugin thinks the stream is alive, so we kill it explicitly
+ (void)mInstance->mPluginIface->destroystream(&mInstance->mData, &mStream,
+ NPRES_NETWORK_ERR);
+ }
+
+ 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!");
+}
+
+mozilla::ipc::IPCResult BrowserStreamChild::RecvWrite(const int32_t& offset,
+ const uint32_t& newlength,
+ const Buffer& data) {
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ AssertPluginThread();
+
+ if (ALIVE != mState)
+ MOZ_CRASH("Unexpected state: received data after NPP_DestroyStream?");
+
+ if (kStreamOpen != mStreamStatus) return IPC_OK();
+
+ 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserStreamChild::RecvNPP_DestroyStream(
+ const NPReason& reason) {
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ if (ALIVE != mState)
+ MOZ_CRASH("Unexpected state: recevied NPP_DestroyStream twice?");
+
+ mState = DYING;
+ mDestroyPending = DESTROY_PENDING;
+ if (NPRES_DONE != reason) mStreamStatus = reason;
+
+ EnsureDeliveryPending();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserStreamChild::Recv__delete__() {
+ AssertPluginThread();
+
+ if (DELETING != mState) MOZ_CRASH("Bad state, not DELETING");
+
+ return IPC_OK();
+}
+
+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();
+
+ if (DESTROY_PENDING == mDestroyPending) {
+ mDestroyPending = DESTROYED;
+ if (mState != DYING) MOZ_CRASH("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) MOZ_CRASH("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
+ mStreamStatus = NPRES_NETWORK_ERR;
+
+ // Set up stream destruction
+ EnsureDeliveryPending();
+ 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 mozilla::plugins
diff --git a/dom/plugins/ipc/BrowserStreamChild.h b/dom/plugins/ipc/BrowserStreamChild.h
new file mode 100644
index 0000000000..5df88d9aab
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamChild.h
@@ -0,0 +1,150 @@
+/* -*- 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);
+
+ mozilla::ipc::IPCResult RecvWrite(const int32_t& offset,
+ const uint32_t& newsize,
+ const Buffer& data);
+ mozilla::ipc::IPCResult RecvNPP_DestroyStream(const NPReason& reason);
+ virtual mozilla::ipc::IPCResult Recv__delete__() override;
+
+ void EnsureCorrectInstance(PluginInstanceChild* i) {
+ if (i != mInstance) MOZ_CRASH("Incorrect stream instance");
+ }
+ void EnsureCorrectStream(NPStream* s) {
+ if (s != &mStream) MOZ_CRASH("Incorrect stream data");
+ }
+
+ 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;
+
+ /**
+ * 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 stream destruction. 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;
+
+ // 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 0000000000..f1a5a0e2dc
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamParent.cpp
@@ -0,0 +1,85 @@
+/* -*- 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 "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::plugins {
+
+BrowserStreamParent::BrowserStreamParent(PluginInstanceParent* npp,
+ NPStream* stream)
+ : mNPP(npp), mStream(stream), 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
+}
+
+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;
+ } else {
+ mState = DYING;
+ Unused << SendNPP_DestroyStream(reason);
+ }
+}
+
+mozilla::ipc::IPCResult BrowserStreamParent::RecvStreamDestroyed() {
+ if (DYING != mState) {
+ NS_ERROR("Unexpected state");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mStreamPeer = nullptr;
+
+ mState = DELETING;
+ IProtocol* mgr = Manager();
+ if (!Send__delete__(this)) {
+ return IPC_FAIL_NO_REASON(mgr);
+ }
+ return IPC_OK();
+}
+
+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;
+}
+
+} // namespace mozilla::plugins
diff --git a/dom/plugins/ipc/BrowserStreamParent.h b/dom/plugins/ipc/BrowserStreamParent.h
new file mode 100644
index 0000000000..492288eb3e
--- /dev/null
+++ b/dom/plugins/ipc/BrowserStreamParent.h
@@ -0,0 +1,58 @@
+/* -*- 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 mozilla::ipc::IPCResult RecvStreamDestroyed() override;
+
+ int32_t WriteReady();
+ int32_t Write(int32_t offset, int32_t len, void* buffer);
+
+ 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;
+
+ enum { INITIALIZING, DEFERRING_DESTROY, ALIVE, DYING, DELETING } mState;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif
diff --git a/dom/plugins/ipc/ChildTimer.cpp b/dom/plugins/ipc/ChildTimer.cpp
new file mode 100644
index 0000000000..e9a096c62c
--- /dev/null
+++ b/dom/plugins/ipc/ChildTimer.cpp
@@ -0,0 +1,31 @@
+/* -*- 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::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 mozilla::plugins
diff --git a/dom/plugins/ipc/ChildTimer.h b/dom/plugins/ipc/ChildTimer.h
new file mode 100644
index 0000000000..8b9ff72ded
--- /dev/null
+++ b/dom/plugins/ipc/ChildTimer.h
@@ -0,0 +1,55 @@
+/* -*- 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"
+#include "mozilla/UniquePtr.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() = default;
+
+ uint32_t ID() const { return mID; }
+
+ class IDComparator {
+ public:
+ bool Equals(const UniquePtr<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 0000000000..429f3be673
--- /dev/null
+++ b/dom/plugins/ipc/D3D11SurfaceHolder.cpp
@@ -0,0 +1,81 @@
+/* -*- 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 0000000000..4031091f7c
--- /dev/null
+++ b/dom/plugins/ipc/D3D11SurfaceHolder.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 _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/FunctionBroker.cpp b/dom/plugins/ipc/FunctionBroker.cpp
new file mode 100644
index 0000000000..9068cf322d
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBroker.cpp
@@ -0,0 +1,1429 @@
+/* -*- 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 "FunctionBroker.h"
+#include "FunctionBrokerParent.h"
+#include "PluginQuirks.h"
+
+#if defined(XP_WIN)
+# include <commdlg.h>
+# include <schannel.h>
+# include <sddl.h>
+#endif // defined(XP_WIN)
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+using namespace mozilla::plugins;
+
+namespace mozilla::plugins {
+
+template <int QuirkFlag>
+static bool CheckQuirks(int aQuirks) {
+ return static_cast<bool>(aQuirks & QuirkFlag);
+}
+
+void FreeDestructor(void* aObj) { free(aObj); }
+
+#if defined(XP_WIN)
+
+// Specialization of EndpointHandlers for Flash file dialog brokering.
+struct FileDlgEHContainer {
+ template <Endpoint e>
+ struct EndpointHandler;
+};
+
+template <>
+struct FileDlgEHContainer::EndpointHandler<CLIENT>
+ : public BaseEndpointHandler<CLIENT,
+ FileDlgEHContainer::EndpointHandler<CLIENT>> {
+ using BaseEndpointHandler<CLIENT, EndpointHandler<CLIENT>>::Copy;
+
+ inline static void Copy(OpenFileNameIPC& aDest, const LPOPENFILENAMEW& aSrc) {
+ aDest.CopyFromOfn(aSrc);
+ }
+ inline static void Copy(LPOPENFILENAMEW& aDest,
+ const OpenFileNameRetIPC& aSrc) {
+ aSrc.AddToOfn(aDest);
+ }
+};
+
+template <>
+struct FileDlgEHContainer::EndpointHandler<SERVER>
+ : public BaseEndpointHandler<SERVER,
+ FileDlgEHContainer::EndpointHandler<SERVER>> {
+ using BaseEndpointHandler<SERVER, EndpointHandler<SERVER>>::Copy;
+
+ inline static void Copy(OpenFileNameRetIPC& aDest,
+ const LPOPENFILENAMEW& aSrc) {
+ aDest.CopyFromOfn(aSrc);
+ }
+ inline static void Copy(ServerCallData* aScd, LPOPENFILENAMEW& aDest,
+ const OpenFileNameIPC& aSrc) {
+ MOZ_ASSERT(!aDest);
+ ServerCallData::DestructorType* destructor = [](void* aObj) {
+ OpenFileNameIPC::FreeOfnStrings(static_cast<LPOPENFILENAMEW>(aObj));
+ DeleteDestructor<OPENFILENAMEW>(aObj);
+ };
+ aDest = aScd->Allocate<OPENFILENAMEW>(destructor);
+ aSrc.AllocateOfnStrings(aDest);
+ aSrc.AddToOfn(aDest);
+ }
+};
+
+// FunctionBroker type that uses FileDlgEHContainer
+template <FunctionHookId functionId, typename FunctionType>
+using FileDlgFunctionBroker =
+ FunctionBroker<functionId, FunctionType, FileDlgEHContainer>;
+
+// Specialization of EndpointHandlers for Flash SSL brokering.
+struct SslEHContainer {
+ template <Endpoint e>
+ struct EndpointHandler;
+};
+
+template <>
+struct SslEHContainer::EndpointHandler<CLIENT>
+ : public BaseEndpointHandler<CLIENT,
+ SslEHContainer::EndpointHandler<CLIENT>> {
+ using BaseEndpointHandler<CLIENT, EndpointHandler<CLIENT>>::Copy;
+
+ inline static void Copy(uint64_t& aDest, const PSecHandle& aSrc) {
+ MOZ_ASSERT((aSrc->dwLower == aSrc->dwUpper) && IsOdd(aSrc->dwLower));
+ aDest = static_cast<uint64_t>(aSrc->dwLower);
+ }
+ inline static void Copy(PSecHandle& aDest, const uint64_t& aSrc) {
+ MOZ_ASSERT(IsOdd(aSrc));
+ aDest->dwLower = static_cast<ULONG_PTR>(aSrc);
+ aDest->dwUpper = static_cast<ULONG_PTR>(aSrc);
+ }
+ inline static void Copy(IPCSchannelCred& aDest, const PSCHANNEL_CRED& aSrc) {
+ if (aSrc) {
+ aDest.CopyFrom(aSrc);
+ }
+ }
+ inline static void Copy(IPCInternetBuffers& aDest,
+ const LPINTERNET_BUFFERSA& aSrc) {
+ aDest.CopyFrom(aSrc);
+ }
+};
+
+template <>
+struct SslEHContainer::EndpointHandler<SERVER>
+ : public BaseEndpointHandler<SERVER,
+ SslEHContainer::EndpointHandler<SERVER>> {
+ using BaseEndpointHandler<SERVER, EndpointHandler<SERVER>>::Copy;
+
+ // PSecHandle is the same thing as PCtxtHandle and PCredHandle.
+ inline static void Copy(uint64_t& aDest, const PSecHandle& aSrc) {
+ // If the SecHandle was an error then don't store it.
+ if (!aSrc) {
+ aDest = 0;
+ return;
+ }
+
+ static uint64_t sNextVal = 1;
+ UlongPair key(aSrc->dwLower, aSrc->dwUpper);
+ // Fetch val by reference to update the value in the map
+ uint64_t& val = sPairToIdMap[key];
+ if (val == 0) {
+ MOZ_ASSERT(IsOdd(sNextVal));
+ val = sNextVal;
+ sIdToPairMap[val] = key;
+ sNextVal += 2;
+ }
+ aDest = val;
+ }
+
+ // HANDLEs and HINTERNETs marshal with obfuscation (for return values)
+ inline static void Copy(uint64_t& aDest, void* const& aSrc) {
+ // If the HANDLE/HINTERNET was an error then don't store it.
+ if (!aSrc) {
+ aDest = 0;
+ return;
+ }
+
+ static uint64_t sNextVal = 1;
+ // Fetch val by reference to update the value in the map
+ uint64_t& val = sPtrToIdMap[aSrc];
+ if (val == 0) {
+ MOZ_ASSERT(IsOdd(sNextVal));
+ val = sNextVal;
+ sIdToPtrMap[val] = aSrc;
+ sNextVal += 2;
+ }
+ aDest = val;
+ }
+
+ // HANDLEs and HINTERNETs unmarshal with obfuscation
+ inline static void Copy(void*& aDest, const uint64_t& aSrc) {
+ aDest = nullptr;
+ MOZ_RELEASE_ASSERT(IsOdd(aSrc));
+
+ // If the src is not found in the map then we get aDest == 0
+ void* ptr = sIdToPtrMap[aSrc];
+ aDest = reinterpret_cast<void*>(ptr);
+ MOZ_RELEASE_ASSERT(aDest);
+ }
+
+ inline static void Copy(PSCHANNEL_CRED& aDest, const IPCSchannelCred& aSrc) {
+ if (aDest) {
+ aSrc.CopyTo(aDest);
+ }
+ }
+
+ inline static void Copy(ServerCallData* aScd, PSecHandle& aDest,
+ const uint64_t& aSrc) {
+ MOZ_ASSERT(!aDest);
+ MOZ_RELEASE_ASSERT(IsOdd(aSrc));
+
+ // If the src is not found in the map then we get the pair { 0, 0 }
+ aDest = aScd->Allocate<SecHandle>();
+ const UlongPair& pair = sIdToPairMap[aSrc];
+ MOZ_RELEASE_ASSERT(pair.first || pair.second);
+ aDest->dwLower = pair.first;
+ aDest->dwUpper = pair.second;
+ }
+
+ inline static void Copy(ServerCallData* aScd, PSCHANNEL_CRED& aDest,
+ const IPCSchannelCred& aSrc) {
+ MOZ_ASSERT(!aDest);
+ aDest = aScd->Allocate<SCHANNEL_CRED>();
+ Copy(aDest, aSrc);
+ }
+
+ inline static void Copy(ServerCallData* aScd, LPINTERNET_BUFFERSA& aDest,
+ const IPCInternetBuffers& aSrc) {
+ MOZ_ASSERT(!aDest);
+ aSrc.CopyTo(aDest);
+ ServerCallData::DestructorType* destructor = [](void* aObj) {
+ LPINTERNET_BUFFERSA inetBuf = static_cast<LPINTERNET_BUFFERSA>(aObj);
+ IPCInternetBuffers::FreeBuffers(inetBuf);
+ FreeDestructor(inetBuf);
+ };
+ aScd->PostDestructor(aDest, destructor);
+ }
+};
+
+// FunctionBroker type that uses SslEHContainer
+template <FunctionHookId functionId, typename FunctionType>
+using SslFunctionBroker =
+ FunctionBroker<functionId, FunctionType, SslEHContainer>;
+
+/* GetKeyState */
+
+typedef FunctionBroker<ID_GetKeyState, decltype(GetKeyState)> GetKeyStateFB;
+
+template <>
+ShouldHookFunc* const GetKeyStateFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_GETKEYSTATE>;
+
+/* SetCursorPos */
+
+typedef FunctionBroker<ID_SetCursorPos, decltype(SetCursorPos)> SetCursorPosFB;
+
+/* GetSaveFileNameW */
+
+typedef FileDlgFunctionBroker<ID_GetSaveFileNameW, decltype(GetSaveFileNameW)>
+ GetSaveFileNameWFB;
+
+// Remember files granted access in the chrome process
+static void GrantFileAccess(base::ProcessId aClientId, LPOPENFILENAME& aLpofn,
+ bool isSave) {
+# if defined(MOZ_SANDBOX)
+ if (aLpofn->Flags & OFN_ALLOWMULTISELECT) {
+ // We only support multiselect with the OFN_EXPLORER flag.
+ // This guarantees that ofn.lpstrFile follows the pattern below.
+ MOZ_ASSERT(aLpofn->Flags & OFN_EXPLORER);
+
+ // lpstrFile is one of two things:
+ // 1. A null terminated full path to a file, or
+ // 2. A path to a folder, followed by a NULL, followed by a
+ // list of file names, each NULL terminated, followed by an
+ // additional NULL (so it is also double-NULL terminated).
+ std::wstring path = std::wstring(aLpofn->lpstrFile);
+ MOZ_ASSERT(aLpofn->nFileOffset > 0);
+ // For condition #1, nFileOffset points to the file name in the path.
+ // It will be preceeded by a non-NULL character from the path.
+ if (aLpofn->lpstrFile[aLpofn->nFileOffset - 1] != L'\0') {
+ FunctionBrokerParent::GetSandboxPermissions()->GrantFileAccess(
+ aClientId, path.c_str(), isSave);
+ } else {
+ // This is condition #2
+ wchar_t* nextFile = aLpofn->lpstrFile + path.size() + 1;
+ while (*nextFile != L'\0') {
+ std::wstring nextFileStr(nextFile);
+ std::wstring fullPath = path + std::wstring(L"\\") + nextFileStr;
+ FunctionBrokerParent::GetSandboxPermissions()->GrantFileAccess(
+ aClientId, fullPath.c_str(), isSave);
+ nextFile += nextFileStr.size() + 1;
+ }
+ }
+ } else {
+ FunctionBrokerParent::GetSandboxPermissions()->GrantFileAccess(
+ aClientId, aLpofn->lpstrFile, isSave);
+ }
+# else
+ MOZ_ASSERT_UNREACHABLE(
+ "GetFileName IPC message is only available on "
+ "Windows builds with sandbox.");
+# endif
+}
+
+template <>
+template <>
+BROKER_DISABLE_CFGUARD BOOL GetSaveFileNameWFB::RunFunction(
+ GetSaveFileNameWFB::FunctionType* aOrigFunction, base::ProcessId aClientId,
+ LPOPENFILENAMEW& aLpofn) const {
+ BOOL result = aOrigFunction(aLpofn);
+ if (result) {
+ // Record any file access permission that was just granted.
+ GrantFileAccess(aClientId, aLpofn, true);
+ }
+ return result;
+}
+
+template <>
+template <>
+struct GetSaveFileNameWFB::Response::Info::ShouldMarshal<0> {
+ static const bool value = true;
+};
+
+/* GetOpenFileNameW */
+
+typedef FileDlgFunctionBroker<ID_GetOpenFileNameW, decltype(GetOpenFileNameW)>
+ GetOpenFileNameWFB;
+
+template <>
+template <>
+BROKER_DISABLE_CFGUARD BOOL GetOpenFileNameWFB::RunFunction(
+ GetOpenFileNameWFB::FunctionType* aOrigFunction, base::ProcessId aClientId,
+ LPOPENFILENAMEW& aLpofn) const {
+ BOOL result = aOrigFunction(aLpofn);
+ if (result) {
+ // Record any file access permission that was just granted.
+ GrantFileAccess(aClientId, aLpofn, false);
+ }
+ return result;
+}
+
+template <>
+template <>
+struct GetOpenFileNameWFB::Response::Info::ShouldMarshal<0> {
+ static const bool value = true;
+};
+
+/* InternetOpenA */
+
+typedef SslFunctionBroker<ID_InternetOpenA, decltype(InternetOpenA)>
+ InternetOpenAFB;
+
+template <>
+ShouldHookFunc* const InternetOpenAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+/* InternetConnectA */
+
+typedef SslFunctionBroker<ID_InternetConnectA, decltype(InternetConnectA)>
+ InternetConnectAFB;
+
+template <>
+ShouldHookFunc* const InternetConnectAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef InternetConnectAFB::Request ICAReqHandler;
+
+template <>
+bool ICAReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h,
+ const LPCSTR& srv, const INTERNET_PORT& port,
+ const LPCSTR& user, const LPCSTR& pass,
+ const DWORD& svc, const DWORD& flags,
+ const DWORD_PTR& cxt) {
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+/* InternetCloseHandle */
+
+typedef SslFunctionBroker<ID_InternetCloseHandle, decltype(InternetCloseHandle)>
+ InternetCloseHandleFB;
+
+template <>
+ShouldHookFunc* const InternetCloseHandleFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef InternetCloseHandleFB::Request ICHReqHandler;
+
+template <>
+bool ICHReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h) {
+ // If we are server side then we were already validated since we had to be
+ // looked up in the "uint64_t <-> HINTERNET" hashtable.
+ // In the client, we check that this is a dummy handle.
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+/* InternetQueryDataAvailable */
+
+typedef SslFunctionBroker<ID_InternetQueryDataAvailable,
+ decltype(InternetQueryDataAvailable)>
+ InternetQueryDataAvailableFB;
+
+template <>
+ShouldHookFunc* const InternetQueryDataAvailableFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef InternetQueryDataAvailableFB::Request IQDAReq;
+typedef InternetQueryDataAvailableFB::RequestDelegate<BOOL HOOK_CALL(HINTERNET)>
+ IQDADelegateReq;
+
+template <>
+void IQDAReq::Marshal(IpdlTuple& aTuple, const HINTERNET& file,
+ const LPDWORD& nBytes, const DWORD& flags,
+ const DWORD_PTR& cxt) {
+ IQDADelegateReq::Marshal(aTuple, file);
+}
+
+template <>
+bool IQDAReq::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple,
+ HINTERNET& file, LPDWORD& nBytes, DWORD& flags,
+ DWORD_PTR& cxt) {
+ bool success = IQDADelegateReq::Unmarshal(aScd, aTuple, file);
+ if (!success) {
+ return false;
+ }
+ flags = 0;
+ cxt = 0;
+ nBytes = aScd.Allocate<DWORD>();
+ return true;
+}
+
+template <>
+bool IQDAReq::ShouldBroker(Endpoint endpoint, const HINTERNET& file,
+ const LPDWORD& nBytes, const DWORD& flags,
+ const DWORD_PTR& cxt) {
+ // If we are server side then we were already validated since we had to be
+ // looked up in the "uint64_t <-> HINTERNET" hashtable.
+ // In the client, we check that this is a dummy handle.
+ return (endpoint == SERVER) || ((flags == 0) && (cxt == 0) &&
+ IsOdd(reinterpret_cast<uint64_t>(file)));
+}
+
+template <>
+template <>
+struct InternetQueryDataAvailableFB::Response::Info::ShouldMarshal<1> {
+ static const bool value = true;
+};
+
+/* InternetReadFile */
+
+typedef SslFunctionBroker<ID_InternetReadFile, decltype(InternetReadFile)>
+ InternetReadFileFB;
+
+template <>
+ShouldHookFunc* const InternetReadFileFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef InternetReadFileFB::Request IRFRequestHandler;
+typedef InternetReadFileFB::RequestDelegate<BOOL HOOK_CALL(HINTERNET, DWORD)>
+ IRFDelegateReq;
+
+template <>
+void IRFRequestHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h,
+ const LPVOID& buf, const DWORD& nBytesToRead,
+ const LPDWORD& nBytesRead) {
+ IRFDelegateReq::Marshal(aTuple, h, nBytesToRead);
+}
+
+template <>
+bool IRFRequestHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple,
+ HINTERNET& h, LPVOID& buf,
+ DWORD& nBytesToRead, LPDWORD& nBytesRead) {
+ bool ret = IRFDelegateReq::Unmarshal(aScd, aTuple, h, nBytesToRead);
+ if (!ret) {
+ return false;
+ }
+
+ nBytesRead = aScd.Allocate<DWORD>();
+ MOZ_ASSERT(nBytesToRead > 0);
+ aScd.AllocateMemory(nBytesToRead, buf);
+ return true;
+}
+
+template <>
+bool IRFRequestHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h,
+ const LPVOID& buf,
+ const DWORD& nBytesToRead,
+ const LPDWORD& nBytesRead) {
+ // For server-side validation, the HINTERNET deserialization will have
+ // required it to already be looked up in the IdToPtrMap. At that point,
+ // any call is valid.
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+typedef InternetReadFileFB::Response IRFResponseHandler;
+typedef InternetReadFileFB::ResponseDelegate<BOOL HOOK_CALL(
+ nsDependentCSubstring)>
+ IRFDelegateResponseHandler;
+
+// Marshal the output parameter that we sent to the response delegate.
+template <>
+template <>
+struct IRFResponseHandler::Info::ShouldMarshal<0> {
+ static const bool value = true;
+};
+
+template <>
+void IRFResponseHandler::Marshal(IpdlTuple& aTuple, const BOOL& ret,
+ const HINTERNET& h, const LPVOID& buf,
+ const DWORD& nBytesToRead,
+ const LPDWORD& nBytesRead) {
+ nsDependentCSubstring str;
+ if (*nBytesRead) {
+ str.Assign(static_cast<const char*>(buf), *nBytesRead);
+ }
+ IRFDelegateResponseHandler::Marshal(aTuple, ret, str);
+}
+
+template <>
+bool IRFResponseHandler::Unmarshal(const IpdlTuple& aTuple, BOOL& ret,
+ HINTERNET& h, LPVOID& buf,
+ DWORD& nBytesToRead, LPDWORD& nBytesRead) {
+ nsDependentCSubstring str;
+ bool success = IRFDelegateResponseHandler::Unmarshal(aTuple, ret, str);
+ if (!success) {
+ return false;
+ }
+
+ if (str.Length()) {
+ memcpy(buf, str.Data(), str.Length());
+ *nBytesRead = str.Length();
+ }
+ return true;
+}
+
+/* InternetWriteFile */
+
+typedef SslFunctionBroker<ID_InternetWriteFile, decltype(InternetWriteFile)>
+ InternetWriteFileFB;
+
+template <>
+ShouldHookFunc* const InternetWriteFileFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef InternetWriteFileFB::Request IWFReqHandler;
+typedef InternetWriteFileFB::RequestDelegate<int HOOK_CALL(
+ HINTERNET, nsDependentCSubstring)>
+ IWFDelegateReqHandler;
+
+template <>
+void IWFReqHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& file,
+ const LPCVOID& buf, const DWORD& nToWrite,
+ const LPDWORD& nWritten) {
+ MOZ_ASSERT(nWritten);
+ IWFDelegateReqHandler::Marshal(
+ aTuple, file,
+ nsDependentCSubstring(static_cast<const char*>(buf), nToWrite));
+}
+
+template <>
+bool IWFReqHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple,
+ HINTERNET& file, LPCVOID& buf, DWORD& nToWrite,
+ LPDWORD& nWritten) {
+ nsDependentCSubstring str;
+ if (!IWFDelegateReqHandler::Unmarshal(aScd, aTuple, file, str)) {
+ return false;
+ }
+
+ aScd.AllocateString(str, buf, false);
+ nToWrite = str.Length();
+ nWritten = aScd.Allocate<DWORD>();
+ return true;
+}
+
+template <>
+bool IWFReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& file,
+ const LPCVOID& buf, const DWORD& nToWrite,
+ const LPDWORD& nWritten) {
+ // For server-side validation, the HINTERNET deserialization will have
+ // required it to already be looked up in the IdToPtrMap. At that point,
+ // any call is valid.
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(file));
+}
+
+template <>
+template <>
+struct InternetWriteFileFB::Response::Info::ShouldMarshal<3> {
+ static const bool value = true;
+};
+
+/* InternetSetOptionA */
+
+typedef SslFunctionBroker<ID_InternetSetOptionA, decltype(InternetSetOptionA)>
+ InternetSetOptionAFB;
+
+template <>
+ShouldHookFunc* const InternetSetOptionAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef InternetSetOptionAFB::Request ISOAReqHandler;
+typedef InternetSetOptionAFB::RequestDelegate<BOOL HOOK_CALL(
+ HINTERNET, DWORD, nsDependentCSubstring)>
+ ISOADelegateReqHandler;
+
+template <>
+void ISOAReqHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h,
+ const DWORD& opt, const LPVOID& buf,
+ const DWORD& bufLen) {
+ ISOADelegateReqHandler::Marshal(
+ aTuple, h, opt,
+ nsDependentCSubstring(static_cast<const char*>(buf), bufLen));
+}
+
+template <>
+bool ISOAReqHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple,
+ HINTERNET& h, DWORD& opt, LPVOID& buf,
+ DWORD& bufLen) {
+ nsDependentCSubstring str;
+ if (!ISOADelegateReqHandler::Unmarshal(aScd, aTuple, h, opt, str)) {
+ return false;
+ }
+
+ aScd.AllocateString(str, buf, false);
+ bufLen = str.Length();
+ return true;
+}
+
+template <>
+bool ISOAReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h,
+ const DWORD& opt, const LPVOID& buf,
+ const DWORD& bufLen) {
+ // For server-side validation, the HINTERNET deserialization will have
+ // required it to already be looked up in the IdToPtrMap. At that point,
+ // any call is valid.
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+/* HttpAddRequestHeadersA */
+
+typedef SslFunctionBroker<ID_HttpAddRequestHeadersA,
+ decltype(HttpAddRequestHeadersA)>
+ HttpAddRequestHeadersAFB;
+
+template <>
+ShouldHookFunc* const HttpAddRequestHeadersAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef HttpAddRequestHeadersAFB::Request HARHAReqHandler;
+
+template <>
+bool HARHAReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h,
+ const LPCSTR& head, const DWORD& headLen,
+ const DWORD& mods) {
+ // For server-side validation, the HINTERNET deserialization will have
+ // required it to already be looked up in the IdToPtrMap. At that point,
+ // any call is valid.
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+/* HttpOpenRequestA */
+
+typedef SslFunctionBroker<ID_HttpOpenRequestA, decltype(HttpOpenRequestA)>
+ HttpOpenRequestAFB;
+
+template <>
+ShouldHookFunc* const HttpOpenRequestAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef HttpOpenRequestAFB::Request HORAReqHandler;
+typedef HttpOpenRequestAFB::RequestDelegate<HINTERNET HOOK_CALL(
+ HINTERNET, LPCSTR, LPCSTR, LPCSTR, LPCSTR, CopyableTArray<nsCString>, DWORD,
+ DWORD_PTR)>
+ HORADelegateReqHandler;
+
+template <>
+void HORAReqHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h,
+ const LPCSTR& verb, const LPCSTR& obj,
+ const LPCSTR& ver, const LPCSTR& ref,
+ LPCSTR* const& acceptTypes, const DWORD& flags,
+ const DWORD_PTR& cxt) {
+ CopyableTArray<nsCString> arrayAcceptTypes;
+ LPCSTR* curAcceptType = acceptTypes;
+ if (curAcceptType) {
+ while (*curAcceptType) {
+ arrayAcceptTypes.AppendElement(nsCString(*curAcceptType));
+ ++curAcceptType;
+ }
+ }
+ // XXX Could we move arrayAcceptTypes here?
+ HORADelegateReqHandler::Marshal(aTuple, h, verb, obj, ver, ref,
+ arrayAcceptTypes, flags, cxt);
+}
+
+template <>
+bool HORAReqHandler::Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple,
+ HINTERNET& h, LPCSTR& verb, LPCSTR& obj,
+ LPCSTR& ver, LPCSTR& ref, LPCSTR*& acceptTypes,
+ DWORD& flags, DWORD_PTR& cxt) {
+ CopyableTArray<nsCString> arrayAcceptTypes;
+ if (!HORADelegateReqHandler::Unmarshal(aScd, aTuple, h, verb, obj, ver, ref,
+ arrayAcceptTypes, flags, cxt)) {
+ return false;
+ }
+ if (arrayAcceptTypes.Length() == 0) {
+ acceptTypes = nullptr;
+ } else {
+ aScd.AllocateMemory((arrayAcceptTypes.Length() + 1) * sizeof(LPCSTR),
+ acceptTypes);
+ for (size_t i = 0; i < arrayAcceptTypes.Length(); ++i) {
+ aScd.AllocateString(arrayAcceptTypes[i], acceptTypes[i]);
+ }
+ acceptTypes[arrayAcceptTypes.Length()] = nullptr;
+ }
+ return true;
+}
+
+template <>
+bool HORAReqHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h,
+ const LPCSTR& verb, const LPCSTR& obj,
+ const LPCSTR& ver, const LPCSTR& ref,
+ LPCSTR* const& acceptTypes,
+ const DWORD& flags, const DWORD_PTR& cxt) {
+ // For the server-side test, the HINTERNET deserialization will have
+ // required it to already be looked up in the IdToPtrMap. At that point,
+ // any call is valid.
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+/* HttpQueryInfoA */
+
+typedef SslFunctionBroker<ID_HttpQueryInfoA, decltype(HttpQueryInfoA)>
+ HttpQueryInfoAFB;
+
+template <>
+ShouldHookFunc* const HttpQueryInfoAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef HttpQueryInfoAFB::Request HQIARequestHandler;
+typedef HttpQueryInfoAFB::RequestDelegate<BOOL HOOK_CALL(HINTERNET, DWORD, BOOL,
+ DWORD, BOOL, DWORD)>
+ HQIADelegateRequestHandler;
+
+template <>
+void HQIARequestHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h,
+ const DWORD& lvl, const LPVOID& buf,
+ const LPDWORD& bufLen, const LPDWORD& idx) {
+ HQIADelegateRequestHandler::Marshal(aTuple, h, lvl, bufLen != nullptr,
+ bufLen ? *bufLen : 0, idx != nullptr,
+ idx ? *idx : 0);
+}
+
+template <>
+bool HQIARequestHandler::Unmarshal(ServerCallData& aScd,
+ const IpdlTuple& aTuple, HINTERNET& h,
+ DWORD& lvl, LPVOID& buf, LPDWORD& bufLen,
+ LPDWORD& idx) {
+ BOOL hasBufLen, hasIdx;
+ DWORD tempBufLen, tempIdx;
+ bool success = HQIADelegateRequestHandler::Unmarshal(
+ aScd, aTuple, h, lvl, hasBufLen, tempBufLen, hasIdx, tempIdx);
+ if (!success) {
+ return false;
+ }
+
+ bufLen = nullptr;
+ if (hasBufLen) {
+ aScd.AllocateMemory(tempBufLen, buf, bufLen);
+ }
+
+ idx = nullptr;
+ if (hasIdx) {
+ idx = aScd.Allocate<DWORD>(tempIdx);
+ }
+
+ return true;
+}
+
+template <>
+bool HQIARequestHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h,
+ const DWORD& lvl, const LPVOID& buf,
+ const LPDWORD& bufLen,
+ const LPDWORD& idx) {
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+// Marshal all of the output parameters that we sent to the response delegate.
+template <>
+template <>
+struct HttpQueryInfoAFB::Response::Info::ShouldMarshal<0> {
+ static const bool value = true;
+};
+template <>
+template <>
+struct HttpQueryInfoAFB::Response::Info::ShouldMarshal<1> {
+ static const bool value = true;
+};
+template <>
+template <>
+struct HttpQueryInfoAFB::Response::Info::ShouldMarshal<2> {
+ static const bool value = true;
+};
+
+typedef HttpQueryInfoAFB::Response HQIAResponseHandler;
+typedef HttpQueryInfoAFB::ResponseDelegate<BOOL HOOK_CALL(nsDependentCSubstring,
+ DWORD, DWORD)>
+ HQIADelegateResponseHandler;
+
+template <>
+void HQIAResponseHandler::Marshal(IpdlTuple& aTuple, const BOOL& ret,
+ const HINTERNET& h, const DWORD& lvl,
+ const LPVOID& buf, const LPDWORD& bufLen,
+ const LPDWORD& idx) {
+ nsDependentCSubstring str;
+ if (buf && ret) {
+ MOZ_ASSERT(bufLen);
+ str.Assign(static_cast<const char*>(buf), *bufLen);
+ }
+ // Note that we send the bufLen separately to handle the case where buf wasn't
+ // allocated or large enough to hold the entire return value. bufLen is then
+ // the required buffer size.
+ HQIADelegateResponseHandler::Marshal(aTuple, ret, str, bufLen ? *bufLen : 0,
+ idx ? *idx : 0);
+}
+
+template <>
+bool HQIAResponseHandler::Unmarshal(const IpdlTuple& aTuple, BOOL& ret,
+ HINTERNET& h, DWORD& lvl, LPVOID& buf,
+ LPDWORD& bufLen, LPDWORD& idx) {
+ DWORD totalBufLen = *bufLen;
+ nsDependentCSubstring str;
+ DWORD tempBufLen, tempIdx;
+ bool success = HQIADelegateResponseHandler::Unmarshal(aTuple, ret, str,
+ tempBufLen, tempIdx);
+ if (!success) {
+ return false;
+ }
+
+ if (bufLen) {
+ *bufLen = tempBufLen;
+ }
+ if (idx) {
+ *idx = tempIdx;
+ }
+
+ if (buf && ret) {
+ // When HttpQueryInfo returns strings, the buffer length will not include
+ // the null terminator. Rather than (brittle-y) trying to determine if the
+ // return buffer is a string, we always tack on a null terminator if the
+ // buffer has room for it.
+ MOZ_ASSERT(str.Length() == *bufLen);
+ memcpy(buf, str.Data(), str.Length());
+ if (str.Length() < totalBufLen) {
+ char* cbuf = static_cast<char*>(buf);
+ cbuf[str.Length()] = '\0';
+ }
+ }
+ return true;
+}
+
+/* HttpSendRequestA */
+
+typedef SslFunctionBroker<ID_HttpSendRequestA, decltype(HttpSendRequestA)>
+ HttpSendRequestAFB;
+
+template <>
+ShouldHookFunc* const HttpSendRequestAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef HttpSendRequestAFB::Request HSRARequestHandler;
+typedef HttpSendRequestAFB::RequestDelegate<BOOL HOOK_CALL(
+ HINTERNET, nsDependentCSubstring, nsDependentCSubstring)>
+ HSRADelegateRequestHandler;
+
+template <>
+void HSRARequestHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h,
+ const LPCSTR& head, const DWORD& headLen,
+ const LPVOID& opt, const DWORD& optLen) {
+ nsDependentCSubstring headStr;
+ headStr.SetIsVoid(head == nullptr);
+ if (head) {
+ // HttpSendRequest allows headLen == -1L for length of a null terminated
+ // string.
+ DWORD ncHeadLen = headLen;
+ if (ncHeadLen == -1L) {
+ ncHeadLen = strlen(head);
+ }
+ headStr.Rebind(head, ncHeadLen);
+ }
+ nsDependentCSubstring optStr;
+ optStr.SetIsVoid(opt == nullptr);
+ if (opt) {
+ optStr.Rebind(static_cast<const char*>(opt), optLen);
+ }
+ HSRADelegateRequestHandler::Marshal(aTuple, h, headStr, optStr);
+}
+
+template <>
+bool HSRARequestHandler::Unmarshal(ServerCallData& aScd,
+ const IpdlTuple& aTuple, HINTERNET& h,
+ LPCSTR& head, DWORD& headLen, LPVOID& opt,
+ DWORD& optLen) {
+ nsDependentCSubstring headStr;
+ nsDependentCSubstring optStr;
+ bool success =
+ HSRADelegateRequestHandler::Unmarshal(aScd, aTuple, h, headStr, optStr);
+ if (!success) {
+ return false;
+ }
+
+ if (headStr.IsVoid()) {
+ head = nullptr;
+ MOZ_ASSERT(headLen == 0);
+ } else {
+ aScd.AllocateString(headStr, head, false);
+ headLen = headStr.Length();
+ }
+
+ if (optStr.IsVoid()) {
+ opt = nullptr;
+ MOZ_ASSERT(optLen == 0);
+ } else {
+ aScd.AllocateString(optStr, opt, false);
+ optLen = optStr.Length();
+ }
+ return true;
+}
+
+template <>
+bool HSRARequestHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h,
+ const LPCSTR& head, const DWORD& headLen,
+ const LPVOID& opt, const DWORD& optLen) {
+ // If we are server side then we were already validated since we had to be
+ // looked up in the "uint64_t <-> HINTERNET" hashtable.
+ // In the client, we check that this is a dummy handle.
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+/* HttpSendRequestExA */
+
+typedef SslFunctionBroker<ID_HttpSendRequestExA, decltype(HttpSendRequestExA)>
+ HttpSendRequestExAFB;
+
+template <>
+ShouldHookFunc* const HttpSendRequestExAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef RequestInfo<ID_HttpSendRequestExA> HSRExAReqInfo;
+
+template <>
+template <>
+struct HSRExAReqInfo::FixedValue<2> {
+ static const LPINTERNET_BUFFERSA value;
+};
+const LPINTERNET_BUFFERSA HSRExAReqInfo::FixedValue<2>::value = nullptr;
+
+// Docs for HttpSendRequestExA say this parameter 'must' be zero but Flash
+// passes other values.
+// template<> template<>
+// struct HSRExAReqInfo::FixedValue<3> { static const DWORD value = 0; };
+
+template <>
+template <>
+struct HSRExAReqInfo::FixedValue<4> {
+ static const DWORD_PTR value;
+};
+const DWORD_PTR HSRExAReqInfo::FixedValue<4>::value = 0;
+
+/* HttpEndRequestA */
+
+typedef SslFunctionBroker<ID_HttpEndRequestA, decltype(HttpEndRequestA)>
+ HttpEndRequestAFB;
+
+template <>
+ShouldHookFunc* const HttpEndRequestAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef RequestInfo<ID_HttpEndRequestA> HERAReqInfo;
+
+template <>
+template <>
+struct HERAReqInfo::FixedValue<1> {
+ static const LPINTERNET_BUFFERSA value;
+};
+const LPINTERNET_BUFFERSA HERAReqInfo::FixedValue<1>::value = nullptr;
+
+template <>
+template <>
+struct HERAReqInfo::FixedValue<2> {
+ static const DWORD value;
+};
+const DWORD HERAReqInfo::FixedValue<2>::value = 0;
+
+template <>
+template <>
+struct HERAReqInfo::FixedValue<3> {
+ static const DWORD_PTR value;
+};
+const DWORD_PTR HERAReqInfo::FixedValue<3>::value = 0;
+
+/* InternetQueryOptionA */
+
+typedef SslFunctionBroker<ID_InternetQueryOptionA,
+ decltype(InternetQueryOptionA)>
+ InternetQueryOptionAFB;
+
+template <>
+ShouldHookFunc* const InternetQueryOptionAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef InternetQueryOptionAFB::Request IQOARequestHandler;
+typedef InternetQueryOptionAFB::RequestDelegate<BOOL HOOK_CALL(HINTERNET, DWORD,
+ DWORD)>
+ IQOADelegateRequestHandler;
+
+template <>
+void IQOARequestHandler::Marshal(IpdlTuple& aTuple, const HINTERNET& h,
+ const DWORD& opt, const LPVOID& buf,
+ const LPDWORD& bufLen) {
+ MOZ_ASSERT(bufLen);
+ IQOADelegateRequestHandler::Marshal(aTuple, h, opt, buf ? *bufLen : 0);
+}
+
+template <>
+bool IQOARequestHandler::Unmarshal(ServerCallData& aScd,
+ const IpdlTuple& aTuple, HINTERNET& h,
+ DWORD& opt, LPVOID& buf, LPDWORD& bufLen) {
+ DWORD tempBufLen;
+ bool success =
+ IQOADelegateRequestHandler::Unmarshal(aScd, aTuple, h, opt, tempBufLen);
+ if (!success) {
+ return false;
+ }
+
+ aScd.AllocateMemory(tempBufLen, buf, bufLen);
+ return true;
+}
+
+template <>
+bool IQOARequestHandler::ShouldBroker(Endpoint endpoint, const HINTERNET& h,
+ const DWORD& opt, const LPVOID& buf,
+ const LPDWORD& bufLen) {
+ // If we are server side then we were already validated since we had to be
+ // looked up in the "uint64_t <-> HINTERNET" hashtable.
+ // In the client, we check that this is a dummy handle.
+ return (endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h));
+}
+
+// Marshal all of the output parameters that we sent to the response delegate.
+template <>
+template <>
+struct InternetQueryOptionAFB::Response::Info::ShouldMarshal<0> {
+ static const bool value = true;
+};
+template <>
+template <>
+struct InternetQueryOptionAFB::Response::Info::ShouldMarshal<1> {
+ static const bool value = true;
+};
+
+typedef InternetQueryOptionAFB::Response IQOAResponseHandler;
+typedef InternetQueryOptionAFB::ResponseDelegate<BOOL HOOK_CALL(
+ nsDependentCSubstring, DWORD)>
+ IQOADelegateResponseHandler;
+
+template <>
+void IQOAResponseHandler::Marshal(IpdlTuple& aTuple, const BOOL& ret,
+ const HINTERNET& h, const DWORD& opt,
+ const LPVOID& buf, const LPDWORD& bufLen) {
+ nsDependentCSubstring str;
+ if (buf && ret) {
+ MOZ_ASSERT(*bufLen);
+ str.Assign(static_cast<const char*>(buf), *bufLen);
+ }
+ IQOADelegateResponseHandler::Marshal(aTuple, ret, str, *bufLen);
+}
+
+template <>
+bool IQOAResponseHandler::Unmarshal(const IpdlTuple& aTuple, BOOL& ret,
+ HINTERNET& h, DWORD& opt, LPVOID& buf,
+ LPDWORD& bufLen) {
+ nsDependentCSubstring str;
+ bool success =
+ IQOADelegateResponseHandler::Unmarshal(aTuple, ret, str, *bufLen);
+ if (!success) {
+ return false;
+ }
+
+ if (buf && ret) {
+ MOZ_ASSERT(str.Length() == *bufLen);
+ memcpy(buf, str.Data(), str.Length());
+ }
+ return true;
+}
+
+/* InternetErrorDlg */
+
+typedef SslFunctionBroker<ID_InternetErrorDlg, decltype(InternetErrorDlg)>
+ InternetErrorDlgFB;
+
+template <>
+ShouldHookFunc* const InternetErrorDlgFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef RequestInfo<ID_InternetErrorDlg> IEDReqInfo;
+
+template <>
+template <>
+struct IEDReqInfo::FixedValue<4> {
+ static LPVOID* const value;
+};
+LPVOID* const IEDReqInfo::FixedValue<4>::value = nullptr;
+
+typedef InternetErrorDlgFB::Request IEDReqHandler;
+
+template <>
+bool IEDReqHandler::ShouldBroker(Endpoint endpoint, const HWND& hwnd,
+ const HINTERNET& h, const DWORD& err,
+ const DWORD& flags, LPVOID* const& data) {
+ const DWORD SUPPORTED_FLAGS =
+ FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
+ FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_NO_UI;
+
+ // We broker if (1) the handle h is brokered (odd in client),
+ // (2) we support the requested action flags and (3) there is no user
+ // data, which wouldn't make sense for our supported flags anyway.
+ return ((endpoint == SERVER) || IsOdd(reinterpret_cast<uint64_t>(h))) &&
+ (!(flags & ~SUPPORTED_FLAGS)) && (data == nullptr);
+}
+
+/* AcquireCredentialsHandleA */
+
+typedef SslFunctionBroker<ID_AcquireCredentialsHandleA,
+ decltype(AcquireCredentialsHandleA)>
+ AcquireCredentialsHandleAFB;
+
+template <>
+ShouldHookFunc* const AcquireCredentialsHandleAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef RequestInfo<ID_AcquireCredentialsHandleA> ACHAReqInfo;
+
+template <>
+template <>
+struct ACHAReqInfo::FixedValue<0> {
+ static const LPSTR value;
+};
+const LPSTR ACHAReqInfo::FixedValue<0>::value = nullptr;
+
+template <>
+template <>
+struct ACHAReqInfo::FixedValue<1> {
+ static const LPSTR value;
+};
+const LPSTR ACHAReqInfo::FixedValue<1>::value =
+ const_cast<char*>(UNISP_NAME_A); // -Wwritable-strings
+
+template <>
+template <>
+struct ACHAReqInfo::FixedValue<2> {
+ static const unsigned long value;
+};
+const unsigned long ACHAReqInfo::FixedValue<2>::value = SECPKG_CRED_OUTBOUND;
+
+template <>
+template <>
+struct ACHAReqInfo::FixedValue<3> {
+ static void* const value;
+};
+void* const ACHAReqInfo::FixedValue<3>::value = nullptr;
+
+template <>
+template <>
+struct ACHAReqInfo::FixedValue<5> {
+ static const SEC_GET_KEY_FN value;
+};
+const SEC_GET_KEY_FN ACHAReqInfo::FixedValue<5>::value = nullptr;
+
+template <>
+template <>
+struct ACHAReqInfo::FixedValue<6> {
+ static void* const value;
+};
+void* const ACHAReqInfo::FixedValue<6>::value = nullptr;
+
+typedef AcquireCredentialsHandleAFB::Request ACHARequestHandler;
+typedef AcquireCredentialsHandleAFB::RequestDelegate<SECURITY_STATUS HOOK_CALL(
+ LPSTR, LPSTR, unsigned long, void*, PSCHANNEL_CRED, SEC_GET_KEY_FN, void*)>
+ ACHADelegateRequestHandler;
+
+template <>
+void ACHARequestHandler::Marshal(IpdlTuple& aTuple, const LPSTR& principal,
+ const LPSTR& pkg, const unsigned long& credUse,
+ const PVOID& logonId, const PVOID& auth,
+ const SEC_GET_KEY_FN& getKeyFn,
+ const PVOID& getKeyArg,
+ const PCredHandle& cred,
+ const PTimeStamp& expiry) {
+ const PSCHANNEL_CRED& scCred = reinterpret_cast<const PSCHANNEL_CRED&>(auth);
+ ACHADelegateRequestHandler::Marshal(aTuple, principal, pkg, credUse, logonId,
+ scCred, getKeyFn, getKeyArg);
+}
+
+template <>
+bool ACHARequestHandler::Unmarshal(ServerCallData& aScd,
+ const IpdlTuple& aTuple, LPSTR& principal,
+ LPSTR& pkg, unsigned long& credUse,
+ PVOID& logonId, PVOID& auth,
+ SEC_GET_KEY_FN& getKeyFn, PVOID& getKeyArg,
+ PCredHandle& cred, PTimeStamp& expiry) {
+ PSCHANNEL_CRED& scCred = reinterpret_cast<PSCHANNEL_CRED&>(auth);
+ if (!ACHADelegateRequestHandler::Unmarshal(aScd, aTuple, principal, pkg,
+ credUse, logonId, scCred, getKeyFn,
+ getKeyArg)) {
+ return false;
+ }
+
+ cred = aScd.Allocate<CredHandle>();
+ expiry = aScd.Allocate<::TimeStamp>();
+ return true;
+}
+
+typedef ResponseInfo<ID_AcquireCredentialsHandleA> ACHARspInfo;
+
+// Response phase must send output parameters
+template <>
+template <>
+struct ACHARspInfo::ShouldMarshal<7> {
+ static const bool value = true;
+};
+template <>
+template <>
+struct ACHARspInfo::ShouldMarshal<8> {
+ static const bool value = true;
+};
+
+/* QueryCredentialsAttributesA */
+
+typedef SslFunctionBroker<ID_QueryCredentialsAttributesA,
+ decltype(QueryCredentialsAttributesA)>
+ QueryCredentialsAttributesAFB;
+
+template <>
+ShouldHookFunc* const QueryCredentialsAttributesAFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+/* FreeCredentialsHandle */
+
+typedef SslFunctionBroker<ID_FreeCredentialsHandle,
+ decltype(FreeCredentialsHandle)>
+ FreeCredentialsHandleFB;
+
+template <>
+ShouldHookFunc* const FreeCredentialsHandleFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_SSL>;
+
+typedef FreeCredentialsHandleFB::Request FCHReq;
+
+template <>
+bool FCHReq::ShouldBroker(Endpoint endpoint, const PCredHandle& h) {
+ // If we are server side then we were already validated since we had to be
+ // looked up in the "uint64_t <-> CredHandle" hashtable.
+ // In the client, we check that this is a dummy handle.
+ return (endpoint == SERVER) || ((h->dwLower == h->dwUpper) &&
+ IsOdd(static_cast<uint64_t>(h->dwLower)));
+}
+
+/* CreateMutexW */
+
+// Get the user's SID as a string. Returns an empty string on failure.
+static std::wstring GetUserSid() {
+ std::wstring ret;
+ // Get user SID from process token information
+ HANDLE token;
+ BOOL success = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token);
+ if (!success) {
+ return ret;
+ }
+ DWORD bufLen;
+ success = ::GetTokenInformation(token, TokenUser, nullptr, 0, &bufLen);
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return ret;
+ }
+ void* buf = malloc(bufLen);
+ success = ::GetTokenInformation(token, TokenUser, buf, bufLen, &bufLen);
+ MOZ_ASSERT(success);
+ if (success) {
+ TOKEN_USER* tokenUser = static_cast<TOKEN_USER*>(buf);
+ PSID sid = tokenUser->User.Sid;
+ LPWSTR sidStr;
+ success = ::ConvertSidToStringSid(sid, &sidStr);
+ if (success) {
+ ret = sidStr;
+ ::LocalFree(sidStr);
+ }
+ }
+ free(buf);
+ ::CloseHandle(token);
+ return ret;
+}
+
+// Get the name Windows uses for the camera mutex. Returns an empty string
+// on failure.
+// The camera mutex is identified in Windows code using a hard-coded GUID
+// string, "eed3bd3a-a1ad-4e99-987b-d7cb3fcfa7f0", and the user's SID. The GUID
+// value was determined by investigating Windows code. It is referenced in
+// CCreateSwEnum::CCreateSwEnum(void) in devenum.dll.
+static std::wstring GetCameraMutexName() {
+ std::wstring userSid = GetUserSid();
+ if (userSid.empty()) {
+ return userSid;
+ }
+ return std::wstring(L"eed3bd3a-a1ad-4e99-987b-d7cb3fcfa7f0 - ") + userSid;
+}
+
+typedef FunctionBroker<ID_CreateMutexW, decltype(CreateMutexW)> CreateMutexWFB;
+
+template <>
+ShouldHookFunc* const CreateMutexWFB::BaseType::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_CREATEMUTEXW>;
+
+typedef CreateMutexWFB::Request CMWReqHandler;
+typedef CMWReqHandler::Info CMWReqInfo;
+typedef CreateMutexWFB::Response CMWRspHandler;
+
+template <>
+bool CMWReqHandler::ShouldBroker(Endpoint endpoint,
+ const LPSECURITY_ATTRIBUTES& aAttribs,
+ const BOOL& aOwner, const LPCWSTR& aName) {
+ // Statically hold the camera mutex name so that we dont recompute it for
+ // every CreateMutexW call in the client process.
+ static std::wstring camMutexName = GetCameraMutexName();
+
+ // Only broker if we are requesting the camera mutex. Note that we only
+ // need to check that the client is actually requesting the camera. The
+ // command is always valid on the server as long as we can construct the
+ // mutex name.
+ if (endpoint == SERVER) {
+ return !camMutexName.empty();
+ }
+
+ return (!aOwner) && aName && (!camMutexName.empty()) &&
+ (camMutexName == aName);
+}
+
+// We dont need to marshal any parameters. We construct all of them
+// server-side.
+template <>
+template <>
+struct CMWReqInfo::ShouldMarshal<0> {
+ static const bool value = false;
+};
+template <>
+template <>
+struct CMWReqInfo::ShouldMarshal<1> {
+ static const bool value = false;
+};
+template <>
+template <>
+struct CMWReqInfo::ShouldMarshal<2> {
+ static const bool value = false;
+};
+
+template <>
+template <>
+BROKER_DISABLE_CFGUARD HANDLE CreateMutexWFB::RunFunction(
+ CreateMutexWFB::FunctionType* aOrigFunction, base::ProcessId aClientId,
+ LPSECURITY_ATTRIBUTES& aAttribs, BOOL& aOwner, LPCWSTR& aName) const {
+ // Use CreateMutexW to get the camera mutex and DuplicateHandle to open it
+ // for use in the child process.
+ // Recall that aAttribs, aOwner and aName are all unmarshaled so they are
+ // unassigned garbage.
+ SECURITY_ATTRIBUTES mutexAttrib = {sizeof(SECURITY_ATTRIBUTES),
+ nullptr /* ignored */, TRUE};
+ std::wstring camMutexName = GetCameraMutexName();
+ if (camMutexName.empty()) {
+ return 0;
+ }
+ HANDLE serverMutex =
+ ::CreateMutexW(&mutexAttrib, FALSE, camMutexName.c_str());
+ if (serverMutex == 0) {
+ return 0;
+ }
+ ScopedProcessHandle clientProcHandle;
+ if (!base::OpenProcessHandle(aClientId, &clientProcHandle.rwget())) {
+ return 0;
+ }
+ HANDLE ret;
+ if (!::DuplicateHandle(::GetCurrentProcess(), serverMutex, clientProcHandle,
+ &ret, SYNCHRONIZE, FALSE, DUPLICATE_CLOSE_SOURCE)) {
+ return 0;
+ }
+ return ret;
+}
+
+#endif // defined(XP_WIN)
+
+/*****************************************************************************/
+
+#define FUN_HOOK(x) static_cast<FunctionHook*>(x)
+void AddBrokeredFunctionHooks(FunctionHookArray& aHooks) {
+ // We transfer ownership of the FunctionHook objects to the array.
+#if defined(XP_WIN)
+ aHooks[ID_GetKeyState] =
+ FUN_HOOK(new GetKeyStateFB("user32.dll", "GetKeyState", &GetKeyState));
+ aHooks[ID_SetCursorPos] =
+ FUN_HOOK(new SetCursorPosFB("user32.dll", "SetCursorPos", &SetCursorPos));
+ aHooks[ID_GetSaveFileNameW] = FUN_HOOK(new GetSaveFileNameWFB(
+ "comdlg32.dll", "GetSaveFileNameW", &GetSaveFileNameW));
+ aHooks[ID_GetOpenFileNameW] = FUN_HOOK(new GetOpenFileNameWFB(
+ "comdlg32.dll", "GetOpenFileNameW", &GetOpenFileNameW));
+ aHooks[ID_InternetOpenA] = FUN_HOOK(
+ new InternetOpenAFB("wininet.dll", "InternetOpenA", &InternetOpenA));
+ aHooks[ID_InternetConnectA] = FUN_HOOK(new InternetConnectAFB(
+ "wininet.dll", "InternetConnectA", &InternetConnectA));
+ aHooks[ID_InternetCloseHandle] = FUN_HOOK(new InternetCloseHandleFB(
+ "wininet.dll", "InternetCloseHandle", &InternetCloseHandle));
+ aHooks[ID_InternetQueryDataAvailable] =
+ FUN_HOOK(new InternetQueryDataAvailableFB("wininet.dll",
+ "InternetQueryDataAvailable",
+ &InternetQueryDataAvailable));
+ aHooks[ID_InternetReadFile] = FUN_HOOK(new InternetReadFileFB(
+ "wininet.dll", "InternetReadFile", &InternetReadFile));
+ aHooks[ID_InternetWriteFile] = FUN_HOOK(new InternetWriteFileFB(
+ "wininet.dll", "InternetWriteFile", &InternetWriteFile));
+ aHooks[ID_InternetSetOptionA] = FUN_HOOK(new InternetSetOptionAFB(
+ "wininet.dll", "InternetSetOptionA", &InternetSetOptionA));
+ aHooks[ID_HttpAddRequestHeadersA] = FUN_HOOK(new HttpAddRequestHeadersAFB(
+ "wininet.dll", "HttpAddRequestHeadersA", &HttpAddRequestHeadersA));
+ aHooks[ID_HttpOpenRequestA] = FUN_HOOK(new HttpOpenRequestAFB(
+ "wininet.dll", "HttpOpenRequestA", &HttpOpenRequestA));
+ aHooks[ID_HttpQueryInfoA] = FUN_HOOK(
+ new HttpQueryInfoAFB("wininet.dll", "HttpQueryInfoA", &HttpQueryInfoA));
+ aHooks[ID_HttpSendRequestA] = FUN_HOOK(new HttpSendRequestAFB(
+ "wininet.dll", "HttpSendRequestA", &HttpSendRequestA));
+ aHooks[ID_HttpSendRequestExA] = FUN_HOOK(new HttpSendRequestExAFB(
+ "wininet.dll", "HttpSendRequestExA", &HttpSendRequestExA));
+ aHooks[ID_HttpEndRequestA] = FUN_HOOK(new HttpEndRequestAFB(
+ "wininet.dll", "HttpEndRequestA", &HttpEndRequestA));
+ aHooks[ID_InternetQueryOptionA] = FUN_HOOK(new InternetQueryOptionAFB(
+ "wininet.dll", "InternetQueryOptionA", &InternetQueryOptionA));
+ aHooks[ID_InternetErrorDlg] = FUN_HOOK(new InternetErrorDlgFB(
+ "wininet.dll", "InternetErrorDlg", InternetErrorDlg));
+ aHooks[ID_AcquireCredentialsHandleA] =
+ FUN_HOOK(new AcquireCredentialsHandleAFB("sspicli.dll",
+ "AcquireCredentialsHandleA",
+ &AcquireCredentialsHandleA));
+ aHooks[ID_QueryCredentialsAttributesA] =
+ FUN_HOOK(new QueryCredentialsAttributesAFB("sspicli.dll",
+ "QueryCredentialsAttributesA",
+ &QueryCredentialsAttributesA));
+ aHooks[ID_FreeCredentialsHandle] = FUN_HOOK(new FreeCredentialsHandleFB(
+ "sspicli.dll", "FreeCredentialsHandle", &FreeCredentialsHandle));
+ aHooks[ID_CreateMutexW] = FUN_HOOK(
+ new CreateMutexWFB("kernel32.dll", "CreateMutexW", &CreateMutexW));
+#endif // defined(XP_WIN)
+}
+
+#undef FUN_HOOK
+
+} // namespace mozilla::plugins
diff --git a/dom/plugins/ipc/FunctionBroker.h b/dom/plugins/ipc/FunctionBroker.h
new file mode 100644
index 0000000000..ddbde631e3
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBroker.h
@@ -0,0 +1,1452 @@
+/* -*- 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_PluginHooksWin_h
+#define dom_plugins_ipc_PluginHooksWin_h 1
+
+#include <map>
+#include <algorithm>
+#include <utility>
+#include "base/task.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "FunctionBrokerChild.h"
+#include "transport/runnable_utils.h"
+#include "PluginMessageUtils.h"
+#include "mozilla/Logging.h"
+#include "FunctionHook.h"
+#include "FunctionBrokerIPCUtils.h"
+
+#if defined(XP_WIN)
+# define SECURITY_WIN32
+# include <security.h>
+# include <wininet.h>
+# include <schnlsp.h>
+# if defined(MOZ_SANDBOX)
+# include "sandboxPermissions.h"
+# endif
+#endif // defined(XP_WIN)
+
+/**
+ * This functionality supports automatic method hooking (FunctionHook) and
+ * brokering (FunctionBroker), which are used to intercept system calls
+ * (using the nsDllInterceptor) and replace them with new functionality (hook)
+ * or proxy them on another process (broker).
+ * There isn't much of a public interface to this (see FunctionHook
+ * for initialization functionality) since the majority of the behavior
+ * comes from intercepting calls to DLL methods (making those DLL methods the
+ * public interface). Generic RPC can be achieved without DLLs or function
+ * interception by directly calling the FunctionBroker::InterceptorStub.
+ *
+ * The system supports the most common logic surrounding brokering by allowing
+ * the client to supply strategies for them. Some examples of common tasks that
+ * are supported by automatic brokering:
+ *
+ * * Intercepting a new Win32 method:
+ *
+ * Step 1: Add a typedef or subclass of either FunctionHook (non-brokering) or
+ * FunctionBroker (automatic brokering) to FunctionBroker.cpp, using a new
+ * FunctionHookID (added to that enum).
+ * For example:
+ * typedef FunctionBroker<ID_GetKeyState, decltype(GetKeyState)> GetKeyStateFB
+ * Use a subclass instead of a typedef if you need to maintain data or state.
+ *
+ * Step 2: Add an instance of that object to the FunctionHookList in
+ * AddFunctionHook(FunctionHookList&) or
+ * AddBrokeredFunctionHook(FunctionHookList&).
+ * This typically just means calling the constructor with the correct info.
+ * At a minimum, this means supplying the names of the DLL and method to
+ * broker, and a pointer to the original version of the method.
+ * For example:
+ * aHooks[ID_GetKeyState] =
+ * new GetKeyStateFB("user32.dll", "GetKeyState", &GetKeyState);
+ *
+ * Step 3: If brokering, make sure the system can (un)marshal the parameters,
+ * either by the means below or by adding the type to IpdlTuple, which we use
+ * for type-safely (un)marshaling the parameter list.
+ *
+ * * Only brokering _some_ calls to the method:
+ *
+ * FunctionBroker's constructor allows the user to supply a ShouldBroker
+ * function, which takes the parameters of the method call and returns false
+ * if we should use the original method instead of brokering.
+ *
+ * * Only passing _some_ parameters to the brokering process / returning
+ * parameters to client:
+ *
+ * If a system call changes a parameter call-by-reference style then the
+ * parameter's value needs to be returned to the client. The FunctionBroker
+ * has "phase" (request/response) objects that it uses to determine which
+ * parameters are sent/returned. This example tells InternetWriteFileFB to
+ * return its third parameter:
+ * template<> template<>
+ * struct InternetWriteFileFB::Response::Info::ShouldMarshal<3> {
+ * static const bool value = true;
+ * };
+ * By default, all parameters have ShouldMarshal set in the request phase
+ * and only the return value (parameter -1) has it set in the response phase.
+ *
+ * * Marshalling special parameter/return types:
+ *
+ * The IPCTypeMap in FunctionBroker maps a parameter or return type
+ * to a type that IpdlTuple knows how to marshal. By default, the map is
+ * the identity but some types need special handling.
+ * The map is endpoint-specific (it is a member of the EndpointHandler),
+ * so a different type can be used
+ * for client -> server and for server -> client. Note that the
+ * types must be able to Copy() from one another -- the default Copy()
+ * implementation uses the type's assignment operator.
+ * The EndpointHandler itself is a template parameter of the FunctionBroker.
+ * The default EndpointHandler recognizes basic types.
+ * See e.g. FileDlgEndpointHandler<CLIENT>::IPCTypeMap<LPOPENFILENAMEW>
+ * for an example of specialization.
+ *
+ * * Anything more complex involving parameter transmission:
+ *
+ * Sometimes marshaling parameters can require something more complex. In
+ * those cases, you will need to specialize the Marshal and Unmarshal
+ * methods of the request or response handler and perform your complex logic
+ * there. A wise approach is to map your complex parameters into a simpler
+ * parameter list and delegate the Marshal/Unmarshal calls to them. For
+ * example, an API might take a void* and an int as a buffer and length.
+ * Obviously a void* cannot generally be marshaled. However, we can delegate
+ * this call to a parameter list that takes a string in place of the buffer and
+ * length. Something like:
+ *
+ * typedef RequestHandler<ID_HookedFunc,
+ * int HOOK_CALL (nsDependentCSubstring)>
+ * HookedFuncDelegateReq;
+ *
+ * template<>
+ * void HookedFuncFB::Request::Marshal(IpdlTuple& aTuple, const void*& aBuf,
+ * const int& aBufLen)
+ * {
+ * MOZ_ASSERT(nWritten);
+ * HookedFuncDelegateReq::Marshal(aTuple,
+ * nsDependentCSubstring(aBuf, aBufLen));
+ * }
+ *
+ * template<>
+ * bool HookedFuncFB::Request::Unmarshal(ServerCallData& aScd, const IpdlTuple&
+ * aTuple, void*& aBuf, int& aBufLen)
+ * {
+ * nsDependentCSubstring str;
+ * if (!HookedFuncDelegateReq::Unmarshal(aScd, aTuple, str)) {
+ * return false;
+ * }
+ *
+ * // Request phase unmarshal uses ServerCallData for dynamically-allocating
+ * // memory.
+ * aScd.AllocateString(str, aBuf, false);
+ * aBufLen = str.Length();
+ * return true;
+ * }
+ *
+ * See e.g. InternetWriteFileFB for a complete example of delegation.
+ *
+ * * Brokering but need the server to do more than just run the function:
+ *
+ * Specialize the FunctionBroker's RunFunction. By default, it just runs
+ * the function. See GetSaveFileNameWFB for an example that does more.
+ *
+ */
+
+#if defined(XP_WIN) && defined(__clang__)
+# if __has_declspec_attribute(guard)
+// Workaround for https://bugs.llvm.org/show_bug.cgi?id=47617
+// Some of the brokered function thunks don't get properly marked as call
+// targets, so we have to disable CFG when returning to the original function.
+# define BROKER_DISABLE_CFGUARD __declspec(guard(nocf))
+# else
+# define BROKER_DISABLE_CFGUARD /* nothing */
+# endif
+#else
+# define BROKER_DISABLE_CFGUARD /* nothing */
+#endif
+
+namespace mozilla {
+namespace plugins {
+
+#if defined(XP_WIN)
+
+// Currently, all methods we hook use the WINAPI calling convention.
+# define HOOK_CALL WINAPI
+
+typedef std::pair<ULONG_PTR, ULONG_PTR> UlongPair;
+typedef std::map<UlongPair, uint64_t> UlongPairToIdMap;
+extern UlongPairToIdMap sPairToIdMap;
+typedef std::map<uint64_t, UlongPair> IdToUlongPairMap;
+extern IdToUlongPairMap sIdToPairMap;
+typedef std::map<void*, uint64_t> PtrToIdMap;
+extern PtrToIdMap sPtrToIdMap;
+typedef std::map<uint64_t, void*> IdToPtrMap;
+extern IdToPtrMap sIdToPtrMap;
+
+#else // defined(XP_WIN)
+
+// Any methods we hook use the default calling convention.
+# define HOOK_CALL
+
+#endif // defined(XP_WIN)
+
+inline bool IsOdd(uint64_t aVal) { return aVal & 1; }
+
+// This enum is used to track if this process is currently running the client
+// or server side of brokering.
+enum Endpoint { SERVER, CLIENT };
+inline const char* EndpointMsg(Endpoint aVal) {
+ return aVal == SERVER ? "SERVER" : "CLIENT";
+}
+
+template <typename ParamType>
+inline void LogParameterValue(int aIndex, const ParamType& aParam) {
+ // To avoid overhead, don't do this in release.
+#ifdef DEBUG
+ if (!MOZ_LOG_TEST(sPluginHooksLog, LogLevel::Verbose)) {
+ return;
+ }
+ std::wstring paramString;
+ IPC::LogParam(aParam, &paramString);
+ HOOK_LOG(LogLevel::Verbose,
+ ("Parameter %d: %S", aIndex, paramString.c_str()));
+#endif
+}
+
+// This specialization is needed to log the common pattern where null is used
+// as a fixed value for a pointer-type that is unknown to IPC.
+template <typename ParamType>
+inline void LogParameterValue(int aIndex, ParamType* const& aParam) {
+#ifdef DEBUG
+ HOOK_LOG(LogLevel::Verbose,
+ ("Parameter %d: pointer value - %p", aIndex, aParam));
+#endif
+}
+
+template <>
+inline void LogParameterValue(int aIndex, const nsDependentCSubstring& aParam) {
+#ifdef DEBUG
+ HOOK_LOG(LogLevel::Verbose,
+ ("Parameter %d : %s", aIndex, FormatBlob(aParam).Data()));
+#endif
+}
+
+template <>
+inline void LogParameterValue(int aIndex, char* const& aParam) {
+#ifdef DEBUG
+ // A char* can be a block of raw memory.
+ nsDependentCSubstring str;
+ if (aParam) {
+ str.Rebind(const_cast<char*>(aParam),
+ strnlen(aParam, MAX_BLOB_CHARS_TO_LOG));
+ } else {
+ str.SetIsVoid(true);
+ }
+ LogParameterValue(aIndex, str);
+#endif
+}
+
+template <>
+inline void LogParameterValue(int aIndex, const char* const& aParam) {
+#ifdef DEBUG
+ LogParameterValue(aIndex, const_cast<char* const&>(aParam));
+#endif
+}
+
+#if defined(XP_WIN)
+template <>
+inline void LogParameterValue(int aIndex, const SEC_GET_KEY_FN& aParam) {
+# ifdef DEBUG
+ MOZ_ASSERT(aParam == nullptr);
+ HOOK_LOG(LogLevel::Verbose, ("Parameter %d: null function.", aIndex));
+# endif
+}
+
+template <>
+inline void LogParameterValue(int aIndex, LPVOID* const& aParam) {
+# ifdef DEBUG
+ MOZ_ASSERT(aParam == nullptr);
+ HOOK_LOG(LogLevel::Verbose, ("Parameter %d: null void pointer.", aIndex));
+# endif
+}
+#endif // defined(XP_WIN)
+
+// Used to check if a fixed parameter value is equal to the parameter given
+// in the original function call.
+template <typename ParamType>
+inline bool ParameterEquality(const ParamType& aParam1,
+ const ParamType& aParam2) {
+ return aParam1 == aParam2;
+}
+
+// Specialization: char* equality is string equality
+template <>
+inline bool ParameterEquality(char* const& aParam1, char* const& aParam2) {
+ return ((!aParam1 && !aParam2) ||
+ (aParam1 && aParam2 && !strcmp(aParam1, aParam2)));
+}
+
+// Specialization: const char* const equality is string equality
+template <>
+inline bool ParameterEquality(const char* const& aParam1,
+ const char* const& aParam2) {
+ return ParameterEquality(const_cast<char* const&>(aParam1),
+ const_cast<char* const&>(aParam2));
+}
+
+/**
+ * A type map _from_ the type of a parameter in the original function
+ * we are brokering _to_ a type that we can marshal. We must be able
+ * to Copy() the marshaled type using the parameter type.
+ * The default maps from type T back to type T.
+ */
+template <typename OrigType>
+struct IPCTypeMap {
+ typedef OrigType ipc_type;
+};
+template <>
+struct IPCTypeMap<char*> {
+ typedef nsDependentCSubstring ipc_type;
+};
+template <>
+struct IPCTypeMap<const char*> {
+ typedef nsDependentCSubstring ipc_type;
+};
+template <>
+struct IPCTypeMap<wchar_t*> {
+ typedef nsString ipc_type;
+};
+template <>
+struct IPCTypeMap<const wchar_t*> {
+ typedef nsString ipc_type;
+};
+template <>
+struct IPCTypeMap<long> {
+ typedef int32_t ipc_type;
+};
+template <>
+struct IPCTypeMap<unsigned long> {
+ typedef uint32_t ipc_type;
+};
+
+#if defined(XP_WIN)
+template <>
+struct IPCTypeMap<PSecHandle> {
+ typedef uint64_t ipc_type;
+};
+template <>
+struct IPCTypeMap<PTimeStamp> {
+ typedef uint64_t ipc_type;
+};
+template <>
+struct IPCTypeMap<void*> {
+ typedef uint64_t ipc_type;
+}; // HANDLEs
+template <>
+struct IPCTypeMap<HWND> {
+ typedef NativeWindowHandle ipc_type;
+};
+template <>
+struct IPCTypeMap<PSCHANNEL_CRED> {
+ typedef IPCSchannelCred ipc_type;
+};
+template <>
+struct IPCTypeMap<LPINTERNET_BUFFERSA> {
+ typedef IPCInternetBuffers ipc_type;
+};
+template <>
+struct IPCTypeMap<LPDWORD> {
+ typedef uint32_t ipc_type;
+};
+#endif
+
+template <typename AllocType>
+static void DeleteDestructor(void* aObj) {
+ delete static_cast<AllocType*>(aObj);
+}
+
+extern void FreeDestructor(void* aObj);
+
+// The ServerCallData is a list of ServerCallItems that should be freed when
+// the server has completed a function call and marshaled a response.
+class ServerCallData {
+ public:
+ typedef void(DestructorType)(void*);
+
+ // Allocate a certain type.
+ template <typename AllocType>
+ AllocType* Allocate(
+ DestructorType* aDestructor = &DeleteDestructor<AllocType>) {
+ AllocType* ret = new AllocType();
+ mList.AppendElement(FreeItem(ret, aDestructor));
+ return ret;
+ }
+
+ template <typename AllocType>
+ AllocType* Allocate(
+ const AllocType& aValueToCopy,
+ DestructorType* aDestructor = &DeleteDestructor<AllocType>) {
+ AllocType* ret = Allocate<AllocType>(aDestructor);
+ *ret = aValueToCopy;
+ return ret;
+ }
+
+ // Allocate memory, storing the pointer in buf.
+ template <typename PtrType>
+ void AllocateMemory(unsigned long aBufLen, PtrType& aBuf) {
+ if (aBufLen) {
+ aBuf = static_cast<PtrType>(malloc(aBufLen));
+ mList.AppendElement(FreeItem(aBuf, FreeDestructor));
+ } else {
+ aBuf = nullptr;
+ }
+ }
+
+ template <typename PtrType>
+ void AllocateString(const nsACString& aStr, PtrType& aBuf,
+ bool aCopyNullTerminator = true) {
+ uint32_t nullByte = aCopyNullTerminator ? 1 : 0;
+ char* tempBuf = static_cast<char*>(malloc(aStr.Length() + nullByte));
+ memcpy(tempBuf, aStr.Data(), aStr.Length() + nullByte);
+ mList.AppendElement(FreeItem(tempBuf, FreeDestructor));
+ aBuf = tempBuf;
+ }
+
+ // Run the given destructor on the given memory, for special cases where
+ // memory is allocated elsewhere but must still be freed.
+ void PostDestructor(void* aMem, DestructorType* aDestructor) {
+ mList.AppendElement(FreeItem(aMem, aDestructor));
+ }
+
+#if defined(XP_WIN)
+ // Allocate memory and a DWORD block-length, storing them in the
+ // corresponding parameters.
+ template <typename PtrType>
+ void AllocateMemory(DWORD aBufLen, PtrType& aBuf, LPDWORD& aBufLenCopy) {
+ aBufLenCopy = static_cast<LPDWORD>(malloc(sizeof(DWORD)));
+ *aBufLenCopy = aBufLen;
+ mList.AppendElement(FreeItem(aBufLenCopy, FreeDestructor));
+ AllocateMemory(aBufLen, aBuf);
+ }
+#endif // defined(XP_WIN)
+
+ private:
+ // FreeItems are used to free objects that were temporarily needed for
+ // dispatch, such as buffers that are given as a parameter.
+ class FreeItem {
+ void* mPtr;
+ DestructorType* mDestructor;
+ FreeItem(FreeItem& aOther); // revoked
+ public:
+ explicit FreeItem(void* aPtr, DestructorType* aDestructor)
+ : mPtr(aPtr), mDestructor(aDestructor) {
+ MOZ_ASSERT(mDestructor || !aPtr);
+ }
+
+ FreeItem(FreeItem&& aOther)
+ : mPtr(aOther.mPtr), mDestructor(aOther.mDestructor) {
+ aOther.mPtr = nullptr;
+ aOther.mDestructor = nullptr;
+ }
+
+ ~FreeItem() {
+ if (mDestructor) {
+ mDestructor(mPtr);
+ }
+ }
+ };
+
+ typedef nsTArray<FreeItem> FreeItemList;
+ FreeItemList mList;
+};
+
+// Holds an IpdlTuple and a ServerCallData. This is used by the phase handlers
+// (RequestHandler and ResponseHandler) in the Unmarshaling phase.
+// Server-side unmarshaling (during the request phase) uses a ServerCallData
+// to keep track of allocated memory. In the client, ServerCallDatas are
+// not used and that value will always be null.
+class IpdlTupleContext {
+ public:
+ explicit IpdlTupleContext(const IpdlTuple* aTuple,
+ ServerCallData* aScd = nullptr)
+ : mTuple(aTuple), mScd(aScd) {
+ MOZ_ASSERT(aTuple);
+ }
+
+ ServerCallData* GetServerCallData() { return mScd; }
+ const IpdlTuple* GetIpdlTuple() { return mTuple; }
+
+ private:
+ const IpdlTuple* mTuple;
+ ServerCallData* mScd;
+};
+
+template <typename DestType, typename SrcType>
+inline void Copy(DestType& aDest, const SrcType& aSrc) {
+ aDest = (DestType)aSrc;
+}
+
+template <>
+inline void Copy(nsDependentCSubstring& aDest,
+ const nsDependentCSubstring& aSrc) {
+ if (aSrc.IsVoid()) {
+ aDest.SetIsVoid(true);
+ } else {
+ aDest.Rebind(aSrc.Data(), aSrc.Length());
+ }
+}
+
+#if defined(XP_WIN)
+
+template <>
+inline void Copy(uint64_t& aDest, const PTimeStamp& aSrc) {
+ aDest = static_cast<uint64_t>(aSrc->QuadPart);
+}
+
+template <>
+inline void Copy(PTimeStamp& aDest, const uint64_t& aSrc) {
+ aDest->QuadPart = static_cast<LONGLONG>(aSrc);
+}
+
+#endif // defined(XP_WIN)
+
+template <Endpoint e, typename SelfType>
+struct BaseEndpointHandler;
+template <typename SelfType>
+struct BaseEndpointHandler<CLIENT, SelfType> {
+ static const Endpoint OtherSide = SERVER;
+
+ template <typename DestType, typename SrcType>
+ inline static void Copy(ServerCallData* aScd, DestType& aDest,
+ const SrcType& aSrc) {
+ MOZ_ASSERT(!aScd); // never used in the CLIENT
+ SelfType::Copy(aDest, aSrc);
+ }
+
+ template <typename DestType, typename SrcType>
+ inline static void Copy(DestType& aDest, const SrcType& aSrc) {
+ mozilla::plugins::Copy(aDest, aSrc);
+ }
+
+ // const char* should be null terminated but this is not always the case.
+ // In those cases, we must override this default behavior.
+ inline static void Copy(nsDependentCSubstring& aDest,
+ const char* const& aSrc) {
+ // In the client, we just bind to the caller's string
+ if (aSrc) {
+ aDest.Rebind(aSrc, strlen(aSrc));
+ } else {
+ aDest.SetIsVoid(true);
+ }
+ }
+
+ inline static void Copy(const char*& aDest,
+ const nsDependentCSubstring& aSrc) {
+ MOZ_ASSERT_UNREACHABLE("Cannot return const parameters.");
+ }
+
+ inline static void Copy(nsDependentCSubstring& aDest, char* const& aSrc) {
+ // In the client, we just bind to the caller's string
+ if (aSrc) {
+ aDest.Rebind(aSrc, strlen(aSrc));
+ } else {
+ aDest.SetIsVoid(true);
+ }
+ }
+
+ inline static void Copy(nsString& aDest, wchar_t* const& aSrc) {
+ if (aSrc) {
+ // We are using nsString as a "raw" container for a wchar_t string. We
+ // just use its data as a wchar_t* later (so the reinterpret_cast is
+ // safe).
+ aDest.Rebind(reinterpret_cast<char16_t*>(aSrc), wcslen(aSrc));
+ } else {
+ aDest.SetIsVoid(true);
+ }
+ }
+
+ inline static void Copy(char*& aDest, const nsDependentCSubstring& aSrc) {
+ MOZ_ASSERT_UNREACHABLE("Returning char* parameters is not yet suported.");
+ }
+
+#if defined(XP_WIN)
+ inline static void Copy(uint32_t& aDest, const LPDWORD& aSrc) {
+ aDest = *aSrc;
+ }
+
+ inline static void Copy(LPDWORD& aDest, const uint32_t& aSrc) {
+ *aDest = aSrc;
+ }
+#endif // #if defined(XP_WIN)
+};
+
+template <typename SelfType>
+struct BaseEndpointHandler<SERVER, SelfType> {
+ static const Endpoint OtherSide = CLIENT;
+
+ // Specializations of this method may allocate memory for types that need it
+ // during Unmarshaling. They record the allocation in the ServerCallData.
+ // When copying values in the SERVER, we should be sure to carefully validate
+ // the information that came from the client as the client may be compromised
+ // by malicious code.
+ template <typename DestType, typename SrcType>
+ inline static void Copy(ServerCallData* aScd, DestType& aDest,
+ const SrcType& aSrc) {
+ SelfType::Copy(aDest, aSrc);
+ }
+
+ template <typename DestType, typename SrcType>
+ inline static void Copy(DestType& aDest, const SrcType& aSrc) {
+ mozilla::plugins::Copy(aDest, aSrc);
+ }
+
+ inline static void Copy(nsDependentCSubstring& aDest,
+ const nsDependentCSubstring& aSrc) {
+ aDest.Rebind(aSrc.Data(), aSrc.Length());
+ aDest.SetIsVoid(aSrc.IsVoid());
+ }
+
+ // const char* should be null terminated but this is not always the case.
+ // In those cases, we override this default behavior.
+ inline static void Copy(nsDependentCSubstring& aDest,
+ const char* const& aSrc) {
+ MOZ_ASSERT_UNREACHABLE(
+ "Const parameter cannot be returned by brokering process.");
+ }
+
+ inline static void Copy(nsDependentCSubstring& aDest, char* const& aSrc) {
+ MOZ_ASSERT_UNREACHABLE("Returning char* parameters is not yet suported.");
+ }
+
+ inline static void Copy(ServerCallData* aScd, char*& aDest,
+ const nsDependentCSubstring& aSrc) {
+ // In the parent, we must allocate the string.
+ MOZ_ASSERT(aScd);
+ if (aSrc.IsVoid()) {
+ aDest = nullptr;
+ return;
+ }
+ aScd->AllocateMemory(aSrc.Length() + 1, aDest);
+ memcpy(aDest, aSrc.Data(), aSrc.Length());
+ aDest[aSrc.Length()] = '\0';
+ }
+
+ inline static void Copy(ServerCallData* aScd, const char*& aDest,
+ const nsDependentCSubstring& aSrc) {
+ char* nonConstDest;
+ Copy(aScd, nonConstDest, aSrc);
+ aDest = nonConstDest;
+ }
+
+ inline static void Copy(ServerCallData* aScd, wchar_t*& aDest,
+ const nsString& aSrc) {
+ // Allocating the string with aScd means it will last during the server call
+ // and be freed when the call is complete.
+ MOZ_ASSERT(aScd);
+ if (aSrc.IsVoid()) {
+ aDest = nullptr;
+ return;
+ }
+ aScd->AllocateMemory((aSrc.Length() + 1) * sizeof(wchar_t), aDest);
+ memcpy(aDest, aSrc.Data(), aSrc.Length() * sizeof(wchar_t));
+ aDest[aSrc.Length()] = L'\0';
+ }
+
+ inline static void Copy(ServerCallData* aScd, const wchar_t*& aDest,
+ const nsString& aSrc) {
+ wchar_t* nonConstDest;
+ Copy(aScd, nonConstDest, aSrc);
+ aDest = nonConstDest;
+ }
+
+#if defined(XP_WIN)
+ inline static void Copy(uint32_t& aDest, const LPDWORD& aSrc) {
+ aDest = *aSrc;
+ }
+
+ inline static void Copy(LPDWORD& aDest, const uint32_t& aSrc) {
+ MOZ_RELEASE_ASSERT(aDest);
+ *aDest = aSrc;
+ }
+
+ inline static void Copy(ServerCallData* aScd, PTimeStamp& aDest,
+ const uint64_t& aSrc) {
+ MOZ_ASSERT(!aDest);
+ aDest = aScd->Allocate<::TimeStamp>();
+ Copy(aDest, aSrc);
+ }
+#endif // defined(XP_WIN)
+};
+
+// PhaseHandler is a RequestHandler or a ResponseHandler.
+template <Endpoint endpoint, typename PhaseHandler>
+struct Marshaler {
+ // Driver
+ template <int firstIndex = 0, typename... VarParams>
+ static void Marshal(IpdlTuple& aMarshaledTuple, const VarParams&... aParams) {
+ MarshalParameters<firstIndex>(aMarshaledTuple, aParams...);
+ }
+
+ // Driver
+ template <int firstIndex = 0, typename... VarParams>
+ static bool Unmarshal(IpdlTupleContext& aUnmarshaledTuple,
+ VarParams&... aParams) {
+ return UnmarshalParameters<firstIndex>(aUnmarshaledTuple, 0, aParams...);
+ }
+
+ template <int paramIndex, typename OrigType,
+ bool shouldMarshal =
+ PhaseHandler::Info::template ShouldMarshal<paramIndex>::value>
+ struct MaybeMarshalParameter {};
+
+ /**
+ * shouldMarshal = true case
+ */
+ template <int paramIndex, typename OrigType>
+ struct MaybeMarshalParameter<paramIndex, OrigType, true> {
+ template <typename IPCType = typename PhaseHandler::template IPCTypeMap<
+ OrigType>::ipc_type>
+ static void MarshalParameter(IpdlTuple& aMarshaledTuple,
+ const OrigType& aParam) {
+ HOOK_LOG(LogLevel::Verbose, ("%s marshaling parameter %d.",
+ EndpointMsg(endpoint), paramIndex));
+ IPCType ipcObject;
+ // EndpointHandler must be able to Copy() from OrigType to IPCType
+ PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(
+ ipcObject, aParam);
+ LogParameterValue(paramIndex, ipcObject);
+ aMarshaledTuple.AddElement(ipcObject);
+ }
+ };
+
+ /**
+ * shouldMarshal = false case
+ */
+ template <int paramIndex, typename OrigType>
+ struct MaybeMarshalParameter<paramIndex, OrigType, false> {
+ static void MarshalParameter(IpdlTuple& aMarshaledTuple,
+ const OrigType& aParam) {
+ HOOK_LOG(LogLevel::Verbose, ("%s not marshaling parameter %d.",
+ EndpointMsg(endpoint), paramIndex));
+ }
+ };
+
+ /**
+ * Recursive case: marshals aFirstParam to aMarshaledTuple (if desired),
+ * then marshals the aRemainingParams.
+ */
+ template <int paramIndex, typename VarParam, typename... VarParams>
+ static void MarshalParameters(IpdlTuple& aMarshaledTuple,
+ const VarParam& aFirstParam,
+ const VarParams&... aRemainingParams) {
+ MaybeMarshalParameter<paramIndex, VarParam>::MarshalParameter(
+ aMarshaledTuple, aFirstParam);
+ MarshalParameters<paramIndex + 1, VarParams...>(aMarshaledTuple,
+ aRemainingParams...);
+ }
+
+ /**
+ * Base case: empty parameter list -- nothing to marshal.
+ */
+ template <int paramIndex>
+ static void MarshalParameters(IpdlTuple& aMarshaledTuple) {}
+
+ template <int tupleIndex, typename OrigType,
+ bool shouldMarshal =
+ PhaseHandler::Info::template ShouldMarshal<tupleIndex>::value,
+ bool hasFixedValue =
+ PhaseHandler::Info::template HasFixedValue<tupleIndex>::value>
+ struct MaybeUnmarshalParameter {};
+
+ /**
+ * ShouldMarshal = true case. HasFixedValue must be false in that case.
+ */
+ template <int tupleIndex, typename VarParam>
+ struct MaybeUnmarshalParameter<tupleIndex, VarParam, true, false> {
+ template <typename IPCType = typename PhaseHandler::template IPCTypeMap<
+ VarParam>::ipc_type>
+ static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
+ int& aNextTupleIdx,
+ VarParam& aParam) {
+ const IPCType* ipcObject =
+ aUnmarshaledTuple.GetIpdlTuple()->Element<IPCType>(aNextTupleIdx);
+ if (!ipcObject) {
+ HOOK_LOG(LogLevel::Error, ("%s failed to unmarshal parameter %d.",
+ EndpointMsg(endpoint), tupleIndex));
+ return false;
+ }
+ HOOK_LOG(LogLevel::Verbose, ("%s unmarshaled parameter %d.",
+ EndpointMsg(endpoint), tupleIndex));
+ LogParameterValue(tupleIndex, *ipcObject);
+ PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(
+ aUnmarshaledTuple.GetServerCallData(), aParam, *ipcObject);
+ ++aNextTupleIdx;
+ return true;
+ }
+ };
+
+ /**
+ * ShouldMarshal = true : nsDependentCSubstring specialization
+ */
+ template <int tupleIndex>
+ struct MaybeUnmarshalParameter<tupleIndex, nsDependentCSubstring, true,
+ false> {
+ static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
+ int& aNextTupleIdx,
+ nsDependentCSubstring& aParam) {
+ // Deserialize as an nsCString and then copy the info into the
+ // nsDependentCSubstring
+ const nsCString* ipcObject =
+ aUnmarshaledTuple.GetIpdlTuple()->Element<nsCString>(aNextTupleIdx);
+ if (!ipcObject) {
+ HOOK_LOG(LogLevel::Error, ("%s failed to unmarshal parameter %d.",
+ EndpointMsg(endpoint), tupleIndex));
+ return false;
+ }
+ HOOK_LOG(LogLevel::Verbose, ("%s unmarshaled parameter %d.",
+ EndpointMsg(endpoint), tupleIndex));
+
+ aParam.Rebind(ipcObject->Data(), ipcObject->Length());
+ aParam.SetIsVoid(ipcObject->IsVoid());
+ LogParameterValue(tupleIndex, aParam);
+ ++aNextTupleIdx;
+ return true;
+ }
+ };
+
+ /**
+ * ShouldMarshal = true : char* specialization
+ */
+ template <int tupleIndex>
+ struct MaybeUnmarshalParameter<tupleIndex, char*, true, false> {
+ static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
+ int& aNextTupleIdx, char*& aParam) {
+ nsDependentCSubstring tempStr;
+ bool ret =
+ MaybeUnmarshalParameter<tupleIndex, nsDependentCSubstring, true,
+ false>::UnmarshalParameter(aUnmarshaledTuple,
+ aNextTupleIdx,
+ tempStr);
+ PhaseHandler::EHContainer::template EndpointHandler<endpoint>::Copy(
+ aUnmarshaledTuple.GetServerCallData(), aParam, tempStr);
+ return ret;
+ }
+ };
+
+ /**
+ * ShouldMarshal = true : const char* specialization
+ */
+ template <int tupleIndex>
+ struct MaybeUnmarshalParameter<tupleIndex, const char*, true, false> {
+ static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
+ int& aNextTupleIdx,
+ const char*& aParam) {
+ char* tempStr;
+ bool ret =
+ MaybeUnmarshalParameter<tupleIndex, char*, true,
+ false>::UnmarshalParameter(aUnmarshaledTuple,
+ aNextTupleIdx,
+ tempStr);
+ aParam = tempStr;
+ return ret;
+ }
+ };
+
+ /**
+ * ShouldMarshal = false, fixed parameter case
+ */
+ template <int tupleIndex, typename VarParam>
+ struct MaybeUnmarshalParameter<tupleIndex, VarParam, false, true> {
+ static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
+ int& aNextTupleIdx,
+ VarParam& aParam) {
+ // Copy default value if this is client->server communication (and if it
+ // exists)
+ PhaseHandler::template CopyFixedParam<tupleIndex, VarParam>(aParam);
+ HOOK_LOG(LogLevel::Verbose,
+ ("%s parameter %d not unmarshaling -- using fixed value.",
+ EndpointMsg(endpoint), tupleIndex));
+ LogParameterValue(tupleIndex, aParam);
+ return true;
+ }
+ };
+
+ /**
+ * ShouldMarshal = false, unfixed parameter case. Assume user has done
+ * special handling.
+ */
+ template <int tupleIndex, typename VarParam>
+ struct MaybeUnmarshalParameter<tupleIndex, VarParam, false, false> {
+ static inline bool UnmarshalParameter(IpdlTupleContext& aUnmarshaledTuple,
+ int& aNextTupleIdx,
+ VarParam& aParam) {
+ HOOK_LOG(LogLevel::Verbose,
+ ("%s parameter %d not automatically unmarshaling.",
+ EndpointMsg(endpoint), tupleIndex));
+ // DLP: TODO: specializations fail LogParameterValue(tupleIndex, aParam);
+ return true;
+ }
+ };
+
+ /**
+ * Recursive case: unmarshals aFirstParam to aUnmarshaledTuple (if desired),
+ * then unmarshals the aRemainingParams.
+ * The endpoint specifies the side this process is on: client or server.
+ */
+ template <int tupleIndex, typename VarParam, typename... VarParams>
+ static bool UnmarshalParameters(IpdlTupleContext& aUnmarshaledTuple,
+ int aNextTupleIdx, VarParam& aFirstParam,
+ VarParams&... aRemainingParams) {
+ // TODO: DLP: I currently increment aNextTupleIdx in the method (its a
+ // reference). This is awful.
+ if (!MaybeUnmarshalParameter<tupleIndex, VarParam>::UnmarshalParameter(
+ aUnmarshaledTuple, aNextTupleIdx, aFirstParam)) {
+ return false;
+ }
+ return UnmarshalParameters<tupleIndex + 1, VarParams...>(
+ aUnmarshaledTuple, aNextTupleIdx, aRemainingParams...);
+ }
+
+ /**
+ * Base case: empty parameter list -- nothing to unmarshal.
+ */
+ template <int>
+ static bool UnmarshalParameters(IpdlTupleContext& aUnmarshaledTuple,
+ int aNextTupleIdx) {
+ return true;
+ }
+};
+
+// The default marshals all parameters.
+template <FunctionHookId functionId>
+struct RequestInfo {
+ template <int paramIndex>
+ struct FixedValue;
+
+ template <int paramIndex, typename = int>
+ struct HasFixedValue {
+ static const bool value = false;
+ };
+ template <int paramIndex>
+ struct HasFixedValue<paramIndex, decltype(FixedValue<paramIndex>::value, 0)> {
+ static const bool value = true;
+ };
+
+ // By default we the request should marshal any non-fixed parameters.
+ template <int paramIndex>
+ struct ShouldMarshal {
+ static const bool value = !HasFixedValue<paramIndex>::value;
+ };
+};
+
+/**
+ * This base stores the RequestHandler's IPCTypeMap. It really only
+ * exists to circumvent the arbitrary C++ rule (enforced by mingw) forbidding
+ * full class specialization of a class (IPCTypeMap<T>) inside of an
+ * unspecialized template class (RequestHandler<T>).
+ */
+struct RequestHandlerBase {
+ // Default to the namespace-level IPCTypeMap
+ template <typename OrigType>
+ struct IPCTypeMap {
+ typedef typename mozilla::plugins::IPCTypeMap<OrigType>::ipc_type ipc_type;
+ };
+};
+
+#if defined(XP_WIN)
+
+// Request phase uses OpenFileNameIPC for an LPOPENFILENAMEW parameter.
+template <>
+struct RequestHandlerBase::IPCTypeMap<LPOPENFILENAMEW> {
+ typedef OpenFileNameIPC ipc_type;
+};
+
+#endif // defined(XP_WIN)
+
+struct BaseEHContainer {
+ template <Endpoint e>
+ struct EndpointHandler : public BaseEndpointHandler<e, EndpointHandler<e>> {};
+};
+
+template <FunctionHookId functionId, typename FunctionType,
+ typename EHContainer>
+struct RequestHandler;
+
+template <FunctionHookId functionId, typename EHContainerType,
+ typename ResultType, typename... ParamTypes>
+struct RequestHandler<functionId, ResultType HOOK_CALL(ParamTypes...),
+ EHContainerType> : public RequestHandlerBase {
+ typedef ResultType(HOOK_CALL FunctionType)(ParamTypes...);
+ typedef RequestHandler<functionId, FunctionType, EHContainerType> SelfType;
+ typedef RequestInfo<functionId> Info;
+ typedef EHContainerType EHContainer;
+
+ static void Marshal(IpdlTuple& aTuple, const ParamTypes&... aParams) {
+ ReqMarshaler::Marshal(aTuple, aParams...);
+ }
+
+ static bool Unmarshal(ServerCallData& aScd, const IpdlTuple& aTuple,
+ ParamTypes&... aParams) {
+ IpdlTupleContext cxt(&aTuple, &aScd);
+ return ReqUnmarshaler::Unmarshal(cxt, aParams...);
+ }
+
+ typedef Marshaler<CLIENT, SelfType> ReqMarshaler;
+ typedef Marshaler<SERVER, SelfType> ReqUnmarshaler;
+
+ /**
+ * Returns true if a call made with the given parameters should be
+ * brokered (vs. passed-through to the original function).
+ */
+ static bool ShouldBroker(Endpoint aEndpoint, const ParamTypes&... aParams) {
+ // True if all filtered parameters match their filter value.
+ return CheckFixedParams(aParams...);
+ }
+
+ template <int paramIndex, typename VarParam>
+ static void CopyFixedParam(VarParam& aParam) {
+ aParam = Info::template FixedValue<paramIndex>::value;
+ }
+
+ protected:
+ // Returns true if filtered parameters match their filter value.
+ static bool CheckFixedParams(const ParamTypes&... aParams) {
+ return CheckFixedParamsHelper<0>(aParams...);
+ }
+
+ // If no FixedValue<paramIndex> is defined and equal to FixedType then always
+ // pass.
+ template <int paramIndex, typename = int>
+ struct CheckFixedParam {
+ template <typename ParamType>
+ static inline bool Check(const ParamType& aParam) {
+ return true;
+ }
+ };
+
+ // If FixedValue<paramIndex> is defined then check equality.
+ template <int paramIndex>
+ struct CheckFixedParam<
+ paramIndex, decltype(Info::template FixedValue<paramIndex>::value, 0)> {
+ template <typename ParamType>
+ static inline bool Check(ParamType& aParam) {
+ return ParameterEquality(aParam,
+ Info::template FixedValue<paramIndex>::value);
+ }
+ };
+
+ // Recursive case: Chcek head parameter, then tail parameters.
+ template <int index, typename VarParam, typename... VarParams>
+ static bool CheckFixedParamsHelper(const VarParam& aParam,
+ const VarParams&... aParams) {
+ if (!CheckFixedParam<index>::Check(aParam)) {
+ return false; // didn't match a fixed parameter
+ }
+ return CheckFixedParamsHelper<index + 1>(aParams...);
+ }
+
+ // Base case: All fixed parameters matched.
+ template <int>
+ static bool CheckFixedParamsHelper() {
+ return true;
+ }
+};
+
+// The default returns no parameters -- only the return value.
+template <FunctionHookId functionId>
+struct ResponseInfo {
+ template <int paramIndex>
+ struct HasFixedValue {
+ static const bool value =
+ RequestInfo<functionId>::template HasFixedValue<paramIndex>::value;
+ };
+
+ // Only the return value (index -1) is sent by default.
+ template <int paramIndex>
+ struct ShouldMarshal {
+ static const bool value = (paramIndex == -1);
+ };
+
+ // This is the condition on the function result that we use to determine if
+ // the windows thread-local error state should be sent to the client. The
+ // error is typically only relevant if the function did not succeed.
+ template <typename ResultType>
+ static bool ShouldTransmitError(const ResultType& aResult) {
+ return !static_cast<bool>(aResult);
+ }
+};
+
+/**
+ * Same rationale as for RequestHandlerBase.
+ */
+struct ResponseHandlerBase {
+ // Default to the namespace-level IPCTypeMap
+ template <typename OrigType>
+ struct IPCTypeMap {
+ typedef typename mozilla::plugins::IPCTypeMap<OrigType>::ipc_type ipc_type;
+ };
+};
+
+#if defined(XP_WIN)
+
+// Response phase uses OpenFileNameRetIPC for an LPOPENFILENAMEW parameter.
+template <>
+struct ResponseHandlerBase::IPCTypeMap<LPOPENFILENAMEW> {
+ typedef OpenFileNameRetIPC ipc_type;
+};
+
+#endif
+
+template <FunctionHookId functionId, typename FunctionType,
+ typename EHContainer>
+struct ResponseHandler;
+
+template <FunctionHookId functionId, typename EHContainerType,
+ typename ResultType, typename... ParamTypes>
+struct ResponseHandler<functionId, ResultType HOOK_CALL(ParamTypes...),
+ EHContainerType> : public ResponseHandlerBase {
+ typedef ResultType(HOOK_CALL FunctionType)(ParamTypes...);
+ typedef ResponseHandler<functionId, FunctionType, EHContainerType> SelfType;
+ typedef ResponseInfo<functionId> Info;
+ typedef EHContainerType EHContainer;
+
+ static void Marshal(IpdlTuple& aTuple, const ResultType& aResult,
+ const ParamTypes&... aParams) {
+ // Note that this "trick" means that the first parameter we marshal is
+ // considered to be parameter #-1 when checking the ResponseInfo.
+ // The parameters in the list therefore start at index 0.
+ RspMarshaler::template Marshal<-1>(aTuple, aResult, aParams...);
+ }
+ static bool Unmarshal(const IpdlTuple& aTuple, ResultType& aResult,
+ ParamTypes&... aParams) {
+ IpdlTupleContext cxt(&aTuple);
+ return RspUnmarshaler::template Unmarshal<-1>(cxt, aResult, aParams...);
+ }
+
+ typedef Marshaler<SERVER, SelfType> RspMarshaler;
+ typedef Marshaler<CLIENT, SelfType> RspUnmarshaler;
+
+ // Fixed parameters are not used in the response phase.
+ template <int tupleIndex, typename VarParam>
+ static void CopyFixedParam(VarParam& aParam) {}
+};
+
+/**
+ * Reference-counted monitor, used to synchronize communication between a
+ * thread using a brokered API and the FunctionDispatch thread.
+ */
+class FDMonitor : public Monitor {
+ public:
+ FDMonitor() : Monitor("FunctionDispatchThread lock") {}
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FDMonitor)
+
+ private:
+ ~FDMonitor() = default;
+};
+
+/**
+ * Data for hooking a function that we automatically broker in a remote
+ * process.
+ */
+template <FunctionHookId functionId, typename FunctionType,
+ typename EHContainer = BaseEHContainer>
+class FunctionBroker;
+
+template <FunctionHookId functionId, typename EHContainer, typename ResultType,
+ typename... ParamTypes>
+class FunctionBroker<functionId, ResultType HOOK_CALL(ParamTypes...),
+ EHContainer>
+ : public BasicFunctionHook<functionId,
+ ResultType HOOK_CALL(ParamTypes...)> {
+ public:
+ typedef Tuple<ParamTypes...> TupleParamTypes;
+ typedef Tuple<mozilla::Maybe<ParamTypes>...> TupleMaybeParamTypes;
+ typedef Tuple<ParamTypes*...> TupleParamPtrTypes;
+ typedef Tuple<ParamTypes&...> TupleParamRefTypes;
+ static const size_t numParams = sizeof...(ParamTypes);
+
+ typedef ResultType(HOOK_CALL FunctionType)(ParamTypes...);
+ typedef FunctionBroker<functionId, FunctionType, EHContainer> SelfType;
+ typedef BasicFunctionHook<functionId, FunctionType> FunctionHookInfoType;
+ typedef FunctionHookInfoType BaseType;
+
+ typedef RequestHandler<functionId, FunctionType, EHContainer> Request;
+ typedef ResponseHandler<functionId, FunctionType, EHContainer> Response;
+
+ template <typename DelegateFcnType>
+ using RequestDelegate =
+ RequestHandler<functionId, DelegateFcnType, EHContainer>;
+ template <typename DelegateFcnType>
+ using ResponseDelegate =
+ ResponseHandler<functionId, DelegateFcnType, EHContainer>;
+
+ FunctionBroker(const char* aModuleName, const char* aMethodName,
+ FunctionType* aOriginalFunction)
+ : BasicFunctionHook<functionId, FunctionType>(
+ aModuleName, aMethodName, aOriginalFunction, InterceptorStub) {}
+
+ // This is the function used to replace the original DLL-intercepted function.
+ static ResultType HOOK_CALL InterceptorStub(ParamTypes... aParams) {
+ MOZ_ASSERT(functionId < FunctionHook::GetHooks()->Length());
+ FunctionHook* self = FunctionHook::GetHooks()->ElementAt(functionId);
+ MOZ_ASSERT(self && self->FunctionId() == functionId);
+ const SelfType* broker = static_cast<const SelfType*>(self);
+ return broker->MaybeBrokerCallClient(aParams...);
+ }
+
+ /**
+ * Handle a call by running the original version or brokering, depending on
+ * ShouldBroker. All parameter types (including the result type)
+ * must have IPDL ParamTraits specializations or appear in this object's
+ * IPCTypeMap. If brokering fails for any reason then this falls back to
+ * calling the original version of the function.
+ */
+ ResultType MaybeBrokerCallClient(ParamTypes&... aParameters) const;
+
+ /**
+ * Called server-side to run the original function using aInTuple
+ * as parameter values. The return value and returned parameters
+ * (in that order) are added to aOutTuple.
+ */
+ bool RunOriginalFunction(base::ProcessId aClientId,
+ const IPC::IpdlTuple& aInTuple,
+ IPC::IpdlTuple* aOutTuple) const override {
+ return BrokerCallServer(aClientId, aInTuple, aOutTuple);
+ }
+
+ protected:
+ bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple& aInTuple,
+ IpdlTuple* aOutTuple) const {
+ return BrokerCallServer(aClientId, aInTuple, aOutTuple,
+ std::index_sequence_for<ParamTypes...>{});
+ }
+
+ bool BrokerCallClient(uint32_t& aWinError, ResultType& aResult,
+ ParamTypes&... aParameters) const;
+ bool PostToDispatchThread(uint32_t& aWinError, ResultType& aRet,
+ ParamTypes&... aParameters) const;
+
+ static void PostToDispatchHelper(const SelfType* bmhi,
+ RefPtr<FDMonitor> monitor, bool* notified,
+ bool* ok, uint32_t* winErr, ResultType* r,
+ ParamTypes*... p) {
+ // Note: p is also non-null... its just hard to assert that.
+ MOZ_ASSERT(bmhi && monitor && notified && ok && winErr && r);
+ MOZ_ASSERT(*notified == false);
+ *ok = bmhi->BrokerCallClient(*winErr, *r, *p...);
+
+ {
+ // We need to grab the lock to make sure that Wait() has been
+ // called in PostToDispatchThread. We need that since we wake it with
+ // Notify().
+ MonitorAutoLock lock(*monitor);
+ *notified = true;
+ }
+
+ monitor->Notify();
+ };
+
+ template <typename... VarParams>
+ BROKER_DISABLE_CFGUARD ResultType RunFunction(FunctionType* aFunction,
+ base::ProcessId aClientId,
+ VarParams&... aParams) const {
+ return aFunction(aParams...);
+ };
+
+ bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple& aInTuple,
+ IpdlTuple* aOutTuple, ParamTypes&... aParams) const;
+
+ template <size_t... Indices>
+ bool BrokerCallServer(base::ProcessId aClientId, const IpdlTuple& aInTuple,
+ IpdlTuple* aOutTuple,
+ std::index_sequence<Indices...>) const {
+ TupleParamTypes paramTuple;
+ return BrokerCallServer(aClientId, aInTuple, aOutTuple,
+ Get<Indices>(paramTuple)...);
+ }
+};
+
+template <FunctionHookId functionId, typename EHContainer, typename ResultType,
+ typename... ParamTypes>
+ResultType FunctionBroker<
+ functionId, ResultType HOOK_CALL(ParamTypes...),
+ EHContainer>::MaybeBrokerCallClient(ParamTypes&... aParameters) const {
+ MOZ_ASSERT(FunctionBrokerChild::GetInstance());
+
+ // Broker the call if ShouldBroker says to. Otherwise, or if brokering
+ // fails, then call the original implementation.
+ if (!FunctionBrokerChild::GetInstance()) {
+ HOOK_LOG(LogLevel::Error,
+ ("[%s] Client attempted to broker call without actor.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ } else if (Request::ShouldBroker(CLIENT, aParameters...)) {
+ HOOK_LOG(LogLevel::Debug, ("[%s] Client attempting to broker call.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ uint32_t winError;
+ ResultType ret;
+ bool success = BrokerCallClient(winError, ret, aParameters...);
+ HOOK_LOG(LogLevel::Info,
+ ("[%s] Client brokering %s.",
+ FunctionHookInfoType::mFunctionName.Data(), SuccessMsg(success)));
+ if (success) {
+#if defined(XP_WIN)
+ if (Response::Info::ShouldTransmitError(ret)) {
+ HOOK_LOG(LogLevel::Debug,
+ ("[%s] Client setting thread error code: %08x.",
+ FunctionHookInfoType::mFunctionName.Data(), winError));
+ ::SetLastError(winError);
+ }
+#endif
+ return ret;
+ }
+ }
+
+ HOOK_LOG(LogLevel::Info,
+ ("[%s] Client could not broker. Running original version.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ return FunctionHookInfoType::mOldFunction(aParameters...);
+}
+
+template <FunctionHookId functionId, typename EHContainer, typename ResultType,
+ typename... ParamTypes>
+bool FunctionBroker<functionId, ResultType HOOK_CALL(ParamTypes...),
+ EHContainer>::BrokerCallClient(uint32_t& aWinError,
+ ResultType& aResult,
+ ParamTypes&... aParameters)
+ const {
+ if (!FunctionBrokerChild::GetInstance()->IsDispatchThread()) {
+ return PostToDispatchThread(aWinError, aResult, aParameters...);
+ }
+
+ if (FunctionBrokerChild::GetInstance()) {
+ IpdlTuple sending, returned;
+ HOOK_LOG(LogLevel::Debug, ("[%s] Client marshaling parameters.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ Request::Marshal(sending, aParameters...);
+ HOOK_LOG(LogLevel::Info, ("[%s] Client sending broker message.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ if (FunctionBrokerChild::GetInstance()->SendBrokerFunction(
+ FunctionHookInfoType::FunctionId(), sending, &returned)) {
+ HOOK_LOG(LogLevel::Debug,
+ ("[%s] Client received broker message response.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ bool success = Response::Unmarshal(returned, aResult, aParameters...);
+ HOOK_LOG(LogLevel::Info, ("[%s] Client response unmarshaling: %s.",
+ FunctionHookInfoType::mFunctionName.Data(),
+ SuccessMsg(success)));
+#if defined(XP_WIN)
+ if (success && Response::Info::ShouldTransmitError(aResult)) {
+ uint32_t* winError =
+ returned.Element<UINT32>(returned.NumElements() - 1);
+ if (!winError) {
+ HOOK_LOG(LogLevel::Error,
+ ("[%s] Client failed to unmarshal error code.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ return false;
+ }
+ HOOK_LOG(LogLevel::Debug,
+ ("[%s] Client response unmarshaled error code: %08x.",
+ FunctionHookInfoType::mFunctionName.Data(), *winError));
+ aWinError = *winError;
+ }
+#endif
+ return success;
+ }
+ }
+
+ HOOK_LOG(LogLevel::Error, ("[%s] Client failed to broker call.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ return false;
+}
+
+template <FunctionHookId functionId, typename EHContainer, typename ResultType,
+ typename... ParamTypes>
+bool FunctionBroker<functionId, ResultType HOOK_CALL(ParamTypes...),
+ EHContainer>::BrokerCallServer(base::ProcessId aClientId,
+ const IpdlTuple& aInTuple,
+ IpdlTuple* aOutTuple,
+ ParamTypes&... aParams)
+ const {
+ HOOK_LOG(LogLevel::Info, ("[%s] Server brokering function.",
+ FunctionHookInfoType::mFunctionName.Data()));
+
+ ServerCallData scd;
+ if (!Request::Unmarshal(scd, aInTuple, aParams...)) {
+ HOOK_LOG(LogLevel::Info, ("[%s] Server failed to unmarshal.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ return false;
+ }
+
+ // Make sure that this call was legal -- do not execute a call that
+ // shouldn't have been brokered in the first place.
+ if (!Request::ShouldBroker(SERVER, aParams...)) {
+ HOOK_LOG(LogLevel::Error, ("[%s] Server rejected brokering request.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ return false;
+ }
+
+ // Run the function we are brokering.
+ HOOK_LOG(LogLevel::Info, ("[%s] Server broker running function.",
+ FunctionHookInfoType::mFunctionName.Data()));
+ ResultType ret =
+ RunFunction(FunctionHookInfoType::mOldFunction, aClientId, aParams...);
+
+#if defined(XP_WIN)
+ // Record the thread-local error state (before it is changed) if needed.
+ uint32_t err = UINT_MAX;
+ bool transmitError = Response::Info::ShouldTransmitError(ret);
+ if (transmitError) {
+ err = ::GetLastError();
+ HOOK_LOG(LogLevel::Info, ("[%s] Server returning thread error code: %08x.",
+ FunctionHookInfoType::mFunctionName.Data(), err));
+ }
+#endif
+
+ // Add the result, win thread error and any returned parameters to the
+ // returned tuple.
+ Response::Marshal(*aOutTuple, ret, aParams...);
+#if defined(XP_WIN)
+ if (transmitError) {
+ aOutTuple->AddElement(err);
+ }
+#endif
+
+ return true;
+}
+
+template <FunctionHookId functionId, typename EHContainer, typename ResultType,
+ typename... ParamTypes>
+bool FunctionBroker<
+ functionId, ResultType HOOK_CALL(ParamTypes...),
+ EHContainer>::PostToDispatchThread(uint32_t& aWinError, ResultType& aRet,
+ ParamTypes&... aParameters) const {
+ MOZ_ASSERT(!FunctionBrokerChild::GetInstance()->IsDispatchThread());
+ HOOK_LOG(LogLevel::Debug, ("Posting broker task '%s' to dispatch thread",
+ FunctionHookInfoType::mFunctionName.Data()));
+
+ // Run PostToDispatchHelper on the dispatch thread. It will notify our
+ // waiting monitor when it is done.
+ RefPtr<FDMonitor> monitor(new FDMonitor());
+ MonitorAutoLock lock(*monitor);
+ bool success = false;
+ bool notified = false;
+ FunctionBrokerChild::GetInstance()->PostToDispatchThread(NewRunnableFunction(
+ "FunctionDispatchThreadRunnable", &PostToDispatchHelper, this, monitor,
+ &notified, &success, &aWinError, &aRet, &aParameters...));
+
+ // We wait to be notified, testing that notified was actually set to make
+ // sure this isn't a spurious wakeup.
+ while (!notified) {
+ monitor->Wait();
+ }
+ return success;
+}
+
+void AddBrokeredFunctionHooks(FunctionHookArray& aHooks);
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_ipc_PluginHooksWin_h
diff --git a/dom/plugins/ipc/FunctionBrokerChild.cpp b/dom/plugins/ipc/FunctionBrokerChild.cpp
new file mode 100644
index 0000000000..a0780d853f
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBrokerChild.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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 "FunctionBrokerChild.h"
+#include "FunctionBrokerThread.h"
+
+#include "mozilla/ipc/Endpoint.h"
+
+namespace mozilla::plugins {
+
+FunctionBrokerChild* FunctionBrokerChild::sInstance = nullptr;
+
+bool FunctionBrokerChild::IsDispatchThread() { return mThread->IsOnThread(); }
+
+void FunctionBrokerChild::PostToDispatchThread(
+ already_AddRefed<nsIRunnable>&& runnable) {
+ mThread->Dispatch(std::move(runnable));
+}
+
+/* static */
+bool FunctionBrokerChild::Initialize(
+ Endpoint<PFunctionBrokerChild>&& aBrokerEndpoint) {
+ MOZ_RELEASE_ASSERT(
+ XRE_IsPluginProcess(),
+ "FunctionBrokerChild can only be used in plugin processes");
+
+ MOZ_ASSERT(!sInstance);
+ FunctionBrokerThread* thread = FunctionBrokerThread::Create();
+ if (!thread) {
+ return false;
+ }
+ sInstance = new FunctionBrokerChild(thread, std::move(aBrokerEndpoint));
+ return true;
+}
+
+/* static */
+FunctionBrokerChild* FunctionBrokerChild::GetInstance() {
+ MOZ_RELEASE_ASSERT(
+ XRE_IsPluginProcess(),
+ "FunctionBrokerChild can only be used in plugin processes");
+
+ MOZ_ASSERT(sInstance, "Must initialize FunctionBrokerChild before using it");
+ return sInstance;
+}
+
+FunctionBrokerChild::FunctionBrokerChild(
+ FunctionBrokerThread* aThread, Endpoint<PFunctionBrokerChild>&& aEndpoint)
+ : mThread(aThread),
+ mShutdownDone(false),
+ mMonitor("FunctionBrokerChild Lock") {
+ MOZ_ASSERT(aThread);
+ PostToDispatchThread(
+ NewNonOwningRunnableMethod<Endpoint<PFunctionBrokerChild>&&>(
+ "FunctionBrokerChild::Bind", this, &FunctionBrokerChild::Bind,
+ std::move(aEndpoint)));
+}
+
+void FunctionBrokerChild::Bind(Endpoint<PFunctionBrokerChild>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+ DebugOnly<bool> ok = aEndpoint.Bind(this);
+ MOZ_ASSERT(ok);
+}
+
+void FunctionBrokerChild::ShutdownOnDispatchThread() {
+ MOZ_ASSERT(mThread->IsOnThread());
+
+ // Set mShutdownDone and notify waiting thread (if any) that we are done.
+ MonitorAutoLock lock(mMonitor);
+ mShutdownDone = true;
+ mMonitor.Notify();
+}
+
+void FunctionBrokerChild::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_ASSERT(mThread->IsOnThread());
+
+ // Queue up a task on the PD thread. When that task is executed then
+ // we know that anything queued before ActorDestroy has completed.
+ // At that point, we can set mShutdownDone and alert any waiting
+ // threads that it is safe to destroy us.
+ sInstance->PostToDispatchThread(NewNonOwningRunnableMethod(
+ "FunctionBrokerChild::ShutdownOnDispatchThread", sInstance,
+ &FunctionBrokerChild::ShutdownOnDispatchThread));
+}
+
+void FunctionBrokerChild::Destroy() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sInstance) {
+ return;
+ }
+
+ // mShutdownDone will tell us when ActorDestroy has been run and any tasks
+ // on the FunctionBrokerThread have completed. At that point, we can
+ // safely delete the actor.
+ {
+ MonitorAutoLock lock(sInstance->mMonitor);
+ while (!sInstance->mShutdownDone) {
+ // Release lock and wait. Regain lock when we are notified that
+ // we have ShutdownOnDispatchThread.
+ sInstance->mMonitor.Wait();
+ }
+ }
+
+ delete sInstance;
+ sInstance = nullptr;
+}
+
+} // namespace mozilla::plugins
diff --git a/dom/plugins/ipc/FunctionBrokerChild.h b/dom/plugins/ipc/FunctionBrokerChild.h
new file mode 100644
index 0000000000..767aaab170
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBrokerChild.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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_functionbrokerchild_h
+#define mozilla_plugins_functionbrokerchild_h
+
+#include "mozilla/plugins/PFunctionBrokerChild.h"
+
+namespace mozilla {
+namespace plugins {
+
+class FunctionBrokerThread;
+
+/**
+ * Dispatches brokered methods to the Parent process to allow functionality
+ * that is otherwise blocked by the sandbox.
+ */
+class FunctionBrokerChild : public PFunctionBrokerChild {
+ public:
+ static bool Initialize(Endpoint<PFunctionBrokerChild>&& aBrokerEndpoint);
+ static FunctionBrokerChild* GetInstance();
+ static void Destroy();
+
+ bool IsDispatchThread();
+ void PostToDispatchThread(already_AddRefed<nsIRunnable>&& runnable);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ private:
+ explicit FunctionBrokerChild(FunctionBrokerThread* aThread,
+ Endpoint<PFunctionBrokerChild>&& aEndpoint);
+ void ShutdownOnDispatchThread();
+ void Bind(Endpoint<PFunctionBrokerChild>&& aEndpoint);
+
+ UniquePtr<FunctionBrokerThread> mThread;
+
+ // True if tasks on the FunctionBrokerThread have completed
+ bool mShutdownDone;
+ // This monitor guards mShutdownDone.
+ Monitor mMonitor;
+
+ static FunctionBrokerChild* sInstance;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_functionbrokerchild_h
diff --git a/dom/plugins/ipc/FunctionBrokerIPCUtils.cpp b/dom/plugins/ipc/FunctionBrokerIPCUtils.cpp
new file mode 100644
index 0000000000..e0ee31e635
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBrokerIPCUtils.cpp
@@ -0,0 +1,334 @@
+/* -*- 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 "FunctionBrokerIPCUtils.h"
+
+#if defined(XP_WIN)
+
+# include <schannel.h>
+
+/* these defines are missing from mingw headers */
+# ifndef SP_PROT_TLS1_1_CLIENT
+# define SP_PROT_TLS1_1_CLIENT 0x00000200
+# endif
+
+# ifndef SP_PROT_TLS1_2_CLIENT
+# define SP_PROT_TLS1_2_CLIENT 0x00000800
+# endif
+
+namespace mozilla {
+namespace plugins {
+
+mozilla::LazyLogModule sPluginHooksLog("PluginHooks");
+
+static const DWORD SCHANNEL_SUPPORTED_PROTOCOLS =
+ SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
+
+static const DWORD SCHANNEL_SUPPORTED_FLAGS =
+ SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |
+ SCH_CRED_REVOCATION_CHECK_END_CERT;
+
+void OpenFileNameIPC::CopyFromOfn(LPOPENFILENAMEW aLpofn) {
+ mHwndOwner = nullptr;
+
+ // Filter is double-NULL terminated. mFilter should include the double-NULL.
+ mHasFilter = aLpofn->lpstrFilter != nullptr;
+ if (mHasFilter) {
+ uint32_t dNullIdx = 0;
+ while (aLpofn->lpstrFilter[dNullIdx] != L'\0' ||
+ aLpofn->lpstrFilter[dNullIdx + 1] != L'\0') {
+ dNullIdx++;
+ }
+ mFilter.assign(aLpofn->lpstrFilter, dNullIdx + 2);
+ }
+ mHasCustomFilter = aLpofn->lpstrCustomFilter != nullptr;
+ if (mHasCustomFilter) {
+ mCustomFilterIn = std::wstring(aLpofn->lpstrCustomFilter);
+ mNMaxCustFilterOut =
+ aLpofn->nMaxCustFilter - (wcslen(aLpofn->lpstrCustomFilter) + 1);
+ } else {
+ mNMaxCustFilterOut = 0;
+ }
+ mFilterIndex = aLpofn->nFilterIndex;
+ mFile = std::wstring(aLpofn->lpstrFile);
+ mNMaxFile = aLpofn->nMaxFile;
+ mNMaxFileTitle =
+ aLpofn->lpstrFileTitle != nullptr ? aLpofn->nMaxFileTitle : 0;
+ mHasInitialDir = aLpofn->lpstrInitialDir != nullptr;
+ if (mHasInitialDir) {
+ mInitialDir = std::wstring(aLpofn->lpstrInitialDir);
+ }
+ mHasTitle = aLpofn->lpstrTitle != nullptr;
+ if (mHasTitle) {
+ mTitle = std::wstring(aLpofn->lpstrTitle);
+ }
+ mHasDefExt = aLpofn->lpstrDefExt != nullptr;
+ if (mHasDefExt) {
+ mDefExt = std::wstring(aLpofn->lpstrDefExt);
+ }
+
+ mFlags = aLpofn->Flags;
+ // If the user sets OFN_ALLOWMULTISELECT then we require OFN_EXPLORER
+ // as well. Without OFN_EXPLORER, the method has ancient legacy
+ // behavior that we don't support.
+ MOZ_ASSERT((mFlags & OFN_EXPLORER) || !(mFlags & OFN_ALLOWMULTISELECT));
+
+ // We ignore any visual customization and callbacks that the user set.
+ mFlags &= ~(OFN_ENABLEHOOK | OFN_ENABLETEMPLATEHANDLE | OFN_ENABLETEMPLATE);
+
+ mFlagsEx = aLpofn->FlagsEx;
+}
+
+void OpenFileNameIPC::AddToOfn(LPOPENFILENAMEW aLpofn) const {
+ aLpofn->lStructSize = sizeof(OPENFILENAMEW);
+ aLpofn->hwndOwner = mHwndOwner;
+ if (mHasFilter) {
+ memcpy(const_cast<LPWSTR>(aLpofn->lpstrFilter), mFilter.data(),
+ mFilter.size() * sizeof(wchar_t));
+ }
+ if (mHasCustomFilter) {
+ aLpofn->nMaxCustFilter = mCustomFilterIn.size() + 1 + mNMaxCustFilterOut;
+ wcscpy(aLpofn->lpstrCustomFilter, mCustomFilterIn.c_str());
+ memset(aLpofn->lpstrCustomFilter + mCustomFilterIn.size() + 1, 0,
+ mNMaxCustFilterOut * sizeof(wchar_t));
+ } else {
+ aLpofn->nMaxCustFilter = 0;
+ }
+ aLpofn->nFilterIndex = mFilterIndex;
+ if (mNMaxFile > 0) {
+ wcsncpy(aLpofn->lpstrFile, mFile.c_str(),
+ std::min(static_cast<uint32_t>(mFile.size() + 1), mNMaxFile));
+ aLpofn->lpstrFile[mNMaxFile - 1] = L'\0';
+ }
+ aLpofn->nMaxFile = mNMaxFile;
+ aLpofn->nMaxFileTitle = mNMaxFileTitle;
+ if (mHasInitialDir) {
+ wcscpy(const_cast<LPWSTR>(aLpofn->lpstrInitialDir), mInitialDir.c_str());
+ }
+ if (mHasTitle) {
+ wcscpy(const_cast<LPWSTR>(aLpofn->lpstrTitle), mTitle.c_str());
+ }
+ aLpofn->Flags = mFlags; /* TODO: Consider adding OFN_NOCHANGEDIR */
+ if (mHasDefExt) {
+ wcscpy(const_cast<LPWSTR>(aLpofn->lpstrDefExt), mDefExt.c_str());
+ }
+ aLpofn->FlagsEx = mFlagsEx;
+}
+
+void OpenFileNameIPC::AllocateOfnStrings(LPOPENFILENAMEW aLpofn) const {
+ if (mHasFilter) {
+ // mFilter is double-NULL terminated and it includes the double-NULL in its
+ // length.
+ aLpofn->lpstrFilter =
+ static_cast<LPCTSTR>(moz_xmalloc(sizeof(wchar_t) * (mFilter.size())));
+ }
+ if (mHasCustomFilter) {
+ aLpofn->lpstrCustomFilter = static_cast<LPTSTR>(moz_xmalloc(
+ sizeof(wchar_t) * (mCustomFilterIn.size() + 1 + mNMaxCustFilterOut)));
+ }
+ aLpofn->lpstrFile =
+ static_cast<LPTSTR>(moz_xmalloc(sizeof(wchar_t) * mNMaxFile));
+ if (mNMaxFileTitle > 0) {
+ aLpofn->lpstrFileTitle =
+ static_cast<LPTSTR>(moz_xmalloc(sizeof(wchar_t) * mNMaxFileTitle));
+ }
+ if (mHasInitialDir) {
+ aLpofn->lpstrInitialDir = static_cast<LPCTSTR>(
+ moz_xmalloc(sizeof(wchar_t) * (mInitialDir.size() + 1)));
+ }
+ if (mHasTitle) {
+ aLpofn->lpstrTitle = static_cast<LPCTSTR>(
+ moz_xmalloc(sizeof(wchar_t) * (mTitle.size() + 1)));
+ }
+ if (mHasDefExt) {
+ aLpofn->lpstrDefExt = static_cast<LPCTSTR>(
+ moz_xmalloc(sizeof(wchar_t) * (mDefExt.size() + 1)));
+ }
+}
+
+// static
+void OpenFileNameIPC::FreeOfnStrings(LPOPENFILENAMEW aLpofn) {
+ if (aLpofn->lpstrFilter) {
+ free(const_cast<LPWSTR>(aLpofn->lpstrFilter));
+ }
+ if (aLpofn->lpstrCustomFilter) {
+ free(aLpofn->lpstrCustomFilter);
+ }
+ if (aLpofn->lpstrFile) {
+ free(aLpofn->lpstrFile);
+ }
+ if (aLpofn->lpstrFileTitle) {
+ free(aLpofn->lpstrFileTitle);
+ }
+ if (aLpofn->lpstrInitialDir) {
+ free(const_cast<LPWSTR>(aLpofn->lpstrInitialDir));
+ }
+ if (aLpofn->lpstrTitle) {
+ free(const_cast<LPWSTR>(aLpofn->lpstrTitle));
+ }
+ if (aLpofn->lpstrDefExt) {
+ free(const_cast<LPWSTR>(aLpofn->lpstrDefExt));
+ }
+}
+
+void OpenFileNameRetIPC::CopyFromOfn(LPOPENFILENAMEW aLpofn) {
+ if (aLpofn->lpstrCustomFilter != nullptr) {
+ mCustomFilterOut = std::wstring(aLpofn->lpstrCustomFilter +
+ wcslen(aLpofn->lpstrCustomFilter) + 1);
+ }
+ mFile.assign(aLpofn->lpstrFile, aLpofn->nMaxFile);
+ if (aLpofn->lpstrFileTitle != nullptr) {
+ mFileTitle.assign(aLpofn->lpstrFileTitle,
+ wcslen(aLpofn->lpstrFileTitle) + 1);
+ }
+ mFileOffset = aLpofn->nFileOffset;
+ mFileExtension = aLpofn->nFileExtension;
+}
+
+void OpenFileNameRetIPC::AddToOfn(LPOPENFILENAMEW aLpofn) const {
+ if (aLpofn->lpstrCustomFilter) {
+ LPWSTR secondString =
+ aLpofn->lpstrCustomFilter + wcslen(aLpofn->lpstrCustomFilter) + 1;
+ const wchar_t* customFilterOut = mCustomFilterOut.c_str();
+ MOZ_ASSERT(wcslen(aLpofn->lpstrCustomFilter) + 1 + wcslen(customFilterOut) +
+ 1 + 1 <=
+ aLpofn->nMaxCustFilter);
+ wcscpy(secondString, customFilterOut);
+ secondString[wcslen(customFilterOut) + 1] =
+ L'\0'; // terminated with two NULLs
+ }
+ MOZ_ASSERT(mFile.size() <= aLpofn->nMaxFile);
+ memcpy(aLpofn->lpstrFile, mFile.data(), mFile.size() * sizeof(wchar_t));
+ if (aLpofn->lpstrFileTitle != nullptr) {
+ MOZ_ASSERT(mFileTitle.size() + 1 < aLpofn->nMaxFileTitle);
+ wcscpy(aLpofn->lpstrFileTitle, mFileTitle.c_str());
+ }
+ aLpofn->nFileOffset = mFileOffset;
+ aLpofn->nFileExtension = mFileExtension;
+}
+
+void IPCSchannelCred::CopyFrom(const PSCHANNEL_CRED& aSCred) {
+ // We assert that the aSCred fields take supported values.
+ // If they do not then we ignore the values we were given.
+ MOZ_ASSERT(aSCred->dwVersion == SCHANNEL_CRED_VERSION);
+ MOZ_ASSERT(aSCred->cCreds == 0);
+ MOZ_ASSERT(aSCred->paCred == nullptr);
+ MOZ_ASSERT(aSCred->hRootStore == nullptr);
+ MOZ_ASSERT(aSCred->cMappers == 0);
+ MOZ_ASSERT(aSCred->aphMappers == nullptr);
+ MOZ_ASSERT(aSCred->cSupportedAlgs == 0);
+ MOZ_ASSERT(aSCred->palgSupportedAlgs == nullptr);
+ MOZ_ASSERT((aSCred->grbitEnabledProtocols & SCHANNEL_SUPPORTED_PROTOCOLS) ==
+ aSCred->grbitEnabledProtocols);
+ mEnabledProtocols =
+ aSCred->grbitEnabledProtocols & SCHANNEL_SUPPORTED_PROTOCOLS;
+ mMinStrength = aSCred->dwMinimumCipherStrength;
+ mMaxStrength = aSCred->dwMaximumCipherStrength;
+ MOZ_ASSERT(aSCred->dwSessionLifespan == 0);
+ MOZ_ASSERT((aSCred->dwFlags & SCHANNEL_SUPPORTED_FLAGS) == aSCred->dwFlags);
+ mFlags = aSCred->dwFlags & SCHANNEL_SUPPORTED_FLAGS;
+ MOZ_ASSERT(aSCred->dwCredFormat == 0);
+}
+
+void IPCSchannelCred::CopyTo(PSCHANNEL_CRED& aSCred) const {
+ // Validate values as they come from an untrusted process.
+ memset(aSCred, 0, sizeof(SCHANNEL_CRED));
+ aSCred->dwVersion = SCHANNEL_CRED_VERSION;
+ aSCred->grbitEnabledProtocols =
+ mEnabledProtocols & SCHANNEL_SUPPORTED_PROTOCOLS;
+ aSCred->dwMinimumCipherStrength = mMinStrength;
+ aSCred->dwMaximumCipherStrength = mMaxStrength;
+ aSCred->dwFlags = mFlags & SCHANNEL_SUPPORTED_FLAGS;
+}
+
+void IPCInternetBuffers::CopyFrom(const LPINTERNET_BUFFERSA& aBufs) {
+ mBuffers.Clear();
+
+ LPINTERNET_BUFFERSA inetBuf = aBufs;
+ while (inetBuf) {
+ MOZ_ASSERT(inetBuf->dwStructSize == sizeof(INTERNET_BUFFERSA));
+ Buffer* ipcBuf = mBuffers.AppendElement();
+
+ ipcBuf->mHeader.SetIsVoid(inetBuf->lpcszHeader == nullptr);
+ if (inetBuf->lpcszHeader) {
+ ipcBuf->mHeader.Assign(inetBuf->lpcszHeader, inetBuf->dwHeadersLength);
+ }
+ ipcBuf->mHeaderTotal = inetBuf->dwHeadersTotal;
+
+ ipcBuf->mBuffer.SetIsVoid(inetBuf->lpvBuffer == nullptr);
+ if (inetBuf->lpvBuffer) {
+ ipcBuf->mBuffer.Assign(static_cast<char*>(inetBuf->lpvBuffer),
+ inetBuf->dwBufferLength);
+ }
+ ipcBuf->mBufferTotal = inetBuf->dwBufferTotal;
+ inetBuf = inetBuf->Next;
+ }
+}
+
+void IPCInternetBuffers::CopyTo(LPINTERNET_BUFFERSA& aBufs) const {
+ MOZ_ASSERT(!aBufs);
+
+ LPINTERNET_BUFFERSA lastBuf = nullptr;
+ for (size_t idx = 0; idx < mBuffers.Length(); ++idx) {
+ const Buffer& ipcBuf = mBuffers[idx];
+ LPINTERNET_BUFFERSA newBuf = static_cast<LPINTERNET_BUFFERSA>(
+ moz_xcalloc(1, sizeof(INTERNET_BUFFERSA)));
+ if (idx == 0) {
+ aBufs = newBuf;
+ } else {
+ MOZ_ASSERT(lastBuf);
+ lastBuf->Next = newBuf;
+ lastBuf = newBuf;
+ }
+
+ newBuf->dwStructSize = sizeof(INTERNET_BUFFERSA);
+
+ newBuf->dwHeadersTotal = ipcBuf.mHeaderTotal;
+ if (!ipcBuf.mHeader.IsVoid()) {
+ newBuf->lpcszHeader =
+ static_cast<LPCSTR>(moz_xmalloc(ipcBuf.mHeader.Length()));
+ memcpy(const_cast<char*>(newBuf->lpcszHeader), ipcBuf.mHeader.Data(),
+ ipcBuf.mHeader.Length());
+ newBuf->dwHeadersLength = ipcBuf.mHeader.Length();
+ }
+
+ newBuf->dwBufferTotal = ipcBuf.mBufferTotal;
+ if (!ipcBuf.mBuffer.IsVoid()) {
+ newBuf->lpvBuffer = moz_xmalloc(ipcBuf.mBuffer.Length());
+ memcpy(newBuf->lpvBuffer, ipcBuf.mBuffer.Data(), ipcBuf.mBuffer.Length());
+ newBuf->dwBufferLength = ipcBuf.mBuffer.Length();
+ }
+ }
+}
+
+/* static */
+void IPCInternetBuffers::FreeBuffers(LPINTERNET_BUFFERSA& aBufs) {
+ if (!aBufs) {
+ return;
+ }
+ while (aBufs) {
+ LPINTERNET_BUFFERSA temp = aBufs->Next;
+ free(const_cast<char*>(aBufs->lpcszHeader));
+ free(aBufs->lpvBuffer);
+ free(aBufs);
+ aBufs = temp;
+ }
+}
+
+void IPCPrintDlg::CopyFrom(const LPPRINTDLGW& aDlg) {
+ // DLP: Trouble -- my prior impl "worked" but didn't return anything
+ // AFAIR. So... ??? But it printed a page!!! How?!
+ MOZ_ASSERT_UNREACHABLE("TODO: DLP:");
+}
+
+void IPCPrintDlg::CopyTo(LPPRINTDLGW& aDlg) const {
+ MOZ_ASSERT_UNREACHABLE("TODO: DLP:");
+}
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // defined(XP_WIN)
diff --git a/dom/plugins/ipc/FunctionBrokerIPCUtils.h b/dom/plugins/ipc/FunctionBrokerIPCUtils.h
new file mode 100644
index 0000000000..c4b72dbe95
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBrokerIPCUtils.h
@@ -0,0 +1,436 @@
+/* -*- 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_functionbrokeripcutils_h
+#define dom_plugins_ipc_functionbrokeripcutils_h 1
+
+#include "ipc/EnumSerializer.h"
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "PluginMessageUtils.h"
+
+#if defined(XP_WIN)
+
+# define SECURITY_WIN32
+# include <security.h>
+# include <wininet.h>
+# include <schannel.h>
+# include <commdlg.h>
+
+#endif // defined(XP_WIN)
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This enum represents all of the methods hooked by the main facility in
+ * BrokerClient. It is used to allow quick lookup in the sFunctionsToHook
+ * structure.
+ */
+enum FunctionHookId {
+#if defined(XP_WIN)
+ ID_GetWindowInfo = 0,
+ ID_GetKeyState,
+ ID_SetCursorPos,
+ ID_GetSaveFileNameW,
+ ID_GetOpenFileNameW,
+ ID_InternetOpenA,
+ ID_InternetConnectA,
+ ID_InternetCloseHandle,
+ ID_InternetQueryDataAvailable,
+ ID_InternetReadFile,
+ ID_InternetWriteFile,
+ ID_InternetSetOptionA,
+ ID_HttpAddRequestHeadersA,
+ ID_HttpOpenRequestA,
+ ID_HttpQueryInfoA,
+ ID_HttpSendRequestA,
+ ID_HttpSendRequestExA,
+ ID_HttpEndRequestA,
+ ID_InternetQueryOptionA,
+ ID_InternetErrorDlg,
+ ID_AcquireCredentialsHandleA,
+ ID_QueryCredentialsAttributesA,
+ ID_FreeCredentialsHandle,
+ ID_PrintDlgW,
+ ID_CreateMutexW
+# if defined(MOZ_SANDBOX)
+ ,
+ ID_GetFileAttributesW
+# endif // defined(MOZ_SANDBOX)
+ ,
+ ID_FunctionHookCount
+#else // defined(XP_WIN)
+ ID_FunctionHookCount
+#endif // defined(XP_WIN)
+};
+
+// Max number of bytes to show when logging a blob of raw memory
+static const uint32_t MAX_BLOB_CHARS_TO_LOG = 12;
+
+// Format strings for safe logging despite the fact that they are sometimes
+// used as raw binary blobs.
+inline nsCString FormatBlob(const nsACString& aParam) {
+ if (aParam.IsVoid() || aParam.IsEmpty()) {
+ return nsCString(aParam.IsVoid() ? "<void>" : "<empty>");
+ }
+
+ nsCString str;
+ uint32_t totalLen = std::min(MAX_BLOB_CHARS_TO_LOG, aParam.Length());
+ // If we are printing only a portion of the string then follow it with
+ // ellipsis
+ const char* maybeEllipsis =
+ (MAX_BLOB_CHARS_TO_LOG < aParam.Length()) ? "..." : "";
+ for (uint32_t idx = 0; idx < totalLen; ++idx) {
+ // Should be %02x but I've run into a AppendPrintf bug...
+ str.AppendPrintf("0x%2x ", aParam.Data()[idx] & 0xff);
+ }
+ str.AppendPrintf("%s | '", maybeEllipsis);
+ for (uint32_t idx = 0; idx < totalLen; ++idx) {
+ str.AppendPrintf("%c", (aParam.Data()[idx] > 0) ? aParam.Data()[idx] : '.');
+ }
+ str.AppendPrintf("'%s", maybeEllipsis);
+ return str;
+}
+
+#if defined(XP_WIN)
+
+// Values indicate GetOpenFileNameW and GetSaveFileNameW.
+enum GetFileNameFunc { OPEN_FUNC, SAVE_FUNC };
+
+typedef CopyableTArray<nsCString> StringArray;
+
+// IPC-capable version of the Windows OPENFILENAMEW struct.
+typedef struct _OpenFileNameIPC {
+ // Allocates memory for the strings in this object. This should usually
+ // be used with a zeroed out OPENFILENAMEW structure.
+ void AllocateOfnStrings(LPOPENFILENAMEW aLpofn) const;
+ static void FreeOfnStrings(LPOPENFILENAMEW aLpofn);
+ void AddToOfn(LPOPENFILENAMEW aLpofn) const;
+ void CopyFromOfn(LPOPENFILENAMEW aLpofn);
+ bool operator==(const _OpenFileNameIPC& o) const {
+ return (o.mHwndOwner == mHwndOwner) && (o.mFilter == mFilter) &&
+ (o.mHasFilter == mHasFilter) &&
+ (o.mCustomFilterIn == mCustomFilterIn) &&
+ (o.mHasCustomFilter == mHasCustomFilter) &&
+ (o.mNMaxCustFilterOut == mNMaxCustFilterOut) &&
+ (o.mFilterIndex == mFilterIndex) && (o.mFile == mFile) &&
+ (o.mNMaxFile == mNMaxFile) && (o.mNMaxFileTitle == mNMaxFileTitle) &&
+ (o.mInitialDir == mInitialDir) &&
+ (o.mHasInitialDir == mHasInitialDir) && (o.mTitle == mTitle) &&
+ (o.mHasTitle == mHasTitle) && (o.mFlags == mFlags) &&
+ (o.mDefExt == mDefExt) && (o.mHasDefExt == mHasDefExt) &&
+ (o.mFlagsEx == mFlagsEx);
+ }
+
+ NativeWindowHandle mHwndOwner;
+ std::wstring
+ mFilter; // Double-NULL terminated (i.e. L"\0\0") if mHasFilter is true
+ bool mHasFilter;
+ std::wstring mCustomFilterIn;
+ bool mHasCustomFilter;
+ uint32_t mNMaxCustFilterOut;
+ uint32_t mFilterIndex;
+ std::wstring mFile;
+ uint32_t mNMaxFile;
+ uint32_t mNMaxFileTitle;
+ std::wstring mInitialDir;
+ bool mHasInitialDir;
+ std::wstring mTitle;
+ bool mHasTitle;
+ uint32_t mFlags;
+ std::wstring mDefExt;
+ bool mHasDefExt;
+ uint32_t mFlagsEx;
+} OpenFileNameIPC;
+
+// GetOpenFileNameW and GetSaveFileNameW overwrite fields of their OPENFILENAMEW
+// parameter. This represents those values so that they can be returned via
+// IPC.
+typedef struct _OpenFileNameRetIPC {
+ void CopyFromOfn(LPOPENFILENAMEW aLpofn);
+ void AddToOfn(LPOPENFILENAMEW aLpofn) const;
+ bool operator==(const _OpenFileNameRetIPC& o) const {
+ return (o.mCustomFilterOut == mCustomFilterOut) && (o.mFile == mFile) &&
+ (o.mFileTitle == mFileTitle) && (o.mFileOffset == mFileOffset) &&
+ (o.mFileExtension == mFileExtension);
+ }
+
+ std::wstring mCustomFilterOut;
+ std::wstring mFile; // Double-NULL terminated (i.e. L"\0\0")
+ std::wstring mFileTitle;
+ uint16_t mFileOffset;
+ uint16_t mFileExtension;
+} OpenFileNameRetIPC;
+
+typedef struct _IPCSchannelCred {
+ void CopyFrom(const PSCHANNEL_CRED& aSCred);
+ void CopyTo(PSCHANNEL_CRED& aSCred) const;
+ bool operator==(const _IPCSchannelCred& o) const {
+ return (o.mEnabledProtocols == mEnabledProtocols) &&
+ (o.mMinStrength == mMinStrength) &&
+ (o.mMaxStrength == mMaxStrength) && (o.mFlags == mFlags);
+ }
+
+ DWORD mEnabledProtocols;
+ DWORD mMinStrength;
+ DWORD mMaxStrength;
+ DWORD mFlags;
+} IPCSchannelCred;
+
+typedef struct _IPCInternetBuffers {
+ void CopyFrom(const LPINTERNET_BUFFERSA& aBufs);
+ void CopyTo(LPINTERNET_BUFFERSA& aBufs) const;
+ bool operator==(const _IPCInternetBuffers& o) const {
+ return o.mBuffers == mBuffers;
+ }
+ static void FreeBuffers(LPINTERNET_BUFFERSA& aBufs);
+
+ struct Buffer {
+ nsCString mHeader;
+ uint32_t mHeaderTotal;
+ nsCString mBuffer;
+ uint32_t mBufferTotal;
+ bool operator==(const Buffer& o) const {
+ return (o.mHeader == mHeader) && (o.mHeaderTotal == mHeaderTotal) &&
+ (o.mBuffer == mBuffer) && (o.mBufferTotal == mBufferTotal);
+ }
+ };
+ CopyableTArray<Buffer> mBuffers;
+} IPCInternetBuffers;
+
+typedef struct _IPCPrintDlg {
+ void CopyFrom(const LPPRINTDLGW& aDlg);
+ void CopyTo(LPPRINTDLGW& aDlg) const;
+ bool operator==(const _IPCPrintDlg& o) const {
+ MOZ_ASSERT_UNREACHABLE("DLP: TODO:");
+ return false;
+ }
+} IPCPrintDlg;
+
+#endif // defined(XP_WIN)
+
+} // namespace plugins
+} // namespace mozilla
+
+namespace IPC {
+
+using mozilla::plugins::FunctionHookId;
+
+#if defined(XP_WIN)
+
+using mozilla::plugins::IPCInternetBuffers;
+using mozilla::plugins::IPCPrintDlg;
+using mozilla::plugins::IPCSchannelCred;
+using mozilla::plugins::NativeWindowHandle;
+using mozilla::plugins::OpenFileNameIPC;
+using mozilla::plugins::OpenFileNameRetIPC;
+using mozilla::plugins::StringArray;
+
+template <>
+struct ParamTraits<OpenFileNameIPC> {
+ typedef OpenFileNameIPC paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mHwndOwner);
+ WriteParam(aMsg, aParam.mFilter);
+ WriteParam(aMsg, aParam.mHasFilter);
+ WriteParam(aMsg, aParam.mCustomFilterIn);
+ WriteParam(aMsg, aParam.mHasCustomFilter);
+ WriteParam(aMsg, aParam.mNMaxCustFilterOut);
+ WriteParam(aMsg, aParam.mFilterIndex);
+ WriteParam(aMsg, aParam.mFile);
+ WriteParam(aMsg, aParam.mNMaxFile);
+ WriteParam(aMsg, aParam.mNMaxFileTitle);
+ WriteParam(aMsg, aParam.mInitialDir);
+ WriteParam(aMsg, aParam.mHasInitialDir);
+ WriteParam(aMsg, aParam.mTitle);
+ WriteParam(aMsg, aParam.mHasTitle);
+ WriteParam(aMsg, aParam.mFlags);
+ WriteParam(aMsg, aParam.mDefExt);
+ WriteParam(aMsg, aParam.mHasDefExt);
+ WriteParam(aMsg, aParam.mFlagsEx);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ if (ReadParam(aMsg, aIter, &aResult->mHwndOwner) &&
+ ReadParam(aMsg, aIter, &aResult->mFilter) &&
+ ReadParam(aMsg, aIter, &aResult->mHasFilter) &&
+ ReadParam(aMsg, aIter, &aResult->mCustomFilterIn) &&
+ ReadParam(aMsg, aIter, &aResult->mHasCustomFilter) &&
+ ReadParam(aMsg, aIter, &aResult->mNMaxCustFilterOut) &&
+ ReadParam(aMsg, aIter, &aResult->mFilterIndex) &&
+ ReadParam(aMsg, aIter, &aResult->mFile) &&
+ ReadParam(aMsg, aIter, &aResult->mNMaxFile) &&
+ ReadParam(aMsg, aIter, &aResult->mNMaxFileTitle) &&
+ ReadParam(aMsg, aIter, &aResult->mInitialDir) &&
+ ReadParam(aMsg, aIter, &aResult->mHasInitialDir) &&
+ ReadParam(aMsg, aIter, &aResult->mTitle) &&
+ ReadParam(aMsg, aIter, &aResult->mHasTitle) &&
+ ReadParam(aMsg, aIter, &aResult->mFlags) &&
+ ReadParam(aMsg, aIter, &aResult->mDefExt) &&
+ ReadParam(aMsg, aIter, &aResult->mHasDefExt) &&
+ ReadParam(aMsg, aIter, &aResult->mFlagsEx)) {
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ aLog->append(StringPrintf(L"[%ls, %ls, %ls, %ls]", aParam.mFilter.c_str(),
+ aParam.mCustomFilterIn.c_str(),
+ aParam.mFile.c_str(), aParam.mTitle.c_str()));
+ }
+};
+
+template <>
+struct ParamTraits<OpenFileNameRetIPC> {
+ typedef OpenFileNameRetIPC paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mCustomFilterOut);
+ WriteParam(aMsg, aParam.mFile);
+ WriteParam(aMsg, aParam.mFileTitle);
+ WriteParam(aMsg, aParam.mFileOffset);
+ WriteParam(aMsg, aParam.mFileExtension);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ if (ReadParam(aMsg, aIter, &aResult->mCustomFilterOut) &&
+ ReadParam(aMsg, aIter, &aResult->mFile) &&
+ ReadParam(aMsg, aIter, &aResult->mFileTitle) &&
+ ReadParam(aMsg, aIter, &aResult->mFileOffset) &&
+ ReadParam(aMsg, aIter, &aResult->mFileExtension)) {
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ aLog->append(StringPrintf(L"[%ls, %ls, %ls, %d, %d]",
+ aParam.mCustomFilterOut.c_str(),
+ aParam.mFile.c_str(), aParam.mFileTitle.c_str(),
+ aParam.mFileOffset, aParam.mFileExtension));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::plugins::GetFileNameFunc>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::plugins::GetFileNameFunc, mozilla::plugins::OPEN_FUNC,
+ mozilla::plugins::SAVE_FUNC> {};
+
+template <>
+struct ParamTraits<IPCSchannelCred> {
+ typedef mozilla::plugins::IPCSchannelCred paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, static_cast<uint32_t>(aParam.mEnabledProtocols));
+ WriteParam(aMsg, static_cast<uint32_t>(aParam.mMinStrength));
+ WriteParam(aMsg, static_cast<uint32_t>(aParam.mMaxStrength));
+ WriteParam(aMsg, static_cast<uint32_t>(aParam.mFlags));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ uint32_t proto, minStr, maxStr, flags;
+ if (!ReadParam(aMsg, aIter, &proto) || !ReadParam(aMsg, aIter, &minStr) ||
+ !ReadParam(aMsg, aIter, &maxStr) || !ReadParam(aMsg, aIter, &flags)) {
+ return false;
+ }
+ aResult->mEnabledProtocols = proto;
+ aResult->mMinStrength = minStr;
+ aResult->mMaxStrength = maxStr;
+ aResult->mFlags = flags;
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ aLog->append(StringPrintf(L"[%d,%d,%d,%d]", aParam.mEnabledProtocols,
+ aParam.mMinStrength, aParam.mMaxStrength,
+ aParam.mFlags));
+ }
+};
+
+template <>
+struct ParamTraits<IPCInternetBuffers::Buffer> {
+ typedef mozilla::plugins::IPCInternetBuffers::Buffer paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mHeader);
+ WriteParam(aMsg, aParam.mHeaderTotal);
+ WriteParam(aMsg, aParam.mBuffer);
+ WriteParam(aMsg, aParam.mBufferTotal);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mHeader) &&
+ ReadParam(aMsg, aIter, &aResult->mHeaderTotal) &&
+ ReadParam(aMsg, aIter, &aResult->mBuffer) &&
+ ReadParam(aMsg, aIter, &aResult->mBufferTotal);
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ nsCString head = mozilla::plugins::FormatBlob(aParam.mHeader);
+ nsCString buffer = mozilla::plugins::FormatBlob(aParam.mBuffer);
+ std::string msg =
+ StringPrintf("[%s, %d, %s, %d]", head.Data(), aParam.mHeaderTotal,
+ buffer.Data(), aParam.mBufferTotal);
+ aLog->append(msg.begin(), msg.end());
+ }
+};
+
+template <>
+struct ParamTraits<IPCInternetBuffers> {
+ typedef mozilla::plugins::IPCInternetBuffers paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mBuffers);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mBuffers);
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ ParamTraits<nsTArray<IPCInternetBuffers::Buffer>>::Log(aParam.mBuffers,
+ aLog);
+ }
+};
+
+template <>
+struct ParamTraits<IPCPrintDlg> {
+ typedef mozilla::plugins::IPCPrintDlg paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ MOZ_ASSERT_UNREACHABLE("TODO: DLP:");
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ MOZ_ASSERT_UNREACHABLE("TODO: DLP:");
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ MOZ_ASSERT_UNREACHABLE("TODO: DLP:");
+ }
+};
+
+#endif // defined(XP_WIN)
+
+template <>
+struct ParamTraits<FunctionHookId>
+ : public ContiguousEnumSerializer<FunctionHookId,
+ static_cast<FunctionHookId>(0),
+ FunctionHookId::ID_FunctionHookCount> {};
+
+} // namespace IPC
+
+#endif /* dom_plugins_ipc_functionbrokeripcutils_h */
diff --git a/dom/plugins/ipc/FunctionBrokerParent.cpp b/dom/plugins/ipc/FunctionBrokerParent.cpp
new file mode 100644
index 0000000000..51e3b62899
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBrokerParent.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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 "FunctionBrokerParent.h"
+#include "FunctionBroker.h"
+#include "FunctionBrokerThread.h"
+
+#include "mozilla/ipc/Endpoint.h"
+
+namespace mozilla::plugins {
+
+#if defined(XP_WIN)
+UlongPairToIdMap sPairToIdMap;
+IdToUlongPairMap sIdToPairMap;
+PtrToIdMap sPtrToIdMap;
+IdToPtrMap sIdToPtrMap;
+#endif // defined(XP_WIN)
+
+/* static */
+FunctionBrokerParent* FunctionBrokerParent::Create(
+ Endpoint<PFunctionBrokerParent>&& aParentEnd) {
+ FunctionBrokerThread* thread = FunctionBrokerThread::Create();
+ if (!thread) {
+ return nullptr;
+ }
+
+ // We get the FunctionHooks so that they are created here, not on the
+ // message thread.
+ FunctionHook::GetHooks();
+
+ return new FunctionBrokerParent(thread, std::move(aParentEnd));
+}
+
+FunctionBrokerParent::FunctionBrokerParent(
+ FunctionBrokerThread* aThread, Endpoint<PFunctionBrokerParent>&& aParentEnd)
+ : mThread(aThread),
+ mMonitor("FunctionBrokerParent Lock"),
+ mShutdownDone(false) {
+ MOZ_ASSERT(mThread);
+ mThread->Dispatch(
+ NewNonOwningRunnableMethod<Endpoint<PFunctionBrokerParent>&&>(
+ "FunctionBrokerParent::Bind", this, &FunctionBrokerParent::Bind,
+ std::move(aParentEnd)));
+}
+
+FunctionBrokerParent::~FunctionBrokerParent() {
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ // Clean up any file permissions that we granted to the child process.
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ RemovePermissionsForProcess(OtherPid());
+#endif
+}
+
+void FunctionBrokerParent::Bind(Endpoint<PFunctionBrokerParent>&& aEnd) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+ DebugOnly<bool> ok = aEnd.Bind(this);
+ MOZ_ASSERT(ok);
+}
+
+void FunctionBrokerParent::ShutdownOnBrokerThread() {
+ MOZ_ASSERT(mThread->IsOnThread());
+ Close();
+
+ // Notify waiting thread that we are done.
+ MonitorAutoLock lock(mMonitor);
+ mShutdownDone = true;
+ mMonitor.Notify();
+}
+
+void FunctionBrokerParent::Destroy(FunctionBrokerParent* aInst) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aInst);
+
+ {
+ // Hold the lock while we destroy the actor on the broker thread.
+ MonitorAutoLock lock(aInst->mMonitor);
+ aInst->mThread->Dispatch(NewNonOwningRunnableMethod(
+ "FunctionBrokerParent::ShutdownOnBrokerThread", aInst,
+ &FunctionBrokerParent::ShutdownOnBrokerThread));
+
+ // Wait for broker thread to complete destruction.
+ while (!aInst->mShutdownDone) {
+ aInst->mMonitor.Wait();
+ }
+ }
+
+ delete aInst;
+}
+
+void FunctionBrokerParent::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+}
+
+mozilla::ipc::IPCResult FunctionBrokerParent::RecvBrokerFunction(
+ const FunctionHookId& aFunctionId, const IpdlTuple& aInTuple,
+ IpdlTuple* aOutTuple) {
+#if defined(XP_WIN)
+ MOZ_ASSERT(mThread->IsOnThread());
+ if (RunBrokeredFunction(OtherPid(), aFunctionId, aInTuple, aOutTuple)) {
+ return IPC_OK();
+ }
+ return IPC_FAIL_NO_REASON(this);
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "BrokerFunction is currently only implemented on Windows.");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+// static
+bool FunctionBrokerParent::RunBrokeredFunction(
+ base::ProcessId aClientId, const FunctionHookId& aFunctionId,
+ const IPC::IpdlTuple& aInTuple, IPC::IpdlTuple* aOutTuple) {
+ if ((size_t)aFunctionId >= FunctionHook::GetHooks()->Length()) {
+ MOZ_ASSERT_UNREACHABLE("Invalid function ID");
+ return false;
+ }
+
+ FunctionHook* hook = FunctionHook::GetHooks()->ElementAt(aFunctionId);
+ MOZ_ASSERT(hook->FunctionId() == aFunctionId);
+ return hook->RunOriginalFunction(aClientId, aInTuple, aOutTuple);
+}
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+
+mozilla::SandboxPermissions FunctionBrokerParent::sSandboxPermissions;
+
+// static
+void FunctionBrokerParent::RemovePermissionsForProcess(
+ base::ProcessId aClientId) {
+ sSandboxPermissions.RemovePermissionsForProcess(aClientId);
+}
+
+#endif // defined(XP_WIN) && defined(MOZ_SANDBOX)
+
+} // namespace mozilla::plugins
diff --git a/dom/plugins/ipc/FunctionBrokerParent.h b/dom/plugins/ipc/FunctionBrokerParent.h
new file mode 100644
index 0000000000..5ad26678b8
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBrokerParent.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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_functionbrokerparent_h
+#define mozilla_plugins_functionbrokerparent_h
+
+#include "mozilla/plugins/PFunctionBrokerParent.h"
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+# include "sandboxPermissions.h"
+#endif
+
+namespace mozilla {
+namespace plugins {
+
+class FunctionBrokerThread;
+
+/**
+ * Top-level actor run on the process to which we broker calls from sandboxed
+ * plugin processes.
+ */
+class FunctionBrokerParent : public PFunctionBrokerParent {
+ public:
+ static FunctionBrokerParent* Create(
+ Endpoint<PFunctionBrokerParent>&& aParentEnd);
+ static void Destroy(FunctionBrokerParent* aInst);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ mozilla::ipc::IPCResult RecvBrokerFunction(const FunctionHookId& aFunctionId,
+ const IpdlTuple& aInTuple,
+ IpdlTuple* aOutTuple) override;
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ static mozilla::SandboxPermissions* GetSandboxPermissions() {
+ return &sSandboxPermissions;
+ }
+#endif // defined(XP_WIN) && defined(MOZ_SANDBOX)
+
+ private:
+ explicit FunctionBrokerParent(FunctionBrokerThread* aThread,
+ Endpoint<PFunctionBrokerParent>&& aParentEnd);
+ ~FunctionBrokerParent();
+ void ShutdownOnBrokerThread();
+ void Bind(Endpoint<PFunctionBrokerParent>&& aEnd);
+
+ static bool RunBrokeredFunction(base::ProcessId aClientId,
+ const FunctionHookId& aFunctionId,
+ const IPC::IpdlTuple& aInTuple,
+ IPC::IpdlTuple* aOutTuple);
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ static void RemovePermissionsForProcess(base::ProcessId aClientId);
+ static mozilla::SandboxPermissions sSandboxPermissions;
+#endif // defined(XP_WIN) && defined(MOZ_SANDBOX)
+
+ UniquePtr<FunctionBrokerThread> mThread;
+ Monitor mMonitor;
+ bool mShutdownDone;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_functionbrokerparent_hk
diff --git a/dom/plugins/ipc/FunctionBrokerThread.h b/dom/plugins/ipc/FunctionBrokerThread.h
new file mode 100644
index 0000000000..f5fe66e9ef
--- /dev/null
+++ b/dom/plugins/ipc/FunctionBrokerThread.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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_functionbrokerthread_h
+#define mozilla_plugins_functionbrokerthread_h
+
+#include "nsThreadManager.h"
+
+namespace mozilla {
+namespace plugins {
+
+class FunctionBrokerThread {
+ public:
+ void Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) {
+ mThread->Dispatch(std::move(aRunnable), nsIEventTarget::NS_DISPATCH_NORMAL);
+ }
+
+ bool IsOnThread() {
+ bool on;
+ return NS_SUCCEEDED(mThread->IsOnCurrentThread(&on)) && on;
+ }
+
+ static FunctionBrokerThread* Create() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIThread> thread;
+ if (NS_FAILED(
+ NS_NewNamedThread("Function Broker", getter_AddRefs(thread)))) {
+ return nullptr;
+ }
+ return new FunctionBrokerThread(thread);
+ }
+
+ ~FunctionBrokerThread() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ mThread->Shutdown();
+ }
+
+ private:
+ explicit FunctionBrokerThread(nsIThread* aThread) : mThread(aThread) {
+ MOZ_ASSERT(mThread);
+ }
+
+ nsCOMPtr<nsIThread> mThread;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_functionbrokerthread_h
diff --git a/dom/plugins/ipc/FunctionHook.cpp b/dom/plugins/ipc/FunctionHook.cpp
new file mode 100644
index 0000000000..b9a0ed9e3e
--- /dev/null
+++ b/dom/plugins/ipc/FunctionHook.cpp
@@ -0,0 +1,359 @@
+/* -*- 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 "mozilla/TextUtils.h"
+
+#include "FunctionHook.h"
+#include "FunctionBroker.h"
+#include "nsClassHashtable.h"
+#include "mozilla/ClearOnShutdown.h"
+
+#if defined(XP_WIN)
+# include <shlobj.h>
+# include "PluginModuleChild.h"
+#endif
+
+namespace mozilla::plugins {
+
+StaticAutoPtr<FunctionHookArray> FunctionHook::sFunctionHooks;
+
+bool AlwaysHook(int) { return true; }
+
+FunctionHookArray* FunctionHook::GetHooks() {
+ if (sFunctionHooks) {
+ return sFunctionHooks;
+ }
+
+ // sFunctionHooks is the StaticAutoPtr to the singleton array of FunctionHook
+ // objects. We free it by clearing the StaticAutoPtr on shutdown.
+ sFunctionHooks = new FunctionHookArray();
+ ClearOnShutdown(&sFunctionHooks);
+ sFunctionHooks->SetLength(ID_FunctionHookCount);
+
+ AddFunctionHooks(*sFunctionHooks);
+ AddBrokeredFunctionHooks(*sFunctionHooks);
+ return sFunctionHooks;
+}
+
+void FunctionHook::HookFunctions(int aQuirks) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Plugin);
+ FunctionHookArray* hooks = FunctionHook::GetHooks();
+ MOZ_ASSERT(hooks);
+ for (size_t i = 0; i < hooks->Length(); ++i) {
+ FunctionHook* mhb = hooks->ElementAt(i);
+ // Check that the FunctionHook array is in the same order as the
+ // FunctionHookId enum.
+ MOZ_ASSERT((size_t)mhb->FunctionId() == i);
+ mhb->Register(aQuirks);
+ }
+}
+
+#if defined(XP_WIN)
+
+// This cache is created when a DLL is registered with a FunctionHook.
+// It is cleared on a call to ClearDllInterceptorCache(). It
+// must be freed before exit to avoid leaks.
+typedef nsClassHashtable<nsStringHashKey, WindowsDllInterceptor>
+ DllInterceptors;
+DllInterceptors* sDllInterceptorCache = nullptr;
+
+WindowsDllInterceptor* FunctionHook::GetDllInterceptorFor(
+ const char* aModuleName) {
+ if (!sDllInterceptorCache) {
+ sDllInterceptorCache = new DllInterceptors();
+ }
+
+ MOZ_ASSERT(IsAsciiNullTerminated(aModuleName),
+ "Non-ASCII module names are not supported");
+ NS_ConvertASCIItoUTF16 moduleName(aModuleName);
+
+ WindowsDllInterceptor* ret = sDllInterceptorCache->LookupOrAdd(moduleName);
+ MOZ_ASSERT(ret);
+ ret->Init(moduleName.get());
+ return ret;
+}
+
+void FunctionHook::ClearDllInterceptorCache() {
+ delete sDllInterceptorCache;
+ sDllInterceptorCache = nullptr;
+}
+
+/* GetWindowInfo */
+
+typedef BasicFunctionHook<ID_GetWindowInfo, decltype(GetWindowInfo)>
+ GetWindowInfoFH;
+
+template <>
+ShouldHookFunc* const GetWindowInfoFH::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_GETWINDOWINFO>;
+
+static const wchar_t* kMozillaWindowClass = L"MozillaWindowClass";
+static HWND sBrowserHwnd = nullptr;
+
+INTERCEPTOR_DISABLE_CFGUARD BOOL WINAPI GetWindowInfoHook(HWND hWnd,
+ PWINDOWINFO pwi) {
+ if (!pwi) {
+ return FALSE;
+ }
+
+ MOZ_ASSERT(ID_GetWindowInfo < FunctionHook::GetHooks()->Length());
+ GetWindowInfoFH* functionHook = static_cast<GetWindowInfoFH*>(
+ FunctionHook::GetHooks()->ElementAt(ID_GetWindowInfo));
+ if (!functionHook->OriginalFunction()) {
+ NS_ASSERTION(FALSE, "Something is horribly wrong in PHGetWindowInfoHook!");
+ return FALSE;
+ }
+
+ if (!sBrowserHwnd) {
+ wchar_t szClass[20];
+ // GetClassNameW returns the length it copied w/o null terminator.
+ // Therefore, if the name and null-terminator fit then it returns a
+ // value less than the buffer's length.
+ int nameLen = GetClassNameW(hWnd, szClass, ArrayLength(szClass));
+ if ((nameLen < (int)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?
+ typedef BOOL(WINAPI * GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
+ GetWindowInfoPtr gwiFunc =
+ static_cast<GetWindowInfoPtr>(functionHook->OriginalFunction());
+ BOOL result = gwiFunc(hWnd, pwi);
+ if (sBrowserHwnd && sBrowserHwnd == hWnd) {
+ pwi->rcWindow = pwi->rcClient;
+ }
+ return result;
+}
+
+/* PrintDlgW */
+
+typedef BasicFunctionHook<ID_PrintDlgW, decltype(PrintDlgW)> PrintDlgWFH;
+
+template <>
+ShouldHookFunc* const PrintDlgWFH::mShouldHook =
+ &CheckQuirks<QUIRK_FLASH_HOOK_PRINTDLGW>;
+
+INTERCEPTOR_DISABLE_CFGUARD BOOL WINAPI PrintDlgWHook(LPPRINTDLGW aDlg) {
+ // Zero out the HWND supplied by the plugin. We are sacrificing window
+ // parentage for the ability to run in the NPAPI sandbox.
+ HWND hwnd = aDlg->hwndOwner;
+ aDlg->hwndOwner = 0;
+ MOZ_ASSERT(ID_PrintDlgW < FunctionHook::GetHooks()->Length());
+ PrintDlgWFH* functionHook = static_cast<PrintDlgWFH*>(
+ FunctionHook::GetHooks()->ElementAt(ID_PrintDlgW));
+ MOZ_ASSERT(functionHook);
+ BOOL ret = functionHook->OriginalFunction()(aDlg);
+ aDlg->hwndOwner = hwnd;
+ return ret;
+}
+
+// Hooking CreateFileW for protected-mode magic
+static WindowsDllInterceptor sKernel32Intercept;
+typedef HANDLE(WINAPI* CreateFileWPtr)(LPCWSTR aFname, DWORD aAccess,
+ DWORD aShare,
+ LPSECURITY_ATTRIBUTES aSecurity,
+ DWORD aCreation, DWORD aFlags,
+ HANDLE aFTemplate);
+static WindowsDllInterceptor::FuncHookType<CreateFileWPtr> sCreateFileWStub;
+typedef HANDLE(WINAPI* CreateFileAPtr)(LPCSTR aFname, DWORD aAccess,
+ DWORD aShare,
+ LPSECURITY_ATTRIBUTES aSecurity,
+ DWORD aCreation, DWORD aFlags,
+ HANDLE aFTemplate);
+static WindowsDllInterceptor::FuncHookType<CreateFileAPtr> sCreateFileAStub;
+
+// 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 aFname, DWORD aAccess,
+ DWORD aShare,
+ LPSECURITY_ATTRIBUTES aSecurity,
+ DWORD aCreation, DWORD aFlags,
+ HANDLE aFTemplate) {
+ while (true) { // goto out
+ // Our hook is for mms.cfg into \Windows\System32\Macromed\Flash
+ // We don't require supporting too long path.
+ WCHAR unicodeName[MAX_PATH];
+ size_t len = strlen(aFname);
+
+ if (len >= MAX_PATH) {
+ break;
+ }
+
+ // We call to CreateFileW for workaround of Windows 8 RTM
+ int newLen = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, aFname, len,
+ unicodeName, MAX_PATH);
+ if (newLen == 0 || newLen >= MAX_PATH) {
+ break;
+ }
+ unicodeName[newLen] = '\0';
+
+ return CreateFileW(unicodeName, aAccess, aShare, aSecurity, aCreation,
+ aFlags, aFTemplate);
+ }
+
+ return sCreateFileAStub(aFname, aAccess, aShare, aSecurity, aCreation, aFlags,
+ aFTemplate);
+}
+
+static bool GetLocalLowTempPath(size_t aLen, LPWSTR aPath) {
+ constexpr auto tempname = u"\\Temp"_ns;
+ LPWSTR path;
+ if (SUCCEEDED(
+ 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 aFname, DWORD aAccess, DWORD aShare,
+ LPSECURITY_ATTRIBUTES aSecurity,
+ DWORD aCreation, DWORD aFlags,
+ HANDLE aFTemplate) {
+ 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(aFname);
+ if (len < kConfigLength) {
+ break;
+ }
+ if (wcscmp(aFname + 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, aShare, aSecurity,
+ TRUNCATE_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
+ nullptr);
+ if (replacement == INVALID_HANDLE_VALUE) {
+ break;
+ }
+
+ HANDLE original = sCreateFileWStub(aFname, aAccess, aShare, aSecurity,
+ aCreation, aFlags, aFTemplate);
+ 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(aFname, aAccess, aShare, aSecurity, aCreation, aFlags,
+ aFTemplate);
+}
+
+void FunctionHook::HookProtectedMode() {
+ // Legacy code. Uses the nsWindowsDLLInterceptor directly instead of
+ // using the FunctionHook
+ sKernel32Intercept.Init("kernel32.dll");
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Plugin);
+ sCreateFileWStub.Set(sKernel32Intercept, "CreateFileW", &CreateFileWHookFn);
+ sCreateFileAStub.Set(sKernel32Intercept, "CreateFileA", &CreateFileAHookFn);
+}
+
+# if defined(MOZ_SANDBOX)
+
+/* GetFileAttributesW */
+
+typedef BasicFunctionHook<ID_GetFileAttributesW, decltype(GetFileAttributesW)>
+ GetFileAttributesWFH;
+
+INTERCEPTOR_DISABLE_CFGUARD DWORD WINAPI
+GetFileAttributesWHook(LPCWSTR aFilename) {
+ MOZ_ASSERT(ID_GetFileAttributesW < FunctionHook::GetHooks()->Length());
+ GetFileAttributesWFH* functionHook = static_cast<GetFileAttributesWFH*>(
+ FunctionHook::GetHooks()->ElementAt(ID_GetFileAttributesW));
+ if (!functionHook->OriginalFunction()) {
+ NS_ASSERTION(FALSE,
+ "Something is horribly wrong in GetFileAttributesWHook!");
+ return FALSE;
+ }
+
+ DWORD ret = functionHook->OriginalFunction()(aFilename);
+ if (ret != INVALID_FILE_ATTRIBUTES) {
+ return ret;
+ }
+
+ // If aFilename is a parent of PluginModuleChild::GetFlashRoamingPath then
+ // assume it was blocked by the sandbox and just report it as a plain
+ // directory.
+ size_t len = wcslen(aFilename);
+ std::wstring roamingPath = PluginModuleChild::GetFlashRoamingPath();
+ bool isParent = (len > 0) && (aFilename[len - 1] == L'\\') &&
+ (_wcsnicmp(aFilename, roamingPath.c_str(), len) == 0);
+ if (!isParent) {
+ return ret;
+ }
+ return FILE_ATTRIBUTE_DIRECTORY;
+}
+
+# endif // defined(MOZ_SANDBOX)
+
+#endif // defined(XP_WIN)
+
+#define FUN_HOOK(x) static_cast<FunctionHook*>(x)
+
+void FunctionHook::AddFunctionHooks(FunctionHookArray& aHooks) {
+ // We transfer ownership of the FunctionHook objects to the array.
+#if defined(XP_WIN)
+ aHooks[ID_GetWindowInfo] = FUN_HOOK(new GetWindowInfoFH(
+ "user32.dll", "GetWindowInfo", &GetWindowInfo, &GetWindowInfoHook));
+ aHooks[ID_PrintDlgW] = FUN_HOOK(
+ new PrintDlgWFH("comdlg32.dll", "PrintDlgW", &PrintDlgW, PrintDlgWHook));
+# if defined(MOZ_SANDBOX)
+ aHooks[ID_GetFileAttributesW] = FUN_HOOK(
+ new GetFileAttributesWFH("kernel32.dll", "GetFileAttributesW",
+ &GetFileAttributesW, &GetFileAttributesWHook));
+# endif // defined(MOZ_SANDBOX)
+#endif // defined(XP_WIN)
+}
+
+#undef FUN_HOOK
+
+} // namespace mozilla::plugins
diff --git a/dom/plugins/ipc/FunctionHook.h b/dom/plugins/ipc/FunctionHook.h
new file mode 100644
index 0000000000..ac259d6bd3
--- /dev/null
+++ b/dom/plugins/ipc/FunctionHook.h
@@ -0,0 +1,206 @@
+/* -*- 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_functionhook_h
+#define dom_plugins_ipc_functionhook_h 1
+
+#include "IpdlTuple.h"
+#include "base/process.h"
+#include "mozilla/Atomics.h"
+
+#if defined(XP_WIN)
+# include "nsWindowsDllInterceptor.h"
+#endif
+
+namespace mozilla {
+
+template <class T>
+class StaticAutoPtr;
+
+namespace plugins {
+
+// "PluginHooks" logging helpers
+extern mozilla::LazyLogModule sPluginHooksLog;
+#define HOOK_LOG(lvl, msg) MOZ_LOG(mozilla::plugins::sPluginHooksLog, lvl, msg);
+inline const char* SuccessMsg(bool aVal) {
+ return aVal ? "succeeded" : "failed";
+}
+
+class FunctionHook;
+class FunctionHookArray;
+
+class FunctionHook {
+ public:
+ virtual ~FunctionHook() = default;
+
+ virtual FunctionHookId FunctionId() const = 0;
+
+ /**
+ * Register to hook the function represented by this class.
+ * Returns false if we should have hooked but didn't.
+ */
+ virtual bool Register(int aQuirks) = 0;
+
+ /**
+ * Run the original function with parameters stored in a tuple.
+ * This is only supported on server-side and for auto-brokered methods.
+ */
+ virtual bool RunOriginalFunction(base::ProcessId aClientId,
+ const IPC::IpdlTuple& aInTuple,
+ IPC::IpdlTuple* aOutTuple) const = 0;
+
+ /**
+ * Hook the Win32 methods needed by the plugin process.
+ */
+ static void HookFunctions(int aQuirks);
+
+ static FunctionHookArray* GetHooks();
+
+#if defined(XP_WIN)
+ /**
+ * Special handler for hooking some kernel32.dll methods that we use to
+ * disable Flash protected mode.
+ */
+ static void HookProtectedMode();
+
+ /**
+ * Get the WindowsDllInterceptor for the given module. Creates a cache of
+ * WindowsDllInterceptors by name.
+ */
+ static WindowsDllInterceptor* GetDllInterceptorFor(const char* aModuleName);
+
+ /**
+ * Must be called to clear the cache created by calls to GetDllInterceptorFor.
+ */
+ static void ClearDllInterceptorCache();
+#endif // defined(XP_WIN)
+
+ private:
+ static StaticAutoPtr<FunctionHookArray> sFunctionHooks;
+ static void AddFunctionHooks(FunctionHookArray& aHooks);
+};
+
+// The FunctionHookArray deletes its FunctionHook objects when freed.
+class FunctionHookArray : public nsTArray<FunctionHook*> {
+ public:
+ ~FunctionHookArray() {
+ for (uint32_t idx = 0; idx < Length(); ++idx) {
+ FunctionHook* elt = ElementAt(idx);
+ MOZ_ASSERT(elt);
+ delete elt;
+ }
+ }
+};
+
+// Type of function that returns true if a function should be hooked according
+// to quirks.
+typedef bool(ShouldHookFunc)(int aQuirks);
+
+template <FunctionHookId functionId, typename FunctionType>
+class BasicFunctionHook : public FunctionHook {
+#if defined(XP_WIN)
+ using FuncHookType = WindowsDllInterceptor::FuncHookType<FunctionType*>;
+#endif // defined(XP_WIN)
+
+ public:
+ BasicFunctionHook(const char* aModuleName, const char* aFunctionName,
+ FunctionType* aOldFunction, FunctionType* aNewFunction)
+ : mOldFunction(aOldFunction),
+ mRegistration(UNREGISTERED),
+ mModuleName(aModuleName),
+ mFunctionName(aFunctionName),
+ mNewFunction(aNewFunction) {
+ MOZ_ASSERT(mOldFunction);
+ MOZ_ASSERT(mNewFunction);
+ }
+
+ /**
+ * Hooks the function if we haven't already and if ShouldHook() says to.
+ */
+ bool Register(int aQuirks) override;
+
+ /**
+ * Can be specialized to perform "extra" operations when running the
+ * function on the server side.
+ */
+ bool RunOriginalFunction(base::ProcessId aClientId,
+ const IPC::IpdlTuple& aInTuple,
+ IPC::IpdlTuple* aOutTuple) const override {
+ return false;
+ }
+
+ FunctionHookId FunctionId() const override { return functionId; }
+
+ FunctionType* OriginalFunction() const { return mOldFunction; }
+
+ protected:
+ // Once the function is hooked, this field will take the value of a pointer to
+ // a function that performs the old behavior. Before that, it is a pointer to
+ // the original function.
+ Atomic<FunctionType*> mOldFunction;
+#if defined(XP_WIN)
+ FuncHookType mStub;
+#endif // defined(XP_WIN)
+
+ enum RegistrationStatus { UNREGISTERED, FAILED, SUCCEEDED };
+ RegistrationStatus mRegistration;
+
+ // The name of the module containing the function to hook. E.g. "user32.dll".
+ const nsCString mModuleName;
+ // The name of the function in the module.
+ const nsCString mFunctionName;
+ // The function that we should replace functionName with. The signature of
+ // newFunction must match that of functionName.
+ FunctionType* const mNewFunction;
+ static ShouldHookFunc* const mShouldHook;
+};
+
+// Default behavior is to hook every registered function.
+extern bool AlwaysHook(int);
+template <FunctionHookId functionId, typename FunctionType>
+ShouldHookFunc* const BasicFunctionHook<functionId, FunctionType>::mShouldHook =
+ AlwaysHook;
+
+template <FunctionHookId functionId, typename FunctionType>
+bool BasicFunctionHook<functionId, FunctionType>::Register(int aQuirks) {
+ MOZ_RELEASE_ASSERT(XRE_IsPluginProcess());
+
+ // If we have already attempted to hook this function or if quirks tell us
+ // not to then don't hook.
+ if (mRegistration != UNREGISTERED || !mShouldHook(aQuirks)) {
+ return true;
+ }
+
+ bool isHooked = false;
+ mRegistration = FAILED;
+
+#if defined(XP_WIN)
+ WindowsDllInterceptor* dllInterceptor =
+ FunctionHook::GetDllInterceptorFor(mModuleName.Data());
+ if (!dllInterceptor) {
+ return false;
+ }
+
+ isHooked = mStub.Set(*dllInterceptor, mFunctionName.Data(), mNewFunction);
+#endif
+
+ if (isHooked) {
+#if defined(XP_WIN)
+ mOldFunction = mStub.GetStub();
+#endif
+ mRegistration = SUCCEEDED;
+ }
+
+ HOOK_LOG(LogLevel::Debug, ("Registering to intercept function '%s' : '%s'",
+ mFunctionName.Data(), SuccessMsg(isHooked)));
+
+ return isHooked;
+}
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // dom_plugins_ipc_functionhook_h
diff --git a/dom/plugins/ipc/IpdlTuple.h b/dom/plugins/ipc/IpdlTuple.h
new file mode 100644
index 0000000000..06db9bfddb
--- /dev/null
+++ b/dom/plugins/ipc/IpdlTuple.h
@@ -0,0 +1,186 @@
+/* -*- 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_ipdltuple_h
+#define dom_plugins_ipc_ipdltuple_h
+
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "mozilla/plugins/FunctionBrokerIPCUtils.h"
+#include "mozilla/Variant.h"
+
+namespace mozilla {
+namespace plugins {
+
+// The stuff in this "internal" namespace used to be inside the IpdlTuple
+// class, but that prevented the MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR
+// that is needed on the IpdlTupleElement struct. Without this, nsTArray can end
+// up using a move constructor on this struct, which is not memmovable on
+// Windows.
+namespace internal {
+
+struct InvalidType {};
+
+// Like Variant but with a default constructor.
+template <typename... Types>
+struct MaybeVariant {
+ public:
+ MaybeVariant() : mValue(InvalidType()) {}
+ MaybeVariant(MaybeVariant&& o) : mValue(std::move(o.mValue)) {}
+
+ template <typename Param>
+ void Set(const Param& aParam) {
+ mValue = mozilla::AsVariant(aParam);
+ }
+
+ typedef mozilla::Variant<InvalidType, Types...> MaybeVariantType;
+ MaybeVariantType& GetVariant() { return mValue; }
+ const MaybeVariantType& GetVariant() const { return mValue; }
+
+ private:
+ MaybeVariantType mValue;
+};
+
+#if defined(XP_WIN)
+typedef MaybeVariant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+ int64_t, uint64_t, nsCString, nsString, bool,
+ OpenFileNameIPC, OpenFileNameRetIPC, NativeWindowHandle,
+ IPCSchannelCred, IPCInternetBuffers, StringArray,
+ IPCPrintDlg>
+ IpdlTupleElement;
+#else
+typedef MaybeVariant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+ int64_t, uint64_t, nsCString, nsString, bool>
+ IpdlTupleElement;
+#endif // defined(XP_WIN)
+
+} // namespace internal
+} // namespace plugins
+} // namespace mozilla
+
+MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(
+ mozilla::plugins::internal::IpdlTupleElement)
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * IpdlTuple is used by automatic function brokering to pass parameter
+ * lists for brokered functions. It supports a limited set of types
+ * (see IpdlTuple::IpdlTupleElement).
+ */
+class IpdlTuple {
+ public:
+ uint32_t NumElements() const { return mTupleElements.Length(); }
+
+ template <typename EltType>
+ EltType* Element(uint32_t index) {
+ if ((index >= mTupleElements.Length()) ||
+ !mTupleElements[index].GetVariant().is<EltType>()) {
+ return nullptr;
+ }
+ return &mTupleElements[index].GetVariant().as<EltType>();
+ }
+
+ template <typename EltType>
+ const EltType* Element(uint32_t index) const {
+ return const_cast<IpdlTuple*>(this)->Element<EltType>(index);
+ }
+
+ template <typename EltType>
+ void AddElement(const EltType& aElt) {
+ IpdlTupleElement* newEntry = mTupleElements.AppendElement();
+ newEntry->Set(aElt);
+ }
+
+ private:
+ typedef mozilla::plugins::internal::InvalidType InvalidType;
+ typedef mozilla::plugins::internal::IpdlTupleElement IpdlTupleElement;
+
+ friend struct IPC::ParamTraits<IpdlTuple>;
+ friend struct IPC::ParamTraits<IpdlTuple::IpdlTupleElement>;
+ friend struct IPC::ParamTraits<IpdlTuple::InvalidType>;
+
+ nsTArray<IpdlTupleElement> mTupleElements;
+};
+
+namespace internal {
+template <>
+template <>
+inline void IpdlTupleElement::Set<nsDependentCSubstring>(
+ const nsDependentCSubstring& aParam) {
+ mValue = MaybeVariantType(mozilla::VariantType<nsCString>(), aParam);
+}
+} // namespace internal
+
+} // namespace plugins
+} // namespace mozilla
+
+namespace IPC {
+
+using namespace mozilla::plugins;
+
+template <>
+struct ParamTraits<IpdlTuple> {
+ typedef IpdlTuple paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mTupleElements);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aParam) {
+ return ReadParam(aMsg, aIter, &aParam->mTupleElements);
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ LogParam(aParam.mTupleElements, aLog);
+ }
+};
+
+template <>
+struct ParamTraits<IpdlTuple::IpdlTupleElement> {
+ typedef IpdlTuple::IpdlTupleElement paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ MOZ_RELEASE_ASSERT(!aParam.GetVariant().is<IpdlTuple::InvalidType>());
+ WriteParam(aMsg, aParam.GetVariant());
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aParam) {
+ bool ret = ReadParam(aMsg, aIter, &aParam->GetVariant());
+ MOZ_RELEASE_ASSERT(!aParam->GetVariant().is<IpdlTuple::InvalidType>());
+ return ret;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ aParam.GetVariant().match(
+ [aLog](const auto& aParam) { LogParam(aParam, aLog); });
+ }
+};
+
+template <>
+struct ParamTraits<IpdlTuple::InvalidType> {
+ typedef IpdlTuple::InvalidType paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ MOZ_ASSERT_UNREACHABLE("Attempt to serialize an invalid tuple element");
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aParam) {
+ MOZ_ASSERT_UNREACHABLE("Attempt to deserialize an invalid tuple element");
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ aLog->append(L"<Invalid Tuple Entry>");
+ }
+};
+
+} // namespace IPC
+
+#endif /* dom_plugins_ipc_ipdltuple_h */
diff --git a/dom/plugins/ipc/MiniShmParent.cpp b/dom/plugins/ipc/MiniShmParent.cpp
new file mode 100644
index 0000000000..874b7fe339
--- /dev/null
+++ b/dom/plugins/ipc/MiniShmParent.cpp
@@ -0,0 +1,178 @@
+/* -*- 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 0000000000..06eec62560
--- /dev/null
+++ b/dom/plugins/ipc/MiniShmParent.h
@@ -0,0 +1,87 @@
+/* -*- 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 0000000000..0aa94a1bc1
--- /dev/null
+++ b/dom/plugins/ipc/NPEventAndroid.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 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 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->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 0000000000..9e24c426e5
--- /dev/null
+++ b/dom/plugins/ipc/NPEventOSX.h
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 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:
+ MOZ_ASSERT_UNREACHABLE(
+ "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:
+ MOZ_ASSERT_UNREACHABLE(
+ "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 0000000000..55494b4d8c
--- /dev/null
+++ b/dom/plugins/ipc/NPEventUnix.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 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;
+};
+
+} // namespace plugins
+
+} // namespace mozilla
+
+//
+// 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 0000000000..b2429883f8
--- /dev/null
+++ b/dom/plugins/ipc/NPEventWindows.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 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 plugins
+
+} // namespace mozilla
+
+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 0000000000..65548ca06f
--- /dev/null
+++ b/dom/plugins/ipc/PBrowserStream.ipdl
@@ -0,0 +1,42 @@
+/* -*- 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);
+
+ /**
+ * 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 StreamDestroyed();
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PFunctionBroker.ipdl b/dom/plugins/ipc/PFunctionBroker.ipdl
new file mode 100644
index 0000000000..d8ecb76746
--- /dev/null
+++ b/dom/plugins/ipc/PFunctionBroker.ipdl
@@ -0,0 +1,23 @@
+/* -*- 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/. */
+
+using mozilla::plugins::FunctionHookId from "mozilla/plugins/FunctionBrokerIPCUtils.h";
+using IPC::IpdlTuple from "mozilla/plugins/IpdlTuple.h";
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * Top-level actor that brokers functions for the client process.
+ */
+sync protocol PFunctionBroker
+{
+parent:
+ sync BrokerFunction(FunctionHookId aFunctionId, IpdlTuple aFunctionParams)
+ returns (IpdlTuple aFunctionRet);
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl b/dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl
new file mode 100644
index 0000000000..d8c552fd86
--- /dev/null
+++ b/dom/plugins/ipc/PPluginBackgroundDestroyer.ipdl
@@ -0,0 +1,34 @@
+/* -*- 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__();
+};
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PPluginInstance.ipdl b/dom/plugins/ipc/PPluginInstance.ipdl
new file mode 100644
index 0000000000..4b54f61e3b
--- /dev/null
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -0,0 +1,294 @@
+/* -*- 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 PStreamNotify;
+include protocol PPluginSurface;
+
+include "gfxipc/ShadowLayerUtils.h";
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.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 "mozilla/ipc/IPCCore.h";
+using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
+using mozilla::plugins::WindowsSharedMemoryHandle from "mozilla/plugins/PluginMessageUtils.h";
+using mozilla::layers::SurfaceDescriptorX11 from "gfxipc/SurfaceDescriptor.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;
+};
+
+intr protocol PPluginInstance
+{
+ manager PPluginModule;
+
+ manages PPluginBackgroundDestroyer;
+ manages PPluginScriptableObject;
+ manages PBrowserStream;
+ manages PStreamNotify;
+ manages PPluginSurface;
+
+child:
+ async __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);
+
+ 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_ConvertPoint(double sourceX, bool ignoreDestX, double sourceY, bool ignoreDestY, NPCoordinateSpace sourceSpace,
+ NPCoordinateSpace destSpace)
+ returns (double destX, double destY, bool result);
+
+ async RedrawPlugin();
+
+ // 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);
+ 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);
+
+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 0000000000..6d98408c2f
--- /dev/null
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -0,0 +1,155 @@
+/* -*- 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 PContent;
+include protocol PProfiler;
+include protocol PFunctionBroker;
+
+using NPError from "npapi.h";
+using NPNVariable from "npapi.h";
+using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.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";
+using struct mozilla::plugins::NPAudioDeviceStateChangedIPC 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
+{
+ manages PPluginInstance;
+
+both:
+ // Window-specific message which instructs the interrupt mechanism to enter
+ // a nested event loop for the current interrupt call.
+ async ProcessNativeEventsInInterruptCall();
+
+child:
+ async InitProfiler(Endpoint<PProfilerChild> aEndPoint);
+
+ 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 PPluginInstance(nsCString aMimeType,
+ 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);
+
+ 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 InitCrashReporter()
+ returns (NativeThreadId tid);
+
+ async SettingChanged(PluginSettings settings);
+
+ async NPP_SetValue_NPNVaudioDeviceChangeDetails(NPAudioDeviceChangeDetailsIPC changeDetails);
+ async NPP_SetValue_NPNVaudioDeviceStateChanged(NPAudioDeviceStateChangedIPC deviceState);
+
+ async InitPluginModuleChild(Endpoint<PPluginModuleChild> endpoint);
+
+ async InitPluginFunctionBroker(Endpoint<PFunctionBrokerChild> endpoint);
+
+parent:
+ /**
+ * 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,
+ double aWidth, double 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();
+
+ // Answers to request about site data
+ async ReturnClearSiteData(NPError aRv, uint64_t aCallbackId);
+
+ async ReturnSitesWithData(nsCString[] aSites, uint64_t aCallbackId);
+
+ 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 0000000000..8bee7b1997
--- /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 "mozilla/ipc/IPCCore.h";
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.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/PPluginSurface.ipdl b/dom/plugins/ipc/PPluginSurface.ipdl
new file mode 100644
index 0000000000..7be038c604
--- /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 0000000000..3e196acab8
--- /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/PluginBackgroundDestroyer.cpp b/dom/plugins/ipc/PluginBackgroundDestroyer.cpp
new file mode 100644
index 0000000000..9c0b50d5e2
--- /dev/null
+++ b/dom/plugins/ipc/PluginBackgroundDestroyer.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 "PluginBackgroundDestroyer.h"
+#include "gfxSharedImageSurface.h"
+
+using namespace mozilla;
+using namespace plugins;
+
+PluginBackgroundDestroyerParent::PluginBackgroundDestroyerParent(
+ gfxASurface* aDyingBackground)
+ : mDyingBackground(aDyingBackground) {}
+
+PluginBackgroundDestroyerParent::~PluginBackgroundDestroyerParent() = default;
+
+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 0000000000..627b09fdc4
--- /dev/null
+++ b/dom/plugins/ipc/PluginBackgroundDestroyer.h
@@ -0,0 +1,56 @@
+/* -*- 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 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() = default;
+ virtual ~PluginBackgroundDestroyerChild() = default;
+
+ 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 0000000000..312b36f97d
--- /dev/null
+++ b/dom/plugins/ipc/PluginBridge.h
@@ -0,0 +1,41 @@
+/* -*- 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 ipc {
+template <class PFooSide>
+class Endpoint;
+} // namespace ipc
+
+namespace plugins {
+
+class PPluginModuleParent;
+
+bool SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent,
+ nsresult* aResult, uint32_t* aRunID,
+ ipc::Endpoint<PPluginModuleParent>* aEndpoint);
+
+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/PluginHangUIParent.cpp b/dom/plugins/ipc/PluginHangUIParent.cpp
new file mode 100644
index 0000000000..4f0dab52ab
--- /dev/null
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -0,0 +1,400 @@
+/* -*- 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 "base/command_line.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)
+ : Runnable("nsPluginHangUITelemetry"),
+ 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());
+
+ nsAutoString localizedStr;
+ rv = nsContentUtils::FormatLocalizedString(
+ localizedStr, nsContentUtils::eDOM_PROPERTIES, "PluginHangUIMessage",
+ aPluginName);
+ 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,
+ "ModalHangUI"_ns, u""_ns);
+ 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 0000000000..8853d5c425
--- /dev/null
+++ b/dom/plugins/ipc/PluginHangUIParent.h
@@ -0,0 +1,142 @@
+/* -*- 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 0000000000..403a735c83
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -0,0 +1,4045 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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 "StreamNotifyChild.h"
+#include "PluginProcessChild.h"
+#include "gfxASurface.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#include "nsNPAPIPluginInstance.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.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 namespace mozilla::plugins;
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+using namespace mozilla::widget;
+
+#ifdef MOZ_WIDGET_GTK
+
+# include <gtk/gtk.h>
+# include <gdk/gdkx.h>
+# include <gdk/gdk.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 WindowsDllInterceptor::FuncHookType<User32TrackPopupMenu>
+ sUser32TrackPopupMenuStub;
+
+static WindowsDllInterceptor sImm32Intercept;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmGetContext)>
+ sImm32ImmGetContextStub;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmGetCompositionStringW)>
+ sImm32ImmGetCompositionStringStub;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmSetCandidateWindow)>
+ sImm32ImmSetCandidateWindowStub;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmNotifyIME)>
+ sImm32ImmNotifyIME;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmAssociateContextEx)>
+ sImm32ImmAssociateContextExStub;
+
+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) {
+ MOZ_CRASH("CreateDrawTargetForSurface failed in plugin");
+ }
+ return drawTarget;
+}
+
+bool PluginInstanceChild::sIsIMEComposing = false;
+
+PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
+ const nsCString& aMimeType,
+ const nsTArray<nsCString>& aNames,
+ const nsTArray<nsCString>& aValues)
+ : mPluginIface(aPluginIface),
+ mMimeType(aMimeType),
+ mNames(aNames.Clone()),
+ mValues(aValues.Clone())
+#if defined(XP_DARWIN) || defined(XP_WIN)
+ ,
+ mContentsScaleFactor(1.0)
+#endif
+ ,
+ mCSSZoomFactor(0.0),
+ mPostingKeyEvents(0),
+ mPostingKeyEventsOutdated(0),
+ mDrawingModel(kDefaultDrawingModel),
+ mCurrentDirectSurface(nullptr),
+ mAsyncInvalidateMutex("PluginInstanceChild::mAsyncInvalidateMutex"),
+ mAsyncInvalidateTask(0),
+ mCachedWindowActor(nullptr),
+ mCachedElementActor(nullptr)
+#if defined(OS_WIN)
+ ,
+ mPluginWindowHWND(0),
+ mPluginWndProc(0),
+ mPluginParentHWND(0),
+ mCachedWinlessPluginHWND(0),
+ mWinlessPopupSurrogateHWND(0),
+ mWinlessThrottleOldWndProc(0),
+ mWinlessHiddenMsgHWND(0)
+#endif // OS_WIN
+#if defined(MOZ_WIDGET_COCOA)
+# if defined(__i386__)
+ ,
+ mEventModel(NPEventModelCarbon)
+# endif
+ ,
+ mShColorSpace(nullptr),
+ mShContext(nullptr),
+ mCGLayer(nullptr),
+ mCARefreshTimer(0),
+ 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),
+ mLastEnableIMEState(true)
+#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;
+# else
+ mWsInfo.display = DefaultXDisplay();
+# endif
+#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
+#if defined(OS_WIN)
+ InitPopupMenuHook();
+ InitImm32Hook();
+#endif // OS_WIN
+}
+
+PluginInstanceChild::~PluginInstanceChild() {
+#if defined(OS_WIN)
+ NS_ASSERTION(!mPluginWindowHWND,
+ "Destroying PluginInstanceChild without NPP_Destroy?");
+ // In the event that we registered for audio device changes, stop.
+ PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+ if (chromeInstance) {
+ 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,
+ NP_EMBED, argc, argn.get(), argv.get(), 0);
+ if (NPERR_NO_ERROR != rv) {
+ return rv;
+ }
+
+ if (!Initialize()) {
+ rv = NPERR_MODULE_LOAD_FAILED_ERROR;
+ }
+ 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)) {
+ result = NPERR_GENERIC_ERROR;
+ PPluginScriptableObjectChild* actorProtocol;
+ if (CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result) &&
+ (result == NPERR_NO_ERROR)) {
+ actor = mCachedWindowActor =
+ static_cast<PluginScriptableObjectChild*>(actorProtocol);
+ NS_ASSERTION(actor, "Null actor!");
+ if (!actor->GetObject(false)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ PluginModuleChild::sBrowserFuncs.retainobject(
+ actor->GetObject(false));
+ }
+ }
+ break;
+
+ case NPNVPluginElementNPObject:
+ if (!(actor = mCachedElementActor)) {
+ result = NPERR_GENERIC_ERROR;
+ PPluginScriptableObjectChild* actorProtocol;
+ if (CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol,
+ &result) &&
+ (result == NPERR_NO_ERROR)) {
+ actor = mCachedElementActor =
+ static_cast<PluginScriptableObjectChild*>(actorProtocol);
+ NS_ASSERTION(actor, "Null actor!");
+ if (!actor->GetObject(false)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ PluginModuleChild::sBrowserFuncs.retainobject(
+ actor->GetObject(false));
+ }
+ }
+ break;
+
+ default:
+ result = NPERR_GENERIC_ERROR;
+ MOZ_ASSERT_UNREACHABLE(
+ "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;
+ if (!(object = actor->GetObject(false))) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ *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.
+ if (!Initialize()) {
+ return NPERR_GENERIC_ERROR;
+ }
+ 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;
+ *((NPObject**)aValue) = nullptr;
+ 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:
+ MOZ_FALLTHROUGH_ASSERT(
+ "NPNVariable should be handled in "
+ "PluginModuleChild.");
+#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 (windowed) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (!CallNPN_SetValue_NPPVpluginWindow(windowed, &rv))
+ return NPERR_GENERIC_ERROR;
+
+ mWindow.type = NPWindowTypeDrawable;
+ 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;
+ }
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+ }
+
+ NS_ERROR("Failed to get actor!");
+ result = NPERR_GENERIC_ERROR;
+ } else {
+ result = NPERR_GENERIC_ERROR;
+ }
+
+ *aValue = nullptr;
+ *aResult = result;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+
+#else
+
+ MOZ_CRASH("shouldn't be called on non-ATK platforms");
+
+#endif
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceChild::AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value,
+ NPError* result) {
+ if (!mPluginIface->setvalue) {
+ *result = NPERR_GENERIC_ERROR;
+ return IPC_OK();
+ }
+
+ NPBool v = value;
+ *result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceChild::AnswerNPP_SetValue_NPNVCSSZoomFactor(const double& value,
+ NPError* result) {
+ if (!mPluginIface->setvalue) {
+ *result = NPERR_GENERIC_ERROR;
+ return IPC_OK();
+ }
+
+ mCSSZoomFactor = value;
+ double v = value;
+ *result = mPluginIface->setvalue(GetNPP(), NPNVCSSZoomFactor, &v);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceChild::AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value,
+ NPError* result) {
+ if (!mPluginIface->setvalue) {
+ *result = NPERR_GENERIC_ERROR;
+ return IPC_OK();
+ }
+
+ NPBool v = value;
+ *result = mPluginIface->setvalue(GetNPP(), NPNVmuteAudioBool, &v);
+ return IPC_OK();
+}
+
+#if defined(XP_WIN)
+NPError PluginInstanceChild::DefaultAudioDeviceChanged(
+ NPAudioDeviceChangeDetails& details) {
+ if (!mPluginIface->setvalue) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return mPluginIface->setvalue(GetNPP(), NPNVaudioDeviceChangeDetails,
+ (void*)&details);
+}
+
+NPError PluginInstanceChild::AudioDeviceStateChanged(
+ NPAudioDeviceStateChanged& aDeviceState) {
+ if (!mPluginIface->setvalue) {
+ return NPERR_GENERIC_ERROR;
+ }
+ return mPluginIface->setvalue(GetNPP(), NPNVaudioDeviceStateChanged,
+ (void*)&aDeviceState);
+}
+
+void SetMouseEventWParam(NPEvent* aEvent) {
+ // Fill in potentially missing key state info. See
+ // nsPluginInstanceOwner::ProcessEvent for circumstances where this happens.
+ const auto kMouseMessages = mozilla::Array<int, 9>(
+ WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN, WM_LBUTTONUP,
+ WM_MBUTTONUP, WM_RBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL);
+
+ bool isInvalidWParam =
+ (aEvent->wParam == NPAPI_INVALID_WPARAM) &&
+ (std::find(kMouseMessages.begin(), kMouseMessages.end(),
+ static_cast<int>(aEvent->event)) != kMouseMessages.end());
+
+ if (!isInvalidWParam) {
+ return;
+ }
+
+ aEvent->wParam = (::GetKeyState(VK_CONTROL) ? MK_CONTROL : 0) |
+ (::GetKeyState(VK_SHIFT) ? MK_SHIFT : 0) |
+ (::GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0) |
+ (::GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0) |
+ (::GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0) |
+ (::GetKeyState(VK_XBUTTON1) ? MK_XBUTTON1 : 0) |
+ (::GetKeyState(VK_XBUTTON2) ? MK_XBUTTON2 : 0);
+}
+#endif
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+
+ SetMouseEventWParam(&evcopy);
+ *handled = WinlessHandleEvent(evcopy);
+ return IPC_OK();
+#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, X11False);
+ }
+#endif
+
+ return IPC_OK();
+}
+
+#ifdef XP_MACOSX
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+ }
+ 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 IPC_OK();
+ }
+ }
+ 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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+}
+
+#else
+mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(
+ const NPRemoteEvent& event, Shmem&& mem, int16_t* handled, Shmem* rtnmem) {
+ MOZ_CRASH("not reached.");
+ *rtnmem = mem;
+ return IPC_OK();
+}
+#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;
+}
+
+mozilla::ipc::IPCResult 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 IPC_FAIL_NO_REASON(this);
+ }
+
+ 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 IPC_FAIL_NO_REASON(this);
+ }
+
+ 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 IPC_FAIL_NO_REASON(this);
+ }
+
+ mCARenderer->Render(mWindow.width, mWindow.height, mContentsScaleFactor,
+ nullptr);
+
+ return IPC_OK();
+}
+
+#else
+mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(
+ const NPRemoteEvent& event, const uint32_t& surfaceid, int16_t* handled) {
+ MOZ_CRASH("NPP_HandleEvent_IOSurface is a OSX-only message");
+}
+#endif
+
+mozilla::ipc::IPCResult 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
+ MOZ_CRASH("WindowPosChanged is a windows-only message");
+#endif
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+#else
+ MOZ_CRASH("ContentsScaleFactorChanged is an Windows or OSX only message");
+#endif
+}
+
+mozilla::ipc::IPCResult PluginInstanceChild::AnswerCreateChildPluginWindow(
+ NativeWindowHandle* aChildPluginWindow) {
+#if defined(XP_WIN)
+ MOZ_ASSERT(!mPluginWindowHWND);
+
+ if (!CreatePluginWindow()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ MOZ_ASSERT(mPluginWindowHWND);
+
+ *aChildPluginWindow = mPluginWindowHWND;
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE("CreateChildPluginWindow not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginInstanceChild::RecvCreateChildPopupSurrogate(
+ const NativeWindowHandle& aNetscapeWindow) {
+#if defined(XP_WIN)
+ mCachedWinlessPluginHWND = aNetscapeWindow;
+ CreateWinlessPopupSurrogate();
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE("CreateChildPluginWindow not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_SetWindow(
+ const NPRemoteWindow& aWindow) {
+ PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%" PRIx64
+ ", 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;
+
+ 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: {
+ 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:
+ MOZ_ASSERT_UNREACHABLE("Bad plugin window type.");
+ return IPC_FAIL_NO_REASON(this);
+ 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) || defined(MOZ_WAYLAND)
+ // Don't care
+#else
+# error Implement me for your OS
+#endif
+
+ return IPC_OK();
+}
+
+bool PluginInstanceChild::Initialize() {
+#if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
+ if (mWsInfo.display) {
+ // Already initialized
+ return true;
+ }
+
+ // Request for windowless plugins is set in newp(), before this call.
+ if (mWindow.type == NPWindowTypeWindow) {
+ return false;
+ }
+
+ mWsInfo.display = DefaultXDisplay();
+#endif
+
+#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) {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+ 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 IPC_OK();
+ }
+
+ // If there is outdated posting key events, we should consume the key
+ // events.
+ if (mPostingKeyEventsOutdated) {
+ mPostingKeyEventsOutdated--;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+#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 IPC_OK();
+}
+
+#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);
+}
+
+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) {
+ MOZ_ASSERT_UNREACHABLE("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 WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrA>
+ sUser32SetWindowLongAHookStub;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrW>
+ sUser32SetWindowLongWHookStub;
+# else
+typedef LONG(WINAPI* User32SetWindowLongA)(HWND hWnd, int nIndex,
+ LONG dwNewLong);
+typedef LONG(WINAPI* User32SetWindowLongW)(HWND hWnd, int nIndex,
+ LONG dwNewLong);
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongA>
+ sUser32SetWindowLongAHookStub;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongW>
+ sUser32SetWindowLongWHookStub;
+# 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
+ sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongPtrA",
+ &SetWindowLongPtrAHook);
+ sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongPtrW",
+ &SetWindowLongPtrWHook);
+# else
+ sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongA",
+ &SetWindowLongAHook);
+ sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongW",
+ &SetWindowLongWHook);
+# endif
+}
+
+/* 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)) {
+ 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.
+ sUser32Intercept.Init("user32.dll");
+ sUser32TrackPopupMenuStub.Set(sUser32Intercept, "TrackPopupMenu",
+ &TrackPopupHookProc);
+}
+
+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
+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) {
+ return FALSE;
+}
+
+// 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;
+}
+
+// static
+BOOL PluginInstanceChild::ImmAssociateContextExProc(HWND hWND, HIMC hImc,
+ DWORD dwFlags) {
+ PluginInstanceChild* self = sCurrentPluginInstance;
+ if (!self) {
+ // If ImmAssociateContextEx calls unexpected window message,
+ // we can use child instance object from window property if available.
+ self = reinterpret_cast<PluginInstanceChild*>(
+ GetProp(hWND, kFlashThrottleProperty));
+ NS_WARNING_ASSERTION(self, "Cannot find PluginInstanceChild");
+ }
+
+ // HIMC is always nullptr on Flash's windowless
+ if (!hImc && self) {
+ // Store the last IME state since Flash may call ImmAssociateContextEx
+ // before taking focus.
+ self->mLastEnableIMEState = !!(dwFlags & IACE_DEFAULT);
+ }
+ return sImm32ImmAssociateContextExStub(hWND, hImc, dwFlags);
+}
+
+void PluginInstanceChild::InitImm32Hook() {
+ if (!(GetQuirks() & QUIRK_WINLESS_HOOK_IME)) {
+ return;
+ }
+
+ // When using windowless plugin, IMM API won't work due to OOP.
+ //
+ // ImmReleaseContext on Windows 7+ just returns TRUE only, so we don't
+ // need to hook this.
+
+ sImm32Intercept.Init("imm32.dll");
+ sImm32ImmGetContextStub.Set(sImm32Intercept, "ImmGetContext",
+ &ImmGetContextProc);
+ sImm32ImmGetCompositionStringStub.Set(sImm32Intercept,
+ "ImmGetCompositionStringW",
+ &ImmGetCompositionStringProc);
+ sImm32ImmSetCandidateWindowStub.Set(sImm32Intercept, "ImmSetCandidateWindow",
+ &ImmSetCandidateWindowProc);
+ sImm32ImmNotifyIME.Set(sImm32Intercept, "ImmNotifyIME", &ImmNotifyIME);
+ sImm32ImmAssociateContextExStub.Set(sImm32Intercept, "ImmAssociateContextEx",
+ &ImmAssociateContextExProc);
+}
+
+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_LBUTTONDOWN ||
+ 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);
+ }
+
+ // This is hack of Flash's behaviour.
+ //
+ // When moving focus from chrome to plugin by mouse click, Gecko sends
+ // mouse message (such as WM_LBUTTONDOWN etc) at first, then sends
+ // WM_SETFOCUS. But Flash will call ImmAssociateContextEx on WM_LBUTTONDOWN
+ // even if it doesn't receive WM_SETFOCUS.
+ //
+ // In this situation, after sending mouse message, content process will be
+ // activated and set input context with PLUGIN. So after activating
+ // content process, we have to set current IME state again.
+
+ if (event.event == WM_KILLFOCUS) {
+ // Flash always calls ImmAssociateContextEx by taking focus.
+ // Although this flag doesn't have to be reset, I add it for safety.
+ mLastEnableIMEState = true;
+ }
+
+ 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) {
+ MOZ_ASSERT_UNREACHABLE("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) {
+ MOZ_ASSERT_UNREACHABLE("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::FlashThrottleMsg::GetProc() {
+ if (mInstance) {
+ return mWindowed ? mInstance->mPluginWndProc
+ : mInstance->mWinlessThrottleOldWndProc;
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+PluginInstanceChild::FlashThrottleMsg::Run() {
+ if (!mInstance) {
+ return NS_OK;
+ }
+
+ mInstance->mPendingFlashThrottleMsgs.RemoveElement(this);
+
+ // GetProc() checks mInstance, and pulls the procedure from
+ // PluginInstanceChild. We don't transport sub-class procedure
+ // ptrs around in FlashThrottleMsg msgs.
+ if (!GetProc()) return NS_OK;
+
+ // deliver the event to flash
+ CallWindowProc(GetProc(), GetWnd(), GetMsg(), GetWParam(), GetLParam());
+ return NS_OK;
+}
+
+nsresult PluginInstanceChild::FlashThrottleMsg::Cancel() {
+ MOZ_ASSERT(mInstance);
+ mInstance = nullptr;
+ return NS_OK;
+}
+
+void PluginInstanceChild::FlashThrottleMessage(HWND aWnd, UINT aMsg,
+ WPARAM aWParam, LPARAM aLParam,
+ bool isWindowed) {
+ // We save a reference to the FlashThrottleMsg so we can cancel it in
+ // Destroy if it's still alive.
+ RefPtr<FlashThrottleMsg> task =
+ new FlashThrottleMsg(this, aWnd, aMsg, aWParam, aLParam, isWindowed);
+
+ mPendingFlashThrottleMsgs.AppendElement(task);
+
+ MessageLoop::current()->PostDelayedTask(task.forget(),
+ kFlashWMUSERMessageThrottleDelayMs);
+}
+
+#endif // OS_WIN
+
+mozilla::ipc::IPCResult 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) return IPC_OK();
+ ::SetFocus(mPluginWindowHWND);
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE("AnswerSetPluginFocus not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE("AnswerUpdateWindow not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginInstanceChild::RecvNPP_DidComposite() {
+ if (mPluginIface->didComposite) {
+ mPluginIface->didComposite(GetNPP());
+ }
+ return IPC_OK();
+}
+
+PPluginScriptableObjectChild*
+PluginInstanceChild::AllocPPluginScriptableObjectChild() {
+ AssertPluginThread();
+ return new PluginScriptableObjectChild(Proxy);
+}
+
+bool PluginInstanceChild::DeallocPPluginScriptableObjectChild(
+ PPluginScriptableObjectChild* aObject) {
+ AssertPluginThread();
+ delete aObject;
+ return true;
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginInstanceChild::RecvPBrowserStreamConstructor(
+ PBrowserStreamChild* aActor, const nsCString& url, const uint32_t& length,
+ const uint32_t& lastmodified, PStreamNotifyChild* notifyData,
+ const nsCString& headers) {
+ return IPC_OK();
+}
+
+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;
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+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;
+}
+
+PStreamNotifyChild* PluginInstanceChild::AllocPStreamNotifyChild(
+ const nsCString& url, const nsCString& target, const bool& post,
+ const nsCString& buffer, const bool& file, NPError* result) {
+ AssertPluginThread();
+ MOZ_CRASH("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;
+}
+
+mozilla::ipc::IPCResult StreamNotifyChild::Recv__delete__(
+ const NPReason& reason) {
+ AssertPluginThread();
+
+ if (mBrowserStream)
+ mBrowserStream->NotifyPending();
+ else
+ NPP_URLNotify(reason);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+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;
+}
+
+void PluginInstanceChild::NPN_URLRedirectResponse(void* notifyData,
+ NPBool allow) {
+ if (!notifyData) {
+ return;
+ }
+
+ nsTArray<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, std::move(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;
+ }
+
+ 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(std::move(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();
+}
+
+mozilla::ipc::IPCResult 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>(
+ "plugins::PluginInstanceChild::DoAsyncSetWindow", this,
+ &PluginInstanceChild::DoAsyncSetWindow, aSurfaceType, aWindow, true);
+ RefPtr<Runnable> addrefedTask = mCurrentAsyncSetWindowTask;
+ MessageLoop::current()->PostTask(addrefedTask.forget());
+
+ return IPC_OK();
+}
+
+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
+
+ 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;
+ }
+
+ MOZ_CRASH("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;
+ }
+#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))
+ MOZ_CRASH("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) {
+ // width and height are stored as units, but narrow to ints here
+ MOZ_RELEASE_ASSERT(mWindow.width <= INT_MAX);
+ MOZ_RELEASE_ASSERT(mWindow.height <= INT_MAX);
+
+ WINDOWPOS winpos = {0,
+ 0,
+ mWindow.x,
+ mWindow.y,
+ (int32_t)mWindow.width,
+ (int32_t)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 = X11False;
+ 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), intptr_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
+ MOZ_CRASH("Surface type not implemented.");
+#endif
+}
+
+void PluginInstanceChild::PaintRectToSurface(const nsIntRect& aRect,
+ gfxASurface* aSurface,
+ const DeviceColor& 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);
+
+ // 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))
+ MOZ_CRASH("Expected SharedDIBSurface!");
+
+ // Paint the plugin directly onto the target, with a white
+ // background and copy the result
+ PaintRectToSurface(rect, aSurface, DeviceColor::MaskOpaqueWhite());
+ {
+ 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, DeviceColor::MaskOpaqueBlack());
+
+ // 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
+ gfxPoint deviceOffset = -targetRect.TopLeft();
+ // Paint onto white background
+ whiteImage->SetDeviceOffset(deviceOffset);
+ PaintRectToSurface(rect, whiteImage, DeviceColor::MaskOpaqueWhite());
+
+ 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, DeviceColor::MaskOpaqueBlack());
+#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, DeviceColor());
+ } 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, DeviceColor());
+ }
+ 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, X11False);
+ } 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 = std::move(
+ static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem());
+ } else {
+ MOZ_CRASH("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;
+
+ // When this method is run asynchronously, we can end up switching to
+ // direct drawing before while we wait to run. In that case, bail.
+ if (IsUsingDirectDrawing()) {
+ return;
+ }
+
+ 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(
+ "plugins::PluginInstanceChild::InvalidateRectDelayed", 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);
+}
+
+mozilla::ipc::IPCResult 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:
+ MOZ_CRASH("Unexpected background surface descriptor");
+ }
+
+ if (!mBackground) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ IntSize bgSize = mBackground->GetSize();
+ mAccumulatedInvalidRect.UnionRect(
+ mAccumulatedInvalidRect, nsIntRect(0, 0, bgSize.width, bgSize.height));
+ AsyncShowPluginFrame();
+ return IPC_OK();
+ }
+
+ // 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 IPC_OK();
+}
+
+PPluginBackgroundDestroyerChild*
+PluginInstanceChild::AllocPPluginBackgroundDestroyerChild() {
+ return new PluginBackgroundDestroyerChild();
+}
+
+mozilla::ipc::IPCResult
+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();
+ }
+
+ if (!PPluginBackgroundDestroyerChild::Send__delete__(aActor)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+bool PluginInstanceChild::DeallocPPluginBackgroundDestroyerChild(
+ PPluginBackgroundDestroyerChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+uint32_t PluginInstanceChild::ScheduleTimer(uint32_t interval, bool repeat,
+ TimerFunc func) {
+ auto* 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::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) {
+ MOZ_CRASH("Destroying plugin instance on the stack.");
+ }
+ mDestroyed = true;
+
+#if defined(OS_WIN)
+ SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
+#endif
+
+ nsTArray<PBrowserStreamChild*> streams;
+ ManagedPBrowserStreamChild(streams);
+
+ // First make sure none of these streams become deleted
+ streams.RemoveElementsBy([](const auto& stream) {
+ return !static_cast<BrowserStreamChild*>(stream)->InstanceDying();
+ });
+ 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 = MakeUnique<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();
+
+ for (uint32_t i = 0; i < mPendingFlashThrottleMsgs.Length(); ++i) {
+ mPendingFlashThrottleMsgs[i]->Cancel();
+ }
+ mPendingFlashThrottleMsgs.Clear();
+#endif
+}
+
+mozilla::ipc::IPCResult PluginInstanceChild::AnswerNPP_Destroy(
+ NPError* aResult) {
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+ *aResult = NPERR_NO_ERROR;
+
+ Destroy();
+
+ return IPC_OK();
+}
+
+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 0000000000..479c060f91
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -0,0 +1,613 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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 "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+#include "ChildTimer.h"
+#include "nsRect.h"
+#include "nsTHashtable.h"
+#include "mozilla/PaintTracker.h"
+#include "mozilla/gfx/Types.h"
+
+#include <map>
+
+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;
+ friend class PPluginInstanceChild;
+
+#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:
+ mozilla::ipc::IPCResult AnswerCreateChildPluginWindow(
+ NativeWindowHandle* aChildPluginWindow);
+
+ mozilla::ipc::IPCResult RecvCreateChildPopupSurrogate(
+ const NativeWindowHandle& aNetscapeWindow);
+
+ mozilla::ipc::IPCResult AnswerNPP_SetWindow(const NPRemoteWindow& window);
+
+ mozilla::ipc::IPCResult AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(
+ bool* wantsAllStreams, NPError* rv);
+ mozilla::ipc::IPCResult AnswerNPP_GetValue_NPPVpluginScriptableNPObject(
+ PPluginScriptableObjectChild** value, NPError* result);
+ mozilla::ipc::IPCResult
+ AnswerNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(nsCString* aPlugId,
+ NPError* aResult);
+ mozilla::ipc::IPCResult AnswerNPP_SetValue_NPNVprivateModeBool(
+ const bool& value, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPP_SetValue_NPNVmuteAudioBool(
+ const bool& value, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPP_SetValue_NPNVCSSZoomFactor(
+ const double& value, NPError* result);
+
+ mozilla::ipc::IPCResult AnswerNPP_HandleEvent(const NPRemoteEvent& event,
+ int16_t* handled);
+ mozilla::ipc::IPCResult AnswerNPP_HandleEvent_Shmem(
+ const NPRemoteEvent& event, Shmem&& mem, int16_t* handled, Shmem* rtnmem);
+ mozilla::ipc::IPCResult AnswerNPP_HandleEvent_IOSurface(
+ const NPRemoteEvent& event, const uint32_t& surface, int16_t* handled);
+
+ // Async rendering
+ mozilla::ipc::IPCResult RecvAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
+ const NPRemoteWindow& aWindow);
+
+ virtual void DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
+ const NPRemoteWindow& aWindow, bool aIsAsync);
+
+ PPluginSurfaceChild* AllocPPluginSurfaceChild(
+ const WindowsSharedMemoryHandle&, const gfx::IntSize&, const bool&) {
+ return new PPluginSurfaceChild();
+ }
+
+ bool DeallocPPluginSurfaceChild(PPluginSurfaceChild* s) {
+ delete s;
+ return true;
+ }
+
+ mozilla::ipc::IPCResult AnswerPaint(const NPRemoteEvent& event,
+ int16_t* handled) {
+ PaintTracker pt;
+ if (!AnswerNPP_HandleEvent(event, handled)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvWindowPosChanged(const NPRemoteEvent& event);
+
+ mozilla::ipc::IPCResult RecvContentsScaleFactorChanged(
+ const double& aContentsScaleFactor);
+
+ mozilla::ipc::IPCResult AnswerNPP_Destroy(NPError* result);
+
+ PPluginScriptableObjectChild* AllocPPluginScriptableObjectChild();
+
+ bool DeallocPPluginScriptableObjectChild(
+ PPluginScriptableObjectChild* aObject);
+
+ virtual mozilla::ipc::IPCResult RecvPPluginScriptableObjectConstructor(
+ PPluginScriptableObjectChild* aActor) override;
+
+ virtual mozilla::ipc::IPCResult RecvPBrowserStreamConstructor(
+ PBrowserStreamChild* aActor, const nsCString& aURL,
+ const uint32_t& aLength, const uint32_t& aLastmodified,
+ PStreamNotifyChild* aNotifyData, const nsCString& aHeaders) override;
+
+ mozilla::ipc::IPCResult AnswerNPP_NewStream(PBrowserStreamChild* actor,
+ const nsCString& mimeType,
+ const bool& seekable, NPError* rv,
+ uint16_t* stype);
+
+ PBrowserStreamChild* AllocPBrowserStreamChild(const nsCString& url,
+ const uint32_t& length,
+ const uint32_t& lastmodified,
+ PStreamNotifyChild* notifyData,
+ const nsCString& headers);
+
+ bool DeallocPBrowserStreamChild(PBrowserStreamChild* stream);
+
+ PStreamNotifyChild* AllocPStreamNotifyChild(
+ const nsCString& url, const nsCString& target, const bool& post,
+ const nsCString& buffer, const bool& file, NPError* result);
+
+ bool DeallocPStreamNotifyChild(PStreamNotifyChild* notifyData);
+
+ mozilla::ipc::IPCResult AnswerSetPluginFocus();
+
+ mozilla::ipc::IPCResult AnswerUpdateWindow();
+
+ mozilla::ipc::IPCResult RecvNPP_DidComposite();
+
+ public:
+ PluginInstanceChild(const NPPluginFuncs* aPluginIface,
+ const nsCString& aMimeType,
+ const nsTArray<nsCString>& aNames,
+ const nsTArray<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);
+
+ 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();
+
+ mozilla::ipc::IPCResult RecvHandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData, const bool& aIsConsumed);
+
+#if defined(XP_WIN)
+ NPError DefaultAudioDeviceChanged(NPAudioDeviceChangeDetails& details);
+ NPError AudioDeviceStateChanged(NPAudioDeviceStateChanged& aDeviceState);
+#endif
+
+ private:
+ friend class PluginModuleChild;
+
+ NPError InternalGetNPObjectForValue(NPNVariable aValue, NPObject** aObject);
+
+ bool IsUsingDirectDrawing();
+
+ mozilla::ipc::IPCResult RecvUpdateBackground(
+ const SurfaceDescriptor& aBackground, const nsIntRect& aRect);
+
+ PPluginBackgroundDestroyerChild* AllocPPluginBackgroundDestroyerChild();
+
+ mozilla::ipc::IPCResult RecvPPluginBackgroundDestroyerConstructor(
+ PPluginBackgroundDestroyerChild* aActor) override;
+
+ bool DeallocPPluginBackgroundDestroyerChild(
+ PPluginBackgroundDestroyerChild* aActor);
+
+#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 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 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 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);
+ static BOOL WINAPI ImmAssociateContextExProc(HWND hWnd, HIMC aIMC,
+ DWORD dwFlags);
+
+ class FlashThrottleMsg : public CancelableRunnable {
+ public:
+ FlashThrottleMsg(PluginInstanceChild* aInstance, HWND aWnd, UINT aMsg,
+ WPARAM aWParam, LPARAM aLParam, bool isWindowed)
+ : CancelableRunnable("FlashThrottleMsg"),
+ mInstance(aInstance),
+ mWnd(aWnd),
+ mMsg(aMsg),
+ mWParam(aWParam),
+ mLParam(aLParam),
+ mWindowed(isWindowed) {}
+
+ NS_IMETHOD Run() override;
+ nsresult Cancel() override;
+
+ WNDPROC GetProc();
+ HWND GetWnd() { return mWnd; }
+ UINT GetMsg() { return mMsg; }
+ WPARAM GetWParam() { return mWParam; }
+ LPARAM GetLParam() { return mLParam; }
+
+ private:
+ PluginInstanceChild* mInstance;
+ 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;
+ nsTArray<nsCString> mNames;
+ nsTArray<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;
+#elif defined(OS_WIN)
+ HWND mPluginWindowHWND;
+ WNDPROC mPluginWndProc;
+ HWND mPluginParentHWND;
+ int mNestedEventLevelDepth;
+ HWND mCachedWinlessPluginHWND;
+ HWND mWinlessPopupSurrogateHWND;
+ nsIntPoint mPluginSize;
+ WNDPROC mWinlessThrottleOldWndProc;
+ HWND mWinlessHiddenMsgHWND;
+#endif
+
+#if defined(OS_WIN)
+ nsTArray<FlashThrottleMsg*> mPendingFlashThrottleMsgs;
+#endif
+ nsTArray<UniquePtr<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.
+ */
+ UniquePtr<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::DeviceColor& 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;
+
+ // Store the last IME state by ImmAssociateContextEx. This will reset by
+ // WM_KILLFOCUS;
+ bool mLastEnableIMEState;
+#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 0000000000..6e29171d91
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -0,0 +1,2326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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/StaticPrefs_dom.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/ToString.h"
+#include "mozilla/dom/Element.h"
+#include "PluginInstanceParent.h"
+#include "BrowserStreamParent.h"
+#include "PluginBackgroundDestroyer.h"
+#include "PluginModuleParent.h"
+#include "StreamNotifyParent.h"
+#include "npfunctions.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"
+#ifdef MOZ_X11
+# include "gfxXlibSurface.h"
+#endif
+#include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "Layers.h"
+#include "ImageContainer.h"
+#include "GLContext.h"
+#include "GLContextProvider.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"
+# include "mozilla/layers/CompositorBridgeChild.h"
+# include "GPUVideoImage.h"
+# include "mozilla/layers/SynchronousTask.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
+}
+
+mozilla::ipc::IPCResult StreamNotifyParent::RecvRedirectNotifyResponse(
+ const bool& allow) {
+ PluginInstanceParent* instance =
+ static_cast<PluginInstanceParent*>(Manager());
+ instance->mNPNIface->urlredirectresponse(instance->mNPP, this,
+ static_cast<NPBool>(allow));
+ return IPC_OK();
+}
+
+#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;
+}
+} // namespace plugins
+} // namespace mozilla
+#endif
+
+PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent, NPP npp,
+ const nsCString& aMimeType,
+ const NPNetscapeFuncs* npniface)
+ : mParent(parent),
+ mNPP(npp),
+ mNPNIface(npniface),
+ mWindowType(NPWindowTypeWindow),
+ mDrawingModel(kDefaultDrawingModel),
+ mLastRecordedDrawingModel(-1),
+ mFrameID(0)
+#if defined(OS_WIN)
+ ,
+ mPluginHWND(nullptr),
+ mChildPluginHWND(nullptr),
+ mChildPluginsParentHWND(nullptr),
+ mPluginWndProc(nullptr)
+#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;
+ }
+ return NS_SUCCEEDED(
+ NS_MakeAbsoluteURI(mSrcAttribute, aSrcAttribute, owner->GetBaseURI()));
+}
+
+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;
+ 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) {
+ MOZ_CRASH("Not reachable");
+ return nullptr;
+}
+
+bool PluginInstanceParent::DeallocPBrowserStreamParent(
+ PBrowserStreamParent* stream) {
+ delete stream;
+ return true;
+}
+
+mozilla::ipc::IPCResult
+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) || defined(MOZ_WAYLAND)
+ // TODO: Need impl
+ int id;
+#else
+# warning Implement me
+#endif
+
+ *result = mNPNIface->getvalue(mNPP, NPNVnetscapeWindow, &id);
+ *value = id;
+ return IPC_OK();
+}
+
+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;
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_GetValue_NPNVWindowNPObject(
+ PPluginScriptableObjectParent** aValue, NPError* aResult) {
+ if (!InternalGetValueForNPObject(NPNVWindowNPObject, aValue, aResult)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_GetValue_NPNVPluginElementNPObject(
+ PPluginScriptableObjectParent** aValue, NPError* aResult) {
+ if (!InternalGetValueForNPObject(NPNVPluginElementNPObject, aValue,
+ aResult)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_GetValue_NPNVprivateModeBool(bool* value,
+ NPError* result) {
+ NPBool v;
+ *result = mNPNIface->getvalue(mNPP, NPNVprivateModeBool, &v);
+ *value = v;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_GetValue_DrawingModelSupport(
+ const NPNVariable& model, bool* value) {
+ *value = false;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+}
+
+static inline bool AllowDirectBitmapSurfaceDrawing() {
+ if (!mozilla::StaticPrefs::dom_ipc_plugins_asyncdrawing_enabled()) {
+ return false;
+ }
+ return gfxPlatform::GetPlatform()->SupportsPluginDirectBitmapDrawing();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_GetValue_SupportsAsyncBitmapSurface(
+ bool* value) {
+ *value = AllowDirectBitmapSurfaceDrawing();
+ return IPC_OK();
+}
+
+/* static */
+bool PluginInstanceParent::SupportsPluginDirectDXGISurfaceDrawing() {
+ bool value = false;
+#if defined(XP_WIN)
+ // When WebRender does not use ANGLE, DXGISurface could not be used.
+ bool useAsyncDXGISurface =
+ StaticPrefs::dom_ipc_plugins_allow_dxgi_surface() &&
+ !(gfx::gfxVars::UseWebRender() && !gfx::gfxVars::UseWebRenderANGLE());
+ if (useAsyncDXGISurface) {
+ auto cbc = CompositorBridgeChild::Get();
+ if (cbc) {
+ cbc->SendSupportsAsyncDXGISurface(&value);
+ }
+ }
+#endif
+ return value;
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_GetValue_PreferredDXGIAdapter(
+ DxgiAdapterDesc* aOutDesc) {
+ PodZero(aOutDesc);
+#if defined(XP_WIN)
+ auto cbc = CompositorBridgeChild::Get();
+ if (cbc) {
+ cbc->SendPreferredDXGIAdapter(aOutDesc);
+ }
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginTransparent(
+ const bool& transparent, NPError* result) {
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginTransparentBool,
+ (void*)(intptr_t)transparent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginUsesDOMForCursor(
+ const bool& useDOMForCursor, NPError* result) {
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginUsesDOMForCursorBool,
+ (void*)(intptr_t)useDOMForCursor);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+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 = SupportsPluginDirectDXGISurfaceDrawing();
+ 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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginEventModel(
+ const int& eventModel, NPError* result) {
+#ifdef XP_MACOSX
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginEventModel,
+ (void*)(intptr_t)eventModel);
+ return IPC_OK();
+#else
+ *result = NPERR_GENERIC_ERROR;
+ return IPC_OK();
+#endif
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(
+ const bool& isAudioPlaying, NPError* result) {
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginIsPlayingAudio,
+ (void*)(intptr_t)isAudioPlaying);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginInstanceParent::AnswerNPN_GetURL(
+ const nsCString& url, const nsCString& target, NPError* result) {
+ *result = mNPNIface->geturl(mNPP, NullableStringGet(url),
+ NullableStringGet(target));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+PStreamNotifyParent* PluginInstanceParent::AllocPStreamNotifyParent(
+ const nsCString& url, const nsCString& target, const bool& post,
+ const nsCString& buffer, const bool& file, NPError* result) {
+ return new StreamNotifyParent();
+}
+
+mozilla::ipc::IPCResult 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) {
+ if (!PStreamNotifyParent::Send__delete__(actor, NPERR_GENERIC_ERROR)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+ }
+ }
+
+ return IPC_OK();
+}
+
+bool PluginInstanceParent::DeallocPStreamNotifyParent(
+ PStreamNotifyParent* notifyData) {
+ delete notifyData;
+ return true;
+}
+
+mozilla::ipc::IPCResult PluginInstanceParent::RecvNPN_InvalidateRect(
+ const NPRect& rect) {
+ mNPNIface->invalidaterect(mNPP, const_cast<NPRect*>(&rect));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginInstanceParent::RecvRevokeCurrentDirectSurface() {
+ ImageContainer* container = GetImageContainer();
+ if (!container) {
+ return IPC_OK();
+ }
+
+ container->ClearAllImages();
+
+ PLUGIN_LOG_DEBUG((" (RecvRevokeCurrentDirectSurface)"));
+ return IPC_OK();
+}
+
+#if defined(XP_WIN)
+// Uses the ImageBridge to perform IGPUVideoSurfaceManager operations
+// in the GPU process.
+class AsyncPluginSurfaceManager : public IGPUVideoSurfaceManager {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPluginSurfaceManager, override)
+
+ already_AddRefed<gfx::SourceSurface> Readback(
+ const SurfaceDescriptorGPUVideo& aSD) override {
+ SurfaceDescriptorPlugin pluginSD = aSD;
+ if (!InImageBridgeChildThread()) {
+ SynchronousTask task("AsyncPluginSurfaceManager readback sync");
+ RefPtr<gfx::SourceSurface> result;
+ ImageBridgeChild::GetSingleton()->GetThread()->Dispatch(
+ NewRunnableFunction("AsyncPluginSurfaceManager readback",
+ &DoSyncReadback, &pluginSD, &result, &task));
+ task.Wait();
+ return result.forget();
+ }
+
+ return DoReadback(pluginSD);
+ }
+
+ void DeallocateSurfaceDescriptor(
+ const SurfaceDescriptorGPUVideo& aSD) override {
+ SurfaceDescriptorPlugin pluginSD = aSD;
+ if (!InImageBridgeChildThread()) {
+ ImageBridgeChild::GetSingleton()->GetThread()->Dispatch(
+ NewRunnableFunction("AsyncPluginSurfaceManager dealloc", &DoDealloc,
+ &pluginSD));
+ return;
+ }
+
+ return DoDealloc(&pluginSD);
+ }
+
+ // Set of display surfaces for which the related plugin surface has been
+ // freed. They are freed when the AsyncPluginSurfaceManager is told it is
+ // safe.
+ static HashSet<WindowsHandle> sOrphanedDisplaySurfaces;
+
+ private:
+ ~AsyncPluginSurfaceManager() {}
+
+ struct SurfaceDescriptorUserData {
+ explicit SurfaceDescriptorUserData(layers::SurfaceDescriptor& aSD)
+ : mSD(aSD) {}
+
+ ~SurfaceDescriptorUserData() {
+ auto ibc = ImageBridgeChild::GetSingleton();
+ if (!ibc) {
+ return;
+ }
+ DestroySurfaceDescriptor(ibc, &mSD);
+ }
+
+ layers::SurfaceDescriptor mSD;
+ };
+
+ static void DeleteSurfaceDescriptorUserData(void* aClosure) {
+ SurfaceDescriptorUserData* sd =
+ reinterpret_cast<SurfaceDescriptorUserData*>(aClosure);
+ delete sd;
+ }
+
+ static already_AddRefed<gfx::SourceSurface> DoReadback(
+ const SurfaceDescriptorPlugin& aSD) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ RefPtr<DataSourceSurface> source;
+ auto ibc = ImageBridgeChild::GetSingleton();
+ if (!ibc) {
+ return nullptr;
+ }
+
+ layers::SurfaceDescriptor dataSD = null_t();
+ ibc->SendReadbackAsyncPluginSurface(aSD, &dataSD);
+ if (!IsSurfaceDescriptorValid(dataSD)) {
+ NS_WARNING("Bad SurfaceDescriptor received in Readback");
+ return nullptr;
+ }
+
+ source = GetSurfaceForDescriptor(dataSD);
+ if (!source) {
+ DestroySurfaceDescriptor(ibc, &dataSD);
+ NS_WARNING("Failed to map SurfaceDescriptor in Readback");
+ return nullptr;
+ }
+
+ static UserDataKey sSurfaceDescriptor;
+ source->AddUserData(&sSurfaceDescriptor,
+ new SurfaceDescriptorUserData(dataSD),
+ DeleteSurfaceDescriptorUserData);
+
+ return source.forget();
+ }
+
+ static void DoSyncReadback(const SurfaceDescriptorPlugin* aSD,
+ RefPtr<gfx::SourceSurface>* aResult,
+ SynchronousTask* aTask) {
+ AutoCompleteTask act(aTask);
+ *aResult = DoReadback(*aSD);
+ }
+
+ static void DoDealloc(const SurfaceDescriptorPlugin* aSD) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ // If the plugin already Finalized and freed its surface then, since the
+ // compositor is now also done with the display surface, we can free
+ // it too.
+ WindowsHandle handle = aSD->displaySurf().handle();
+ auto surfIt = sOrphanedDisplaySurfaces.lookup(handle);
+ if (!surfIt) {
+ // We wil continue to use the surfaces with future GPUVideoImages.
+ return;
+ }
+
+ sOrphanedDisplaySurfaces.remove(surfIt);
+ auto ibc = ImageBridgeChild::GetSingleton();
+ if (!ibc) {
+ return;
+ }
+ ibc->SendRemoveAsyncPluginSurface(*aSD, true);
+ }
+};
+
+/* static */ HashSet<WindowsHandle>
+ AsyncPluginSurfaceManager::sOrphanedDisplaySurfaces;
+
+void InitDXGISurface(const gfx::SurfaceFormat& aFormat,
+ const gfx::IntSize& aSize,
+ SurfaceDescriptorPlugin* aSDPlugin,
+ SynchronousTask* aTask) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ AutoCompleteTask act(aTask);
+ auto ibc = ImageBridgeChild::GetSingleton();
+ if (!ibc) {
+ return;
+ }
+
+ layers::SurfaceDescriptorPlugin sd;
+ if (!ibc->SendMakeAsyncPluginSurfaces(aFormat, aSize, &sd)) {
+ return;
+ }
+ *aSDPlugin = sd;
+}
+
+void FinalizeDXGISurface(const SurfaceDescriptorPlugin& aSD) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ Unused << AsyncPluginSurfaceManager::sOrphanedDisplaySurfaces.put(
+ aSD.displaySurf().handle());
+
+ auto ibc = ImageBridgeChild::GetSingleton();
+ if (!ibc) {
+ return;
+ }
+ ibc->SendRemoveAsyncPluginSurface(aSD, false);
+}
+
+void CopyDXGISurface(const SurfaceDescriptorPlugin& aSD,
+ SynchronousTask* aTask) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ AutoCompleteTask act(aTask);
+ auto ibc = ImageBridgeChild::GetSingleton();
+ if (!ibc) {
+ return;
+ }
+ ibc->SendUpdateAsyncPluginSurface(aSD);
+}
+
+#endif // defined(XP_WIN)
+
+mozilla::ipc::IPCResult PluginInstanceParent::RecvInitDXGISurface(
+ const gfx::SurfaceFormat& format, const gfx::IntSize& size,
+ WindowsHandle* outHandle, NPError* outError) {
+ *outHandle = 0;
+ *outError = NPERR_GENERIC_ERROR;
+
+#if defined(XP_WIN)
+ MOZ_ASSERT(NS_IsMainThread());
+ if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
+ *outError = NPERR_INVALID_PARAM;
+ return IPC_OK();
+ }
+ if (size.width <= 0 || size.height <= 0) {
+ *outError = NPERR_INVALID_PARAM;
+ return IPC_OK();
+ }
+
+ if (!ImageBridgeChild::GetSingleton()) {
+ return IPC_OK();
+ }
+
+ // Ask the ImageBridge thread to generate two SurfaceDescriptorPlugins --
+ // one for the GPU process to display and one for the Plugin process to
+ // render to.
+ SurfaceDescriptorPlugin sd;
+ SynchronousTask task("SendMakeAsyncPluginSurfaces sync");
+ ImageBridgeChild::GetSingleton()->GetThread()->Dispatch(
+ NewRunnableFunction("SendingMakeAsyncPluginSurfaces", &InitDXGISurface,
+ format, size, &sd, &task));
+ task.Wait();
+
+ if (!sd.id()) {
+ NS_WARNING("SendMakeAsyncPluginSurfaces failed");
+ return IPC_OK();
+ }
+
+ WindowsHandle pluginSurfHandle = sd.pluginSurf().handle();
+ bool ok = mAsyncSurfaceMap.put(pluginSurfHandle, AsyncSurfaceInfo{sd, size});
+ if (!ok) {
+ return IPC_OK();
+ }
+
+ *outHandle = pluginSurfHandle;
+ *outError = NPERR_NO_ERROR;
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginInstanceParent::RecvFinalizeDXGISurface(
+ const WindowsHandle& pluginSurfHandle) {
+#if defined(XP_WIN)
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!ImageBridgeChild::GetSingleton()) {
+ return IPC_OK();
+ }
+
+ auto asiIt = mAsyncSurfaceMap.lookup(pluginSurfHandle);
+ if (!asiIt) {
+ NS_WARNING("Plugin surface did not exist to finalize");
+ return IPC_OK();
+ }
+
+ AsyncSurfaceInfo& asi = asiIt->value();
+
+ // Release the plugin surface but keep the display surface since it may
+ // still be displayed. Also let the display surface know that it should
+ // not receive further requests to copy from the plugin surface.
+ ImageBridgeChild::GetSingleton()->GetThread()->Dispatch(NewRunnableFunction(
+ "SendingRemoveAsyncPluginSurface", &FinalizeDXGISurface, asi.mSD));
+
+ mAsyncSurfaceMap.remove(asiIt);
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_FAIL_NO_REASON(this);
+ }
+ if (size.width <= 0 || size.height <= 0) {
+ MOZ_ASSERT_UNREACHABLE("bad image size");
+ return IPC_FAIL_NO_REASON(this);
+ }
+ if (mDrawingModel != NPDrawingModelAsyncBitmapSurface) {
+ MOZ_ASSERT_UNREACHABLE("plugin did not set a bitmap drawing model");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ // 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 IPC_FAIL_NO_REASON(this);
+ }
+
+ ImageContainer* container = GetImageContainer();
+ if (!container) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ RefPtr<gfx::DataSourceSurface> source =
+ gfx::Factory::CreateWrappingDataSourceSurface(buffer.get<uint8_t>(),
+ stride, size, format);
+ if (!source) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ // 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 IPC_FAIL_NO_REASON(this);
+ }
+
+ // Upload the plugin buffer.
+ {
+ TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ 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, ToString(size).c_str(),
+ ToString(dirty).c_str()));
+ return IPC_OK();
+}
+
+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();
+}
+
+mozilla::ipc::IPCResult PluginInstanceParent::RecvShowDirectDXGISurface(
+ const WindowsHandle& pluginSurfHandle, const gfx::IntRect& dirty) {
+#if defined(XP_WIN)
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!ImageBridgeChild::GetSingleton()) {
+ return IPC_OK();
+ }
+
+ auto asiIt = mAsyncSurfaceMap.lookup(pluginSurfHandle);
+ if (!asiIt) {
+ NS_WARNING("Plugin surface did not exist to finalize");
+ return IPC_OK();
+ }
+
+ AsyncSurfaceInfo& asi = asiIt->value();
+
+ // Tell the ImageBridge to copy from the plugin surface to the display surface
+ SynchronousTask task("SendUpdateAsyncPluginSurface sync");
+ ImageBridgeChild::GetSingleton()->GetThread()->Dispatch(NewRunnableFunction(
+ "SendingUpdateAsyncPluginSurface", &CopyDXGISurface, asi.mSD, &task));
+ task.Wait();
+
+ // Make sure we have an ImageContainer for SetCurrentImage.
+ ImageContainer* container = GetImageContainer();
+ if (!container) {
+ return IPC_OK();
+ }
+
+ SetCurrentImage(
+ new GPUVideoImage(new AsyncPluginSurfaceManager(), asi.mSD, asi.mSize));
+
+ PLUGIN_LOG_DEBUG((" (RecvShowDirectDXGISurface received handle=%p rect=%s)",
+ reinterpret_cast<void*>(pluginSurfHandle),
+ ToString(dirty).c_str()));
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_FAIL_NO_REASON(this);
+ }
+ 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 IPC_FAIL_NO_REASON(this);
+ }
+
+ 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 IPC_OK();
+ }
+#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 = std::move(
+ 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::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 IPC_OK();
+}
+
+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 defined(OS_WIN)
+ // Windows async surfaces must be Win32. In particular, it is incompatible
+ // with in-memory surface types.
+ gfxSurfaceType surfType = gfxSurfaceType::Win32;
+#else
+ gfxSurfaceType surfType =
+ gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
+#endif
+
+ if (surfType == (gfxSurfaceType)-1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!SendAsyncSetWindow(surfType, 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::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(), X11False);
+#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(CompositableHandle(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
+
+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 mozilla::plugins::SurfaceDescriptor(std::move(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() {
+ MOZ_CRASH("'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));
+
+ 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);
+ window.contentsScaleFactor = floatScaleFactor;
+#endif
+#if defined(XP_MACOSX)
+ int scaleFactor = ceil(floatScaleFactor);
+ 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;
+ }
+
+ 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.
+ // mPluginHWND is always NULL for non-windowed plugins.
+ if (mPluginHWND) {
+ 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, X11False);
+ 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, std::move(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;
+ }
+
+ NPError err = NPERR_NO_ERROR;
+ 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;
+ }
+ MOZ_ASSERT(s->IsBrowserStream());
+ BrowserStreamParent* sp = static_cast<BrowserStreamParent*>(s);
+ if (sp->mNPP != this) MOZ_CRASH("Mismatched plugin data");
+ sp->NPP_DestroyStream(reason);
+ return NPERR_NO_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;
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+}
+
+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
+}
+
+mozilla::ipc::IPCResult PluginInstanceParent::AnswerNPN_PushPopupsEnabledState(
+ const bool& aState) {
+ mNPNIface->pushpopupsenabledstate(mNPP, aState ? 1 : 0);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginInstanceParent::AnswerNPN_PopPopupsEnabledState() {
+ mNPNIface->poppopupsenabledstate(mNPP);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginInstanceParent::RecvRedrawPlugin() {
+ nsNPAPIPluginInstance* inst =
+ static_cast<nsNPAPIPluginInstance*>(mNPP->ndata);
+ if (!inst) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ inst->RedrawPlugin();
+ return IPC_OK();
+}
+
+nsPluginInstanceOwner* PluginInstanceParent::GetOwner() {
+ nsNPAPIPluginInstance* inst =
+ static_cast<nsNPAPIPluginInstance*>(mNPP->ndata);
+ if (!inst) {
+ return nullptr;
+ }
+ return inst->GetOwner();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE("RecvSetNetscapeWindowAsParent not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#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) {
+ MOZ_ASSERT_UNREACHABLE(
+ "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) {
+ MOZ_ASSERT_UNREACHABLE(
+ "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
+ mozilla::UniquePtr<PluginInstanceParent> tmp;
+ MOZ_ASSERT(sPluginInstanceList);
+ sPluginInstanceList->Remove((void*)mPluginHWND, &tmp);
+ mozilla::Unused << tmp.release();
+ 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)
+
+mozilla::ipc::IPCResult 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) {
+ RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
+ RefPtr<dom::Element> element;
+ owner->GetDOMElement(getter_AddRefs(element));
+ if (fm && element) {
+ fm->SetFocus(element, 0);
+ }
+ }
+ }
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE("AnswerPluginFocusChange not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+PluginInstanceParent* PluginInstanceParent::Cast(NPP aInstance) {
+ auto ip = static_cast<PluginInstanceParent*>(aInstance->pdata);
+
+ // If the plugin crashed and the PluginInstanceParent was deleted,
+ // aInstance->pdata will be nullptr.
+ if (!ip) {
+ return nullptr;
+ }
+
+ if (aInstance != ip->mNPP) {
+ MOZ_CRASH("Corrupted plugin data.");
+ }
+
+ return ip;
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+
+ if (!owner->GetCompositionString(aIndex, aDist, aLength)) {
+ *aLength = IMM_ERROR_NODATA;
+ }
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginInstanceParent::RecvRequestCommitOrCancel(
+ const bool& aCommitted) {
+#if defined(OS_WIN)
+ nsPluginInstanceOwner* owner = GetOwner();
+ if (owner) {
+ owner->RequestCommitOrCancel(aCommitted);
+ }
+#endif
+ return IPC_OK();
+}
+
+nsresult PluginInstanceParent::HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData, bool aIsConsumed) {
+ if (NS_WARN_IF(
+ !SendHandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed))) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+ owner->OnWindowedPluginKeyEvent(aKeyEventData);
+ return IPC_OK();
+}
+
+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 0000000000..bef637b21f
--- /dev/null
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -0,0 +1,403 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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"
+# include "mozilla/layers/LayersSurfaces.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 "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 {
+ friend class PluginModuleParent;
+ friend class BrowserStreamParent;
+ friend class StreamNotifyParent;
+ friend class PPluginInstanceParent;
+
+#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;
+
+ PPluginScriptableObjectParent* AllocPPluginScriptableObjectParent();
+
+ virtual mozilla::ipc::IPCResult RecvPPluginScriptableObjectConstructor(
+ PPluginScriptableObjectParent* aActor) override;
+
+ bool DeallocPPluginScriptableObjectParent(
+ PPluginScriptableObjectParent* aObject);
+ PBrowserStreamParent* AllocPBrowserStreamParent(
+ const nsCString& url, const uint32_t& length,
+ const uint32_t& lastmodified, PStreamNotifyParent* notifyData,
+ const nsCString& headers);
+ bool DeallocPBrowserStreamParent(PBrowserStreamParent* stream);
+
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_NPNVnetscapeWindow(
+ NativeWindowHandle* value, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_NPNVWindowNPObject(
+ PPluginScriptableObjectParent** value, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_NPNVPluginElementNPObject(
+ PPluginScriptableObjectParent** value, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_NPNVprivateModeBool(
+ bool* value, NPError* result);
+
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_DrawingModelSupport(
+ const NPNVariable& model, bool* value);
+
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_NPNVdocumentOrigin(
+ nsCString* value, NPError* result);
+
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_SupportsAsyncBitmapSurface(
+ bool* value);
+
+ static bool SupportsPluginDirectDXGISurfaceDrawing();
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_SupportsAsyncDXGISurface(
+ bool* value) {
+ *value = SupportsPluginDirectDXGISurfaceDrawing();
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult AnswerNPN_GetValue_PreferredDXGIAdapter(
+ DxgiAdapterDesc* desc);
+
+ mozilla::ipc::IPCResult AnswerNPN_SetValue_NPPVpluginWindow(
+ const bool& windowed, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPN_SetValue_NPPVpluginTransparent(
+ const bool& transparent, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPN_SetValue_NPPVpluginUsesDOMForCursor(
+ const bool& useDOMForCursor, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPN_SetValue_NPPVpluginDrawingModel(
+ const int& drawingModel, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPN_SetValue_NPPVpluginEventModel(
+ const int& eventModel, NPError* result);
+ mozilla::ipc::IPCResult AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(
+ const bool& isAudioPlaying, NPError* result);
+
+ mozilla::ipc::IPCResult AnswerNPN_GetURL(const nsCString& url,
+ const nsCString& target,
+ NPError* result);
+
+ mozilla::ipc::IPCResult AnswerNPN_PostURL(const nsCString& url,
+ const nsCString& target,
+ const nsCString& buffer,
+ const bool& file, NPError* result);
+
+ PStreamNotifyParent* AllocPStreamNotifyParent(
+ const nsCString& url, const nsCString& target, const bool& post,
+ const nsCString& buffer, const bool& file, NPError* result);
+
+ virtual mozilla::ipc::IPCResult AnswerPStreamNotifyConstructor(
+ PStreamNotifyParent* actor, const nsCString& url, const nsCString& target,
+ const bool& post, const nsCString& buffer, const bool& file,
+ NPError* result) override;
+
+ bool DeallocPStreamNotifyParent(PStreamNotifyParent* notifyData);
+
+ mozilla::ipc::IPCResult RecvNPN_InvalidateRect(const NPRect& rect);
+
+ mozilla::ipc::IPCResult RecvRevokeCurrentDirectSurface();
+
+ /**
+ * Windows async plugin rendering uses DXGI surface objects, entirely
+ * maintained by the compositor process, to back the rendering of plugins.
+ * The expected mechanics are:
+ * - The PluginInstanceChild (PIC) in the plugin process sends
+ * InitDXGISurface to us, the content process' PluginInstanceParent (PIP).
+ * - The PIP uses the ImageBridge to tell the compositor to create 2
+ * surfaces -- one to be written to by the plugin process and another
+ * to be displayed by the compositor. The PIP returns the plugin
+ * surface to the PIC.
+ * - The PIC repeatedly issues ShowDirectDXGISurface calls to tell the
+ * PIP to blit the plugin surface to the display surface. These
+ * requests are forwarded to the compositor via the ImageBridge.
+ * - The PIC sends FinalizeDXGISurface tio the PIP when it no longer needs
+ * the surface. The PIP then tells the compositor to destroy the
+ * plugin surface. After this, the PIP will tell the compositor to
+ * also destroy the display surface as soon as the ImageBridge says it
+ * no longer needs it.
+ */
+ mozilla::ipc::IPCResult RecvInitDXGISurface(const gfx::SurfaceFormat& format,
+ const gfx::IntSize& size,
+ WindowsHandle* outHandle,
+ NPError* outError);
+ mozilla::ipc::IPCResult RecvShowDirectDXGISurface(const WindowsHandle& handle,
+ const gfx::IntRect& rect);
+
+ mozilla::ipc::IPCResult RecvFinalizeDXGISurface(const WindowsHandle& handle);
+
+ // --
+
+ mozilla::ipc::IPCResult RecvShowDirectBitmap(Shmem&& buffer,
+ const gfx::SurfaceFormat& format,
+ const uint32_t& stride,
+ const gfx::IntSize& size,
+ const gfx::IntRect& dirty);
+
+ mozilla::ipc::IPCResult RecvShow(const NPRect& updatedRect,
+ const SurfaceDescriptor& newSurface,
+ SurfaceDescriptor* prevSurface);
+
+ PPluginSurfaceParent* AllocPPluginSurfaceParent(
+ const WindowsSharedMemoryHandle& handle,
+ const mozilla::gfx::IntSize& size, const bool& transparent);
+
+ bool DeallocPPluginSurfaceParent(PPluginSurfaceParent* s);
+
+ mozilla::ipc::IPCResult AnswerNPN_PushPopupsEnabledState(const bool& aState);
+
+ mozilla::ipc::IPCResult AnswerNPN_PopPopupsEnabledState();
+
+ mozilla::ipc::IPCResult AnswerNPN_GetValueForURL(
+ const NPNURLVariable& variable, const nsCString& url, nsCString* value,
+ NPError* result);
+
+ mozilla::ipc::IPCResult AnswerNPN_SetValueForURL(
+ const NPNURLVariable& variable, const nsCString& url,
+ const nsCString& value, NPError* result);
+
+ mozilla::ipc::IPCResult 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);
+
+ mozilla::ipc::IPCResult RecvRedrawPlugin();
+
+ mozilla::ipc::IPCResult RecvSetNetscapeWindowAsParent(
+ const NativeWindowHandle& childWindow);
+
+ 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; }
+
+ void GetSrcAttribute(nsACString& aOutput) const { aOutput = mSrcAttribute; }
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult AnswerPluginFocusChange(
+ const bool& gotFocus);
+
+ 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();
+
+ static PluginInstanceParent* Cast(NPP instance);
+
+ // for IME hook
+ mozilla::ipc::IPCResult RecvGetCompositionString(const uint32_t& aIndex,
+ nsTArray<uint8_t>* aBuffer,
+ int32_t* aLength);
+ mozilla::ipc::IPCResult RecvRequestCommitOrCancel(const bool& aCommitted);
+
+ // for reserved shortcut key handling with windowed plugin on Windows
+ nsresult HandledWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData, bool aIsConsumed);
+ mozilla::ipc::IPCResult RecvOnWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData);
+
+ 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();
+
+ PPluginBackgroundDestroyerParent* AllocPPluginBackgroundDestroyerParent();
+
+ bool DeallocPPluginBackgroundDestroyerParent(
+ PPluginBackgroundDestroyerParent* aActor);
+
+ 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;
+ 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;
+
+ struct AsyncSurfaceInfo {
+ layers::SurfaceDescriptorPlugin mSD;
+ gfx::IntSize mSize;
+ };
+ // Key is plugin surface's texture's handle
+ HashMap<WindowsHandle, AsyncSurfaceInfo> mAsyncSurfaceMap;
+#endif // defined(OS_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 0000000000..2f10dbd6c7
--- /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 0000000000..3f9f3ff747
--- /dev/null
+++ b/dom/plugins/ipc/PluginInterposeOSX.mm
@@ -0,0 +1,1040 @@
+/* -*- 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
+ MOZ_ASSERT_UNREACHABLE("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 0000000000..ce8cc3af17
--- /dev/null
+++ b/dom/plugins/ipc/PluginLibrary.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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 "nsStringFwd.h"
+#include "nsTArray.h"
+#include "nsError.h"
+#include "mozilla/EventForwards.h"
+#include "nsSize.h"
+#include "nsRect.h"
+
+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(nsTArray<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() = default;
+
+ /**
+ * 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)
+ 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, 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 0000000000..4d639b6f39
--- /dev/null
+++ b/dom/plugins/ipc/PluginMessageUtils.cpp
@@ -0,0 +1,141 @@
+/* -*- 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 "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)
+ : Runnable("DeferNPObjectReleaseRunnable"), mFuncs(f), mObject(o) {
+ NS_ASSERTION(o, "no release null objects");
+ }
+
+ NS_IMETHOD Run() override;
+
+ private:
+ const NPNetscapeFuncs* mFuncs;
+ NPObject* mObject;
+};
+
+NS_IMETHODIMP
+DeferNPObjectReleaseRunnable::Run() {
+ mFuncs->releaseobject(mObject);
+ return NS_OK;
+}
+
+} // namespace
+
+namespace mozilla::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) || defined(OS_SOLARIS)
+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) || defined(OS_SOLARIS)
+ // 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) || defined(OS_SOLARIS)
+ 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 mozilla::plugins
diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h
new file mode 100644
index 0000000000..c21aa10966
--- /dev/null
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -0,0 +1,662 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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/EnumSerializer.h"
+#include "base/message_loop.h"
+#include "base/shared_memory.h"
+
+#include "mozilla/ipc/CrossProcessMutex.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "gfxipc/SurfaceDescriptor.h"
+
+#include "npapi.h"
+#include "npruntime.h"
+#include "npfunctions.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/Logging.h"
+#include "nsHashKeys.h"
+
+#ifdef XP_MACOSX
+# include "PluginInterposeOSX.h"
+#else
+namespace mac_plugin_interposing {
+class NSCursorInfo {};
+} // namespace mac_plugin_interposing
+#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;
+};
+
+struct NPAudioDeviceStateChangedIPC {
+ std::wstring device;
+ uint32_t state;
+};
+
+#ifdef XP_WIN
+typedef HWND NativeWindowHandle;
+#elif defined(MOZ_X11)
+typedef XID NativeWindowHandle;
+#elif defined(XP_DARWIN) || defined(ANDROID) || defined(MOZ_WAYLAND)
+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 // XP_WIN
+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 VoidCString();
+ }
+ 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>
+ : public ContiguousEnumSerializerInclusive<
+ NPWindowType, NPWindowType::NPWindowTypeWindow,
+ NPWindowType::NPWindowTypeDrawable> {};
+
+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*> {
+ // 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, NPNSString* 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,
+ NPNSString** 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) {
+ MOZ_CRASH("NSCursorInfo isn't meaningful on this platform");
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ MOZ_CRASH("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>
+ : public ContiguousEnumSerializer<NPNVariable, NPNVariable::NPNVxDisplay,
+ NPNVariable::NPNVLast> {};
+
+// The only accepted value is NPNURLVariable::NPNURLVProxy
+template <>
+struct ParamTraits<NPNURLVariable>
+ : public ContiguousEnumSerializerInclusive<NPNURLVariable,
+ NPNURLVariable::NPNURLVProxy,
+ NPNURLVariable::NPNURLVProxy> {};
+
+template <>
+struct ParamTraits<NPCoordinateSpace>
+ : public ContiguousEnumSerializerInclusive<
+ NPCoordinateSpace, NPCoordinateSpace::NPCoordinateSpacePlugin,
+ NPCoordinateSpace::NPCoordinateSpaceFlippedScreen> {};
+
+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()));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::plugins::NPAudioDeviceStateChangedIPC> {
+ typedef mozilla::plugins::NPAudioDeviceStateChangedIPC paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.device);
+ WriteParam(aMsg, aParam.state);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ int32_t state;
+ std::wstring device;
+ if (ReadParam(aMsg, aIter, &device) && ReadParam(aMsg, aIter, &state)) {
+ aResult->device = device;
+ aResult->state = state;
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {
+ aLog->append(StringPrintf(L"[%S,%d]", aParam.device.c_str(), aParam.state));
+ }
+};
+} /* 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 0000000000..81eb8467d4
--- /dev/null
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -0,0 +1,1984 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 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>
+# include <gdk/gdkx.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/CrashReporterClient.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/plugins/PluginInstanceChild.h"
+#include "mozilla/plugins/StreamNotifyChild.h"
+#include "mozilla/plugins/BrowserStreamChild.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
+
+#include "nsNPAPIPlugin.h"
+#include "FunctionHook.h"
+#include "FunctionBrokerChild.h"
+
+#ifdef XP_WIN
+# include "mozilla/widget/AudioSession.h"
+# include <knownfolders.h>
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+# include "PluginInterposeOSX.h"
+# include "PluginUtilsOSX.h"
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+# include "ChildProfilerController.h"
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "mozilla/Sandbox.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+using namespace mozilla::plugins;
+using namespace mozilla::widget;
+
+#if defined(XP_WIN)
+const wchar_t* kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
+# if defined(MOZ_SANDBOX)
+std::wstring sRoamingPath;
+# endif
+#endif
+
+namespace {
+// see PluginModuleChild::GetChrome()
+PluginModuleChild* gChromeInstance = nullptr;
+} // namespace
+
+#ifdef XP_WIN
+// Used with fix for flash fullscreen window loosing focus.
+static bool gDelayFlashFocusReplyUntilEval = false;
+#endif
+
+/* static */
+bool PluginModuleChild::CreateForContentProcess(
+ Endpoint<PPluginModuleChild>&& aEndpoint) {
+ auto* child = new PluginModuleChild(false);
+ return child->InitForContent(std::move(aEndpoint));
+}
+
+PluginModuleChild::PluginModuleChild(bool aIsChrome)
+ : mLibrary(0),
+ mPluginFilename(""),
+ mQuirks(QUIRKS_NOT_INITIALIZED),
+ mIsChrome(aIsChrome),
+ mHasShutdown(false),
+ 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
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ ,
+ mFlashSandboxLevel(0),
+ mEnableFlashSandboxLogging(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;
+}
+
+void PluginModuleChild::CommonInit() {
+ 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);
+
+ memset((void*)&mFunctions, 0, sizeof(mFunctions));
+ mFunctions.size = sizeof(mFunctions);
+ mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+}
+
+bool PluginModuleChild::InitForContent(
+ Endpoint<PPluginModuleChild>&& aEndpoint) {
+ CommonInit();
+
+ if (!aEndpoint.Bind(this)) {
+ return false;
+ }
+
+ mLibrary = GetChrome()->mLibrary;
+ mFunctions = GetChrome()->mFunctions;
+
+ return true;
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvInitProfiler(
+ Endpoint<mozilla::PProfilerChild>&& aEndpoint) {
+#ifdef MOZ_GECKO_PROFILER
+ mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvDisableFlashProtectedMode() {
+ MOZ_ASSERT(mIsChrome);
+#ifdef XP_WIN
+ FunctionHook::HookProtectedMode();
+#else
+ MOZ_ASSERT(false, "Should not be called");
+#endif
+ return IPC_OK();
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+void PluginModuleChild::EnableFlashSandbox(int aLevel,
+ bool aShouldEnableLogging) {
+ mFlashSandboxLevel = aLevel;
+ mEnableFlashSandboxLogging = aShouldEnableLogging;
+}
+#endif
+
+#if defined(OS_WIN) && defined(MOZ_SANDBOX)
+/* static */
+void PluginModuleChild::SetFlashRoamingPath(const std::wstring& aRoamingPath) {
+ MOZ_ASSERT(sRoamingPath.empty());
+ sRoamingPath = aRoamingPath;
+}
+
+/* static */ std::wstring PluginModuleChild::GetFlashRoamingPath() {
+ return sRoamingPath;
+}
+#endif
+
+bool PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
+ base::ProcessId aParentPid,
+ MessageLoop* aIOLoop,
+ UniquePtr<IPC::Channel> aChannel) {
+ NS_ASSERTION(aChannel, "need a channel");
+
+#if defined(OS_WIN) && defined(MOZ_SANDBOX)
+ MOZ_ASSERT(!sRoamingPath.empty(),
+ "Should have already called SetFlashRoamingPath");
+#endif
+
+ 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)
+ constexpr auto flash10Head = "Shockwave Flash 10."_ns;
+ 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");
+
+ CommonInit();
+
+ if (!Open(std::move(aChannel), aParentPid, aIOLoop)) {
+ return false;
+ }
+
+ GetIPCChannel()->SetAbortOnError(true);
+
+#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
+ 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
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ if (mFlashSandboxLevel > 0) {
+ MacSandboxInfo flashSandboxInfo;
+ flashSandboxInfo.type = MacSandboxType_Flash;
+ flashSandboxInfo.pluginBinaryPath = aPluginFilename;
+ flashSandboxInfo.level = mFlashSandboxLevel;
+ flashSandboxInfo.shouldLog = mEnableFlashSandboxLogging;
+
+ std::string sbError;
+ if (!mozilla::StartMacSandbox(flashSandboxInfo, sbError)) {
+ fprintf(stderr, "Failed to start sandbox:\n%s\n", sbError.c_str());
+ return false;
+ }
+ }
+#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 = X11True;
+
+ gdk_error_trap_push();
+
+ XSendEvent(dpy, xevent.xbutton.window, X11True, ButtonPressMask, &xevent);
+
+ xevent.xbutton.type = ButtonRelease;
+ xevent.xbutton.state |= button_mask;
+ XSendEvent(dpy, xevent.xbutton.window, X11True, 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
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvSetParentHangTimeout(
+ const uint32_t& aSeconds) {
+#ifdef XP_WIN
+ SetReplyTimeoutMs(((aSeconds > 0) ? (1000 * aSeconds) : 0));
+#endif
+ return IPC_OK();
+}
+
+bool PluginModuleChild::ShouldContinueFromReplyTimeout() {
+#ifdef XP_WIN
+ MOZ_CRASH("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;
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::AnswerNP_Shutdown(NPError* rv) {
+ *rv = NP_Shutdown();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::AnswerOptionalFunctionsSupported(
+ bool* aURLRedirectNotify, bool* aClearSiteData, bool* aGetSitesWithData) {
+ *aURLRedirectNotify = !!mFunctions.urlredirectnotify;
+ *aClearSiteData = !!mFunctions.clearsitedata;
+ *aGetSitesWithData = !!mFunctions.getsiteswithdata;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvNPP_GetSitesWithData(
+ const uint64_t& aCallbackId) {
+ char** result = mFunctions.getsiteswithdata();
+ nsTArray<nsCString> array;
+ if (!result) {
+ SendReturnSitesWithData(array, aCallbackId);
+ return IPC_OK();
+ }
+ char** iterator = result;
+ while (*iterator) {
+ array.AppendElement(*iterator);
+ free(*iterator);
+ ++iterator;
+ }
+ SendReturnSitesWithData(array, aCallbackId);
+ free(result);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvSetAudioSessionData(
+ const nsID& aId, const nsString& aDisplayName, const nsString& aIconPath) {
+#if !defined XP_WIN
+ MOZ_CRASH("Not Reached!");
+#else
+ nsresult rv =
+ mozilla::widget::RecvAudioSessionData(aId, aDisplayName, aIconPath);
+ NS_ENSURE_SUCCESS(rv, IPC_OK()); // Bail early if this fails
+
+ // Ignore failures here; we can't really do anything about them
+ mozilla::widget::StartAudioSession();
+ return IPC_OK();
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvInitPluginModuleChild(
+ Endpoint<PPluginModuleChild>&& aEndpoint) {
+ if (!CreateForContentProcess(std::move(aEndpoint))) {
+ return IPC_FAIL(this, "CreateForContentProcess failed");
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvInitPluginFunctionBroker(
+ Endpoint<PFunctionBrokerChild>&& aEndpoint) {
+#if defined(XP_WIN)
+ MOZ_ASSERT(mIsChrome);
+ if (!FunctionBrokerChild::Initialize(std::move(aEndpoint))) {
+ return IPC_FAIL(
+ this, "InitPluginFunctionBroker failed to initialize broker child.");
+ }
+ return IPC_OK();
+#else
+ return IPC_FAIL(this,
+ "InitPluginFunctionBroker not supported on this platform.");
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::AnswerInitCrashReporter(
+ mozilla::dom::NativeThreadId* aOutId) {
+ CrashReporterClient::InitSingleton();
+ *aOutId = CrashReporter::CurrentThreadId();
+
+ return IPC_OK();
+}
+
+void PluginModuleChild::ActorDestroy(ActorDestroyReason why) {
+#ifdef MOZ_GECKO_PROFILER
+ if (mProfilerController) {
+ mProfilerController->Shutdown();
+ mProfilerController = nullptr;
+ }
+#endif
+
+ 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();
+ }
+
+#if defined(XP_WIN)
+ FunctionBrokerChild::Destroy();
+ FunctionHook::ClearDllInterceptorCache();
+#endif
+
+ // doesn't matter why we're being destroyed; it's up to us to
+ // initiate (clean) shutdown
+ CrashReporterClient::DestroySingleton();
+
+ 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::plugins::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 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 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 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 mozilla::plugins::child
+
+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,
+ nullptr, // _newstream, unimplemented
+ nullptr, // _write, unimplemented
+ nullptr, // _destroystream, unimplemented
+ 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,
+ nullptr, // pluginthreadasynccall, not used
+ mozilla::plugins::child::_construct,
+ mozilla::plugins::child::_getvalueforurl,
+ mozilla::plugins::child::_setvalueforurl,
+ nullptr, // NPN GetAuthenticationInfo, not supported
+ 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::plugins::child {
+
+NPError _requestread(NPStream* aStream, NPByteRange* aRangeList) {
+ return NPERR_STREAM_NOT_SEEKABLE;
+}
+
+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);
+ auto* sn = new StreamNotifyChild(url);
+
+ NPError err;
+ if (!InstCast(aNPP)->CallPStreamNotifyConstructor(
+ sn, url, NullableString(aTarget), false, nsCString(), false, &err)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ 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:
+ // We don't support windowed xembed any more. But we still deliver
+ // events based on X/GTK, not Xt, so we continue to return true
+ // (and Flash requires that we return true).
+ *(NPBool*)aValue = true;
+ return NPERR_NO_ERROR;
+ case NPNVSupportsWindowless:
+ *(NPBool*)aValue = true;
+ return NPERR_NO_ERROR;
+#if defined(MOZ_WIDGET_GTK)
+ case NPNVxDisplay: {
+ if (!aNPP) {
+ return NPERR_INVALID_INSTANCE_ERROR;
+ }
+ return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
+ }
+ 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;
+ }
+ }
+
+ MOZ_ASSERT_UNREACHABLE("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;
+
+ if (aIsFile) {
+ PLUGIN_LOG_DEBUG(
+ ("NPN_PostURLNotify with file=true is no longer supported"));
+ return NPERR_GENERIC_ERROR;
+ }
+
+ nsCString url = NullableString(aRelativeURL);
+ auto* sn = new StreamNotifyChild(url);
+
+ NPError err;
+ if (!InstCast(aNPP)->CallPStreamNotifyConstructor(
+ sn, url, NullableString(aTarget), true, nsCString(aBuffer, aLength),
+ aIsFile, &err)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ 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);
+
+ if (aIsFile) {
+ PLUGIN_LOG_DEBUG(("NPN_PostURL with file=true is no longer supported"));
+ return NPERR_GENERIC_ERROR;
+ }
+ 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;
+}
+
+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();
+}
+
+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;
+
+ if (variable == 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;
+
+ if (variable == NPNURLVProxy) {
+ NPError result;
+ InstCast(npp)->CallNPN_SetValueForURL(
+ variable, nsCString(url), nsDependentCString(value, len), &result);
+ return result;
+ }
+
+ return NPERR_INVALID_PARAM;
+}
+
+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 mozilla::plugins::child
+
+//-----------------------------------------------------------------------------
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvSettingChanged(
+ const PluginSettings& aSettings) {
+ mCachedSettings = aSettings;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::AnswerNP_GetEntryPoints(
+ NPError* _retval) {
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+ MOZ_ASSERT(mIsChrome);
+
+#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
+ return IPC_OK();
+#elif defined(OS_WIN) || defined(OS_MACOSX)
+ *_retval = mGetEntryPointsFunc(&mFunctions);
+ return IPC_OK();
+#else
+# error Please implement me for your platform
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::AnswerNP_Initialize(
+ const PluginSettings& aSettings, NPError* rv) {
+ *rv = DoNP_Initialize(aSettings);
+ return IPC_OK();
+}
+
+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
+# ifdef MOZ_WIDGET_GTK
+ if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+ // We don't support NPAPI plugins on Wayland.
+ return NPERR_GENERIC_ERROR;
+ }
+# endif
+ // 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) || defined(OS_SOLARIS)
+ 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;
+}
+
+PPluginInstanceChild* PluginModuleChild::AllocPPluginInstanceChild(
+ const nsCString& aMimeType, const nsTArray<nsCString>& aNames,
+ const nsTArray<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
+ FunctionHook::HookFunctions(mQuirks);
+#endif
+
+ return new PluginInstanceChild(&mFunctions, aMimeType, aNames, aValues);
+}
+
+void PluginModuleChild::InitQuirksModes(const nsCString& aMimeType) {
+ if (mQuirks != QUIRKS_NOT_INITIALIZED) {
+ return;
+ }
+
+ mQuirks = GetQuirksFromMimeTypeAndFilename(aMimeType, mPluginFilename);
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::AnswerModuleSupportsAsyncRender(
+ bool* aResult) {
+#if defined(XP_WIN)
+ *aResult = gChromeInstance->mAsyncRenderSupport;
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE("Shouldn't get here!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::RecvPPluginInstanceConstructor(
+ PPluginInstanceChild* aActor, const nsCString& aMimeType,
+ nsTArray<nsCString>&& aNames, nsTArray<nsCString>&& aValues) {
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+
+ NS_ASSERTION(aActor, "Null actor!");
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleChild::AnswerSyncNPP_New(
+ PPluginInstanceChild* aActor, NPError* rv) {
+ PLUGIN_LOG_DEBUG_METHOD;
+ PluginInstanceChild* childInstance =
+ reinterpret_cast<PluginInstanceChild*>(aActor);
+ AssertPluginThread();
+ *rv = childInstance->DoNPP_New();
+ return IPC_OK();
+}
+
+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();
+
+ if (NS_WARN_IF(!aNPObj)) {
+ return nullptr;
+ }
+
+#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) {
+ // The PluginInstanceChild was destroyed
+ 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;
+ }
+}
+
+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)) {
+ MOZ_CRASH("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");
+ const IncallFrame& f = mIncallPumpingStack.LastElement();
+ if (f._spinning)
+ MessageLoop::current()->SetNestableTasksAllowed(
+ f._savedNestableTasksAllowed);
+
+ // XXX Is RemoveLastElement intentionally called only after calling
+ // SetNestableTasksAllowed? Otherwise, PopLastElement could be used above.
+ mIncallPumpingStack.RemoveLastElement();
+}
+
+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
+
+mozilla::ipc::IPCResult
+PluginModuleChild::RecvProcessNativeEventsInInterruptCall() {
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(OS_WIN)
+ ProcessNativeEventsInInterruptCall();
+ return IPC_OK();
+#else
+ MOZ_CRASH(
+ "PluginModuleChild::RecvProcessNativeEventsInInterruptCall not "
+ "implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+#ifdef MOZ_WIDGET_COCOA
+void PluginModuleChild::ProcessNativeEvents() { CallProcessSomeEvents(); }
+#endif
+
+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
+ MOZ_CRASH(
+ "PluginRequiresAudioDeviceChanges is not available on this platform.");
+#endif // XP_WIN
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+#else
+ MOZ_CRASH(
+ "NPP_SetValue_NPNVaudioDeviceChangeDetails is a Windows-only message");
+#endif
+}
+
+mozilla::ipc::IPCResult
+PluginModuleChild::RecvNPP_SetValue_NPNVaudioDeviceStateChanged(
+ const NPAudioDeviceStateChangedIPC& aDeviceStateIPC) {
+#if defined(XP_WIN)
+ NPAudioDeviceStateChanged stateChange;
+ stateChange.newState = aDeviceStateIPC.state;
+ stateChange.device = aDeviceStateIPC.device.c_str();
+ for (auto iter = mAudioNotificationSet.ConstIter(); !iter.Done();
+ iter.Next()) {
+ PluginInstanceChild* pluginInst = iter.Get()->GetKey();
+ pluginInst->AudioDeviceStateChanged(stateChange);
+ }
+ return IPC_OK();
+#else
+ MOZ_CRASH("NPP_SetValue_NPNVaudioDeviceRemoved is a Windows-only message");
+#endif
+}
diff --git a/dom/plugins/ipc/PluginModuleChild.h b/dom/plugins/ipc/PluginModuleChild.h
new file mode 100644
index 0000000000..31d4eafb8f
--- /dev/null
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -0,0 +1,345 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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"
+
+#if defined(MOZ_WIDGET_GTK)
+# include <glib.h>
+#endif
+
+namespace mozilla {
+
+class ChildProfilerController;
+
+namespace plugins {
+
+class PluginInstanceChild;
+
+class PluginModuleChild : public PPluginModuleChild {
+ friend class PPluginModuleChild;
+
+ protected:
+ virtual mozilla::ipc::RacyInterruptPolicy MediateInterruptRace(
+ const MessageInfo& parent, const MessageInfo& child) override {
+ return MediateRace(parent, child);
+ }
+
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+ mozilla::ipc::IPCResult RecvSettingChanged(const PluginSettings& aSettings);
+
+ // Implement the PPluginModuleChild interface
+ mozilla::ipc::IPCResult RecvInitProfiler(
+ Endpoint<mozilla::PProfilerChild>&& aEndpoint);
+ mozilla::ipc::IPCResult RecvDisableFlashProtectedMode();
+ mozilla::ipc::IPCResult AnswerNP_GetEntryPoints(NPError* rv);
+ mozilla::ipc::IPCResult AnswerNP_Initialize(const PluginSettings& aSettings,
+ NPError* rv);
+ mozilla::ipc::IPCResult AnswerSyncNPP_New(PPluginInstanceChild* aActor,
+ NPError* rv);
+
+ mozilla::ipc::IPCResult RecvInitPluginModuleChild(
+ Endpoint<PPluginModuleChild>&& endpoint);
+
+ mozilla::ipc::IPCResult RecvInitPluginFunctionBroker(
+ Endpoint<PFunctionBrokerChild>&& endpoint);
+
+ PPluginInstanceChild* AllocPPluginInstanceChild(
+ const nsCString& aMimeType, const nsTArray<nsCString>& aNames,
+ const nsTArray<nsCString>& aValues);
+
+ bool DeallocPPluginInstanceChild(PPluginInstanceChild* aActor);
+
+ mozilla::ipc::IPCResult RecvPPluginInstanceConstructor(
+ PPluginInstanceChild* aActor, const nsCString& aMimeType,
+ nsTArray<nsCString>&& aNames, nsTArray<nsCString>&& aValues) override;
+ mozilla::ipc::IPCResult AnswerNP_Shutdown(NPError* rv);
+
+ mozilla::ipc::IPCResult AnswerOptionalFunctionsSupported(
+ bool* aURLRedirectNotify, bool* aClearSiteData, bool* aGetSitesWithData);
+
+ mozilla::ipc::IPCResult RecvNPP_ClearSiteData(const nsCString& aSite,
+ const uint64_t& aFlags,
+ const uint64_t& aMaxAge,
+ const uint64_t& aCallbackId);
+
+ mozilla::ipc::IPCResult RecvNPP_GetSitesWithData(const uint64_t& aCallbackId);
+
+ mozilla::ipc::IPCResult RecvSetAudioSessionData(const nsID& aId,
+ const nsString& aDisplayName,
+ const nsString& aIconPath);
+
+ mozilla::ipc::IPCResult RecvSetParentHangTimeout(const uint32_t& aSeconds);
+
+ mozilla::ipc::IPCResult AnswerInitCrashReporter(
+ mozilla::dom::NativeThreadId* aId);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ mozilla::ipc::IPCResult RecvProcessNativeEventsInInterruptCall();
+
+ mozilla::ipc::IPCResult AnswerModuleSupportsAsyncRender(bool* aResult);
+
+ public:
+ explicit PluginModuleChild(bool aIsChrome);
+ virtual ~PluginModuleChild();
+
+ void CommonInit();
+
+#if defined(OS_WIN) && defined(MOZ_SANDBOX)
+ // Path to the roaming Flash Player folder. This is used to restore some
+ // behavior blocked by the sandbox.
+ static void SetFlashRoamingPath(const std::wstring& aRoamingPath);
+ static std::wstring GetFlashRoamingPath();
+#endif
+
+ // aPluginFilename is UTF8, not native-charset!
+ bool InitForChrome(const std::string& aPluginFilename,
+ base::ProcessId aParentPid, MessageLoop* aIOLoop,
+ UniquePtr<IPC::Channel> aChannel);
+
+ bool InitForContent(Endpoint<PPluginModuleChild>&& aEndpoint);
+
+ static bool CreateForContentProcess(Endpoint<PPluginModuleChild>&& aEndpoint);
+
+ 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);
+ mozilla::ipc::IPCResult RecvNPP_SetValue_NPNVaudioDeviceChangeDetails(
+ const NPAudioDeviceChangeDetailsIPC& detailsIPC);
+ mozilla::ipc::IPCResult RecvNPP_SetValue_NPNVaudioDeviceStateChanged(
+ const NPAudioDeviceStateChangedIPC& aDeviceStateIPC);
+
+ 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(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
+
+#ifdef MOZ_GECKO_PROFILER
+ RefPtr<ChildProfilerController> mProfilerController;
+#endif
+
+ // we get this from the plugin
+ NP_PLUGINSHUTDOWN mShutdownFunc;
+#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
+ 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);
+ }
+
+#if defined(OS_MACOSX) && defined(MOZ_SANDBOX)
+ void EnableFlashSandbox(int aLevel, bool aShouldEnableLogging);
+#endif
+
+ private:
+#if defined(OS_MACOSX) && defined(MOZ_SANDBOX)
+ int mFlashSandboxLevel;
+ bool mEnableFlashSandboxLogging;
+#endif
+
+#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 100644
index 0000000000..6743ed1893
--- /dev/null
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -0,0 +1,2541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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/AutoRestore.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/ipc/CrashReporterHost.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/plugins/BrowserStreamParent.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/plugins/PluginInstanceParent.h"
+#include "mozilla/plugins/PPluginBackgroundDestroyerParent.h"
+#include "mozilla/plugins/PStreamNotifyParent.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/Services.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsICrashService.h"
+#include "nsIObserverService.h"
+#include "nsNPAPIPlugin.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsPrintfCString.h"
+#include "prsystem.h"
+#include "prclist.h"
+#include "PluginQuirks.h"
+#include "gfxPlatform.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 "FunctionBrokerParent.h"
+# include "PluginUtilsWin.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+# include <glib.h>
+#elif XP_MACOSX
+# include "PluginInterposeOSX.h"
+# include "PluginUtilsOSX.h"
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+# include "ProfilerParent.h"
+#endif
+
+using base::KillProcess;
+
+using mozilla::PluginLibrary;
+using mozilla::ipc::GeckoChildProcessHost;
+using mozilla::ipc::MessageChannel;
+
+using namespace mozilla;
+using namespace mozilla::plugins;
+using namespace mozilla::plugins::parent;
+
+using namespace CrashReporter;
+
+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";
+#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, nsresult* rv,
+ uint32_t* runID, ipc::Endpoint<PPluginModuleParent>* aEndpoint) {
+ AUTO_PROFILER_LABEL("plugins::SetupBridge", OTHER);
+ if (NS_WARN_IF(!rv) || NS_WARN_IF(!runID)) {
+ return false;
+ }
+
+ 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());
+ *rv = chromeParent->GetRunID(runID);
+ if (NS_FAILED(*rv)) {
+ return true;
+ }
+
+ ipc::Endpoint<PPluginModuleParent> parent;
+ ipc::Endpoint<PPluginModuleChild> child;
+
+ *rv = PPluginModule::CreateEndpoints(
+ aContentParent->OtherPid(), chromeParent->OtherPid(), &parent, &child);
+ if (NS_FAILED(*rv)) {
+ return true;
+ }
+
+ *aEndpoint = std::move(parent);
+
+ if (!chromeParent->SendInitPluginModuleChild(std::move(child))) {
+ *rv = NS_ERROR_BRIDGE_OPEN_CHILD;
+ return true;
+ }
+
+ return true;
+}
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+
+/**
+ * Use for executing CreateToolhelp32Snapshot off main thread
+ */
+class mozilla::plugins::FinishInjectorInitTask
+ : public mozilla::CancelableRunnable {
+ public:
+ FinishInjectorInitTask()
+ : CancelableRunnable("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 an error occurs in the
+ * plugin initialization sequence.
+ */
+class PluginModuleMapping : public PRCList {
+ public:
+ explicit PluginModuleMapping(uint32_t aPluginId)
+ : mPluginId(aPluginId),
+ mProcessIdValid(false),
+ mProcessId(0),
+ 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();
+ }
+ 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;
+ }
+
+ class MOZ_RAII NotifyLoadingModule {
+ public:
+ explicit NotifyLoadingModule() {
+ PluginModuleMapping::sIsLoadModuleOnStack = true;
+ }
+
+ ~NotifyLoadingModule() {
+ PluginModuleMapping::sIsLoadModuleOnStack = false;
+ }
+
+ private:
+ };
+
+ private:
+ void AssociateWithProcessId(base::ProcessId aProcessId) {
+ MOZ_ASSERT(!mProcessIdValid);
+ mProcessId = aProcessId;
+ mProcessIdValid = true;
+ }
+
+ uint32_t mPluginId;
+ 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;
+ UniquePtr<PluginModuleMapping> mapping(new PluginModuleMapping(aPluginId));
+
+ 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;
+ Endpoint<PPluginModuleParent> endpoint;
+ if (!cp->SendLoadPlugin(aPluginId, &rv, &runID, &endpoint) || NS_FAILED(rv)) {
+ return nullptr;
+ }
+ Initialize(std::move(endpoint));
+
+ PluginModuleContentParent* parent = mapping->GetModule();
+ MOZ_ASSERT(parent);
+
+ if (!mapping->IsChannelOpened()) {
+ // mapping is linked into PluginModuleMapping::sModuleListHead and is
+ // needed later, so since this function is returning successfully we
+ // forget it here.
+ Unused << mapping.release();
+ }
+
+ parent->mPluginId = aPluginId;
+ parent->mRunID = runID;
+
+ return parent;
+}
+
+/* static */
+void PluginModuleContentParent::Initialize(
+ Endpoint<PPluginModuleParent>&& aEndpoint) {
+ UniquePtr<PluginModuleMapping> moduleMapping(
+ PluginModuleMapping::Resolve(aEndpoint.OtherPid()));
+ MOZ_ASSERT(moduleMapping);
+ PluginModuleContentParent* parent = moduleMapping->GetModule();
+ MOZ_ASSERT(parent);
+
+ DebugOnly<bool> ok = aEndpoint.Bind(parent);
+ MOZ_ASSERT(ok);
+
+ moduleMapping->SetChannelOpened();
+
+ if (XRE_UseNativeEventProcessing()) {
+ // If we're processing native events in our message pump, 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.
+ Unused << moduleMapping.release();
+}
+
+// static
+PluginLibrary* PluginModuleChromeParent::LoadModule(const char* aFilePath,
+ uint32_t aPluginId,
+ nsPluginTag* aPluginTag) {
+ PLUGIN_LOG_DEBUG_FUNCTION;
+
+ UniquePtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(
+ aFilePath, aPluginId, aPluginTag->mSandboxLevel));
+ UniquePtr<LaunchCompleteTask> onLaunchedRunnable(
+ new LaunchedTask(parent.get()));
+ bool launched = parent->mSubprocess->Launch(
+ std::move(onLaunchedRunnable), aPluginTag->mSandboxLevel,
+ aPluginTag->mIsSandboxLoggingEnabled);
+ 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;
+ int32_t launchTimeoutSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
+ if (!parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000)) {
+ parent->mShutdown = true;
+ return nullptr;
+ }
+
+#if defined(XP_WIN)
+ Endpoint<PFunctionBrokerParent> brokerParentEnd;
+ Endpoint<PFunctionBrokerChild> brokerChildEnd;
+ rv = PFunctionBroker::CreateEndpoints(base::GetCurrentProcId(),
+ parent->OtherPid(), &brokerParentEnd,
+ &brokerChildEnd);
+ if (NS_FAILED(rv)) {
+ parent->mShutdown = true;
+ return nullptr;
+ }
+
+ parent->mBrokerParent =
+ FunctionBrokerParent::Create(std::move(brokerParentEnd));
+ if (parent->mBrokerParent) {
+ Unused << parent->SendInitPluginFunctionBroker(std::move(brokerChildEnd));
+ }
+#endif
+ return parent.release();
+}
+
+static const char* gCallbackPrefs[] = {
+ kChildTimeoutPref,
+ kParentTimeoutPref,
+#ifdef XP_WIN
+ kHangUITimeoutPref,
+ kHangUIMinDisplayPref,
+#endif
+ nullptr,
+};
+
+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 (mShutdown) {
+ return;
+ }
+
+ Open(mSubprocess->TakeChannel(),
+ 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::RegisterCallbacks(TimeoutChanged, gCallbackPrefs,
+ static_cast<PluginModuleParent*>(this));
+
+ RegisterSettingsCallbacks();
+
+ // If this fails, we're having IPC troubles, and we're doomed anyways.
+ if (!InitCrashReporter()) {
+ mShutdown = true;
+ Close();
+ OnInitFailure();
+ return;
+ }
+
+#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)) {
+ Unused << SendDisableFlashProtectedMode();
+ }
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+ Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
+#endif
+}
+
+bool PluginModuleChromeParent::InitCrashReporter() {
+ NativeThreadId threadId;
+ if (!CallInitCrashReporter(&threadId)) {
+ return false;
+ }
+
+ {
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+ mCrashReporter =
+ MakeUnique<ipc::CrashReporterHost>(GeckoProcessType_Plugin, threadId);
+ }
+
+ return true;
+}
+
+PluginModuleParent::PluginModuleParent(bool aIsChrome)
+ : 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),
+ mRunID(0),
+ mCrashReporterMutex("PluginModuleChromeParent::mCrashReporterMutex") {}
+
+PluginModuleParent::~PluginModuleParent() {
+ if (!OkToCleanup()) {
+ MOZ_CRASH("unsafe destruction");
+ }
+
+ if (!mShutdown) {
+ NS_WARNING("Plugin host deleted the module without shutting down.");
+ NPError err;
+ NP_Shutdown(&err);
+ }
+}
+
+PluginModuleContentParent::PluginModuleContentParent()
+ : PluginModuleParent(false), mPluginId(0) {
+ Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref,
+ static_cast<PluginModuleParent*>(this));
+}
+
+PluginModuleContentParent::~PluginModuleContentParent() {
+ Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref,
+ static_cast<PluginModuleParent*>(this));
+}
+
+PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
+ uint32_t aPluginId,
+ int32_t aSandboxLevel)
+ : PluginModuleParent(true),
+ mSubprocess(new PluginProcessParent(aFilePath)),
+ mPluginId(aPluginId),
+ mChromeTaskFactory(this),
+ mHangAnnotationFlags(0)
+#ifdef XP_WIN
+ ,
+ mPluginCpuUsageOnHang(),
+ mHangUIParent(nullptr),
+ mHangUIEnabled(true),
+ mIsTimerReset(true),
+ mBrokerParent(nullptr)
+#endif
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ ,
+ mFlashProcess1(0),
+ mFlashProcess2(0),
+ mFinishInitTask(nullptr)
+#endif
+ ,
+ mIsBlocklisted(false),
+ mIsCleaningFromTimeout(false) {
+ NS_ASSERTION(mSubprocess, "Out of memory!");
+ mSandboxLevel = aSandboxLevel;
+ mRunID = GeckoChildProcessHost::GetUniqueID();
+
+ mozilla::BackgroundHangMonitor::RegisterAnnotator(*this);
+}
+
+PluginModuleChromeParent::~PluginModuleChromeParent() {
+ if (!OkToCleanup()) {
+ MOZ_CRASH("unsafe destruction");
+ }
+
+#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->Destroy();
+ 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::UnregisterCallbacks(TimeoutChanged, gCallbackPrefs,
+ static_cast<PluginModuleParent*>(this));
+
+#ifdef XP_WIN
+ if (mHangUIParent) {
+ delete mHangUIParent;
+ mHangUIParent = nullptr;
+ }
+#endif
+
+ mozilla::BackgroundHangMonitor::UnregisterAnnotator(*this);
+}
+
+void PluginModuleChromeParent::AddCrashAnnotations() {
+ // mCrashReporterMutex is already held by the caller
+ mCrashReporterMutex.AssertCurrentThreadOwns();
+
+ typedef nsDependentCString cstring;
+
+ // 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++;
+ mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginFilename,
+ cstring(pluginFile.substr(filePos).c_str()));
+ mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginName,
+ mPluginName);
+ mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginVersion,
+ mPluginVersion);
+
+ if (mCrashReporter) {
+#ifdef XP_WIN
+ if (mPluginCpuUsageOnHang.Length() > 0) {
+ nsCString cpuUsageStr;
+ cpuUsageStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[0] * 100) / 100);
+ mCrashReporter->AddAnnotation(CrashReporter::Annotation::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);
+ // HACK: There can only be at most two flash processes hence
+ // the hardcoded annotations
+ CrashReporter::Annotation annotation =
+ (i == 1) ? CrashReporter::Annotation::CpuUsageFlashProcess1
+ : CrashReporter::Annotation::CpuUsageFlashProcess2;
+ mCrashReporter->AddAnnotation(annotation, tempStr);
+ }
+# endif
+ }
+#endif
+ }
+}
+
+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) {
+ auto 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;
+ }
+
+ // Avoid recursively calling this method. MessageChannel::Close() can
+ // cause this task to be re-launched.
+ if (mIsCleaningFromTimeout) {
+ return;
+ }
+
+ AutoRestore<bool> resetCleaningFlag(mIsCleaningFromTimeout);
+ mIsCleaningFromTimeout = true;
+
+ /* 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 nsTArray<base::ProcessHandle>& processHandles,
+ nsTArray<float>& cpuUsage) {
+ nsTArray<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->GetProtocolId()) {
+ 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 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 BackgroundHangMonitor thread.
+ */
+void PluginModuleChromeParent::AnnotateHang(
+ mozilla::BackgroundHangAnnotations& 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 hang stack that we're in a plugin
+ call when the hang occurred. */
+ if (flags & kHangUIShown) {
+ aAnnotations.AddAnnotation(u"HangUIShown"_ns, true);
+ }
+ if (flags & kHangUIContinued) {
+ aAnnotations.AddAnnotation(u"HangUIContinued"_ns, true);
+ }
+ if (flags & kHangUIDontShow) {
+ aAnnotations.AddAnnotation(u"HangUIDontShow"_ns, true);
+ }
+ aAnnotations.AddAnnotation(u"pluginName"_ns, mPluginName);
+ aAnnotations.AddAnnotation(u"pluginVersion"_ns, mPluginVersion);
+ }
+}
+
+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);
+}
+
+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,
+ "ModalHangUI"_ns, u""_ns);
+ 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) {
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+
+ if (!mCrashReporter) {
+ 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.
+ nsCOMPtr<nsIFile> browserDumpFile;
+ if (CrashReporter::GetMinidumpForID(aBrowserDumpId,
+ getter_AddRefs(browserDumpFile))) {
+ // We have a single browser report, generate a new plugin process parent
+ // report and pair it up with the browser report handed in.
+ reportsReady = mCrashReporter->GenerateMinidumpAndPair(
+ this, browserDumpFile, "browser"_ns);
+
+ 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 = mCrashReporter->GenerateMinidumpAndPair(
+ this,
+ nullptr, // Pair with a dump of this process and thread.
+ "browser"_ns);
+ }
+
+ if (reportsReady) {
+ aDumpId = mCrashReporter->MinidumpID();
+ PLUGIN_LOG_DEBUG(("generated paired browser/plugin minidumps: %s)",
+ NS_ConvertUTF16toUTF8(aDumpId).get()));
+ nsAutoCString additionalDumps("browser");
+ nsCOMPtr<nsIFile> pluginDumpFile;
+ if (GetMinidumpForID(aDumpId, getter_AddRefs(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,
+ "flash1"_ns)) {
+ additionalDumps.AppendLiteral(",flash1");
+ }
+ if (CreatePluginMinidump(mFlashProcess2, 0, pluginDumpFile,
+ "flash2"_ns)) {
+ additionalDumps.AppendLiteral(",flash2");
+ }
+#endif // MOZ_CRASHREPORTER_INJECTOR
+ if (aContentPid != mozilla::ipc::kInvalidProcessId) {
+ // Include the content process minidump
+ if (CreatePluginMinidump(aContentPid, 0, pluginDumpFile,
+ "content"_ns)) {
+ additionalDumps.AppendLiteral(",content");
+ }
+ }
+ }
+ mCrashReporter->AddAnnotation(Annotation::additional_minidumps,
+ additionalDumps);
+ } else {
+ NS_WARNING("failed to capture paired minidumps from hang");
+ }
+}
+
+void PluginModuleChromeParent::TerminateChildProcess(
+ MessageLoop* aMsgLoop, base::ProcessId aContentPid,
+ const nsCString& aMonitorDescription, const nsAString& aDumpId) {
+ // 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 recursive locking.
+ nsAutoString dumpId;
+ if (aDumpId.IsEmpty()) {
+ TakeFullMinidump(aContentPid, u""_ns, dumpId);
+ }
+
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+ if (!mCrashReporter) {
+ // If mCrashReporter is null then the hang has ended, the plugin module
+ // is shutting down. There's nothing to do here.
+ return;
+ }
+ mCrashReporter->AddAnnotation(Annotation::PluginHang, true);
+ mCrashReporter->AddAnnotation(Annotation::HangMonitorDescription,
+ aMonitorDescription);
+#ifdef XP_WIN
+ if (mHangUIParent) {
+ unsigned int hangUIDuration = mHangUIParent->LastShowDurationMs();
+ if (hangUIDuration) {
+ mCrashReporter->AddAnnotation(Annotation::PluginHangUIDuration,
+ hangUIDuration);
+ }
+ }
+#endif // XP_WIN
+
+ mozilla::ipc::ScopedProcessHandle geckoChildProcess;
+ bool childOpened =
+ base::OpenProcessHandle(OtherPid(), &geckoChildProcess.rwget());
+
+#ifdef XP_WIN
+ // collect cpu usage for plugin processes
+
+ nsTArray<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_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() {
+ mozilla::MutexAutoLock lock(mCrashReporterMutex);
+
+ if (!mCrashReporter) {
+ HandleOrphanedMinidump();
+ return;
+ }
+
+ AddCrashAnnotations();
+
+ if (mCrashReporter->HasMinidump()) {
+ // A minidump 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.
+ mCrashReporter->FinalizeCrashReport();
+ return;
+ }
+
+ AnnotationTable annotations;
+ uint32_t sequence = UINT32_MAX;
+ nsAutoCString flashProcessType;
+ RefPtr<nsIFile> dumpFile =
+ mCrashReporter->TakeCrashedChildMinidump(OtherPid(), &sequence);
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ nsCOMPtr<nsIFile> childDumpFile;
+ uint32_t childSequence;
+
+ if (mFlashProcess1 &&
+ TakeMinidumpForChild(mFlashProcess1, getter_AddRefs(childDumpFile),
+ annotations, &childSequence)) {
+ if (childSequence < sequence &&
+ mCrashReporter->AdoptMinidump(childDumpFile, annotations)) {
+ RemoveMinidump(dumpFile);
+ dumpFile = childDumpFile;
+ sequence = childSequence;
+ flashProcessType.AssignLiteral("Broker");
+ } else {
+ RemoveMinidump(childDumpFile);
+ }
+ }
+ if (mFlashProcess2 &&
+ TakeMinidumpForChild(mFlashProcess2, getter_AddRefs(childDumpFile),
+ annotations, &childSequence)) {
+ if (childSequence < sequence &&
+ mCrashReporter->AdoptMinidump(childDumpFile, annotations)) {
+ 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(mCrashReporter->MinidumpID()).get()));
+
+ if (!flashProcessType.IsEmpty()) {
+ mCrashReporter->AddAnnotation(Annotation::FlashProcessDump,
+ flashProcessType);
+ }
+ mCrashReporter->FinalizeCrashReport();
+}
+
+void PluginModuleChromeParent::HandleOrphanedMinidump() {
+ if (CrashReporter::FinalizeOrphanedMinidump(
+ OtherPid(), GeckoProcessType_Plugin, &mOrphanedDumpId)) {
+ ipc::CrashReporterHost::RecordCrash(GeckoProcessType_Plugin,
+ nsICrashService::CRASH_TYPE_CRASH,
+ mOrphanedDumpId);
+ } else {
+ NS_WARNING(nsPrintfCString("plugin process pid = %d crashed without "
+ "leaving a minidump behind",
+ OtherPid())
+ .get());
+ }
+}
+
+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:
+ MOZ_CRASH("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) {
+ ProcessFirstMinidump();
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, "plugin"_ns, 1);
+ }
+
+ // We can't broadcast settings changes anymore.
+ UnregisterSettingsCallbacks();
+
+#if defined(XP_WIN)
+ if (mBrokerParent) {
+ FunctionBrokerParent::Destroy(mBrokerParent);
+ mBrokerParent = nullptr;
+ }
+#endif
+
+ 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) {
+ return;
+ }
+
+ nsString dumpID;
+ nsCString additionalMinidumps;
+
+ if (mCrashReporter && mCrashReporter->HasMinidump()) {
+ dumpID = mCrashReporter->MinidumpID();
+ additionalMinidumps = mCrashReporter->AdditionalMinidumps();
+ } else {
+ dumpID = mOrphanedDumpId;
+ }
+
+ mPlugin->PluginCrashed(dumpID, additionalMinidumps);
+}
+
+PPluginInstanceParent* PluginModuleParent::AllocPPluginInstanceParent(
+ const nsCString& aMimeType, const nsTArray<nsCString>& aNames,
+ const nsTArray<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->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;
+ }
+}
+
+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;
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ if (!pip) return NPERR_NO_ERROR;
+
+ NPError retval = pip->Destroy();
+ instance->pdata = nullptr;
+
+ Unused << PluginInstanceParent::Send__delete__(pip);
+ return retval;
+}
+
+NPError PluginModuleParent::NPP_NewStream(NPP instance, NPMIMEType type,
+ NPStream* stream, NPBool seekable,
+ uint16_t* stype) {
+ AUTO_PROFILER_LABEL("PluginModuleParent::NPP_NewStream", OTHER);
+
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_NewStream(type, stream, seekable, stype)
+ : NPERR_GENERIC_ERROR;
+}
+
+NPError PluginModuleParent::NPP_SetWindow(NPP instance, NPWindow* window) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_SetWindow(window) : NPERR_GENERIC_ERROR;
+}
+
+NPError PluginModuleParent::NPP_DestroyStream(NPP instance, NPStream* stream,
+ NPReason reason) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_DestroyStream(stream, reason) : NPERR_GENERIC_ERROR;
+}
+
+int32_t PluginModuleParent::NPP_WriteReady(NPP instance, NPStream* stream) {
+ BrowserStreamParent* s = StreamCast(instance, stream);
+ return s ? s->WriteReady() : -1;
+}
+
+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_Print(NPP instance, NPPrint* platformPrint) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_Print(platformPrint) : (void)0;
+}
+
+int16_t PluginModuleParent::NPP_HandleEvent(NPP instance, void* event) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_HandleEvent(event) : NPERR_GENERIC_ERROR;
+}
+
+void PluginModuleParent::NPP_URLNotify(NPP instance, const char* url,
+ NPReason reason, void* notifyData) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_URLNotify(url, reason, notifyData) : (void)0;
+}
+
+NPError PluginModuleParent::NPP_GetValue(NPP instance, NPPVariable variable,
+ void* ret_value) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_GetValue(variable, ret_value) : NPERR_GENERIC_ERROR;
+}
+
+NPError PluginModuleParent::NPP_SetValue(NPP instance, NPNVariable variable,
+ void* value) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_SetValue(variable, value) : NPERR_GENERIC_ERROR;
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+#else
+ MOZ_CRASH(
+ "NPPVpluginRequiresAudioDeviceChanges is not valid on this platform.");
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvBackUpXResources(
+ const FileDescriptor& aXSocketFd) {
+#ifndef MOZ_X11
+ MOZ_CRASH("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 IPC_OK();
+}
+
+void PluginModuleParent::NPP_URLRedirectNotify(NPP instance, const char* url,
+ int32_t status,
+ void* notifyData) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->NPP_URLRedirectNotify(url, status, notifyData) : (void)0;
+}
+
+BrowserStreamParent* PluginModuleParent::StreamCast(NPP instance, NPStream* s) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ if (!pip) {
+ return nullptr;
+ }
+
+ BrowserStreamParent* sp =
+ static_cast<BrowserStreamParent*>(static_cast<AStream*>(s->pdata));
+ if (sp && (sp->mNPP != pip || s != sp->mStream)) {
+ MOZ_CRASH("Corrupted plugin stream data.");
+ }
+ return sp;
+}
+
+bool PluginModuleParent::HasRequiredFunctions() { return true; }
+
+nsresult PluginModuleParent::AsyncSetWindow(NPP instance, NPWindow* window) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->AsyncSetWindow(window) : NS_ERROR_FAILURE;
+}
+
+nsresult PluginModuleParent::GetImageContainer(
+ NPP instance, mozilla::layers::ImageContainer** aContainer) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->GetImageContainer(aContainer) : NS_ERROR_FAILURE;
+}
+
+nsresult PluginModuleParent::GetImageSize(NPP instance, nsIntSize* aSize) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->GetImageSize(aSize) : NS_ERROR_FAILURE;
+}
+
+void PluginModuleParent::DidComposite(NPP aInstance) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(aInstance);
+ return pip ? pip->DidComposite() : (void)0;
+}
+
+nsresult PluginModuleParent::SetBackgroundUnknown(NPP instance) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->SetBackgroundUnknown() : NS_ERROR_FAILURE;
+}
+
+nsresult PluginModuleParent::BeginUpdateBackground(NPP instance,
+ const nsIntRect& aRect,
+ DrawTarget** aDrawTarget) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->BeginUpdateBackground(aRect, aDrawTarget)
+ : NS_ERROR_FAILURE;
+}
+
+nsresult PluginModuleParent::EndUpdateBackground(NPP instance,
+ const nsIntRect& aRect) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->EndUpdateBackground(aRect) : NS_ERROR_FAILURE;
+}
+
+#if defined(XP_WIN)
+nsresult PluginModuleParent::GetScrollCaptureContainer(
+ NPP aInstance, mozilla::layers::ImageContainer** aContainer) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(aInstance);
+ return pip ? pip->GetScrollCaptureContainer(aContainer) : NS_ERROR_FAILURE;
+}
+#endif
+
+nsresult PluginModuleParent::HandledWindowedPluginKeyEvent(
+ NPP aInstance, const NativeEventData& aNativeKeyData, bool aIsConsumed) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(aInstance);
+ return pip ? pip->HandledWindowedPluginKeyEvent(aNativeKeyData, aIsConsumed)
+ : NS_ERROR_FAILURE;
+}
+
+void PluginModuleParent::OnInitFailure() {
+ if (GetIPCChannel()->CanSend()) {
+ Close();
+ }
+
+ mShutdown = true;
+}
+
+class PluginOfflineObserver final : public nsIObserver {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit PluginOfflineObserver(PluginModuleChromeParent* pmp) : mPmp(pmp) {}
+
+ private:
+ ~PluginOfflineObserver() = default;
+ 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;
+}
+
+void PluginModuleChromeParent::RegisterSettingsCallbacks() {
+ Preferences::RegisterCallback(CachedSettingChanged, "javascript.enabled",
+ this);
+ Preferences::RegisterCallback(CachedSettingChanged,
+ "dom.ipc.plugins.nativeCursorSupport", this);
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ mPluginOfflineObserver = new PluginOfflineObserver(this);
+ observerService->AddObserver(mPluginOfflineObserver,
+ "ipc:network:set-offline", false);
+ }
+}
+
+void PluginModuleChromeParent::UnregisterSettingsCallbacks() {
+ Preferences::UnregisterCallback(CachedSettingChanged, "javascript.enabled",
+ this);
+ Preferences::UnregisterCallback(CachedSettingChanged,
+ "dom.ipc.plugins.nativeCursorSupport", 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) {
+ auto module = static_cast<PluginModuleChromeParent*>(aModule);
+ module->CachedSettingChanged();
+}
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+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;
+ 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;
+
+ PluginSettings settings;
+ GetSettings(&settings);
+
+ if (!CallNP_Initialize(settings, error)) {
+ Close();
+ return NS_ERROR_FAILURE;
+ } else if (*error != NPERR_NO_ERROR) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+
+ if (*error != NPERR_NO_ERROR) {
+ OnInitFailure();
+ return NS_OK;
+ }
+
+ SetPluginFuncs(mNPPIface);
+
+ return NS_OK;
+}
+
+#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;
+ return PluginModuleParent::NP_Initialize(bFuncs, error);
+}
+
+# endif
+
+nsresult PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs,
+ NPError* error) {
+ nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
+ if (NS_FAILED(rv)) return rv;
+
+ PluginSettings settings;
+ GetSettings(&settings);
+
+ if (!CallNP_Initialize(settings, error)) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+
+ bool ok = true;
+ if (*error == NPERR_NO_ERROR) {
+ // Initialization steps for (e10s && !asyncInit) || !e10s
+# if defined XP_WIN
+ // 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
+ }
+
+ if (!ok) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (*error != NPERR_NO_ERROR) {
+ OnInitFailure();
+ return NS_OK;
+ }
+
+ return NS_OK;
+}
+
+#endif
+
+nsresult PluginModuleParent::NP_Shutdown(NPError* error) {
+ PLUGIN_LOG_DEBUG_METHOD;
+
+ if (mShutdown) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ 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;
+ SetPluginFuncs(pFuncs);
+
+ return NS_OK;
+}
+
+nsresult PluginModuleChromeParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs,
+ NPError* error) {
+# if !defined(XP_MACOSX)
+ if (!mSubprocess->IsConnected()) {
+ mNPPIface = pFuncs;
+ *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,
+ 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;
+ }
+
+ // create the instance on the other side
+ nsTArray<nsCString> names;
+ nsTArray<nsCString> values;
+
+ for (int i = 0; i < argc; ++i) {
+ names.AppendElement(NullableString(argn[i]));
+ values.AppendElement(NullableString(argv[i]));
+ }
+
+ return NPP_NewInternal(pluginType, instance, names, values, saved, error);
+}
+
+class nsCaseInsensitiveUTF8StringArrayComparator {
+ public:
+ template <class A, class B>
+ bool Equals(const A& a, const B& b) const {
+ return a.Equals(b.get(), nsCaseInsensitiveUTF8StringComparator);
+ }
+};
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+static void ForceWindowless(nsTArray<nsCString>& names,
+ nsTArray<nsCString>& values) {
+ nsCaseInsensitiveUTF8StringArrayComparator comparator;
+ constexpr auto wmodeAttributeName = "wmode"_ns;
+ constexpr auto opaqueAttributeValue = "opaque"_ns;
+ 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 // windows or linux
+#if defined(XP_WIN)
+static void ForceDirect(nsTArray<nsCString>& names,
+ nsTArray<nsCString>& values) {
+ nsCaseInsensitiveUTF8StringArrayComparator comparator;
+ constexpr auto wmodeAttributeName = "wmode"_ns;
+ constexpr auto directAttributeValue = "direct"_ns;
+ auto wmodeAttributeIndex = names.IndexOf(wmodeAttributeName, 0, comparator);
+ if (wmodeAttributeIndex != names.NoIndex) {
+ if ((!values[wmodeAttributeIndex].EqualsLiteral("transparent")) &&
+ (!values[wmodeAttributeIndex].EqualsLiteral("opaque"))) {
+ values[wmodeAttributeIndex].Assign(directAttributeValue);
+ }
+ } else {
+ names.AppendElement(wmodeAttributeName);
+ values.AppendElement(directAttributeValue);
+ }
+}
+#endif // windows
+
+nsresult PluginModuleParent::NPP_NewInternal(
+ NPMIMEType pluginType, NPP instance, nsTArray<nsCString>& names,
+ nsTArray<nsCString>& values, NPSavedData* saved, NPError* error) {
+ MOZ_ASSERT(names.Length() == values.Length());
+ if (mPluginName.IsEmpty()) {
+ GetPluginDetails();
+ InitQuirksModes(nsDependentCString(pluginType));
+ }
+
+ nsCaseInsensitiveUTF8StringArrayComparator comparator;
+ constexpr auto srcAttributeName = "src"_ns;
+ 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);
+ bool supportsForceDirect =
+ Preferences::GetBool("dom.ipc.plugins.forcedirect.enabled", false);
+ if (supportsAsyncRender) {
+ // Prefs indicates we want async plugin rendering, make sure
+ // the flash module has support.
+ if (!CallModuleSupportsAsyncRender(&supportsAsyncRender)) {
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+ }
+# 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
+ ForceWindowless(names, values);
+ }
+#elif defined(MOZ_WIDGET_GTK)
+ // We no longer support windowed mode on Linux.
+ ForceWindowless(names, values);
+#endif
+#ifdef XP_WIN
+ // For all builds that use async rendering force use of the accelerated
+ // direct path for flash objects that have wmode=window or no wmode
+ // specified.
+ if (supportsAsyncRender && supportsForceDirect &&
+ PluginInstanceParent::SupportsPluginDirectDXGISurfaceDrawing()) {
+ ForceDirect(names, values);
+ }
+#endif
+ }
+
+ instance->pdata = parentInstance;
+
+ // Any IPC messages for the PluginInstance actor should be dispatched to the
+ // DocGroup for the plugin's document.
+ RefPtr<nsPluginInstanceOwner> owner = parentInstance->GetOwner();
+ RefPtr<dom::Element> elt;
+ owner->GetDOMElement(getter_AddRefs(elt));
+ if (elt) {
+ RefPtr<dom::Document> doc = elt->OwnerDoc();
+ nsCOMPtr<nsISerialEventTarget> eventTarget =
+ doc->EventTargetFor(TaskCategory::Other);
+ SetEventTargetForActor(parentInstance, eventTarget);
+ }
+
+ if (!SendPPluginInstanceConstructor(
+ parentInstance, nsDependentCString(pluginType), names, values)) {
+ // |parentInstance| is automatically deleted.
+ instance->pdata = nullptr;
+ *error = NPERR_GENERIC_ERROR;
+ return NS_ERROR_FAILURE;
+ }
+
+ 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) {
+ NPP_Destroy(instance, 0);
+ return NS_ERROR_FAILURE;
+ }
+
+ Telemetry::ScalarAdd(Telemetry::ScalarID::BROWSER_USAGE_PLUGIN_INSTANTIATED,
+ 1);
+
+ 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* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->IsRemoteDrawingCoreAnimation(aDrawing) : NS_ERROR_FAILURE;
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+nsresult PluginModuleParent::ContentsScaleFactorChanged(
+ NPP instance, double aContentsScaleFactor) {
+ PluginInstanceParent* pip = PluginInstanceParent::Cast(instance);
+ return pip ? pip->ContentsScaleFactorChanged(aContentsScaleFactor)
+ : NS_ERROR_FAILURE;
+}
+#endif // #if defined(XP_MACOSX)
+
+#if defined(XP_MACOSX)
+mozilla::ipc::IPCResult PluginModuleParent::AnswerProcessSomeEvents() {
+ mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop();
+ return IPC_OK();
+}
+
+#elif !defined(MOZ_WIDGET_GTK)
+mozilla::ipc::IPCResult PluginModuleParent::AnswerProcessSomeEvents() {
+ MOZ_CRASH("unreached");
+}
+
+#else
+static const int kMaxChancesToProcessEvents = 20;
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+#endif
+
+mozilla::ipc::IPCResult
+PluginModuleParent::RecvProcessNativeEventsInInterruptCall() {
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(OS_WIN)
+ ProcessNativeEventsInInterruptCall();
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "PluginModuleParent::RecvProcessNativeEventsInInterruptCall not "
+ "implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+void PluginModuleParent::ProcessRemoteNativeEventsInInterruptCall() {
+#if defined(OS_WIN)
+ Unused << SendProcessNativeEventsInInterruptCall();
+ return;
+#endif
+ MOZ_ASSERT_UNREACHABLE(
+ "PluginModuleParent::ProcessRemoteNativeEventsInInterruptCall not "
+ "implemented!");
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvPluginShowWindow(
+ const uint32_t& aWindowId, const bool& aModal, const int32_t& aX,
+ const int32_t& aY, const double& aWidth, const double& 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 IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "PluginInstanceParent::RecvPluginShowWindow not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvPluginHideWindow(
+ const uint32_t& aWindowId) {
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPluginHideWindow(aWindowId, OtherPid());
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "PluginInstanceParent::RecvPluginHideWindow not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvSetCursor(
+ const NSCursorInfo& aCursorInfo) {
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnSetCursor(aCursorInfo);
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "PluginInstanceParent::RecvSetCursor not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvShowCursor(const bool& aShow) {
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnShowCursor(aShow);
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "PluginInstanceParent::RecvShowCursor not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvPushCursor(
+ const NSCursorInfo& aCursorInfo) {
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPushCursor(aCursorInfo);
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "PluginInstanceParent::RecvPushCursor not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvPopCursor() {
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPopCursor();
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "PluginInstanceParent::RecvPopCursor not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvNPN_ReloadPlugins(
+ const bool& aReloadPages) {
+ PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+
+ mozilla::plugins::parent::_reloadplugins(aReloadPages);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+PluginModuleChromeParent::RecvNotifyContentModuleDestroyed() {
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (host) {
+ host->NotifyContentModuleDestroyed(mPluginId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvReturnClearSiteData(
+ const NPError& aRv, const uint64_t& aCallbackId) {
+ if (mClearSiteDataCallbacks.find(aCallbackId) ==
+ mClearSiteDataCallbacks.end()) {
+ return IPC_OK();
+ }
+ 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginModuleParent::RecvReturnSitesWithData(
+ nsTArray<nsCString>&& aSites, const uint64_t& aCallbackId) {
+ if (mSitesWithDataCallbacks.find(aCallbackId) ==
+ mSitesWithDataCallbacks.end()) {
+ return IPC_OK();
+ }
+
+ if (!!mSitesWithDataCallbacks[aCallbackId]) {
+ mSitesWithDataCallbacks[aCallbackId]->SitesWithData(aSites);
+ }
+ mSitesWithDataCallbacks.erase(aCallbackId);
+ return IPC_OK();
+}
+
+layers::TextureClientRecycleAllocator*
+PluginModuleParent::EnsureTextureAllocatorForDirectBitmap() {
+ if (!mTextureAllocatorForDirectBitmap) {
+ mTextureAllocatorForDirectBitmap =
+ new layers::TextureClientRecycleAllocator(
+ layers::ImageBridgeChild::GetSingleton().get());
+ }
+ return mTextureAllocatorForDirectBitmap;
+}
+
+layers::TextureClientRecycleAllocator*
+PluginModuleParent::EnsureTextureAllocatorForDXGISurface() {
+ if (!mTextureAllocatorForDXGISurface) {
+ mTextureAllocatorForDXGISurface = new layers::TextureClientRecycleAllocator(
+ layers::ImageBridgeChild::GetSingleton().get());
+ }
+ return mTextureAllocatorForDXGISurface;
+}
+
+mozilla::ipc::IPCResult
+PluginModuleParent::AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
+ const bool& shouldRegister, NPError* result) {
+ MOZ_CRASH(
+ "SetValue_NPPVpluginRequiresAudioDeviceChanges is only valid "
+ "with PluginModuleChromeParent");
+}
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+
+// We only add the crash reporter to subprocess which have the filename
+// FlashPlayerPlugin*
+# define FLASH_PROCESS_PREFIX u"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, nsLiteralString(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),
+ nsLiteralCString(FLASH_PLUGIN_PREFIX)))
+ return;
+
+ mFinishInitTask = mChromeTaskFactory.NewTask<FinishInjectorInitTask>();
+ mFinishInitTask->Init(this);
+ if (!::QueueUserWorkItem(&PluginModuleChromeParent::GetToolhelpSnapshot,
+ mFinishInitTask, WT_EXECUTEDEFAULT)) {
+ mFinishInitTask = nullptr;
+ return;
+ }
+}
+
+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
diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h
new file mode 100644
index 0000000000..9fd74904a2
--- /dev/null
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -0,0 +1,545 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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 "mozilla/Unused.h"
+#include "npapi.h"
+#include "npfunctions.h"
+#include "nsExceptionHandler.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsIObserver.h"
+#ifdef XP_WIN
+# include "nsWindowsHelpers.h"
+#endif
+
+class nsPluginTag;
+
+namespace mozilla {
+
+namespace ipc {
+class CrashReporterHost;
+} // namespace ipc
+namespace layers {
+class TextureClientRecycleAllocator;
+} // namespace layers
+
+namespace plugins {
+//-----------------------------------------------------------------------------
+
+class BrowserStreamParent;
+class PluginInstanceParent;
+
+#ifdef XP_WIN
+class PluginHangUIParent;
+class FunctionBrokerParent;
+#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
+{
+ friend class PPluginModuleParent;
+
+ protected:
+ typedef mozilla::PluginLibrary PluginLibrary;
+
+ PPluginInstanceParent* AllocPPluginInstanceParent(
+ const nsCString& aMimeType, const nsTArray<nsCString>& aNames,
+ const nsTArray<nsCString>& aValues);
+
+ bool DeallocPPluginInstanceParent(PPluginInstanceParent* aActor);
+
+ public:
+ explicit PluginModuleParent(bool aIsChrome);
+ virtual ~PluginModuleParent();
+
+ 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 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);
+ }
+
+ mozilla::ipc::IPCResult RecvBackUpXResources(
+ const FileDescriptor& aXSocketFd);
+
+ mozilla::ipc::IPCResult AnswerProcessSomeEvents();
+
+ mozilla::ipc::IPCResult RecvProcessNativeEventsInInterruptCall();
+
+ mozilla::ipc::IPCResult RecvPluginShowWindow(
+ const uint32_t& aWindowId, const bool& aModal, const int32_t& aX,
+ const int32_t& aY, const double& aWidth, const double& aHeight);
+
+ mozilla::ipc::IPCResult RecvPluginHideWindow(const uint32_t& aWindowId);
+
+ mozilla::ipc::IPCResult RecvSetCursor(const NSCursorInfo& aCursorInfo);
+
+ mozilla::ipc::IPCResult RecvShowCursor(const bool& aShow);
+
+ mozilla::ipc::IPCResult RecvPushCursor(const NSCursorInfo& aCursorInfo);
+
+ mozilla::ipc::IPCResult RecvPopCursor();
+
+ mozilla::ipc::IPCResult RecvNPN_SetException(const nsCString& aMessage);
+
+ mozilla::ipc::IPCResult RecvNPN_ReloadPlugins(const bool& aReloadPages);
+
+ static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
+
+ virtual mozilla::ipc::IPCResult
+ AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
+ const bool& shouldRegister, NPError* result);
+
+ protected:
+ void SetChildTimeout(const int32_t aChildTimeout);
+ static void TimeoutChanged(const char* aPref, void* aModule);
+
+ virtual void UpdatePluginTimeout() {}
+
+ virtual mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() {
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvReturnClearSiteData(const NPError& aRv,
+ const uint64_t& aCallbackId);
+
+ mozilla::ipc::IPCResult RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
+ const uint64_t& aCallbackId);
+
+ void SetPluginFuncs(NPPluginFuncs* aFuncs);
+
+ nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance,
+ nsTArray<nsCString>& names,
+ nsTArray<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_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)
+ 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, 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
+
+ layers::TextureClientRecycleAllocator*
+ EnsureTextureAllocatorForDirectBitmap();
+ layers::TextureClientRecycleAllocator* EnsureTextureAllocatorForDXGISurface();
+
+ protected:
+ void NotifyFlashHang();
+ void NotifyPluginCrashed();
+ void OnInitFailure();
+ 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 mHangID;
+ 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();
+
+ uint32_t mRunID;
+
+ RefPtr<layers::TextureClientRecycleAllocator>
+ mTextureAllocatorForDirectBitmap;
+ RefPtr<layers::TextureClientRecycleAllocator> mTextureAllocatorForDXGISurface;
+
+ /**
+ * 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 CrashReporterHost object
+ * that mCrashReporter refers to.
+ */
+ mozilla::Mutex mCrashReporterMutex;
+ UniquePtr<ipc::CrashReporterHost> mCrashReporter;
+ nsString mOrphanedDumpId;
+};
+
+class PluginModuleContentParent : public PluginModuleParent {
+ public:
+ explicit PluginModuleContentParent();
+
+ static PluginLibrary* LoadModule(uint32_t aPluginId, nsPluginTag* aPluginTag);
+
+ virtual ~PluginModuleContentParent();
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override;
+#endif
+
+ private:
+ static void Initialize(Endpoint<PPluginModuleParent>&& aEndpoint);
+
+ 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::BackgroundHangAnnotator {
+ friend class mozilla::ipc::CrashReporterHost;
+
+ 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);
+
+ 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
+
+ void CachedSettingChanged();
+
+ private:
+ virtual void EnteredCxxStack() override;
+
+ void ExitedCxxStack() override;
+
+ mozilla::ipc::IProtocol* GetInvokingProtocol();
+ PluginInstanceParent* GetManagingInstance(mozilla::ipc::IProtocol* aProtocol);
+
+ virtual void AnnotateHang(
+ mozilla::BackgroundHangAnnotations& aAnnotations) override;
+
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+ void ProcessFirstMinidump();
+ void HandleOrphanedMinidump();
+ void AddCrashAnnotations();
+
+ PluginProcessParent* Process() const { return mSubprocess; }
+ base::ProcessHandle ChildProcessHandle() {
+ return mSubprocess->GetChildProcessHandle();
+ }
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ 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);
+
+ void CleanupFromTimeout(const bool aByHangUI);
+
+ virtual void UpdatePluginTimeout() override;
+
+ void RegisterSettingsCallbacks();
+ void UnregisterSettingsCallbacks();
+
+ bool InitCrashReporter();
+
+ mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() override;
+
+ static void CachedSettingChanged(const char* aPref, void* aModule);
+
+ mozilla::ipc::IPCResult
+ 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
+ nsTArray<float> mPluginCpuUsageOnHang;
+ PluginHangUIParent* mHangUIParent;
+ bool mHangUIEnabled;
+ bool mIsTimerReset;
+
+ /**
+ * 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();
+
+ FunctionBrokerParent* mBrokerParent;
+#endif
+
+#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;
+
+ nsCOMPtr<nsIObserver> mPluginOfflineObserver;
+ bool mIsBlocklisted;
+ bool mIsCleaningFromTimeout;
+};
+
+} // 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 0000000000..986243f93f
--- /dev/null
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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/PluginProcessChild.h"
+
+#include "ClearOnShutdown.h"
+#include "base/command_line.h"
+#include "base/message_loop.h" // for MessageLoop
+#include "base/string_util.h"
+#include "mozilla/AbstractThread.h"
+#include "mozilla/TaskController.h"
+#include "mozilla/ipc/IOThreadChild.h"
+#include "nsDebugImpl.h"
+#include "nsThreadManager.h"
+#include "prlink.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "mozilla/SandboxSettings.h"
+#endif
+
+#ifdef XP_WIN
+# if defined(MOZ_SANDBOX)
+# include "mozilla/sandboxTarget.h"
+# include "ProcessUtils.h"
+# include "nsDirectoryService.h"
+# endif
+#endif
+
+using mozilla::ipc::IOThreadChild;
+
+#ifdef OS_WIN
+# include <algorithm>
+#endif
+
+namespace mozilla::plugins {
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+static void SetSandboxTempPath(const std::wstring& aFullTmpPath) {
+ // Save the TMP environment variable so that is is picked up by GetTempPath().
+ // Note that we specifically write to the TMP variable, as that is the first
+ // variable that is checked by GetTempPath() to determine its output.
+ Unused << NS_WARN_IF(!SetEnvironmentVariableW(L"TMP", aFullTmpPath.c_str()));
+
+ // We also set TEMP in case there is naughty third-party code that is
+ // referencing the environment variable directly.
+ Unused << NS_WARN_IF(!SetEnvironmentVariableW(L"TEMP", aFullTmpPath.c_str()));
+}
+#endif
+
+bool PluginProcessChild::Init(int aArgc, char* aArgv[]) {
+ nsDebugImpl::SetMultiprocessMode("NPAPI");
+
+#if defined(XP_MACOSX)
+ // Remove the trigger for "dyld interposing" that we added in
+ // GeckoChildProcessHost::PerformAsyncLaunch(), 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]);
+
+# if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ int level;
+ if (values.size() >= 4 && values[2] == "-flashSandboxLevel" &&
+ (level = std::stoi(values[3], nullptr)) > 0) {
+ level = ClampFlashSandboxLevel(level);
+ MOZ_ASSERT(level > 0);
+
+ bool enableLogging = false;
+ if (values.size() >= 5 && values[4] == "-flashSandboxLogging") {
+ enableLogging = true;
+ }
+
+ mPlugin.EnableFlashSandbox(level, enableLogging);
+ }
+# endif
+
+#elif defined(OS_WIN)
+ std::vector<std::wstring> values =
+ CommandLine::ForCurrentProcess()->GetLooseValues();
+ MOZ_ASSERT(values.size() >= 1, "not enough loose args");
+
+ // parameters are:
+ // values[0] is path to plugin DLL
+ // values[1] is path to folder that should be used for temp files
+ // values[2] is path to the Flash Player roaming folder
+ // (this is always that Flash folder, regardless of what plugin is being
+ // run)
+ pluginFilename = WideToUTF8(values[0]);
+
+ // We don't initialize XPCOM but we need the thread manager and the
+ // logging framework for the FunctionBroker.
+ NS_SetMainThread();
+ mozilla::TimeStamp::Startup();
+ NS_LogInit();
+ mozilla::LogModule::Init(aArgc, aArgv);
+ nsThreadManager::get().Init();
+
+# if defined(MOZ_SANDBOX)
+ MOZ_ASSERT(values.size() >= 3,
+ "not enough loose args for sandboxed plugin process");
+
+ // The sandbox closes off the default location temp file location so we set
+ // a new one here (regardless of whether or not we are sandboxing).
+ SetSandboxTempPath(values[1]);
+ PluginModuleChild::SetFlashRoamingPath(values[2]);
+
+ // 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
+
+ return mPlugin.InitForChrome(pluginFilename, ParentPid(),
+ IOThreadChild::message_loop(),
+ IOThreadChild::TakeChannel());
+}
+
+void PluginProcessChild::CleanUp() {
+#if defined(OS_WIN)
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Shutdown components we started in Init. Note that KillClearOnShutdown
+ // is an event that is regularly part of XPCOM shutdown. We do not
+ // call XPCOM's shutdown but we need this event to be sent to avoid
+ // leaking objects labeled as ClearOnShutdown.
+ nsThreadManager::get().Shutdown();
+ NS_LogTerm();
+#endif
+
+ mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownFinal);
+
+ AbstractThread::ShutdownMainThread();
+
+ mozilla::TaskController::Shutdown();
+}
+
+} // namespace mozilla::plugins
diff --git a/dom/plugins/ipc/PluginProcessChild.h b/dom/plugins/ipc/PluginProcessChild.h
new file mode 100644
index 0000000000..36c8077ce8
--- /dev/null
+++ b/dom/plugins/ipc/PluginProcessChild.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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/ProcessRuntime.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() = default;
+
+ virtual bool Init(int aArgc, char* aArgv[]) override;
+ virtual void CleanUp() override;
+
+ protected:
+ static PluginProcessChild* current() {
+ return static_cast<PluginProcessChild*>(ProcessChild::current());
+ }
+
+ private:
+#if defined(XP_WIN)
+ /* Drag-and-drop depends on the host initializing COM.
+ * This object initializes and configures COM. */
+ mozilla::mscom::ProcessRuntime 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 0000000000..2f65311263
--- /dev/null
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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 "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsIProperties.h"
+#include "nsServiceManagerUtils.h"
+
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/plugins/PluginMessageUtils.h"
+#include "mozilla/Telemetry.h"
+#include "nsThreadUtils.h"
+
+using std::string;
+using std::vector;
+
+using mozilla::ipc::BrowserProcessSubThread;
+using mozilla::ipc::GeckoChildProcessHost;
+using mozilla::plugins::LaunchCompleteTask;
+using mozilla::plugins::PluginProcessParent;
+
+#ifdef XP_WIN
+PluginProcessParent::PidSet* PluginProcessParent::sPidSet = nullptr;
+#endif
+
+PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath)
+ : GeckoChildProcessHost(GeckoProcessType_Plugin),
+ mPluginFilePath(aPluginFilePath),
+ mTaskFactory(this),
+ mMainMsgLoop(MessageLoop::current())
+#ifdef XP_WIN
+ ,
+ mChildPid(0)
+#endif
+{
+}
+
+PluginProcessParent::~PluginProcessParent() {
+#ifdef XP_WIN
+ if (sPidSet && mChildPid) {
+ sPidSet->RemoveEntry(mChildPid);
+ if (sPidSet->IsEmpty()) {
+ delete sPidSet;
+ sPidSet = nullptr;
+ }
+ }
+#endif
+}
+
+bool PluginProcessParent::Launch(
+ mozilla::UniquePtr<LaunchCompleteTask> aLaunchCompleteTask,
+ int32_t aSandboxLevel, bool aIsSandboxLoggingEnabled) {
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_SANDBOX)
+ // At present, the Mac Flash plugin sandbox does not support different
+ // levels and is enabled via a boolean pref or environment variable.
+ // On Mac, when |aSandboxLevel| is positive, we enable the sandbox.
+# if defined(XP_WIN)
+ mSandboxLevel = aSandboxLevel;
+
+ // The sandbox process sometimes needs read access to the plugin file.
+ if (aSandboxLevel >= 3) {
+ std::wstring pluginFile(
+ NS_ConvertUTF8toUTF16(mPluginFilePath.c_str()).get());
+ mAllowedFilesRead.push_back(pluginFile);
+ }
+# endif // XP_WIN
+#else
+ if (aSandboxLevel != 0) {
+ MOZ_ASSERT(false,
+ "Can't enable an NPAPI process sandbox for platform/build.");
+ }
+#endif
+
+ mLaunchCompleteTask = std::move(aLaunchCompleteTask);
+
+ vector<string> args;
+ args.push_back(MungePluginDsoPath(mPluginFilePath));
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ if (aSandboxLevel > 0) {
+ args.push_back("-flashSandboxLevel");
+ args.push_back(std::to_string(aSandboxLevel));
+ if (aIsSandboxLoggingEnabled) {
+ args.push_back("-flashSandboxLogging");
+ }
+ }
+#elif defined(XP_WIN) && defined(MOZ_SANDBOX)
+ nsresult rv;
+ nsCOMPtr<nsIProperties> dirSvc =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false, "Failed to get directory service.");
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> dir;
+ rv = dirSvc->Get(NS_APP_PLUGIN_PROCESS_TEMP_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(dir));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to get plugin process temp directory.");
+ return false;
+ }
+
+ nsAutoString tempDir;
+ MOZ_ALWAYS_SUCCEEDS(dir->GetPath(tempDir));
+ args.push_back(NS_ConvertUTF16toUTF8(tempDir).get());
+
+ rv =
+ dirSvc->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsIFile), getter_AddRefs(dir));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to get appdata directory.");
+ return false;
+ }
+
+ nsAutoString appdataDir;
+ MOZ_ALWAYS_SUCCEEDS(dir->GetPath(appdataDir));
+ appdataDir.Append(L"\\Adobe\\");
+ args.push_back(NS_ConvertUTF16toUTF8(appdataDir).get());
+#endif
+
+ bool result = AsyncLaunch(args);
+ if (!result) {
+ mLaunchCompleteTask = nullptr;
+ }
+ return result;
+}
+
+/**
+ * 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 (mLaunchCompleteTask) {
+ if (result) {
+ mLaunchCompleteTask->SetLaunchSucceeded();
+ }
+ RunLaunchCompleteTask();
+ }
+ return result;
+}
+
+void PluginProcessParent::OnChannelConnected(int32_t peer_pid) {
+#ifdef XP_WIN
+ mChildPid = static_cast<uint32_t>(peer_pid);
+ if (!sPidSet) {
+ sPidSet = new PluginProcessParent::PidSet();
+ }
+ sPidSet->PutEntry(mChildPid);
+#endif
+
+ GeckoChildProcessHost::OnChannelConnected(peer_pid);
+}
+
+void PluginProcessParent::OnChannelError() {
+ GeckoChildProcessHost::OnChannelError();
+}
+
+bool PluginProcessParent::IsConnected() {
+ mozilla::MonitorAutoLock lock(mMonitor);
+ return mProcessState == PROCESS_CONNECTED;
+}
+
+bool PluginProcessParent::IsPluginProcessId(base::ProcessId procId) {
+#ifdef XP_WIN
+ MOZ_ASSERT(XRE_IsParentProcess());
+ return sPidSet && sPidSet->Contains(static_cast<uint32_t>(procId));
+#else
+ NS_ERROR("IsPluginProcessId not available on this platform.");
+ return false;
+#endif
+}
diff --git a/dom/plugins/ipc/PluginProcessParent.h b/dom/plugins/ipc/PluginProcessParent.h
new file mode 100644
index 0000000000..48ce24cc70
--- /dev/null
+++ b/dom/plugins/ipc/PluginProcessParent.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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 "nsTHashtable.h"
+#include "nsHashKeys.h"
+
+namespace mozilla {
+namespace plugins {
+
+class LaunchCompleteTask : public Runnable {
+ public:
+ LaunchCompleteTask()
+ : Runnable("plugins::LaunchCompleteTask"), mLaunchSucceeded(false) {}
+
+ void SetLaunchSucceeded() { mLaunchSucceeded = true; }
+
+ protected:
+ bool mLaunchSucceeded;
+};
+
+class PluginProcessParent final : public mozilla::ipc::GeckoChildProcessHost {
+ public:
+ explicit PluginProcessParent(const std::string& aPluginFilePath);
+
+ /**
+ * 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.
+ * @param aIsSandboxLoggingEnabled Indicates if sandbox violation
+ * logging should be enabled for the plugin process.
+ */
+ bool Launch(UniquePtr<LaunchCompleteTask> aLaunchCompleteTask =
+ UniquePtr<LaunchCompleteTask>(),
+ int32_t aSandboxLevel = 0, bool aIsSandboxLoggingEnabled = false);
+
+ virtual bool CanShutdown() override { return true; }
+
+ const std::string& GetPluginFilePath() { return mPluginFilePath; }
+
+ using mozilla::ipc::GeckoChildProcessHost::GetChannel;
+
+ virtual bool WaitUntilConnected(int32_t aTimeoutMs = 0) override;
+
+ virtual void OnChannelConnected(int32_t peer_pid) override;
+ virtual void OnChannelError() override;
+
+ bool IsConnected();
+
+ static bool IsPluginProcessId(base::ProcessId procId);
+
+ private:
+ ~PluginProcessParent();
+
+ void RunLaunchCompleteTask();
+
+ std::string mPluginFilePath;
+ ipc::TaskFactory<PluginProcessParent> mTaskFactory;
+ UniquePtr<LaunchCompleteTask> mLaunchCompleteTask;
+ MessageLoop* mMainMsgLoop;
+#ifdef XP_WIN
+ typedef nsTHashtable<nsUint32HashKey> PidSet;
+ // Set of PIDs for all plugin child processes or NULL if empty.
+ static PidSet* sPidSet;
+ uint32_t mChildPid;
+#endif
+
+ 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 0000000000..2e83cbc37b
--- /dev/null
+++ b/dom/plugins/ipc/PluginQuirks.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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::plugins {
+
+int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
+ const nsCString& aPluginFilename) {
+ int quirks = 0;
+
+ nsPluginHost::SpecialType specialType =
+ nsPluginHost::GetSpecialType(aMimeType);
+
+ 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;
+ quirks |= QUIRK_FLASH_HOOK_PRINTDLGW;
+ quirks |= QUIRK_FLASH_HOOK_SSL;
+ quirks |= QUIRK_FLASH_HOOK_CREATEMUTEXW;
+# endif
+#endif
+ }
+
+#ifdef XP_MACOSX
+ // Whitelist Flash to support offline renderer.
+ if (specialType == nsPluginHost::eSpecialType_Flash) {
+ quirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
+ }
+#endif
+
+#ifdef OS_WIN
+ if (specialType == nsPluginHost::eSpecialType_Test) {
+ quirks |= QUIRK_WINLESS_HOOK_IME;
+ }
+#endif
+
+ return quirks;
+}
+
+} // namespace mozilla::plugins
diff --git a/dom/plugins/ipc/PluginQuirks.h b/dom/plugins/ipc/PluginQuirks.h
new file mode 100644
index 0000000000..852ebb6b7a
--- /dev/null
+++ b/dom/plugins/ipc/PluginQuirks.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 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
+
+#include "nsString.h"
+
+namespace mozilla {
+namespace plugins {
+
+// Quirks mode support for various plugin mime types
+enum PluginQuirks {
+ QUIRKS_NOT_INITIALIZED = 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,
+ // 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: 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,
+ // Win: Hook PrintDlgW to show print settings dialog on sandbox process
+ QUIRK_FLASH_HOOK_PRINTDLGW = 1 << 14,
+ // Win: Broker Win32 SSL operations
+ QUIRK_FLASH_HOOK_SSL = 1 << 15,
+ // Win: Hook CreateMutexW for brokering when using the camera
+ QUIRK_FLASH_HOOK_CREATEMUTEXW = 1 << 16,
+};
+
+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 0000000000..86c4b83d18
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -0,0 +1,1205 @@
+/* -*- 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()) {
+ MOZ_CRASH("Huh?! Wrong class!");
+ }
+
+ return new ChildNPObject();
+}
+
+// static
+void PluginScriptableObjectChild::ScriptableInvalidate(NPObject* aObject) {
+ AssertPluginThread();
+
+ if (aObject->_class != GetClass()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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()) {
+ MOZ_CRASH("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;
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectChild::AnswerInvalidate() {
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->hasMethod)) {
+ *aHasMethod = false;
+ return IPC_OK();
+ }
+
+ StackIdentifier id(aId);
+ *aHasMethod = mObject->_class->hasMethod(mObject, id.ToNPIdentifier());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectChild::AnswerInvoke(
+ const PluginIdentifier& aId, nsTArray<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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, mozilla::fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ Variant convertedResult;
+ success =
+ ConvertToRemoteVariant(result, convertedResult, GetInstance(), false);
+
+ DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ *aSuccess = true;
+ *aResult = convertedResult;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectChild::AnswerInvokeDefault(
+ nsTArray<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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, mozilla::fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ Variant convertedResult;
+ success =
+ ConvertToRemoteVariant(result, convertedResult, GetInstance(), false);
+
+ DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ *aResult = convertedResult;
+ *aSuccess = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->hasProperty)) {
+ *aHasProperty = false;
+ return IPC_OK();
+ }
+
+ StackIdentifier id(aId);
+ *aHasProperty = mObject->_class->hasProperty(mObject, id.ToNPIdentifier());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ Variant converted;
+ if ((*aSuccess =
+ ConvertToRemoteVariant(result, converted, GetInstance(), false))) {
+ DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
+ *aResult = converted;
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ StackIdentifier stackID(aId);
+ NPIdentifier id = stackID.ToNPIdentifier();
+
+ if (!mObject->_class->hasProperty(mObject, id)) {
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ NPVariant converted;
+ ConvertToVariant(aValue, converted);
+
+ if ((*aSuccess = mObject->_class->setProperty(mObject, id, &converted))) {
+ PluginModuleChild::sBrowserFuncs.releasevariantvalue(&converted);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ StackIdentifier stackID(aId);
+ NPIdentifier id = stackID.ToNPIdentifier();
+ *aSuccess = mObject->_class->hasProperty(mObject, id)
+ ? mObject->_class->removeProperty(mObject, id)
+ : true;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectChild::AnswerEnumerate(
+ nsTArray<PluginIdentifier>* aProperties, bool* aSuccess) {
+ AssertPluginThread();
+ PluginInstanceChild::AutoStackHelper guard(mInstance);
+
+ if (mInvalidated) {
+ NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ if (!(mObject->_class && mObject->_class->enumerate)) {
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ NPIdentifier* ids;
+ uint32_t idCount;
+ if (!mObject->_class->enumerate(mObject, &ids, &idCount)) {
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ aProperties->SetCapacity(idCount);
+
+ for (uint32_t index = 0; index < idCount; index++) {
+ aProperties->AppendElement(FromNPIdentifier(ids[index]));
+ }
+
+ PluginModuleChild::sBrowserFuncs.memfree(ids);
+ *aSuccess = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectChild::AnswerConstruct(
+ nsTArray<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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, mozilla::fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ Variant convertedResult;
+ success =
+ ConvertToRemoteVariant(result, convertedResult, GetInstance(), false);
+
+ DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ *aResult = convertedResult;
+ *aSuccess = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectChild::RecvProtect() {
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ Protect();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectChild::RecvUnprotect() {
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ Unprotect();
+ return IPC_OK();
+}
+
+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();
+ if (!sObjectMap) {
+ // All PluginInstanceChilds have been destroyed
+ return nullptr;
+ }
+
+ 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 0000000000..46ce160c9a
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.h
@@ -0,0 +1,276 @@
+/* -*- 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);
+ }
+
+ MOZ_COUNTED_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);
+
+ mozilla::ipc::IPCResult AnswerInvalidate();
+
+ mozilla::ipc::IPCResult AnswerHasMethod(const PluginIdentifier& aId,
+ bool* aHasMethod);
+
+ mozilla::ipc::IPCResult AnswerInvoke(const PluginIdentifier& aId,
+ nsTArray<Variant>&& aArgs,
+ Variant* aResult, bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerInvokeDefault(nsTArray<Variant>&& aArgs,
+ Variant* aResult, bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerHasProperty(const PluginIdentifier& aId,
+ bool* aHasProperty);
+
+ mozilla::ipc::IPCResult AnswerGetChildProperty(const PluginIdentifier& aId,
+ bool* aHasProperty,
+ bool* aHasMethod,
+ Variant* aResult,
+ bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerSetProperty(const PluginIdentifier& aId,
+ const Variant& aValue,
+ bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerRemoveProperty(const PluginIdentifier& aId,
+ bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerEnumerate(
+ nsTArray<PluginIdentifier>* aProperties, bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerConstruct(nsTArray<Variant>&& aArgs,
+ Variant* aResult, bool* aSuccess);
+
+ mozilla::ipc::IPCResult RecvProtect();
+
+ mozilla::ipc::IPCResult RecvUnprotect();
+
+ 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);
+ }
+
+ MOZ_COUNTED_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 0000000000..d12474c999
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.cpp
@@ -0,0 +1,1289 @@
+/* -*- 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 "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) {
+ 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.
+ MOZ_ASSERT_UNREACHABLE("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!");
+
+ 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
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::AnswerHasMethod(
+ const PluginIdentifier& aId, bool* aHasMethod) {
+ if (!mObject) {
+ NS_WARNING("Calling AnswerHasMethod with an invalidated object!");
+ *aHasMethod = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aHasMethod = false;
+ return IPC_OK();
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aHasMethod = false;
+ return IPC_OK();
+ }
+ *aHasMethod =
+ npn->hasmethod(instance->GetNPP(), mObject, stackID.ToNPIdentifier());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::AnswerInvoke(
+ const PluginIdentifier& aId, nsTArray<Variant>&& aArgs, Variant* aResult,
+ bool* aSuccess) {
+ if (!mObject) {
+ NS_WARNING("Calling AnswerInvoke with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+ }
+
+ 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 IPC_OK();
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
+
+ DeferNPVariantLastRelease(npn, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ *aResult = convertedResult;
+ *aSuccess = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::AnswerInvokeDefault(
+ nsTArray<Variant>&& aArgs, Variant* aResult, bool* aSuccess) {
+ if (!mObject) {
+ NS_WARNING("Calling AnswerInvoke with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+ }
+
+ 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 IPC_OK();
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, GetInstance());
+
+ DeferNPVariantLastRelease(npn, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ *aResult = convertedResult;
+ *aSuccess = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::AnswerHasProperty(
+ const PluginIdentifier& aId, bool* aHasProperty) {
+ if (!mObject) {
+ NS_WARNING("Calling AnswerHasProperty with an invalidated object!");
+ *aHasProperty = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aHasProperty = false;
+ return IPC_OK();
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aHasProperty = false;
+ return IPC_OK();
+ }
+
+ *aHasProperty =
+ npn->hasproperty(instance->GetNPP(), mObject, stackID.ToNPIdentifier());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ NPVariant result;
+ if (!npn->getproperty(instance->GetNPP(), mObject, stackID.ToNPIdentifier(),
+ &result)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ Variant converted;
+ if ((*aSuccess = ConvertToRemoteVariant(result, converted, instance))) {
+ DeferNPVariantLastRelease(npn, &result);
+ *aResult = converted;
+ } else {
+ *aResult = void_t();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::AnswerSetProperty(
+ const PluginIdentifier& aId, const Variant& aValue, bool* aSuccess) {
+ if (!mObject) {
+ NS_WARNING("Calling AnswerSetProperty with an invalidated object!");
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ NPVariant converted;
+ if (!ConvertToVariant(aValue, converted, instance)) {
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ if ((*aSuccess = npn->setproperty(instance->GetNPP(), mObject,
+ stackID.ToNPIdentifier(), &converted))) {
+ ReleaseVariant(converted, instance);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::AnswerRemoveProperty(
+ const PluginIdentifier& aId, bool* aSuccess) {
+ if (!mObject) {
+ NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!");
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ StackIdentifier stackID(aId);
+ if (stackID.Failed()) {
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ *aSuccess = npn->removeproperty(instance->GetNPP(), mObject,
+ stackID.ToNPIdentifier());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::AnswerEnumerate(
+ nsTArray<PluginIdentifier>* aProperties, bool* aSuccess) {
+ if (!mObject) {
+ NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_WARNING("No netscape funcs?!");
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ NPIdentifier* ids;
+ uint32_t idCount;
+ if (!npn->enumerate(instance->GetNPP(), mObject, &ids, &idCount)) {
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ aProperties->SetCapacity(idCount);
+
+ for (uint32_t index = 0; index < idCount; index++) {
+ PluginIdentifier id;
+ if (!FromNPIdentifier(ids[index], &id)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ aProperties->AppendElement(id);
+ }
+
+ npn->memfree(ids);
+ *aSuccess = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::AnswerConstruct(
+ nsTArray<Variant>&& aArgs, Variant* aResult, bool* aSuccess) {
+ if (!mObject) {
+ NS_WARNING("Calling AnswerConstruct with an invalidated object!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ AutoTArray<NPVariant, 10> convertedArgs;
+ uint32_t argCount = aArgs.Length();
+
+ if (!convertedArgs.SetLength(argCount, fallible)) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+ }
+
+ 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 IPC_OK();
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, instance);
+
+ DeferNPVariantLastRelease(npn, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ *aSuccess = true;
+ *aResult = convertedResult;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::RecvProtect() {
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ Protect();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginScriptableObjectParent::RecvUnprotect() {
+ NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
+ NS_ASSERTION(mType == LocalObject, "Bad type!");
+
+ Unprotect();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult 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 IPC_OK();
+ }
+
+ const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
+ if (!npn) {
+ NS_ERROR("No netscape funcs?!");
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ 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 IPC_OK();
+ }
+
+ Variant convertedResult;
+ success = ConvertToRemoteVariant(result, convertedResult, instance);
+
+ DeferNPVariantLastRelease(npn, &result);
+
+ if (!success) {
+ *aResult = void_t();
+ *aSuccess = false;
+ return IPC_OK();
+ }
+
+ *aSuccess = true;
+ *aResult = convertedResult;
+ return IPC_OK();
+}
+
+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 0000000000..cf8bc5f04f
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.h
@@ -0,0 +1,169 @@
+/* -*- 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;
+
+ mozilla::ipc::IPCResult AnswerHasMethod(const PluginIdentifier& aId,
+ bool* aHasMethod);
+
+ mozilla::ipc::IPCResult AnswerInvoke(const PluginIdentifier& aId,
+ nsTArray<Variant>&& aArgs,
+ Variant* aResult, bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerInvokeDefault(nsTArray<Variant>&& aArgs,
+ Variant* aResult, bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerHasProperty(const PluginIdentifier& aId,
+ bool* aHasProperty);
+
+ mozilla::ipc::IPCResult AnswerGetParentProperty(const PluginIdentifier& aId,
+ Variant* aResult,
+ bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerSetProperty(const PluginIdentifier& aId,
+ const Variant& aValue,
+ bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerRemoveProperty(const PluginIdentifier& aId,
+ bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerEnumerate(
+ nsTArray<PluginIdentifier>* aProperties, bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerConstruct(nsTArray<Variant>&& aArgs,
+ Variant* aResult, bool* aSuccess);
+
+ mozilla::ipc::IPCResult AnswerNPN_Evaluate(const nsCString& aScript,
+ Variant* aResult, bool* aSuccess);
+
+ mozilla::ipc::IPCResult RecvProtect();
+
+ mozilla::ipc::IPCResult RecvUnprotect();
+
+ 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 0000000000..b211e1687f
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
@@ -0,0 +1,154 @@
+/* -*- 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:
+ MOZ_ASSERT_UNREACHABLE("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 {
+ MOZ_ASSERT_UNREACHABLE("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 0000000000..e620e017e9
--- /dev/null
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils.h
@@ -0,0 +1,253 @@
+/* -*- 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:
+ MOZ_ASSERT_UNREACHABLE("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() {
+ nsTArray<Variant>& vars = EnsureAndGetShadowArray();
+ uint32_t count = vars.Length();
+ for (uint32_t index = 0; index < count; index++) {
+ ReleaseRemoteVariant(vars[index]);
+ }
+ }
+
+ operator const nsTArray<Variant>&() { return EnsureAndGetShadowArray(); }
+
+ bool IsOk() { return mOk; }
+
+ private:
+ nsTArray<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;
+ nsTArray<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/PluginSurfaceParent.cpp b/dom/plugins/ipc/PluginSurfaceParent.cpp
new file mode 100644
index 0000000000..251572995e
--- /dev/null
+++ b/dom/plugins/ipc/PluginSurfaceParent.cpp
@@ -0,0 +1,29 @@
+/* -*- 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
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginSurfaceParent.h b/dom/plugins/ipc/PluginSurfaceParent.h
new file mode 100644
index 0000000000..201ecf9da7
--- /dev/null
+++ b/dom/plugins/ipc/PluginSurfaceParent.h
@@ -0,0 +1,38 @@
+/* -*- 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 0000000000..0c674c3653
--- /dev/null
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -0,0 +1,48 @@
+/* -*- 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 URIParams;
+
+namespace mozilla {
+namespace plugins {
+
+struct PluginTag
+{
+ uint32_t id;
+ nsCString name;
+ nsCString description;
+ nsCString[] mimeTypes;
+ nsCString[] mimeDescriptions;
+ nsCString[] extensions;
+ bool isFlashPlugin;
+ bool supportsAsyncRender; // flash specific
+ nsCString filename;
+ nsCString version;
+ int64_t lastModifiedTime;
+ int32_t sandboxLevel;
+ uint16_t blocklistState;
+};
+
+struct FakePluginTag
+{
+ uint32_t id;
+ URIParams handlerURI;
+ nsCString name;
+ nsCString description;
+ nsCString[] mimeTypes;
+ nsCString[] mimeDescriptions;
+ nsCString[] extensions;
+ nsCString niceName;
+ nsString sandboxScript;
+};
+
+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 0000000000..bf03c90c78
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsOSX.h
@@ -0,0 +1,94 @@
+/* -*- 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 0000000000..9aac0b3c3d
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsOSX.mm
@@ -0,0 +1,429 @@
+/* -*- 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"));
+ if (!sApplicationASN) {
+ NS_WARNING("Failed to set process name: Could not get function pointer "
+ "for LaunchServices");
+ return false;
+ }
+ }
+
+ 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 ? getASNFunc() : nullptr;
+
+ 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 0000000000..647d0d385e
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsWin.cpp
@@ -0,0 +1,272 @@
+/* -*- 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 {
+
+class AudioNotification;
+typedef nsTHashtable<nsPtrHashKey<PluginModuleParent>> PluginModuleSet;
+StaticMutex sMutex;
+
+class AudioDeviceMessageRunnable : public Runnable {
+ public:
+ explicit AudioDeviceMessageRunnable(
+ AudioNotification* aAudioNotification,
+ NPAudioDeviceChangeDetailsIPC aChangeDetails);
+
+ explicit AudioDeviceMessageRunnable(
+ AudioNotification* aAudioNotification,
+ NPAudioDeviceStateChangedIPC aDeviceState);
+
+ NS_IMETHOD Run() override;
+
+ protected:
+ // The potential payloads for the message. Type determined by mMessageType.
+ NPAudioDeviceChangeDetailsIPC mChangeDetails;
+ NPAudioDeviceStateChangedIPC mDeviceState;
+ enum { DEFAULT_DEVICE_CHANGED, DEVICE_STATE_CHANGED } mMessageType;
+
+ AudioNotification* mAudioNotification;
+};
+
+class AudioNotification final : public IMMNotificationClient {
+ public:
+ AudioNotification() : mIsRegistered(false), mRefCt(1) {
+ 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;
+ }
+
+ // 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<AudioDeviceMessageRunnable> runnable =
+ new AudioDeviceMessageRunnable(this, 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 {
+ NPAudioDeviceStateChangedIPC deviceStateIPC;
+ deviceStateIPC.device = device_id ? std::wstring(device_id) : L"";
+ deviceStateIPC.state = (uint32_t)new_state;
+
+ // Make sure that plugin is notified on the main thread.
+ RefPtr<AudioDeviceMessageRunnable> runnable =
+ new AudioDeviceMessageRunnable(this, deviceStateIPC);
+ NS_DispatchToMainThread(runnable);
+ 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(); }
+
+ const PluginModuleSet* GetModuleSet() const { return &mAudioNotificationSet; }
+
+ 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;
+
+ ~AudioNotification() {
+ MOZ_ASSERT(!mIsRegistered,
+ "Destroying AudioNotification without first calling Unregister");
+ if (mDeviceEnum) {
+ mDeviceEnum->Release();
+ }
+ }
+}; // 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;
+}
+
+AudioDeviceMessageRunnable::AudioDeviceMessageRunnable(
+ AudioNotification* aAudioNotification,
+ NPAudioDeviceChangeDetailsIPC aChangeDetails)
+ : Runnable("AudioDeviceMessageRunnableCD"),
+ mChangeDetails(aChangeDetails),
+ mMessageType(DEFAULT_DEVICE_CHANGED),
+ mAudioNotification(aAudioNotification) {
+ // We increment the AudioNotification ref-count here -- the runnable will
+ // decrement it when it is done with us.
+ mAudioNotification->AddRef();
+}
+
+AudioDeviceMessageRunnable::AudioDeviceMessageRunnable(
+ AudioNotification* aAudioNotification,
+ NPAudioDeviceStateChangedIPC aDeviceState)
+ : Runnable("AudioDeviceMessageRunnableSC"),
+ mDeviceState(aDeviceState),
+ mMessageType(DEVICE_STATE_CHANGED),
+ mAudioNotification(aAudioNotification) {
+ // We increment the AudioNotification ref-count here -- the runnable will
+ // decrement it when it is done with us.
+ mAudioNotification->AddRef();
+}
+
+NS_IMETHODIMP
+AudioDeviceMessageRunnable::Run() {
+ StaticMutexAutoLock lock(sMutex);
+ PLUGIN_LOG_DEBUG(("Notifying %d plugins of audio device change.",
+ mAudioNotification->GetModuleSet()->Count()));
+
+ bool success = true;
+ for (auto iter = mAudioNotification->GetModuleSet()->ConstIter();
+ !iter.Done(); iter.Next()) {
+ PluginModuleParent* pluginModule = iter.Get()->GetKey();
+ switch (mMessageType) {
+ case DEFAULT_DEVICE_CHANGED:
+ success &= pluginModule->SendNPP_SetValue_NPNVaudioDeviceChangeDetails(
+ mChangeDetails);
+ break;
+ case DEVICE_STATE_CHANGED:
+ success &= pluginModule->SendNPP_SetValue_NPNVaudioDeviceStateChanged(
+ mDeviceState);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("bad AudioDeviceMessageRunnable state");
+ }
+ }
+ mAudioNotification->Release();
+ return success ? NS_OK : NS_ERROR_FAILURE;
+}
+
+} // 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 0000000000..f3afa79d2c
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsWin.h
@@ -0,0 +1,27 @@
+/* -*- 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"
+#include "nscore.h"
+
+namespace mozilla {
+namespace plugins {
+
+class PluginModuleParent;
+
+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 0000000000..c25c8529fd
--- /dev/null
+++ b/dom/plugins/ipc/PluginWidgetChild.cpp
@@ -0,0 +1,60 @@
+/* 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/BrowserChild.h"
+#include "mozilla/plugins/PluginWidgetParent.h"
+#include "PluginWidgetProxy.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDebug.h"
+
+#include "mozilla/plugins/PluginInstanceParent.h"
+
+#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::BrowserChild*>(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 0000000000..1fc2d6ee98
--- /dev/null
+++ b/dom/plugins/ipc/PluginWidgetChild.h
@@ -0,0 +1,41 @@
+/* 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
+
+#ifndef XP_WIN
+# error "This header should be Windows-only."
+#endif
+
+#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 0000000000..0def364dc9
--- /dev/null
+++ b/dom/plugins/ipc/PluginWidgetParent.cpp
@@ -0,0 +1,168 @@
+/* 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/BrowserParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsWidgetsCID.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDebug.h"
+
+using namespace mozilla;
+using namespace mozilla::widget;
+
+#define PWLOG(...)
+//#define PWLOG(...) printf_stderr(__VA_ARGS__)
+
+namespace mozilla {
+namespace dom {
+// For nsWindow
+const wchar_t* kPluginWidgetContentParentProperty =
+ L"kPluginWidgetParentProperty";
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla {
+namespace plugins {
+
+// This macro returns IPC_OK() 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 IPC_OK(); \
+ } \
+ }
+
+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::BrowserParent* PluginWidgetParent::GetBrowserParent() {
+ return static_cast<mozilla::dom::BrowserParent*>(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.
+
+mozilla::ipc::IPCResult PluginWidgetParent::RecvCreate(
+ nsresult* aResult, uint64_t* aScrollCaptureId,
+ uintptr_t* aPluginInstanceId) {
+ PWLOG("PluginWidgetParent::RecvCreate()\n");
+
+ *aScrollCaptureId = 0;
+ *aPluginInstanceId = 0;
+
+ mWidget = nsIWidget::CreateChildWindow();
+ *aResult = mWidget ? NS_OK : NS_ERROR_FAILURE;
+
+ // This returns the top level window widget
+ nsCOMPtr<nsIWidget> parentWidget = GetBrowserParent()->GetWidget();
+ // If this fails, bail.
+ if (!parentWidget) {
+ *aResult = NS_ERROR_NOT_AVAILABLE;
+ KillWidget();
+ return IPC_OK();
+ }
+
+ nsWidgetInitData initData;
+ initData.mWindowType = eWindowType_plugin_ipc_chrome;
+ 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 IPC_FAIL_NO_REASON(this);
+ }
+
+ mWidget->EnableDragDrop(true);
+
+ // 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 IPC_OK();
+}
+
+void PluginWidgetParent::KillWidget() {
+ PWLOG("PluginWidgetParent::KillWidget() widget=%p\n", (void*)mWidget.get());
+ if (mWidget) {
+ mWidget->UnregisterPluginWindowForRemoteUpdates();
+ mWidget->Destroy();
+ ::RemovePropW((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW),
+ mozilla::dom::kPluginWidgetContentParentProperty);
+ mWidget = nullptr;
+ }
+}
+
+void PluginWidgetParent::ActorDestroy(ActorDestroyReason aWhy) {
+ PWLOG("PluginWidgetParent::ActorDestroy(%d)\n", aWhy);
+ KillWidget();
+}
+
+// Called by BrowserParent'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");
+}
+
+mozilla::ipc::IPCResult PluginWidgetParent::RecvSetFocus(
+ const bool& aRaise, const mozilla::dom::CallerType& aCallerType) {
+ ENSURE_CHANNEL;
+ PWLOG("PluginWidgetParent::RecvSetFocus(%d)\n", aRaise);
+ mWidget->SetFocus(aRaise ? nsIWidget::Raise::Yes : nsIWidget::Raise::No,
+ aCallerType);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginWidgetParent::RecvGetNativePluginPort(
+ uintptr_t* value) {
+ ENSURE_CHANNEL;
+ *value = (uintptr_t)mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
+ NS_ASSERTION(*value, "no native port??");
+ PWLOG("PluginWidgetParent::RecvGetNativeData() %p\n", (void*)*value);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult PluginWidgetParent::RecvSetNativeChildWindow(
+ const uintptr_t& aChildWindow) {
+ ENSURE_CHANNEL;
+ PWLOG("PluginWidgetParent::RecvSetNativeChildWindow(%p)\n",
+ (void*)aChildWindow);
+ mWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW, aChildWindow);
+ return IPC_OK();
+}
+
+} // namespace plugins
+} // namespace mozilla
diff --git a/dom/plugins/ipc/PluginWidgetParent.h b/dom/plugins/ipc/PluginWidgetParent.h
new file mode 100644
index 0000000000..78228345cb
--- /dev/null
+++ b/dom/plugins/ipc/PluginWidgetParent.h
@@ -0,0 +1,64 @@
+/* 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
+
+#ifndef XP_WIN
+# error "This header should be Windows-only."
+#endif
+
+#include "mozilla/plugins/PPluginWidgetParent.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIWidget.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+
+namespace dom {
+class BrowserParent;
+} // namespace dom
+
+namespace plugins {
+
+class PluginWidgetParent : public PPluginWidgetParent {
+ public:
+ PluginWidgetParent();
+ virtual ~PluginWidgetParent();
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ virtual mozilla::ipc::IPCResult RecvCreate(
+ nsresult* aResult, uint64_t* aScrollCaptureId,
+ uintptr_t* aPluginInstanceId) override;
+ virtual mozilla::ipc::IPCResult RecvSetFocus(
+ const bool& aRaise, const mozilla::dom::CallerType& aCallerType) override;
+ virtual mozilla::ipc::IPCResult RecvGetNativePluginPort(
+ uintptr_t* value) override;
+ mozilla::ipc::IPCResult 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::BrowserParent* GetBrowserParent();
+
+ private:
+ void KillWidget();
+
+ // The chrome side native widget.
+ nsCOMPtr<nsIWidget> mWidget;
+};
+
+} // 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 0000000000..a08f37e723
--- /dev/null
+++ b/dom/plugins/ipc/StreamNotifyChild.h
@@ -0,0 +1,60 @@
+/* -*- 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;
+ friend class PStreamNotifyChild;
+
+ 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:
+ mozilla::ipc::IPCResult Recv__delete__(const NPReason& reason);
+
+ mozilla::ipc::IPCResult RecvRedirectNotify(const nsCString& url,
+ const int32_t& status);
+
+ /**
+ * 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 0000000000..6020f702ac
--- /dev/null
+++ b/dom/plugins/ipc/StreamNotifyParent.h
@@ -0,0 +1,41 @@
+/* -*- 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;
+ friend class PStreamNotifyParent;
+
+ 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:
+ mozilla::ipc::IPCResult RecvRedirectNotifyResponse(const bool& allow);
+
+ 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 0000000000..79cdfc74b4
--- /dev/null
+++ b/dom/plugins/ipc/hangui/HangUIDlg.h
@@ -0,0 +1,17 @@
+/* -*- 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 0000000000..62e98ca249
--- /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 0000000000..9782330b12
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmBase.h
@@ -0,0 +1,280 @@
+/* -*- 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 ||
+ (int)T::identifier <= (int)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 ||
+ (int)T::identifier > (int)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 0000000000..ec5a79714f
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmChild.cpp
@@ -0,0 +1,160 @@
+/* -*- 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 0000000000..ddaa3277b2
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmChild.h
@@ -0,0 +1,63 @@
+/* -*- 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 0000000000..c0880f18cc
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUI.h
@@ -0,0 +1,39 @@
+/* -*- 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 0000000000..01bae5cb68
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUIChild.cpp
@@ -0,0 +1,386 @@
+/* -*- 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 0000000000..d21c717666
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUIChild.h
@@ -0,0 +1,100 @@
+/* -*- 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 0000000000..d11506f4a1
--- /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 0000000000..db07f43d9b
--- /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
+
+DisableStlWrapping()
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ 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 0000000000..f5b7345f99
--- /dev/null
+++ b/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest
@@ -0,0 +1,37 @@
+<?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}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/dom/plugins/ipc/interpose/moz.build b/dom/plugins/ipc/interpose/moz.build
new file mode 100644
index 0000000000..ee24953570
--- /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 0000000000..7f7e0ea372
--- /dev/null
+++ b/dom/plugins/ipc/interpose/plugin_child_interpose.mm
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// 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::
+// PerformAsyncLaunch()). 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 0000000000..62a726e25c
--- /dev/null
+++ b/dom/plugins/ipc/moz.build
@@ -0,0 +1,149 @@
+# -*- 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",
+ "ChildTimer.h",
+ "FunctionBrokerIPCUtils.h",
+ "IpdlTuple.h",
+ "NPEventAndroid.h",
+ "NPEventOSX.h",
+ "NPEventUnix.h",
+ "NPEventWindows.h",
+ "PluginBridge.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",
+ "PluginUtilsOSX.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",
+ "ChildTimer.cpp",
+ "FunctionBroker.cpp",
+ "FunctionBrokerChild.cpp",
+ "FunctionBrokerIPCUtils.cpp",
+ "FunctionBrokerParent.cpp",
+ "FunctionHook.cpp",
+ "PluginBackgroundDestroyer.cpp",
+ "PluginInstanceParent.cpp",
+ "PluginMessageUtils.cpp",
+ "PluginModuleChild.cpp",
+ "PluginModuleParent.cpp",
+ "PluginProcessChild.cpp",
+ "PluginProcessParent.cpp",
+ "PluginQuirks.cpp",
+ "PluginScriptableObjectChild.cpp",
+ "PluginScriptableObjectParent.cpp",
+]
+
+SOURCES += [
+ "PluginInstanceChild.cpp", # 'PluginThreadCallback' : ambiguous symbol
+]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ UNIFIED_SOURCES += [
+ "PluginInterposeOSX.mm",
+ "PluginUtilsOSX.mm",
+ ]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
+ EXPORTS.mozilla.plugins += [
+ "PluginWidgetChild.h",
+ "PluginWidgetParent.h",
+ ]
+ UNIFIED_SOURCES += ["D3D11SurfaceHolder.cpp", "PluginUtilsWin.cpp"]
+ SOURCES += [
+ "PluginWidgetChild.cpp",
+ "PluginWidgetParent.cpp",
+ ]
+
+IPDL_SOURCES += [
+ "PBrowserStream.ipdl",
+ "PFunctionBroker.ipdl",
+ "PluginTypes.ipdlh",
+ "PPluginBackgroundDestroyer.ipdl",
+ "PPluginInstance.ipdl",
+ "PPluginModule.ipdl",
+ "PPluginScriptableObject.ipdl",
+ "PPluginSurface.ipdl",
+ "PStreamNotify.ipdl",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+LOCAL_INCLUDES += [
+ "../base",
+ "/xpcom/base/",
+ "/xpcom/threads/",
+]
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "WINNT":
+ LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+ "/security/sandbox/chromium-shim",
+ "/security/sandbox/win/src/sandboxpermissions",
+ ]
+
+DEFINES["FORCE_PR_LOG"] = True
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] != "gtk":
+ 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["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=shadow"]