Bug 1596812 Part 2 - NSIS WebBrowser plugin. r=agashlin draft
authorMolly Howell <mhowell@mozilla.com>
Tue, 10 Dec 2019 18:30:32 +0000
changeset 2527066 15b69ecd8ad0daf2f78729bbc997dd5e6c746da4
parent 2527065 fe5b36b1c906cc2287c43eda57c37c72b43074ab
child 2527067 0ee8cff383c2eff2e005b4248ed200042243d017
push id463258
push userreviewbot
push dateTue, 10 Dec 2019 18:31:05 +0000
treeherdertry@2ad657296ca4 [default view] [failures only]
reviewersagashlin
bugs1596812
milestone73.0a1
Bug 1596812 Part 2 - NSIS WebBrowser plugin. r=agashlin Summary: This is all the code and build files for an NSIS plugin that enables rendering a web page as the content of an NSIS dialog. Documentation and the compiled binary are in later commits in this series. Depends on D56576 Test Plan: Reviewers: agashlin Subscribers: Bug #: 1596812 Differential Diff: PHID-DIFF-k3cj6jvleppo3zuv726w
other-licenses/nsis/Contrib/WebBrowser/CustomFunctions.cpp
other-licenses/nsis/Contrib/WebBrowser/Timers.cpp
other-licenses/nsis/Contrib/WebBrowser/WebBrowser.cpp
other-licenses/nsis/Contrib/WebBrowser/WebBrowser.h
other-licenses/nsis/Contrib/WebBrowser/WebBrowser.sln
other-licenses/nsis/Contrib/WebBrowser/WebBrowser.vcxproj
other-licenses/nsis/Contrib/WebBrowser/exdll.cpp
other-licenses/nsis/Contrib/WebBrowser/exdll.h
other-licenses/nsis/Contrib/WebBrowser/main.cpp
other-licenses/nsis/Contrib/WebBrowser/resource.h
other-licenses/nsis/Contrib/WebBrowser/resource.rc
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/CustomFunctions.cpp
@@ -0,0 +1,69 @@
+// 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 "WebBrowser.h"
+#include "exdll.h"
+
+extern WebBrowser* gBrowser;
+void Init(HWND hWndParent, int string_size, TCHAR* variables, stack_t** stacktop, extra_parameters* extra);
+
+static void CustomFunctionWrapper(void* NSISFunctionAddr, VARIANT jsArg, VARIANT* retVal) {
+	// Marshal the argument passed to the JavaScript function onto the NSIS stack.
+	switch (jsArg.vt) {
+	case VT_BSTR:
+		pushstring(jsArg.bstrVal);
+		break;
+	case VT_I4: {
+		TCHAR intArgStr[32] = _T("");
+		_itot_s(jsArg.intVal, intArgStr, 10);
+		pushstring(intArgStr);
+		break;
+	}
+	case VT_BOOL:
+		pushstring(jsArg.boolVal == VARIANT_TRUE ? _T("1") : _T("0"));
+		break;
+	default:
+		// No other argument types supported.
+		pushstring(_T(""));
+		break;
+	}
+
+	// Call the NSIS function.
+	int rv = g_executeCodeSegment((int)NSISFunctionAddr, g_hwndParent);
+
+	// Retrieve the return value from the NSIS stack.
+	TCHAR* nsisRetval = (TCHAR*)HeapAlloc(GetProcessHeap(), 0, g_stringsize * sizeof(TCHAR));
+	popstring(nsisRetval);
+
+	// Pass the return value back to JavaScript, if it asked for one.
+	if (retVal) {
+		VariantInit(retVal);
+		retVal->vt = VT_BSTR;
+		retVal->bstrVal = SysAllocString(nsisRetval);
+	}
+
+	HeapFree(GetProcessHeap(), 0, nsisRetval);
+}
+
+PLUGINFUNCTION(RegisterCustomFunction) {
+	if (!gBrowser) {
+		Init(hWndParent, string_size, variables, stacktop, extra);
+	}
+
+	TCHAR* funcAddrStr = (TCHAR*)HeapAlloc(GetProcessHeap(), 0, g_stringsize * sizeof(TCHAR));
+	popstring(funcAddrStr);
+
+	TCHAR* funcName = (TCHAR*)HeapAlloc(GetProcessHeap(), 0, g_stringsize * sizeof(TCHAR));
+	popstring(funcName);
+
+	if (gBrowser && funcAddrStr && funcName) {
+		// Apparently GetFunctionAddress returnes a 1-indexed offset, but
+		// ExecuteCodeSegment expects a 0-indexed one. Or something.
+		uintptr_t funcAddr = static_cast<uintptr_t>(_ttoi64(funcAddrStr)) - 1;
+		gBrowser->AddCustomFunction(funcName, CustomFunctionWrapper, (void*)funcAddr);
+	}
+
+	HeapFree(GetProcessHeap(), 0, funcName);
+	HeapFree(GetProcessHeap(), 0, funcAddrStr);
+}
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/Timers.cpp
@@ -0,0 +1,51 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0.If a copy of the MPL was not distributed with this
+// file, You can obtain one at http ://mozilla.org/MPL/2.0/.
+
+#include <windows.h>
+#include "exdll.h"
+
+static void APIENTRY TimerCallback(LPVOID NSISFunctionAddr, DWORD, DWORD)
+{
+	g_executeCodeSegment((int)NSISFunctionAddr, g_hwndParent);
+}
+
+PLUGINFUNCTION(CreateTimer)
+{
+	TCHAR* funcAddrStr = (TCHAR*)HeapAlloc(GetProcessHeap(), 0, g_stringsize * sizeof(TCHAR));
+	popstring(funcAddrStr);
+	// Apparently GetFunctionAddress returnes a 1-indexed offset, but
+	// ExecuteCodeSegment expects a 0-indexed one. Or something.
+	uintptr_t funcAddr = static_cast<uintptr_t>(_ttoi64(funcAddrStr)) - 1;
+	HeapFree(GetProcessHeap(), 0, funcAddrStr);
+
+	TCHAR* intervalStr = (TCHAR*)HeapAlloc(GetProcessHeap(), 0, g_stringsize * sizeof(TCHAR));
+	popstring(intervalStr);
+	long interval = _ttol(intervalStr);
+	HeapFree(GetProcessHeap(), 0, intervalStr);
+
+	HANDLE timer = CreateWaitableTimer(nullptr, FALSE, nullptr);
+	if (!timer) {
+		return;
+	}
+
+	LARGE_INTEGER dueTime;
+	dueTime.QuadPart = -(interval * 10000);
+
+	SetWaitableTimer(timer, &dueTime, interval, TimerCallback, (void*)funcAddr, FALSE);
+
+	TCHAR timerStr[32] = _T("");
+	_ltot_s((long)timer, timerStr, 10);
+	pushstring(timerStr);
+}
+
+PLUGINFUNCTION(CancelTimer)
+{
+	TCHAR* timerStr = (TCHAR*)HeapAlloc(GetProcessHeap(), 0, g_stringsize * sizeof(TCHAR));
+	popstring(timerStr);
+	HANDLE timer = reinterpret_cast<HANDLE>(_ttoi(timerStr));
+	HeapFree(GetProcessHeap(), 0, timerStr);
+
+	CancelWaitableTimer(timer);
+	CloseHandle(timer);
+}
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/WebBrowser.cpp
@@ -0,0 +1,660 @@
+// 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 "WebBrowser.h"
+#include <mshtmdid.h>
+
+WebBrowser::WebBrowser(HWND _hWndParent)
+	: mHwndParent(_hWndParent)
+{
+	HRESULT hr = ::OleCreate(CLSID_WebBrowser,
+		IID_IOleObject, OLERENDER_DRAW, 0, this, this,
+		(void**)&mOleObject);
+	if (FAILED(hr)) {
+		return;
+	}
+
+	RECT posRect;
+	::GetClientRect(mHwndParent, &posRect);
+
+	hr = mOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE,
+		nullptr, this, 0, mHwndParent, &posRect);
+	if (FAILED(hr)) {
+		mOleObject->Release();	
+		return;
+	}
+
+	SetRect(posRect);
+
+	hr = mOleObject->QueryInterface(&mWebBrowser2);
+	if (FAILED(hr)) {
+		mOleObject->Release();
+		return;
+	}
+
+	mWebBrowser2->put_Silent(VARIANT_TRUE);
+}
+
+WebBrowser::~WebBrowser()
+{
+	if (mWebBrowser2) {
+		mWebBrowser2->Release();
+		mWebBrowser2 = nullptr;
+	}
+  if (mOleInPlaceObject) {
+		mOleInPlaceObject->Release();
+		mOleInPlaceObject = nullptr;
+  }
+	if (mOleObject) {
+		mOleObject->Release();
+		mOleObject = nullptr;
+	}
+}
+
+void WebBrowser::Shutdown() {
+	// SetClientSite() is going to release the reference to us that it holds,
+	// which is the last reference at this point, so that would invoke our
+	// destructor, which would destroy mOleObject. But we'd be in the middle of
+	// a call in mOleObject, so we need to take an artificial reference to
+	// ourself to prevent a crash inside SetClientSite().
+	AddRef();
+
+	if (mOleObject) {
+		mOleObject->Close(OLECLOSE_NOSAVE);
+		mOleObject->SetClientSite(nullptr);
+	}
+
+	// This call should now invoke our destructor.
+	Release();
+}
+
+void WebBrowser::SetRect(const RECT& _rc)
+{
+	mRect = _rc;
+
+	if (mOleInPlaceObject) {
+		mOleInPlaceObject->SetObjectRects(&mRect, &mRect);
+	}
+}
+
+void WebBrowser::Resize(DWORD width, DWORD height)
+{
+  RECT r = mRect;
+  r.bottom = r.top + height;
+  r.right = r.left + width;
+  SetRect(r);
+}
+
+void WebBrowser::Navigate(wchar_t* url)
+{
+  VARIANT flags;
+  VariantInit(&flags);
+  flags.vt = VT_I4;
+  flags.intVal = navNoHistory | navEnforceRestricted | navUntrustedForDownload | navBlockRedirectsXDomain;
+
+	mWebBrowser2->Navigate(url, &flags, nullptr, nullptr, nullptr);
+}
+
+void WebBrowser::AddCustomFunction(wchar_t* name, CustomFunction function, void* arg)
+{
+	const DISPID funcID = InterlockedIncrement(&mNextCustomFunctionID);
+	CustomFunctionRecord record = { name, function, arg };
+
+	// We've disabled exceptions but push_back can throw on an allocation
+	// failure, so we need to suppress a warning trying to tell us that
+	// that combination doesn't make any sense.
+	#pragma warning(suppress : 4530)
+	mCustomFunctions.push_back(record);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IUnknown
+//////////////////////////////////////////////////////////////////////////////
+// This is a standard IUnknown implementation, we don't need anything special.
+
+HRESULT STDMETHODCALLTYPE WebBrowser::QueryInterface(REFIID riid,
+													 void**ppvObject) {
+	if (riid == __uuidof(IUnknown)) {
+		*ppvObject = static_cast<IOleClientSite*>(this);
+  } else if (riid == __uuidof(IOleClientSite)) {
+    *ppvObject = static_cast<IOleClientSite*>(this);
+  } else if (riid == __uuidof(IOleInPlaceSite)) {
+    *ppvObject = static_cast<IOleInPlaceSite*>(this);
+  } else if (riid == __uuidof(IStorage)) {
+    *ppvObject = static_cast<IStorage*>(this);
+  } else if (riid == __uuidof(IDocHostUIHandler)) {
+    *ppvObject = static_cast<IDocHostUIHandler*>(this);
+  } else if (riid == __uuidof(IDocHostShowUI)) {
+    *ppvObject = static_cast<IDocHostShowUI*>(this);
+  } else if (riid == __uuidof(IDispatch)) {
+    *ppvObject = static_cast<IDispatch*>(this);
+  } else {
+    *ppvObject = nullptr;
+		return E_NOINTERFACE;
+	}
+
+	AddRef();
+	return S_OK;
+}
+
+ULONG STDMETHODCALLTYPE WebBrowser::AddRef()
+{
+  return InterlockedIncrement(&mComRefCount);
+}
+
+ULONG STDMETHODCALLTYPE WebBrowser::Release()
+{
+  ULONG refCount = InterlockedDecrement(&mComRefCount);
+  if (refCount == 0) {
+    delete this;
+  }
+  return refCount;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IOleWindow
+//////////////////////////////////////////////////////////////////////////////
+
+HRESULT STDMETHODCALLTYPE WebBrowser::GetWindow( 
+	__RPC__deref_out_opt HWND *phwnd)
+{
+	*phwnd = mHwndParent;
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::ContextSensitiveHelp(
+	BOOL fEnterMode)
+{
+	// We don't provide context-sensitive help.
+	return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IOleInPlaceSite
+//////////////////////////////////////////////////////////////////////////////
+
+HRESULT STDMETHODCALLTYPE WebBrowser::CanInPlaceActivate()
+{
+	// We always support in-place activation.
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceActivate()
+{
+	OleLockRunning(mOleObject, TRUE, FALSE);
+	mOleObject->QueryInterface(&mOleInPlaceObject);
+	mOleInPlaceObject->SetObjectRects(&mRect, &mRect);
+
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnUIActivate()
+{
+	// Nothing to do before activating the control's UI.
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::GetWindowContext( 
+	__RPC__deref_out_opt IOleInPlaceFrame **ppFrame,
+	__RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc,
+	__RPC__out LPRECT lprcPosRect,
+	__RPC__out LPRECT lprcClipRect,
+	__RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo)
+{
+	*ppFrame = nullptr;
+	*ppDoc = nullptr;
+	*lprcPosRect = mRect;
+	*lprcClipRect = mRect;
+
+	lpFrameInfo->fMDIApp = false;
+	lpFrameInfo->hwndFrame = mHwndParent;
+	lpFrameInfo->haccel = nullptr;
+	lpFrameInfo->cAccelEntries = 0;
+
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::Scroll( 
+	SIZE scrollExtant)
+{
+	// We should have disabled all scrollbars.
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnUIDeactivate( 
+	BOOL fUndoable)
+{
+	// Nothing to do after deactivating the control's UI.
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceDeactivate()
+{
+	if (mOleInPlaceObject) {
+		mOleInPlaceObject->Release();
+		mOleInPlaceObject = nullptr;
+	}
+
+	return S_OK;
+}
+
+// We don't support the concept of undo.
+HRESULT STDMETHODCALLTYPE WebBrowser::DiscardUndoState()
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::DeactivateAndUndo()
+{
+	return E_NOTIMPL;
+}
+
+// We don't support moving or resizing the control.
+HRESULT STDMETHODCALLTYPE WebBrowser::OnPosRectChange( 
+	__RPC__in LPCRECT lprcPosRect)
+{
+	return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IOleClientSite
+//////////////////////////////////////////////////////////////////////////////
+// We don't need anything that IOleClientSite does, because we're doing OLE
+// only in the most basic sense and we don't support linking (or, indeed,
+// embedding), but some implementation of this interface is required for
+// OleCreate to work, so we have to have a stub version.
+
+HRESULT STDMETHODCALLTYPE WebBrowser::SaveObject()
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::GetMoniker( 
+	DWORD dwAssign,
+	DWORD dwWhichMoniker,
+	__RPC__deref_out_opt IMoniker **ppmk)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::GetContainer( 
+	__RPC__deref_out_opt IOleContainer **ppContainer)
+{
+	return E_NOINTERFACE;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::ShowObject()
+{
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnShowWindow( 
+	BOOL fShow)
+{
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::RequestNewObjectLayout()
+{
+	return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IStorage
+//////////////////////////////////////////////////////////////////////////////
+// We don't need anything that IStorage does, but we have to pass some
+// implementation of it to OleCreate, so we need to have a stub version.
+
+HRESULT STDMETHODCALLTYPE WebBrowser::CreateStream( 
+	__RPC__in_string const OLECHAR *pwcsName,
+	DWORD grfMode,
+	DWORD reserved1,
+	DWORD reserved2,
+	__RPC__deref_out_opt IStream **ppstm)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OpenStream( 
+	const OLECHAR *pwcsName,
+	void *reserved1,
+	DWORD grfMode,
+	DWORD reserved2,
+	IStream **ppstm)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::CreateStorage( 
+	__RPC__in_string const OLECHAR *pwcsName,
+	DWORD grfMode,
+	DWORD reserved1,
+	DWORD reserved2,
+	__RPC__deref_out_opt IStorage **ppstg)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OpenStorage( 
+	__RPC__in_opt_string const OLECHAR *pwcsName,
+	__RPC__in_opt IStorage *pstgPriority,
+	DWORD grfMode,
+	__RPC__deref_opt_in_opt SNB snbExclude,
+	DWORD reserved,
+	__RPC__deref_out_opt IStorage **ppstg)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::CopyTo( 
+	DWORD ciidExclude,
+	const IID *rgiidExclude,
+	__RPC__in_opt  SNB snbExclude,
+	IStorage *pstgDest)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::MoveElementTo( 
+	__RPC__in_string const OLECHAR *pwcsName,
+	__RPC__in_opt IStorage *pstgDest,
+	__RPC__in_string const OLECHAR *pwcsNewName,
+	DWORD grfFlags)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::Commit( 
+	DWORD grfCommitFlags)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::Revert()
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::EnumElements( 
+	DWORD reserved1,
+	void *reserved2,
+	DWORD reserved3,
+	IEnumSTATSTG **ppenum)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::DestroyElement( 
+	__RPC__in_string const OLECHAR *pwcsName)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::RenameElement( 
+	__RPC__in_string const OLECHAR *pwcsOldName,
+	__RPC__in_string const OLECHAR *pwcsNewName)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::SetElementTimes( 
+	__RPC__in_opt_string const OLECHAR *pwcsName,
+	__RPC__in_opt const FILETIME *pctime,
+	__RPC__in_opt const FILETIME *patime,
+	__RPC__in_opt const FILETIME *pmtime)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::SetClass(
+	__RPC__in REFCLSID clsid)
+{
+	return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::SetStateBits( 
+	DWORD grfStateBits,
+	DWORD grfMask)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::Stat( 
+	__RPC__out STATSTG *pstatstg,
+	DWORD grfStatFlag)
+{
+	return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IDocHostUIHandler
+//////////////////////////////////////////////////////////////////////////////
+// Our implementation for this interface is basically all about disabling
+// things that we don't want/need.
+
+HRESULT __stdcall WebBrowser::ShowContextMenu(DWORD dwID,
+                                              POINT* ppt,
+                                              IUnknown* pcmdtReserved,
+                                              IDispatch* pdispReserved)
+{
+	// Returning S_OK signals that we've handled the request for a context menu
+	// (which we did, by doing nothing), so the control won't try to open one.
+  return S_OK;
+}
+
+HRESULT __stdcall WebBrowser::GetHostInfo(DOCHOSTUIINFO* pInfo)
+{
+  pInfo->cbSize = sizeof(DOCHOSTUIINFO);
+  pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG |
+                   DOCHOSTUIFLAG_DISABLE_HELP_MENU |
+                   DOCHOSTUIFLAG_NO3DBORDER |
+                   DOCHOSTUIFLAG_SCROLL_NO |
+									 DOCHOSTUIFLAG_OPENNEWWIN |
+									 DOCHOSTUIFLAG_OVERRIDEBEHAVIORFACTORY |
+                   DOCHOSTUIFLAG_THEME |
+                   DOCHOSTUIFLAG_LOCAL_MACHINE_ACCESS_CHECK |
+                   DOCHOSTUIFLAG_DISABLE_UNTRUSTEDPROTOCOL |
+                   DOCHOSTUIFLAG_DPI_AWARE;
+  pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
+  pInfo->pchHostCss = nullptr;
+  pInfo->pchHostNS = nullptr;
+  return S_OK;
+}
+
+HRESULT __stdcall WebBrowser::ShowUI(DWORD dwID,
+                                     IOleInPlaceActiveObject* pActiveObject,
+                                     IOleCommandTarget* pCommandTarget,
+                                     IOleInPlaceFrame* pFrame,
+                                     IOleInPlaceUIWindow* pDoc)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::HideUI()
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::UpdateUI()
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::EnableModeless(BOOL fEnable)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::OnDocWindowActivate(BOOL fActivate)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::OnFrameWindowActivate(BOOL fActivate)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::ResizeBorder(LPCRECT prcBorder,
+                                           IOleInPlaceUIWindow* pUIWindow,
+                                           BOOL fRameWindow)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::TranslateAccelerator(LPMSG lpMsg,
+                                                   const GUID* pguidCmdGroup,
+                                                   DWORD nCmdID)
+{
+  return S_FALSE;
+}
+
+HRESULT __stdcall WebBrowser::GetOptionKeyPath(LPOLESTR* pchKey, DWORD dw)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::GetDropTarget(IDropTarget* pDropTarget,
+                                            IDropTarget** ppDropTarget)
+{
+	ppDropTarget = nullptr;
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::GetExternal(IDispatch** ppDispatch)
+{
+	// This object has to implement IDispatch anyway so that we can use
+	// DISPID_AMBIENT_DLCONTROL, so we'll make this the external handler also.
+  *ppDispatch = this;
+  return S_OK;
+}
+
+HRESULT __stdcall WebBrowser::TranslateUrl(DWORD dwTranslate,
+                                           LPWSTR pchURLIn,
+                                           LPWSTR* ppchURLOut)
+{
+	ppchURLOut = nullptr;
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::FilterDataObject(IDataObject* pDO,
+                                               IDataObject** ppDORet)
+{
+	ppDORet = nullptr;
+  return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IDocHostShowUI
+//////////////////////////////////////////////////////////////////////////////
+
+HRESULT __stdcall WebBrowser::ShowMessage(HWND hwnd,
+                                          LPOLESTR lpstrText,
+                                          LPOLESTR lpstrCaption,
+                                          DWORD dwType,
+                                          LPOLESTR lpstrHelpFile,
+                                          DWORD dwHelpContext,
+                                          LRESULT* plResult)
+{
+	// Don't allow MSHTML to generate message boxes.
+  return S_OK;
+}
+
+HRESULT __stdcall WebBrowser::ShowHelp(HWND hwnd,
+                                       LPOLESTR pszHelpFile,
+                                       UINT uCommand,
+                                       DWORD dwData,
+                                       POINT ptMouse,
+                                       IDispatch* pDispatchObjectHit)
+{
+	// Don't allow MSHTML to show any help.
+  return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IDispatch
+//////////////////////////////////////////////////////////////////////////////
+
+// We're not using a type library.	
+HRESULT __stdcall WebBrowser::GetTypeInfoCount(UINT* pctinfo)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::GetTypeInfo(UINT iTInfo,
+                                          LCID lcid,
+                                          ITypeInfo** ppTInfo)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::GetIDsOfNames(REFIID riid,
+                                            LPOLESTR* rgszNames,
+                                            UINT cNames,
+                                            LCID lcid,
+                                            DISPID* rgDispId)
+{
+  if (cNames != 1) {
+    return E_NOTIMPL;
+  }
+
+	for (size_t i = 0; i < mCustomFunctions.size(); ++i) {
+		if (mCustomFunctions[i].mName == rgszNames[0]) {
+			// DISPID values need to be 1-indexed because 0 is reserved (DISPID_VALUE).
+			*rgDispId = i + 1;
+			return S_OK;
+		}
+	}
+
+  return DISP_E_UNKNOWNNAME;
+}
+
+HRESULT __stdcall WebBrowser::Invoke(DISPID dispIdMember,
+                                     REFIID riid,
+                                     LCID lcid,
+                                     WORD wFlags,
+                                     DISPPARAMS* pDispParams,
+                                     VARIANT* pVarResult,
+                                     EXCEPINFO* pExcepInfo,
+                                     UINT* puArgErr)
+{
+  if (dispIdMember == DISPID_AMBIENT_DLCONTROL && pVarResult) {
+    VariantClear(pVarResult);
+    pVarResult->vt = VT_I4;
+    // As a light security measure, disable a bunch of stuff we don't want
+    // to be able to run in the web control.
+    pVarResult->intVal = DLCTL_NO_JAVA |
+                         DLCTL_NO_DLACTIVEXCTLS |
+                         DLCTL_NO_RUNACTIVEXCTLS |
+			                   DLCTL_NO_FRAMEDOWNLOAD |
+                         DLCTL_NO_BEHAVIORS |
+			                   DLCTL_NO_CLIENTPULL |
+												 DLCTL_NOFRAMES |
+												 DLCTL_FORCEOFFLINE |
+												 DLCTL_SILENT |
+			                   DLCTL_OFFLINE |
+                         DLCTL_DLIMAGES;
+    return S_OK;
+	}
+
+	// Otherwise this should be one of our custom functions.
+	if (dispIdMember <= DISPID_VALUE || dispIdMember > (DISPID)mCustomFunctions.size()) {
+		return DISP_E_MEMBERNOTFOUND;
+	}
+
+	if ((wFlags & DISPATCH_METHOD) == 0) {
+		// We only support invoking these as methods, not property access.
+		return DISP_E_TYPEMISMATCH;
+	}
+
+	// DISPID values need to be 1-indexed because 0 is reserved (DISPID_VALUE).
+	CustomFunctionRecord foundFunction = mCustomFunctions[dispIdMember - 1];
+	foundFunction.mFunction(foundFunction.mArg, pDispParams->rgvarg[0], pVarResult);
+	// The JScript GC appears to call Release() on us once for each call that
+	// runs through here, but JScript never does a corresponding AddRef(), so
+	// we need our own to keep the refcount balanced.
+	AddRef();
+
+	return S_OK;
+}
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/WebBrowser.h
@@ -0,0 +1,270 @@
+// 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 <exdisp.h>
+#include <mshtmhst.h>
+
+#include <vector>
+#include <string>
+
+class WebBrowser final :
+	/* public IUnknown, */
+	/* public IOleWindow, */
+	public IOleInPlaceSite,
+	public IOleClientSite,
+	public IStorage,
+  public IDocHostUIHandler,
+  public IDocHostShowUI,
+  public IDispatch
+{
+
+public:
+	/////////////////////////////////////////////////////////////////////////////
+	// Our own methods
+	/////////////////////////////////////////////////////////////////////////////
+	WebBrowser(HWND hWndParent);
+  ~WebBrowser();
+
+	WebBrowser(const WebBrowser&) = delete;
+	WebBrowser& operator=(const WebBrowser&) = delete;
+
+	void Shutdown();
+
+	void SetRect(const RECT& _rc);
+  void Resize(DWORD width, DWORD height);
+	void Navigate(wchar_t* szUrl);
+
+	using CustomFunction = void(*)(void* context, VARIANT parameter, VARIANT* retVal);
+	void AddCustomFunction(wchar_t* name, CustomFunction function, void* arg);
+
+	/////////////////////////////////////////////////////////////////////////////
+	// Data members
+	/////////////////////////////////////////////////////////////////////////////
+private:
+	IOleObject* mOleObject = nullptr;
+	IOleInPlaceObject* mOleInPlaceObject = nullptr;
+	IWebBrowser2* mWebBrowser2 = nullptr;
+
+	LONG mComRefCount = 0;
+
+	RECT mRect = { 0, 0, 0, 0 };
+
+	HWND mHwndParent = nullptr;
+
+	struct CustomFunctionRecord {
+		std::wstring mName;
+		CustomFunction mFunction;
+		void* mArg;
+	};
+	std::vector<CustomFunctionRecord> mCustomFunctions;
+	// By my reading of oaidl.h, DISPID values greater than DISPID_VALUE
+	// less than 0x80010000 are unreserved and available to use.
+	DISPID mNextCustomFunctionID = DISPID_VALUE + 1;
+
+	//////////////////////////////////////////////////////////////////////////////
+	// COM interface methods
+	//////////////////////////////////////////////////////////////////////////////
+public:
+	// IUnknown
+	virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override;
+	virtual ULONG STDMETHODCALLTYPE AddRef() override;
+	virtual ULONG STDMETHODCALLTYPE Release() override;
+
+	// IOleWindow
+	virtual HRESULT STDMETHODCALLTYPE GetWindow( 
+		__RPC__deref_out_opt HWND *phwnd) override;
+	virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp( 
+		BOOL fEnterMode) override;
+
+	// IOleInPlaceSite
+	virtual HRESULT STDMETHODCALLTYPE CanInPlaceActivate() override;
+	virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivate() override;
+	virtual HRESULT STDMETHODCALLTYPE OnUIActivate() override;
+	virtual HRESULT STDMETHODCALLTYPE GetWindowContext( 
+		__RPC__deref_out_opt IOleInPlaceFrame **ppFrame,
+		__RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc,
+		__RPC__out LPRECT lprcPosRect,
+		__RPC__out LPRECT lprcClipRect,
+		__RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
+	virtual HRESULT STDMETHODCALLTYPE Scroll( 
+		SIZE scrollExtant) override;
+	virtual HRESULT STDMETHODCALLTYPE OnUIDeactivate( 
+		BOOL fUndoable) override;
+	virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate() override;
+	virtual HRESULT STDMETHODCALLTYPE DiscardUndoState() override;
+	virtual HRESULT STDMETHODCALLTYPE DeactivateAndUndo() override;
+	virtual HRESULT STDMETHODCALLTYPE OnPosRectChange( 
+		__RPC__in LPCRECT lprcPosRect) override;
+
+	// IOleClientSite
+	virtual HRESULT STDMETHODCALLTYPE SaveObject() override;
+	virtual HRESULT STDMETHODCALLTYPE GetMoniker( 
+		DWORD dwAssign,
+		DWORD dwWhichMoniker,
+		__RPC__deref_out_opt IMoniker **ppmk) override;
+	virtual HRESULT STDMETHODCALLTYPE GetContainer( 
+		__RPC__deref_out_opt IOleContainer **ppContainer) override;
+	virtual HRESULT STDMETHODCALLTYPE ShowObject() override;
+	virtual HRESULT STDMETHODCALLTYPE OnShowWindow( 
+		BOOL fShow) override;
+	virtual HRESULT STDMETHODCALLTYPE RequestNewObjectLayout() override;
+
+	// IStorage
+	virtual HRESULT STDMETHODCALLTYPE CreateStream( 
+		__RPC__in_string const OLECHAR *pwcsName,
+		DWORD grfMode,
+		DWORD reserved1,
+		DWORD reserved2,
+		__RPC__deref_out_opt IStream **ppstm) override;
+	virtual HRESULT STDMETHODCALLTYPE OpenStream( 
+		const OLECHAR *pwcsName,
+		void *reserved1,
+		DWORD grfMode,
+		DWORD reserved2,
+		IStream **ppstm) override;
+	virtual HRESULT STDMETHODCALLTYPE CreateStorage( 
+		__RPC__in_string const OLECHAR *pwcsName,
+		DWORD grfMode,
+		DWORD reserved1,
+		DWORD reserved2,
+		__RPC__deref_out_opt IStorage **ppstg) override;
+	virtual HRESULT STDMETHODCALLTYPE OpenStorage( 
+		__RPC__in_opt_string const OLECHAR *pwcsName,
+		__RPC__in_opt IStorage *pstgPriority,
+		DWORD grfMode,
+		__RPC__deref_opt_in_opt SNB snbExclude,
+		DWORD reserved,
+		__RPC__deref_out_opt IStorage **ppstg) override;
+	virtual HRESULT STDMETHODCALLTYPE CopyTo( 
+		DWORD ciidExclude,
+		const IID *rgiidExclude,
+		__RPC__in_opt  SNB snbExclude,
+		IStorage *pstgDest) override;
+	virtual HRESULT STDMETHODCALLTYPE MoveElementTo( 
+		__RPC__in_string const OLECHAR *pwcsName,
+		__RPC__in_opt IStorage *pstgDest,
+		__RPC__in_string const OLECHAR *pwcsNewName,
+		DWORD grfFlags) override;
+	virtual HRESULT STDMETHODCALLTYPE Commit( 
+		DWORD grfCommitFlags) override;
+	virtual HRESULT STDMETHODCALLTYPE Revert( void) override;
+	virtual HRESULT STDMETHODCALLTYPE EnumElements( 
+		DWORD reserved1,
+		void *reserved2,
+		DWORD reserved3,
+		IEnumSTATSTG **ppenum) override;
+	virtual HRESULT STDMETHODCALLTYPE DestroyElement( 
+		__RPC__in_string const OLECHAR *pwcsName) override;
+	virtual HRESULT STDMETHODCALLTYPE RenameElement( 
+		__RPC__in_string const OLECHAR *pwcsOldName,
+		__RPC__in_string const OLECHAR *pwcsNewName) override;
+	virtual HRESULT STDMETHODCALLTYPE SetElementTimes( 
+		__RPC__in_opt_string const OLECHAR *pwcsName,
+		__RPC__in_opt const FILETIME *pctime,
+		__RPC__in_opt const FILETIME *patime,
+		__RPC__in_opt const FILETIME *pmtime) override;
+	virtual HRESULT STDMETHODCALLTYPE SetClass( 
+		__RPC__in REFCLSID clsid) override;
+	virtual HRESULT STDMETHODCALLTYPE SetStateBits( 
+		DWORD grfStateBits,
+		DWORD grfMask) override;
+	virtual HRESULT STDMETHODCALLTYPE Stat( 
+		__RPC__out STATSTG *pstatstg,
+		DWORD grfStatFlag) override;
+
+	// IDocHostUIHandler
+  virtual HRESULT STDMETHODCALLTYPE ShowContextMenu(
+    _In_ DWORD dwID,
+    _In_ POINT* ppt,
+    _In_ IUnknown* pcmdtReserved,
+    _In_ IDispatch* pdispReserved) override;
+  virtual HRESULT STDMETHODCALLTYPE GetHostInfo(
+    _Inout_ DOCHOSTUIINFO* pInfo) override;
+  virtual HRESULT STDMETHODCALLTYPE ShowUI(
+    _In_ DWORD dwID,
+    _In_ IOleInPlaceActiveObject* pActiveObject,
+    _In_ IOleCommandTarget* pCommandTarget,
+    _In_ IOleInPlaceFrame* pFrame,
+    _In_ IOleInPlaceUIWindow* pDoc) override;
+  virtual HRESULT STDMETHODCALLTYPE HideUI() override;
+  virtual HRESULT STDMETHODCALLTYPE UpdateUI() override;
+  virtual HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override;
+  virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate(BOOL fActivate) override;
+  virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate(BOOL fActivate) override;
+  virtual HRESULT STDMETHODCALLTYPE ResizeBorder(
+    _In_ LPCRECT prcBorder,
+    _In_ IOleInPlaceUIWindow* pUIWindow,
+    _In_ BOOL fRameWindow) override;
+  virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator(
+    LPMSG lpMsg,
+    const GUID* pguidCmdGroup,
+     DWORD nCmdID) override;
+  virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath(
+    _Out_ LPOLESTR* pchKey,
+    DWORD dw) override;
+  virtual HRESULT STDMETHODCALLTYPE GetDropTarget(
+    _In_ IDropTarget* pDropTarget,
+    _Outptr_ IDropTarget** ppDropTarget) override;
+  virtual HRESULT STDMETHODCALLTYPE GetExternal(
+    _Outptr_result_maybenull_ IDispatch** ppDispatch) override;
+  virtual HRESULT STDMETHODCALLTYPE TranslateUrl(
+    DWORD dwTranslate,
+    _In_ LPWSTR pchURLIn,
+    _Outptr_ LPWSTR* ppchURLOut) override;
+  virtual HRESULT STDMETHODCALLTYPE FilterDataObject(
+    _In_ IDataObject* pDO,
+    _Outptr_result_maybenull_ IDataObject** ppDORet) override;
+
+  // IDocHostShowUI
+  virtual HRESULT STDMETHODCALLTYPE ShowMessage(
+    /* [in] */ HWND hwnd,
+    /* [annotation][in] */
+    _In_ LPOLESTR lpstrText,
+    /* [annotation][in] */
+    _In_ LPOLESTR lpstrCaption,
+    /* [in] */ DWORD dwType,
+    /* [annotation][in] */
+    _In_ LPOLESTR lpstrHelpFile,
+    /* [in] */ DWORD dwHelpContext,
+    /* [out] */ LRESULT* plResult) override;
+  virtual HRESULT STDMETHODCALLTYPE ShowHelp(
+    /* [in] */ HWND hwnd,
+    /* [annotation][in] */
+    _In_ LPOLESTR pszHelpFile,
+    /* [in] */ UINT uCommand,
+    /* [in] */ DWORD dwData,
+    /* [in] */ POINT ptMouse,
+    /* [out] */ IDispatch* pDispatchObjectHit) override;
+
+  // IDispatch
+  virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
+    /* [out] */ __RPC__out UINT* pctinfo) override;
+  virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
+    /* [in] */ UINT iTInfo,
+    /* [in] */ LCID lcid,
+    /* [out] */ __RPC__deref_out_opt ITypeInfo** ppTInfo) override;
+  virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(
+    /* [in] */ __RPC__in REFIID riid,
+    /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR* rgszNames,
+    /* [range][in] */ __RPC__in_range(0, 16384) UINT cNames,
+    /* [in] */ LCID lcid,
+    /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID* rgDispId) override;
+  virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
+    /* [annotation][in] */
+    _In_ DISPID dispIdMember,
+    /* [annotation][in] */
+    _In_ REFIID riid,
+    /* [annotation][in] */
+    _In_ LCID lcid,
+    /* [annotation][in] */
+    _In_ WORD wFlags,
+    /* [annotation][out][in] */
+    _In_ DISPPARAMS* pDispParams,
+    /* [annotation][out] */
+    _Out_opt_ VARIANT* pVarResult,
+    /* [annotation][out] */
+    _Out_opt_ EXCEPINFO* pExcepInfo,
+    /* [annotation][out] */
+    _Out_opt_ UINT* puArgErr) override;
+};
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/WebBrowser.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28803.452
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WebBrowser", "WebBrowser.vcxproj", "{EF903B79-AD97-45E0-BC6E-4FF846D6A2ED}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x86 = Debug|x86
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{EF903B79-AD97-45E0-BC6E-4FF846D6A2ED}.Debug|x86.ActiveCfg = Debug|Win32
+		{EF903B79-AD97-45E0-BC6E-4FF846D6A2ED}.Debug|x86.Build.0 = Debug|Win32
+		{EF903B79-AD97-45E0-BC6E-4FF846D6A2ED}.Release|x86.ActiveCfg = Release|Win32
+		{EF903B79-AD97-45E0-BC6E-4FF846D6A2ED}.Release|x86.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {3A087FCC-55DC-4C48-8A22-A6FFB3247674}
+	EndGlobalSection
+EndGlobal
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/WebBrowser.vcxproj
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <SccProjectName />
+    <SccLocalPath />
+    <ProjectGuid>{EF903B79-AD97-45E0-BC6E-4FF846D6A2ED}</ProjectGuid>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <PlatformToolset>v142</PlatformToolset>
+    <UseOfMfc>false</UseOfMfc>
+    <CharacterSet>Unicode</CharacterSet>
+    <SpectreMitigation>false</SpectreMitigation>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <PlatformToolset>v142</PlatformToolset>
+    <UseOfMfc>false</UseOfMfc>
+    <CharacterSet>Unicode</CharacterSet>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <SpectreMitigation>false</SpectreMitigation>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>.\Release\</OutDir>
+    <IntDir>.\Release\</IntDir>
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>.\Debug\</OutDir>
+    <IntDir>.\Debug\</IntDir>
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <StringPooling>true</StringPooling>
+      <Optimization>MinSpace</Optimization>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+      <DebugInformationFormat>None</DebugInformationFormat>
+      <ExceptionHandling>false</ExceptionHandling>
+      <BufferSecurityCheck>false</BufferSecurityCheck>
+      <RuntimeTypeInfo>false</RuntimeTypeInfo>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+    </ClCompile>
+    <Midl>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <TypeLibraryName>.\Release\WebBrowser.tlb</TypeLibraryName>
+      <MkTypLibCompatible>true</MkTypLibCompatible>
+      <TargetEnvironment>Win32</TargetEnvironment>
+    </Midl>
+    <ResourceCompile>
+      <Culture>0x080a</Culture>
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ResourceCompile>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>.\Release\WebBrowser.bsc</OutputFile>
+    </Bscmake>
+    <Link>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <LinkDLL>true</LinkDLL>
+      <SubSystem>Windows</SubSystem>
+      <OutputFile>.\Release\WebBrowser.dll</OutputFile>
+      <ImportLibrary>.\Release\WebBrowser.lib</ImportLibrary>
+      <AdditionalDependencies>odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <GenerateDebugInformation>false</GenerateDebugInformation>
+      <OptimizeReferences>true</OptimizeReferences>
+      <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <EntryPointSymbol>
+      </EntryPointSymbol>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+      <Optimization>Disabled</Optimization>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <ExceptionHandling>false</ExceptionHandling>
+      <LanguageStandard>Default</LanguageStandard>
+    </ClCompile>
+    <Midl>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <TypeLibraryName>.\Debug\WebBrowser.tlb</TypeLibraryName>
+      <MkTypLibCompatible>true</MkTypLibCompatible>
+      <TargetEnvironment>Win32</TargetEnvironment>
+    </Midl>
+    <ResourceCompile>
+      <Culture>0x080a</Culture>
+      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ResourceCompile>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>.\Debug\WebBrowser.bsc</OutputFile>
+    </Bscmake>
+    <Link>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <LinkDLL>true</LinkDLL>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Windows</SubSystem>
+      <OutputFile>.\Debug\WebBrowser.dll</OutputFile>
+      <ImportLibrary>.\Debug\WebBrowser.lib</ImportLibrary>
+      <AdditionalDependencies>odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OptimizeReferences>false</OptimizeReferences>
+      <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="CustomFunctions.cpp" />
+    <ClCompile Include="exdll.cpp" />
+    <ClCompile Include="main.cpp" />
+    <ClCompile Include="Timers.cpp" />
+    <ClCompile Include="WebBrowser.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="exdll.h" />
+    <ClInclude Include="resource.h" />
+    <ClInclude Include="WebBrowser.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="resource.rc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/exdll.cpp
@@ -0,0 +1,31 @@
+// 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 "exdll.h"
+
+unsigned int g_stringsize;
+stack_t** g_stacktop;
+int(__stdcall* g_executeCodeSegment)(int, HWND);
+HWND g_hwndParent;
+
+int popstring(TCHAR* str)
+{
+	stack_t* th;
+	if (!g_stacktop || !*g_stacktop) return 1;
+	th = (*g_stacktop);
+	lstrcpy(str, th->text);
+	*g_stacktop = th->next;
+	GlobalFree((HGLOBAL)th);
+	return 0;
+}
+
+void pushstring(const TCHAR* str)
+{
+	stack_t* th;
+	if (!g_stacktop) return;
+	th = (stack_t*)GlobalAlloc(GPTR, sizeof(stack_t) + (g_stringsize * sizeof(TCHAR)));
+	lstrcpyn(th->text, str, g_stringsize);
+	th->next = *g_stacktop;
+	*g_stacktop = th;
+}
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/exdll.h
@@ -0,0 +1,74 @@
+// 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 _EXDLL_H_
+#define _EXDLL_H_
+
+#include <windows.h>
+#include <tchar.h>
+
+#define PLUGINFUNCTION(name) \
+  extern "C" void __declspec(dllexport) name(HWND hWndParent, \
+                                             int string_size, \
+                                             TCHAR* variables, \
+                                             stack_t** stacktop, \
+                                             extra_parameters* extra)
+
+#define EXDLL_INIT()           {  \
+        g_stringsize = string_size; \
+        g_stacktop = stacktop;      \
+        g_executeCodeSegment = extra->ExecuteCodeSegment; \
+        g_hwndParent = hWndParent; }
+
+#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8)
+#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd)
+
+typedef struct _stack_t {
+  struct _stack_t *next;
+  TCHAR text[1]; // the real length of this buffer should be string_size
+} stack_t;
+
+extern unsigned int g_stringsize;
+extern stack_t** g_stacktop;
+extern int(__stdcall* g_executeCodeSegment)(int, HWND);
+extern HWND g_hwndParent;
+
+typedef struct {
+  int autoclose;
+  int all_user_var;
+  int exec_error;
+  int abort;
+  int exec_reboot;
+  int reboot_called;
+  int XXX_cur_insttype; // deprecated
+  int XXX_insttype_changed; // deprecated
+  int silent;
+  int instdir_error;
+  int rtl;
+  int errlvl;
+} exec_flags;
+
+// NSIS Plug-In Callback Messages
+enum NSPIM
+{
+  NSPIM_UNLOAD,    // This is the last message a plugin gets, do final cleanup
+  NSPIM_GUIUNLOAD, // Called after .onGUIEnd
+};
+
+typedef UINT_PTR (*NSISPLUGINCALLBACK)(enum NSPIM);
+
+typedef struct {
+  exec_flags *exec_flags;
+  int (__stdcall *ExecuteCodeSegment)(int, HWND);
+  void(__stdcall* validate_filename)(LPWSTR);
+  int(__stdcall* RegisterPluginCallback)(
+    HMODULE,
+    NSISPLUGINCALLBACK); // returns 0 on success, 1 if already
+                         // registered and < 0 on errors
+} extra_parameters;
+
+int popstring(TCHAR* str);
+void pushstring(const TCHAR* str);
+
+#endif //_EXDLL_H_
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/main.cpp
@@ -0,0 +1,209 @@
+// 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 <windows.h>
+#include "resource.h"
+#include "WebBrowser.h"
+#include "exdll.h"
+
+// These variables are global because they're needed by more than one of
+// our plugin methods. The expectation is that these are safe because the
+// NSIS framework doesn't really support more than one dialog or thread
+// at a time, and that means we don't have to either.
+
+// Instance handle for this DLL
+HINSTANCE gHInst = nullptr;
+// Parent window proc which we'll need to override and then restore
+WNDPROC gWndProcOld = nullptr;
+// Handle to the dialog we'll create
+HWND gHwnd = nullptr;
+// Set to true when our dialog should be destroyed
+bool gDone = false;
+// Web browser OLE site
+WebBrowser* gBrowser = nullptr;
+
+// Set web browser control feature flags that are configured on a per-process basis,
+// not for individual instances of the control.
+// This mainly means disabling things that could turn into security holes.
+static void ConfigurePerProcessBrowserFeatures() {
+	// For most of the features we're configuring, setting them to TRUE means we're
+	// disabling something, but for a few setting them to FALSE disables the thing.
+	// We don't necessarily care about *every* feature that's in the
+	// INTERNETFEATURELIST enum, but it seems safer to set them all anyway, to
+	// make sure we don't miss anything we *do* care about.
+	struct Feature {
+		INTERNETFEATURELIST id;
+		BOOL value;
+	} features[] = {
+		{FEATURE_OBJECT_CACHING, TRUE},
+		{FEATURE_ZONE_ELEVATION, TRUE},
+		{FEATURE_MIME_HANDLING, TRUE},
+		{FEATURE_MIME_SNIFFING, FALSE},
+		{FEATURE_WINDOW_RESTRICTIONS, TRUE},
+		{FEATURE_WEBOC_POPUPMANAGEMENT, TRUE},
+		{FEATURE_BEHAVIORS, TRUE},
+		{FEATURE_DISABLE_MK_PROTOCOL, TRUE},
+		{FEATURE_LOCALMACHINE_LOCKDOWN, TRUE},
+		{FEATURE_SECURITYBAND, FALSE},
+		{FEATURE_RESTRICT_ACTIVEXINSTALL, TRUE},
+		{FEATURE_VALIDATE_NAVIGATE_URL, TRUE},
+		{FEATURE_RESTRICT_FILEDOWNLOAD, TRUE},
+		{FEATURE_ADDON_MANAGEMENT, TRUE},
+		{FEATURE_PROTOCOL_LOCKDOWN, TRUE},
+		{FEATURE_HTTP_USERNAME_PASSWORD_DISABLE, TRUE},
+		{FEATURE_SAFE_BINDTOOBJECT, TRUE},
+		{FEATURE_UNC_SAVEDFILECHECK, TRUE},
+		{FEATURE_GET_URL_DOM_FILEPATH_UNENCODED, FALSE},
+		{FEATURE_TABBED_BROWSING, FALSE},
+		{FEATURE_SSLUX, FALSE},
+		{FEATURE_DISABLE_NAVIGATION_SOUNDS, TRUE},
+		{FEATURE_DISABLE_LEGACY_COMPRESSION, TRUE},
+		{FEATURE_FORCE_ADDR_AND_STATUS, FALSE},
+		{FEATURE_XMLHTTP, FALSE},
+		{FEATURE_DISABLE_TELNET_PROTOCOL, TRUE},
+		{FEATURE_FEEDS, FALSE},
+		{FEATURE_BLOCK_INPUT_PROMPTS, TRUE}
+	};
+
+	for (Feature feature : features) {
+		CoInternetSetFeatureEnabled(feature.id, SET_FEATURE_ON_PROCESS, feature.value);
+	}
+
+	// Setting FEATURE_BEHAVIORS allows controlling whether binary behaviors can run,
+	// but it doesn't actually disable them, so we'll do that separately.
+	// The only available interface for that requires you to do it for each zone,
+	// so we'll run through every possible zone just to be thorough.
+	IInternetZoneManager* pZoneManager = nullptr;
+	HRESULT hr = CoInternetCreateZoneManager(nullptr, &pZoneManager, 0);
+	if (SUCCEEDED(hr) && pZoneManager) {
+		DWORD zoneEnum = 0, zoneCount = 0;
+		hr = pZoneManager->CreateZoneEnumerator(&zoneEnum, &zoneCount, 0);
+		if (SUCCEEDED(hr)) {
+			DWORD dwPolicy = URLPOLICY_DISALLOW;
+			DWORD zoneIndex = 0;
+			while (zoneIndex < zoneCount) {
+				DWORD zone = 0;
+				hr = pZoneManager->GetZoneAt(zoneEnum, zoneIndex, &zone);
+				if (SUCCEEDED(hr)) {
+					pZoneManager->SetZoneActionPolicy(
+						zone,
+						URLACTION_BEHAVIOR_RUN,
+						(BYTE*)&dwPolicy,
+						sizeof(dwPolicy),
+						URLZONEREG_DEFAULT);
+				}
+				zoneIndex++;
+			}
+		}
+		pZoneManager->Release();
+	}
+}
+
+BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
+{
+	if (reason == DLL_PROCESS_ATTACH) {
+		gHInst = instance;
+		(void)OleInitialize(nullptr);
+		ConfigurePerProcessBrowserFeatures();
+	}
+	return TRUE;
+}
+
+UINT_PTR __cdecl NSISPluginCallback(NSPIM msg) {
+	if (msg == NSPIM_UNLOAD) {
+		OleUninitialize();
+	}
+	return 0;
+}
+
+BOOL CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+	BOOL bRes = CallWindowProc((WNDPROC)gWndProcOld, hwnd, message, wParam, lParam);
+	if (!bRes && message == WM_NOTIFY_OUTER_NEXT) {
+		gDone = true;
+		PostMessage(gHwnd, WM_CLOSE, 0, 0);
+	}
+	return bRes;
+}
+
+BOOL CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+  return FALSE;
+}
+
+void Init(HWND hWndParent,	 int string_size, 	TCHAR* variables, 	stack_t** stacktop, 	extra_parameters* extra)
+{
+	EXDLL_INIT();
+	extra->RegisterPluginCallback(gHInst, NSISPluginCallback);
+
+	HWND hwndChild = GetDlgItem(hWndParent, 1018);
+	if (!hwndChild) {
+		return;
+	}
+
+	HWND hwnd = CreateDialog(gHInst, MAKEINTRESOURCE(IDD_DIALOG1), hWndParent, DlgProc);
+	if (!hwnd) {
+		gDone = true;
+	} else {
+		gDone = false;
+		gWndProcOld = (WNDPROC)SetWindowLong(hWndParent, DWL_DLGPROC, (LONG)ParentWndProc);
+
+		// Tell NSIS to replace its inner dialog with ours.
+		SendMessage(hWndParent, WM_NOTIFY_CUSTOM_READY, (WPARAM)hwnd, 0);
+
+		// Initialize the browser control.
+		if (gBrowser) {
+			gBrowser->Shutdown();
+		}
+		gBrowser = new WebBrowser(hwnd);
+
+		gHwnd = hwnd;
+
+		// Move our dialog to match the size of the parent.
+		RECT r;
+		GetClientRect(hwndChild, &r);
+		MoveWindow(hwnd, r.left, r.top, r.right - r.left, r.bottom - r.top, FALSE);
+		gBrowser->SetRect(r);
+	}
+}
+
+PLUGINFUNCTION(ShowPage)
+{
+	if (!gBrowser) {
+		Init(hWndParent, string_size, variables, stacktop, extra);
+	}
+
+	TCHAR* sUrl = (TCHAR*)HeapAlloc(GetProcessHeap(), 0, g_stringsize * sizeof(TCHAR));
+	popstring(sUrl);
+
+	if (gBrowser) {
+		gBrowser->Navigate(sUrl);
+
+		ShowWindow(gHwnd, SW_SHOWNA);
+
+		while (!gDone) {
+			// This explicit wait call is needed rather than just a blocking GetMessage
+			// because we need this thread to be alertable so that our timers can wake it
+			// up and run their callbacks.
+			MsgWaitForMultipleObjectsEx(0, nullptr, 0, QS_ALLINPUT, MWMO_ALERTABLE);
+			MSG msg;
+			GetMessage(&msg, nullptr, 0, 0);
+			if (!IsDialogMessage(g_hwndParent, &msg))
+			{
+				TranslateMessage(&msg);
+				DispatchMessage(&msg);
+			}
+		}
+
+		SetWindowLong(g_hwndParent, DWL_DLGPROC, (LONG)gWndProcOld);
+		if (gHwnd) {
+			DestroyWindow(gHwnd);
+		}
+
+		gBrowser->Shutdown();
+		gBrowser = nullptr;
+	}
+
+	HeapFree(GetProcessHeap(), 0, (char*)sUrl);
+}
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/resource.h
@@ -0,0 +1,5 @@
+// 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/.
+
+#define IDD_DIALOG1 101
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/resource.rc
@@ -0,0 +1,14 @@
+// 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 "resource.h"
+#include "afxres.h"
+
+// The size of this dialog is a placeholder;
+// it's going to get resized to fit its parent.
+IDD_DIALOG1 DIALOGEX 0, 0, 1, 1
+STYLE DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
+FONT 8, "MS Shell Dlg"
+BEGIN
+END