test_FirefoxBridgeExtensionUtilsNativeManifest.js (9933B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { AppConstants } = ChromeUtils.importESModule( 7 "resource://gre/modules/AppConstants.sys.mjs" 8 ); 9 const { FileUtils } = ChromeUtils.importESModule( 10 "resource://gre/modules/FileUtils.sys.mjs" 11 ); 12 const { FirefoxBridgeExtensionUtils } = ChromeUtils.importESModule( 13 "resource:///modules/FirefoxBridgeExtensionUtils.sys.mjs" 14 ); 15 16 const DUAL_BROWSER_EXTENSION_ORIGIN = ["chrome-extension://fake-origin/"]; 17 const NATIVE_MESSAGING_HOST_ID = "org.mozilla.firefox_bridge_test"; 18 19 if (AppConstants.platform == "win") { 20 var { MockRegistry } = ChromeUtils.importESModule( 21 "resource://testing-common/MockRegistry.sys.mjs" 22 ); 23 } 24 25 let registry = null; 26 add_setup(() => { 27 if (AppConstants.platform == "win") { 28 registry = new MockRegistry(); 29 registerCleanupFunction(() => { 30 registry.shutdown(); 31 }); 32 } 33 }); 34 35 function resetMockRegistry() { 36 if (AppConstants.platform != "win") { 37 return; 38 } 39 registry.shutdown(); 40 registry = new MockRegistry(); 41 } 42 43 let dir = FileUtils.getDir("TmpD", ["NativeMessagingHostsTest"]); 44 dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); 45 46 let userDir = dir.clone(); 47 userDir.append("user"); 48 userDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); 49 50 let appDir = dir.clone(); 51 appDir.append("app"); 52 appDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); 53 54 let dirProvider = { 55 getFile(property) { 56 if (property == "Home") { 57 return userDir.clone(); 58 } else if (property == "AppData") { 59 return appDir.clone(); 60 } 61 return null; 62 }, 63 }; 64 65 try { 66 Services.dirsvc.undefine("Home"); 67 } catch (e) {} 68 try { 69 Services.dirsvc.undefine("AppData"); 70 } catch (e) {} 71 Services.dirsvc.registerProvider(dirProvider); 72 73 registerCleanupFunction(() => { 74 Services.dirsvc.unregisterProvider(dirProvider); 75 dir.remove(true); 76 }); 77 78 const USER_TEST_PATH = PathUtils.join(userDir.path, "manifestDir"); 79 80 let binFile = null; 81 add_setup(async function () { 82 binFile = Services.dirsvc.get("XREExeF", Ci.nsIFile).parent.clone(); 83 if (AppConstants.platform == "win") { 84 binFile.append("nmhproxy.exe"); 85 } else if (AppConstants.platform == "macosx") { 86 binFile.append("nmhproxy"); 87 } else { 88 throw new Error("Unsupported platform"); 89 } 90 }); 91 92 function getExpectedOutput() { 93 return { 94 name: NATIVE_MESSAGING_HOST_ID, 95 description: "Firefox Native Messaging Host", 96 path: binFile.path, 97 type: "stdio", 98 allowed_origins: DUAL_BROWSER_EXTENSION_ORIGIN, 99 }; 100 } 101 102 function DumpWindowsRegistry() { 103 let key = ""; 104 let pathBuffer = []; 105 106 if (AppConstants.platform == "win") { 107 function bufferPrint(line) { 108 let regPath = line.trimStart(); 109 if (regPath.includes(":")) { 110 // After trimming white space, keys are formatted as 111 // ": <key> (<value_type>)". We can assume it's only ever 112 // going to be of type REG_SZ for this test. 113 key = regPath.slice(2, regPath.length - " (REG_SZ)".length); 114 } else { 115 pathBuffer.push(regPath); 116 } 117 } 118 119 MockRegistry.dump( 120 MockRegistry.getRoot(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER), 121 "", 122 bufferPrint 123 ); 124 } else { 125 Assert.ok(false, "Only windows has a registry!"); 126 } 127 128 return [pathBuffer, key]; 129 } 130 131 add_task(async function test_maybeWriteManifestFiles() { 132 await FirefoxBridgeExtensionUtils.maybeWriteManifestFiles( 133 USER_TEST_PATH, 134 NATIVE_MESSAGING_HOST_ID, 135 DUAL_BROWSER_EXTENSION_ORIGIN 136 ); 137 let expectedOutput = JSON.stringify(getExpectedOutput()); 138 let nmhManifestFilePath = PathUtils.join( 139 USER_TEST_PATH, 140 `${NATIVE_MESSAGING_HOST_ID}.json` 141 ); 142 let nmhManifestFileContent = await IOUtils.readUTF8(nmhManifestFilePath); 143 await IOUtils.remove(nmhManifestFilePath); 144 Assert.equal(nmhManifestFileContent, expectedOutput); 145 }); 146 147 add_task(async function test_maybeWriteManifestFilesIncorrect() { 148 let nmhManifestFile = await IOUtils.getFile( 149 USER_TEST_PATH, 150 `${NATIVE_MESSAGING_HOST_ID}.json` 151 ); 152 153 let incorrectInput = { 154 name: NATIVE_MESSAGING_HOST_ID, 155 description: "Manifest with unexpected description", 156 path: binFile.path, 157 type: "stdio", 158 allowed_origins: DUAL_BROWSER_EXTENSION_ORIGIN, 159 }; 160 await IOUtils.writeJSON(nmhManifestFile.path, incorrectInput); 161 162 // Write correct JSON to the file and check to make sure it matches 163 // the expected output 164 await FirefoxBridgeExtensionUtils.maybeWriteManifestFiles( 165 USER_TEST_PATH, 166 NATIVE_MESSAGING_HOST_ID, 167 DUAL_BROWSER_EXTENSION_ORIGIN 168 ); 169 let expectedOutput = JSON.stringify(getExpectedOutput()); 170 171 let nmhManifestFilePath = PathUtils.join( 172 USER_TEST_PATH, 173 `${NATIVE_MESSAGING_HOST_ID}.json` 174 ); 175 let nmhManifestFileContent = await IOUtils.readUTF8(nmhManifestFilePath); 176 await IOUtils.remove(nmhManifestFilePath); 177 Assert.equal(nmhManifestFileContent, expectedOutput); 178 }); 179 180 add_task(async function test_maybeWriteManifestFilesAlreadyExists() { 181 // Write file and confirm it exists 182 await FirefoxBridgeExtensionUtils.maybeWriteManifestFiles( 183 USER_TEST_PATH, 184 NATIVE_MESSAGING_HOST_ID, 185 DUAL_BROWSER_EXTENSION_ORIGIN 186 ); 187 let nmhManifestFile = await IOUtils.getFile( 188 USER_TEST_PATH, 189 `${NATIVE_MESSAGING_HOST_ID}.json` 190 ); 191 192 // Modify file modificatiomn time to be older than the write time 193 let oldModificationTime = Date.now() - 1000000; 194 let setModificationTime = await IOUtils.setModificationTime( 195 nmhManifestFile.path, 196 oldModificationTime 197 ); 198 Assert.equal(oldModificationTime, setModificationTime); 199 200 // Call function which writes correct JSON to the file and make sure 201 // the modification time is the same, meaning we haven't written anything 202 await FirefoxBridgeExtensionUtils.maybeWriteManifestFiles( 203 USER_TEST_PATH, 204 NATIVE_MESSAGING_HOST_ID, 205 DUAL_BROWSER_EXTENSION_ORIGIN 206 ); 207 let stat = await IOUtils.stat(nmhManifestFile.path); 208 await IOUtils.remove(nmhManifestFile.path); 209 Assert.equal(stat.lastModified, oldModificationTime); 210 }); 211 212 add_task(async function test_maybeWriteManifestFilesDirDoesNotExist() { 213 let testDir = dir.clone(); 214 // This folder does not exist, so we want to make sure it's created 215 testDir.append("dirDoesNotExist"); 216 await FirefoxBridgeExtensionUtils.maybeWriteManifestFiles( 217 testDir.path, 218 NATIVE_MESSAGING_HOST_ID, 219 DUAL_BROWSER_EXTENSION_ORIGIN 220 ); 221 222 ok(await IOUtils.exists(testDir.path)); 223 ok( 224 await IOUtils.exists( 225 PathUtils.join(testDir.path, `${NATIVE_MESSAGING_HOST_ID}.json`) 226 ) 227 ); 228 await IOUtils.remove(testDir.path, { recursive: true }); 229 }); 230 231 add_task(async function test_ensureRegistered() { 232 let expectedJSONDirPath = null; 233 let nativeHostId = "org.mozilla.firefox_bridge_nmh"; 234 if (AppConstants.NIGHTLY_BUILD) { 235 nativeHostId = "org.mozilla.firefox_bridge_nmh_nightly"; 236 } else if (AppConstants.MOZ_DEV_EDITION) { 237 nativeHostId = "org.mozilla.firefox_bridge_nmh_dev"; 238 } else if (AppConstants.IS_ESR) { 239 nativeHostId = "org.mozilla.firefox_bridge_nmh_esr"; 240 } 241 242 if (AppConstants.platform == "macosx") { 243 expectedJSONDirPath = PathUtils.joinRelative( 244 userDir.path, 245 "Library/Application Support/Google/Chrome/NativeMessagingHosts/" 246 ); 247 } else if (AppConstants.platform == "win") { 248 expectedJSONDirPath = PathUtils.joinRelative( 249 appDir.path, 250 "Mozilla\\Firefox" 251 ); 252 resetMockRegistry(); 253 } else { 254 throw new Error("Unsupported platform"); 255 } 256 257 ok(!(await IOUtils.exists(expectedJSONDirPath))); 258 let expectedJSONPath = PathUtils.join( 259 expectedJSONDirPath, 260 `${nativeHostId}.json` 261 ); 262 263 await FirefoxBridgeExtensionUtils.ensureRegistered(); 264 let realOutput = { 265 name: nativeHostId, 266 description: "Firefox Native Messaging Host", 267 path: binFile.path, 268 type: "stdio", 269 allowed_origins: FirefoxBridgeExtensionUtils.getExtensionOrigins(), 270 }; 271 272 let expectedOutput = JSON.stringify(realOutput); 273 let JSONContent = await IOUtils.readUTF8(expectedJSONPath); 274 await IOUtils.remove(expectedJSONPath); 275 Assert.equal(JSONContent, expectedOutput); 276 277 // Test that the registry key is written for Windows only 278 if (AppConstants.platform == "win") { 279 let [pathBuffer, key] = DumpWindowsRegistry(); 280 Assert.equal( 281 pathBuffer.toString(), 282 [ 283 "Software", 284 "Google", 285 "Chrome", 286 "NativeMessagingHosts", 287 nativeHostId, 288 ].toString() 289 ); 290 Assert.equal(key, expectedJSONPath); 291 } 292 }); 293 294 add_task(async function test_maybeWriteNativeMessagingRegKeys() { 295 if (AppConstants.platform != "win") { 296 return; 297 } 298 resetMockRegistry(); 299 FirefoxBridgeExtensionUtils.maybeWriteNativeMessagingRegKeys( 300 "Test\\Path\\For\\Reg\\Key", 301 binFile.parent.path, 302 NATIVE_MESSAGING_HOST_ID 303 ); 304 let [pathBuffer, key] = DumpWindowsRegistry(); 305 registry.shutdown(); 306 Assert.equal( 307 pathBuffer.toString(), 308 ["Test", "Path", "For", "Reg", "Key", NATIVE_MESSAGING_HOST_ID].toString() 309 ); 310 console.log("The key is: " + key); 311 Assert.equal(key, `${binFile.parent.path}\\${NATIVE_MESSAGING_HOST_ID}.json`); 312 }); 313 314 add_task(async function test_maybeWriteNativeMessagingRegKeysIncorrectValue() { 315 if (AppConstants.platform != "win") { 316 return; 317 } 318 resetMockRegistry(); 319 registry.setValue( 320 Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, 321 `Test\\Path\\For\\Reg\\Key\\${NATIVE_MESSAGING_HOST_ID}`, 322 "", 323 "IncorrectValue" 324 ); 325 FirefoxBridgeExtensionUtils.maybeWriteNativeMessagingRegKeys( 326 "Test\\Path\\For\\Reg\\Key", 327 binFile.parent.path, 328 NATIVE_MESSAGING_HOST_ID 329 ); 330 let [pathBuffer, key] = DumpWindowsRegistry(); 331 registry.shutdown(); 332 Assert.equal( 333 pathBuffer.toString(), 334 ["Test", "Path", "For", "Reg", "Key", NATIVE_MESSAGING_HOST_ID].toString() 335 ); 336 Assert.equal(key, `${binFile.parent.path}\\${NATIVE_MESSAGING_HOST_ID}.json`); 337 });