forked from mirrors/gecko-dev
		
	 ea09f270ab
			
		
	
	
		ea09f270ab
		
	
	
	
	
		
			
			MozReview-Commit-ID: EXJNufdKKhJ --HG-- extra : rebase_source : 66d17c7981c4b0987c482ce092b25990b42c07fb
		
			
				
	
	
		
			337 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			337 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 = [];
 | |
|   this.mFilters = [];
 | |
|   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;
 | |
| }
 |