diff options
Diffstat (limited to 'security/sandbox/test/browser_bug1393259.js')
-rw-r--r-- | security/sandbox/test/browser_bug1393259.js | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/security/sandbox/test/browser_bug1393259.js b/security/sandbox/test/browser_bug1393259.js new file mode 100644 index 0000000000..58ee9ca06f --- /dev/null +++ b/security/sandbox/test/browser_bug1393259.js @@ -0,0 +1,200 @@ +/* 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/. */ +"use strict"; + +/* + * This test validates that an OTF font installed in a directory not + * accessible to content processes is rendered correctly by checking that + * content displayed never uses the OS fallback font "LastResort". When + * a content process renders a page with the fallback font, that is an + * indication the content process failed to read or load the computed font. + * The test uses a version of the Fira Sans font and depends on the font + * not being already installed and enabled. + */ + +const kPageURL = + "http://example.com/browser/security/sandbox/test/bug1393259.html"; + +// Parameters for running the python script that registers/unregisters fonts. +const kPythonPath = "/usr/bin/python"; +const kFontInstallerPath = "browser/security/sandbox/test/mac_register_font.py"; +const kUninstallFlag = "-u"; +const kVerboseFlag = "-v"; + +// Where to find the font in the test environment. +const kRepoFontPath = "browser/security/sandbox/test/FiraSans-Regular.otf"; + +// Font name strings to check for. +const kLastResortFontName = "LastResort"; +const kTestFontName = "Fira Sans"; + +// Home-relative path to install a private font. Where a private font is +// a font at a location not readable by content processes. +const kPrivateFontSubPath = "/FiraSans-Regular.otf"; + +add_task(async function () { + await new Promise(resolve => waitForFocus(resolve, window)); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: kPageURL, + }, + async function (aBrowser) { + function runProcess(aCmd, aArgs, blocking = true) { + let cmdFile = Cc["@mozilla.org/file/local;1"].createInstance( + Ci.nsIFile + ); + cmdFile.initWithPath(aCmd); + + let process = Cc["@mozilla.org/process/util;1"].createInstance( + Ci.nsIProcess + ); + process.init(cmdFile); + process.run(blocking, aArgs, aArgs.length); + return process.exitValue; + } + + // Register the font at path |fontPath| and wait + // for the browser to detect the change. + async function registerFont(fontPath) { + let fontRegistered = getFontNotificationPromise(); + let exitCode = runProcess(kPythonPath, [ + kFontInstallerPath, + kVerboseFlag, + fontPath, + ]); + Assert.ok(exitCode == 0, "registering font" + fontPath); + if (exitCode == 0) { + // Wait for the font registration to be detected by the browser. + await fontRegistered; + } + } + + // Unregister the font at path |fontPath|. If |waitForUnreg| is true, + // don't wait for the browser to detect the change and don't use + // the verbose arg for the unregister command. + async function unregisterFont(fontPath, waitForUnreg = true) { + let args = [kFontInstallerPath, kUninstallFlag]; + let fontUnregistered; + + if (waitForUnreg) { + args.push(kVerboseFlag); + fontUnregistered = getFontNotificationPromise(); + } + + let exitCode = runProcess(kPythonPath, args.concat(fontPath)); + if (waitForUnreg) { + Assert.ok(exitCode == 0, "unregistering font" + fontPath); + if (exitCode == 0) { + await fontUnregistered; + } + } + } + + // Returns a promise that resolves when font info is changed. + let getFontNotificationPromise = () => + new Promise(resolve => { + const kTopic = "font-info-updated"; + function observe() { + Services.obs.removeObserver(observe, kTopic); + resolve(); + } + + Services.obs.addObserver(observe, kTopic); + }); + + let homeDir = Services.dirsvc.get("Home", Ci.nsIFile); + let privateFontPath = homeDir.path + kPrivateFontSubPath; + + registerCleanupFunction(function () { + unregisterFont(privateFontPath, /* waitForUnreg = */ false); + runProcess("/bin/rm", [privateFontPath], /* blocking = */ false); + }); + + // Copy the font file to the private path. + runProcess("/bin/cp", [kRepoFontPath, privateFontPath]); + + // Cleanup previous aborted tests. + unregisterFont(privateFontPath, /* waitForUnreg = */ false); + + // Get the original width, using the fallback monospaced font + let origWidth = await SpecialPowers.spawn( + aBrowser, + [], + async function () { + let window = content.window.wrappedJSObject; + let contentDiv = window.document.getElementById("content"); + return contentDiv.offsetWidth; + } + ); + + // Activate the font we want to test at a non-standard path. + await registerFont(privateFontPath); + + // Assign the new font to the content. + await SpecialPowers.spawn(aBrowser, [], async function () { + let window = content.window.wrappedJSObject; + let contentDiv = window.document.getElementById("content"); + contentDiv.style.fontFamily = "'Fira Sans', monospace"; + }); + + // Wait until the width has changed, indicating the content process + // has recognized the newly-activated font. + while (true) { + let width = await SpecialPowers.spawn(aBrowser, [], async function () { + let window = content.window.wrappedJSObject; + let contentDiv = window.document.getElementById("content"); + return contentDiv.offsetWidth; + }); + if (width != origWidth) { + break; + } + // If the content wasn't ready yet, wait a little before re-checking. + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(c => setTimeout(c, 100)); + } + + // Get a list of fonts now being used to display the web content. + let fontList = await SpecialPowers.spawn(aBrowser, [], async function () { + let window = content.window.wrappedJSObject; + let range = window.document.createRange(); + let contentDiv = window.document.getElementById("content"); + range.selectNode(contentDiv); + let fonts = InspectorUtils.getUsedFontFaces(range); + + let fontList = []; + for (let i = 0; i < fonts.length; i++) { + fontList.push({ name: fonts[i].name }); + } + return fontList; + }); + + let lastResortFontUsed = false; + let testFontUsed = false; + + for (let font of fontList) { + // Did we fall back to the "LastResort" font? + if (!lastResortFontUsed && font.name.includes(kLastResortFontName)) { + lastResortFontUsed = true; + continue; + } + // Did we render using our test font as expected? + if (!testFontUsed && font.name.includes(kTestFontName)) { + testFontUsed = true; + continue; + } + } + + Assert.ok( + !lastResortFontUsed, + `The ${kLastResortFontName} fallback font was not used` + ); + + Assert.ok(testFontUsed, `The test font "${kTestFontName}" was used`); + + await unregisterFont(privateFontPath); + } + ); +}); |