Bug 1357846 - Introducing nsIFilePicker.displaySpecialDirectory, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 26 Apr 2017 18:20:19 +0200
changeset 405562 272037dc7f5763e96cf7cb0e25375656f5ac769a
parent 405561 7ea39575ae0cf1ae5a0958c1bc3de9ac9e3a37e1
child 405563 c556baa9feb8a9beae76a667458dc32043c3a4b7
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1357846
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1357846 - Introducing nsIFilePicker.displaySpecialDirectory, r=smaug nsIFilePicker.displaySpecialDirectory is a string that can be set to TmpD, Desk, or any other special directory value. The real value of this directory will be read in the parent process.
dom/html/HTMLInputElement.cpp
dom/html/test/test_filepicker_default_directory.html
dom/ipc/FilePickerParent.cpp
dom/ipc/FilePickerParent.h
dom/ipc/PFilePicker.ipdl
layout/forms/test/test_bug536567_perwindowpb.html
mobile/android/components/FilePicker.js
mobile/android/components/geckoview/GeckoViewPrompt.js
testing/specialpowers/content/MockFilePicker.jsm
toolkit/components/filepicker/content/filepicker.js
toolkit/components/filepicker/nsFilePicker.js
widget/nsBaseFilePicker.cpp
widget/nsBaseFilePicker.h
widget/nsFilePickerProxy.cpp
widget/nsIFilePicker.idl
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -424,34 +424,39 @@ NS_IMPL_ISUPPORTS(UploadLastDir::Content
 NS_IMETHODIMP
 UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason)
 {
   nsCOMPtr<nsIFile> localFile;
   nsAutoString prefStr;
 
   if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
     prefStr = Preferences::GetString("dom.input.fallbackUploadDir");
-    if (prefStr.IsEmpty()) {
-      // If no custom directory was set through the pref, default to
-      // "desktop" directory for each platform.
-      NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(localFile));
-    }
-  }
-
-  if (!localFile) {
-    if (prefStr.IsEmpty() && mResult) {
-      nsCOMPtr<nsIVariant> pref;
-      mResult->GetValue(getter_AddRefs(pref));
-      pref->GetAsAString(prefStr);
-    }
+  }
+
+  if (prefStr.IsEmpty() && mResult) {
+    nsCOMPtr<nsIVariant> pref;
+    mResult->GetValue(getter_AddRefs(pref));
+    pref->GetAsAString(prefStr);
+  }
+
+  if (!prefStr.IsEmpty()) {
     localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
-    localFile->InitWithPath(prefStr);
-  }
-
-  mFilePicker->SetDisplayDirectory(localFile);
+    if (localFile && NS_WARN_IF(NS_FAILED(localFile->InitWithPath(prefStr)))) {
+      localFile = nullptr;
+    }
+  }
+
+  if (localFile) {
+    mFilePicker->SetDisplayDirectory(localFile);
+  } else {
+    // If no custom directory was set through the pref, default to
+    // "desktop" directory for each platform.
+    mFilePicker->SetDisplaySpecialDirectory(NS_LITERAL_STRING(NS_OS_DESKTOP_DIR));
+  }
+
   mFilePicker->Open(mFpCallback);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UploadLastDir::ContentPrefCallback::HandleResult(nsIContentPref* pref)
 {
   mResult = pref;
--- a/dom/html/test/test_filepicker_default_directory.html
+++ b/dom/html/test/test_filepicker_default_directory.html
@@ -42,25 +42,28 @@ var MockFilePicker = SpecialPowers.MockF
 MockFilePicker.init(window);
 
 // need to show the MockFilePicker so .displayDirectory gets set
 var f = document.getElementById("f");
 f.focus();
 
 var testIndex = 0;
 var tests = [
-  ["", defaultUploadDirectory.path],
-  [customUploadDirectory.path, customUploadDirectory.path]
+  ["", null, "Desk"],
+  [customUploadDirectory.path, customUploadDirectory.path, ""]
 ]
 
 MockFilePicker.showCallback = function(filepicker) {
-  info(SpecialPowers.wrap(MockFilePicker).displayDirectory.path);
+  if (tests[testIndex][1] === null) {
+    is(SpecialPowers.wrap(MockFilePicker).displayDirectory, null, "DisplayDirectory is null");
+  } else {
+    is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path, tests[testIndex][1], "DisplayDirectory matches the path");
+  }
 
-  is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path,
-  tests[testIndex][1]);
+  is(SpecialPowers.wrap(MockFilePicker).displaySpecialDirectory, tests[testIndex][2], "DisplaySpecialDirectory matches the path");
 
   if (++testIndex == tests.length) {
     MockFilePicker.cleanup();
     SimpleTest.finish();
   } else {
     launchNextTest();
   }
 }
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -255,16 +255,17 @@ FilePickerParent::CreateFilePicker()
 mozilla::ipc::IPCResult
 FilePickerParent::RecvOpen(const int16_t& aSelectedType,
                            const bool& aAddToRecentDocs,
                            const nsString& aDefaultFile,
                            const nsString& aDefaultExtension,
                            InfallibleTArray<nsString>&& aFilters,
                            InfallibleTArray<nsString>&& aFilterNames,
                            const nsString& aDisplayDirectory,
+                           const nsString& aDisplaySpecialDirectory,
                            const nsString& aOkButtonLabel)
 {
   if (!CreateFilePicker()) {
     Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
     return IPC_OK();
   }
 
   mFilePicker->SetAddToRecentDocs(aAddToRecentDocs);
@@ -279,16 +280,18 @@ FilePickerParent::RecvOpen(const int16_t
   mFilePicker->SetOkButtonLabel(aOkButtonLabel);
 
   if (!aDisplayDirectory.IsEmpty()) {
     nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
     if (localFile) {
       localFile->InitWithPath(aDisplayDirectory);
       mFilePicker->SetDisplayDirectory(localFile);
     }
+  } else if (!aDisplaySpecialDirectory.IsEmpty()) {
+    mFilePicker->SetDisplaySpecialDirectory(aDisplaySpecialDirectory);
   }
 
   mCallback = new FilePickerShownCallback(this);
 
   mFilePicker->Open(mCallback);
   return IPC_OK();
 }
 
--- a/dom/ipc/FilePickerParent.h
+++ b/dom/ipc/FilePickerParent.h
@@ -47,16 +47,17 @@ class FilePickerParent : public PFilePic
 
   virtual mozilla::ipc::IPCResult RecvOpen(const int16_t& aSelectedType,
                                            const bool& aAddToRecentDocs,
                                            const nsString& aDefaultFile,
                                            const nsString& aDefaultExtension,
                                            InfallibleTArray<nsString>&& aFilters,
                                            InfallibleTArray<nsString>&& aFilterNames,
                                            const nsString& aDisplayDirectory,
+                                           const nsString& aDisplaySpecialDirectory,
                                            const nsString& aOkButtonLabel) override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   class FilePickerShownCallback : public nsIFilePickerShownCallback
   {
   public:
     explicit FilePickerShownCallback(FilePickerParent* aFilePickerParent)
--- a/dom/ipc/PFilePicker.ipdl
+++ b/dom/ipc/PFilePicker.ipdl
@@ -36,16 +36,17 @@ union MaybeInputData
 
 protocol PFilePicker
 {
   manager PBrowser;
 
 parent:
     async Open(int16_t selectedType, bool addToRecentDocs, nsString defaultFile,
                nsString defaultExtension, nsString[] filters, nsString[] filterNames,
-               nsString displayDirectory, nsString okButtonLabel);
+               nsString displayDirectory, nsString displaySpecialDirectory,
+               nsString okButtonLabel);
 
 child:
     async __delete__(MaybeInputData data, int16_t result);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/layout/forms/test/test_bug536567_perwindowpb.html
+++ b/layout/forms/test/test_bug536567_perwindowpb.html
@@ -165,17 +165,20 @@ function testOnWindow(aIsPrivate, aCallb
     win.setTimeout(function() { win.gBrowser.loadURI(contentPage); }, 0);
   });
 }
 
 MockFilePicker.showCallback = function(filepicker) {
   var test = tests[testIndex];
   var returned = -1;
   for (var i = 0; i < dirs.length; i++) {
-    if (dirs[i].path == MockFilePicker.displayDirectory.path) {
+     var dir = MockFilePicker.displayDirectory
+                 ? MockFilePicker.displayDirectory
+                 : Services.dirsvc.get(MockFilePicker.displaySpecialDirectory, Ci.nsILocalFile);
+    if (dirs[i].path == dir.path) {
       returned = i;
       break;
     }
   }
   if (test[1] == -1) {
     ok(false, "We should never get an unknown directory back");
   } else {
     is(returned, test[1], 'test ' + testIndex);
--- a/mobile/android/components/FilePicker.js
+++ b/mobile/android/components/FilePicker.js
@@ -19,16 +19,17 @@ function FilePicker() {
 FilePicker.prototype = {
   _mimeTypeFilter: 0,
   _extensionsFilter: "",
   _defaultString: "",
   _domWin: null,
   _domFile: null,
   _defaultExtension: null,
   _displayDirectory: null,
+  _displaySpecialDirectory: null,
   _filePath: null,
   _promptActive: false,
   _filterIndex: 0,
   _addToRecentDocs: false,
   _title: "",
 
   init: function(aParent, aTitle, aMode) {
     this._domWin = aParent;
@@ -117,16 +118,24 @@ FilePicker.prototype = {
   get displayDirectory() {
     return this._displayDirectory;
   },
 
   set displayDirectory(dir) {
     this._displayDirectory = dir;
   },
 
+  get displaySpecialDirectory() {
+    return this._displaySpecialDirectory;
+  },
+
+  set displaySpecialDirectory(dir) {
+    this._displaySpecialDirectory = dir;
+  },
+
   get file() {
     if (!this._filePath) {
         return null;
     }
 
     return new FileUtils.File(this._filePath);
   },
 
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.js
@@ -1012,16 +1012,23 @@ FilePickerDelegate.prototype = {
 
   get displayDirectory() {
     return null;
   },
 
   set displayDirectory(aValue) {
   },
 
+  get displaySpecialDirectory() {
+    return "";
+  },
+
+  set displaySpecialDirectory(aValue) {
+  },
+
   get addToRecentDocs() {
     return false;
   },
 
   set addToRecentDocs(aValue) {
   },
 
   get okButtonLabel() {
--- a/testing/specialpowers/content/MockFilePicker.jsm
+++ b/testing/specialpowers/content/MockFilePicker.jsm
@@ -67,16 +67,17 @@ this.MockFilePicker = {
       registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory);
     }
   },
 
   reset: function() {
     this.appendFilterCallback = null;
     this.appendFiltersCallback = null;
     this.displayDirectory = null;
+    this.displaySpecialDirectory = "";
     this.filterIndex = 0;
     this.mode = null;
     this.returnData = [];
     this.returnValue = null;
     this.showCallback = null;
     this.afterOpenCallback = null;
     this.shown = false;
     this.showing = false;
@@ -174,16 +175,17 @@ MockFilePickerInstance.prototype = {
     if (typeof MockFilePicker.appendFiltersCallback == "function")
       MockFilePicker.appendFiltersCallback(this, aFilterMask);
   },
   defaultString: "",
   defaultExtension: "",
   parent: null,
   filterIndex: 0,
   displayDirectory: null,
+  displaySpecialDirectory: "",
   get file() {
     if (MockFilePicker.returnData.length >= 1) {
       return MockFilePicker.returnData[0].nsIFile;
     }
 
     return null;
   },
 
@@ -264,16 +266,17 @@ MockFilePickerInstance.prototype = {
         // Nothing else has to be done.
         MockFilePicker.pendingPromises = [];
 
         if (result == Ci.nsIFilePicker.returnCancel) {
           return result;
         }
 
         MockFilePicker.displayDirectory = this.displayDirectory;
+        MockFilePicker.displaySpecialDirectory = this.displaySpecialDirectory;
         MockFilePicker.shown = true;
         if (typeof MockFilePicker.showCallback == "function") {
           try {
             var returnValue = MockFilePicker.showCallback(this);
             if (typeof returnValue != "undefined") {
               return returnValue;
             }
           } catch(ex) {
--- a/toolkit/components/filepicker/content/filepicker.js
+++ b/toolkit/components/filepicker/content/filepicker.js
@@ -42,16 +42,17 @@ function filepickerLoad() {
   if (window.arguments) {
     var o = window.arguments[0];
     retvals = o.retvals; /* set this to a global var so we can set return values */
     const title = o.title;
     filePickerMode = o.mode;
     if (o.displayDirectory) {
       var directory = o.displayDirectory.path;
     }
+    var specialDirectory = o.displaySpecialDirectory;
 
     const initialText = o.defaultString;
     var filterTitles = o.filters.titles;
     var filterTypes = o.filters.types;
     var numFilters = filterTitles.length;
 
     document.title = title;
     allowURLs = o.allowURLs;
@@ -128,31 +129,33 @@ function filepickerLoad() {
 
   // Start out with the ok button disabled since nothing will be
   // selected and nothing will be in the text field.
   okButton.disabled = filePickerMode != nsIFilePicker.modeGetFolder;
 
   // This allows the window to show onscreen before we begin
   // loading the file list
 
-  setTimeout(setInitialDirectory, 0, directory);
+  setTimeout(setInitialDirectory, 0, { directory, specialDirectory });
 }
 
-function setInitialDirectory(directory) {
+function setInitialDirectory(directories) {
   // Start in the user's home directory
   var dirService = Components.classes[NS_DIRECTORYSERVICE_CONTRACTID]
                              .getService(nsIProperties);
-  homeDir = dirService.get("Home", Components.interfaces.nsIFile);
+  homeDir = dirService.get(directories.specialDirectory
+                             ? directories.specialDirectory : "Home",
+                           Components.interfaces.nsIFile);
 
-  if (directory) {
-    sfile.initWithPath(directory);
+  if (directories.directory) {
+    sfile.initWithPath(directories.directory);
     if (!sfile.exists() || !sfile.isDirectory())
-      directory = false;
+      directories.directory = false;
   }
-  if (!directory) {
+  if (!directories.directory) {
     sfile.initWithPath(homeDir.path);
   }
 
   gotoDirectory(sfile);
 }
 
 function onFilterChanged(target) {
   // Do this on a timeout callback so the filter list can roll up
--- a/toolkit/components/filepicker/nsFilePicker.js
+++ b/toolkit/components/filepicker/nsFilePicker.js
@@ -50,16 +50,17 @@ function nsFilePicker() {
     filterBundle = srGetStrBundle("chrome://global/content/filepicker.properties");
 
   /* attributes */
   this.mDefaultString = "";
   this.mFilterIndex = 0;
   this.mFilterTitles = new Array();
   this.mFilters = new Array();
   this.mDisplayDirectory = null;
+  this.mDisplaySpecialDirectory = null;
   if (lastDirectory) {
     try {
       var dir = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile);
       dir.initWithPath(lastDirectory);
       this.mDisplayDirectory = dir;
     } catch (e) {}
   }
 }
@@ -82,16 +83,24 @@ nsFilePicker.prototype = {
       a.clone().QueryInterface(nsILocalFile);
   },
   get displayDirectory() {
     return this.mDisplayDirectory &&
            this.mDisplayDirectory.clone()
                .QueryInterface(nsILocalFile);
   },
 
+  /* attribute AString displaySpecialDirectory; */
+  set displaySpecialDirectory(a) {
+    this.mDisplaySpecialDirectory = a;
+  },
+  get displaySpecialDirectory() {
+    return this.mDisplaySpecialDirectory;
+  },
+
   /* readonly attribute nsILocalFile file; */
   get file() { return this.mFilesEnumerator.mFiles[0]; },
 
   /* readonly attribute nsISimpleEnumerator files; */
   get files() { return this.mFilesEnumerator; },
 
   /* we don't support directories, yet */
   get domFileOrDirectory() {
@@ -251,16 +260,17 @@ nsFilePicker.prototype = {
     });
   },
 
   show() {
     var o = {};
     o.title = this.mTitle;
     o.mode = this.mMode;
     o.displayDirectory = this.mDisplayDirectory;
+    o.displaySpecialDirectory = this.mDisplaySpecialDirectory;
     o.defaultString = this.mDefaultString;
     o.filterIndex = this.mFilterIndex;
     o.filters = {};
     o.filters.titles = this.mFilterTitles;
     o.filters.types = this.mFilters;
     o.allowURLs = this.mAllowURLs;
     o.retvals = {};
 
--- a/widget/nsBaseFilePicker.cpp
+++ b/widget/nsBaseFilePicker.cpp
@@ -290,43 +290,81 @@ NS_IMETHODIMP nsBaseFilePicker::GetFiles
   files.AppendObject(file);
 
   return NS_NewArrayEnumerator(aFiles, files);
 }
 
 // Set the display directory
 NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile *aDirectory)
 {
+  // if displaySpecialDirectory has been previously called, let's abort this
+  // operation.
+  if (!mDisplaySpecialDirectory.IsEmpty()) {
+    return NS_OK;
+  }
+
   if (!aDirectory) {
     mDisplayDirectory = nullptr;
     return NS_OK;
   }
   nsCOMPtr<nsIFile> directory;
   nsresult rv = aDirectory->Clone(getter_AddRefs(directory));
   if (NS_FAILED(rv))
     return rv;
   mDisplayDirectory = do_QueryInterface(directory, &rv);
   return rv;
 }
 
 // Get the display directory
 NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory)
 {
   *aDirectory = nullptr;
+
+  // if displaySpecialDirectory has been previously called, let's abort this
+  // operation.
+  if (!mDisplaySpecialDirectory.IsEmpty()) {
+    return NS_OK;
+  }
+
   if (!mDisplayDirectory)
     return NS_OK;
   nsCOMPtr<nsIFile> directory;
   nsresult rv = mDisplayDirectory->Clone(getter_AddRefs(directory));
   if (NS_FAILED(rv)) {
     return rv;
   }
   directory.forget(aDirectory);
   return NS_OK;
 }
 
+// Set the display special directory
+NS_IMETHODIMP nsBaseFilePicker::SetDisplaySpecialDirectory(const nsAString& aDirectory)
+{
+  // if displayDirectory has been previously called, let's abort this operation.
+  if (mDisplayDirectory && mDisplaySpecialDirectory.IsEmpty()) {
+    return NS_OK;
+  }
+
+  mDisplaySpecialDirectory = aDirectory;
+  if (mDisplaySpecialDirectory.IsEmpty()) {
+    mDisplayDirectory = nullptr;
+    return NS_OK;
+  }
+
+  return NS_GetSpecialDirectory(NS_ConvertUTF16toUTF8(mDisplaySpecialDirectory).get(),
+                                getter_AddRefs(mDisplayDirectory));
+}
+
+// Get the display special directory
+NS_IMETHODIMP nsBaseFilePicker::GetDisplaySpecialDirectory(nsAString& aDirectory)
+{
+  aDirectory = mDisplaySpecialDirectory;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsBaseFilePicker::GetAddToRecentDocs(bool *aFlag)
 {
   *aFlag = mAddToRecentDocs;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/widget/nsBaseFilePicker.h
+++ b/widget/nsBaseFilePicker.h
@@ -29,30 +29,33 @@ public:
 
   NS_IMETHOD Open(nsIFilePickerShownCallback *aCallback);
   NS_IMETHOD AppendFilters(int32_t filterMask);
   NS_IMETHOD GetFilterIndex(int32_t *aFilterIndex);
   NS_IMETHOD SetFilterIndex(int32_t aFilterIndex);
   NS_IMETHOD GetFiles(nsISimpleEnumerator **aFiles);
   NS_IMETHOD GetDisplayDirectory(nsIFile * *aDisplayDirectory);
   NS_IMETHOD SetDisplayDirectory(nsIFile * aDisplayDirectory);
+  NS_IMETHOD GetDisplaySpecialDirectory(nsAString& aDisplayDirectory);
+  NS_IMETHOD SetDisplaySpecialDirectory(const nsAString& aDisplayDirectory);
   NS_IMETHOD GetAddToRecentDocs(bool *aFlag);
   NS_IMETHOD SetAddToRecentDocs(bool aFlag);
   NS_IMETHOD GetMode(int16_t *aMode);
   NS_IMETHOD SetOkButtonLabel(const nsAString& aLabel);
   NS_IMETHOD GetOkButtonLabel(nsAString& aLabel);
 
   NS_IMETHOD GetDomFileOrDirectory(nsISupports** aValue);
   NS_IMETHOD GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aValue);
 
 protected:
 
   virtual void InitNative(nsIWidget *aParent, const nsAString& aTitle) = 0;
 
   bool mAddToRecentDocs;
   nsCOMPtr<nsIFile> mDisplayDirectory;
+  nsString mDisplaySpecialDirectory;
 
   nsCOMPtr<nsPIDOMWindowOuter> mParent;
   int16_t mMode;
   nsString mOkButtonLabel;
 };
 
 #endif // nsBaseFilePicker_h__
--- a/widget/nsFilePickerProxy.cpp
+++ b/widget/nsFilePickerProxy.cpp
@@ -139,17 +139,18 @@ nsFilePickerProxy::Open(nsIFilePickerSho
     mDisplayDirectory->GetPath(displayDirectory);
   }
 
   if (!mIPCActive) {
     return NS_ERROR_FAILURE;
   }
 
   SendOpen(mSelectedType, mAddToRecentDocs, mDefault, mDefaultExtension,
-           mFilters, mFilterNames, displayDirectory, mOkButtonLabel);
+           mFilters, mFilterNames, displayDirectory, mDisplaySpecialDirectory,
+           mOkButtonLabel);
 
   return NS_OK;
 }
 
 mozilla::ipc::IPCResult
 nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData,
                                   const int16_t& aResult)
 {
--- a/widget/nsIFilePicker.idl
+++ b/widget/nsIFilePicker.idl
@@ -107,22 +107,35 @@ interface nsIFilePicker : nsISupports
   * The filter which is currently selected in the File Picker dialog
   *
   * @return Returns the index (0 based) of the selected filter in the filter list. 
   */
   attribute long filterIndex;
 
  /**
   * Set the directory that the file open/save dialog initially displays
+  * Note that, if displaySpecialDirectory has been already set, this value will
+  * be ignored.
   *
   * @param      displayDirectory  the name of the directory
   *
   */
   attribute nsIFile displayDirectory;
 
+ /**
+  * Set the directory that the file open/save dialog initially displays using
+  * one of the special name as such as 'Desk', 'TmpD', and so on.
+  * Note that, if displayDirectory has been already set, this value will be
+  * ignored.
+  *
+  * @param      displaySpecialDirectory  the name of the special directory
+  *
+  */
+  attribute AString displaySpecialDirectory;
+
 
  /**
   * Get the nsIFile for the file or directory.
   *
   * @return Returns the file currently selected
   */
   readonly attribute nsIFile file;