/* -*- 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 "AboutThirdParty.h" #include #include #include namespace { class FileDialogEventsForTesting final : public IFileDialogEvents { mozilla::Atomic mRefCnt; const nsString mTargetName; RefPtr mTargetDir; ~FileDialogEventsForTesting() = default; public: FileDialogEventsForTesting(const nsAString& aTargetName, IShellItem* aTargetDir) : mRefCnt(0), mTargetName(PromiseFlatString(aTargetName)), mTargetDir(aTargetDir) {} // IUnknown STDMETHODIMP QueryInterface(REFIID aRefIID, void** aResult) { if (!aResult) { return E_INVALIDARG; } if (aRefIID == IID_IFileDialogEvents) { RefPtr ref(static_cast(this)); ref.forget(aResult); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) AddRef() { return ++mRefCnt; } STDMETHODIMP_(ULONG) Release() { ULONG result = --mRefCnt; if (!result) { delete this; } return result; } // IFileDialogEvents STDMETHODIMP OnFileOk(IFileDialog*) { return E_NOTIMPL; } STDMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return E_NOTIMPL; } STDMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return E_NOTIMPL; } STDMETHODIMP OnTypeChange(IFileDialog*) { return E_NOTIMPL; } STDMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return E_NOTIMPL; } STDMETHODIMP OnFolderChange(IFileDialog*) { return E_NOTIMPL; } STDMETHODIMP OnSelectionChange(IFileDialog* aDialog) { if (::GetModuleHandleW(mTargetName.get())) { aDialog->Close(S_OK); } else { // This sends a notification which is processed asynchronously. Calling // SetFolder from OnSelectionChange gives the main thread some cycles to // process other window messages, while calling SetFolder from // OnFolderChange causes freeze. Thus we can safely wait until a common // dialog loads a shell extension without blocking UI. aDialog->SetFolder(mTargetDir); } return E_NOTIMPL; } }; } // anonymous namespace namespace mozilla { NS_IMETHODIMP AboutThirdParty::OpenAndCloseFileDialogForTesting( const nsAString& aModuleName, const nsAString& aInitialDir, const nsAString& aFilter) { // Notify the shell of a new icon handler which should have been registered // by the test script. ::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); RefPtr dialog; if (FAILED(::CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, getter_AddRefs(dialog)))) { return NS_ERROR_UNEXPECTED; } const nsString& filter = PromiseFlatString(aFilter); COMDLG_FILTERSPEC fileFilter = {L"Test Target", filter.get()}; if (FAILED(dialog->SetFileTypes(1, &fileFilter))) { return NS_ERROR_UNEXPECTED; } RefPtr folder; if (FAILED(::SHCreateItemFromParsingName(PromiseFlatString(aInitialDir).get(), nullptr, IID_IShellItem, getter_AddRefs(folder)))) { return NS_ERROR_UNEXPECTED; } // Need to send a first notification outside FileDialogEventsForTesting. if (FAILED(dialog->SetFolder(folder))) { return NS_ERROR_UNEXPECTED; } RefPtr events(new FileDialogEventsForTesting(aModuleName, folder)); DWORD cookie; if (FAILED(dialog->Advise(events, &cookie))) { return NS_ERROR_UNEXPECTED; } if (FAILED(dialog->Show(nullptr))) { return NS_ERROR_UNEXPECTED; } if (FAILED(dialog->Unadvise(cookie))) { return NS_ERROR_UNEXPECTED; } return NS_OK; } } // namespace mozilla