summaryrefslogtreecommitdiffstats
path: root/security/sandbox/test/browser_bug1393259.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/test/browser_bug1393259.js')
-rw-r--r--security/sandbox/test/browser_bug1393259.js200
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);
+ }
+ );
+});