summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/fonts/test
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/inspector/fonts/test/OstrichLicense.txt41
-rw-r--r--devtools/client/inspector/fonts/test/browser.ini32
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector.js94
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_all-fonts.js82
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_copy-URL.js27
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_edit-previews.js70
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_editor-font-size-conversion.js85
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_editor-keywords.js44
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_editor-letter-spacing-conversion.js71
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_editor-values.js36
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js34
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_font-type-telemetry.js20
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_input-element-used-font.js22
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_no-fonts.js25
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_reveal-in-page.js100
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_text-node.js35
-rw-r--r--devtools/client/inspector/fonts/test/browser_fontinspector_theme-change.js66
-rw-r--r--devtools/client/inspector/fonts/test/doc_browser_fontinspector.html67
-rw-r--r--devtools/client/inspector/fonts/test/doc_browser_fontinspector_iframe.html5
-rw-r--r--devtools/client/inspector/fonts/test/head.js277
-rw-r--r--devtools/client/inspector/fonts/test/ostrich-black.ttfbin0 -> 12872 bytes
-rw-r--r--devtools/client/inspector/fonts/test/ostrich-regular.ttfbin0 -> 12476 bytes
-rw-r--r--devtools/client/inspector/fonts/test/test_iframe.html11
23 files changed, 1244 insertions, 0 deletions
diff --git a/devtools/client/inspector/fonts/test/OstrichLicense.txt b/devtools/client/inspector/fonts/test/OstrichLicense.txt
new file mode 100644
index 0000000000..14c043d601
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/OstrichLicense.txt
@@ -0,0 +1,41 @@
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
+
+5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file
diff --git a/devtools/client/inspector/fonts/test/browser.ini b/devtools/client/inspector/fonts/test/browser.ini
new file mode 100644
index 0000000000..346c0a699a
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser.ini
@@ -0,0 +1,32 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+ doc_browser_fontinspector.html
+ doc_browser_fontinspector_iframe.html
+ test_iframe.html
+ ostrich-black.ttf
+ ostrich-regular.ttf
+ head.js
+ !/devtools/client/inspector/test/head.js
+ !/devtools/client/inspector/test/shared-head.js
+ !/devtools/client/shared/test/shared-head.js
+ !/devtools/client/shared/test/telemetry-test-helpers.js
+ !/devtools/client/shared/test/highlighter-test-actor.js
+
+[browser_fontinspector.js]
+[browser_fontinspector_copy-URL.js]
+[browser_fontinspector_all-fonts.js]
+[browser_fontinspector_edit-previews.js]
+[browser_fontinspector_editor-font-size-conversion.js]
+[browser_fontinspector_editor-keywords.js]
+[browser_fontinspector_editor-letter-spacing-conversion.js]
+[browser_fontinspector_editor-values.js]
+[browser_fontinspector_expand-css-code.js]
+[browser_fontinspector_font-type-telemetry.js]
+[browser_fontinspector_input-element-used-font.js]
+[browser_fontinspector_no-fonts.js]
+[browser_fontinspector_reveal-in-page.js]
+skip-if = http3 # Bug 1829298
+[browser_fontinspector_text-node.js]
+[browser_fontinspector_theme-change.js]
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector.js b/devtools/client/inspector/fonts/test/browser_fontinspector.js
new file mode 100644
index 0000000000..98c4fc6b9f
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+requestLongerTimeout(2);
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { inspector, view } = await openFontInspectorForURL(TEST_URI);
+ ok(!!view, "Font inspector document is alive.");
+
+ const viewDoc = view.document;
+
+ await testBodyFonts(inspector, viewDoc);
+ await testDivFonts(inspector, viewDoc);
+});
+
+async function testBodyFonts(inspector, viewDoc) {
+ const FONTS = [
+ {
+ familyName: "bar",
+ name: ["Ostrich Sans Medium", "Ostrich Sans Black"],
+ },
+ {
+ familyName: "barnormal",
+ name: "Ostrich Sans Medium",
+ },
+ {
+ // On Linux, Arial does not exist. Liberation Sans is used instead.
+ familyName: ["Arial", "Liberation Sans"],
+ name: ["Arial", "Liberation Sans"],
+ },
+ ];
+
+ await selectNode("body", inspector);
+
+ const groups = getUsedFontGroupsEls(viewDoc);
+ is(groups.length, 3, "Found 3 font families used on BODY");
+
+ for (let i = 0; i < FONTS.length; i++) {
+ const groupEL = groups[i];
+ const font = FONTS[i];
+
+ const familyName = getFamilyName(groupEL);
+ ok(
+ font.familyName.includes(familyName),
+ `Font families used on BODY include: ${familyName}`
+ );
+
+ const fontName = getName(groupEL);
+ ok(font.name.includes(fontName), `Fonts used on BODY include: ${fontName}`);
+ }
+}
+
+async function testDivFonts(inspector, viewDoc) {
+ const FONTS = [
+ {
+ selector: "div",
+ familyName: "bar",
+ name: "Ostrich Sans Medium",
+ },
+ {
+ selector: ".normal-text",
+ familyName: "barnormal",
+ name: "Ostrich Sans Medium",
+ },
+ {
+ selector: ".bold-text",
+ familyName: "bar",
+ name: "Ostrich Sans Black",
+ },
+ {
+ selector: ".black-text",
+ familyName: "bar",
+ name: "Ostrich Sans Black",
+ },
+ ];
+
+ for (let i = 0; i < FONTS.length; i++) {
+ await selectNode(FONTS[i].selector, inspector);
+ const groups = getUsedFontGroupsEls(viewDoc);
+ const groupEl = groups[0];
+ const font = FONTS[i];
+
+ is(groups.length, 1, `Found 1 font on ${FONTS[i].selector}`);
+ is(getName(groupEl), font.name, "The DIV font has the right name");
+ is(
+ getFamilyName(groupEl),
+ font.familyName,
+ `font has the right family name`
+ );
+ }
+}
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_all-fonts.js b/devtools/client/inspector/fonts/test/browser_fontinspector_all-fonts.js
new file mode 100644
index 0000000000..7f2acfaebb
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_all-fonts.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Check that the font editor has a section for "All fonts" which shows all fonts
+// used on the page.
+
+const TEST_URI = URL_ROOT_SSL + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { view } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+
+ const allFontsAccordion = getFontsAccordion(viewDoc);
+ ok(allFontsAccordion, "There's an accordion in the panel");
+ is(
+ allFontsAccordion.textContent,
+ "All Fonts on Page",
+ "It has the right title"
+ );
+
+ await expandAccordion(allFontsAccordion);
+ const allFontsEls = getAllFontsEls(viewDoc);
+
+ const FONTS = [
+ {
+ familyName: ["bar"],
+ name: ["Ostrich Sans Medium"],
+ remote: true,
+ url: URL_ROOT_SSL + "ostrich-regular.ttf",
+ },
+ {
+ familyName: ["bar"],
+ name: ["Ostrich Sans Black"],
+ remote: true,
+ url: URL_ROOT_SSL + "ostrich-black.ttf",
+ },
+ {
+ familyName: ["bar"],
+ name: ["Ostrich Sans Black"],
+ remote: true,
+ url: URL_ROOT_SSL + "ostrich-black.ttf",
+ },
+ {
+ familyName: ["barnormal"],
+ name: ["Ostrich Sans Medium"],
+ remote: true,
+ url: URL_ROOT_SSL + "ostrich-regular.ttf",
+ },
+ {
+ // On Linux, Arial does not exist. Liberation Sans is used instead.
+ familyName: ["Arial", "Liberation Sans"],
+ name: ["Arial", "Liberation Sans"],
+ remote: false,
+ url: "system",
+ },
+ {
+ // On Linux, Times New Roman does not exist. Liberation Serif is used instead.
+ familyName: ["Times New Roman", "Liberation Serif"],
+ name: ["Times New Roman", "Liberation Serif"],
+ remote: false,
+ url: "system",
+ },
+ ];
+
+ is(allFontsEls.length, FONTS.length, "All fonts used are listed");
+
+ for (let i = 0; i < FONTS.length; i++) {
+ const li = allFontsEls[i];
+ const font = FONTS[i];
+
+ ok(font.name.includes(getName(li)), "The DIV font has the right name");
+ info(getName(li));
+ ok(
+ font.familyName.includes(getFamilyName(li)),
+ `font has the right family name`
+ );
+ info(getFamilyName(li));
+ is(isRemote(li), font.remote, `font remote value correct`);
+ is(getURL(li), font.url, `font url correct`);
+ }
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_copy-URL.js b/devtools/client/inspector/fonts/test/browser_fontinspector_copy-URL.js
new file mode 100644
index 0000000000..e871fb42f3
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_copy-URL.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that an icon appears next to web font URLs, and that clicking it copies the URL
+// to the clipboard thanks to it.
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { view, inspector } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+ await selectNode("div", inspector);
+ await expandFontsAccordion(viewDoc);
+ const allFontsEls = getAllFontsEls(viewDoc);
+ const fontEl = allFontsEls[0];
+
+ const linkEl = fontEl.querySelector(".font-origin");
+ const iconEl = linkEl.querySelector(".copy-icon");
+
+ ok(iconEl, "The icon is displayed");
+ is(iconEl.getAttribute("title"), "Copy URL", "This is the right icon");
+
+ info("Clicking the button and waiting for the clipboard to receive the URL");
+ await waitForClipboardPromise(() => iconEl.click(), linkEl.textContent);
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_edit-previews.js b/devtools/client/inspector/fonts/test/browser_fontinspector_edit-previews.js
new file mode 100644
index 0000000000..b29be4ca3f
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_edit-previews.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that previews change when the preview text changes. It doesn't check the
+// exact preview images because they are drawn on a canvas causing them to vary
+// between systems, platforms and software versions.
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { view, inspector } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+ await selectNode("div", inspector);
+ await expandFontsAccordion(viewDoc);
+
+ const previews = viewDoc.querySelectorAll("#font-container .font-preview");
+ const initialPreviews = [...previews].map(p => p.src);
+
+ info("Typing 'Abc' to check that the reference previews are correct.");
+ await updatePreviewText(view, "Abc");
+ checkPreviewImages(viewDoc, initialPreviews, true);
+
+ info("Typing something else to the preview box.");
+ await updatePreviewText(view, "The quick brown");
+ checkPreviewImages(viewDoc, initialPreviews, false);
+
+ info("Blanking the input to restore default previews.");
+ await updatePreviewText(view, "");
+ checkPreviewImages(viewDoc, initialPreviews, true);
+});
+
+/**
+ * Compares the previous preview image URIs to the current URIs.
+ *
+ * @param {Document} viewDoc
+ * The FontInspector document.
+ * @param {Array[String]} originalURIs
+ * An array of URIs to compare with the current URIs.
+ * @param {Boolean} assertIdentical
+ * If true, this method asserts that the previous and current URIs are
+ * identical. If false, this method asserts that the previous and current
+ * URI's are different.
+ */
+function checkPreviewImages(viewDoc, originalURIs, assertIdentical) {
+ const previews = viewDoc.querySelectorAll("#font-container .font-preview");
+ const newURIs = [...previews].map(p => p.src);
+
+ is(
+ newURIs.length,
+ originalURIs.length,
+ "The number of previews has not changed."
+ );
+
+ for (let i = 0; i < newURIs.length; ++i) {
+ if (assertIdentical) {
+ is(
+ newURIs[i],
+ originalURIs[i],
+ `The preview image at index ${i} has stayed the same.`
+ );
+ } else {
+ isnot(
+ newURIs[i],
+ originalURIs[i],
+ `The preview image at index ${i} has changed.`
+ );
+ }
+ }
+}
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_editor-font-size-conversion.js b/devtools/client/inspector/fonts/test/browser_fontinspector_editor-font-size-conversion.js
new file mode 100644
index 0000000000..f12769ff01
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_editor-font-size-conversion.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Unit test for math behind conversion of units for font-size. A reference element is
+// needed for converting to and from relative units (rem, em, %). A controlled viewport
+// is needed (iframe) for converting to and from viewport units (vh, vw, vmax, vmin).
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector_iframe.html";
+
+add_task(async function () {
+ const { inspector, view } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+ const property = "font-size";
+ const selector = ".viewport-size";
+ const UNITS = {
+ px: 50,
+ vw: 10,
+ vh: 20,
+ vmin: 20,
+ vmax: 10,
+ em: 1.389,
+ rem: 3.125,
+ "%": 138.889,
+ pt: 37.5,
+ pc: 3.125,
+ mm: 13.229,
+ cm: 1.323,
+ in: 0.521,
+ };
+
+ await selectNodeInFrames(["#frame", selector], inspector);
+
+ info("Check that font editor shows font-size value in original units");
+ const fontSize = getPropertyValue(viewDoc, property);
+ is(fontSize.unit, "vw", "Original unit for font size is vw");
+ is(fontSize.value + fontSize.unit, "10vw", "Original font size is 10vw");
+
+ // Starting value and unit for conversion.
+ let prevValue = fontSize.value;
+ let prevUnit = fontSize.unit;
+
+ for (const unit in UNITS) {
+ const value = UNITS[unit];
+
+ info(`Convert font-size from ${prevValue}${prevUnit} to ${unit}`);
+ const convertedValue = await view.convertUnits(
+ property,
+ prevValue,
+ prevUnit,
+ unit
+ );
+ is(
+ parseFloat(convertedValue),
+ parseFloat(value),
+ `Converting to ${unit} returns transformed value.`
+ );
+
+ // Store current unit and value to use in conversion on the next iteration.
+ prevUnit = unit;
+ prevValue = value;
+ }
+
+ info(`Check that conversion from fake unit returns 1-to-1 mapping.`);
+ const valueFromFakeUnit = await view.convertUnits(property, 1, "fake", "px");
+ is(valueFromFakeUnit, 1, `Converting from fake unit returns same value.`);
+
+ info(`Check that conversion to fake unit returns 1-to-1 mapping`);
+ const valueToFakeUnit = await view.convertUnits(property, 1, "px", "fake");
+ is(valueToFakeUnit, 1, `Converting to fake unit returns same value.`);
+
+ info(`Check that conversion between fake units returns 1-to-1 mapping.`);
+ const valueBetweenFakeUnit = await view.convertUnits(
+ property,
+ 1,
+ "bogus",
+ "fake"
+ );
+ is(
+ valueBetweenFakeUnit,
+ 1,
+ `Converting between fake units returns same value.`
+ );
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_editor-keywords.js b/devtools/client/inspector/fonts/test/browser_fontinspector_editor-keywords.js
new file mode 100644
index 0000000000..328a9c9bcd
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_editor-keywords.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* global getPropertyValue */
+
+"use strict";
+
+// Test that keyword values for font properties don't show up in the font editor,
+// but their computed style values show up instead.
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { inspector, view } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+
+ await testKeywordValues(inspector, viewDoc);
+});
+
+async function testKeywordValues(inspector, viewDoc) {
+ await selectNode(".bold-text", inspector);
+
+ info(
+ "Check font-weight shows its computed style instead of the bold keyword value."
+ );
+ const fontWeight = getPropertyValue(viewDoc, "font-weight");
+ isnot(fontWeight.value, "bold", "Font weight is not shown as keyword");
+ is(
+ parseInt(fontWeight.value, 10),
+ 700,
+ "Font weight is shown as computed style"
+ );
+
+ info(
+ "Check font-size shows its computed style instead of the inherit keyword value."
+ );
+ const fontSize = getPropertyValue(viewDoc, "font-size");
+ isnot(fontSize.unit, "inherit", "Font size unit is not shown as keyword");
+ is(fontSize.unit, "px", "Font size unit is shown as computed style");
+ is(
+ fontSize.value + fontSize.unit,
+ "36px",
+ "Font size is read as computed style"
+ );
+}
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_editor-letter-spacing-conversion.js b/devtools/client/inspector/fonts/test/browser_fontinspector_editor-letter-spacing-conversion.js
new file mode 100644
index 0000000000..ff680717a9
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_editor-letter-spacing-conversion.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* global getPropertyValue */
+
+"use strict";
+
+// Unit test for math behind conversion of units for letter-spacing.
+
+const TEST_URI = `
+ <style type='text/css'>
+ body {
+ /* Set root font-size to equivalent of 32px (2*16px) */
+ font-size: 200%;
+ }
+ div {
+ letter-spacing: 1em;
+ }
+ </style>
+ <div>LETTER SPACING</div>
+`;
+
+add_task(async function () {
+ const URI = "data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI);
+ const { inspector, view } = await openFontInspectorForURL(URI);
+ const viewDoc = view.document;
+ const property = "letter-spacing";
+ const UNITS = {
+ px: 32,
+ rem: 2,
+ em: 1,
+ };
+
+ await selectNode("div", inspector);
+
+ info("Check that font editor shows letter-spacing value in original units");
+ const letterSpacing = getPropertyValue(viewDoc, property);
+ is(
+ letterSpacing.value + letterSpacing.unit,
+ "1em",
+ "Original letter spacing is 1em"
+ );
+
+ // Starting value and unit for conversion.
+ let prevValue = letterSpacing.value;
+ let prevUnit = letterSpacing.unit;
+
+ for (const unit in UNITS) {
+ const value = UNITS[unit];
+
+ info(`Convert letter-spacing from ${prevValue}${prevUnit} to ${unit}`);
+ const convertedValue = await view.convertUnits(
+ property,
+ prevValue,
+ prevUnit,
+ unit
+ );
+ is(
+ convertedValue,
+ value,
+ `Converting to ${unit} returns transformed value.`
+ );
+
+ // Store current unit and value to use in conversion on the next iteration.
+ prevUnit = unit;
+ prevValue = value;
+ }
+
+ info(`Check that conversion to fake unit returns 1-to-1 mapping`);
+ const valueToFakeUnit = await view.convertUnits(property, 1, "px", "fake");
+ is(valueToFakeUnit, 1, `Converting to fake unit returns same value.`);
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_editor-values.js b/devtools/client/inspector/fonts/test/browser_fontinspector_editor-values.js
new file mode 100644
index 0000000000..dd9879eff4
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_editor-values.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { inspector, view } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+
+ await testDiv(inspector, viewDoc);
+ await testNestedSpan(inspector, viewDoc);
+});
+
+async function testDiv(inspector, viewDoc) {
+ await selectNode("DIV", inspector);
+ const { value, unit } = getPropertyValue(viewDoc, "font-size");
+
+ is(value + unit, "1em", "DIV should be have font-size of 1em");
+}
+
+async function testNestedSpan(inspector, viewDoc) {
+ await selectNode(".nested-span", inspector);
+ const { value, unit } = getPropertyValue(viewDoc, "font-size");
+
+ isnot(
+ value + unit,
+ "1em",
+ "Nested span should not reflect parent's font size."
+ );
+ is(
+ value + unit,
+ "36px",
+ "Nested span should have computed font-size of 36px"
+ );
+}
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js b/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
new file mode 100644
index 0000000000..66aedf93e7
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the font-face css rule code is collapsed by default, and can be expanded.
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { view, inspector } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+ await selectNode("div", inspector);
+
+ await expandFontsAccordion(viewDoc);
+ info("Checking that the css font-face rule is collapsed by default");
+ const fontEl = getAllFontsEls(viewDoc)[0];
+ const codeEl = fontEl.querySelector(".font-css-code");
+ is(codeEl.textContent, "@font-face {}", "The font-face rule is collapsed");
+
+ info("Expanding the rule by clicking on the expander icon");
+ const onExpanded = BrowserTestUtils.waitForCondition(() => {
+ return (
+ codeEl.textContent ===
+ `@font-face { font-family: bar; src: url("bad/font/name.ttf"), url("ostrich-regular.ttf") format("truetype"); }`
+ );
+ }, "Waiting for the font-face rule 1");
+
+ const expander = fontEl.querySelector(".font-css-code .theme-twisty");
+ expander.click();
+ await onExpanded;
+
+ ok(true, "Font-face rule is now expanded");
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_font-type-telemetry.js b/devtools/client/inspector/fonts/test/browser_fontinspector_font-type-telemetry.js
new file mode 100644
index 0000000000..3c3e402437
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_font-type-telemetry.js
@@ -0,0 +1,20 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that telemetry works for tracking the font type shown in the Font Editor.
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { inspector } = await openFontInspectorForURL(TEST_URI);
+ startTelemetry();
+ await selectNode(".normal-text", inspector);
+ await selectNode(".bold-text", inspector);
+ checkTelemetry(
+ "DEVTOOLS_FONTEDITOR_FONT_TYPE_DISPLAYED",
+ "",
+ null,
+ "hasentries"
+ );
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_input-element-used-font.js b/devtools/client/inspector/fonts/test/browser_fontinspector_input-element-used-font.js
new file mode 100644
index 0000000000..313162d525
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_input-element-used-font.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+// Verify that a styled input field element is showing proper font information
+// in its font tab.
+// Non-regression test for https://bugzilla.mozilla.org/show_bug.cgi?id=1435469
+add_task(async function () {
+ const { inspector, view } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+
+ await selectNode(".input-field", inspector);
+
+ const fontEls = getUsedFontsEls(viewDoc);
+ ok(fontEls.length == 1, `Used fonts found for styled input element`);
+ ok(
+ fontEls[0].textContent == "Ostrich Sans Medium",
+ `Proper font found: 'Ostrich Sans Medium' for styled input.`
+ );
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_no-fonts.js b/devtools/client/inspector/fonts/test/browser_fontinspector_no-fonts.js
new file mode 100644
index 0000000000..454cabdf92
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_no-fonts.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Check that the warning message for no fonts found shows up when selecting a node
+// that does not have any used fonts.
+// Ensure that no used fonts are listed.
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { view, inspector } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+ await selectNode(".empty", inspector);
+
+ info("Test the warning message for no fonts found on empty element");
+ const warning = viewDoc.querySelector(
+ "#font-editor .devtools-sidepanel-no-result"
+ );
+ ok(warning, "The warning for no fonts found is shown for the empty element");
+
+ info("Test that no fonts are listed for the empty element");
+ const fontsEls = getUsedFontsEls(viewDoc);
+ is(fontsEls.length, 0, "There are no used fonts listed");
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_reveal-in-page.js b/devtools/client/inspector/fonts/test/browser_fontinspector_reveal-in-page.js
new file mode 100644
index 0000000000..bcefe0b3bf
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_reveal-in-page.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that fonts usage can be revealed in the page using the FontsHighlighter.
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ // Make sure the toolbox is tall enough to accomodate all fonts, otherwise mouseover
+ // events simulation will fail.
+ await pushPref("devtools.toolbox.footer.height", 500);
+
+ const { view } = await openFontInspectorForURL(TEST_URI);
+ await testFontHighlighting(view);
+
+ info("Check that highlighting still works after reloading the page");
+ await reloadBrowser();
+ await testFontHighlighting(view);
+});
+
+async function testFontHighlighting(view) {
+ // The number of window selection change events we expect to get as we hover over each
+ // font in the list. Waiting for those events is how we know that text-runs were
+ // highlighted in the page.
+ // The reason why these numbers vary is because the highlighter may create more than
+ // 1 selection range object, depending on the number of text-runs found.
+ const expectedSelectionChangeEvents = [2, 2, 2, 1, 1];
+
+ const viewDoc = view.document;
+
+ // Wait for the view to have all the expected used fonts.
+ const fontEls = await waitFor(() => {
+ const els = getUsedFontsEls(viewDoc);
+ if (els.length !== expectedSelectionChangeEvents.length) {
+ return false;
+ }
+
+ return els;
+ });
+
+ for (let i = 0; i < fontEls.length; i++) {
+ info(
+ `Mousing over and out of font number ${i} ("${fontEls[i].textContent}") in the list`
+ );
+
+ // Simulating a mouse over event on the font name and expecting a selectionchange.
+ const nameEl = fontEls[i];
+ let onEvents = waitForNSelectionEvents(expectedSelectionChangeEvents[i]);
+ EventUtils.synthesizeMouse(
+ nameEl,
+ 2,
+ 2,
+ { type: "mouseover" },
+ viewDoc.defaultView
+ );
+ await onEvents;
+
+ ok(
+ true,
+ `${expectedSelectionChangeEvents[i]} selectionchange events detected on mouseover`
+ );
+
+ // Simulating a mouse out event on the font name and expecting a selectionchange.
+ const otherEl = viewDoc.querySelector("body");
+ onEvents = waitForNSelectionEvents(1);
+ EventUtils.synthesizeMouse(
+ otherEl,
+ 2,
+ 2,
+ { type: "mouseover" },
+ viewDoc.defaultView
+ );
+ await onEvents;
+
+ ok(true, "1 selectionchange events detected on mouseout");
+ }
+}
+
+async function waitForNSelectionEvents(numberOfTimes) {
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [numberOfTimes],
+ async function (n) {
+ const win = content.wrappedJSObject;
+
+ await new Promise(resolve => {
+ let received = 0;
+ win.document.addEventListener("selectionchange", function listen() {
+ received++;
+
+ if (received === n) {
+ win.document.removeEventListener("selectionchange", listen);
+ resolve();
+ }
+ });
+ });
+ }
+ );
+}
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_text-node.js b/devtools/client/inspector/fonts/test/browser_fontinspector_text-node.js
new file mode 100644
index 0000000000..b5e58b9745
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_text-node.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that selecting a text node invokes the font editor on its parent node.
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+
+add_task(async function () {
+ const { inspector, view } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+
+ info("Select the first text node of <body>");
+ const bodyNode = await getNodeFront("body", inspector);
+ const { nodes } = await inspector.walker.children(bodyNode);
+ const onInspectorUpdated = inspector.once("fontinspector-updated");
+ info("Select the text node");
+ await selectNode(nodes[0], inspector);
+
+ info("Waiting for font editor to render");
+ await onInspectorUpdated;
+
+ const textFonts = getUsedFontsEls(viewDoc);
+
+ info("Select the <body> element");
+ await selectNode("body", inspector);
+
+ const parentFonts = getUsedFontsEls(viewDoc);
+ is(
+ textFonts.length,
+ parentFonts.length,
+ "Font inspector shows same number of fonts"
+ );
+});
diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_theme-change.js b/devtools/client/inspector/fonts/test/browser_fontinspector_theme-change.js
new file mode 100644
index 0000000000..b3c91a727d
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_theme-change.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+requestLongerTimeout(2);
+
+// Test that the preview images are updated when the theme changes.
+
+const {
+ getTheme,
+ setTheme,
+} = require("resource://devtools/client/shared/theme.js");
+
+const TEST_URI = URL_ROOT + "doc_browser_fontinspector.html";
+const originalTheme = getTheme();
+
+registerCleanupFunction(() => {
+ info(`Restoring theme to '${originalTheme}.`);
+ setTheme(originalTheme);
+});
+
+add_task(async function () {
+ const { inspector, view } = await openFontInspectorForURL(TEST_URI);
+ const viewDoc = view.document;
+
+ await selectNode(".normal-text", inspector);
+ await expandFontsAccordion(viewDoc);
+ const allFontsEls = getAllFontsEls(viewDoc);
+ const fontEl = allFontsEls[0];
+
+ // Store the original preview URI for later comparison.
+ const originalURI = fontEl.querySelector(".font-preview").src;
+ const newTheme = originalTheme === "light" ? "dark" : "light";
+
+ info(`Original theme was '${originalTheme}'.`);
+
+ await setThemeAndWaitForUpdate(newTheme, inspector);
+ isnot(
+ fontEl.querySelector(".font-preview").src,
+ originalURI,
+ "The preview image changed with the theme."
+ );
+
+ await setThemeAndWaitForUpdate(originalTheme, inspector);
+ is(
+ fontEl.querySelector(".font-preview").src,
+ originalURI,
+ "The preview image is correct after the original theme was restored."
+ );
+});
+
+/**
+ * Sets the current theme and waits for fontinspector-updated event.
+ *
+ * @param {String} theme - the new theme
+ * @param {Object} inspector - the inspector panel
+ */
+async function setThemeAndWaitForUpdate(theme, inspector) {
+ const onUpdated = inspector.once("fontinspector-updated");
+
+ info(`Setting theme to '${theme}'.`);
+ setTheme(theme);
+
+ info("Waiting for font-inspector to update.");
+ await onUpdated;
+}
diff --git a/devtools/client/inspector/fonts/test/doc_browser_fontinspector.html b/devtools/client/inspector/fonts/test/doc_browser_fontinspector.html
new file mode 100644
index 0000000000..27e24e2fd0
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/doc_browser_fontinspector.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+
+<style>
+ @font-face {
+ font-family: bar;
+ src: url(bad/font/name.ttf), url(ostrich-regular.ttf) format("truetype");
+ }
+ @font-face {
+ font-family: barnormal;
+ font-weight: normal;
+ src: url(ostrich-regular.ttf);
+ }
+ @font-face {
+ font-family: bar;
+ font-weight: bold;
+ src: url(ostrich-black.ttf);
+ }
+ @font-face {
+ font-family: bar;
+ font-weight: 800;
+ src: url(ostrich-black.ttf);
+ }
+ body{
+ /* Arial doesn't exist on Linux. Liberation Sans is the default sans-serif there. */
+ font-family:Arial, "Liberation Sans";
+ font-size: 36px;
+ }
+ div {
+ font-size: 1em;
+ font-family:bar, "Missing Family", sans-serif;
+ }
+ .normal-text {
+ font-family: barnormal;
+ font-weight: normal;
+ }
+ .bold-text {
+ font-family: bar;
+ font-weight: bold;
+ font-size: inherit;
+ }
+ .black-text {
+ font-family: bar;
+ font-weight: 800;
+ }
+ .viewport-size {
+ font-size: 10vw;
+ }
+ .input-field {
+ font-family: bar;
+ font-size: 36px;
+ color: blue;
+ }
+</style>
+
+<body>
+ BODY
+ <div>DIV
+ <span class="nested-span">NESTED SPAN</span>
+ </div>
+ <iframe src="test_iframe.html"></iframe>
+ <div class="normal-text">NORMAL DIV</div>
+ <div class="bold-text">BOLD DIV</div>
+ <div class="black-text">800 DIV</div>
+ <div class="empty"></div>
+ <div class="viewport-size">VIEWPORT SIZE</div>
+ <input class="input-field" value="Input text value"/>
+</body>
diff --git a/devtools/client/inspector/fonts/test/doc_browser_fontinspector_iframe.html b/devtools/client/inspector/fonts/test/doc_browser_fontinspector_iframe.html
new file mode 100644
index 0000000000..a7ef3385e7
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/doc_browser_fontinspector_iframe.html
@@ -0,0 +1,5 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<iframe id="frame" src="doc_browser_fontinspector.html" width="500" height="250"></iframe>
diff --git a/devtools/client/inspector/fonts/test/head.js b/devtools/client/inspector/fonts/test/head.js
new file mode 100644
index 0000000000..058029f13a
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/head.js
@@ -0,0 +1,277 @@
+/* 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/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
+"use strict";
+
+// Import the inspector's head.js first (which itself imports shared-head.js).
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
+ this
+);
+
+Services.prefs.setCharPref("devtools.inspector.activeSidebar", "fontinspector");
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
+ Services.prefs.clearUserPref("devtools.inspector.selectedSidebar");
+});
+
+var nodeConstants = require("resource://devtools/shared/dom-node-constants.js");
+
+/**
+ * The font-inspector doesn't participate in the inspector's update mechanism
+ * (i.e. it doesn't call inspector.updating() when updating), so simply calling
+ * the default selectNode isn't enough to guaranty that the panel has finished
+ * updating. We also need to wait for the fontinspector-updated event.
+ *
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector
+ * The instance of InspectorPanel currently loaded in the toolbox.
+ * @param {String} reason
+ * Defaults to "test" which instructs the inspector not to highlight the
+ * node upon selection.
+ */
+var _selectNode = selectNode;
+selectNode = async function (node, inspector, reason) {
+ // Ensure node is a NodeFront and not a selector (which is also accepted as
+ // an argument to selectNode).
+ node = await getNodeFront(node, inspector);
+
+ // The FontInspector will fallback to the parent node when a text node is
+ // selected.
+ const isTextNode = node.nodeType == nodeConstants.TEXT_NODE;
+ const expectedNode = isTextNode ? node.parentNode() : node;
+
+ const onEditorUpdated = inspector.once("fonteditor-updated");
+ const onFontInspectorUpdated = new Promise(resolve => {
+ inspector.on("fontinspector-updated", function onUpdated(eventNode) {
+ if (eventNode === expectedNode) {
+ inspector.off("fontinspector-updated", onUpdated);
+ resolve();
+ }
+ });
+ });
+ await _selectNode(node, inspector, reason);
+
+ // Wait for both the font inspector and font editor before proceeding.
+ await Promise.all([onFontInspectorUpdated, onEditorUpdated]);
+};
+
+/**
+ * Adds a new tab with the given URL, opens the inspector and selects the
+ * font-inspector tab.
+ * @return {Promise} resolves to a {tab, toolbox, inspector, view} object
+ */
+var openFontInspectorForURL = async function (url) {
+ const tab = await addTab(url);
+ const { toolbox, inspector } = await openInspector();
+
+ // Call selectNode again here to force a fontinspector update since we don't
+ // know if the fontinspector-updated event has been sent while the inspector
+ // was being opened or not.
+ await selectNode("body", inspector);
+
+ return {
+ tab,
+ toolbox,
+ inspector,
+ view: inspector.getPanel("fontinspector"),
+ };
+};
+
+/**
+ * Focus the preview input, clear it, type new text into it and wait for the
+ * preview images to be updated.
+ *
+ * @param {FontInspector} view - The FontInspector instance.
+ * @param {String} text - The text to preview.
+ */
+async function updatePreviewText(view, text) {
+ info(`Changing the preview text to '${text}'`);
+
+ const doc = view.document;
+ const input = doc.querySelector("#font-preview-input-container input");
+ input.focus();
+
+ info("Blanking the input field.");
+ while (input.value.length) {
+ const update = view.inspector.once("fontinspector-updated");
+ EventUtils.sendKey("BACK_SPACE", doc.defaultView);
+ await update;
+ }
+
+ if (text) {
+ info(`Typing "${text}" into the input field.`);
+ const update = view.inspector.once("fontinspector-updated");
+ EventUtils.sendString(text, doc.defaultView);
+ await update;
+ }
+
+ is(input.value, text, `The input now contains "${text}".`);
+}
+
+/**
+ * Get all of the <li> elements for the fonts used on the currently selected element.
+ *
+ * NOTE: This method is used by tests which check the old Font Inspector. It, along with
+ * the tests should be removed once the Font Editor reaches Firefox Stable.
+ * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1485324
+ *
+ * @param {Document} viewDoc
+ * @return {NodeList}
+ */
+function getUsedFontsEls_obsolete(viewDoc) {
+ return viewDoc.querySelectorAll("#font-editor .fonts-list li");
+}
+
+/**
+ * Get all of the elements with names of fonts used on the currently selected element.
+ *
+ * @param {Document} viewDoc
+ * @return {NodeList}
+ */
+function getUsedFontsEls(viewDoc) {
+ return viewDoc.querySelectorAll(
+ "#font-editor .font-control-used-fonts .font-name"
+ );
+}
+
+/**
+ * Get all of the elements with groups of fonts used on the currently selected element.
+ *
+ * @param {Document} viewDoc
+ * @return {NodeList}
+ */
+function getUsedFontGroupsEls(viewDoc) {
+ return viewDoc.querySelectorAll(
+ "#font-editor .font-control-used-fonts .font-group"
+ );
+}
+
+/**
+ * Get the DOM element for the accordion widget that contains the fonts used elsewhere in
+ * the document.
+ *
+ * @param {Document} viewDoc
+ * @return {DOMNode}
+ */
+function getFontsAccordion(viewDoc) {
+ return viewDoc.querySelector("#font-container .accordion");
+}
+
+/**
+ * Expand a given accordion widget.
+ *
+ * @param {DOMNode} accordion
+ */
+async function expandAccordion(accordion) {
+ const isExpanded = () => accordion.querySelector(".fonts-list");
+ if (isExpanded()) {
+ return;
+ }
+
+ const onExpanded = BrowserTestUtils.waitForCondition(
+ isExpanded,
+ "Waiting for other fonts section"
+ );
+ accordion.querySelector(".theme-twisty").click();
+ await onExpanded;
+}
+
+/**
+ * Expand the fonts accordion.
+ *
+ * @param {Document} viewDoc
+ */
+async function expandFontsAccordion(viewDoc) {
+ info("Expanding the other fonts section");
+ await expandAccordion(getFontsAccordion(viewDoc));
+}
+
+/**
+ * Get all of the <li> elements for the fonts used elsewhere in the document.
+ *
+ * @param {Document} viewDoc
+ * @return {NodeList}
+ */
+function getAllFontsEls(viewDoc) {
+ return getFontsAccordion(viewDoc).querySelectorAll(".fonts-list > li");
+}
+
+/**
+ * Given a font element, return its name.
+ *
+ * @param {DOMNode} fontEl
+ * The font element.
+ * @return {String}
+ * The name of the font as shown in the UI.
+ */
+function getName(fontEl) {
+ return fontEl.querySelector(".font-name").textContent;
+}
+
+/**
+ * Given a font element, return the font's URL.
+ *
+ * @param {DOMNode} fontEl
+ * The font element.
+ * @return {String}
+ * The URL where the font was loaded from as shown in the UI.
+ */
+function getURL(fontEl) {
+ return fontEl.querySelector(".font-origin").textContent;
+}
+
+/**
+ * Given a font element, return its family name.
+ *
+ * @param {DOMNode} fontEl
+ * The font element.
+ * @return {String}
+ * The name of the font family as shown in the UI.
+ */
+function getFamilyName(fontEl) {
+ return fontEl.querySelector(".font-family-name").textContent;
+}
+
+/**
+ * Get the value and unit of a CSS font property or font axis from the font editor.
+ *
+ * @param {Document} viewDoc
+ * Host document of the font inspector panel.
+ * @param {String} name
+ * Font property name or axis tag
+ * @return {Object}
+ * Object with the value and unit of the given font property or axis tag
+ * from the corresponding input fron the font editor.
+ * @Example:
+ * {
+ * value: {String|null}
+ * unit: {String|null}
+ * }
+ */
+function getPropertyValue(viewDoc, name) {
+ const selector = `#font-editor .font-value-input[name=${name}]`;
+ return {
+ // Ensure value input exists before querying its value
+ value:
+ viewDoc.querySelector(selector) &&
+ parseFloat(viewDoc.querySelector(selector).value),
+ // Ensure unit dropdown exists before querying its value
+ unit:
+ viewDoc.querySelector(selector + ` ~ .font-value-select`) &&
+ viewDoc.querySelector(selector + ` ~ .font-value-select`).value,
+ };
+}
+
+/**
+ * Given a font element, check whether its font source is remote.
+ *
+ * @param {DOMNode} fontEl
+ * The font element.
+ * @return {Boolean}
+ */
+function isRemote(fontEl) {
+ return fontEl.querySelector(".font-origin").classList.contains("remote");
+}
diff --git a/devtools/client/inspector/fonts/test/ostrich-black.ttf b/devtools/client/inspector/fonts/test/ostrich-black.ttf
new file mode 100644
index 0000000000..a0ef8fe1c9
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/ostrich-black.ttf
Binary files differ
diff --git a/devtools/client/inspector/fonts/test/ostrich-regular.ttf b/devtools/client/inspector/fonts/test/ostrich-regular.ttf
new file mode 100644
index 0000000000..9682c07350
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/ostrich-regular.ttf
Binary files differ
diff --git a/devtools/client/inspector/fonts/test/test_iframe.html b/devtools/client/inspector/fonts/test/test_iframe.html
new file mode 100644
index 0000000000..29393a9e9a
--- /dev/null
+++ b/devtools/client/inspector/fonts/test/test_iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+
+<style>
+ div{
+ font-family: "Times New Roman";
+ }
+</style>
+
+<body>
+ <div>Hello world</div>
+</body>