summaryrefslogtreecommitdiffstats
path: root/dom/plugins/ipc/hangui
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/ipc/hangui')
-rw-r--r--dom/plugins/ipc/hangui/HangUIDlg.h18
-rw-r--r--dom/plugins/ipc/hangui/HangUIDlg.rc26
-rw-r--r--dom/plugins/ipc/hangui/MiniShmBase.h334
-rw-r--r--dom/plugins/ipc/hangui/MiniShmChild.cpp173
-rw-r--r--dom/plugins/ipc/hangui/MiniShmChild.h68
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUI.h54
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUIChild.cpp425
-rw-r--r--dom/plugins/ipc/hangui/PluginHangUIChild.h113
-rw-r--r--dom/plugins/ipc/hangui/module.ver6
-rw-r--r--dom/plugins/ipc/hangui/moz.build27
-rw-r--r--dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest38
11 files changed, 1282 insertions, 0 deletions
diff --git a/dom/plugins/ipc/hangui/HangUIDlg.h b/dom/plugins/ipc/hangui/HangUIDlg.h
new file mode 100644
index 000000000..47339acc2
--- /dev/null
+++ b/dom/plugins/ipc/hangui/HangUIDlg.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_plugins_HangUIDlg_h
+#define mozilla_plugins_HangUIDlg_h
+
+#define IDD_HANGUIDLG 102
+#define IDC_MSG 1000
+#define IDC_CONTINUE 1001
+#define IDC_STOP 1002
+#define IDC_NOFUTURE 1003
+#define IDC_DLGICON 1004
+
+#endif // mozilla_plugins_HangUIDlg_h
+
diff --git a/dom/plugins/ipc/hangui/HangUIDlg.rc b/dom/plugins/ipc/hangui/HangUIDlg.rc
new file mode 100644
index 000000000..62e98ca24
--- /dev/null
+++ b/dom/plugins/ipc/hangui/HangUIDlg.rc
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "HangUIDlg.h"
+#include <windows.h>
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_HANGUIDLG DIALOGEX 0, 0, 400, 75
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "Continue",IDC_CONTINUE,283,51,50,18
+ PUSHBUTTON "Stop",IDC_STOP,341,51,50,18
+ CONTROL "Check1",IDC_NOFUTURE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,37,32,354,10
+ LTEXT "Static",IDC_MSG,37,7,353,24
+ ICON "",IDC_DLGICON,7,7,20,20
+END
+
diff --git a/dom/plugins/ipc/hangui/MiniShmBase.h b/dom/plugins/ipc/hangui/MiniShmBase.h
new file mode 100644
index 000000000..0ac8840dd
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmBase.h
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_plugins_MiniShmBase_h
+#define mozilla_plugins_MiniShmBase_h
+
+#include "base/basictypes.h"
+
+#include "nsDebug.h"
+
+#include <windows.h>
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This class is used to provide RAII semantics for mapped views.
+ * @see ScopedHandle
+ */
+class ScopedMappedFileView
+{
+public:
+ explicit
+ ScopedMappedFileView(LPVOID aView)
+ : mView(aView)
+ {
+ }
+
+ ~ScopedMappedFileView()
+ {
+ Close();
+ }
+
+ void
+ Close()
+ {
+ if (mView) {
+ ::UnmapViewOfFile(mView);
+ mView = nullptr;
+ }
+ }
+
+ void
+ Set(LPVOID aView)
+ {
+ Close();
+ mView = aView;
+ }
+
+ LPVOID
+ Get() const
+ {
+ return mView;
+ }
+
+ LPVOID
+ Take()
+ {
+ LPVOID result = mView;
+ mView = nullptr;
+ return result;
+ }
+
+ operator LPVOID()
+ {
+ return mView;
+ }
+
+ bool
+ IsValid() const
+ {
+ return (mView);
+ }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedMappedFileView);
+
+ LPVOID mView;
+};
+
+class MiniShmBase;
+
+class MiniShmObserver
+{
+public:
+ /**
+ * This function is called whenever there is a new shared memory request.
+ * @param aMiniShmObj MiniShmBase object that may be used to read and
+ * write from shared memory.
+ */
+ virtual void OnMiniShmEvent(MiniShmBase *aMiniShmObj) = 0;
+ /**
+ * This function is called once when a MiniShmParent and a MiniShmChild
+ * object have successfully negotiated a connection.
+ *
+ * @param aMiniShmObj MiniShmBase object that may be used to read and
+ * write from shared memory.
+ */
+ virtual void OnMiniShmConnect(MiniShmBase *aMiniShmObj) { }
+};
+
+/**
+ * Base class for MiniShm connections. This class defines the common
+ * interfaces and code between parent and child.
+ */
+class MiniShmBase
+{
+public:
+ /**
+ * Obtains a writable pointer into shared memory of type T.
+ * typename T must be plain-old-data and contain an unsigned integral
+ * member T::identifier that uniquely identifies T with respect to
+ * other types used by the protocol being implemented.
+ *
+ * @param aPtr Pointer to receive the shared memory address.
+ * This value is set if and only if the function
+ * succeeded.
+ * @return NS_OK if and only if aPtr was successfully obtained.
+ * NS_ERROR_ILLEGAL_VALUE if type T is not valid for MiniShm.
+ * NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
+ * NS_ERROR_NOT_AVAILABLE if the memory is not safe to write.
+ */
+ template<typename T> nsresult
+ GetWritePtr(T*& aPtr)
+ {
+ if (!mWriteHeader || !mGuard) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (sizeof(T) > mPayloadMaxLen ||
+ T::identifier <= RESERVED_CODE_LAST) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (::WaitForSingleObject(mGuard, mTimeout) != WAIT_OBJECT_0) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ mWriteHeader->mId = T::identifier;
+ mWriteHeader->mPayloadLen = sizeof(T);
+ aPtr = reinterpret_cast<T*>(mWriteHeader + 1);
+ return NS_OK;
+ }
+
+ /**
+ * Obtains a readable pointer into shared memory of type T.
+ * typename T must be plain-old-data and contain an unsigned integral
+ * member T::identifier that uniquely identifies T with respect to
+ * other types used by the protocol being implemented.
+ *
+ * @param aPtr Pointer to receive the shared memory address.
+ * This value is set if and only if the function
+ * succeeded.
+ * @return NS_OK if and only if aPtr was successfully obtained.
+ * NS_ERROR_ILLEGAL_VALUE if type T is not valid for MiniShm or if
+ * type T does not match the type of the data
+ * stored in shared memory.
+ * NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
+ */
+ template<typename T> nsresult
+ GetReadPtr(const T*& aPtr)
+ {
+ if (!mReadHeader) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (mReadHeader->mId != T::identifier ||
+ sizeof(T) != mReadHeader->mPayloadLen) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ aPtr = reinterpret_cast<const T*>(mReadHeader + 1);
+ return NS_OK;
+ }
+
+ /**
+ * Fires the peer's event causing its request handler to execute.
+ *
+ * @return Should return NS_OK if the send was successful.
+ */
+ virtual nsresult
+ Send() = 0;
+
+protected:
+ /**
+ * MiniShm reserves some identifier codes for its own use. Any
+ * identifiers used by MiniShm protocol implementations must be
+ * greater than RESERVED_CODE_LAST.
+ */
+ enum ReservedCodes
+ {
+ RESERVED_CODE_INIT = 0,
+ RESERVED_CODE_INIT_COMPLETE = 1,
+ RESERVED_CODE_LAST = RESERVED_CODE_INIT_COMPLETE
+ };
+
+ struct MiniShmHeader
+ {
+ unsigned int mId;
+ unsigned int mPayloadLen;
+ };
+
+ struct MiniShmInit
+ {
+ enum identifier_t
+ {
+ identifier = RESERVED_CODE_INIT
+ };
+ HANDLE mParentEvent;
+ HANDLE mParentGuard;
+ HANDLE mChildEvent;
+ HANDLE mChildGuard;
+ };
+
+ struct MiniShmInitComplete
+ {
+ enum identifier_t
+ {
+ identifier = RESERVED_CODE_INIT_COMPLETE
+ };
+ bool mSucceeded;
+ };
+
+ MiniShmBase()
+ : mObserver(nullptr),
+ mWriteHeader(nullptr),
+ mReadHeader(nullptr),
+ mPayloadMaxLen(0),
+ mGuard(nullptr),
+ mTimeout(INFINITE)
+ {
+ }
+ virtual ~MiniShmBase()
+ { }
+
+ virtual void
+ OnEvent()
+ {
+ if (mObserver) {
+ mObserver->OnMiniShmEvent(this);
+ }
+ }
+
+ virtual void
+ OnConnect()
+ {
+ if (mObserver) {
+ mObserver->OnMiniShmConnect(this);
+ }
+ }
+
+ nsresult
+ SetView(LPVOID aView, const unsigned int aSize, bool aIsChild)
+ {
+ if (!aView || aSize <= 2 * sizeof(MiniShmHeader)) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ // Divide the region into halves for parent and child
+ if (aIsChild) {
+ mReadHeader = static_cast<MiniShmHeader*>(aView);
+ mWriteHeader = reinterpret_cast<MiniShmHeader*>(static_cast<char*>(aView)
+ + aSize / 2U);
+ } else {
+ mWriteHeader = static_cast<MiniShmHeader*>(aView);
+ mReadHeader = reinterpret_cast<MiniShmHeader*>(static_cast<char*>(aView)
+ + aSize / 2U);
+ }
+ mPayloadMaxLen = aSize / 2U - sizeof(MiniShmHeader);
+ return NS_OK;
+ }
+
+ nsresult
+ SetGuard(HANDLE aGuard, DWORD aTimeout)
+ {
+ if (!aGuard || !aTimeout) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ mGuard = aGuard;
+ mTimeout = aTimeout;
+ return NS_OK;
+ }
+
+ inline void
+ SetObserver(MiniShmObserver *aObserver) { mObserver = aObserver; }
+
+ /**
+ * Obtains a writable pointer into shared memory of type T. This version
+ * differs from GetWritePtr in that it allows typename T to be one of
+ * the private data structures declared in MiniShmBase.
+ *
+ * @param aPtr Pointer to receive the shared memory address.
+ * This value is set if and only if the function
+ * succeeded.
+ * @return NS_OK if and only if aPtr was successfully obtained.
+ * NS_ERROR_ILLEGAL_VALUE if type T not an internal MiniShm struct.
+ * NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
+ */
+ template<typename T> nsresult
+ GetWritePtrInternal(T*& aPtr)
+ {
+ if (!mWriteHeader) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (sizeof(T) > mPayloadMaxLen ||
+ T::identifier > RESERVED_CODE_LAST) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ mWriteHeader->mId = T::identifier;
+ mWriteHeader->mPayloadLen = sizeof(T);
+ aPtr = reinterpret_cast<T*>(mWriteHeader + 1);
+ return NS_OK;
+ }
+
+ static VOID CALLBACK
+ SOnEvent(PVOID aContext, BOOLEAN aIsTimer)
+ {
+ MiniShmBase* object = static_cast<MiniShmBase*>(aContext);
+ object->OnEvent();
+ }
+
+private:
+ MiniShmObserver* mObserver;
+ MiniShmHeader* mWriteHeader;
+ MiniShmHeader* mReadHeader;
+ unsigned int mPayloadMaxLen;
+ HANDLE mGuard;
+ DWORD mTimeout;
+
+ DISALLOW_COPY_AND_ASSIGN(MiniShmBase);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_MiniShmBase_h
+
diff --git a/dom/plugins/ipc/hangui/MiniShmChild.cpp b/dom/plugins/ipc/hangui/MiniShmChild.cpp
new file mode 100644
index 000000000..0683340a1
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmChild.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MiniShmChild.h"
+
+#include <limits>
+#include <sstream>
+
+namespace mozilla {
+namespace plugins {
+
+MiniShmChild::MiniShmChild()
+ : mParentEvent(nullptr),
+ mParentGuard(nullptr),
+ mChildEvent(nullptr),
+ mChildGuard(nullptr),
+ mFileMapping(nullptr),
+ mRegWait(nullptr),
+ mView(nullptr),
+ mTimeout(INFINITE)
+{}
+
+MiniShmChild::~MiniShmChild()
+{
+ if (mRegWait) {
+ ::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
+ }
+ if (mParentGuard) {
+ // Try to avoid shutting down while the parent's event handler is running.
+ ::WaitForSingleObject(mParentGuard, mTimeout);
+ ::CloseHandle(mParentGuard);
+ }
+ if (mParentEvent) {
+ ::CloseHandle(mParentEvent);
+ }
+ if (mChildEvent) {
+ ::CloseHandle(mChildEvent);
+ }
+ if (mChildGuard) {
+ ::CloseHandle(mChildGuard);
+ }
+ if (mView) {
+ ::UnmapViewOfFile(mView);
+ }
+ if (mFileMapping) {
+ ::CloseHandle(mFileMapping);
+ }
+}
+
+nsresult
+MiniShmChild::Init(MiniShmObserver* aObserver, const std::wstring& aCookie,
+ const DWORD aTimeout)
+{
+ if (aCookie.empty() || !aTimeout) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (mFileMapping) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+ std::wistringstream iss(aCookie);
+ HANDLE mapHandle = nullptr;
+ iss >> mapHandle;
+ if (!iss) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ ScopedMappedFileView view(::MapViewOfFile(mapHandle,
+ FILE_MAP_WRITE,
+ 0, 0, 0));
+ if (!view.IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+ MEMORY_BASIC_INFORMATION memInfo = {0};
+ SIZE_T querySize = ::VirtualQuery(view, &memInfo, sizeof(memInfo));
+ unsigned int mappingSize = 0;
+ if (querySize) {
+ if (memInfo.RegionSize <= std::numeric_limits<unsigned int>::max()) {
+ mappingSize = static_cast<unsigned int>(memInfo.RegionSize);
+ }
+ }
+ if (!querySize || !mappingSize) {
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv = SetView(view, mappingSize, true);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ const MiniShmInit* initStruct = nullptr;
+ rv = GetReadPtr(initStruct);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!initStruct->mParentEvent || !initStruct->mParentGuard ||
+ !initStruct->mChildEvent || !initStruct->mChildGuard) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = SetGuard(initStruct->mParentGuard, aTimeout);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!::RegisterWaitForSingleObject(&mRegWait,
+ initStruct->mChildEvent,
+ &SOnEvent,
+ this,
+ INFINITE,
+ WT_EXECUTEDEFAULT)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ MiniShmInitComplete* initCompleteStruct = nullptr;
+ rv = GetWritePtrInternal(initCompleteStruct);
+ if (NS_FAILED(rv)) {
+ ::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
+ mRegWait = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ initCompleteStruct->mSucceeded = true;
+
+ // We must set the member variables before we signal the event
+ mFileMapping = mapHandle;
+ mView = view.Take();
+ mParentEvent = initStruct->mParentEvent;
+ mParentGuard = initStruct->mParentGuard;
+ mChildEvent = initStruct->mChildEvent;
+ mChildGuard = initStruct->mChildGuard;
+ SetObserver(aObserver);
+ mTimeout = aTimeout;
+
+ rv = Send();
+ if (NS_FAILED(rv)) {
+ initCompleteStruct->mSucceeded = false;
+ mFileMapping = nullptr;
+ view.Set(mView);
+ mView = nullptr;
+ mParentEvent = nullptr;
+ mParentGuard = nullptr;
+ mChildEvent = nullptr;
+ mChildGuard = nullptr;
+ ::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
+ mRegWait = nullptr;
+ return rv;
+ }
+
+ OnConnect();
+ return NS_OK;
+}
+
+nsresult
+MiniShmChild::Send()
+{
+ if (!mParentEvent) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ if (!::SetEvent(mParentEvent)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+void
+MiniShmChild::OnEvent()
+{
+ MiniShmBase::OnEvent();
+ ::SetEvent(mChildGuard);
+}
+
+} // namespace plugins
+} // namespace mozilla
+
diff --git a/dom/plugins/ipc/hangui/MiniShmChild.h b/dom/plugins/ipc/hangui/MiniShmChild.h
new file mode 100644
index 000000000..19c9deea7
--- /dev/null
+++ b/dom/plugins/ipc/hangui/MiniShmChild.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_plugins_MiniShmChild_h
+#define mozilla_plugins_MiniShmChild_h
+
+#include "MiniShmBase.h"
+
+#include <string>
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This class provides a lightweight shared memory interface for a child
+ * process in Win32.
+ * This code assumes that there is a parent-child relationship between
+ * processes, as it inherits handles from the parent process.
+ * Note that this class is *not* an IPDL actor.
+ *
+ * @see MiniShmParent
+ */
+class MiniShmChild : public MiniShmBase
+{
+public:
+ MiniShmChild();
+ virtual ~MiniShmChild();
+
+ /**
+ * Initialize shared memory on the child side.
+ *
+ * @param aObserver A MiniShmObserver object to receive event notifications.
+ * @param aCookie Cookie obtained from MiniShmParent::GetCookie
+ * @param aTimeout Timeout in milliseconds.
+ * @return nsresult error code
+ */
+ nsresult
+ Init(MiniShmObserver* aObserver, const std::wstring& aCookie,
+ const DWORD aTimeout);
+
+ virtual nsresult
+ Send() override;
+
+protected:
+ void
+ OnEvent() override;
+
+private:
+ HANDLE mParentEvent;
+ HANDLE mParentGuard;
+ HANDLE mChildEvent;
+ HANDLE mChildGuard;
+ HANDLE mFileMapping;
+ HANDLE mRegWait;
+ LPVOID mView;
+ DWORD mTimeout;
+
+ DISALLOW_COPY_AND_ASSIGN(MiniShmChild);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_MiniShmChild_h
+
diff --git a/dom/plugins/ipc/hangui/PluginHangUI.h b/dom/plugins/ipc/hangui/PluginHangUI.h
new file mode 100644
index 000000000..2c6df78bb
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUI.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_plugins_PluginHangUI_h
+#define mozilla_plugins_PluginHangUI_h
+
+namespace mozilla {
+namespace plugins {
+
+enum HangUIUserResponse
+{
+ HANGUI_USER_RESPONSE_CANCEL = 1,
+ HANGUI_USER_RESPONSE_CONTINUE = 2,
+ HANGUI_USER_RESPONSE_STOP = 4,
+ HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN = 8
+};
+
+enum PluginHangUIStructID
+{
+ PLUGIN_HANGUI_COMMAND = 0x10,
+ PLUGIN_HANGUI_RESULT
+};
+
+struct PluginHangUICommand
+{
+ enum
+ {
+ identifier = PLUGIN_HANGUI_COMMAND
+ };
+ enum CmdCode
+ {
+ HANGUI_CMD_SHOW = 1,
+ HANGUI_CMD_CANCEL = 2
+ };
+ CmdCode mCode;
+};
+
+struct PluginHangUIResponse
+{
+ enum
+ {
+ identifier = PLUGIN_HANGUI_RESULT
+ };
+ unsigned int mResponseBits;
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginHangUI_h
+
diff --git a/dom/plugins/ipc/hangui/PluginHangUIChild.cpp b/dom/plugins/ipc/hangui/PluginHangUIChild.cpp
new file mode 100644
index 000000000..c1730a207
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUIChild.cpp
@@ -0,0 +1,425 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PluginHangUI.h"
+
+#include "PluginHangUIChild.h"
+#include "HangUIDlg.h"
+
+#include <assert.h>
+#include <commctrl.h>
+#include <windowsx.h>
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+namespace mozilla {
+namespace plugins {
+
+struct WinInfo
+{
+ WinInfo(HWND aHwnd, POINT& aPos, SIZE& aSize)
+ :hwnd(aHwnd)
+ {
+ pos.x = aPos.x;
+ pos.y = aPos.y;
+ size.cx = aSize.cx;
+ size.cy = aSize.cy;
+ }
+ HWND hwnd;
+ POINT pos;
+ SIZE size;
+};
+typedef std::vector<WinInfo> WinInfoVec;
+
+PluginHangUIChild* PluginHangUIChild::sSelf = nullptr;
+const int PluginHangUIChild::kExpectedMinimumArgc = 10;
+
+PluginHangUIChild::PluginHangUIChild()
+ : mResponseBits(0),
+ mParentWindow(nullptr),
+ mDlgHandle(nullptr),
+ mMainThread(nullptr),
+ mParentProcess(nullptr),
+ mRegWaitProcess(nullptr),
+ mIPCTimeoutMs(0)
+{
+}
+
+PluginHangUIChild::~PluginHangUIChild()
+{
+ if (mMainThread) {
+ CloseHandle(mMainThread);
+ }
+ if (mRegWaitProcess) {
+ UnregisterWaitEx(mRegWaitProcess, INVALID_HANDLE_VALUE);
+ }
+ if (mParentProcess) {
+ CloseHandle(mParentProcess);
+ }
+ sSelf = nullptr;
+}
+
+bool
+PluginHangUIChild::Init(int aArgc, wchar_t* aArgv[])
+{
+ if (aArgc < kExpectedMinimumArgc) {
+ return false;
+ }
+ unsigned int i = 1;
+ mMessageText = aArgv[i];
+ mWindowTitle = aArgv[++i];
+ mWaitBtnText = aArgv[++i];
+ mKillBtnText = aArgv[++i];
+ mNoFutureText = aArgv[++i];
+ std::wistringstream issHwnd(aArgv[++i]);
+ issHwnd >> reinterpret_cast<HANDLE&>(mParentWindow);
+ if (!issHwnd) {
+ return false;
+ }
+ std::wistringstream issProc(aArgv[++i]);
+ issProc >> mParentProcess;
+ if (!issProc) {
+ return false;
+ }
+ // Only set the App User Model ID if it's present in the args
+ if (wcscmp(aArgv[++i], L"-")) {
+ HMODULE shell32 = LoadLibrary(L"shell32.dll");
+ if (shell32) {
+ SETAPPUSERMODELID fSetAppUserModelID = (SETAPPUSERMODELID)
+ GetProcAddress(shell32, "SetCurrentProcessExplicitAppUserModelID");
+ if (fSetAppUserModelID) {
+ fSetAppUserModelID(aArgv[i]);
+ }
+ FreeLibrary(shell32);
+ }
+ }
+ std::wistringstream issTimeout(aArgv[++i]);
+ issTimeout >> mIPCTimeoutMs;
+ if (!issTimeout) {
+ return false;
+ }
+
+ nsresult rv = mMiniShm.Init(this,
+ std::wstring(aArgv[++i]),
+ IsDebuggerPresent() ? INFINITE : mIPCTimeoutMs);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ sSelf = this;
+ return true;
+}
+
+void
+PluginHangUIChild::OnMiniShmEvent(MiniShmBase* aMiniShmObj)
+{
+ const PluginHangUICommand* cmd = nullptr;
+ nsresult rv = aMiniShmObj->GetReadPtr(cmd);
+ assert(NS_SUCCEEDED(rv));
+ bool returnStatus = false;
+ if (NS_SUCCEEDED(rv)) {
+ switch (cmd->mCode) {
+ case PluginHangUICommand::HANGUI_CMD_SHOW:
+ returnStatus = RecvShow();
+ break;
+ case PluginHangUICommand::HANGUI_CMD_CANCEL:
+ returnStatus = RecvCancel();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// static
+INT_PTR CALLBACK
+PluginHangUIChild::SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode,
+ WPARAM aWParam, LPARAM aLParam)
+{
+ PluginHangUIChild *self = PluginHangUIChild::sSelf;
+ if (self) {
+ return self->HangUIDlgProc(aDlgHandle, aMsgCode, aWParam, aLParam);
+ }
+ return FALSE;
+}
+
+void
+PluginHangUIChild::ResizeButtons()
+{
+ // Control IDs are specified right-to-left as they appear in the dialog
+ UINT ids[] = { IDC_STOP, IDC_CONTINUE };
+ UINT numIds = sizeof(ids)/sizeof(ids[0]);
+
+ // Pass 1: Compute the ideal size
+ bool needResizing = false;
+ SIZE idealSize = {0};
+ WinInfoVec winInfo;
+ for (UINT i = 0; i < numIds; ++i) {
+ HWND wnd = GetDlgItem(mDlgHandle, ids[i]);
+ if (!wnd) {
+ return;
+ }
+
+ // Get the button's dimensions in screen coordinates
+ RECT curRect;
+ if (!GetWindowRect(wnd, &curRect)) {
+ return;
+ }
+
+ // Get (x,y) position of the button in client coordinates
+ POINT pt;
+ pt.x = curRect.left;
+ pt.y = curRect.top;
+ if (!ScreenToClient(mDlgHandle, &pt)) {
+ return;
+ }
+
+ // Request the button's text margins
+ RECT margins;
+ if (!Button_GetTextMargin(wnd, &margins)) {
+ return;
+ }
+
+ // Compute the button's width and height
+ SIZE curSize;
+ curSize.cx = curRect.right - curRect.left;
+ curSize.cy = curRect.bottom - curRect.top;
+
+ // Request the button's ideal width and height and add in the margins
+ SIZE size = {0};
+ if (!Button_GetIdealSize(wnd, &size)) {
+ return;
+ }
+ size.cx += margins.left + margins.right;
+ size.cy += margins.top + margins.bottom;
+
+ // Size all buttons to be the same width as the longest button encountered
+ idealSize.cx = std::max(idealSize.cx, size.cx);
+ idealSize.cy = std::max(idealSize.cy, size.cy);
+
+ // We won't bother resizing unless we need extra space
+ if (idealSize.cx > curSize.cx) {
+ needResizing = true;
+ }
+
+ // Save the relevant info for the resize, if any. We do this even if
+ // needResizing is false because another button may trigger a resize later.
+ winInfo.push_back(WinInfo(wnd, pt, curSize));
+ }
+
+ if (!needResizing) {
+ return;
+ }
+
+ // Pass 2: Resize the windows
+ int deltaX = 0;
+ HDWP hwp = BeginDeferWindowPos((int) winInfo.size());
+ if (!hwp) {
+ return;
+ }
+ for (WinInfoVec::const_iterator itr = winInfo.begin();
+ itr != winInfo.end(); ++itr) {
+ // deltaX accumulates the size changes so that each button's x coordinate
+ // can compensate for the width increases
+ deltaX += idealSize.cx - itr->size.cx;
+ hwp = DeferWindowPos(hwp, itr->hwnd, nullptr,
+ itr->pos.x - deltaX, itr->pos.y,
+ idealSize.cx, itr->size.cy,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ if (!hwp) {
+ return;
+ }
+ }
+ EndDeferWindowPos(hwp);
+}
+
+INT_PTR
+PluginHangUIChild::HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
+ LPARAM aLParam)
+{
+ mDlgHandle = aDlgHandle;
+ switch (aMsgCode) {
+ case WM_INITDIALOG: {
+ // Register a wait on the Firefox process so that we will be informed
+ // if it dies while the dialog is showing
+ RegisterWaitForSingleObject(&mRegWaitProcess,
+ mParentProcess,
+ &SOnParentProcessExit,
+ this,
+ INFINITE,
+ WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE);
+ SetWindowText(aDlgHandle, mWindowTitle);
+ SetDlgItemText(aDlgHandle, IDC_MSG, mMessageText);
+ SetDlgItemText(aDlgHandle, IDC_NOFUTURE, mNoFutureText);
+ SetDlgItemText(aDlgHandle, IDC_CONTINUE, mWaitBtnText);
+ SetDlgItemText(aDlgHandle, IDC_STOP, mKillBtnText);
+ ResizeButtons();
+ HANDLE icon = LoadImage(nullptr, IDI_QUESTION, IMAGE_ICON, 0, 0,
+ LR_DEFAULTSIZE | LR_SHARED);
+ if (icon) {
+ SendDlgItemMessage(aDlgHandle, IDC_DLGICON, STM_SETICON, (WPARAM)icon, 0);
+ }
+ EnableWindow(mParentWindow, FALSE);
+ return TRUE;
+ }
+ case WM_CLOSE: {
+ mResponseBits |= HANGUI_USER_RESPONSE_CANCEL;
+ EndDialog(aDlgHandle, 0);
+ SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+ case WM_COMMAND: {
+ switch (LOWORD(aWParam)) {
+ case IDC_CONTINUE:
+ if (HIWORD(aWParam) == BN_CLICKED) {
+ mResponseBits |= HANGUI_USER_RESPONSE_CONTINUE;
+ EndDialog(aDlgHandle, 1);
+ SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+ break;
+ case IDC_STOP:
+ if (HIWORD(aWParam) == BN_CLICKED) {
+ mResponseBits |= HANGUI_USER_RESPONSE_STOP;
+ EndDialog(aDlgHandle, 1);
+ SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+ break;
+ case IDC_NOFUTURE:
+ if (HIWORD(aWParam) == BN_CLICKED) {
+ if (Button_GetCheck(GetDlgItem(aDlgHandle,
+ IDC_NOFUTURE)) == BST_CHECKED) {
+ mResponseBits |= HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN;
+ } else {
+ mResponseBits &=
+ ~static_cast<DWORD>(HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN);
+ }
+ SetWindowLongPtr(aDlgHandle, DWLP_MSGRESULT, 0);
+ return TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case WM_DESTROY: {
+ EnableWindow(mParentWindow, TRUE);
+ SetForegroundWindow(mParentWindow);
+ break;
+ }
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+// static
+VOID CALLBACK
+PluginHangUIChild::SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer)
+{
+ // Simulate a cancel if the parent process died
+ PluginHangUIChild* object = static_cast<PluginHangUIChild*>(aObject);
+ object->RecvCancel();
+}
+
+bool
+PluginHangUIChild::RecvShow()
+{
+ return (QueueUserAPC(&ShowAPC,
+ mMainThread,
+ reinterpret_cast<ULONG_PTR>(this)));
+}
+
+bool
+PluginHangUIChild::Show()
+{
+ INT_PTR dlgResult = DialogBox(GetModuleHandle(nullptr),
+ MAKEINTRESOURCE(IDD_HANGUIDLG),
+ nullptr,
+ &SHangUIDlgProc);
+ mDlgHandle = nullptr;
+ assert(dlgResult != -1);
+ bool result = false;
+ if (dlgResult != -1) {
+ PluginHangUIResponse* response = nullptr;
+ nsresult rv = mMiniShm.GetWritePtr(response);
+ if (NS_SUCCEEDED(rv)) {
+ response->mResponseBits = mResponseBits;
+ result = NS_SUCCEEDED(mMiniShm.Send());
+ }
+ }
+ return result;
+}
+
+// static
+VOID CALLBACK
+PluginHangUIChild::ShowAPC(ULONG_PTR aContext)
+{
+ PluginHangUIChild* object = reinterpret_cast<PluginHangUIChild*>(aContext);
+ object->Show();
+}
+
+bool
+PluginHangUIChild::RecvCancel()
+{
+ if (mDlgHandle) {
+ PostMessage(mDlgHandle, WM_CLOSE, 0, 0);
+ }
+ return true;
+}
+
+bool
+PluginHangUIChild::WaitForDismissal()
+{
+ if (!SetMainThread()) {
+ return false;
+ }
+ DWORD waitResult = WaitForSingleObjectEx(mParentProcess,
+ mIPCTimeoutMs,
+ TRUE);
+ return waitResult == WAIT_OBJECT_0 ||
+ waitResult == WAIT_IO_COMPLETION;
+}
+
+bool
+PluginHangUIChild::SetMainThread()
+{
+ if (mMainThread) {
+ CloseHandle(mMainThread);
+ mMainThread = nullptr;
+ }
+ mMainThread = OpenThread(THREAD_SET_CONTEXT,
+ FALSE,
+ GetCurrentThreadId());
+ return !(!mMainThread);
+}
+
+} // namespace plugins
+} // namespace mozilla
+
+#ifdef __MINGW32__
+extern "C"
+#endif
+int
+wmain(int argc, wchar_t *argv[])
+{
+ INITCOMMONCONTROLSEX icc = { sizeof(INITCOMMONCONTROLSEX),
+ ICC_STANDARD_CLASSES };
+ if (!InitCommonControlsEx(&icc)) {
+ return 1;
+ }
+ mozilla::plugins::PluginHangUIChild hangui;
+ if (!hangui.Init(argc, argv)) {
+ return 1;
+ }
+ if (!hangui.WaitForDismissal()) {
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/dom/plugins/ipc/hangui/PluginHangUIChild.h b/dom/plugins/ipc/hangui/PluginHangUIChild.h
new file mode 100644
index 000000000..000e003ec
--- /dev/null
+++ b/dom/plugins/ipc/hangui/PluginHangUIChild.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_plugins_PluginHangUIChild_h
+#define mozilla_plugins_PluginHangUIChild_h
+
+#include "MiniShmChild.h"
+
+#include <string>
+
+#include <windows.h>
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This class implements the plugin-hang-ui.
+ *
+ * NOTE: PluginHangUIChild is *not* an IPDL actor! In this case, "Child"
+ * is describing the fact that plugin-hang-ui is a child process to the
+ * firefox process, which is the PluginHangUIParent.
+ * PluginHangUIParent and PluginHangUIChild are a matched pair.
+ * @see PluginHangUIParent
+ */
+class PluginHangUIChild : public MiniShmObserver
+{
+public:
+ PluginHangUIChild();
+ virtual ~PluginHangUIChild();
+
+ bool
+ Init(int aArgc, wchar_t* aArgv[]);
+
+ /**
+ * Displays the Plugin Hang UI and does not return until the UI has
+ * been dismissed.
+ *
+ * @return true if the UI was displayed and the user response was
+ * successfully sent back to the parent. Otherwise false.
+ */
+ bool
+ Show();
+
+ /**
+ * Causes the calling thread to wait either for the Hang UI to be
+ * dismissed or for the parent process to terminate. This should
+ * be called by the main thread.
+ *
+ * @return true unless there was an error initiating the wait
+ */
+ bool
+ WaitForDismissal();
+
+ virtual void
+ OnMiniShmEvent(MiniShmBase* aMiniShmObj) override;
+
+private:
+ bool
+ RecvShow();
+
+ bool
+ RecvCancel();
+
+ bool
+ SetMainThread();
+
+ void
+ ResizeButtons();
+
+ INT_PTR
+ HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam, LPARAM aLParam);
+
+ static VOID CALLBACK
+ ShowAPC(ULONG_PTR aContext);
+
+ static INT_PTR CALLBACK
+ SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
+ LPARAM aLParam);
+
+ static VOID CALLBACK
+ SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer);
+
+ static PluginHangUIChild *sSelf;
+
+ const wchar_t* mMessageText;
+ const wchar_t* mWindowTitle;
+ const wchar_t* mWaitBtnText;
+ const wchar_t* mKillBtnText;
+ const wchar_t* mNoFutureText;
+ unsigned int mResponseBits;
+ HWND mParentWindow;
+ HWND mDlgHandle;
+ HANDLE mMainThread;
+ HANDLE mParentProcess;
+ HANDLE mRegWaitProcess;
+ DWORD mIPCTimeoutMs;
+ MiniShmChild mMiniShm;
+
+ static const int kExpectedMinimumArgc;
+
+ typedef HRESULT (WINAPI *SETAPPUSERMODELID)(PCWSTR);
+
+ DISALLOW_COPY_AND_ASSIGN(PluginHangUIChild);
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginHangUIChild_h
+
diff --git a/dom/plugins/ipc/hangui/module.ver b/dom/plugins/ipc/hangui/module.ver
new file mode 100644
index 000000000..d11506f4a
--- /dev/null
+++ b/dom/plugins/ipc/hangui/module.ver
@@ -0,0 +1,6 @@
+WIN32_MODULE_COMPANYNAME=Mozilla Corporation
+WIN32_MODULE_PRODUCTVERSION=@MOZ_APP_WINVERSION@
+WIN32_MODULE_PRODUCTVERSION_STRING=@MOZ_APP_VERSION@
+WIN32_MODULE_DESCRIPTION=Plugin Hang UI for @MOZ_APP_DISPLAYNAME@
+WIN32_MODULE_PRODUCTNAME=@MOZ_APP_DISPLAYNAME@
+WIN32_MODULE_NAME=@MOZ_APP_DISPLAYNAME@
diff --git a/dom/plugins/ipc/hangui/moz.build b/dom/plugins/ipc/hangui/moz.build
new file mode 100644
index 000000000..0b84cfb8f
--- /dev/null
+++ b/dom/plugins/ipc/hangui/moz.build
@@ -0,0 +1,27 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Program('plugin-hang-ui')
+
+UNIFIED_SOURCES += [
+ 'MiniShmChild.cpp',
+ 'PluginHangUIChild.cpp',
+]
+include('/ipc/chromium/chromium-config.mozbuild')
+
+DEFINES['NS_NO_XPCOM'] = True
+DEFINES['_HAS_EXCEPTIONS'] = 0
+
+DISABLE_STL_WRAPPING = True
+
+if CONFIG['GNU_CC']:
+ WIN32_EXE_LDFLAGS += ['-municode']
+
+RCINCLUDE = 'HangUIDlg.rc'
+
+OS_LIBS += [
+ 'comctl32',
+]
diff --git a/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest b/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest
new file mode 100644
index 000000000..8d6149c58
--- /dev/null
+++ b/dom/plugins/ipc/hangui/plugin-hang-ui.exe.manifest
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="*"
+ name="plugin-hang-ui"
+ type="win32"
+/>
+<description>Firefox Plugin Hang User Interface</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+ <ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+ <ms_asmv3:security>
+ <ms_asmv3:requestedPrivileges>
+ <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </ms_asmv3:requestedPrivileges>
+ </ms_asmv3:security>
+ </ms_asmv3:trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ </application>
+ </compatibility>
+</assembly>