widget/windows/nsFilePicker.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsFilePicker.h"
     9 #include <shlobj.h>
    10 #include <shlwapi.h>
    11 #include <cderr.h>
    13 #include "mozilla/WindowsVersion.h"
    14 #include "nsReadableUtils.h"
    15 #include "nsNetUtil.h"
    16 #include "nsWindow.h"
    17 #include "nsILoadContext.h"
    18 #include "nsIServiceManager.h"
    19 #include "nsIURL.h"
    20 #include "nsIStringBundle.h"
    21 #include "nsEnumeratorUtils.h"
    22 #include "nsCRT.h"
    23 #include "nsString.h"
    24 #include "nsToolkit.h"
    25 #include "WinUtils.h"
    26 #include "nsPIDOMWindow.h"
    28 using mozilla::IsVistaOrLater;
    29 using namespace mozilla::widget;
    31 char16_t *nsFilePicker::mLastUsedUnicodeDirectory;
    32 char nsFilePicker::mLastUsedDirectory[MAX_PATH+1] = { 0 };
    34 static const wchar_t kDialogPtrProp[] = L"DialogPtrProperty";
    35 static const DWORD kDialogTimerID = 9999;
    36 static const unsigned long kDialogTimerTimeout = 300;
    38 #define MAX_EXTENSION_LENGTH 10
    39 #define FILE_BUFFER_SIZE     4096 
    41 typedef DWORD FILEOPENDIALOGOPTIONS;
    43 ///////////////////////////////////////////////////////////////////////////////
    44 // Helper classes
    46 // Manages matching SuppressBlurEvents calls on the parent widget.
    47 class AutoSuppressEvents
    48 {
    49 public:
    50   explicit AutoSuppressEvents(nsIWidget* aWidget) :
    51     mWindow(static_cast<nsWindow *>(aWidget)) {
    52     SuppressWidgetEvents(true);
    53   }
    55   ~AutoSuppressEvents() {
    56     SuppressWidgetEvents(false);
    57   }
    58 private:
    59   void SuppressWidgetEvents(bool aFlag) {
    60     if (mWindow) {
    61       mWindow->SuppressBlurEvents(aFlag);
    62     }
    63   }
    64   nsRefPtr<nsWindow> mWindow;
    65 };
    67 // Manages the current working path.
    68 class AutoRestoreWorkingPath
    69 {
    70 public:
    71   AutoRestoreWorkingPath() {
    72     DWORD bufferLength = GetCurrentDirectoryW(0, nullptr);
    73     mWorkingPath = new wchar_t[bufferLength];
    74     if (GetCurrentDirectoryW(bufferLength, mWorkingPath) == 0) {
    75       mWorkingPath = nullptr;
    76     }
    77   }
    79   ~AutoRestoreWorkingPath() {
    80     if (HasWorkingPath()) {
    81       ::SetCurrentDirectoryW(mWorkingPath);
    82     }
    83   }
    85   inline bool HasWorkingPath() const {
    86     return mWorkingPath != nullptr;
    87   }
    88 private:
    89   nsAutoArrayPtr<wchar_t> mWorkingPath;
    90 };
    92 // Manages NS_NATIVE_TMP_WINDOW child windows. NS_NATIVE_TMP_WINDOWs are
    93 // temporary child windows of mParentWidget created to address RTL issues
    94 // in picker dialogs. We are responsible for destroying these.
    95 class AutoDestroyTmpWindow
    96 {
    97 public:
    98   explicit AutoDestroyTmpWindow(HWND aTmpWnd) :
    99     mWnd(aTmpWnd) {
   100   }
   102   ~AutoDestroyTmpWindow() {
   103     if (mWnd)
   104       DestroyWindow(mWnd);
   105   }
   107   inline HWND get() const { return mWnd; }
   108 private:
   109   HWND mWnd;
   110 };
   112 // Manages matching PickerOpen/PickerClosed calls on the parent widget.
   113 class AutoWidgetPickerState
   114 {
   115 public:
   116   explicit AutoWidgetPickerState(nsIWidget* aWidget) :
   117     mWindow(static_cast<nsWindow *>(aWidget)) {
   118     PickerState(true);
   119   }
   121   ~AutoWidgetPickerState() {
   122     PickerState(false);
   123   }
   124 private:
   125   void PickerState(bool aFlag) {
   126     if (mWindow) {
   127       if (aFlag)
   128         mWindow->PickerOpen();
   129       else
   130         mWindow->PickerClosed();
   131     }
   132   }
   133   nsRefPtr<nsWindow> mWindow;
   134 };
   136 // Manages a simple callback timer
   137 class AutoTimerCallbackCancel
   138 {
   139 public:
   140   AutoTimerCallbackCancel(nsFilePicker* aTarget,
   141                           nsTimerCallbackFunc aCallbackFunc) {
   142     Init(aTarget, aCallbackFunc);
   143   }
   145   ~AutoTimerCallbackCancel() {
   146     if (mPickerCallbackTimer) {
   147       mPickerCallbackTimer->Cancel();
   148     }
   149   }
   151 private:
   152   void Init(nsFilePicker* aTarget,
   153             nsTimerCallbackFunc aCallbackFunc) {
   154     mPickerCallbackTimer = do_CreateInstance("@mozilla.org/timer;1");
   155     if (!mPickerCallbackTimer) {
   156       NS_WARNING("do_CreateInstance for timer failed??");
   157       return;
   158     }
   159     mPickerCallbackTimer->InitWithFuncCallback(aCallbackFunc,
   160                                                aTarget,
   161                                                kDialogTimerTimeout,
   162                                                nsITimer::TYPE_REPEATING_SLACK);
   163   }
   164   nsCOMPtr<nsITimer> mPickerCallbackTimer;
   166 };
   168 ///////////////////////////////////////////////////////////////////////////////
   169 // nsIFilePicker
   171 nsFilePicker::nsFilePicker() :
   172   mSelectedType(1)
   173   , mDlgWnd(nullptr)
   174   , mFDECookie(0)
   175 {
   176    CoInitialize(nullptr);
   177 }
   179 nsFilePicker::~nsFilePicker()
   180 {
   181   if (mLastUsedUnicodeDirectory) {
   182     NS_Free(mLastUsedUnicodeDirectory);
   183     mLastUsedUnicodeDirectory = nullptr;
   184   }
   185   CoUninitialize();
   186 }
   188 NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker)
   190 NS_IMETHODIMP nsFilePicker::Init(nsIDOMWindow *aParent, const nsAString& aTitle, int16_t aMode)
   191 {
   192   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aParent);
   193   nsIDocShell* docShell = window ? window->GetDocShell() : nullptr;  
   194   mLoadContext = do_QueryInterface(docShell);
   196   return nsBaseFilePicker::Init(aParent, aTitle, aMode);
   197 }
   199 STDMETHODIMP nsFilePicker::QueryInterface(REFIID refiid, void** ppvResult)
   200 {
   201   *ppvResult = nullptr;
   202   if (IID_IUnknown == refiid ||
   203       refiid == IID_IFileDialogEvents) {
   204     *ppvResult = this;
   205   }
   207   if (nullptr != *ppvResult) {
   208     ((LPUNKNOWN)*ppvResult)->AddRef();
   209     return S_OK;
   210   }
   212   return E_NOINTERFACE;
   213 }
   215 /*
   216  * XP picker callbacks
   217  */
   219 // Show - Display the file dialog
   220 int CALLBACK
   221 BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
   222 {
   223   if (uMsg == BFFM_INITIALIZED)
   224   {
   225     char16_t * filePath = (char16_t *) lpData;
   226     if (filePath)
   227       ::SendMessageW(hwnd, BFFM_SETSELECTIONW,
   228                      TRUE /* true because lpData is a path string */,
   229                      lpData);
   230   }
   231   return 0;
   232 }
   234 static void
   235 EnsureWindowVisible(HWND hwnd) 
   236 {
   237   // Obtain the monitor which has the largest area of intersection 
   238   // with the window, or nullptr if there is no intersection.
   239   HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
   240   if (!monitor) {
   241     // The window is not visible, we should reposition it to the same place as its parent
   242     HWND parentHwnd = GetParent(hwnd);
   243     RECT parentRect;
   244     GetWindowRect(parentHwnd, &parentRect);
   245     SetWindowPos(hwnd, nullptr, parentRect.left, parentRect.top, 0, 0,
   246                  SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
   247   }
   248 }
   250 // Callback hook which will ensure that the window is visible. Currently
   251 // only in use on os <= XP.
   252 UINT_PTR CALLBACK
   253 nsFilePicker::FilePickerHook(HWND hwnd,
   254                              UINT msg,
   255                              WPARAM wParam,
   256                              LPARAM lParam) 
   257 {
   258   switch(msg) {
   259     case WM_NOTIFY:
   260       {
   261         LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
   262         if (!lpofn || !lpofn->lpOFN) {
   263           return 0;
   264         }
   266         if (CDN_INITDONE == lpofn->hdr.code) {
   267           // The Window will be automatically moved to the last position after
   268           // CDN_INITDONE.  We post a message to ensure the window will be visible
   269           // so it will be done after the automatic last position window move.
   270           PostMessage(hwnd, MOZ_WM_ENSUREVISIBLE, 0, 0);
   271         }
   272       }
   273       break;
   274     case MOZ_WM_ENSUREVISIBLE:
   275       EnsureWindowVisible(GetParent(hwnd));
   276       break;
   277     case WM_INITDIALOG:
   278       {
   279         OPENFILENAMEW* pofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
   280         SetProp(hwnd, kDialogPtrProp, (HANDLE)pofn->lCustData);
   281         nsFilePicker* picker = reinterpret_cast<nsFilePicker*>(pofn->lCustData);
   282         if (picker) {
   283           picker->SetDialogHandle(hwnd);
   284           SetTimer(hwnd, kDialogTimerID, kDialogTimerTimeout, nullptr);
   285         }
   286       }
   287       break;
   288     case WM_TIMER:
   289       {
   290         // Check to see if our parent has been torn down, if so, we close too.
   291         if (wParam == kDialogTimerID) {
   292           nsFilePicker* picker = 
   293             reinterpret_cast<nsFilePicker*>(GetProp(hwnd, kDialogPtrProp));
   294           if (picker && picker->ClosePickerIfNeeded(true)) {
   295             KillTimer(hwnd, kDialogTimerID);
   296           }
   297         }
   298       }
   299       break;
   300   }
   301   return 0;
   302 }
   305 // Callback hook which will dynamically allocate a buffer large enough
   306 // for the file picker dialog.  Currently only in use on  os <= XP.
   307 UINT_PTR CALLBACK
   308 nsFilePicker::MultiFilePickerHook(HWND hwnd,
   309                                   UINT msg,
   310                                   WPARAM wParam,
   311                                   LPARAM lParam)
   312 {
   313   switch (msg) {
   314     case WM_INITDIALOG:
   315       {
   316         // Finds the child drop down of a File Picker dialog and sets the 
   317         // maximum amount of text it can hold when typed in manually.
   318         // A wParam of 0 mean 0x7FFFFFFE characters.
   319         HWND comboBox = FindWindowEx(GetParent(hwnd), nullptr, 
   320                                      L"ComboBoxEx32", nullptr );
   321         if(comboBox)
   322           SendMessage(comboBox, CB_LIMITTEXT, 0, 0);
   323         // Store our nsFilePicker ptr for future use
   324         OPENFILENAMEW* pofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
   325         SetProp(hwnd, kDialogPtrProp, (HANDLE)pofn->lCustData);
   326         nsFilePicker* picker =
   327           reinterpret_cast<nsFilePicker*>(pofn->lCustData);
   328         if (picker) {
   329           picker->SetDialogHandle(hwnd);
   330           SetTimer(hwnd, kDialogTimerID, kDialogTimerTimeout, nullptr);
   331         }
   332       }
   333       break;
   334     case WM_NOTIFY:
   335       {
   336         LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
   337         if (!lpofn || !lpofn->lpOFN) {
   338           return 0;
   339         }
   340         // CDN_SELCHANGE is sent when the selection in the list box of the file
   341         // selection dialog changes
   342         if (lpofn->hdr.code == CDN_SELCHANGE) {
   343           HWND parentHWND = GetParent(hwnd);
   345           // Get the required size for the selected files buffer
   346           UINT newBufLength = 0; 
   347           int requiredBufLength = CommDlg_OpenSave_GetSpecW(parentHWND, 
   348                                                             nullptr, 0);
   349           if(requiredBufLength >= 0)
   350             newBufLength += requiredBufLength;
   351           else
   352             newBufLength += MAX_PATH;
   354           // If the user selects multiple files, the buffer contains the 
   355           // current directory followed by the file names of the selected 
   356           // files. So make room for the directory path.  If the user
   357           // selects a single file, it is no harm to add extra space.
   358           requiredBufLength = CommDlg_OpenSave_GetFolderPathW(parentHWND, 
   359                                                               nullptr, 0);
   360           if(requiredBufLength >= 0)
   361             newBufLength += requiredBufLength;
   362           else
   363             newBufLength += MAX_PATH;
   365           // Check if lpstrFile and nMaxFile are large enough
   366           if (newBufLength > lpofn->lpOFN->nMaxFile) {
   367             if (lpofn->lpOFN->lpstrFile)
   368               delete[] lpofn->lpOFN->lpstrFile;
   370             // We allocate FILE_BUFFER_SIZE more bytes than is needed so that
   371             // if the user selects a file and holds down shift and down to 
   372             // select  additional items, we will not continuously reallocate
   373             newBufLength += FILE_BUFFER_SIZE;
   375             wchar_t* filesBuffer = new wchar_t[newBufLength];
   376             ZeroMemory(filesBuffer, newBufLength * sizeof(wchar_t));
   378             lpofn->lpOFN->lpstrFile = filesBuffer;
   379             lpofn->lpOFN->nMaxFile  = newBufLength;
   380           }
   381         }
   382       }
   383       break;
   384     case WM_TIMER:
   385       {
   386         // Check to see if our parent has been torn down, if so, we close too.
   387         if (wParam == kDialogTimerID) {
   388           nsFilePicker* picker =
   389             reinterpret_cast<nsFilePicker*>(GetProp(hwnd, kDialogPtrProp));
   390           if (picker && picker->ClosePickerIfNeeded(true)) {
   391             KillTimer(hwnd, kDialogTimerID);
   392           }
   393         }
   394       }
   395       break;
   396   }
   398   return FilePickerHook(hwnd, msg, wParam, lParam);
   399 }
   401 /*
   402  * Vista+ callbacks
   403  */
   405 HRESULT
   406 nsFilePicker::OnFileOk(IFileDialog *pfd)
   407 {
   408   return S_OK;
   409 }
   411 HRESULT
   412 nsFilePicker::OnFolderChanging(IFileDialog *pfd,
   413                                IShellItem *psiFolder)
   414 {
   415   return S_OK;
   416 }
   418 HRESULT
   419 nsFilePicker::OnFolderChange(IFileDialog *pfd)
   420 {
   421   return S_OK;
   422 }
   424 HRESULT
   425 nsFilePicker::OnSelectionChange(IFileDialog *pfd)
   426 {
   427   return S_OK;
   428 }
   430 HRESULT
   431 nsFilePicker::OnShareViolation(IFileDialog *pfd,
   432                                IShellItem *psi,
   433                                FDE_SHAREVIOLATION_RESPONSE *pResponse)
   434 {
   435   return S_OK;
   436 }
   438 HRESULT
   439 nsFilePicker::OnTypeChange(IFileDialog *pfd)
   440 {
   441   // Failures here result in errors due to security concerns.
   442   nsRefPtr<IOleWindow> win;
   443   pfd->QueryInterface(IID_IOleWindow, getter_AddRefs(win));
   444   if (!win) {
   445     NS_ERROR("Could not retrieve the IOleWindow interface for IFileDialog.");
   446     return S_OK;
   447   }
   448   HWND hwnd = nullptr;
   449   win->GetWindow(&hwnd);
   450   if (!hwnd) {
   451     NS_ERROR("Could not retrieve the HWND for IFileDialog.");
   452     return S_OK;
   453   }
   455   SetDialogHandle(hwnd);
   456   return S_OK;
   457 }
   459 HRESULT
   460 nsFilePicker::OnOverwrite(IFileDialog *pfd,
   461                           IShellItem *psi,
   462                           FDE_OVERWRITE_RESPONSE *pResponse)
   463 {
   464   return S_OK;
   465 }
   467 /*
   468  * Close on parent close logic
   469  */
   471 bool
   472 nsFilePicker::ClosePickerIfNeeded(bool aIsXPDialog)
   473 {
   474   if (!mParentWidget || !mDlgWnd)
   475     return false;
   477   nsWindow *win = static_cast<nsWindow *>(mParentWidget.get());
   478   // Note, the xp callbacks hand us an inner window, so we have to step up
   479   // one to get the actual dialog.
   480   HWND dlgWnd;
   481   if (aIsXPDialog)
   482     dlgWnd = GetParent(mDlgWnd);
   483   else
   484     dlgWnd = mDlgWnd;
   485   if (IsWindow(dlgWnd) && IsWindowVisible(dlgWnd) && win->DestroyCalled()) {
   486     wchar_t className[64];
   487     // Make sure we have the right window
   488     if (GetClassNameW(dlgWnd, className, mozilla::ArrayLength(className)) &&
   489         !wcscmp(className, L"#32770") &&
   490         DestroyWindow(dlgWnd)) {
   491       mDlgWnd = nullptr;
   492       return true;
   493     }
   494   }
   495   return false;
   496 }
   498 void
   499 nsFilePicker::PickerCallbackTimerFunc(nsITimer *aTimer, void *aCtx)
   500 {
   501   nsFilePicker* picker = (nsFilePicker*)aCtx;
   502   if (picker->ClosePickerIfNeeded(false)) {
   503     aTimer->Cancel();
   504   }
   505 }
   507 void
   508 nsFilePicker::SetDialogHandle(HWND aWnd)
   509 {
   510   if (!aWnd || mDlgWnd)
   511     return;
   512   mDlgWnd = aWnd;
   513 }
   515 /*
   516  * Folder picker invocation
   517  */
   519 // Open the older XP style folder picker dialog. We end up in this call
   520 // on XP systems or when platform is built without the longhorn SDK.
   521 bool
   522 nsFilePicker::ShowXPFolderPicker(const nsString& aInitialDir)
   523 {
   524   bool result = false;
   526   nsAutoArrayPtr<wchar_t> dirBuffer(new wchar_t[FILE_BUFFER_SIZE]);
   527   wcsncpy(dirBuffer, aInitialDir.get(), FILE_BUFFER_SIZE);
   528   dirBuffer[FILE_BUFFER_SIZE-1] = '\0';
   530   AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
   531     mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
   533   BROWSEINFOW browserInfo = {0};
   534   browserInfo.pidlRoot       = nullptr;
   535   browserInfo.pszDisplayName = dirBuffer;
   536   browserInfo.lpszTitle      = mTitle.get();
   537   browserInfo.ulFlags        = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
   538   browserInfo.hwndOwner      = adtw.get(); 
   539   browserInfo.iImage         = 0;
   540   browserInfo.lParam         = reinterpret_cast<LPARAM>(this);
   542   if (!aInitialDir.IsEmpty()) {
   543     // the dialog is modal so that |initialDir.get()| will be valid in 
   544     // BrowserCallbackProc. Thus, we don't need to clone it.
   545     browserInfo.lParam = (LPARAM) aInitialDir.get();
   546     browserInfo.lpfn   = &BrowseCallbackProc;
   547   } else {
   548     browserInfo.lParam = 0;
   549     browserInfo.lpfn   = nullptr;
   550   }
   552   LPITEMIDLIST list = ::SHBrowseForFolderW(&browserInfo);
   553   if (list) {
   554     result = ::SHGetPathFromIDListW(list, dirBuffer);
   555     if (result)
   556       mUnicodeFile.Assign(static_cast<const wchar_t*>(dirBuffer));
   557     // free PIDL
   558     CoTaskMemFree(list);
   559   }
   561   return result;
   562 }
   564 /*
   565  * Show a folder picker post Windows XP
   566  * 
   567  * @param aInitialDir   The initial directory, the last used directory will be
   568  *                      used if left blank.
   569  * @param aWasInitError Out parameter will hold true if there was an error
   570  *                      before the folder picker is shown.
   571  * @return true if a file was selected successfully.
   572 */
   573 bool
   574 nsFilePicker::ShowFolderPicker(const nsString& aInitialDir, bool &aWasInitError)
   575 {
   576   nsRefPtr<IFileOpenDialog> dialog;
   577   if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC,
   578                               IID_IFileOpenDialog,
   579                               getter_AddRefs(dialog)))) {
   580     aWasInitError = true;
   581     return false;
   582   }
   583   aWasInitError = false;
   585   // hook up event callbacks
   586   dialog->Advise(this, &mFDECookie);
   588   // options
   589   FILEOPENDIALOGOPTIONS fos = FOS_PICKFOLDERS;
   590   dialog->SetOptions(fos);
   592   // initial strings
   593   dialog->SetTitle(mTitle.get());
   594   if (!aInitialDir.IsEmpty()) {
   595     nsRefPtr<IShellItem> folder;
   596     if (SUCCEEDED(
   597           WinUtils::SHCreateItemFromParsingName(aInitialDir.get(), nullptr,
   598                                                 IID_IShellItem,
   599                                                 getter_AddRefs(folder)))) {
   600       dialog->SetFolder(folder);
   601     }
   602   }
   604   AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
   605     mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
   607   // display
   608   nsRefPtr<IShellItem> item;
   609   if (FAILED(dialog->Show(adtw.get())) ||
   610       FAILED(dialog->GetResult(getter_AddRefs(item))) ||
   611       !item) {
   612     dialog->Unadvise(mFDECookie);
   613     return false;
   614   }
   615   dialog->Unadvise(mFDECookie);
   617   // results
   619   // If the user chose a Win7 Library, resolve to the library's
   620   // default save folder.
   621   nsRefPtr<IShellItem> folderPath;
   622   nsRefPtr<IShellLibrary> shellLib;
   623   CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC,
   624                    IID_IShellLibrary, getter_AddRefs(shellLib));
   625   if (shellLib &&
   626       SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) &&
   627       SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
   628                                                getter_AddRefs(folderPath)))) {
   629     item.swap(folderPath);
   630   }
   632   // get the folder's file system path
   633   return WinUtils::GetShellItemPath(item, mUnicodeFile);
   634 }
   636 /*
   637  * File open and save picker invocation
   638  */
   640 bool
   641 nsFilePicker::FilePickerWrapper(OPENFILENAMEW* ofn, PickerType aType)
   642 {
   643   if (!ofn)
   644     return false;
   646   bool result = false;
   647   AutoWidgetPickerState awps(mParentWidget);
   648   MOZ_SEH_TRY {
   649     if (aType == PICKER_TYPE_OPEN) 
   650       result = ::GetOpenFileNameW(ofn);
   651     else if (aType == PICKER_TYPE_SAVE)
   652       result = ::GetSaveFileNameW(ofn);
   653   } MOZ_SEH_EXCEPT(true) {
   654     NS_ERROR("nsFilePicker GetFileName win32 call generated an exception! This is bad!");
   655   }
   656   return result;
   657 }
   659 bool
   660 nsFilePicker::ShowXPFilePicker(const nsString& aInitialDir)
   661 {
   662   OPENFILENAMEW ofn = {0};
   663   ofn.lStructSize = sizeof(ofn);
   664   nsString filterBuffer = mFilterList;
   666   nsAutoArrayPtr<wchar_t> fileBuffer(new wchar_t[FILE_BUFFER_SIZE]);
   667   wcsncpy(fileBuffer,  mDefaultFilePath.get(), FILE_BUFFER_SIZE);
   668   fileBuffer[FILE_BUFFER_SIZE-1] = '\0'; // null terminate in case copy truncated
   670   if (!aInitialDir.IsEmpty()) {
   671     ofn.lpstrInitialDir = aInitialDir.get();
   672   }
   674   AutoDestroyTmpWindow adtw((HWND) (mParentWidget.get() ?
   675     mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
   677   ofn.lpstrTitle   = (LPCWSTR)mTitle.get();
   678   ofn.lpstrFilter  = (LPCWSTR)filterBuffer.get();
   679   ofn.nFilterIndex = mSelectedType;
   680   ofn.lpstrFile    = fileBuffer;
   681   ofn.nMaxFile     = FILE_BUFFER_SIZE;
   682   ofn.hwndOwner    = adtw.get();
   683   ofn.lCustData    = reinterpret_cast<LPARAM>(this);
   684   ofn.Flags = OFN_SHAREAWARE | OFN_LONGNAMES | OFN_OVERWRITEPROMPT |
   685               OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_ENABLESIZING | 
   686               OFN_EXPLORER;
   688   // Windows Vista and up won't allow you to use the new looking dialogs with
   689   // a hook procedure.  The hook procedure fixes a problem on XP dialogs for
   690   // file picker visibility.  Vista and up automatically ensures the file 
   691   // picker is always visible.
   692   if (!IsVistaOrLater()) {
   693     ofn.lpfnHook = FilePickerHook;
   694     ofn.Flags |= OFN_ENABLEHOOK;
   695   }
   697   // Handle add to recent docs settings
   698   if (IsPrivacyModeEnabled() || !mAddToRecentDocs) {
   699     ofn.Flags |= OFN_DONTADDTORECENT;
   700   }
   702   NS_NAMED_LITERAL_STRING(htmExt, "html");
   704   if (!mDefaultExtension.IsEmpty()) {
   705     ofn.lpstrDefExt = mDefaultExtension.get();
   706   } else if (IsDefaultPathHtml()) {
   707     // Get file extension from suggested filename to detect if we are
   708     // saving an html file.
   709     // This is supposed to append ".htm" if user doesn't supply an
   710     // extension but the behavior is sort of weird:
   711     // - Often appends ".html" even if you have an extension
   712     // - It obeys your extension if you put quotes around name
   713     ofn.lpstrDefExt = htmExt.get();
   714   }
   716   // When possible, instead of using OFN_NOCHANGEDIR to ensure the current
   717   // working directory will not change from this call, we will retrieve the
   718   // current working directory before the call and restore it after the 
   719   // call.  This flag causes problems on Windows XP for paths that are
   720   // selected like  C:test.txt where the user is currently at C:\somepath
   721   // In which case expected result should be C:\somepath\test.txt
   722   AutoRestoreWorkingPath restoreWorkingPath;
   723   // If we can't get the current working directory, the best case is to
   724   // use the OFN_NOCHANGEDIR flag
   725   if (!restoreWorkingPath.HasWorkingPath()) {
   726     ofn.Flags |= OFN_NOCHANGEDIR;
   727   }
   729   bool result = false;
   731   switch(mMode) {
   732     case modeOpen:
   733       // FILE MUST EXIST!
   734       ofn.Flags |= OFN_FILEMUSTEXIST;
   735       result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
   736       break;
   738     case modeOpenMultiple:
   739       ofn.Flags |= OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT;
   741       // The hook set here ensures that the buffer returned will always be
   742       // large enough to hold all selected files.  The hook may modify the
   743       // value of ofn.lpstrFile and deallocate the old buffer that it pointed
   744       // to (fileBuffer). The hook assumes that the passed in value is heap 
   745       // allocated and that the returned value should be freed by the caller.
   746       // If the hook changes the buffer, it will deallocate the old buffer.
   747       // This fix would be nice to have in Vista and up, but it would force
   748       // the file picker to use the old style dialogs because hooks are not
   749       // allowed in the new file picker UI.  We need to eventually move to
   750       // the new Common File Dialogs for Vista and up.
   751       if (!IsVistaOrLater()) {
   752         ofn.lpfnHook = MultiFilePickerHook;
   753         fileBuffer.forget();
   754         result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
   755         fileBuffer = ofn.lpstrFile;
   756       } else {
   757         result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
   758       }
   759       break;
   761     case modeSave:
   762       {
   763         ofn.Flags |= OFN_NOREADONLYRETURN;
   765         // Don't follow shortcuts when saving a shortcut, this can be used
   766         // to trick users (bug 271732)
   767         if (IsDefaultPathLink())
   768           ofn.Flags |= OFN_NODEREFERENCELINKS;
   770         result = FilePickerWrapper(&ofn, PICKER_TYPE_SAVE);
   771         if (!result) {
   772           // Error, find out what kind.
   773           if (GetLastError() == ERROR_INVALID_PARAMETER ||
   774               CommDlgExtendedError() == FNERR_INVALIDFILENAME) {
   775             // Probably the default file name is too long or contains illegal
   776             // characters. Try again, without a starting file name.
   777             ofn.lpstrFile[0] = L'\0';
   778             result = FilePickerWrapper(&ofn, PICKER_TYPE_SAVE);
   779           }
   780         }
   781       }
   782       break;
   784     default:
   785       NS_NOTREACHED("unsupported file picker mode");
   786       return false;
   787   }
   789   if (!result)
   790     return false;
   792   // Remember what filter type the user selected
   793   mSelectedType = (int16_t)ofn.nFilterIndex;
   795   // Single file selection, we're done
   796   if (mMode != modeOpenMultiple) {
   797     GetQualifiedPath(fileBuffer, mUnicodeFile);
   798     return true;
   799   }
   801   // Set user-selected location of file or directory.  From msdn's "Open and
   802   // Save As Dialog Boxes" section:
   803   // If you specify OFN_EXPLORER, the directory and file name strings are '\0'
   804   // separated, with an extra '\0' character after the last file name. This
   805   // format enables the Explorer-style dialog boxes to return long file names
   806   // that include spaces. 
   807   wchar_t *current = fileBuffer;
   809   nsAutoString dirName(current);
   810   // Sometimes dirName contains a trailing slash and sometimes it doesn't:
   811   if (current[dirName.Length() - 1] != '\\')
   812     dirName.Append((char16_t)'\\');
   814   while (current && *current && *(current + wcslen(current) + 1)) {
   815     current = current + wcslen(current) + 1;
   817     nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
   818     NS_ENSURE_TRUE(file, false);
   820     // Only prepend the directory if the path specified is a relative path
   821     nsAutoString path;
   822     if (PathIsRelativeW(current)) {
   823       path = dirName + nsDependentString(current);
   824     } else {
   825       path = current;
   826     }
   828     nsAutoString canonicalizedPath;
   829     GetQualifiedPath(path.get(), canonicalizedPath);
   830     if (NS_FAILED(file->InitWithPath(canonicalizedPath)) ||
   831         !mFiles.AppendObject(file))
   832       return false;
   833   }
   835   // Handle the case where the user selected just one file. From msdn: If you
   836   // specify OFN_ALLOWMULTISELECT and the user selects only one file the
   837   // lpstrFile string does not have a separator between the path and file name.
   838   if (current && *current && (current == fileBuffer)) {
   839     nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
   840     NS_ENSURE_TRUE(file, false);
   842     nsAutoString canonicalizedPath;
   843     GetQualifiedPath(current, canonicalizedPath);
   844     if (NS_FAILED(file->InitWithPath(canonicalizedPath)) ||
   845         !mFiles.AppendObject(file))
   846       return false;
   847   }
   849   return true;
   850 }
   852 /*
   853  * Show a file picker post Windows XP
   854  * 
   855  * @param aInitialDir   The initial directory, the last used directory will be
   856  *                      used if left blank.
   857  * @param aWasInitError Out parameter will hold true if there was an error
   858  *                      before the file picker is shown.
   859  * @return true if a file was selected successfully.
   860 */
   861 bool
   862 nsFilePicker::ShowFilePicker(const nsString& aInitialDir, bool &aWasInitError)
   863 {
   864   nsRefPtr<IFileDialog> dialog;
   865   if (mMode != modeSave) {
   866     if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC,
   867                                 IID_IFileOpenDialog,
   868                                 getter_AddRefs(dialog)))) {
   869       aWasInitError = true;
   870       return false;
   871     }
   872   } else {
   873     if (FAILED(CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC,
   874                                 IID_IFileSaveDialog,
   875                                 getter_AddRefs(dialog)))) {
   876       aWasInitError = true;
   877       return false;
   878     }
   879   }
   880   aWasInitError = false;
   882   // hook up event callbacks
   883   dialog->Advise(this, &mFDECookie);
   885   // options
   887   FILEOPENDIALOGOPTIONS fos = 0;
   888   fos |= FOS_SHAREAWARE | FOS_OVERWRITEPROMPT |
   889          FOS_FORCEFILESYSTEM;
   891   // Handle add to recent docs settings
   892   if (IsPrivacyModeEnabled() || !mAddToRecentDocs) {
   893     fos |= FOS_DONTADDTORECENT;
   894   }
   896   // Msdn claims FOS_NOCHANGEDIR is not needed. We'll add this
   897   // just in case.
   898   AutoRestoreWorkingPath arw;
   900   // mode specific
   901   switch(mMode) {
   902     case modeOpen:
   903       fos |= FOS_FILEMUSTEXIST;
   904       break;
   906     case modeOpenMultiple:
   907       fos |= FOS_FILEMUSTEXIST | FOS_ALLOWMULTISELECT;
   908       break;
   910     case modeSave:
   911       fos |= FOS_NOREADONLYRETURN;
   912       // Don't follow shortcuts when saving a shortcut, this can be used
   913       // to trick users (bug 271732)
   914       if (IsDefaultPathLink())
   915         fos |= FOS_NODEREFERENCELINKS;
   916       break;
   917   }
   919   dialog->SetOptions(fos);
   921   // initial strings
   923   // title
   924   dialog->SetTitle(mTitle.get());
   926   // default filename
   927   if (!mDefaultFilename.IsEmpty()) {
   928     dialog->SetFileName(mDefaultFilename.get());
   929   }
   931   NS_NAMED_LITERAL_STRING(htmExt, "html");
   933   // default extension to append to new files
   934   if (!mDefaultExtension.IsEmpty()) {
   935     dialog->SetDefaultExtension(mDefaultExtension.get());
   936   } else if (IsDefaultPathHtml()) {
   937     dialog->SetDefaultExtension(htmExt.get());
   938   }
   940   // initial location
   941   if (!aInitialDir.IsEmpty()) {
   942     nsRefPtr<IShellItem> folder;
   943     if (SUCCEEDED(
   944           WinUtils::SHCreateItemFromParsingName(aInitialDir.get(), nullptr,
   945                                                 IID_IShellItem,
   946                                                 getter_AddRefs(folder)))) {
   947       dialog->SetFolder(folder);
   948     }
   949   }
   951   // filter types and the default index
   952   if (!mComFilterList.IsEmpty()) {
   953     dialog->SetFileTypes(mComFilterList.Length(), mComFilterList.get());
   954     dialog->SetFileTypeIndex(mSelectedType);
   955   }
   957   // display
   959   {
   960     AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
   961       mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
   962     AutoTimerCallbackCancel atcc(this, PickerCallbackTimerFunc);
   963     AutoWidgetPickerState awps(mParentWidget);
   965     if (FAILED(dialog->Show(adtw.get()))) {
   966       dialog->Unadvise(mFDECookie);
   967       return false;
   968     }
   969     dialog->Unadvise(mFDECookie);
   970   }
   972   // results
   974   // Remember what filter type the user selected
   975   UINT filterIdxResult;
   976   if (SUCCEEDED(dialog->GetFileTypeIndex(&filterIdxResult))) {
   977     mSelectedType = (int16_t)filterIdxResult;
   978   }
   980   // single selection
   981   if (mMode != modeOpenMultiple) {
   982     nsRefPtr<IShellItem> item;
   983     if (FAILED(dialog->GetResult(getter_AddRefs(item))) || !item)
   984       return false;
   985     return WinUtils::GetShellItemPath(item, mUnicodeFile);
   986   }
   988   // multiple selection
   989   nsRefPtr<IFileOpenDialog> openDlg;
   990   dialog->QueryInterface(IID_IFileOpenDialog, getter_AddRefs(openDlg));
   991   if (!openDlg) {
   992     // should not happen
   993     return false;
   994   }
   996   nsRefPtr<IShellItemArray> items;
   997   if (FAILED(openDlg->GetResults(getter_AddRefs(items))) || !items) {
   998     return false;
   999   }
  1001   DWORD count = 0;
  1002   items->GetCount(&count);
  1003   for (unsigned int idx = 0; idx < count; idx++) {
  1004     nsRefPtr<IShellItem> item;
  1005     nsAutoString str;
  1006     if (SUCCEEDED(items->GetItemAt(idx, getter_AddRefs(item)))) {
  1007       if (!WinUtils::GetShellItemPath(item, str))
  1008         continue;
  1009       nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
  1010       if (file && NS_SUCCEEDED(file->InitWithPath(str)))
  1011         mFiles.AppendObject(file);
  1014   return true;
  1017 ///////////////////////////////////////////////////////////////////////////////
  1018 // nsIFilePicker impl.
  1020 NS_IMETHODIMP
  1021 nsFilePicker::ShowW(int16_t *aReturnVal)
  1023   NS_ENSURE_ARG_POINTER(aReturnVal);
  1025   *aReturnVal = returnCancel;
  1027   AutoSuppressEvents supress(mParentWidget);
  1029   nsAutoString initialDir;
  1030   if (mDisplayDirectory)
  1031     mDisplayDirectory->GetPath(initialDir);
  1033   // If no display directory, re-use the last one.
  1034   if(initialDir.IsEmpty()) {
  1035     // Allocate copy of last used dir.
  1036     initialDir = mLastUsedUnicodeDirectory;
  1039   // Clear previous file selections
  1040   mUnicodeFile.Truncate();
  1041   mFiles.Clear();
  1043   // Launch the XP file/folder picker on XP and as a fallback on Vista+. 
  1044   // The CoCreateInstance call to CLSID_FileOpenDialog fails with "(0x80040111)
  1045   // ClassFactory cannot supply requested class" when the checkbox for
  1046   // Disable Visual Themes is on in the compatability tab within the shortcut
  1047   // properties.
  1048   bool result = false, wasInitError = true;
  1049   if (mMode == modeGetFolder) {
  1050     if (IsVistaOrLater())
  1051       result = ShowFolderPicker(initialDir, wasInitError);
  1052     if (!result && wasInitError)
  1053       result = ShowXPFolderPicker(initialDir);
  1054   } else {
  1055     if (IsVistaOrLater())
  1056       result = ShowFilePicker(initialDir, wasInitError);
  1057     if (!result && wasInitError)
  1058       result = ShowXPFilePicker(initialDir);
  1061   // exit, and return returnCancel in aReturnVal
  1062   if (!result)
  1063     return NS_OK;
  1065   RememberLastUsedDirectory();
  1067   int16_t retValue = returnOK;
  1068   if (mMode == modeSave) {
  1069     // Windows does not return resultReplace, we must check if file
  1070     // already exists.
  1071     nsCOMPtr<nsIFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
  1072     bool flag = false;
  1073     if (file && NS_SUCCEEDED(file->InitWithPath(mUnicodeFile)) &&
  1074         NS_SUCCEEDED(file->Exists(&flag)) && flag) {
  1075       retValue = returnReplace;
  1079   *aReturnVal = retValue;
  1080   return NS_OK;
  1083 NS_IMETHODIMP
  1084 nsFilePicker::Show(int16_t *aReturnVal)
  1086   return ShowW(aReturnVal);
  1089 NS_IMETHODIMP
  1090 nsFilePicker::GetFile(nsIFile **aFile)
  1092   NS_ENSURE_ARG_POINTER(aFile);
  1093   *aFile = nullptr;
  1095   if (mUnicodeFile.IsEmpty())
  1096       return NS_OK;
  1098   nsCOMPtr<nsIFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
  1100   NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
  1102   file->InitWithPath(mUnicodeFile);
  1104   NS_ADDREF(*aFile = file);
  1106   return NS_OK;
  1109 NS_IMETHODIMP
  1110 nsFilePicker::GetFileURL(nsIURI **aFileURL)
  1112   *aFileURL = nullptr;
  1113   nsCOMPtr<nsIFile> file;
  1114   nsresult rv = GetFile(getter_AddRefs(file));
  1115   if (!file)
  1116     return rv;
  1118   return NS_NewFileURI(aFileURL, file);
  1121 NS_IMETHODIMP
  1122 nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
  1124   NS_ENSURE_ARG_POINTER(aFiles);
  1125   return NS_NewArrayEnumerator(aFiles, mFiles);
  1128 // Get the file + path
  1129 NS_IMETHODIMP
  1130 nsBaseWinFilePicker::SetDefaultString(const nsAString& aString)
  1132   mDefaultFilePath = aString;
  1134   // First, make sure the file name is not too long.
  1135   int32_t nameLength;
  1136   int32_t nameIndex = mDefaultFilePath.RFind("\\");
  1137   if (nameIndex == kNotFound)
  1138     nameIndex = 0;
  1139   else
  1140     nameIndex ++;
  1141   nameLength = mDefaultFilePath.Length() - nameIndex;
  1142   mDefaultFilename.Assign(Substring(mDefaultFilePath, nameIndex));
  1144   if (nameLength > MAX_PATH) {
  1145     int32_t extIndex = mDefaultFilePath.RFind(".");
  1146     if (extIndex == kNotFound)
  1147       extIndex = mDefaultFilePath.Length();
  1149     // Let's try to shave the needed characters from the name part.
  1150     int32_t charsToRemove = nameLength - MAX_PATH;
  1151     if (extIndex - nameIndex >= charsToRemove) {
  1152       mDefaultFilePath.Cut(extIndex - charsToRemove, charsToRemove);
  1156   // Then, we need to replace illegal characters. At this stage, we cannot
  1157   // replace the backslash as the string might represent a file path.
  1158   mDefaultFilePath.ReplaceChar(FILE_ILLEGAL_CHARACTERS, '-');
  1159   mDefaultFilename.ReplaceChar(FILE_ILLEGAL_CHARACTERS, '-');
  1161   return NS_OK;
  1164 NS_IMETHODIMP
  1165 nsBaseWinFilePicker::GetDefaultString(nsAString& aString)
  1167   return NS_ERROR_FAILURE;
  1170 // The default extension to use for files
  1171 NS_IMETHODIMP
  1172 nsBaseWinFilePicker::GetDefaultExtension(nsAString& aExtension)
  1174   aExtension = mDefaultExtension;
  1175   return NS_OK;
  1178 NS_IMETHODIMP
  1179 nsBaseWinFilePicker::SetDefaultExtension(const nsAString& aExtension)
  1181   mDefaultExtension = aExtension;
  1182   return NS_OK;
  1185 // Set the filter index
  1186 NS_IMETHODIMP
  1187 nsFilePicker::GetFilterIndex(int32_t *aFilterIndex)
  1189   // Windows' filter index is 1-based, we use a 0-based system.
  1190   *aFilterIndex = mSelectedType - 1;
  1191   return NS_OK;
  1194 NS_IMETHODIMP
  1195 nsFilePicker::SetFilterIndex(int32_t aFilterIndex)
  1197   // Windows' filter index is 1-based, we use a 0-based system.
  1198   mSelectedType = aFilterIndex + 1;
  1199   return NS_OK;
  1202 void
  1203 nsFilePicker::InitNative(nsIWidget *aParent,
  1204                          const nsAString& aTitle)
  1206   mParentWidget = aParent;
  1207   mTitle.Assign(aTitle);
  1210 void 
  1211 nsFilePicker::GetQualifiedPath(const wchar_t *aInPath, nsString &aOutPath)
  1213   // Prefer a qualified path over a non qualified path.
  1214   // Things like c:file.txt would be accepted in Win XP but would later
  1215   // fail to open from the download manager.
  1216   wchar_t qualifiedFileBuffer[MAX_PATH];
  1217   if (PathSearchAndQualifyW(aInPath, qualifiedFileBuffer, MAX_PATH)) {
  1218     aOutPath.Assign(qualifiedFileBuffer);
  1219   } else {
  1220     aOutPath.Assign(aInPath);
  1224 void
  1225 nsFilePicker::AppendXPFilter(const nsAString& aTitle, const nsAString& aFilter)
  1227   mFilterList.Append(aTitle);
  1228   mFilterList.Append(char16_t('\0'));
  1230   if (aFilter.EqualsLiteral("..apps"))
  1231     mFilterList.AppendLiteral("*.exe;*.com");
  1232   else
  1234     nsAutoString filter(aFilter);
  1235     filter.StripWhitespace();
  1236     if (filter.EqualsLiteral("*"))
  1237       filter.AppendLiteral(".*");
  1238     mFilterList.Append(filter);
  1241   mFilterList.Append(char16_t('\0'));
  1244 NS_IMETHODIMP
  1245 nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
  1247   if (IsVistaOrLater()) {
  1248     mComFilterList.Append(aTitle, aFilter);
  1249   } else {
  1250     AppendXPFilter(aTitle, aFilter);
  1252   return NS_OK;
  1255 void
  1256 nsFilePicker::RememberLastUsedDirectory()
  1258   nsCOMPtr<nsIFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
  1259   if (!file || NS_FAILED(file->InitWithPath(mUnicodeFile))) {
  1260     NS_WARNING("RememberLastUsedDirectory failed to init file path.");
  1261     return;
  1264   nsCOMPtr<nsIFile> dir;
  1265   nsAutoString newDir;
  1266   if (NS_FAILED(file->GetParent(getter_AddRefs(dir))) ||
  1267       !(mDisplayDirectory = do_QueryInterface(dir)) ||
  1268       NS_FAILED(mDisplayDirectory->GetPath(newDir)) ||
  1269       newDir.IsEmpty()) {
  1270     NS_WARNING("RememberLastUsedDirectory failed to get parent directory.");
  1271     return;
  1274   if (mLastUsedUnicodeDirectory) {
  1275     NS_Free(mLastUsedUnicodeDirectory);
  1276     mLastUsedUnicodeDirectory = nullptr;
  1278   mLastUsedUnicodeDirectory = ToNewUnicode(newDir);
  1281 bool
  1282 nsFilePicker::IsPrivacyModeEnabled()
  1284   return mLoadContext && mLoadContext->UsePrivateBrowsing();
  1287 bool
  1288 nsFilePicker::IsDefaultPathLink()
  1290   NS_ConvertUTF16toUTF8 ext(mDefaultFilePath);
  1291   ext.Trim(" .", false, true); // watch out for trailing space and dots
  1292   ToLowerCase(ext);
  1293   if (StringEndsWith(ext, NS_LITERAL_CSTRING(".lnk")) ||
  1294       StringEndsWith(ext, NS_LITERAL_CSTRING(".pif")) ||
  1295       StringEndsWith(ext, NS_LITERAL_CSTRING(".url")))
  1296     return true;
  1297   return false;
  1300 bool
  1301 nsFilePicker::IsDefaultPathHtml()
  1303   int32_t extIndex = mDefaultFilePath.RFind(".");
  1304   if (extIndex >= 0) {
  1305     nsAutoString ext;
  1306     mDefaultFilePath.Right(ext, mDefaultFilePath.Length() - extIndex);
  1307     if (ext.LowerCaseEqualsLiteral(".htm")  ||
  1308         ext.LowerCaseEqualsLiteral(".html") ||
  1309         ext.LowerCaseEqualsLiteral(".shtml"))
  1310       return true;
  1312   return false;
  1315 void
  1316 nsFilePicker::ComDlgFilterSpec::Append(const nsAString& aTitle, const nsAString& aFilter)
  1318   COMDLG_FILTERSPEC* pSpecForward = mSpecList.AppendElement();
  1319   if (!pSpecForward) {
  1320     NS_WARNING("mSpecList realloc failed.");
  1321     return;
  1323   memset(pSpecForward, 0, sizeof(*pSpecForward));
  1324   nsString* pStr = mStrings.AppendElement(aTitle);
  1325   if (!pStr) {
  1326     NS_WARNING("mStrings.AppendElement failed.");
  1327     return;
  1329   pSpecForward->pszName = pStr->get();
  1330   pStr = mStrings.AppendElement(aFilter);
  1331   if (!pStr) {
  1332     NS_WARNING("mStrings.AppendElement failed.");
  1333     return;
  1335   if (aFilter.EqualsLiteral("..apps"))
  1336     pStr->AssignLiteral("*.exe;*.com");
  1337   else {
  1338     pStr->StripWhitespace();
  1339     if (pStr->EqualsLiteral("*"))
  1340       pStr->AppendLiteral(".*");
  1342   pSpecForward->pszSpec = pStr->get();

mercurial