mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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¯os";
|
||||
// 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;
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}];
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -455,6 +455,7 @@ public:
|
||||
eOperatingSystemVersion_Windows7,
|
||||
eOperatingSystemVersion_Windows8,
|
||||
eOperatingSystemVersion_Windows10,
|
||||
eOperatingSystemVersion_Windows11,
|
||||
eOperatingSystemVersion_Unknown
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user