browser_createWindowsShortcut.js (5403B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 ChromeUtils.defineESModuleGetters(this, { 7 FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs", 8 MockRegistrar: "resource://testing-common/MockRegistrar.sys.mjs", 9 }); 10 11 const gBase = Services.dirsvc.get("ProfD", Ci.nsIFile); 12 gBase.append("CreateWindowsShortcut"); 13 createDirectory(gBase); 14 15 const gTmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile); 16 17 const gDirectoryServiceProvider = { 18 getFile(prop, persistent) { 19 persistent.value = false; 20 21 // We only expect a narrow range of calls. 22 let folder = gBase.clone(); 23 switch (prop) { 24 case "Progs": 25 folder.append("Programs"); 26 break; 27 case "Desk": 28 folder.append("Desktop"); 29 break; 30 case "UpdRootD": 31 // We really want DataRoot, but UpdateSubdir is what we usually get. 32 folder.append("DataRoot"); 33 folder.append("UpdateDir"); 34 folder.append("UpdateSubdir"); 35 break; 36 case "ProfD": 37 // Used by test infrastructure. 38 folder = folder.parent; 39 break; 40 case "TmpD": 41 // Used by FileTestUtils. 42 folder = gTmpDir; 43 break; 44 default: 45 console.error(`Access to unexpected directory '${prop}'`); 46 return Cr.NS_ERROR_FAILURE; 47 } 48 49 createDirectory(folder); 50 return folder; 51 }, 52 QueryInterface: ChromeUtils.generateQI([Ci.nsIDirectoryServiceProvider]), 53 }; 54 55 add_setup(() => { 56 Services.dirsvc 57 .QueryInterface(Ci.nsIDirectoryService) 58 .registerProvider(gDirectoryServiceProvider); 59 }); 60 61 registerCleanupFunction(() => { 62 gBase.remove(true); 63 Services.dirsvc 64 .QueryInterface(Ci.nsIDirectoryService) 65 .unregisterProvider(gDirectoryServiceProvider); 66 }); 67 68 add_task(async function test_CreateWindowsShortcut() { 69 const DEST = "browser_createWindowsShortcut_TestFile.lnk"; 70 71 const file = FileTestUtils.getTempFile("program.exe"); 72 const iconPath = FileTestUtils.getTempFile("program.ico"); 73 74 let shortcut; 75 76 const defaults = { 77 shellService: Cc["@mozilla.org/toolkit/shell-service;1"].getService(), 78 targetFile: file, 79 iconFile: iconPath, 80 description: "made by browser_createWindowsShortcut.js", 81 aumid: "TESTTEST", 82 }; 83 84 shortcut = Services.dirsvc.get("Progs", Ci.nsIFile); 85 shortcut.append(DEST); 86 await testShortcut({ 87 shortcutFile: shortcut, 88 relativePath: DEST, 89 specialFolder: "Programs", 90 logHeader: "STARTMENU", 91 ...defaults, 92 }); 93 94 let subdir = Services.dirsvc.get("Progs", Ci.nsIFile); 95 subdir.append("Shortcut Test"); 96 tryRemove(subdir); 97 98 shortcut = subdir.clone(); 99 shortcut.append(DEST); 100 await testShortcut({ 101 shortcutFile: shortcut, 102 relativePath: "Shortcut Test\\" + DEST, 103 specialFolder: "Programs", 104 logHeader: "STARTMENU", 105 ...defaults, 106 }); 107 tryRemove(subdir); 108 109 shortcut = Services.dirsvc.get("Desk", Ci.nsIFile); 110 shortcut.append(DEST); 111 await testShortcut({ 112 shortcutFile: shortcut, 113 relativePath: DEST, 114 specialFolder: "Desktop", 115 logHeader: "DESKTOP", 116 ...defaults, 117 }); 118 }); 119 120 async function testShortcut({ 121 shortcutFile, 122 relativePath, 123 specialFolder, 124 logHeader, 125 126 // Generally provided by the defaults. 127 shellService, 128 targetFile, 129 iconFile, 130 description, 131 aumid, 132 }) { 133 // If it already exists, remove it. 134 tryRemove(shortcutFile); 135 136 await shellService.createShortcut( 137 targetFile, 138 [], 139 description, 140 iconFile, 141 0, 142 aumid, 143 specialFolder, 144 relativePath 145 ); 146 ok( 147 shortcutFile.exists(), 148 `${specialFolder}\\${relativePath}: Shortcut should exist` 149 ); 150 ok( 151 queryShortcutLog(relativePath, logHeader), 152 `${specialFolder}\\${relativePath}: Shortcut log entry was added` 153 ); 154 await shellService.deleteShortcut(specialFolder, relativePath); 155 ok( 156 !shortcutFile.exists(), 157 `${specialFolder}\\${relativePath}: Shortcut does not exist after deleting` 158 ); 159 ok( 160 !queryShortcutLog(relativePath, logHeader), 161 `${specialFolder}\\${relativePath}: Shortcut log entry was removed` 162 ); 163 } 164 165 function queryShortcutLog(aShortcutName, aSection) { 166 const parserFactory = Cc[ 167 "@mozilla.org/xpcom/ini-parser-factory;1" 168 ].createInstance(Ci.nsIINIParserFactory); 169 170 const dir = Services.dirsvc.get("UpdRootD", Ci.nsIFile).parent.parent; 171 const enumerator = dir.directoryEntries; 172 173 for (const file of enumerator) { 174 // We don't know the user's SID from JS-land, so just look at all of them. 175 if (!file.path.match(/[^_]+_S[^_]*_shortcuts.ini/)) { 176 continue; 177 } 178 179 const parser = parserFactory.createINIParser(file); 180 parser.QueryInterface(Ci.nsIINIParser); 181 parser.QueryInterface(Ci.nsIINIParserWriter); 182 183 for (let i = 0; ; i++) { 184 try { 185 let string = parser.getString(aSection, `Shortcut${i}`); 186 if (string == aShortcutName) { 187 enumerator.close(); 188 return true; 189 } 190 } catch (e) { 191 // The key didn't exist, stop here. 192 break; 193 } 194 } 195 } 196 197 enumerator.close(); 198 return false; 199 } 200 201 function createDirectory(aFolder) { 202 try { 203 aFolder.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); 204 } catch (e) { 205 if (e.result != Cr.NS_ERROR_FILE_ALREADY_EXISTS) { 206 throw e; 207 } 208 } 209 } 210 211 function tryRemove(file) { 212 try { 213 file.remove(false); 214 return true; 215 } catch (e) { 216 return false; 217 } 218 }