michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const { Cc, Ci, Cu } = require("chrome"); michael@0: michael@0: const { Promise: promise } = require("resource://gre/modules/Promise.jsm"); michael@0: michael@0: const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {}); michael@0: const { TextEncoder, TextDecoder } = Cu.import('resource://gre/modules/commonjs/toolkit/loader.js', {}); michael@0: const gcli = require("gcli/index"); michael@0: michael@0: loader.lazyGetter(this, "prefBranch", function() { michael@0: let prefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService); michael@0: return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2); michael@0: }); michael@0: michael@0: loader.lazyGetter(this, "supportsString", function() { michael@0: return Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); michael@0: }); michael@0: michael@0: loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); michael@0: michael@0: const PREF_DIR = "devtools.commands.dir"; michael@0: michael@0: /** michael@0: * Load all the .mozcmd files in the directory pointed to by PREF_DIR michael@0: * @return A promise of an array of items suitable for gcli.addItems or michael@0: * using in gcli.addItemsByModule michael@0: */ michael@0: function loadItemsFromMozDir() { michael@0: let dirName = prefBranch.getComplexValue(PREF_DIR, michael@0: Ci.nsISupportsString).data.trim(); michael@0: if (dirName == "") { michael@0: return promise.resolve([]); michael@0: } michael@0: michael@0: // replaces ~ with the home directory path in unix and windows michael@0: if (dirName.indexOf("~") == 0) { michael@0: let dirService = Cc["@mozilla.org/file/directory_service;1"] michael@0: .getService(Ci.nsIProperties); michael@0: let homeDirFile = dirService.get("Home", Ci.nsIFile); michael@0: let homeDir = homeDirFile.path; michael@0: dirName = dirName.substr(1); michael@0: dirName = homeDir + dirName; michael@0: } michael@0: michael@0: // statPromise resolves to nothing if dirName is a directory, or it michael@0: // rejects with an error message otherwise michael@0: let statPromise = OS.File.stat(dirName); michael@0: statPromise = statPromise.then( michael@0: function onSuccess(stat) { michael@0: if (!stat.isDir) { michael@0: throw new Error("'" + dirName + "' is not a directory."); michael@0: } michael@0: }, michael@0: function onFailure(reason) { michael@0: if (reason instanceof OS.File.Error && reason.becauseNoSuchFile) { michael@0: throw new Error("'" + dirName + "' does not exist."); michael@0: } else { michael@0: throw reason; michael@0: } michael@0: } michael@0: ); michael@0: michael@0: // We need to return (a promise of) an array of items from the *.mozcmd michael@0: // files in dirName (which we can assume to be a valid directory now) michael@0: return statPromise.then(() => { michael@0: let itemPromises = []; michael@0: michael@0: let iterator = new OS.File.DirectoryIterator(dirName); michael@0: let iterPromise = iterator.forEach(entry => { michael@0: if (entry.name.match(/.*\.mozcmd$/) && !entry.isDir) { michael@0: itemPromises.push(loadCommandFile(entry)); michael@0: } michael@0: }); michael@0: michael@0: return iterPromise.then(() => { michael@0: iterator.close(); michael@0: return promise.all(itemPromises).then((itemsArray) => { michael@0: return itemsArray.reduce((prev, curr) => { michael@0: return prev.concat(curr); michael@0: }, []); michael@0: }); michael@0: }, reason => { iterator.close(); throw reason; }); michael@0: }); michael@0: } michael@0: michael@0: exports.mozDirLoader = function(name) { michael@0: return loadItemsFromMozDir().then(items => { michael@0: return { items: items }; michael@0: }); michael@0: }; michael@0: michael@0: /** michael@0: * Load the commands from a single file michael@0: * @param OS.File.DirectoryIterator.Entry entry The DirectoryIterator michael@0: * Entry of the file containing the commands that we should read michael@0: */ michael@0: function loadCommandFile(entry) { michael@0: let readPromise = OS.File.read(entry.path); michael@0: return readPromise = readPromise.then(array => { michael@0: let decoder = new TextDecoder(); michael@0: let source = decoder.decode(array); michael@0: var principal = Cc["@mozilla.org/systemprincipal;1"] michael@0: .createInstance(Ci.nsIPrincipal); michael@0: michael@0: let sandbox = new Cu.Sandbox(principal, { michael@0: sandboxName: entry.path michael@0: }); michael@0: let data = Cu.evalInSandbox(source, sandbox, "1.8", entry.name, 1); michael@0: michael@0: if (!Array.isArray(data)) { michael@0: console.error("Command file '" + entry.name + "' does not have top level array."); michael@0: return; michael@0: } michael@0: michael@0: return data; michael@0: }); michael@0: } michael@0: michael@0: exports.items = [ michael@0: { michael@0: name: "cmd", michael@0: get hidden() { michael@0: return !prefBranch.prefHasUserValue(PREF_DIR); michael@0: }, michael@0: description: gcli.lookup("cmdDesc") michael@0: }, michael@0: { michael@0: name: "cmd refresh", michael@0: description: gcli.lookup("cmdRefreshDesc"), michael@0: get hidden() { michael@0: return !prefBranch.prefHasUserValue(PREF_DIR); michael@0: }, michael@0: exec: function(args, context) { michael@0: gcli.load(); michael@0: michael@0: let dirName = prefBranch.getComplexValue(PREF_DIR, michael@0: Ci.nsISupportsString).data.trim(); michael@0: return gcli.lookupFormat("cmdStatus2", [ dirName ]); michael@0: } michael@0: }, michael@0: { michael@0: name: "cmd setdir", michael@0: description: gcli.lookup("cmdSetdirDesc"), michael@0: params: [ michael@0: { michael@0: name: "directory", michael@0: description: gcli.lookup("cmdSetdirDirectoryDesc"), michael@0: type: { michael@0: name: "file", michael@0: filetype: "directory", michael@0: existing: "yes" michael@0: }, michael@0: defaultValue: null michael@0: } michael@0: ], michael@0: returnType: "string", michael@0: get hidden() { michael@0: return true; // !prefBranch.prefHasUserValue(PREF_DIR); michael@0: }, michael@0: exec: function(args, context) { michael@0: supportsString.data = args.directory; michael@0: prefBranch.setComplexValue(PREF_DIR, Ci.nsISupportsString, supportsString); michael@0: michael@0: gcli.load(); michael@0: michael@0: return gcli.lookupFormat("cmdStatus2", [ args.directory ]); michael@0: } michael@0: } michael@0: ];