Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
2025-08-14 08:55:16 +08:00
38 changed files with 85 additions and 1993 deletions
-5
View File
@@ -37,8 +37,6 @@ const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup")
const HTML_NS = "http://www.w3.org/1999/xhtml";
const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
const PREF_ENABLE_MDN_DOCS_TOOLTIP =
"devtools.inspector.mdnDocsTooltip.enabled";
const FILTER_CHANGED_TIMEOUT = 150;
// This is used to parse user input when filtering.
@@ -157,11 +155,8 @@ function CssRuleView(inspector, document, store, pageStyle) {
this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
this._prefObserver.on(PREF_UA_STYLES, this._handlePrefChange);
this._prefObserver.on(PREF_DEFAULT_COLOR_UNIT, this._handlePrefChange);
this._prefObserver.on(PREF_ENABLE_MDN_DOCS_TOOLTIP, this._handlePrefChange);
this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES);
this.enableMdnDocsTooltip =
Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP);
// The popup will be attached to the toolbox document.
this.popup = new AutocompletePopup(inspector._toolbox.doc, {
@@ -85,9 +85,6 @@ support-files =
[browser_rules_content_01.js]
[browser_rules_content_02.js]
skip-if = e10s && debug # Bug 1250058 - Docshell leak on debug e10s
[browser_rules_context-menu-show-mdn-docs-01.js]
[browser_rules_context-menu-show-mdn-docs-02.js]
[browser_rules_context-menu-show-mdn-docs-03.js]
[browser_rules_copy_styles.js]
subsuite = clipboard
[browser_rules_cssom.js]
@@ -1,138 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the code that integrates the Style Inspector's rule view
* with the MDN docs tooltip.
*
* If you display the context click on a property name in the rule view, you
* should see a menu item "Show MDN Docs". If you click that item, the MDN
* docs tooltip should be shown, containing docs from MDN for that property.
*
* This file tests that the context menu item is shown when it should be
* shown and hidden when it should be hidden.
*/
"use strict";
/**
* The test document tries to confuse the context menu
* code by having a tag called "padding" and a property
* value called "margin".
*/
const { PrefObserver } = require("devtools/client/shared/prefs");
const PREF_ENABLE_MDN_DOCS_TOOLTIP =
"devtools.inspector.mdnDocsTooltip.enabled";
const TEST_URI = `
<html>
<head>
<style>
padding {font-family: margin;}
</style>
</head>
<body>
<padding>MDN tooltip testing</padding>
</body>
</html>
`;
add_task(function* () {
info("Ensure the pref is true to begin with");
let initial = Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP);
if (initial != true) {
yield setBooleanPref(PREF_ENABLE_MDN_DOCS_TOOLTIP, true);
}
yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("padding", inspector);
yield testMdnContextMenuItemVisibility(view);
info("Ensure the pref is reset to its initial value");
let eventual = Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP);
if (eventual != initial) {
yield setBooleanPref(PREF_ENABLE_MDN_DOCS_TOOLTIP, initial);
}
});
/**
* Set a boolean pref, and wait for the pref observer to
* trigger, so that code listening for the pref change
* has had a chance to update itself.
*
* @param pref {string} Name of the pref to change
* @param state {boolean} Desired value of the pref.
*
* Note that if the pref already has the value in `state`,
* then the prefObserver will not trigger. So you should only
* call this function if you know the pref's current value is
* not `state`.
*/
function* setBooleanPref(pref, state) {
let oncePrefChanged = defer();
let prefObserver = new PrefObserver("devtools.");
prefObserver.on(pref, oncePrefChanged.resolve);
info("Set the pref " + pref + " to: " + state);
Services.prefs.setBoolPref(pref, state);
info("Wait for prefObserver to call back so the UI can update");
yield oncePrefChanged.promise;
prefObserver.off(pref, oncePrefChanged.resolve);
}
/**
* Tests that the MDN context menu item is shown when it should be,
* and hidden when it should be.
* - iterate through every node in the rule view
* - set that node as popupNode (the node that the context menu
* is shown for)
* - update the context menu's state
* - test that the MDN context menu item is hidden, or not,
* depending on popupNode
*/
function* testMdnContextMenuItemVisibility(view) {
info("Test that MDN context menu item is shown only when it should be.");
let root = rootElement(view);
for (let node of iterateNodes(root)) {
info("Setting " + node + " as popupNode");
info("Creating context menu with " + node + " as popupNode");
let allMenuItems = openStyleContextMenuAndGetAllItems(view, node);
let menuitemShowMdnDocs = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.showMdnDocs"));
let isVisible = menuitemShowMdnDocs.visible;
let shouldBeVisible = isPropertyNameNode(node);
let message = shouldBeVisible ? "shown" : "hidden";
is(isVisible, shouldBeVisible,
"The MDN context menu item is " + message + " ; content : " +
node.textContent + " ; type : " + node.nodeType);
}
}
/**
* Check if a node is a property name.
*/
function isPropertyNameNode(node) {
return node.textContent === "font-family";
}
/**
* A generator that iterates recursively through all child nodes of baseNode.
*/
function* iterateNodes(baseNode) {
yield baseNode;
for (let child of baseNode.childNodes) {
yield* iterateNodes(child);
}
}
/**
* Returns the root element for the rule view.
*/
var rootElement = view => (view.element) ? view.element : view.styleDocument;
@@ -1,60 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the code that integrates the Style Inspector's rule view
* with the MDN docs tooltip.
*
* If you display the context click on a property name in the rule view, you
* should see a menu item "Show MDN Docs". If you click that item, the MDN
* docs tooltip should be shown, containing docs from MDN for that property.
*
* This file tests that:
* - clicking the context menu item shows the tooltip
* - the tooltip content matches the property name for which the context menu was opened
*/
"use strict";
const {setBaseCssDocsUrl} =
require("devtools/client/shared/widgets/MdnDocsWidget");
const PROPERTYNAME = "color";
const TEST_DOC = `
<html>
<body>
<div style="color: red">
Test "Show MDN Docs" context menu option
</div>
</body>
</html>
`;
add_task(function* () {
yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_DOC));
let {inspector, view} = yield openRuleView();
yield selectNode("div", inspector);
setBaseCssDocsUrl(URL_ROOT);
info("Setting the popupNode for the MDN docs tooltip");
let {nameSpan} = getRuleViewProperty(view, "element", PROPERTYNAME);
let allMenuItems = openStyleContextMenuAndGetAllItems(view, nameSpan.firstChild);
let menuitemShowMdnDocs = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.showMdnDocs"));
let cssDocs = view.tooltips.cssDocs;
info("Showing the MDN docs tooltip");
let onShown = cssDocs.tooltip.once("shown");
menuitemShowMdnDocs.click();
yield onShown;
ok(true, "The MDN docs tooltip was shown");
info("Quick check that the tooltip contents are set");
let h1 = cssDocs.tooltip.container.querySelector(".mdn-property-name");
is(h1.textContent, PROPERTYNAME, "The MDN docs tooltip h1 is correct");
});
@@ -1,117 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the "devtools.inspector.mdnDocsTooltip.enabled" preference,
* that we use to enable/disable the MDN tooltip in the Inspector.
*
* The desired behavior is:
* - if the preference is true, show the "Show MDN Docs" context menu item
* - if the preference is false, don't show the item
* - listen for changes to the pref, so we can show/hide the item dynamically
*/
"use strict";
const { PrefObserver } = require("devtools/client/styleeditor/utils");
const PREF_ENABLE_MDN_DOCS_TOOLTIP =
"devtools.inspector.mdnDocsTooltip.enabled";
const PROPERTY_NAME_CLASS = "ruleview-propertyname";
const TEST_DOC = `
<html>
<body>
<div style="color: red">
Test the pref to enable/disable the "Show MDN Docs" context menu option
</div>
</body>
</html>
`;
add_task(function* () {
info("Ensure the pref is true to begin with");
let initial = Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP);
if (initial != true) {
yield setBooleanPref(PREF_ENABLE_MDN_DOCS_TOOLTIP, true);
}
yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_DOC));
let {inspector, view} = yield openRuleView();
yield selectNode("div", inspector);
yield testMdnContextMenuItemVisibility(view, true);
yield setBooleanPref(PREF_ENABLE_MDN_DOCS_TOOLTIP, false);
yield testMdnContextMenuItemVisibility(view, false);
info("Close the Inspector");
let target = TargetFactory.forTab(gBrowser.selectedTab);
yield gDevTools.closeToolbox(target);
({inspector, view} = yield openRuleView());
yield selectNode("div", inspector);
yield testMdnContextMenuItemVisibility(view, false);
yield setBooleanPref(PREF_ENABLE_MDN_DOCS_TOOLTIP, true);
yield testMdnContextMenuItemVisibility(view, true);
info("Ensure the pref is reset to its initial value");
let eventual = Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP);
if (eventual != initial) {
yield setBooleanPref(PREF_ENABLE_MDN_DOCS_TOOLTIP, initial);
}
});
/**
* Set a boolean pref, and wait for the pref observer to
* trigger, so that code listening for the pref change
* has had a chance to update itself.
*
* @param pref {string} Name of the pref to change
* @param state {boolean} Desired value of the pref.
*
* Note that if the pref already has the value in `state`,
* then the prefObserver will not trigger. So you should only
* call this function if you know the pref's current value is
* not `state`.
*/
function* setBooleanPref(pref, state) {
let oncePrefChanged = defer();
let prefObserver = new PrefObserver("devtools.");
prefObserver.on(pref, oncePrefChanged.resolve);
info("Set the pref " + pref + " to: " + state);
Services.prefs.setBoolPref(pref, state);
info("Wait for prefObserver to call back so the UI can update");
yield oncePrefChanged.promise;
prefObserver.off(pref, oncePrefChanged.resolve);
}
/**
* Test whether the MDN tooltip context menu item is visible when it should be.
*
* @param view The rule view
* @param shouldBeVisible {boolean} Whether we expect the context
* menu item to be visible or not.
*/
function* testMdnContextMenuItemVisibility(view, shouldBeVisible) {
let message = shouldBeVisible ? "shown" : "hidden";
info("Test that MDN context menu item is " + message);
info("Set a CSS property name as popupNode");
let root = rootElement(view);
let node = root.querySelector("." + PROPERTY_NAME_CLASS).firstChild;
let allMenuItems = openStyleContextMenuAndGetAllItems(view, node);
let menuitemShowMdnDocs = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.showMdnDocs"));
let isVisible = menuitemShowMdnDocs.visible;
is(isVisible, shouldBeVisible,
"The MDN context menu item is " + message);
}
/**
* Returns the root element for the rule view.
*/
var rootElement = view => (view.element) ? view.element : view.styleDocument;
@@ -25,9 +25,6 @@ const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.prope
const {LocalizationHelper} = require("devtools/shared/l10n");
const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
const PREF_ENABLE_MDN_DOCS_TOOLTIP =
"devtools.inspector.mdnDocsTooltip.enabled";
/**
* Style inspector context menu
*
@@ -56,7 +53,6 @@ function StyleInspectorMenu(view, options) {
this._onCopySelector = this._onCopySelector.bind(this);
this._onCopyUrl = this._onCopyUrl.bind(this);
this._onSelectAll = this._onSelectAll.bind(this);
this._onShowMdnDocs = this._onShowMdnDocs.bind(this);
this._onToggleOrigSources = this._onToggleOrigSources.bind(this);
}
@@ -229,19 +225,6 @@ StyleInspectorMenu.prototype = {
});
menu.append(menuitemAddRule);
// Show MDN Docs
let mdnDocsAccessKey = "styleinspector.contextmenu.showMdnDocs.accessKey";
let menuitemShowMdnDocs = new MenuItem({
label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.showMdnDocs"),
accesskey: STYLE_INSPECTOR_L10N.getStr(mdnDocsAccessKey),
click: () => {
this._onShowMdnDocs();
},
visible: (Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP) &&
this._isPropertyName()),
});
menu.append(menuitemShowMdnDocs);
// Show Original Sources
let sourcesAccessKey = "styleinspector.contextmenu.toggleOrigSources.accessKey";
let menuitemSources = new MenuItem({
@@ -407,16 +390,6 @@ StyleInspectorMenu.prototype = {
clipboardHelper.copyString(message);
}),
/**
* Show docs from MDN for a CSS property.
*/
_onShowMdnDocs: function () {
let cssPropertyName = this.styleDocument.popupNode.textContent;
let anchor = this.styleDocument.popupNode.parentNode;
let cssDocsTooltip = this.view.tooltips.cssDocs;
cssDocsTooltip.show(anchor, cssPropertyName);
},
/**
* Add a new rule to the current element.
*/
@@ -18,7 +18,6 @@ const {
} = require("devtools/client/inspector/shared/node-types");
const { getColor } = require("devtools/client/shared/theme");
const { getCssProperties } = require("devtools/shared/fronts/css-properties");
const CssDocsTooltip = require("devtools/client/shared/widgets/tooltip/CssDocsTooltip");
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const {
getImageDimensions,
@@ -82,9 +81,6 @@ TooltipsOverlay.prototype = {
this.previewTooltip.startTogglingOnHover(this.view.element,
this._onPreviewTooltipTargetHover.bind(this));
// MDN CSS help tooltip
this.cssDocs = new CssDocsTooltip(toolbox.doc);
if (this.isRuleView) {
// Color picker tooltip
this.colorPicker = new SwatchColorPickerTooltip(toolbox.doc,
@@ -120,10 +116,6 @@ TooltipsOverlay.prototype = {
this.cubicBezier.destroy();
}
if (this.cssDocs) {
this.cssDocs.destroy();
}
if (this.filterEditor) {
this.filterEditor.destroy();
}
@@ -191,10 +183,6 @@ TooltipsOverlay.prototype = {
this.cubicBezier.hide();
}
if (this.isRuleView && this.cssDocs.tooltip.isVisible()) {
this.cssDocs.hide();
}
if (this.isRuleView && this.filterEditor.tooltip.isVisible()) {
this.filterEditor.revert();
this.filterEdtior.hide();
@@ -295,10 +283,6 @@ TooltipsOverlay.prototype = {
this.cubicBezier.hide();
}
if (this.cssDocs) {
this.cssDocs.hide();
}
if (this.filterEditor) {
this.filterEditor.hide();
}
-1
View File
@@ -117,7 +117,6 @@ devtools.jar:
content/framework/connect/connect.js (framework/connect/connect.js)
content/shared/widgets/graphs-frame.xhtml (shared/widgets/graphs-frame.xhtml)
content/shared/widgets/cubic-bezier.css (shared/widgets/cubic-bezier.css)
content/shared/widgets/mdn-docs.css (shared/widgets/mdn-docs.css)
content/shared/widgets/filter-widget.css (shared/widgets/filter-widget.css)
content/shared/widgets/spectrum.css (shared/widgets/spectrum.css)
content/aboutdebugging/aboutdebugging.xhtml (aboutdebugging/aboutdebugging.xhtml)
@@ -48,14 +48,6 @@ eyedropper.disabled.title=Unavailable in non-HTML documents
#LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded
eventsTooltip.openInDebugger=Open in Debugger
# LOCALIZATION NOTE (docsTooltip.visitMDN): Shown in the tooltip that displays
# help from MDN. This is a link to the complete MDN documentation page.
docsTooltip.visitMDN=Visit MDN page
# LOCALIZATION NOTE (docsTooltip.visitMDN): Shown in the docs tooltip when the MDN page
# could not be loaded (for example, because of a connectivity problem).
docsTooltip.loadDocsError=Could not load docs page.
# LOCALIZATION NOTE (inspector.collapsePane): This is the tooltip for the button
# that collapses the right panel (rules, computed, box-model, etc...) in the
# inspector UI.
-2
View File
@@ -50,8 +50,6 @@ pref("devtools.inspector.imagePreviewTooltipSize", 300);
pref("devtools.inspector.showUserAgentStyles", false);
// Show all native anonymous content (like controls in <video> tags)
pref("devtools.inspector.showAllAnonymousContent", false);
// Enable the MDN docs tooltip
pref("devtools.inspector.mdnDocsTooltip.enabled", false);
// Enable the Font Inspector
pref("devtools.fontinspector.enabled", true);
-8
View File
@@ -13,11 +13,6 @@ support-files =
helper_color_data.js
helper_html_tooltip.js
helper_inplace_editor.js
html-mdn-css-basic-testing.html
html-mdn-css-no-summary.html
html-mdn-css-no-summary-or-syntax.html
html-mdn-css-no-syntax.html
html-mdn-css-syntax-old-style.html
leakhunt.js
test-actor.js
test-actor-registry.js
@@ -140,9 +135,6 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
skip-if = e10s # Layouthelpers test should not run in a content page.
[browser_layoutHelpers-getBoxQuads.js]
skip-if = e10s # Layouthelpers test should not run in a content page.
[browser_mdn-docs-01.js]
[browser_mdn-docs-02.js]
[browser_mdn-docs-03.js]
[browser_num-l10n.js]
[browser_options-view-01.js]
[browser_outputparser.js]
@@ -1,167 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the MdnDocsWidget object, and specifically its
* loadCssDocs() function.
*
* The MdnDocsWidget is initialized with a document which has a specific
* structure. You then call loadCssDocs(), passing in a CSS property name.
* MdnDocsWidget then fetches docs for that property by making an XHR to
* a docs page, and loads the results into the document. While the XHR is
* still not resolved the document is put into an "initializing" state in
* which the devtools throbber is displayed.
*
* In this file we test:
* - the initial state of the document before the docs have loaded
* - the state of the document after the docs have loaded
*/
"use strict";
const {setBaseCssDocsUrl, MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
/**
* Test properties
*
* In the real tooltip, a CSS property name is used to look up an MDN page
* for that property.
* In the test code, the names defined here is used to look up a page
* served by the test server.
*/
const BASIC_TESTING_PROPERTY = "html-mdn-css-basic-testing.html";
const BASIC_EXPECTED_SUMMARY = "A summary of the property.";
const BASIC_EXPECTED_SYNTAX = [{type: "comment", text: "/* The part we want */"},
{type: "text", text: "\n"},
{type: "property-name", text: "this"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "is-the-part-we-want"},
{type: "text", text: ";"}];
const URI_PARAMS =
"?utm_source=mozilla&utm_medium=firefox-inspector&utm_campaign=default";
add_task(function* () {
setBaseCssDocsUrl(TEST_URI_ROOT);
yield addTab("about:blank");
let [host, win] = yield createHost("bottom", "data:text/html," +
"<div class='mdn-container'></div>");
let widget = new MdnDocsWidget(win.document.querySelector("div"));
yield testTheBasics(widget);
host.destroy();
gBrowser.removeCurrentTab();
});
/**
* Test all the basics
* - initial content, before docs have loaded, is as expected
* - throbber is set before docs have loaded
* - contents are as expected after docs have loaded
* - throbber is gone after docs have loaded
* - mdn link text is correct and onclick behavior is correct
*/
function* testTheBasics(widget) {
info("Test all the basic functionality in the widget");
info("Get the widget state before docs have loaded");
let promise = widget.loadCssDocs(BASIC_TESTING_PROPERTY);
info("Check initial contents before docs have loaded");
checkTooltipContents(widget.elements, {
propertyName: BASIC_TESTING_PROPERTY,
summary: "",
syntax: ""
});
// throbber is set
ok(widget.elements.info.classList.contains("devtools-throbber"),
"Throbber is set");
info("Now let the widget finish loading");
yield promise;
info("Check contents after docs have loaded");
checkTooltipContents(widget.elements, {
propertyName: BASIC_TESTING_PROPERTY,
summary: BASIC_EXPECTED_SUMMARY,
syntax: BASIC_EXPECTED_SYNTAX
});
// throbber is gone
ok(!widget.elements.info.classList.contains("devtools-throbber"),
"Throbber is not set");
info("Check that MDN link text is correct and onclick behavior is correct");
let mdnLink = widget.elements.linkToMdn;
let expectedHref = TEST_URI_ROOT + BASIC_TESTING_PROPERTY + URI_PARAMS;
is(mdnLink.href, expectedHref, "MDN link href is correct");
let uri = yield checkLinkClick(mdnLink);
is(uri, expectedHref, "New tab opened with the expected URI");
}
/**
* Clicking the "Visit MDN Page" in the tooltip panel
* should open a new browser tab with the page loaded.
*
* To test this we'll listen for a new tab opening, and
* when it does, add a listener to that new tab to tell
* us when it has loaded.
*
* Then we click the link.
*
* In the tab's load listener, we'll resolve the promise
* with the URI, which is expected to match the href
* in the orginal link.
*
* One complexity is that when you open a new tab,
* "about:blank" is first loaded into the tab before the
* actual page. So we ignore that first load event, and keep
* listening until "load" is triggered for a different URI.
*/
function checkLinkClick(link) {
function loadListener(tab) {
let browser = getBrowser().getBrowserForTab(tab);
let uri = browser.currentURI.spec;
info("New browser tab has loaded");
gBrowser.removeTab(tab);
info("Resolve promise with new tab URI");
deferred.resolve(uri);
}
function newTabListener(e) {
gBrowser.tabContainer.removeEventListener("TabOpen", newTabListener);
let tab = e.target;
BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, url => url != "about:blank")
.then(url => loadListener(tab));
}
let deferred = defer();
info("Check that clicking the link opens a new tab with the correct URI");
gBrowser.tabContainer.addEventListener("TabOpen", newTabListener, false);
info("Click the link to MDN");
link.click();
return deferred.promise;
}
/**
* Utility function to check content of the tooltip.
*/
function checkTooltipContents(doc, expected) {
is(doc.heading.textContent,
expected.propertyName,
"Property name is correct");
is(doc.summary.textContent,
expected.summary,
"Summary is correct");
checkCssSyntaxHighlighterOutput(expected.syntax, doc.syntax);
}
@@ -1,127 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the MdnDocsWidget object, and specifically its
* loadCssDocs() function.
*
* The MdnDocsWidget is initialized with a document which has a specific
* structure. You then call loadCssDocs(), passing in a CSS property name.
* MdnDocsWidget then fetches docs for that property by making an XHR to
* a docs page, and loads the results into the document.
*
* In this file we test that the tooltip can properly handle the different
* structures that the docs page might have, including variant structures and
* error conditions like parts of the document being missing.
*
* We also test that the tooltip properly handles the case where the page
* doesn't exist at all.
*/
"use strict";
const {
setBaseCssDocsUrl,
MdnDocsWidget
} = require("devtools/client/shared/widgets/MdnDocsWidget");
const BASIC_EXPECTED_SUMMARY = "A summary of the property.";
const BASIC_EXPECTED_SYNTAX = [{type: "comment", text: "/* The part we want */"},
{type: "text", text: "\n"},
{type: "property-name", text: "this"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "is-the-part-we-want"},
{type: "text", text: ";"}];
const ERROR_MESSAGE = "Could not load docs page.";
/**
* Test properties
*
* In the real tooltip, a CSS property name is used to look up an MDN page
* for that property.
* In the test code, the names defined here are used to look up a page
* served by the test server. We have different properties to test
* different ways that the docs pages might be constructed, including errors
* like pages that don't include docs where we expect.
*/
const SYNTAX_OLD_STYLE = "html-mdn-css-syntax-old-style.html";
const NO_SUMMARY = "html-mdn-css-no-summary.html";
const NO_SYNTAX = "html-mdn-css-no-syntax.html";
const NO_SUMMARY_OR_SYNTAX = "html-mdn-css-no-summary-or-syntax.html";
const TEST_DATA = [{
desc: "Test a property for which we don't have a page",
docsPageUrl: "i-dont-exist.html",
expectedContents: {
propertyName: "i-dont-exist.html",
summary: ERROR_MESSAGE,
syntax: []
}
}, {
desc: "Test a property whose syntax section is specified using an old-style page",
docsPageUrl: SYNTAX_OLD_STYLE,
expectedContents: {
propertyName: SYNTAX_OLD_STYLE,
summary: BASIC_EXPECTED_SUMMARY,
syntax: BASIC_EXPECTED_SYNTAX
}
}, {
desc: "Test a property whose page doesn't have a summary",
docsPageUrl: NO_SUMMARY,
expectedContents: {
propertyName: NO_SUMMARY,
summary: "",
syntax: BASIC_EXPECTED_SYNTAX
}
}, {
desc: "Test a property whose page doesn't have a syntax",
docsPageUrl: NO_SYNTAX,
expectedContents: {
propertyName: NO_SYNTAX,
summary: BASIC_EXPECTED_SUMMARY,
syntax: []
}
}, {
desc: "Test a property whose page doesn't have a summary or a syntax",
docsPageUrl: NO_SUMMARY_OR_SYNTAX,
expectedContents: {
propertyName: NO_SUMMARY_OR_SYNTAX,
summary: ERROR_MESSAGE,
syntax: []
}
}
];
add_task(function* () {
setBaseCssDocsUrl(TEST_URI_ROOT);
yield addTab("about:blank");
let [host, win] = yield createHost("bottom", "data:text/html," +
"<div class='mdn-container'></div>");
let widget = new MdnDocsWidget(win.document.querySelector("div"));
for (let {desc, docsPageUrl, expectedContents} of TEST_DATA) {
info(desc);
yield widget.loadCssDocs(docsPageUrl);
checkTooltipContents(widget.elements, expectedContents);
}
host.destroy();
gBrowser.removeCurrentTab();
});
/*
* Utility function to check content of the tooltip.
*/
function checkTooltipContents(doc, expected) {
is(doc.heading.textContent,
expected.propertyName,
"Property name is correct");
is(doc.summary.textContent,
expected.summary,
"Summary is correct");
checkCssSyntaxHighlighterOutput(expected.syntax, doc.syntax);
}
@@ -1,276 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the CSS syntax highlighter in the MdnDocsWidget object.
*
* The CSS syntax highlighter accepts:
* - a string containing CSS
* - a DOM node
*
* It parses the string and creates a collection of DOM nodes for different
* CSS token types. These DOM nodes have CSS classes applied to them,
* to apply the right style for that particular token type. The DOM nodes
* are returned as children of the node that was passed to the function.
*
* This test code defines a number of different strings containing valid and
* invalid CSS in various forms. For each string it defines the DOM nodes
* that it expects to get from the syntax highlighter.
*
* It then calls the syntax highlighter, and checks that the resulting
* collection of DOM nodes is what we expected.
*/
"use strict";
const {appendSyntaxHighlightedCSS} = require("devtools/client/shared/widgets/MdnDocsWidget");
/**
* An array containing the actual test cases.
*
* The test code tests every case in the array. If you want to add more
* test cases, just add more items to the array.
*
* Each test case consists of:
* - description: string describing the salient features of this test case
* - example: the string to test
* - expected: an array of objects, one for each DOM node we expect, that
* captures the information about the node that we expect to test.
*/
const TEST_DATA = [{
description: "Valid syntax, string value.",
example: "name: stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, numeric value.",
example: "name: 1;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, url value.",
example: "name: url(./name);",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "url(./name)"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, space before ':'.",
example: "name : stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: " "},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, space before ';'.",
example: "name: stringValue ;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: " "},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, trailing space.",
example: "name: stringValue; ",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"},
{type: "text", text: " "}
]}, {
description: "Valid syntax, leading space.",
example: " name: stringValue;",
expected: [{type: "text", text: " "},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, two spaces.",
example: "name: stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, no spaces.",
example: "name:stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, two-part value.",
example: "name: stringValue 1;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, two declarations.",
example: "name: stringValue;\n" +
"name: 1;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, commented, numeric value.",
example: "/* comment */\n" +
"name: 1;",
expected: [{type: "comment", text: "/* comment */"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, multiline commented, string value.",
example: "/* multiline \n" +
"comment */\n" +
"name: stringValue;",
expected: [{type: "comment", text: "/* multiline \ncomment */"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, commented, two declarations.",
example: "/* comment 1 */\n" +
"name: 1;\n" +
"/* comment 2 */\n" +
"name: stringValue;",
expected: [{type: "comment", text: "/* comment 1 */"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"},
{type: "text", text: "\n"},
{type: "comment", text: "/* comment 2 */"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, multiline.",
example: "name: \n" +
"stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " \n"},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, multiline, two declarations.",
example: "name: \n" +
"stringValue \n" +
"stringValue2;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " \n"},
{type: "property-value", text: "stringValue"},
{type: "text", text: " \n"},
{type: "property-value", text: "stringValue2"},
{type: "text", text: ";"}
]}, {
description: "Invalid: not CSS at all.",
example: "not CSS at all",
expected: [{type: "property-name", text: "not"},
{type: "text", text: " "},
{type: "property-name", text: "CSS"},
{type: "text", text: " "},
{type: "property-name", text: "at"},
{type: "text", text: " "},
{type: "property-name", text: "all"}
]}, {
description: "Invalid: switched ':' and ';'.",
example: "name; stringValue:",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ";"},
{type: "text", text: " "},
{type: "property-name", text: "stringValue"},
{type: "text", text: ":"}
]}, {
description: "Invalid: unterminated comment.",
example: "/* unterminated comment\n" +
"name: stringValue;",
expected: [{type: "comment", text: "/* unterminated comment\nname: stringValue;"}
]}, {
description: "Invalid: bad comment syntax.",
example: "// invalid comment\n" +
"name: stringValue;",
expected: [{type: "text", text: "/"},
{type: "text", text: "/"},
{type: "text", text: " "},
{type: "property-name", text: "invalid"},
{type: "text", text: " "},
{type: "property-name", text: "comment"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Invalid: no trailing ';'.",
example: "name: stringValue\n" +
"name: stringValue2",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: "\n"},
{type: "property-value", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue2"},
]}
];
/**
* Iterate through every test case, calling the syntax highlighter,
* then calling a helper function to check the output.
*/
add_task(function* () {
let doc = gBrowser.selectedTab.ownerDocument;
let parent = doc.createElement("div");
info("Testing all CSS syntax highlighter test cases");
for (let {description, example, expected} of TEST_DATA) {
info("Testing: " + description);
appendSyntaxHighlightedCSS(example, parent);
checkCssSyntaxHighlighterOutput(expected, parent);
while (parent.firstChild) {
parent.firstChild.remove();
}
}
});
-102
View File
@@ -242,105 +242,3 @@ Task.async(function* (widget, name, value) {
yield onRender;
});
/**
* Utility function for testing CSS code samples that have been
* syntax-highlighted.
*
* The CSS syntax highlighter emits a collection of DOM nodes that have
* CSS classes applied to them. This function checks that those nodes
* are what we expect.
*
* @param {array} expectedNodes
* A representation of the nodes we expect to see.
* Each node is an object containing two properties:
* - type: a string which can be one of:
* - text, comment, property-name, property-value
* - text: the textContent of the node
*
* For example, given a string like this:
* "<comment> The part we want </comment>\n this: is-the-part-we-want;"
*
* we would represent the expected output like this:
* [{type: "comment", text: "<comment> The part we want </comment>"},
* {type: "text", text: "\n"},
* {type: "property-name", text: "this"},
* {type: "text", text: ":"},
* {type: "text", text: " "},
* {type: "property-value", text: "is-the-part-we-want"},
* {type: "text", text: ";"}];
*
* @param {Node} parent
* The DOM node whose children are the output of the syntax highlighter.
*/
function checkCssSyntaxHighlighterOutput(expectedNodes, parent) {
/**
* The classes applied to the output nodes by the syntax highlighter.
* These must be same as the definitions in MdnDocsWidget.js.
*/
const PROPERTY_NAME_COLOR = "theme-fg-color5";
const PROPERTY_VALUE_COLOR = "theme-fg-color1";
const COMMENT_COLOR = "theme-comment";
/**
* Check the type and content of a single node.
*/
function checkNode(expected, actual) {
ok(actual.textContent == expected.text,
"Check that node has the expected textContent");
info("Expected text content: [" + expected.text + "]");
info("Actual text content: [" + actual.textContent + "]");
info("Check that node has the expected type");
if (expected.type == "text") {
ok(actual.nodeType == 3, "Check that node is a text node");
} else {
ok(actual.tagName.toUpperCase() == "SPAN", "Check that node is a SPAN");
}
info("Check that node has the expected className");
let expectedClassName = null;
let actualClassName = null;
switch (expected.type) {
case "property-name":
expectedClassName = PROPERTY_NAME_COLOR;
break;
case "property-value":
expectedClassName = PROPERTY_VALUE_COLOR;
break;
case "comment":
expectedClassName = COMMENT_COLOR;
break;
default:
ok(!actual.classList, "No className expected");
return;
}
ok(actual.classList.length == 1, "One className expected");
actualClassName = actual.classList[0];
ok(expectedClassName == actualClassName, "Check className value");
info("Expected className: " + expectedClassName);
info("Actual className: " + actualClassName);
}
info("Logging the actual nodes we have:");
for (let j = 0; j < parent.childNodes.length; j++) {
let n = parent.childNodes[j];
info(j + " / " +
"nodeType: " + n.nodeType + " / " +
"textContent: " + n.textContent);
}
ok(parent.childNodes.length == parent.childNodes.length,
"Check we have the expected number of nodes");
info("Expected node count " + expectedNodes.length);
info("Actual node count " + expectedNodes.length);
for (let i = 0; i < expectedNodes.length; i++) {
info("Check node " + i);
checkNode(expectedNodes[i], parent.childNodes[i]);
}
}
@@ -1,21 +0,0 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h2 id="Summary">Summary</h2>
<p>A summary of the property.</p>
<h2 id="Syntax">Syntax</h2>
<pre>/* The part we want */
this: is-the-part-we-want;</pre>
</body>
</html>
@@ -1,12 +0,0 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<p>This is not the summary or the syntax.</p>
</body>
</html>
@@ -1,21 +0,0 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<p>This is not the summary.</p>
<h2 id="Syntax">Syntax</h2>
<pre>To be ignored.</pre>
<pre>/* The part we want */
this: is-the-part-we-want;</pre>
</body>
</html>
@@ -1,17 +0,0 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h2 id="Summary">Summary</h2>
<p>A summary of the property.</p>
<p>This is not the syntax.</p>
</body>
</html>
@@ -1,23 +0,0 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h2 id="Summary">Summary</h2>
<p>A summary of the property.</p>
<h2 id="Syntax">Syntax</h2>
<pre>The part we should ignore</pre>
<pre>/* The part we want */
this: is-the-part-we-want;</pre>
</body>
</html>
@@ -1,510 +0,0 @@
/* 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/. */
/**
* This file contains functions to retrieve docs content from
* MDN (developer.mozilla.org) for particular items, and to display
* the content in a tooltip.
*
* At the moment it only supports fetching content for CSS properties,
* but it might support other types of content in the future
* (Web APIs, for example).
*
* It's split into two parts:
*
* - functions like getCssDocs that just fetch content from MDN,
* without any constraints on what to do with the content. If you
* want to embed the content in some custom way, use this.
*
* - the MdnDocsWidget class, that manages and updates a tooltip
* document whose content is taken from MDN. If you want to embed
* the content in a tooltip, use this in conjunction with Tooltip.js.
*/
"use strict";
const Services = require("Services");
const defer = require("devtools/shared/defer");
const {getCSSLexer} = require("devtools/shared/css/lexer");
const EventEmitter = require("devtools/shared/event-emitter");
const {gDevTools} = require("devtools/client/framework/devtools");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
// Parameters for the XHR request
// see https://developer.mozilla.org/en-US/docs/MDN/Kuma/API#Document_parameters
const XHR_PARAMS = "?raw&macros";
// URL for the XHR request
var XHR_CSS_URL = "https://developer.mozilla.org/en-US/docs/Web/CSS/";
// Parameters for the link to MDN in the tooltip, so
// so we know which MDN visits come from this feature
const PAGE_LINK_PARAMS =
"?utm_source=mozilla&utm_medium=firefox-inspector&utm_campaign=default";
// URL for the page link omits locale, so a locale-specific page will be loaded
var PAGE_LINK_URL = "https://developer.mozilla.org/docs/Web/CSS/";
exports.PAGE_LINK_URL = PAGE_LINK_URL;
const PROPERTY_NAME_COLOR = "theme-fg-color5";
const PROPERTY_VALUE_COLOR = "theme-fg-color1";
const COMMENT_COLOR = "theme-comment";
/**
* Turns a string containing a series of CSS declarations into
* a series of DOM nodes, with classes applied to provide syntax
* highlighting.
*
* It uses the CSS tokenizer to generate a stream of CSS tokens.
* https://dxr.mozilla.org/mozilla-central/source/dom/webidl/CSSLexer.webidl
* lists all the token types.
*
* - "whitespace", "comment", and "symbol" tokens are appended as TEXT nodes,
* and will inherit the default style for text.
*
* - "ident" tokens that we think are property names are considered to be
* a property name, and are appended as SPAN nodes with a distinct color class.
*
* - "ident" nodes which we do not think are property names, and nodes
* of all other types ("number", "url", "percentage", ...) are considered
* to be part of a property value, and are appended as SPAN nodes with
* a different color class.
*
* @param {Document} doc
* Used to create nodes.
*
* @param {String} syntaxText
* The CSS input. This is assumed to consist of a series of
* CSS declarations, with trailing semicolons.
*
* @param {DOM node} syntaxSection
* This is the parent for the output nodes. Generated nodes
* are appended to this as children.
*/
function appendSyntaxHighlightedCSS(cssText, parentElement) {
let doc = parentElement.ownerDocument;
let identClass = PROPERTY_NAME_COLOR;
let lexer = getCSSLexer(cssText);
/**
* Create a SPAN node with the given text content and class.
*/
function createStyledNode(textContent, className) {
let newNode = doc.createElementNS(XHTML_NS, "span");
newNode.classList.add(className);
newNode.textContent = textContent;
return newNode;
}
/**
* If the symbol is ":", we will expect the next
* "ident" token to be part of a property value.
*
* If the symbol is ";", we will expect the next
* "ident" token to be a property name.
*/
function updateIdentClass(tokenText) {
if (tokenText === ":") {
identClass = PROPERTY_VALUE_COLOR;
} else if (tokenText === ";") {
identClass = PROPERTY_NAME_COLOR;
}
}
/**
* Create the appropriate node for this token type.
*
* If this token is a symbol, also update our expectations
* for what the next "ident" token represents.
*/
function tokenToNode(token, tokenText) {
switch (token.tokenType) {
case "ident":
return createStyledNode(tokenText, identClass);
case "symbol":
updateIdentClass(tokenText);
return doc.createTextNode(tokenText);
case "whitespace":
return doc.createTextNode(tokenText);
case "comment":
return createStyledNode(tokenText, COMMENT_COLOR);
default:
return createStyledNode(tokenText, PROPERTY_VALUE_COLOR);
}
}
let token = lexer.nextToken();
while (token) {
let tokenText = cssText.slice(token.startOffset, token.endOffset);
let newNode = tokenToNode(token, tokenText);
parentElement.appendChild(newNode);
token = lexer.nextToken();
}
}
exports.appendSyntaxHighlightedCSS = appendSyntaxHighlightedCSS;
/**
* Fetch an MDN page.
*
* @param {string} pageUrl
* URL of the page to fetch.
*
* @return {promise}
* The promise is resolved with the page as an XML document.
*
* The promise is rejected with an error message if
* we could not load the page.
*/
function getMdnPage(pageUrl) {
let deferred = defer();
let xhr = new XMLHttpRequest();
xhr.addEventListener("load", onLoaded, false);
xhr.addEventListener("error", onError, false);
xhr.open("GET", pageUrl);
xhr.responseType = "document";
xhr.send();
function onLoaded(e) {
if (xhr.status != 200) {
deferred.reject({page: pageUrl, status: xhr.status});
} else {
deferred.resolve(xhr.responseXML);
}
}
function onError(e) {
deferred.reject({page: pageUrl, status: xhr.status});
}
return deferred.promise;
}
/**
* Gets some docs for the given CSS property.
* Loads an MDN page for the property and gets some
* information about the property.
*
* @param {string} cssProperty
* The property for which we want docs.
*
* @return {promise}
* The promise is resolved with an object containing:
* - summary: a short summary of the property
* - syntax: some example syntax
*
* The promise is rejected with an error message if
* we could not load the page.
*/
function getCssDocs(cssProperty) {
let deferred = defer();
let pageUrl = XHR_CSS_URL + cssProperty + XHR_PARAMS;
getMdnPage(pageUrl).then(parseDocsFromResponse, handleRejection);
function parseDocsFromResponse(responseDocument) {
let theDocs = {};
theDocs.summary = getSummary(responseDocument);
theDocs.syntax = getSyntax(responseDocument);
if (theDocs.summary || theDocs.syntax) {
deferred.resolve(theDocs);
} else {
deferred.reject("Couldn't find the docs in the page.");
}
}
function handleRejection(e) {
deferred.reject(e.status);
}
return deferred.promise;
}
exports.getCssDocs = getCssDocs;
/**
* The MdnDocsWidget is used by tooltip code that needs to display docs
* from MDN in a tooltip.
*
* In the constructor, the widget does some general setup that's not
* dependent on the particular item we need docs for.
*
* After that, when the tooltip code needs to display docs for an item, it
* asks the widget to retrieve the docs and update the document with them.
*
* @param {Element} tooltipContainer
* A DOM element where the MdnDocs widget markup should be created.
*/
function MdnDocsWidget(tooltipContainer) {
EventEmitter.decorate(this);
tooltipContainer.innerHTML =
`<header>
<h1 class="mdn-property-name theme-fg-color5"></h1>
</header>
<div class="mdn-property-info">
<div class="mdn-summary"></div>
<pre class="mdn-syntax devtools-monospace"></pre>
</div>
<footer>
<a class="mdn-visit-page theme-link" href="#">Visit MDN (placeholder)</a>
</footer>`;
// fetch all the bits of the document that we will manipulate later
this.elements = {
heading: tooltipContainer.querySelector(".mdn-property-name"),
summary: tooltipContainer.querySelector(".mdn-summary"),
syntax: tooltipContainer.querySelector(".mdn-syntax"),
info: tooltipContainer.querySelector(".mdn-property-info"),
linkToMdn: tooltipContainer.querySelector(".mdn-visit-page")
};
// get the localized string for the link text
this.elements.linkToMdn.textContent = L10N.getStr("docsTooltip.visitMDN");
// listen for clicks and open in the browser window instead
let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
this.elements.linkToMdn.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
mainWindow.openUILinkIn(e.target.href, "tab");
this.emit("visitlink");
});
}
exports.MdnDocsWidget = MdnDocsWidget;
MdnDocsWidget.prototype = {
/**
* This is called just before the tooltip is displayed, and is
* passed the CSS property for which we want to display help.
*
* Its job is to make sure the document contains the docs
* content for that CSS property.
*
* First, it initializes the document, setting the things it can
* set synchronously, resetting the things it needs to get
* asynchronously, and making sure the throbber is throbbing.
*
* Then it tries to get the content asynchronously, updating
* the document with the content or with an error message.
*
* It returns immediately, so the caller can display the tooltip
* without waiting for the asynch operation to complete.
*
* @param {string} propertyName
* The name of the CSS property for which we need to display help.
*/
loadCssDocs: function (propertyName) {
/**
* Do all the setup we can do synchronously, and get the document in
* a state where it can be displayed while we are waiting for the
* MDN docs content to be retrieved.
*/
function initializeDocument(propName) {
// set property name heading
elements.heading.textContent = propName;
// set link target
elements.linkToMdn.setAttribute("href",
PAGE_LINK_URL + propName + PAGE_LINK_PARAMS);
// clear docs summary and syntax
elements.summary.textContent = "";
while (elements.syntax.firstChild) {
elements.syntax.firstChild.remove();
}
// reset the scroll position
elements.info.scrollTop = 0;
elements.info.scrollLeft = 0;
// show the throbber
elements.info.classList.add("devtools-throbber");
}
/**
* This is called if we successfully got the docs content.
* Finishes setting up the tooltip content, and disables the throbber.
*/
function finalizeDocument({summary, syntax}) {
// set docs summary and syntax
elements.summary.textContent = summary;
appendSyntaxHighlightedCSS(syntax, elements.syntax);
// hide the throbber
elements.info.classList.remove("devtools-throbber");
deferred.resolve(this);
}
/**
* This is called if we failed to get the docs content.
* Sets the content to contain an error message, and disables the throbber.
*/
function gotError(error) {
// show error message
elements.summary.textContent = L10N.getStr("docsTooltip.loadDocsError");
// hide the throbber
elements.info.classList.remove("devtools-throbber");
// although gotError is called when there's an error, we have handled
// the error, so call resolve not reject.
deferred.resolve(this);
}
let deferred = defer();
let elements = this.elements;
initializeDocument(propertyName);
getCssDocs(propertyName).then(finalizeDocument, gotError);
return deferred.promise;
},
destroy: function () {
this.elements = null;
}
};
/**
* Test whether a node is all whitespace.
*
* @return {boolean}
* True if the node all whitespace, otherwise false.
*/
function isAllWhitespace(node) {
return !(/[^\t\n\r ]/.test(node.textContent));
}
/**
* Test whether a node is a comment or whitespace node.
*
* @return {boolean}
* True if the node is a comment node or is all whitespace, otherwise false.
*/
function isIgnorable(node) {
// Comment nodes (8), text nodes (3) or whitespace
return (node.nodeType == 8) ||
((node.nodeType == 3) && isAllWhitespace(node));
}
/**
* Get the next node, skipping comments and whitespace.
*
* @return {node}
* The next sibling node that is not a comment or whitespace, or null if
* there isn't one.
*/
function nodeAfter(sib) {
while ((sib = sib.nextSibling)) {
if (!isIgnorable(sib)) {
return sib;
}
}
return null;
}
/**
* Test whether the argument `node` is a node whose tag is `tagName`.
*
* @param {node} node
* The code to test. May be null.
*
* @param {string} tagName
* The tag name to test against.
*
* @return {boolean}
* True if the node is not null and has the tag name `tagName`,
* otherwise false.
*/
function hasTagName(node, tagName) {
return node && node.tagName &&
node.tagName.toLowerCase() == tagName.toLowerCase();
}
/**
* Given an MDN page, get the "summary" portion.
*
* This is the textContent of the first non-whitespace
* element in the #Summary section of the document.
*
* It's expected to be a <P> element.
*
* @param {Document} mdnDocument
* The document in which to look for the "summary" section.
*
* @return {string}
* The summary section as a string, or null if it could not be found.
*/
function getSummary(mdnDocument) {
let summary = mdnDocument.getElementById("Summary");
if (!hasTagName(summary, "H2")) {
return null;
}
let firstParagraph = nodeAfter(summary);
if (!hasTagName(firstParagraph, "P")) {
return null;
}
return firstParagraph.textContent;
}
/**
* Given an MDN page, get the "syntax" portion.
*
* First we get the #Syntax section of the document. The syntax
* section we want is somewhere inside there.
*
* If the page is in the old structure, then the *first two*
* non-whitespace elements in the #Syntax section will be <PRE>
* nodes, and the second of these will be the syntax section.
*
* If the page is in the new structure, then the only the *first*
* non-whitespace element in the #Syntax section will be a <PRE>
* node, and it will be the syntax section.
*
* @param {Document} mdnDocument
* The document in which to look for the "syntax" section.
*
* @return {string}
* The syntax section as a string, or null if it could not be found.
*/
function getSyntax(mdnDocument) {
let syntax = mdnDocument.getElementById("Syntax");
if (!hasTagName(syntax, "H2")) {
return null;
}
let firstParagraph = nodeAfter(syntax);
if (!hasTagName(firstParagraph, "PRE")) {
return null;
}
let secondParagraph = nodeAfter(firstParagraph);
if (hasTagName(secondParagraph, "PRE")) {
return secondParagraph.textContent;
}
return firstParagraph.textContent;
}
/**
* Use a different URL for CSS docs pages. Used only for testing.
*
* @param {string} baseUrl
* The baseURL to use.
*/
function setBaseCssDocsUrl(baseUrl) {
PAGE_LINK_URL = baseUrl;
XHR_CSS_URL = baseUrl;
}
exports.setBaseCssDocsUrl = setBaseCssDocsUrl;
@@ -1,39 +0,0 @@
/* 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/. */
.mdn-container {
height: 300px;
margin: 4px;
overflow: auto;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.mdn-container header,
.mdn-container footer {
flex: 1;
padding: 0 1em;
}
.mdn-property-info {
flex: 10;
padding: 0 1em;
overflow: auto;
transition: opacity 400ms ease-in;
}
.mdn-syntax {
margin-top: 1em;
}
.devtools-throbber {
align-self: center;
opacity: 0;
}
.mdn-visit-page {
display: inline-block;
padding: 1em 0;
}
-1
View File
@@ -20,7 +20,6 @@ DevToolsModules(
'Graphs.js',
'GraphsWorker.js',
'LineGraphWidget.js',
'MdnDocsWidget.js',
'MountainGraphWidget.js',
'SideMenuWidget.jsm',
'SimpleListWidget.jsm',
@@ -1,93 +0,0 @@
/* 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";
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const {MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const TOOLTIP_WIDTH = 418;
const TOOLTIP_HEIGHT = 308;
/**
* Tooltip for displaying docs for CSS properties from MDN.
*
* @param {Document} toolboxDoc
* The toolbox document to attach the CSS docs tooltip.
*/
function CssDocsTooltip(toolboxDoc) {
this.tooltip = new HTMLTooltip(toolboxDoc, {
type: "arrow",
consumeOutsideClicks: true,
autofocus: true,
useXulWrapper: true,
stylesheet: "chrome://devtools/content/shared/widgets/mdn-docs.css",
});
this.widget = this.setMdnDocsContent();
this._onVisitLink = this._onVisitLink.bind(this);
this.widget.on("visitlink", this._onVisitLink);
// Initialize keyboard shortcuts
this.shortcuts = new KeyShortcuts({ window: this.tooltip.topWindow });
this._onShortcut = this._onShortcut.bind(this);
this.shortcuts.on("Escape", this._onShortcut);
}
CssDocsTooltip.prototype = {
/**
* Load CSS docs for the given property,
* then display the tooltip.
*/
show: function (anchor, propertyName) {
this.tooltip.once("shown", () => {
this.widget.loadCssDocs(propertyName);
});
this.tooltip.show(anchor);
},
hide: function () {
this.tooltip.hide();
},
_onShortcut: function (shortcut, event) {
if (!this.tooltip.isVisible()) {
return;
}
event.stopPropagation();
event.preventDefault();
this.hide();
},
_onVisitLink: function () {
this.hide();
},
/**
* Set the content of this tooltip to the MDN docs widget. This is called when the
* tooltip is first constructed.
* The caller can use the MdnDocsWidget to update the tooltip's UI with new content
* each time the tooltip is shown.
*
* @return {MdnDocsWidget} the created MdnDocsWidget instance.
*/
setMdnDocsContent: function () {
let container = this.tooltip.doc.createElementNS(XHTML_NS, "div");
container.setAttribute("class", "mdn-container theme-body");
this.tooltip.setContent(container, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
return new MdnDocsWidget(container);
},
destroy: function () {
this.widget.off("visitlink", this._onVisitLink);
this.widget.destroy();
this.shortcuts.destroy();
this.tooltip.destroy();
}
};
module.exports = CssDocsTooltip;
@@ -4,7 +4,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'CssDocsTooltip.js',
'EventTooltipHelper.js',
'HTMLTooltip.js',
'ImageTooltipHelper.js',
-1
View File
@@ -65,7 +65,6 @@ exports.devtoolsModules = [
"devtools/shared/gcli/commands/inject",
"devtools/shared/gcli/commands/jsb",
"devtools/shared/gcli/commands/listen",
"devtools/shared/gcli/commands/mdn",
"devtools/shared/gcli/commands/measure",
"devtools/shared/gcli/commands/media",
"devtools/shared/gcli/commands/pagemod",
-83
View File
@@ -1,83 +0,0 @@
/* 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";
const l10n = require("gcli/l10n");
var MdnDocsWidget;
try {
MdnDocsWidget = require("devtools/client/shared/widgets/MdnDocsWidget");
} catch (e) {
// DevTools MdnDocsWidget only available in Firefox Desktop
}
exports.items = [{
name: "mdn",
description: l10n.lookup("mdnDesc")
}, {
item: "command",
runAt: "client",
name: "mdn css",
description: l10n.lookup("mdnCssDesc"),
returnType: "cssPropertyOutput",
params: [{
name: "property",
type: { name: "string" },
defaultValue: null,
description: l10n.lookup("mdnCssProp")
}],
exec: function(args) {
if (!MdnDocsWidget) {
return null;
}
return MdnDocsWidget.getCssDocs(args.property).then(result => {
return {
data: result,
url: MdnDocsWidget.PAGE_LINK_URL + args.property,
property: args.property
};
}, error => {
return { error, property: args.property };
});
}
}, {
item: "converter",
from: "cssPropertyOutput",
to: "dom",
exec: function(result, context) {
let propertyName = result.property;
let document = context.document;
let root = document.createElement("div");
if (result.error) {
// The css property specified doesn't exist.
root.appendChild(document.createTextNode(
l10n.lookupFormat("mdnCssPropertyNotFound", [ propertyName ]) +
" (" + result.error + ")"));
} else {
let title = document.createElement("h2");
title.textContent = propertyName;
root.appendChild(title);
let link = document.createElement("p");
link.classList.add("gcli-mdn-url");
link.textContent = l10n.lookup("mdnCssVisitPage");
root.appendChild(link);
link.addEventListener("click", () => {
let mainWindow = context.environment.chromeWindow;
mainWindow.openUILinkIn(result.url, "tab");
});
let summary = document.createElement("p");
summary.textContent = result.data.summary;
root.appendChild(summary);
}
return root;
}
}];
-1
View File
@@ -16,7 +16,6 @@ DevToolsModules(
'inject.js',
'jsb.js',
'listen.js',
'mdn.js',
'measure.js',
'media.js',
'pagemod.js',
@@ -1457,24 +1457,6 @@ folderInvalidPath=Please enter a valid path
# The argument (%1$S) is the folder path.
folderOpenDirResult=Opened %1$S
# LOCALIZATION NOTE (mdnDesc) A very short string used to describe the
# use of 'mdn' command.
mdnDesc=Retrieve documentation from MDN
# LOCALIZATION NOTE (mdnCssDesc) A very short string used to describe the
# result of the 'mdn css' command.
mdnCssDesc=Retrieve documentation about a given CSS property name from MDN
# LOCALIZATION NOTE (mdnCssProp) String used to describe the 'property name'
# parameter used in the 'mdn css' command.
mdnCssProp=Property name
# LOCALIZATION NOTE (mdnCssPropertyNotFound) String used to display an error in
# the result of the 'mdn css' command. Errors occur when a given CSS property
# wasn't found on MDN. The %1$S parameter will be replaced with the name of the
# CSS property.
mdnCssPropertyNotFound=MDN documentation for the CSS property %1$S was not found.
# LOCALIZATION NOTE (mdnCssVisitPage) String used as the label of a link to the
# MDN page for a given CSS property.
mdnCssVisitPage=Visit MDN page
# LOCALIZATION NOTE (security)
securityPrivacyDesc=Display supported security and privacy features
securityManual=Commands to list and get suggestions about security features for the current domain.
@@ -130,14 +130,6 @@ styleinspector.contextmenu.toggleOrigSources=Show Original Sources
# the rule view context menu "Show original sources" entry.
styleinspector.contextmenu.toggleOrigSources.accessKey=O
# LOCALIZATION NOTE (styleinspector.contextmenu.showMdnDocs): Text displayed in the rule view
# context menu to display docs from MDN for an item.
styleinspector.contextmenu.showMdnDocs=Show MDN Docs
# LOCALIZATION NOTE (styleinspector.contextmenu.showMdnDocs.accessKey): Access key for
# the rule view context menu "Show MDN docs" entry.
styleinspector.contextmenu.showMdnDocs.accessKey=D
# LOCALIZATION NOTE (styleinspector.contextmenu.addNewRule): Text displayed in the
# rule view context menu for adding a new rule to the element.
# This should match inspector.addRule.tooltip in inspector.properties
+1
View File
@@ -2243,6 +2243,7 @@ GK_ATOM(scrollbar_end_forward, "scrollbar-end-forward")
GK_ATOM(scrollbar_thumb_proportional, "scrollbar-thumb-proportional")
GK_ATOM(overlay_scrollbars, "overlay-scrollbars")
GK_ATOM(prefers_color_scheme, "prefers-color-scheme")
GK_ATOM(prefers_reduced_motion, "prefers-reduced-motion")
GK_ATOM(windows_accent_color_applies, "windows-accent-color-applies")
GK_ATOM(windows_accent_color_is_dark, "windows-accent-color-is-dark")
GK_ATOM(windows_default_theme, "windows-default-theme")
+2
View File
@@ -420,6 +420,7 @@ CSS_KEY(no-drag, no_drag)
CSS_KEY(no-drop, no_drop)
CSS_KEY(no-historical-ligatures, no_historical_ligatures)
CSS_KEY(no-open-quote, no_open_quote)
CSS_KEY(no-preference, no_preference)
CSS_KEY(no-repeat, no_repeat)
CSS_KEY(none, none)
CSS_KEY(normal, normal)
@@ -471,6 +472,7 @@ CSS_KEY(px, px)
CSS_KEY(rad, rad)
CSS_KEY(read-only, read_only)
CSS_KEY(read-write, read_write)
CSS_KEY(reduce, reduce)
CSS_KEY(relative, relative)
CSS_KEY(repeat, repeat)
CSS_KEY(repeat-x, repeat_x)
+35 -4
View File
@@ -47,9 +47,15 @@ static const nsCSSProps::KTableEntry kDisplayModeKeywords[] = {
};
static const nsCSSProps::KTableEntry kPrefersColorSchemeKeywords[] = {
{ eCSSKeyword_light, NS_STYLE_PREFERS_COLOR_SCHEME_LIGHT },
{ eCSSKeyword_dark, NS_STYLE_PREFERS_COLOR_SCHEME_DARK },
{ eCSSKeyword_UNKNOWN, -1 },
{ eCSSKeyword_light, NS_STYLE_PREFERS_COLOR_SCHEME_LIGHT },
{ eCSSKeyword_dark, NS_STYLE_PREFERS_COLOR_SCHEME_DARK },
{ eCSSKeyword_UNKNOWN, -1 },
};
static const nsCSSProps::KTableEntry kPrefersMotionKeywords[] = {
{ eCSSKeyword_no_preference, NS_STYLE_PREFERS_FULL_MOTION },
{ eCSSKeyword_reduce, NS_STYLE_PREFERS_REDUCED_MOTION },
{ eCSSKeyword_UNKNOWN, -1 },
};
#ifdef XP_WIN
@@ -81,7 +87,8 @@ const OperatingSystemVersionInfo osVersionStrings[] = {
{ LookAndFeel::eOperatingSystemVersion_WindowsVista, L"windows-vista" },
{ LookAndFeel::eOperatingSystemVersion_Windows7, L"windows-win7" },
{ LookAndFeel::eOperatingSystemVersion_Windows8, L"windows-win8" },
{ LookAndFeel::eOperatingSystemVersion_Windows10, L"windows-win10" }
{ LookAndFeel::eOperatingSystemVersion_Windows10, L"windows-win10" },
{ LookAndFeel::eOperatingSystemVersion_Windows11, L"windows-win11" }
};
#endif
@@ -497,6 +504,22 @@ GetPrefersColorScheme(nsPresContext* aPresContext, const nsMediaFeature* aFeatur
return NS_OK;
}
static nsresult
GetPrefersMotion(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
nsCSSValue& aResult)
{
switch(Preferences::GetInt("ui.prefersReducedMotion", 0)) {
case 1:
aResult.SetIntValue(NS_STYLE_PREFERS_REDUCED_MOTION,
eCSSUnit_Enumerated);
break;
default:
aResult.Reset();
}
return NS_OK;
}
static nsresult
GetDarkTheme(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
nsCSSValue& aResult)
@@ -630,6 +653,14 @@ nsMediaFeatures::features[] = {
{ kPrefersColorSchemeKeywords },
GetPrefersColorScheme
},
{
&nsGkAtoms::prefers_reduced_motion,
nsMediaFeature::eMinMaxNotAllowed,
nsMediaFeature::eEnumerated,
nsMediaFeature::eNoRequirements,
{ kPrefersMotionKeywords },
GetPrefersMotion
},
{
&nsGkAtoms::resolution,
nsMediaFeature::eMinMaxAllowed,
+6 -2
View File
@@ -1265,8 +1265,12 @@ enum class StyleDisplay : uint8_t {
#define NS_STYLE_DISPLAY_MODE_FULLSCREEN 3
// prefers-color-scheme
#define NS_STYLE_PREFERS_COLOR_SCHEME_LIGHT 0
#define NS_STYLE_PREFERS_COLOR_SCHEME_DARK 1
#define NS_STYLE_PREFERS_COLOR_SCHEME_LIGHT 0
#define NS_STYLE_PREFERS_COLOR_SCHEME_DARK 1
// prefers-reduced-motion
#define NS_STYLE_PREFERS_FULL_MOTION 0
#define NS_STYLE_PREFERS_REDUCED_MOTION 1
} // namespace mozilla
+32 -79
View File
@@ -83,82 +83,6 @@ IsWindowsBuildOrLater(uint32_t aBuild)
return false;
}
inline bool
IsWindows10BuildOrLater(uint32_t aBuild)
{
static uint32_t minBuild = 0;
static uint32_t maxBuild = UINT32_MAX;
if (minBuild >= aBuild) {
return true;
}
if (aBuild >= maxBuild) {
return false;
}
OSVERSIONINFOEX info;
ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
info.dwMajorVersion = 10;
info.dwBuildNumber = aBuild;
DWORDLONG conditionMask = 0;
VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
if (VerifyVersionInfo(&info, VER_MAJORVERSION | VER_MINORVERSION |
VER_BUILDNUMBER | VER_SERVICEPACKMAJOR |
VER_SERVICEPACKMINOR, conditionMask)) {
minBuild = aBuild;
return true;
}
maxBuild = aBuild;
return false;
}
#if 0//defined(_M_X64) || defined(_M_AMD64)
// We support only Win7 or later on Win64.
MOZ_ALWAYS_INLINE bool
IsXPSP3OrLater()
{
return true;
}
MOZ_ALWAYS_INLINE bool
IsWin2003OrLater()
{
return true;
}
MOZ_ALWAYS_INLINE bool
IsWin2003SP2OrLater()
{
return true;
}
MOZ_ALWAYS_INLINE bool
IsVistaOrLater()
{
return true;
}
MOZ_ALWAYS_INLINE bool
IsVistaSP1OrLater()
{
return true;
}
MOZ_ALWAYS_INLINE bool
IsWin7OrLater()
{
return true;
}
#else
MOZ_ALWAYS_INLINE bool
IsXPSP3OrLater()
{
@@ -194,7 +118,6 @@ IsWin7OrLater()
{
return IsWindowsVersionOrLater(0x06010000ul);
}
#endif
MOZ_ALWAYS_INLINE bool
IsWin7SP1OrLater()
@@ -220,10 +143,40 @@ IsWin10OrLater()
return IsWindowsVersionOrLater(0x0a000000ul);
}
enum class WinBuild : uint32_t {
Win10RTM = 10240,
Win10v1511 = 10586,
Win10v1607 = 14393,
Win10v1703 = 15063,
Win10v1709 = 16299,
Win10v1803 = 17134,
Win10v1809 = 17763,
Win10v1903 = 18362,
Win10v19H2 = 18363,
Win10v20H1 = 19041,
Win10v20H2 = 19042,
Win10v21H1 = 19043,
Win10v21H2 = 19044,
Win10v22H2 = 19045,
Win11RTM = 22000,
Win11v22H2 = 22621,
Win11v23H2 = 22631,
Win11v24H2 = 26100,
Win11v25H2 = 26200
};
// Check for at least named build aBuild taken from WinBuild enum above.
inline bool
IsWindows10BuildOrLater(WinBuild aBuild) {
uint32_t build = static_cast<uint32_t>(aBuild);
return IsWin10OrLater() && IsWindowsBuildOrLater(build);
}
// Windows 11 RTM
MOZ_ALWAYS_INLINE bool
IsWin10CreatorsUpdateOrLater()
IsWin11OrLater()
{
return IsWindows10BuildOrLater(15063);
return IsWindows10BuildOrLater(WinBuild::Win11RTM);
}
MOZ_ALWAYS_INLINE bool
+5 -1
View File
@@ -258,7 +258,6 @@ pref("browser.sessionhistory.max_total_viewers", -1);
// See https://github.com/MoonchildProductions/UXP/issues/719
pref("browser.newtabpage.add_to_session_history", false);
// Determines whether the browser's current theme should be light or dark.
// 0 = feature disabled
// 1 = default: light theme
@@ -266,6 +265,11 @@ pref("browser.newtabpage.add_to_session_history", false);
pref("ui.color_scheme", 1);
pref("ui.use_native_colors", true);
// Whether websites should use reduced animation styles.
// Used for CSS @media query.
pref("ui.prefersReducedMotion", 0);
#ifdef MOZ_WIDGET_GTK
// Determines whether the menubar is shown in the global menubar or not.
pref("ui.use_global_menubar", false);
+1
View File
@@ -455,6 +455,7 @@ public:
eOperatingSystemVersion_Windows7,
eOperatingSystemVersion_Windows8,
eOperatingSystemVersion_Windows10,
eOperatingSystemVersion_Windows11,
eOperatingSystemVersion_Unknown
};
+3 -1
View File
@@ -27,7 +27,9 @@ nsLookAndFeel::GetOperatingSystemVersion()
return version;
}
if (IsWin10OrLater()) {
if (IsWin11OrLater()) {
version = eOperatingSystemVersion_Windows11;
} else if (IsWin10OrLater()) {
version = eOperatingSystemVersion_Windows10;
} else if (IsWin8OrLater()) {
version = eOperatingSystemVersion_Windows8;