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.
338 lines
11 KiB
JavaScript
338 lines
11 KiB
JavaScript
/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* No magic constructor behaviour, as is de rigeur for XPCOM.
|
|
* If you must perform some initialization, and it could possibly fail (even
|
|
* due to an out-of-memory condition), you should use an Init method, which
|
|
* can convey failure appropriately (thrown exception in JS,
|
|
* NS_FAILED(nsresult) return in C++).
|
|
*
|
|
* In JS, you can actually cheat, because a thrown exception will cause the
|
|
* CreateInstance call to fail in turn, but not all languages are so lucky.
|
|
* (Though ANSI C++ provides exceptions, they are verboten in Mozilla code
|
|
* for portability reasons -- and even when you're building completely
|
|
* platform-specific code, you can't throw across an XPCOM method boundary.)
|
|
*/
|
|
|
|
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
const DEBUG = false; /* set to true to enable debug messages */
|
|
var debug;
|
|
|
|
const LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1";
|
|
const APPSHELL_SERV_CONTRACTID = "@mozilla.org/appshell/appShellService;1";
|
|
const STRBUNDLE_SERV_CONTRACTID = "@mozilla.org/intl/stringbundle;1";
|
|
|
|
const nsIAppShellService = Components.interfaces.nsIAppShellService;
|
|
const nsILocalFile = Components.interfaces.nsILocalFile;
|
|
const nsIFileURL = Components.interfaces.nsIFileURL;
|
|
const nsISupports = Components.interfaces.nsISupports;
|
|
const nsIFactory = Components.interfaces.nsIFactory;
|
|
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
|
const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor;
|
|
const nsIDOMWindow = Components.interfaces.nsIDOMWindow;
|
|
const nsIStringBundleService = Components.interfaces.nsIStringBundleService;
|
|
const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
|
|
const nsIDocShellTreeItem = Components.interfaces.nsIDocShellTreeItem;
|
|
const nsIBaseWindow = Components.interfaces.nsIBaseWindow;
|
|
|
|
var titleBundle = null;
|
|
var filterBundle = null;
|
|
var lastDirectory = null;
|
|
|
|
function nsFilePicker() {
|
|
if (!titleBundle)
|
|
titleBundle = srGetStrBundle("chrome://global/locale/filepicker.properties");
|
|
if (!filterBundle)
|
|
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) {}
|
|
}
|
|
}
|
|
|
|
nsFilePicker.prototype = {
|
|
classID: Components.ID("{54ae32f8-1dd2-11b2-a209-df7c505370f8}"),
|
|
|
|
QueryInterface(iid) {
|
|
if (iid.equals(nsIFilePicker) ||
|
|
iid.equals(nsISupports))
|
|
return this;
|
|
|
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
|
},
|
|
|
|
|
|
/* attribute nsILocalFile displayDirectory; */
|
|
set displayDirectory(a) {
|
|
this.mDisplayDirectory = a &&
|
|
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() {
|
|
let enumerator = this.domFileOrDirectoryEnumerator;
|
|
return enumerator ? enumerator.mFiles[0] : null;
|
|
},
|
|
|
|
/* readonly attribute nsISimpleEnumerator domFileOrDirectoryEnumerator; */
|
|
get domFileOrDirectoryEnumerator() {
|
|
if (!this.mFilesEnumerator) {
|
|
return null;
|
|
}
|
|
return this.mDOMFilesEnumerator;
|
|
},
|
|
|
|
/* readonly attribute nsIURI fileURL; */
|
|
get fileURL() {
|
|
if (this.mFileURL)
|
|
return this.mFileURL;
|
|
|
|
if (!this.mFilesEnumerator)
|
|
return null;
|
|
|
|
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
|
|
.getService(Components.interfaces.nsIIOService);
|
|
|
|
return this.mFileURL = ioService.newFileURI(this.file);
|
|
},
|
|
|
|
/* attribute wstring defaultString; */
|
|
set defaultString(a) { this.mDefaultString = a; },
|
|
get defaultString() { return this.mDefaultString; },
|
|
|
|
/* attribute wstring defaultExtension */
|
|
set defaultExtension(ext) { },
|
|
get defaultExtension() { return ""; },
|
|
|
|
/* attribute long filterIndex; */
|
|
set filterIndex(a) { this.mFilterIndex = a; },
|
|
get filterIndex() { return this.mFilterIndex; },
|
|
|
|
/* attribute boolean addToRecentDocs; */
|
|
set addToRecentDocs(a) {},
|
|
get addToRecentDocs() { return false; },
|
|
|
|
/* readonly attribute short mode; */
|
|
get mode() { return this.mMode; },
|
|
|
|
/* members */
|
|
mFilesEnumerator: undefined,
|
|
mDOMFilesEnumerator: undefined,
|
|
mParentWindow: null,
|
|
|
|
/* methods */
|
|
init(parent, title, mode) {
|
|
this.mParentWindow = parent;
|
|
this.mTitle = title;
|
|
this.mMode = mode;
|
|
},
|
|
|
|
appendFilters(filterMask) {
|
|
if (filterMask & nsIFilePicker.filterHTML) {
|
|
this.appendFilter(titleBundle.GetStringFromName("htmlTitle"),
|
|
filterBundle.GetStringFromName("htmlFilter"));
|
|
}
|
|
if (filterMask & nsIFilePicker.filterText) {
|
|
this.appendFilter(titleBundle.GetStringFromName("textTitle"),
|
|
filterBundle.GetStringFromName("textFilter"));
|
|
}
|
|
if (filterMask & nsIFilePicker.filterImages) {
|
|
this.appendFilter(titleBundle.GetStringFromName("imageTitle"),
|
|
filterBundle.GetStringFromName("imageFilter"));
|
|
}
|
|
if (filterMask & nsIFilePicker.filterXML) {
|
|
this.appendFilter(titleBundle.GetStringFromName("xmlTitle"),
|
|
filterBundle.GetStringFromName("xmlFilter"));
|
|
}
|
|
if (filterMask & nsIFilePicker.filterXUL) {
|
|
this.appendFilter(titleBundle.GetStringFromName("xulTitle"),
|
|
filterBundle.GetStringFromName("xulFilter"));
|
|
}
|
|
this.mAllowURLs = !!(filterMask & nsIFilePicker.filterAllowURLs);
|
|
if (filterMask & nsIFilePicker.filterApps) {
|
|
// We use "..apps" as a special filter for executable files
|
|
this.appendFilter(titleBundle.GetStringFromName("appsTitle"),
|
|
"..apps");
|
|
}
|
|
if (filterMask & nsIFilePicker.filterAudio) {
|
|
this.appendFilter(titleBundle.GetStringFromName("audioTitle"),
|
|
filterBundle.GetStringFromName("audioFilter"));
|
|
}
|
|
if (filterMask & nsIFilePicker.filterVideo) {
|
|
this.appendFilter(titleBundle.GetStringFromName("videoTitle"),
|
|
filterBundle.GetStringFromName("videoFilter"));
|
|
}
|
|
if (filterMask & nsIFilePicker.filterAll) {
|
|
this.appendFilter(titleBundle.GetStringFromName("allTitle"),
|
|
filterBundle.GetStringFromName("allFilter"));
|
|
}
|
|
},
|
|
|
|
appendFilter(title, extensions) {
|
|
this.mFilterTitles.push(title);
|
|
this.mFilters.push(extensions);
|
|
},
|
|
|
|
open(aFilePickerShownCallback) {
|
|
var tm = Components.classes["@mozilla.org/thread-manager;1"]
|
|
.getService(Components.interfaces.nsIThreadManager);
|
|
tm.dispatchToMainThread(() => {
|
|
let result = Components.interfaces.nsIFilePicker.returnCancel;
|
|
try {
|
|
result = this.show();
|
|
} catch (ex) {
|
|
}
|
|
|
|
let promises = [];
|
|
|
|
// Let's create the DOMFileEnumerator right now because it requires some
|
|
// async operation.
|
|
if (this.mFilesEnumerator) {
|
|
this.mDOMFilesEnumerator = {
|
|
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISimpleEnumerator]),
|
|
|
|
mFiles: [],
|
|
mIndex: 0,
|
|
|
|
hasMoreElements() {
|
|
return (this.mIndex < this.mFiles.length);
|
|
},
|
|
|
|
getNext() {
|
|
if (this.mIndex >= this.mFiles.length) {
|
|
throw Components.results.NS_ERROR_FAILURE;
|
|
}
|
|
return this.mFiles[this.mIndex++];
|
|
}
|
|
};
|
|
|
|
for (let i = 0; i < this.mFilesEnumerator.mFiles.length; ++i) {
|
|
if (this.mFilesEnumerator.mFiles[i].exists()) {
|
|
let promise =
|
|
this.mParentWindow.File.createFromNsIFile(
|
|
this.mFilesEnumerator.mFiles[i]).then(file => {
|
|
this.mDOMFilesEnumerator.mFiles.push(file);
|
|
});
|
|
promises.push(promise);
|
|
}
|
|
}
|
|
}
|
|
|
|
Promise.all(promises).then(() => {
|
|
if (aFilePickerShownCallback) {
|
|
aFilePickerShownCallback.done(result);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
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 = {};
|
|
|
|
var parent;
|
|
if (this.mParentWindow) {
|
|
parent = this.mParentWindow;
|
|
} else if (typeof(window) == "object" && window != null) {
|
|
parent = window;
|
|
} else {
|
|
try {
|
|
var appShellService = Components.classes[APPSHELL_SERV_CONTRACTID].getService(nsIAppShellService);
|
|
parent = appShellService.hiddenDOMWindow;
|
|
} catch (ex) {
|
|
debug("Can't get parent. xpconnect hates me so we can't get one from the appShellService.\n");
|
|
debug(ex + "\n");
|
|
}
|
|
}
|
|
|
|
try {
|
|
parent.openDialog("chrome://global/content/filepicker.xul",
|
|
"",
|
|
"chrome,modal,titlebar,resizable=yes,dependent=yes",
|
|
o);
|
|
|
|
this.mFilterIndex = o.retvals.filterIndex;
|
|
this.mFilesEnumerator = o.retvals.files;
|
|
this.mFileURL = o.retvals.fileURL;
|
|
lastDirectory = o.retvals.directory;
|
|
return o.retvals.buttonStatus;
|
|
} catch (ex) { dump("unable to open file picker\n" + ex + "\n"); }
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (DEBUG)
|
|
debug = function(s) { dump("-*- filepicker: " + s + "\n"); };
|
|
else
|
|
debug = function(s) {};
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsFilePicker]);
|
|
|
|
/* crap from strres.js that I want to use for string bundles since I can't include another .js file.... */
|
|
|
|
var strBundleService = null;
|
|
|
|
function srGetStrBundle(path) {
|
|
var strBundle = null;
|
|
|
|
if (!strBundleService) {
|
|
try {
|
|
strBundleService = Components.classes[STRBUNDLE_SERV_CONTRACTID].getService(nsIStringBundleService);
|
|
} catch (ex) {
|
|
dump("\n--** strBundleService createInstance failed **--\n");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
strBundle = strBundleService.createBundle(path);
|
|
if (!strBundle) {
|
|
dump("\n--** strBundle createInstance failed **--\n");
|
|
}
|
|
return strBundle;
|
|
}
|