--- a/widget/src/windows/nsFilePicker.cpp
+++ b/widget/src/windows/nsFilePicker.cpp
@@ -58,16 +58,20 @@
#include "nsEnumeratorUtils.h"
#include "nsCRT.h"
#include "nsString.h"
#include "nsToolkit.h"
PRUnichar *nsFilePicker::mLastUsedUnicodeDirectory;
char nsFilePicker::mLastUsedDirectory[MAX_PATH+1] = { 0 };
+static const PRUnichar kDialogPtrProp[] = L"DialogPtrProperty";
+static const DWORD kDialogTimerID = 9999;
+static const unsigned long kDialogTimerTimeout = 300;
+
#define MAX_EXTENSION_LENGTH 10
///////////////////////////////////////////////////////////////////////////////
// Helper classes
// Manages matching SuppressBlurEvents calls on the parent widget.
class AutoSuppressEvents
{
@@ -153,21 +157,54 @@ private:
mWindow->PickerOpen();
else
mWindow->PickerClosed();
}
}
nsRefPtr<nsWindow> mWindow;
};
+// Manages a simple callback timer
+class AutoTimerCallbackCancel
+{
+public:
+ AutoTimerCallbackCancel(nsFilePicker* aTarget,
+ nsTimerCallbackFunc aCallbackFunc) {
+ Init(aTarget, aCallbackFunc);
+ }
+
+ ~AutoTimerCallbackCancel() {
+ if (mPickerCallbackTimer) {
+ mPickerCallbackTimer->Cancel();
+ }
+ }
+
+private:
+ void Init(nsFilePicker* aTarget,
+ nsTimerCallbackFunc aCallbackFunc) {
+ mPickerCallbackTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (!mPickerCallbackTimer) {
+ NS_WARNING("do_CreateInstance for timer failed??");
+ return;
+ }
+ mPickerCallbackTimer->InitWithFuncCallback(aCallbackFunc,
+ aTarget,
+ kDialogTimerTimeout,
+ nsITimer::TYPE_REPEATING_SLACK);
+ }
+ nsCOMPtr<nsITimer> mPickerCallbackTimer;
+
+};
+
///////////////////////////////////////////////////////////////////////////////
// nsIFilePicker
nsFilePicker::nsFilePicker() :
mSelectedType(1)
+ , mDlgWnd(NULL)
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
, mFDECookie(0)
#endif
{
CoInitialize(NULL);
}
nsFilePicker::~nsFilePicker()
@@ -176,16 +213,17 @@ nsFilePicker::~nsFilePicker()
NS_Free(mLastUsedUnicodeDirectory);
mLastUsedUnicodeDirectory = nsnull;
}
CoUninitialize();
}
NS_IMPL_ISUPPORTS1(nsFilePicker, nsIFilePicker)
+
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
STDMETHODIMP nsFilePicker::QueryInterface(REFIID refiid, void** ppvResult)
{
*ppvResult = NULL;
if (IID_IUnknown == refiid ||
refiid == IID_IFileDialogEvents) {
*ppvResult = this;
}
@@ -233,53 +271,96 @@ EnsureWindowVisible(HWND hwnd)
parentRect.left,
parentRect.top, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
}
// Callback hook which will ensure that the window is visible. Currently
// only in use on os <= XP.
-static UINT_PTR CALLBACK
-FilePickerHook(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+UINT_PTR CALLBACK
+nsFilePicker::FilePickerHook(HWND hwnd,
+ UINT msg,
+ WPARAM wParam,
+ LPARAM lParam)
{
- if (msg == WM_NOTIFY) {
- LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
- if (!lpofn || !lpofn->lpOFN) {
- return 0;
- }
-
- if (CDN_INITDONE == lpofn->hdr.code) {
- // The Window will be automatically moved to the last position after
- // CDN_INITDONE. We post a message to ensure the window will be visible
- // so it will be done after the automatic last position window move.
- PostMessage(hwnd, MOZ_WM_ENSUREVISIBLE, 0, 0);
- }
- } else if (msg == MOZ_WM_ENSUREVISIBLE) {
- EnsureWindowVisible(GetParent(hwnd));
+ switch(msg) {
+ case WM_NOTIFY:
+ {
+ LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
+ if (!lpofn || !lpofn->lpOFN) {
+ return 0;
+ }
+
+ if (CDN_INITDONE == lpofn->hdr.code) {
+ // The Window will be automatically moved to the last position after
+ // CDN_INITDONE. We post a message to ensure the window will be visible
+ // so it will be done after the automatic last position window move.
+ PostMessage(hwnd, MOZ_WM_ENSUREVISIBLE, 0, 0);
+ }
+ }
+ break;
+ case MOZ_WM_ENSUREVISIBLE:
+ EnsureWindowVisible(GetParent(hwnd));
+ break;
+ case WM_INITDIALOG:
+ {
+ OPENFILENAMEW* pofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
+ SetProp(hwnd, kDialogPtrProp, (HANDLE)pofn->lCustData);
+ nsFilePicker* picker = reinterpret_cast<nsFilePicker*>(pofn->lCustData);
+ if (picker) {
+ picker->SetDialogHandle(hwnd);
+ SetTimer(hwnd, kDialogTimerID, kDialogTimerTimeout, NULL);
+ }
+ }
+ break;
+ case WM_TIMER:
+ {
+ // Check to see if our parent has been torn down, if so, we close too.
+ if (wParam == kDialogTimerID) {
+ nsFilePicker* picker =
+ reinterpret_cast<nsFilePicker*>(GetProp(hwnd, kDialogPtrProp));
+ if (picker && picker->ClosePickerIfNeeded(true)) {
+ KillTimer(hwnd, kDialogTimerID);
+ }
+ }
+ }
+ break;
}
return 0;
}
// Callback hook which will dynamically allocate a buffer large enough
// for the file picker dialog. Currently only in use on os <= XP.
-static UINT_PTR CALLBACK
-MultiFilePickerHook(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+UINT_PTR CALLBACK
+nsFilePicker::MultiFilePickerHook(HWND hwnd,
+ UINT msg,
+ WPARAM wParam,
+ LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
{
// Finds the child drop down of a File Picker dialog and sets the
// maximum amount of text it can hold when typed in manually.
// A wParam of 0 mean 0x7FFFFFFE characters.
HWND comboBox = FindWindowEx(GetParent(hwnd), NULL,
L"ComboBoxEx32", NULL );
if(comboBox)
SendMessage(comboBox, CB_LIMITTEXT, 0, 0);
+ // Store our nsFilePicker ptr for future use
+ OPENFILENAMEW* pofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
+ SetProp(hwnd, kDialogPtrProp, (HANDLE)pofn->lCustData);
+ nsFilePicker* picker =
+ reinterpret_cast<nsFilePicker*>(pofn->lCustData);
+ if (picker) {
+ picker->SetDialogHandle(hwnd);
+ SetTimer(hwnd, kDialogTimerID, kDialogTimerTimeout, NULL);
+ }
}
break;
case WM_NOTIFY:
{
LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
if (!lpofn || !lpofn->lpOFN) {
return 0;
}
@@ -322,16 +403,28 @@ MultiFilePickerHook(HWND hwnd, UINT msg,
ZeroMemory(filesBuffer, newBufLength * sizeof(PRUnichar));
lpofn->lpOFN->lpstrFile = filesBuffer;
lpofn->lpOFN->nMaxFile = newBufLength;
}
}
}
break;
+ case WM_TIMER:
+ {
+ // Check to see if our parent has been torn down, if so, we close too.
+ if (wParam == kDialogTimerID) {
+ nsFilePicker* picker =
+ reinterpret_cast<nsFilePicker*>(GetProp(hwnd, kDialogPtrProp));
+ if (picker && picker->ClosePickerIfNeeded(true)) {
+ KillTimer(hwnd, kDialogTimerID);
+ }
+ }
+ }
+ break;
}
return FilePickerHook(hwnd, msg, wParam, lParam);
}
/*
* Vista+ callbacks
*/
@@ -369,30 +462,93 @@ nsFilePicker::OnShareViolation(IFileDial
FDE_SHAREVIOLATION_RESPONSE *pResponse)
{
return S_OK;
}
HRESULT
nsFilePicker::OnTypeChange(IFileDialog *pfd)
{
+ // Failures here result in errors due to security concerns.
+ nsRefPtr<IOleWindow> win;
+ pfd->QueryInterface(IID_IOleWindow, getter_AddRefs(win));
+ if (!win) {
+ NS_ERROR("Could not retrieve the IOleWindow interface for IFileDialog.");
+ return S_OK;
+ }
+ HWND hwnd = NULL;
+ win->GetWindow(&hwnd);
+ if (!hwnd) {
+ NS_ERROR("Could not retrieve the HWND for IFileDialog.");
+ return S_OK;
+ }
+
+ SetDialogHandle(hwnd);
return S_OK;
}
HRESULT
nsFilePicker::OnOverwrite(IFileDialog *pfd,
IShellItem *psi,
FDE_OVERWRITE_RESPONSE *pResponse)
{
return S_OK;
}
#endif // MOZ_NTDDI_LONGHORN
/*
+ * Close on parent close logic
+ */
+
+bool
+nsFilePicker::ClosePickerIfNeeded(bool aIsXPDialog)
+{
+ if (!mParentWidget || !mDlgWnd)
+ return false;
+
+ nsWindow *win = static_cast<nsWindow *>(mParentWidget.get());
+ // Note, the xp callbacks hand us an inner window, so we have to step up
+ // one to get the actual dialog.
+ HWND dlgWnd;
+ if (aIsXPDialog)
+ dlgWnd = GetParent(mDlgWnd);
+ else
+ dlgWnd = mDlgWnd;
+ if (IsWindow(dlgWnd) && IsWindowVisible(dlgWnd) && win->DestroyCalled()) {
+ PRUnichar className[64];
+ // Make sure we have the right window
+ if (GetClassNameW(dlgWnd, className, mozilla::ArrayLength(className)) &&
+ !wcscmp(className, L"#32770") &&
+ DestroyWindow(dlgWnd)) {
+ mDlgWnd = NULL;
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+nsFilePicker::PickerCallbackTimerFunc(nsITimer *aTimer, void *aCtx)
+{
+ nsFilePicker* picker = (nsFilePicker*)aCtx;
+ if (picker->ClosePickerIfNeeded(false)) {
+ aTimer->Cancel();
+ }
+}
+
+void
+nsFilePicker::SetDialogHandle(HWND aWnd)
+{
+ if (!aWnd || mDlgWnd)
+ return;
+ mDlgWnd = aWnd;
+}
+
+/*
* Folder picker invocation
*/
// Open the older XP style folder picker dialog. We end up in this call
// on XP systems or when platform is built without the longhorn SDK.
bool
nsFilePicker::ShowXPFolderPicker(const nsString& aInitialDir)
{
@@ -407,16 +563,17 @@ nsFilePicker::ShowXPFolderPicker(const n
BROWSEINFOW browserInfo = {0};
browserInfo.pidlRoot = nsnull;
browserInfo.pszDisplayName = (LPWSTR)dirBuffer;
browserInfo.lpszTitle = mTitle.get();
browserInfo.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
browserInfo.hwndOwner = adtw.get();
browserInfo.iImage = nsnull;
+ browserInfo.lParam = reinterpret_cast<LPARAM>(this);
if (!aInitialDir.IsEmpty()) {
// the dialog is modal so that |initialDir.get()| will be valid in
// BrowserCallbackProc. Thus, we don't need to clone it.
browserInfo.lParam = (LPARAM) aInitialDir.get();
browserInfo.lpfn = &BrowseCallbackProc;
} else {
browserInfo.lParam = nsnull;
@@ -531,16 +688,17 @@ nsFilePicker::ShowXPFilePicker(const nsS
mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : NULL));
ofn.lpstrTitle = (LPCWSTR)mTitle.get();
ofn.lpstrFilter = (LPCWSTR)filterBuffer.get();
ofn.nFilterIndex = mSelectedType;
ofn.lpstrFile = fileBuffer;
ofn.nMaxFile = FILE_BUFFER_SIZE;
ofn.hwndOwner = adtw.get();
+ ofn.lCustData = reinterpret_cast<LPARAM>(this);
ofn.Flags = OFN_SHAREAWARE | OFN_LONGNAMES | OFN_OVERWRITEPROMPT |
OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_ENABLESIZING |
OFN_EXPLORER;
// Windows Vista and up won't allow you to use the new looking dialogs with
// a hook procedure. The hook procedure fixes a problem on XP dialogs for
// file picker visibility. Vista and up automatically ensures the file
// picker is always visible.
@@ -792,25 +950,28 @@ nsFilePicker::ShowFilePicker(const nsStr
// filter types and the default index
if (!mComFilterList.IsEmpty()) {
dialog->SetFileTypes(mComFilterList.Length(), mComFilterList.get());
dialog->SetFileTypeIndex(mSelectedType);
}
// display
- AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
- mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : NULL));
+ {
+ AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
+ mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : NULL));
+ AutoTimerCallbackCancel atcc(this, PickerCallbackTimerFunc);
+ AutoWidgetPickerState awps(mParentWidget);
- AutoWidgetPickerState awps(mParentWidget);
- if (FAILED(dialog->Show(adtw.get()))) {
+ if (FAILED(dialog->Show(adtw.get()))) {
+ dialog->Unadvise(mFDECookie);
+ return false;
+ }
dialog->Unadvise(mFDECookie);
- return false;
}
- dialog->Unadvise(mFDECookie);
// results
// single selection
if (mMode != modeOpenMultiple) {
nsRefPtr<IShellItem> item;
if (FAILED(dialog->GetResult(getter_AddRefs(item))) || !item) {
return false;
--- a/widget/src/windows/nsFilePicker.h
+++ b/widget/src/windows/nsFilePicker.h
@@ -52,16 +52,17 @@
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#define _WIN32_IE_bak _WIN32_IE
#undef _WIN32_IE
#define _WIN32_IE _WIN32_IE_IE70
#endif
#endif
#include "nsILocalFile.h"
+#include "nsITimer.h"
#include "nsISimpleEnumerator.h"
#include "nsCOMArray.h"
#include "nsAutoPtr.h"
#include "nsICharsetConverterManager.h"
#include "nsBaseFilePicker.h"
#include "nsString.h"
#include "nsdefs.h"
#include <commdlg.h>
@@ -129,63 +130,69 @@ protected:
bool ShowXPFolderPicker(const nsString& aInitialDir);
bool ShowFilePicker(const nsString& aInitialDir);
bool ShowXPFilePicker(const nsString& aInitialDir);
void AppendXPFilter(const nsAString& aTitle, const nsAString& aFilter);
void RememberLastUsedDirectory();
bool IsPrivacyModeEnabled();
bool IsDefaultPathLink();
bool IsDefaultPathHtml();
+ void SetDialogHandle(HWND aWnd);
+ bool ClosePickerIfNeeded(bool aIsXPDialog);
+ static void PickerCallbackTimerFunc(nsITimer *aTimer, void *aPicker);
+ static UINT_PTR CALLBACK MultiFilePickerHook(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ static UINT_PTR CALLBACK FilePickerHook(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
nsCOMPtr<nsIWidget> mParentWidget;
nsString mTitle;
PRInt16 mMode;
nsCString mFile;
nsString mDefaultFilePath;
nsString mDefaultFilename;
nsString mDefaultExtension;
nsString mFilterList;
PRInt16 mSelectedType;
nsCOMArray<nsILocalFile> mFiles;
static char mLastUsedDirectory[];
nsString mUnicodeFile;
static PRUnichar *mLastUsedUnicodeDirectory;
- DWORD mFDECookie;
+ HWND mDlgWnd;
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
class ComDlgFilterSpec
{
public:
ComDlgFilterSpec() :
mSpecList(nsnull),
mLength(0) {}
~ComDlgFilterSpec() {
free(mSpecList);
}
- PRUint32 Length() {
+ const PRUint32 Length() {
return mLength;
}
- bool IsEmpty() {
+ const bool IsEmpty() {
return (mLength == 0);
}
const COMDLG_FILTERSPEC* get() {
return mSpecList;
}
void Append(const nsAString& aTitle, const nsAString& aFilter);
private:
COMDLG_FILTERSPEC* mSpecList;
nsAutoTArray<nsString, 2> mStrings;
PRUint32 mLength;
};
- ComDlgFilterSpec mComFilterList;
+ ComDlgFilterSpec mComFilterList;
+ DWORD mFDECookie;
#endif
};
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
#if defined(_WIN32_WINNT_bak)
#undef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_bak
#undef _WIN32_IE