widget/windows/nsFilePicker.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/windows/nsFilePicker.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1343 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsFilePicker.h"
    1.11 +
    1.12 +#include <shlobj.h>
    1.13 +#include <shlwapi.h>
    1.14 +#include <cderr.h>
    1.15 +
    1.16 +#include "mozilla/WindowsVersion.h"
    1.17 +#include "nsReadableUtils.h"
    1.18 +#include "nsNetUtil.h"
    1.19 +#include "nsWindow.h"
    1.20 +#include "nsILoadContext.h"
    1.21 +#include "nsIServiceManager.h"
    1.22 +#include "nsIURL.h"
    1.23 +#include "nsIStringBundle.h"
    1.24 +#include "nsEnumeratorUtils.h"
    1.25 +#include "nsCRT.h"
    1.26 +#include "nsString.h"
    1.27 +#include "nsToolkit.h"
    1.28 +#include "WinUtils.h"
    1.29 +#include "nsPIDOMWindow.h"
    1.30 +
    1.31 +using mozilla::IsVistaOrLater;
    1.32 +using namespace mozilla::widget;
    1.33 +
    1.34 +char16_t *nsFilePicker::mLastUsedUnicodeDirectory;
    1.35 +char nsFilePicker::mLastUsedDirectory[MAX_PATH+1] = { 0 };
    1.36 +
    1.37 +static const wchar_t kDialogPtrProp[] = L"DialogPtrProperty";
    1.38 +static const DWORD kDialogTimerID = 9999;
    1.39 +static const unsigned long kDialogTimerTimeout = 300;
    1.40 +
    1.41 +#define MAX_EXTENSION_LENGTH 10
    1.42 +#define FILE_BUFFER_SIZE     4096 
    1.43 +
    1.44 +typedef DWORD FILEOPENDIALOGOPTIONS;
    1.45 +
    1.46 +///////////////////////////////////////////////////////////////////////////////
    1.47 +// Helper classes
    1.48 +
    1.49 +// Manages matching SuppressBlurEvents calls on the parent widget.
    1.50 +class AutoSuppressEvents
    1.51 +{
    1.52 +public:
    1.53 +  explicit AutoSuppressEvents(nsIWidget* aWidget) :
    1.54 +    mWindow(static_cast<nsWindow *>(aWidget)) {
    1.55 +    SuppressWidgetEvents(true);
    1.56 +  }
    1.57 +
    1.58 +  ~AutoSuppressEvents() {
    1.59 +    SuppressWidgetEvents(false);
    1.60 +  }
    1.61 +private:
    1.62 +  void SuppressWidgetEvents(bool aFlag) {
    1.63 +    if (mWindow) {
    1.64 +      mWindow->SuppressBlurEvents(aFlag);
    1.65 +    }
    1.66 +  }
    1.67 +  nsRefPtr<nsWindow> mWindow;
    1.68 +};
    1.69 +
    1.70 +// Manages the current working path.
    1.71 +class AutoRestoreWorkingPath
    1.72 +{
    1.73 +public:
    1.74 +  AutoRestoreWorkingPath() {
    1.75 +    DWORD bufferLength = GetCurrentDirectoryW(0, nullptr);
    1.76 +    mWorkingPath = new wchar_t[bufferLength];
    1.77 +    if (GetCurrentDirectoryW(bufferLength, mWorkingPath) == 0) {
    1.78 +      mWorkingPath = nullptr;
    1.79 +    }
    1.80 +  }
    1.81 +
    1.82 +  ~AutoRestoreWorkingPath() {
    1.83 +    if (HasWorkingPath()) {
    1.84 +      ::SetCurrentDirectoryW(mWorkingPath);
    1.85 +    }
    1.86 +  }
    1.87 +
    1.88 +  inline bool HasWorkingPath() const {
    1.89 +    return mWorkingPath != nullptr;
    1.90 +  }
    1.91 +private:
    1.92 +  nsAutoArrayPtr<wchar_t> mWorkingPath;
    1.93 +};
    1.94 +
    1.95 +// Manages NS_NATIVE_TMP_WINDOW child windows. NS_NATIVE_TMP_WINDOWs are
    1.96 +// temporary child windows of mParentWidget created to address RTL issues
    1.97 +// in picker dialogs. We are responsible for destroying these.
    1.98 +class AutoDestroyTmpWindow
    1.99 +{
   1.100 +public:
   1.101 +  explicit AutoDestroyTmpWindow(HWND aTmpWnd) :
   1.102 +    mWnd(aTmpWnd) {
   1.103 +  }
   1.104 +
   1.105 +  ~AutoDestroyTmpWindow() {
   1.106 +    if (mWnd)
   1.107 +      DestroyWindow(mWnd);
   1.108 +  }
   1.109 +  
   1.110 +  inline HWND get() const { return mWnd; }
   1.111 +private:
   1.112 +  HWND mWnd;
   1.113 +};
   1.114 +
   1.115 +// Manages matching PickerOpen/PickerClosed calls on the parent widget.
   1.116 +class AutoWidgetPickerState
   1.117 +{
   1.118 +public:
   1.119 +  explicit AutoWidgetPickerState(nsIWidget* aWidget) :
   1.120 +    mWindow(static_cast<nsWindow *>(aWidget)) {
   1.121 +    PickerState(true);
   1.122 +  }
   1.123 +
   1.124 +  ~AutoWidgetPickerState() {
   1.125 +    PickerState(false);
   1.126 +  }
   1.127 +private:
   1.128 +  void PickerState(bool aFlag) {
   1.129 +    if (mWindow) {
   1.130 +      if (aFlag)
   1.131 +        mWindow->PickerOpen();
   1.132 +      else
   1.133 +        mWindow->PickerClosed();
   1.134 +    }
   1.135 +  }
   1.136 +  nsRefPtr<nsWindow> mWindow;
   1.137 +};
   1.138 +
   1.139 +// Manages a simple callback timer
   1.140 +class AutoTimerCallbackCancel
   1.141 +{
   1.142 +public:
   1.143 +  AutoTimerCallbackCancel(nsFilePicker* aTarget,
   1.144 +                          nsTimerCallbackFunc aCallbackFunc) {
   1.145 +    Init(aTarget, aCallbackFunc);
   1.146 +  }
   1.147 +
   1.148 +  ~AutoTimerCallbackCancel() {
   1.149 +    if (mPickerCallbackTimer) {
   1.150 +      mPickerCallbackTimer->Cancel();
   1.151 +    }
   1.152 +  }
   1.153 +
   1.154 +private:
   1.155 +  void Init(nsFilePicker* aTarget,
   1.156 +            nsTimerCallbackFunc aCallbackFunc) {
   1.157 +    mPickerCallbackTimer = do_CreateInstance("@mozilla.org/timer;1");
   1.158 +    if (!mPickerCallbackTimer) {
   1.159 +      NS_WARNING("do_CreateInstance for timer failed??");
   1.160 +      return;
   1.161 +    }
   1.162 +    mPickerCallbackTimer->InitWithFuncCallback(aCallbackFunc,
   1.163 +                                               aTarget,
   1.164 +                                               kDialogTimerTimeout,
   1.165 +                                               nsITimer::TYPE_REPEATING_SLACK);
   1.166 +  }
   1.167 +  nsCOMPtr<nsITimer> mPickerCallbackTimer;
   1.168 +    
   1.169 +};
   1.170 +
   1.171 +///////////////////////////////////////////////////////////////////////////////
   1.172 +// nsIFilePicker
   1.173 +
   1.174 +nsFilePicker::nsFilePicker() :
   1.175 +  mSelectedType(1)
   1.176 +  , mDlgWnd(nullptr)
   1.177 +  , mFDECookie(0)
   1.178 +{
   1.179 +   CoInitialize(nullptr);
   1.180 +}
   1.181 +
   1.182 +nsFilePicker::~nsFilePicker()
   1.183 +{
   1.184 +  if (mLastUsedUnicodeDirectory) {
   1.185 +    NS_Free(mLastUsedUnicodeDirectory);
   1.186 +    mLastUsedUnicodeDirectory = nullptr;
   1.187 +  }
   1.188 +  CoUninitialize();
   1.189 +}
   1.190 +
   1.191 +NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker)
   1.192 +
   1.193 +NS_IMETHODIMP nsFilePicker::Init(nsIDOMWindow *aParent, const nsAString& aTitle, int16_t aMode)
   1.194 +{
   1.195 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aParent);
   1.196 +  nsIDocShell* docShell = window ? window->GetDocShell() : nullptr;  
   1.197 +  mLoadContext = do_QueryInterface(docShell);
   1.198 +  
   1.199 +  return nsBaseFilePicker::Init(aParent, aTitle, aMode);
   1.200 +}
   1.201 +
   1.202 +STDMETHODIMP nsFilePicker::QueryInterface(REFIID refiid, void** ppvResult)
   1.203 +{
   1.204 +  *ppvResult = nullptr;
   1.205 +  if (IID_IUnknown == refiid ||
   1.206 +      refiid == IID_IFileDialogEvents) {
   1.207 +    *ppvResult = this;
   1.208 +  }
   1.209 +
   1.210 +  if (nullptr != *ppvResult) {
   1.211 +    ((LPUNKNOWN)*ppvResult)->AddRef();
   1.212 +    return S_OK;
   1.213 +  }
   1.214 +
   1.215 +  return E_NOINTERFACE;
   1.216 +}
   1.217 +
   1.218 +/*
   1.219 + * XP picker callbacks
   1.220 + */
   1.221 +
   1.222 +// Show - Display the file dialog
   1.223 +int CALLBACK
   1.224 +BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
   1.225 +{
   1.226 +  if (uMsg == BFFM_INITIALIZED)
   1.227 +  {
   1.228 +    char16_t * filePath = (char16_t *) lpData;
   1.229 +    if (filePath)
   1.230 +      ::SendMessageW(hwnd, BFFM_SETSELECTIONW,
   1.231 +                     TRUE /* true because lpData is a path string */,
   1.232 +                     lpData);
   1.233 +  }
   1.234 +  return 0;
   1.235 +}
   1.236 +
   1.237 +static void
   1.238 +EnsureWindowVisible(HWND hwnd) 
   1.239 +{
   1.240 +  // Obtain the monitor which has the largest area of intersection 
   1.241 +  // with the window, or nullptr if there is no intersection.
   1.242 +  HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
   1.243 +  if (!monitor) {
   1.244 +    // The window is not visible, we should reposition it to the same place as its parent
   1.245 +    HWND parentHwnd = GetParent(hwnd);
   1.246 +    RECT parentRect;
   1.247 +    GetWindowRect(parentHwnd, &parentRect);
   1.248 +    SetWindowPos(hwnd, nullptr, parentRect.left, parentRect.top, 0, 0,
   1.249 +                 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
   1.250 +  }
   1.251 +}
   1.252 +
   1.253 +// Callback hook which will ensure that the window is visible. Currently
   1.254 +// only in use on os <= XP.
   1.255 +UINT_PTR CALLBACK
   1.256 +nsFilePicker::FilePickerHook(HWND hwnd,
   1.257 +                             UINT msg,
   1.258 +                             WPARAM wParam,
   1.259 +                             LPARAM lParam) 
   1.260 +{
   1.261 +  switch(msg) {
   1.262 +    case WM_NOTIFY:
   1.263 +      {
   1.264 +        LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
   1.265 +        if (!lpofn || !lpofn->lpOFN) {
   1.266 +          return 0;
   1.267 +        }
   1.268 +        
   1.269 +        if (CDN_INITDONE == lpofn->hdr.code) {
   1.270 +          // The Window will be automatically moved to the last position after
   1.271 +          // CDN_INITDONE.  We post a message to ensure the window will be visible
   1.272 +          // so it will be done after the automatic last position window move.
   1.273 +          PostMessage(hwnd, MOZ_WM_ENSUREVISIBLE, 0, 0);
   1.274 +        }
   1.275 +      }
   1.276 +      break;
   1.277 +    case MOZ_WM_ENSUREVISIBLE:
   1.278 +      EnsureWindowVisible(GetParent(hwnd));
   1.279 +      break;
   1.280 +    case WM_INITDIALOG:
   1.281 +      {
   1.282 +        OPENFILENAMEW* pofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
   1.283 +        SetProp(hwnd, kDialogPtrProp, (HANDLE)pofn->lCustData);
   1.284 +        nsFilePicker* picker = reinterpret_cast<nsFilePicker*>(pofn->lCustData);
   1.285 +        if (picker) {
   1.286 +          picker->SetDialogHandle(hwnd);
   1.287 +          SetTimer(hwnd, kDialogTimerID, kDialogTimerTimeout, nullptr);
   1.288 +        }
   1.289 +      }
   1.290 +      break;
   1.291 +    case WM_TIMER:
   1.292 +      {
   1.293 +        // Check to see if our parent has been torn down, if so, we close too.
   1.294 +        if (wParam == kDialogTimerID) {
   1.295 +          nsFilePicker* picker = 
   1.296 +            reinterpret_cast<nsFilePicker*>(GetProp(hwnd, kDialogPtrProp));
   1.297 +          if (picker && picker->ClosePickerIfNeeded(true)) {
   1.298 +            KillTimer(hwnd, kDialogTimerID);
   1.299 +          }
   1.300 +        }
   1.301 +      }
   1.302 +      break;
   1.303 +  }
   1.304 +  return 0;
   1.305 +}
   1.306 +
   1.307 +
   1.308 +// Callback hook which will dynamically allocate a buffer large enough
   1.309 +// for the file picker dialog.  Currently only in use on  os <= XP.
   1.310 +UINT_PTR CALLBACK
   1.311 +nsFilePicker::MultiFilePickerHook(HWND hwnd,
   1.312 +                                  UINT msg,
   1.313 +                                  WPARAM wParam,
   1.314 +                                  LPARAM lParam)
   1.315 +{
   1.316 +  switch (msg) {
   1.317 +    case WM_INITDIALOG:
   1.318 +      {
   1.319 +        // Finds the child drop down of a File Picker dialog and sets the 
   1.320 +        // maximum amount of text it can hold when typed in manually.
   1.321 +        // A wParam of 0 mean 0x7FFFFFFE characters.
   1.322 +        HWND comboBox = FindWindowEx(GetParent(hwnd), nullptr, 
   1.323 +                                     L"ComboBoxEx32", nullptr );
   1.324 +        if(comboBox)
   1.325 +          SendMessage(comboBox, CB_LIMITTEXT, 0, 0);
   1.326 +        // Store our nsFilePicker ptr for future use
   1.327 +        OPENFILENAMEW* pofn = reinterpret_cast<OPENFILENAMEW*>(lParam);
   1.328 +        SetProp(hwnd, kDialogPtrProp, (HANDLE)pofn->lCustData);
   1.329 +        nsFilePicker* picker =
   1.330 +          reinterpret_cast<nsFilePicker*>(pofn->lCustData);
   1.331 +        if (picker) {
   1.332 +          picker->SetDialogHandle(hwnd);
   1.333 +          SetTimer(hwnd, kDialogTimerID, kDialogTimerTimeout, nullptr);
   1.334 +        }
   1.335 +      }
   1.336 +      break;
   1.337 +    case WM_NOTIFY:
   1.338 +      {
   1.339 +        LPOFNOTIFYW lpofn = (LPOFNOTIFYW) lParam;
   1.340 +        if (!lpofn || !lpofn->lpOFN) {
   1.341 +          return 0;
   1.342 +        }
   1.343 +        // CDN_SELCHANGE is sent when the selection in the list box of the file
   1.344 +        // selection dialog changes
   1.345 +        if (lpofn->hdr.code == CDN_SELCHANGE) {
   1.346 +          HWND parentHWND = GetParent(hwnd);
   1.347 +
   1.348 +          // Get the required size for the selected files buffer
   1.349 +          UINT newBufLength = 0; 
   1.350 +          int requiredBufLength = CommDlg_OpenSave_GetSpecW(parentHWND, 
   1.351 +                                                            nullptr, 0);
   1.352 +          if(requiredBufLength >= 0)
   1.353 +            newBufLength += requiredBufLength;
   1.354 +          else
   1.355 +            newBufLength += MAX_PATH;
   1.356 +
   1.357 +          // If the user selects multiple files, the buffer contains the 
   1.358 +          // current directory followed by the file names of the selected 
   1.359 +          // files. So make room for the directory path.  If the user
   1.360 +          // selects a single file, it is no harm to add extra space.
   1.361 +          requiredBufLength = CommDlg_OpenSave_GetFolderPathW(parentHWND, 
   1.362 +                                                              nullptr, 0);
   1.363 +          if(requiredBufLength >= 0)
   1.364 +            newBufLength += requiredBufLength;
   1.365 +          else
   1.366 +            newBufLength += MAX_PATH;
   1.367 +
   1.368 +          // Check if lpstrFile and nMaxFile are large enough
   1.369 +          if (newBufLength > lpofn->lpOFN->nMaxFile) {
   1.370 +            if (lpofn->lpOFN->lpstrFile)
   1.371 +              delete[] lpofn->lpOFN->lpstrFile;
   1.372 +
   1.373 +            // We allocate FILE_BUFFER_SIZE more bytes than is needed so that
   1.374 +            // if the user selects a file and holds down shift and down to 
   1.375 +            // select  additional items, we will not continuously reallocate
   1.376 +            newBufLength += FILE_BUFFER_SIZE;
   1.377 +
   1.378 +            wchar_t* filesBuffer = new wchar_t[newBufLength];
   1.379 +            ZeroMemory(filesBuffer, newBufLength * sizeof(wchar_t));
   1.380 +
   1.381 +            lpofn->lpOFN->lpstrFile = filesBuffer;
   1.382 +            lpofn->lpOFN->nMaxFile  = newBufLength;
   1.383 +          }
   1.384 +        }
   1.385 +      }
   1.386 +      break;
   1.387 +    case WM_TIMER:
   1.388 +      {
   1.389 +        // Check to see if our parent has been torn down, if so, we close too.
   1.390 +        if (wParam == kDialogTimerID) {
   1.391 +          nsFilePicker* picker =
   1.392 +            reinterpret_cast<nsFilePicker*>(GetProp(hwnd, kDialogPtrProp));
   1.393 +          if (picker && picker->ClosePickerIfNeeded(true)) {
   1.394 +            KillTimer(hwnd, kDialogTimerID);
   1.395 +          }
   1.396 +        }
   1.397 +      }
   1.398 +      break;
   1.399 +  }
   1.400 +
   1.401 +  return FilePickerHook(hwnd, msg, wParam, lParam);
   1.402 +}
   1.403 +
   1.404 +/*
   1.405 + * Vista+ callbacks
   1.406 + */
   1.407 +
   1.408 +HRESULT
   1.409 +nsFilePicker::OnFileOk(IFileDialog *pfd)
   1.410 +{
   1.411 +  return S_OK;
   1.412 +}
   1.413 +
   1.414 +HRESULT
   1.415 +nsFilePicker::OnFolderChanging(IFileDialog *pfd,
   1.416 +                               IShellItem *psiFolder)
   1.417 +{
   1.418 +  return S_OK;
   1.419 +}
   1.420 +
   1.421 +HRESULT
   1.422 +nsFilePicker::OnFolderChange(IFileDialog *pfd)
   1.423 +{
   1.424 +  return S_OK;
   1.425 +}
   1.426 +
   1.427 +HRESULT
   1.428 +nsFilePicker::OnSelectionChange(IFileDialog *pfd)
   1.429 +{
   1.430 +  return S_OK;
   1.431 +}
   1.432 +
   1.433 +HRESULT
   1.434 +nsFilePicker::OnShareViolation(IFileDialog *pfd,
   1.435 +                               IShellItem *psi,
   1.436 +                               FDE_SHAREVIOLATION_RESPONSE *pResponse)
   1.437 +{
   1.438 +  return S_OK;
   1.439 +}
   1.440 +
   1.441 +HRESULT
   1.442 +nsFilePicker::OnTypeChange(IFileDialog *pfd)
   1.443 +{
   1.444 +  // Failures here result in errors due to security concerns.
   1.445 +  nsRefPtr<IOleWindow> win;
   1.446 +  pfd->QueryInterface(IID_IOleWindow, getter_AddRefs(win));
   1.447 +  if (!win) {
   1.448 +    NS_ERROR("Could not retrieve the IOleWindow interface for IFileDialog.");
   1.449 +    return S_OK;
   1.450 +  }
   1.451 +  HWND hwnd = nullptr;
   1.452 +  win->GetWindow(&hwnd);
   1.453 +  if (!hwnd) {
   1.454 +    NS_ERROR("Could not retrieve the HWND for IFileDialog.");
   1.455 +    return S_OK;
   1.456 +  }
   1.457 +  
   1.458 +  SetDialogHandle(hwnd);
   1.459 +  return S_OK;
   1.460 +}
   1.461 +
   1.462 +HRESULT
   1.463 +nsFilePicker::OnOverwrite(IFileDialog *pfd,
   1.464 +                          IShellItem *psi,
   1.465 +                          FDE_OVERWRITE_RESPONSE *pResponse)
   1.466 +{
   1.467 +  return S_OK;
   1.468 +}
   1.469 +
   1.470 +/*
   1.471 + * Close on parent close logic
   1.472 + */
   1.473 +
   1.474 +bool
   1.475 +nsFilePicker::ClosePickerIfNeeded(bool aIsXPDialog)
   1.476 +{
   1.477 +  if (!mParentWidget || !mDlgWnd)
   1.478 +    return false;
   1.479 +
   1.480 +  nsWindow *win = static_cast<nsWindow *>(mParentWidget.get());
   1.481 +  // Note, the xp callbacks hand us an inner window, so we have to step up
   1.482 +  // one to get the actual dialog.
   1.483 +  HWND dlgWnd;
   1.484 +  if (aIsXPDialog)
   1.485 +    dlgWnd = GetParent(mDlgWnd);
   1.486 +  else
   1.487 +    dlgWnd = mDlgWnd;
   1.488 +  if (IsWindow(dlgWnd) && IsWindowVisible(dlgWnd) && win->DestroyCalled()) {
   1.489 +    wchar_t className[64];
   1.490 +    // Make sure we have the right window
   1.491 +    if (GetClassNameW(dlgWnd, className, mozilla::ArrayLength(className)) &&
   1.492 +        !wcscmp(className, L"#32770") &&
   1.493 +        DestroyWindow(dlgWnd)) {
   1.494 +      mDlgWnd = nullptr;
   1.495 +      return true;
   1.496 +    }
   1.497 +  }
   1.498 +  return false;
   1.499 +}
   1.500 +
   1.501 +void
   1.502 +nsFilePicker::PickerCallbackTimerFunc(nsITimer *aTimer, void *aCtx)
   1.503 +{
   1.504 +  nsFilePicker* picker = (nsFilePicker*)aCtx;
   1.505 +  if (picker->ClosePickerIfNeeded(false)) {
   1.506 +    aTimer->Cancel();
   1.507 +  }
   1.508 +}
   1.509 +
   1.510 +void
   1.511 +nsFilePicker::SetDialogHandle(HWND aWnd)
   1.512 +{
   1.513 +  if (!aWnd || mDlgWnd)
   1.514 +    return;
   1.515 +  mDlgWnd = aWnd;
   1.516 +}
   1.517 +
   1.518 +/*
   1.519 + * Folder picker invocation
   1.520 + */
   1.521 +
   1.522 +// Open the older XP style folder picker dialog. We end up in this call
   1.523 +// on XP systems or when platform is built without the longhorn SDK.
   1.524 +bool
   1.525 +nsFilePicker::ShowXPFolderPicker(const nsString& aInitialDir)
   1.526 +{
   1.527 +  bool result = false;
   1.528 +
   1.529 +  nsAutoArrayPtr<wchar_t> dirBuffer(new wchar_t[FILE_BUFFER_SIZE]);
   1.530 +  wcsncpy(dirBuffer, aInitialDir.get(), FILE_BUFFER_SIZE);
   1.531 +  dirBuffer[FILE_BUFFER_SIZE-1] = '\0';
   1.532 +
   1.533 +  AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
   1.534 +    mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
   1.535 +
   1.536 +  BROWSEINFOW browserInfo = {0};
   1.537 +  browserInfo.pidlRoot       = nullptr;
   1.538 +  browserInfo.pszDisplayName = dirBuffer;
   1.539 +  browserInfo.lpszTitle      = mTitle.get();
   1.540 +  browserInfo.ulFlags        = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
   1.541 +  browserInfo.hwndOwner      = adtw.get(); 
   1.542 +  browserInfo.iImage         = 0;
   1.543 +  browserInfo.lParam         = reinterpret_cast<LPARAM>(this);
   1.544 +
   1.545 +  if (!aInitialDir.IsEmpty()) {
   1.546 +    // the dialog is modal so that |initialDir.get()| will be valid in 
   1.547 +    // BrowserCallbackProc. Thus, we don't need to clone it.
   1.548 +    browserInfo.lParam = (LPARAM) aInitialDir.get();
   1.549 +    browserInfo.lpfn   = &BrowseCallbackProc;
   1.550 +  } else {
   1.551 +    browserInfo.lParam = 0;
   1.552 +    browserInfo.lpfn   = nullptr;
   1.553 +  }
   1.554 +
   1.555 +  LPITEMIDLIST list = ::SHBrowseForFolderW(&browserInfo);
   1.556 +  if (list) {
   1.557 +    result = ::SHGetPathFromIDListW(list, dirBuffer);
   1.558 +    if (result)
   1.559 +      mUnicodeFile.Assign(static_cast<const wchar_t*>(dirBuffer));
   1.560 +    // free PIDL
   1.561 +    CoTaskMemFree(list);
   1.562 +  }
   1.563 +
   1.564 +  return result;
   1.565 +}
   1.566 +
   1.567 +/*
   1.568 + * Show a folder picker post Windows XP
   1.569 + * 
   1.570 + * @param aInitialDir   The initial directory, the last used directory will be
   1.571 + *                      used if left blank.
   1.572 + * @param aWasInitError Out parameter will hold true if there was an error
   1.573 + *                      before the folder picker is shown.
   1.574 + * @return true if a file was selected successfully.
   1.575 +*/
   1.576 +bool
   1.577 +nsFilePicker::ShowFolderPicker(const nsString& aInitialDir, bool &aWasInitError)
   1.578 +{
   1.579 +  nsRefPtr<IFileOpenDialog> dialog;
   1.580 +  if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC,
   1.581 +                              IID_IFileOpenDialog,
   1.582 +                              getter_AddRefs(dialog)))) {
   1.583 +    aWasInitError = true;
   1.584 +    return false;
   1.585 +  }
   1.586 +  aWasInitError = false;
   1.587 +
   1.588 +  // hook up event callbacks
   1.589 +  dialog->Advise(this, &mFDECookie);
   1.590 +
   1.591 +  // options
   1.592 +  FILEOPENDIALOGOPTIONS fos = FOS_PICKFOLDERS;
   1.593 +  dialog->SetOptions(fos);
   1.594 + 
   1.595 +  // initial strings
   1.596 +  dialog->SetTitle(mTitle.get());
   1.597 +  if (!aInitialDir.IsEmpty()) {
   1.598 +    nsRefPtr<IShellItem> folder;
   1.599 +    if (SUCCEEDED(
   1.600 +          WinUtils::SHCreateItemFromParsingName(aInitialDir.get(), nullptr,
   1.601 +                                                IID_IShellItem,
   1.602 +                                                getter_AddRefs(folder)))) {
   1.603 +      dialog->SetFolder(folder);
   1.604 +    }
   1.605 +  }
   1.606 +
   1.607 +  AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
   1.608 +    mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
   1.609 + 
   1.610 +  // display
   1.611 +  nsRefPtr<IShellItem> item;
   1.612 +  if (FAILED(dialog->Show(adtw.get())) ||
   1.613 +      FAILED(dialog->GetResult(getter_AddRefs(item))) ||
   1.614 +      !item) {
   1.615 +    dialog->Unadvise(mFDECookie);
   1.616 +    return false;
   1.617 +  }
   1.618 +  dialog->Unadvise(mFDECookie);
   1.619 +
   1.620 +  // results
   1.621 +
   1.622 +  // If the user chose a Win7 Library, resolve to the library's
   1.623 +  // default save folder.
   1.624 +  nsRefPtr<IShellItem> folderPath;
   1.625 +  nsRefPtr<IShellLibrary> shellLib;
   1.626 +  CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC,
   1.627 +                   IID_IShellLibrary, getter_AddRefs(shellLib));
   1.628 +  if (shellLib &&
   1.629 +      SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) &&
   1.630 +      SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
   1.631 +                                               getter_AddRefs(folderPath)))) {
   1.632 +    item.swap(folderPath);
   1.633 +  }
   1.634 +
   1.635 +  // get the folder's file system path
   1.636 +  return WinUtils::GetShellItemPath(item, mUnicodeFile);
   1.637 +}
   1.638 +
   1.639 +/*
   1.640 + * File open and save picker invocation
   1.641 + */
   1.642 +
   1.643 +bool
   1.644 +nsFilePicker::FilePickerWrapper(OPENFILENAMEW* ofn, PickerType aType)
   1.645 +{
   1.646 +  if (!ofn)
   1.647 +    return false;
   1.648 +
   1.649 +  bool result = false;
   1.650 +  AutoWidgetPickerState awps(mParentWidget);
   1.651 +  MOZ_SEH_TRY {
   1.652 +    if (aType == PICKER_TYPE_OPEN) 
   1.653 +      result = ::GetOpenFileNameW(ofn);
   1.654 +    else if (aType == PICKER_TYPE_SAVE)
   1.655 +      result = ::GetSaveFileNameW(ofn);
   1.656 +  } MOZ_SEH_EXCEPT(true) {
   1.657 +    NS_ERROR("nsFilePicker GetFileName win32 call generated an exception! This is bad!");
   1.658 +  }
   1.659 +  return result;
   1.660 +}
   1.661 +
   1.662 +bool
   1.663 +nsFilePicker::ShowXPFilePicker(const nsString& aInitialDir)
   1.664 +{
   1.665 +  OPENFILENAMEW ofn = {0};
   1.666 +  ofn.lStructSize = sizeof(ofn);
   1.667 +  nsString filterBuffer = mFilterList;
   1.668 +                                
   1.669 +  nsAutoArrayPtr<wchar_t> fileBuffer(new wchar_t[FILE_BUFFER_SIZE]);
   1.670 +  wcsncpy(fileBuffer,  mDefaultFilePath.get(), FILE_BUFFER_SIZE);
   1.671 +  fileBuffer[FILE_BUFFER_SIZE-1] = '\0'; // null terminate in case copy truncated
   1.672 +
   1.673 +  if (!aInitialDir.IsEmpty()) {
   1.674 +    ofn.lpstrInitialDir = aInitialDir.get();
   1.675 +  }
   1.676 +
   1.677 +  AutoDestroyTmpWindow adtw((HWND) (mParentWidget.get() ?
   1.678 +    mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
   1.679 +
   1.680 +  ofn.lpstrTitle   = (LPCWSTR)mTitle.get();
   1.681 +  ofn.lpstrFilter  = (LPCWSTR)filterBuffer.get();
   1.682 +  ofn.nFilterIndex = mSelectedType;
   1.683 +  ofn.lpstrFile    = fileBuffer;
   1.684 +  ofn.nMaxFile     = FILE_BUFFER_SIZE;
   1.685 +  ofn.hwndOwner    = adtw.get();
   1.686 +  ofn.lCustData    = reinterpret_cast<LPARAM>(this);
   1.687 +  ofn.Flags = OFN_SHAREAWARE | OFN_LONGNAMES | OFN_OVERWRITEPROMPT |
   1.688 +              OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_ENABLESIZING | 
   1.689 +              OFN_EXPLORER;
   1.690 +
   1.691 +  // Windows Vista and up won't allow you to use the new looking dialogs with
   1.692 +  // a hook procedure.  The hook procedure fixes a problem on XP dialogs for
   1.693 +  // file picker visibility.  Vista and up automatically ensures the file 
   1.694 +  // picker is always visible.
   1.695 +  if (!IsVistaOrLater()) {
   1.696 +    ofn.lpfnHook = FilePickerHook;
   1.697 +    ofn.Flags |= OFN_ENABLEHOOK;
   1.698 +  }
   1.699 +
   1.700 +  // Handle add to recent docs settings
   1.701 +  if (IsPrivacyModeEnabled() || !mAddToRecentDocs) {
   1.702 +    ofn.Flags |= OFN_DONTADDTORECENT;
   1.703 +  }
   1.704 +
   1.705 +  NS_NAMED_LITERAL_STRING(htmExt, "html");
   1.706 +
   1.707 +  if (!mDefaultExtension.IsEmpty()) {
   1.708 +    ofn.lpstrDefExt = mDefaultExtension.get();
   1.709 +  } else if (IsDefaultPathHtml()) {
   1.710 +    // Get file extension from suggested filename to detect if we are
   1.711 +    // saving an html file.
   1.712 +    // This is supposed to append ".htm" if user doesn't supply an
   1.713 +    // extension but the behavior is sort of weird:
   1.714 +    // - Often appends ".html" even if you have an extension
   1.715 +    // - It obeys your extension if you put quotes around name
   1.716 +    ofn.lpstrDefExt = htmExt.get();
   1.717 +  }
   1.718 +
   1.719 +  // When possible, instead of using OFN_NOCHANGEDIR to ensure the current
   1.720 +  // working directory will not change from this call, we will retrieve the
   1.721 +  // current working directory before the call and restore it after the 
   1.722 +  // call.  This flag causes problems on Windows XP for paths that are
   1.723 +  // selected like  C:test.txt where the user is currently at C:\somepath
   1.724 +  // In which case expected result should be C:\somepath\test.txt
   1.725 +  AutoRestoreWorkingPath restoreWorkingPath;
   1.726 +  // If we can't get the current working directory, the best case is to
   1.727 +  // use the OFN_NOCHANGEDIR flag
   1.728 +  if (!restoreWorkingPath.HasWorkingPath()) {
   1.729 +    ofn.Flags |= OFN_NOCHANGEDIR;
   1.730 +  }
   1.731 +
   1.732 +  bool result = false;
   1.733 +
   1.734 +  switch(mMode) {
   1.735 +    case modeOpen:
   1.736 +      // FILE MUST EXIST!
   1.737 +      ofn.Flags |= OFN_FILEMUSTEXIST;
   1.738 +      result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
   1.739 +      break;
   1.740 +
   1.741 +    case modeOpenMultiple:
   1.742 +      ofn.Flags |= OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT;
   1.743 +
   1.744 +      // The hook set here ensures that the buffer returned will always be
   1.745 +      // large enough to hold all selected files.  The hook may modify the
   1.746 +      // value of ofn.lpstrFile and deallocate the old buffer that it pointed
   1.747 +      // to (fileBuffer). The hook assumes that the passed in value is heap 
   1.748 +      // allocated and that the returned value should be freed by the caller.
   1.749 +      // If the hook changes the buffer, it will deallocate the old buffer.
   1.750 +      // This fix would be nice to have in Vista and up, but it would force
   1.751 +      // the file picker to use the old style dialogs because hooks are not
   1.752 +      // allowed in the new file picker UI.  We need to eventually move to
   1.753 +      // the new Common File Dialogs for Vista and up.
   1.754 +      if (!IsVistaOrLater()) {
   1.755 +        ofn.lpfnHook = MultiFilePickerHook;
   1.756 +        fileBuffer.forget();
   1.757 +        result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
   1.758 +        fileBuffer = ofn.lpstrFile;
   1.759 +      } else {
   1.760 +        result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
   1.761 +      }
   1.762 +      break;
   1.763 +
   1.764 +    case modeSave:
   1.765 +      {
   1.766 +        ofn.Flags |= OFN_NOREADONLYRETURN;
   1.767 +
   1.768 +        // Don't follow shortcuts when saving a shortcut, this can be used
   1.769 +        // to trick users (bug 271732)
   1.770 +        if (IsDefaultPathLink())
   1.771 +          ofn.Flags |= OFN_NODEREFERENCELINKS;
   1.772 +
   1.773 +        result = FilePickerWrapper(&ofn, PICKER_TYPE_SAVE);
   1.774 +        if (!result) {
   1.775 +          // Error, find out what kind.
   1.776 +          if (GetLastError() == ERROR_INVALID_PARAMETER ||
   1.777 +              CommDlgExtendedError() == FNERR_INVALIDFILENAME) {
   1.778 +            // Probably the default file name is too long or contains illegal
   1.779 +            // characters. Try again, without a starting file name.
   1.780 +            ofn.lpstrFile[0] = L'\0';
   1.781 +            result = FilePickerWrapper(&ofn, PICKER_TYPE_SAVE);
   1.782 +          }
   1.783 +        }
   1.784 +      }
   1.785 +      break;
   1.786 +
   1.787 +    default:
   1.788 +      NS_NOTREACHED("unsupported file picker mode");
   1.789 +      return false;
   1.790 +  }
   1.791 +
   1.792 +  if (!result)
   1.793 +    return false;
   1.794 +
   1.795 +  // Remember what filter type the user selected
   1.796 +  mSelectedType = (int16_t)ofn.nFilterIndex;
   1.797 +
   1.798 +  // Single file selection, we're done
   1.799 +  if (mMode != modeOpenMultiple) {
   1.800 +    GetQualifiedPath(fileBuffer, mUnicodeFile);
   1.801 +    return true;
   1.802 +  }
   1.803 +
   1.804 +  // Set user-selected location of file or directory.  From msdn's "Open and
   1.805 +  // Save As Dialog Boxes" section:
   1.806 +  // If you specify OFN_EXPLORER, the directory and file name strings are '\0'
   1.807 +  // separated, with an extra '\0' character after the last file name. This
   1.808 +  // format enables the Explorer-style dialog boxes to return long file names
   1.809 +  // that include spaces. 
   1.810 +  wchar_t *current = fileBuffer;
   1.811 +  
   1.812 +  nsAutoString dirName(current);
   1.813 +  // Sometimes dirName contains a trailing slash and sometimes it doesn't:
   1.814 +  if (current[dirName.Length() - 1] != '\\')
   1.815 +    dirName.Append((char16_t)'\\');
   1.816 +  
   1.817 +  while (current && *current && *(current + wcslen(current) + 1)) {
   1.818 +    current = current + wcslen(current) + 1;
   1.819 +    
   1.820 +    nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
   1.821 +    NS_ENSURE_TRUE(file, false);
   1.822 +
   1.823 +    // Only prepend the directory if the path specified is a relative path
   1.824 +    nsAutoString path;
   1.825 +    if (PathIsRelativeW(current)) {
   1.826 +      path = dirName + nsDependentString(current);
   1.827 +    } else {
   1.828 +      path = current;
   1.829 +    }
   1.830 +
   1.831 +    nsAutoString canonicalizedPath;
   1.832 +    GetQualifiedPath(path.get(), canonicalizedPath);
   1.833 +    if (NS_FAILED(file->InitWithPath(canonicalizedPath)) ||
   1.834 +        !mFiles.AppendObject(file))
   1.835 +      return false;
   1.836 +  }
   1.837 +  
   1.838 +  // Handle the case where the user selected just one file. From msdn: If you
   1.839 +  // specify OFN_ALLOWMULTISELECT and the user selects only one file the
   1.840 +  // lpstrFile string does not have a separator between the path and file name.
   1.841 +  if (current && *current && (current == fileBuffer)) {
   1.842 +    nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
   1.843 +    NS_ENSURE_TRUE(file, false);
   1.844 +    
   1.845 +    nsAutoString canonicalizedPath;
   1.846 +    GetQualifiedPath(current, canonicalizedPath);
   1.847 +    if (NS_FAILED(file->InitWithPath(canonicalizedPath)) ||
   1.848 +        !mFiles.AppendObject(file))
   1.849 +      return false;
   1.850 +  }
   1.851 +
   1.852 +  return true;
   1.853 +}
   1.854 +
   1.855 +/*
   1.856 + * Show a file picker post Windows XP
   1.857 + * 
   1.858 + * @param aInitialDir   The initial directory, the last used directory will be
   1.859 + *                      used if left blank.
   1.860 + * @param aWasInitError Out parameter will hold true if there was an error
   1.861 + *                      before the file picker is shown.
   1.862 + * @return true if a file was selected successfully.
   1.863 +*/
   1.864 +bool
   1.865 +nsFilePicker::ShowFilePicker(const nsString& aInitialDir, bool &aWasInitError)
   1.866 +{
   1.867 +  nsRefPtr<IFileDialog> dialog;
   1.868 +  if (mMode != modeSave) {
   1.869 +    if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC,
   1.870 +                                IID_IFileOpenDialog,
   1.871 +                                getter_AddRefs(dialog)))) {
   1.872 +      aWasInitError = true;
   1.873 +      return false;
   1.874 +    }
   1.875 +  } else {
   1.876 +    if (FAILED(CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC,
   1.877 +                                IID_IFileSaveDialog,
   1.878 +                                getter_AddRefs(dialog)))) {
   1.879 +      aWasInitError = true;
   1.880 +      return false;
   1.881 +    }
   1.882 +  }
   1.883 +  aWasInitError = false;
   1.884 +
   1.885 +  // hook up event callbacks
   1.886 +  dialog->Advise(this, &mFDECookie);
   1.887 +
   1.888 +  // options
   1.889 +
   1.890 +  FILEOPENDIALOGOPTIONS fos = 0;
   1.891 +  fos |= FOS_SHAREAWARE | FOS_OVERWRITEPROMPT |
   1.892 +         FOS_FORCEFILESYSTEM;
   1.893 +
   1.894 +  // Handle add to recent docs settings
   1.895 +  if (IsPrivacyModeEnabled() || !mAddToRecentDocs) {
   1.896 +    fos |= FOS_DONTADDTORECENT;
   1.897 +  }
   1.898 +
   1.899 +  // Msdn claims FOS_NOCHANGEDIR is not needed. We'll add this
   1.900 +  // just in case.
   1.901 +  AutoRestoreWorkingPath arw;
   1.902 +
   1.903 +  // mode specific
   1.904 +  switch(mMode) {
   1.905 +    case modeOpen:
   1.906 +      fos |= FOS_FILEMUSTEXIST;
   1.907 +      break;
   1.908 +
   1.909 +    case modeOpenMultiple:
   1.910 +      fos |= FOS_FILEMUSTEXIST | FOS_ALLOWMULTISELECT;
   1.911 +      break;
   1.912 +
   1.913 +    case modeSave:
   1.914 +      fos |= FOS_NOREADONLYRETURN;
   1.915 +      // Don't follow shortcuts when saving a shortcut, this can be used
   1.916 +      // to trick users (bug 271732)
   1.917 +      if (IsDefaultPathLink())
   1.918 +        fos |= FOS_NODEREFERENCELINKS;
   1.919 +      break;
   1.920 +  }
   1.921 +
   1.922 +  dialog->SetOptions(fos);
   1.923 +
   1.924 +  // initial strings
   1.925 +
   1.926 +  // title
   1.927 +  dialog->SetTitle(mTitle.get());
   1.928 +
   1.929 +  // default filename
   1.930 +  if (!mDefaultFilename.IsEmpty()) {
   1.931 +    dialog->SetFileName(mDefaultFilename.get());
   1.932 +  }
   1.933 +  
   1.934 +  NS_NAMED_LITERAL_STRING(htmExt, "html");
   1.935 +
   1.936 +  // default extension to append to new files
   1.937 +  if (!mDefaultExtension.IsEmpty()) {
   1.938 +    dialog->SetDefaultExtension(mDefaultExtension.get());
   1.939 +  } else if (IsDefaultPathHtml()) {
   1.940 +    dialog->SetDefaultExtension(htmExt.get());
   1.941 +  }
   1.942 +
   1.943 +  // initial location
   1.944 +  if (!aInitialDir.IsEmpty()) {
   1.945 +    nsRefPtr<IShellItem> folder;
   1.946 +    if (SUCCEEDED(
   1.947 +          WinUtils::SHCreateItemFromParsingName(aInitialDir.get(), nullptr,
   1.948 +                                                IID_IShellItem,
   1.949 +                                                getter_AddRefs(folder)))) {
   1.950 +      dialog->SetFolder(folder);
   1.951 +    }
   1.952 +  }
   1.953 +
   1.954 +  // filter types and the default index
   1.955 +  if (!mComFilterList.IsEmpty()) {
   1.956 +    dialog->SetFileTypes(mComFilterList.Length(), mComFilterList.get());
   1.957 +    dialog->SetFileTypeIndex(mSelectedType);
   1.958 +  }
   1.959 +
   1.960 +  // display
   1.961 +
   1.962 +  {
   1.963 +    AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
   1.964 +      mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
   1.965 +    AutoTimerCallbackCancel atcc(this, PickerCallbackTimerFunc);
   1.966 +    AutoWidgetPickerState awps(mParentWidget);
   1.967 +
   1.968 +    if (FAILED(dialog->Show(adtw.get()))) {
   1.969 +      dialog->Unadvise(mFDECookie);
   1.970 +      return false;
   1.971 +    }
   1.972 +    dialog->Unadvise(mFDECookie);
   1.973 +  }
   1.974 +
   1.975 +  // results
   1.976 +
   1.977 +  // Remember what filter type the user selected
   1.978 +  UINT filterIdxResult;
   1.979 +  if (SUCCEEDED(dialog->GetFileTypeIndex(&filterIdxResult))) {
   1.980 +    mSelectedType = (int16_t)filterIdxResult;
   1.981 +  }
   1.982 +
   1.983 +  // single selection
   1.984 +  if (mMode != modeOpenMultiple) {
   1.985 +    nsRefPtr<IShellItem> item;
   1.986 +    if (FAILED(dialog->GetResult(getter_AddRefs(item))) || !item)
   1.987 +      return false;
   1.988 +    return WinUtils::GetShellItemPath(item, mUnicodeFile);
   1.989 +  }
   1.990 +
   1.991 +  // multiple selection
   1.992 +  nsRefPtr<IFileOpenDialog> openDlg;
   1.993 +  dialog->QueryInterface(IID_IFileOpenDialog, getter_AddRefs(openDlg));
   1.994 +  if (!openDlg) {
   1.995 +    // should not happen
   1.996 +    return false;
   1.997 +  }
   1.998 +
   1.999 +  nsRefPtr<IShellItemArray> items;
  1.1000 +  if (FAILED(openDlg->GetResults(getter_AddRefs(items))) || !items) {
  1.1001 +    return false;
  1.1002 +  }
  1.1003 +
  1.1004 +  DWORD count = 0;
  1.1005 +  items->GetCount(&count);
  1.1006 +  for (unsigned int idx = 0; idx < count; idx++) {
  1.1007 +    nsRefPtr<IShellItem> item;
  1.1008 +    nsAutoString str;
  1.1009 +    if (SUCCEEDED(items->GetItemAt(idx, getter_AddRefs(item)))) {
  1.1010 +      if (!WinUtils::GetShellItemPath(item, str))
  1.1011 +        continue;
  1.1012 +      nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
  1.1013 +      if (file && NS_SUCCEEDED(file->InitWithPath(str)))
  1.1014 +        mFiles.AppendObject(file);
  1.1015 +    }
  1.1016 +  }
  1.1017 +  return true;
  1.1018 +}
  1.1019 +
  1.1020 +///////////////////////////////////////////////////////////////////////////////
  1.1021 +// nsIFilePicker impl.
  1.1022 +
  1.1023 +NS_IMETHODIMP
  1.1024 +nsFilePicker::ShowW(int16_t *aReturnVal)
  1.1025 +{
  1.1026 +  NS_ENSURE_ARG_POINTER(aReturnVal);
  1.1027 +
  1.1028 +  *aReturnVal = returnCancel;
  1.1029 +
  1.1030 +  AutoSuppressEvents supress(mParentWidget);
  1.1031 +
  1.1032 +  nsAutoString initialDir;
  1.1033 +  if (mDisplayDirectory)
  1.1034 +    mDisplayDirectory->GetPath(initialDir);
  1.1035 +
  1.1036 +  // If no display directory, re-use the last one.
  1.1037 +  if(initialDir.IsEmpty()) {
  1.1038 +    // Allocate copy of last used dir.
  1.1039 +    initialDir = mLastUsedUnicodeDirectory;
  1.1040 +  }
  1.1041 +
  1.1042 +  // Clear previous file selections
  1.1043 +  mUnicodeFile.Truncate();
  1.1044 +  mFiles.Clear();
  1.1045 +
  1.1046 +  // Launch the XP file/folder picker on XP and as a fallback on Vista+. 
  1.1047 +  // The CoCreateInstance call to CLSID_FileOpenDialog fails with "(0x80040111)
  1.1048 +  // ClassFactory cannot supply requested class" when the checkbox for
  1.1049 +  // Disable Visual Themes is on in the compatability tab within the shortcut
  1.1050 +  // properties.
  1.1051 +  bool result = false, wasInitError = true;
  1.1052 +  if (mMode == modeGetFolder) {
  1.1053 +    if (IsVistaOrLater())
  1.1054 +      result = ShowFolderPicker(initialDir, wasInitError);
  1.1055 +    if (!result && wasInitError)
  1.1056 +      result = ShowXPFolderPicker(initialDir);
  1.1057 +  } else {
  1.1058 +    if (IsVistaOrLater())
  1.1059 +      result = ShowFilePicker(initialDir, wasInitError);
  1.1060 +    if (!result && wasInitError)
  1.1061 +      result = ShowXPFilePicker(initialDir);
  1.1062 +  }
  1.1063 +
  1.1064 +  // exit, and return returnCancel in aReturnVal
  1.1065 +  if (!result)
  1.1066 +    return NS_OK;
  1.1067 +
  1.1068 +  RememberLastUsedDirectory();
  1.1069 +
  1.1070 +  int16_t retValue = returnOK;
  1.1071 +  if (mMode == modeSave) {
  1.1072 +    // Windows does not return resultReplace, we must check if file
  1.1073 +    // already exists.
  1.1074 +    nsCOMPtr<nsIFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
  1.1075 +    bool flag = false;
  1.1076 +    if (file && NS_SUCCEEDED(file->InitWithPath(mUnicodeFile)) &&
  1.1077 +        NS_SUCCEEDED(file->Exists(&flag)) && flag) {
  1.1078 +      retValue = returnReplace;
  1.1079 +    }
  1.1080 +  }
  1.1081 +
  1.1082 +  *aReturnVal = retValue;
  1.1083 +  return NS_OK;
  1.1084 +}
  1.1085 +
  1.1086 +NS_IMETHODIMP
  1.1087 +nsFilePicker::Show(int16_t *aReturnVal)
  1.1088 +{
  1.1089 +  return ShowW(aReturnVal);
  1.1090 +}
  1.1091 +
  1.1092 +NS_IMETHODIMP
  1.1093 +nsFilePicker::GetFile(nsIFile **aFile)
  1.1094 +{
  1.1095 +  NS_ENSURE_ARG_POINTER(aFile);
  1.1096 +  *aFile = nullptr;
  1.1097 +
  1.1098 +  if (mUnicodeFile.IsEmpty())
  1.1099 +      return NS_OK;
  1.1100 +
  1.1101 +  nsCOMPtr<nsIFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
  1.1102 +    
  1.1103 +  NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
  1.1104 +
  1.1105 +  file->InitWithPath(mUnicodeFile);
  1.1106 +
  1.1107 +  NS_ADDREF(*aFile = file);
  1.1108 +
  1.1109 +  return NS_OK;
  1.1110 +}
  1.1111 +
  1.1112 +NS_IMETHODIMP
  1.1113 +nsFilePicker::GetFileURL(nsIURI **aFileURL)
  1.1114 +{
  1.1115 +  *aFileURL = nullptr;
  1.1116 +  nsCOMPtr<nsIFile> file;
  1.1117 +  nsresult rv = GetFile(getter_AddRefs(file));
  1.1118 +  if (!file)
  1.1119 +    return rv;
  1.1120 +
  1.1121 +  return NS_NewFileURI(aFileURL, file);
  1.1122 +}
  1.1123 +
  1.1124 +NS_IMETHODIMP
  1.1125 +nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
  1.1126 +{
  1.1127 +  NS_ENSURE_ARG_POINTER(aFiles);
  1.1128 +  return NS_NewArrayEnumerator(aFiles, mFiles);
  1.1129 +}
  1.1130 +
  1.1131 +// Get the file + path
  1.1132 +NS_IMETHODIMP
  1.1133 +nsBaseWinFilePicker::SetDefaultString(const nsAString& aString)
  1.1134 +{
  1.1135 +  mDefaultFilePath = aString;
  1.1136 +
  1.1137 +  // First, make sure the file name is not too long.
  1.1138 +  int32_t nameLength;
  1.1139 +  int32_t nameIndex = mDefaultFilePath.RFind("\\");
  1.1140 +  if (nameIndex == kNotFound)
  1.1141 +    nameIndex = 0;
  1.1142 +  else
  1.1143 +    nameIndex ++;
  1.1144 +  nameLength = mDefaultFilePath.Length() - nameIndex;
  1.1145 +  mDefaultFilename.Assign(Substring(mDefaultFilePath, nameIndex));
  1.1146 +  
  1.1147 +  if (nameLength > MAX_PATH) {
  1.1148 +    int32_t extIndex = mDefaultFilePath.RFind(".");
  1.1149 +    if (extIndex == kNotFound)
  1.1150 +      extIndex = mDefaultFilePath.Length();
  1.1151 +
  1.1152 +    // Let's try to shave the needed characters from the name part.
  1.1153 +    int32_t charsToRemove = nameLength - MAX_PATH;
  1.1154 +    if (extIndex - nameIndex >= charsToRemove) {
  1.1155 +      mDefaultFilePath.Cut(extIndex - charsToRemove, charsToRemove);
  1.1156 +    }
  1.1157 +  }
  1.1158 +
  1.1159 +  // Then, we need to replace illegal characters. At this stage, we cannot
  1.1160 +  // replace the backslash as the string might represent a file path.
  1.1161 +  mDefaultFilePath.ReplaceChar(FILE_ILLEGAL_CHARACTERS, '-');
  1.1162 +  mDefaultFilename.ReplaceChar(FILE_ILLEGAL_CHARACTERS, '-');
  1.1163 +
  1.1164 +  return NS_OK;
  1.1165 +}
  1.1166 +
  1.1167 +NS_IMETHODIMP
  1.1168 +nsBaseWinFilePicker::GetDefaultString(nsAString& aString)
  1.1169 +{
  1.1170 +  return NS_ERROR_FAILURE;
  1.1171 +}
  1.1172 +
  1.1173 +// The default extension to use for files
  1.1174 +NS_IMETHODIMP
  1.1175 +nsBaseWinFilePicker::GetDefaultExtension(nsAString& aExtension)
  1.1176 +{
  1.1177 +  aExtension = mDefaultExtension;
  1.1178 +  return NS_OK;
  1.1179 +}
  1.1180 +
  1.1181 +NS_IMETHODIMP
  1.1182 +nsBaseWinFilePicker::SetDefaultExtension(const nsAString& aExtension)
  1.1183 +{
  1.1184 +  mDefaultExtension = aExtension;
  1.1185 +  return NS_OK;
  1.1186 +}
  1.1187 +
  1.1188 +// Set the filter index
  1.1189 +NS_IMETHODIMP
  1.1190 +nsFilePicker::GetFilterIndex(int32_t *aFilterIndex)
  1.1191 +{
  1.1192 +  // Windows' filter index is 1-based, we use a 0-based system.
  1.1193 +  *aFilterIndex = mSelectedType - 1;
  1.1194 +  return NS_OK;
  1.1195 +}
  1.1196 +
  1.1197 +NS_IMETHODIMP
  1.1198 +nsFilePicker::SetFilterIndex(int32_t aFilterIndex)
  1.1199 +{
  1.1200 +  // Windows' filter index is 1-based, we use a 0-based system.
  1.1201 +  mSelectedType = aFilterIndex + 1;
  1.1202 +  return NS_OK;
  1.1203 +}
  1.1204 +
  1.1205 +void
  1.1206 +nsFilePicker::InitNative(nsIWidget *aParent,
  1.1207 +                         const nsAString& aTitle)
  1.1208 +{
  1.1209 +  mParentWidget = aParent;
  1.1210 +  mTitle.Assign(aTitle);
  1.1211 +}
  1.1212 +
  1.1213 +void 
  1.1214 +nsFilePicker::GetQualifiedPath(const wchar_t *aInPath, nsString &aOutPath)
  1.1215 +{
  1.1216 +  // Prefer a qualified path over a non qualified path.
  1.1217 +  // Things like c:file.txt would be accepted in Win XP but would later
  1.1218 +  // fail to open from the download manager.
  1.1219 +  wchar_t qualifiedFileBuffer[MAX_PATH];
  1.1220 +  if (PathSearchAndQualifyW(aInPath, qualifiedFileBuffer, MAX_PATH)) {
  1.1221 +    aOutPath.Assign(qualifiedFileBuffer);
  1.1222 +  } else {
  1.1223 +    aOutPath.Assign(aInPath);
  1.1224 +  }
  1.1225 +}
  1.1226 +
  1.1227 +void
  1.1228 +nsFilePicker::AppendXPFilter(const nsAString& aTitle, const nsAString& aFilter)
  1.1229 +{
  1.1230 +  mFilterList.Append(aTitle);
  1.1231 +  mFilterList.Append(char16_t('\0'));
  1.1232 +
  1.1233 +  if (aFilter.EqualsLiteral("..apps"))
  1.1234 +    mFilterList.AppendLiteral("*.exe;*.com");
  1.1235 +  else
  1.1236 +  {
  1.1237 +    nsAutoString filter(aFilter);
  1.1238 +    filter.StripWhitespace();
  1.1239 +    if (filter.EqualsLiteral("*"))
  1.1240 +      filter.AppendLiteral(".*");
  1.1241 +    mFilterList.Append(filter);
  1.1242 +  }
  1.1243 +
  1.1244 +  mFilterList.Append(char16_t('\0'));
  1.1245 +}
  1.1246 +
  1.1247 +NS_IMETHODIMP
  1.1248 +nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
  1.1249 +{
  1.1250 +  if (IsVistaOrLater()) {
  1.1251 +    mComFilterList.Append(aTitle, aFilter);
  1.1252 +  } else {
  1.1253 +    AppendXPFilter(aTitle, aFilter);
  1.1254 +  }
  1.1255 +  return NS_OK;
  1.1256 +}
  1.1257 +
  1.1258 +void
  1.1259 +nsFilePicker::RememberLastUsedDirectory()
  1.1260 +{
  1.1261 +  nsCOMPtr<nsIFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
  1.1262 +  if (!file || NS_FAILED(file->InitWithPath(mUnicodeFile))) {
  1.1263 +    NS_WARNING("RememberLastUsedDirectory failed to init file path.");
  1.1264 +    return;
  1.1265 +  }
  1.1266 +
  1.1267 +  nsCOMPtr<nsIFile> dir;
  1.1268 +  nsAutoString newDir;
  1.1269 +  if (NS_FAILED(file->GetParent(getter_AddRefs(dir))) ||
  1.1270 +      !(mDisplayDirectory = do_QueryInterface(dir)) ||
  1.1271 +      NS_FAILED(mDisplayDirectory->GetPath(newDir)) ||
  1.1272 +      newDir.IsEmpty()) {
  1.1273 +    NS_WARNING("RememberLastUsedDirectory failed to get parent directory.");
  1.1274 +    return;
  1.1275 +  }
  1.1276 +
  1.1277 +  if (mLastUsedUnicodeDirectory) {
  1.1278 +    NS_Free(mLastUsedUnicodeDirectory);
  1.1279 +    mLastUsedUnicodeDirectory = nullptr;
  1.1280 +  }
  1.1281 +  mLastUsedUnicodeDirectory = ToNewUnicode(newDir);
  1.1282 +}
  1.1283 +
  1.1284 +bool
  1.1285 +nsFilePicker::IsPrivacyModeEnabled()
  1.1286 +{
  1.1287 +  return mLoadContext && mLoadContext->UsePrivateBrowsing();
  1.1288 +}
  1.1289 +
  1.1290 +bool
  1.1291 +nsFilePicker::IsDefaultPathLink()
  1.1292 +{
  1.1293 +  NS_ConvertUTF16toUTF8 ext(mDefaultFilePath);
  1.1294 +  ext.Trim(" .", false, true); // watch out for trailing space and dots
  1.1295 +  ToLowerCase(ext);
  1.1296 +  if (StringEndsWith(ext, NS_LITERAL_CSTRING(".lnk")) ||
  1.1297 +      StringEndsWith(ext, NS_LITERAL_CSTRING(".pif")) ||
  1.1298 +      StringEndsWith(ext, NS_LITERAL_CSTRING(".url")))
  1.1299 +    return true;
  1.1300 +  return false;
  1.1301 +}
  1.1302 +
  1.1303 +bool
  1.1304 +nsFilePicker::IsDefaultPathHtml()
  1.1305 +{
  1.1306 +  int32_t extIndex = mDefaultFilePath.RFind(".");
  1.1307 +  if (extIndex >= 0) {
  1.1308 +    nsAutoString ext;
  1.1309 +    mDefaultFilePath.Right(ext, mDefaultFilePath.Length() - extIndex);
  1.1310 +    if (ext.LowerCaseEqualsLiteral(".htm")  ||
  1.1311 +        ext.LowerCaseEqualsLiteral(".html") ||
  1.1312 +        ext.LowerCaseEqualsLiteral(".shtml"))
  1.1313 +      return true;
  1.1314 +  }
  1.1315 +  return false;
  1.1316 +}
  1.1317 +
  1.1318 +void
  1.1319 +nsFilePicker::ComDlgFilterSpec::Append(const nsAString& aTitle, const nsAString& aFilter)
  1.1320 +{
  1.1321 +  COMDLG_FILTERSPEC* pSpecForward = mSpecList.AppendElement();
  1.1322 +  if (!pSpecForward) {
  1.1323 +    NS_WARNING("mSpecList realloc failed.");
  1.1324 +    return;
  1.1325 +  }
  1.1326 +  memset(pSpecForward, 0, sizeof(*pSpecForward));
  1.1327 +  nsString* pStr = mStrings.AppendElement(aTitle);
  1.1328 +  if (!pStr) {
  1.1329 +    NS_WARNING("mStrings.AppendElement failed.");
  1.1330 +    return;
  1.1331 +  }
  1.1332 +  pSpecForward->pszName = pStr->get();
  1.1333 +  pStr = mStrings.AppendElement(aFilter);
  1.1334 +  if (!pStr) {
  1.1335 +    NS_WARNING("mStrings.AppendElement failed.");
  1.1336 +    return;
  1.1337 +  }
  1.1338 +  if (aFilter.EqualsLiteral("..apps"))
  1.1339 +    pStr->AssignLiteral("*.exe;*.com");
  1.1340 +  else {
  1.1341 +    pStr->StripWhitespace();
  1.1342 +    if (pStr->EqualsLiteral("*"))
  1.1343 +      pStr->AppendLiteral(".*");
  1.1344 +  }
  1.1345 +  pSpecForward->pszSpec = pStr->get();
  1.1346 +}

mercurial