зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound. CLOSED TREE
This commit is contained in:
Коммит
ec50e0e5af
|
@ -288,15 +288,15 @@ window:not([chromehidden~="toolbar"]) #nav-bar[nonemptyoverflow] > .overflow-but
|
||||||
|
|
||||||
%ifdef MENUBAR_CAN_AUTOHIDE
|
%ifdef MENUBAR_CAN_AUTOHIDE
|
||||||
#toolbar-menubar:not([autohide=true]) + #TabsToolbar > .titlebar-item,
|
#toolbar-menubar:not([autohide=true]) + #TabsToolbar > .titlebar-item,
|
||||||
#toolbar-menubar:not([autohide=true]) + #TabsToolbar .titlebar-placeholder,
|
#toolbar-menubar:not([autohide=true]) + #TabsToolbar .titlebar-spacer,
|
||||||
%endif
|
%endif
|
||||||
%ifndef MOZ_WIDGET_COCOA
|
%ifndef MOZ_WIDGET_COCOA
|
||||||
#main-window:not([sizemode=normal]) .titlebar-placeholder[type="pre-tabs"],
|
#main-window:not([sizemode=normal]) .titlebar-spacer[type="pre-tabs"],
|
||||||
%endif
|
%endif
|
||||||
#main-window:not([chromemargin]) .titlebar-buttonbox-container,
|
#main-window:not([chromemargin]) .titlebar-buttonbox-container,
|
||||||
#main-window[inFullscreen] .titlebar-buttonbox-container,
|
#main-window[inFullscreen] .titlebar-buttonbox-container,
|
||||||
#main-window[inFullscreen] .titlebar-placeholder,
|
#main-window[inFullscreen] .titlebar-spacer,
|
||||||
#main-window:not([tabsintitlebar]) .titlebar-placeholder {
|
#main-window:not([tabsintitlebar]) .titlebar-spacer {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -760,7 +760,7 @@ xmlns="http://www.w3.org/1999/xhtml"
|
||||||
<spacer flex="1000"/>
|
<spacer flex="1000"/>
|
||||||
|
|
||||||
<hbox id="TabsToolbar-customization-target" flex="1">
|
<hbox id="TabsToolbar-customization-target" flex="1">
|
||||||
<hbox class="titlebar-placeholder" type="pre-tabs"
|
<hbox class="titlebar-spacer" type="pre-tabs"
|
||||||
skipintoolbarset="true"/>
|
skipintoolbarset="true"/>
|
||||||
|
|
||||||
<tabs id="tabbrowser-tabs"
|
<tabs id="tabbrowser-tabs"
|
||||||
|
@ -791,7 +791,7 @@ xmlns="http://www.w3.org/1999/xhtml"
|
||||||
tooltiptext="&listAllTabs.label;"
|
tooltiptext="&listAllTabs.label;"
|
||||||
removable="false"/>
|
removable="false"/>
|
||||||
|
|
||||||
<hbox class="titlebar-placeholder" type="post-tabs"
|
<hbox class="titlebar-spacer" type="post-tabs"
|
||||||
ordinal="1000"
|
ordinal="1000"
|
||||||
skipintoolbarset="true"/>
|
skipintoolbarset="true"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
|
@ -843,7 +843,7 @@ var CustomizableUIInternal = {
|
||||||
// node is not removable, we leave it where it is. However, we can only
|
// node is not removable, we leave it where it is. However, we can only
|
||||||
// safely touch elements that have an ID - both because we depend on
|
// safely touch elements that have an ID - both because we depend on
|
||||||
// IDs (or are specials), and because such elements are not intended to
|
// IDs (or are specials), and because such elements are not intended to
|
||||||
// be widgets (eg, titlebar-placeholder elements).
|
// be widgets (eg, titlebar-spacer elements).
|
||||||
if ((node.id || this.isSpecialWidget(node)) &&
|
if ((node.id || this.isSpecialWidget(node)) &&
|
||||||
node.getAttribute("skipintoolbarset") != "true") {
|
node.getAttribute("skipintoolbarset") != "true") {
|
||||||
if (this.isWidgetRemovable(node)) {
|
if (this.isWidgetRemovable(node)) {
|
||||||
|
|
|
@ -1027,7 +1027,8 @@ BrowserGlue.prototype = {
|
||||||
name: gBrowserBundle.GetStringFromName("lightTheme.name"),
|
name: gBrowserBundle.GetStringFromName("lightTheme.name"),
|
||||||
description: gBrowserBundle.GetStringFromName("lightTheme.description"),
|
description: gBrowserBundle.GetStringFromName("lightTheme.description"),
|
||||||
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/light.icon.svg",
|
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/light.icon.svg",
|
||||||
textcolor: "#18191a",
|
textcolor: "rgb(24, 25, 26)",
|
||||||
|
icon_color: "rgb(24, 25, 26, 0.7)",
|
||||||
accentcolor: "#E3E4E6",
|
accentcolor: "#E3E4E6",
|
||||||
popup: "#fff",
|
popup: "#fff",
|
||||||
popup_text: "#0c0c0d",
|
popup_text: "#0c0c0d",
|
||||||
|
@ -1045,6 +1046,7 @@ BrowserGlue.prototype = {
|
||||||
description: gBrowserBundle.GetStringFromName("darkTheme.description"),
|
description: gBrowserBundle.GetStringFromName("darkTheme.description"),
|
||||||
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/dark.icon.svg",
|
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/dark.icon.svg",
|
||||||
textcolor: "rgb(249, 249, 250)",
|
textcolor: "rgb(249, 249, 250)",
|
||||||
|
icon_color: "rgb(249, 249, 250, 0.7)",
|
||||||
accentcolor: "hsl(240, 5%, 5%)",
|
accentcolor: "hsl(240, 5%, 5%)",
|
||||||
popup: "#4a4a4f",
|
popup: "#4a4a4f",
|
||||||
popup_text: "rgb(249, 249, 250)",
|
popup_text: "rgb(249, 249, 250)",
|
||||||
|
|
|
@ -621,7 +621,7 @@ notification[value="translation"] menulist > .menulist-dropmarker {
|
||||||
* and enable it for desktop environment which do that by default.
|
* and enable it for desktop environment which do that by default.
|
||||||
* See nsWindow::TopLevelWindowUseARGBVisual() for details. */
|
* See nsWindow::TopLevelWindowUseARGBVisual() for details. */
|
||||||
@media (-moz-gtk-csd-transparent-background) {
|
@media (-moz-gtk-csd-transparent-background) {
|
||||||
:root[tabsintitlebar]:not(:-moz-lwtheme) {
|
:root[tabsintitlebar][sizemode="normal"]:not(:-moz-lwtheme) {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,3 @@
|
||||||
|
|
||||||
%include ../shared/compacttheme.inc.css
|
%include ../shared/compacttheme.inc.css
|
||||||
|
|
||||||
/* The menubar and tabs toolbar should match the devedition theme */
|
|
||||||
#TabsToolbar,
|
|
||||||
#toolbar-menubar {
|
|
||||||
-moz-appearance: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-menubar > menu:not([open]) {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@
|
||||||
/* The fullscreen button doesnt show on Yosemite(10.10) or above so dont give it a
|
/* The fullscreen button doesnt show on Yosemite(10.10) or above so dont give it a
|
||||||
border there */
|
border there */
|
||||||
@media (-moz-mac-yosemite-theme: 0) {
|
@media (-moz-mac-yosemite-theme: 0) {
|
||||||
.titlebar-placeholder[type="fullscreen-button"] {
|
.titlebar-spacer[type="fullscreen-button"] {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
--toolbar-non-lwt-bgcolor: var(--toolbar-bgcolor);
|
--toolbar-non-lwt-bgcolor: var(--toolbar-bgcolor);
|
||||||
--toolbar-non-lwt-textcolor: var(--lwt-text-color);
|
--toolbar-non-lwt-textcolor: var(--lwt-text-color);
|
||||||
--toolbar-non-lwt-bgimage: none;
|
--toolbar-non-lwt-bgimage: none;
|
||||||
|
|
||||||
--toolbarbutton-icon-fill-opacity: .7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:root:-moz-lwtheme-brighttext {
|
:root:-moz-lwtheme-brighttext {
|
||||||
|
|
|
@ -629,20 +629,20 @@
|
||||||
|
|
||||||
/* Drag space */
|
/* Drag space */
|
||||||
|
|
||||||
.titlebar-placeholder[type="pre-tabs"],
|
.titlebar-spacer[type="pre-tabs"],
|
||||||
.titlebar-placeholder[type="post-tabs"] {
|
.titlebar-spacer[type="post-tabs"] {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
@media (max-width: 500px) {
|
||||||
.titlebar-placeholder[type="post-tabs"] {
|
.titlebar-spacer[type="post-tabs"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tab separators */
|
/* Tab separators */
|
||||||
|
|
||||||
.titlebar-placeholder[type="pre-tabs"] {
|
.titlebar-spacer[type="pre-tabs"] {
|
||||||
border-inline-end: 1px solid var(--lwt-background-tab-separator-color, currentColor);
|
border-inline-end: 1px solid var(--lwt-background-tab-separator-color, currentColor);
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,9 +59,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (-moz-windows-glass) {
|
@media (-moz-windows-glass) {
|
||||||
/* Set to full fill-opacity to improve visibility of toolbar buttons on aero glass. */
|
/* Use opaque white icons on Aero Glass. */
|
||||||
#TabsToolbar {
|
#TabsToolbar {
|
||||||
--toolbarbutton-icon-fill-opacity: 1;
|
--lwt-toolbarbutton-icon-fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the menubar text readable on aero glass (copied from browser-aero.css). */
|
/* Make the menubar text readable on aero glass (copied from browser-aero.css). */
|
||||||
|
|
|
@ -26,7 +26,7 @@ class CSSDeclaration extends PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { property, value, className } = this.props;
|
const { property, value, className } = this.props;
|
||||||
|
|
||||||
return dom.div({ className },
|
return dom.div({ className: `declaration ${className}` },
|
||||||
dom.span({ className: "declaration-name theme-fg-color5"}, property),
|
dom.span({ className: "declaration-name theme-fg-color5"}, property),
|
||||||
":",
|
":",
|
||||||
dom.span({ className: "declaration-value theme-fg-color1"}, value),
|
dom.span({ className: "declaration-value theme-fg-color1"}, value),
|
||||||
|
|
|
@ -35,11 +35,21 @@ class ChangesApp extends PureComponent {
|
||||||
|
|
||||||
renderDeclarations(remove = {}, add = {}) {
|
renderDeclarations(remove = {}, add = {}) {
|
||||||
const removals = Object.entries(remove).map(([property, value]) => {
|
const removals = Object.entries(remove).map(([property, value]) => {
|
||||||
return CSSDeclaration({ className: "level diff-remove", property, value });
|
return CSSDeclaration({
|
||||||
|
key: "remove-" + property,
|
||||||
|
className: "level diff-remove",
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const additions = Object.entries(add).map(([property, value]) => {
|
const additions = Object.entries(add).map(([property, value]) => {
|
||||||
return CSSDeclaration({ className: "level diff-add", property, value });
|
return CSSDeclaration({
|
||||||
|
key: "add-" + property,
|
||||||
|
className: "level diff-add",
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return [removals, additions];
|
return [removals, additions];
|
||||||
|
@ -55,13 +65,21 @@ class ChangesApp extends PureComponent {
|
||||||
// Mark this rule as rendered so we don't render it again.
|
// Mark this rule as rendered so we don't render it again.
|
||||||
this.renderedRules.push(ruleId);
|
this.renderedRules.push(ruleId);
|
||||||
|
|
||||||
|
let diffClass = "";
|
||||||
|
if (rule.changeType === "rule-add") {
|
||||||
|
diffClass = "diff-add";
|
||||||
|
} else if (rule.changeType === "rule-remove") {
|
||||||
|
diffClass = "diff-remove";
|
||||||
|
}
|
||||||
|
|
||||||
return dom.div(
|
return dom.div(
|
||||||
{
|
{
|
||||||
|
key: ruleId,
|
||||||
className: "rule",
|
className: "rule",
|
||||||
},
|
},
|
||||||
dom.div(
|
dom.div(
|
||||||
{
|
{
|
||||||
className: "level selector",
|
className: `level selector ${diffClass}`,
|
||||||
title: selector,
|
title: selector,
|
||||||
},
|
},
|
||||||
selector,
|
selector,
|
||||||
|
@ -73,7 +91,7 @@ class ChangesApp extends PureComponent {
|
||||||
}),
|
}),
|
||||||
// Render any changed CSS declarations.
|
// Render any changed CSS declarations.
|
||||||
this.renderDeclarations(rule.remove, rule.add),
|
this.renderDeclarations(rule.remove, rule.add),
|
||||||
dom.span({ className: "level bracket-close" }, "}")
|
dom.div({ className: `level bracket-close ${diffClass}` }, "}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +103,7 @@ class ChangesApp extends PureComponent {
|
||||||
|
|
||||||
return dom.details(
|
return dom.details(
|
||||||
{
|
{
|
||||||
|
key: sourceId,
|
||||||
className: "source devtools-monospace",
|
className: "source devtools-monospace",
|
||||||
open: true,
|
open: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -133,6 +133,7 @@ function removeRule(ruleId, rules) {
|
||||||
* rules: {
|
* rules: {
|
||||||
* <ruleId>: {
|
* <ruleId>: {
|
||||||
* selector: "" // String CSS selector or CSS at-rule text
|
* selector: "" // String CSS selector or CSS at-rule text
|
||||||
|
* changeType: // Optional string: "rule-add" or "rule-remove"
|
||||||
* children: [] // Array of <ruleId> for child rules of this rule.
|
* children: [] // Array of <ruleId> for child rules of this rule.
|
||||||
* parent: // <ruleId> of the parent rule
|
* parent: // <ruleId> of the parent rule
|
||||||
* add: {
|
* add: {
|
||||||
|
@ -166,9 +167,12 @@ const reducers = {
|
||||||
state = cloneState(state);
|
state = cloneState(state);
|
||||||
|
|
||||||
const { type, href, index } = change.source;
|
const { type, href, index } = change.source;
|
||||||
const { selector, ancestors, ruleIndex } = change;
|
const { selector, ancestors, ruleIndex, type: changeType } = change;
|
||||||
const sourceId = getSourceHash(change.source);
|
const sourceId = getSourceHash(change.source);
|
||||||
const ruleId = getRuleHash({ selector, ancestors, ruleIndex });
|
const ruleId = getRuleHash({ selector, ancestors, ruleIndex });
|
||||||
|
// Type coerce to boolean: `false` if value is undefined or `null`, `true` otherwise.
|
||||||
|
const hasAdd = !!change.add;
|
||||||
|
const hasRemove = !!change.remove;
|
||||||
|
|
||||||
// Copy or create object identifying the source (styelsheet/element) for this change.
|
// Copy or create object identifying the source (styelsheet/element) for this change.
|
||||||
const source = Object.assign({}, state[sourceId], { type, href, index });
|
const source = Object.assign({}, state[sourceId], { type, href, index });
|
||||||
|
@ -178,37 +182,41 @@ const reducers = {
|
||||||
let rule = rules[ruleId];
|
let rule = rules[ruleId];
|
||||||
if (!rule) {
|
if (!rule) {
|
||||||
rule = createRule({ selector, ancestors, ruleIndex }, rules);
|
rule = createRule({ selector, ancestors, ruleIndex }, rules);
|
||||||
|
if (changeType.startsWith("rule-")) {
|
||||||
|
rule.changeType = changeType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Copy or create collection of all CSS declarations ever added to this rule.
|
// Copy or create collection of all CSS declarations ever added to this rule.
|
||||||
const add = Object.assign({}, rule.add);
|
const add = Object.assign({}, rule.add);
|
||||||
// Copy or create collection of all CSS declarations ever removed from this rule.
|
// Copy or create collection of all CSS declarations ever removed from this rule.
|
||||||
const remove = Object.assign({}, rule.remove);
|
const remove = Object.assign({}, rule.remove);
|
||||||
|
|
||||||
if (change.remove && change.remove.property) {
|
if (hasRemove) {
|
||||||
// Track the remove operation only if the property was not previously introduced
|
Object.entries(change.remove).forEach(([property, value]) => {
|
||||||
// by an add operation. This ensures repeated changes of the same property
|
// Track the remove operation only if the property was not previously introduced
|
||||||
// register as a single remove operation of its original value.
|
// by an add operation. This ensures repeated changes of the same property
|
||||||
if (!add[change.remove.property]) {
|
// register as a single remove operation of its original value.
|
||||||
remove[change.remove.property] = change.remove.value;
|
if (!add[property]) {
|
||||||
}
|
remove[property] = value;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete any previous add operation which would be canceled out by this remove.
|
// Delete any previous add operation which would be canceled out by this remove.
|
||||||
if (add[change.remove.property] === change.remove.value) {
|
if (add[property] === value) {
|
||||||
delete add[change.remove.property];
|
delete add[property];
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (change.add && change.add.property) {
|
if (hasAdd) {
|
||||||
add[change.add.property] = change.add.value;
|
Object.entries(change.add).forEach(([property, value]) => {
|
||||||
}
|
add[property] = value;
|
||||||
|
|
||||||
const property = change.add && change.add.property ||
|
// Remove previously tracked declarations if they cancel each other out.
|
||||||
change.remove && change.remove.property;
|
if (add[property] === remove[property]) {
|
||||||
|
delete add[property];
|
||||||
// Remove tracked operations if they cancel each other out.
|
delete remove[property];
|
||||||
if (add[property] === remove[property]) {
|
}
|
||||||
delete add[property];
|
});
|
||||||
delete remove[property];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove information about the rule if none its declarations changed.
|
// Remove information about the rule if none its declarations changed.
|
||||||
|
|
|
@ -15,3 +15,4 @@ support-files =
|
||||||
[browser_changes_declaration_disable.js]
|
[browser_changes_declaration_disable.js]
|
||||||
[browser_changes_declaration_edit_value.js]
|
[browser_changes_declaration_edit_value.js]
|
||||||
[browser_changes_declaration_remove.js]
|
[browser_changes_declaration_remove.js]
|
||||||
|
[browser_changes_rule_selector.js]
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test that renaming the selector of a CSS declaration in the Rule view is tracked as
|
||||||
|
// one rule removal with the old selector and one rule addition with the new selector.
|
||||||
|
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type='text/css'>
|
||||||
|
div {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
|
const { inspector, view: ruleView } = await openRuleView();
|
||||||
|
const { document: doc, store } = selectChangesView(inspector);
|
||||||
|
const panel = doc.querySelector("#sidebar-panel-changes");
|
||||||
|
|
||||||
|
await selectNode("div", inspector);
|
||||||
|
const ruleEditor = getRuleViewRuleEditor(ruleView, 1);
|
||||||
|
|
||||||
|
info("Focusing the first rule's selector name in the Rule view");
|
||||||
|
const editor = await focusEditableField(ruleView, ruleEditor.selectorText);
|
||||||
|
info("Entering a new selector name");
|
||||||
|
editor.input.value = ".test";
|
||||||
|
|
||||||
|
// Expect two "TRACK_CHANGE" actions: one for rule removal, one for rule addition.
|
||||||
|
const onTrackChange = waitUntilAction(store, "TRACK_CHANGE", 2);
|
||||||
|
const onRuleViewChanged = once(ruleView, "ruleview-changed");
|
||||||
|
info("Pressing Enter key to commit the change");
|
||||||
|
EventUtils.synthesizeKey("KEY_Enter");
|
||||||
|
info("Waiting for rule view to update");
|
||||||
|
await onRuleViewChanged;
|
||||||
|
info("Wait for the change to be tracked");
|
||||||
|
await onTrackChange;
|
||||||
|
|
||||||
|
const rules = panel.querySelectorAll(".rule");
|
||||||
|
is(rules.length, 2, "Two rules were tracked as changed");
|
||||||
|
|
||||||
|
const firstSelector = rules.item(0).querySelector(".selector");
|
||||||
|
is(firstSelector.title, "div", "Old selector name was tracked.");
|
||||||
|
ok(firstSelector.classList.contains("diff-remove"), "Old selector was removed.");
|
||||||
|
|
||||||
|
const secondSelector = rules.item(1).querySelector(".selector");
|
||||||
|
is(secondSelector.title, ".test", "New selector name was tracked.");
|
||||||
|
ok(secondSelector.classList.contains("diff-add"), "New selector was added.");
|
||||||
|
|
||||||
|
info("Checking that the two rules have identical declarations");
|
||||||
|
const firstDecl = rules.item(0).querySelectorAll(".declaration");
|
||||||
|
is(firstDecl.length, 1, "First rule has only one declaration");
|
||||||
|
is(firstDecl.item(0).textContent, "color:red;", "First rule has correct declaration");
|
||||||
|
ok(firstDecl.item(0).classList.contains("diff-remove"),
|
||||||
|
"First rule has declaration tracked as removed");
|
||||||
|
|
||||||
|
const secondDecl = rules.item(1).querySelectorAll(".declaration");
|
||||||
|
is(secondDecl.length, 1, "Second rule has only one declaration");
|
||||||
|
is(secondDecl.item(0).textContent, "color:red;", "Second rule has correct declaration");
|
||||||
|
ok(secondDecl.item(0).classList.contains("diff-add"),
|
||||||
|
"Second rule has declaration tracked as added");
|
||||||
|
});
|
|
@ -86,8 +86,8 @@ class FlexItemSizingProperties extends PureComponent {
|
||||||
const flexBasisValue = properties["flex-basis"];
|
const flexBasisValue = properties["flex-basis"];
|
||||||
const dimensionValue = properties[dimension];
|
const dimensionValue = properties[dimension];
|
||||||
|
|
||||||
|
let title = getStr("flexbox.itemSizing.baseSizeSectionHeader");
|
||||||
let property = null;
|
let property = null;
|
||||||
let reason = null;
|
|
||||||
|
|
||||||
if (flexBasisValue) {
|
if (flexBasisValue) {
|
||||||
// If flex-basis is defined, then that's what is used for the base size.
|
// If flex-basis is defined, then that's what is used for the base size.
|
||||||
|
@ -97,19 +97,18 @@ class FlexItemSizingProperties extends PureComponent {
|
||||||
property = this.renderCssProperty(dimension, dimensionValue);
|
property = this.renderCssProperty(dimension, dimensionValue);
|
||||||
} else {
|
} else {
|
||||||
// Finally, if nothing is set, then the base size is the max-content size.
|
// Finally, if nothing is set, then the base size is the max-content size.
|
||||||
reason = this.renderReasons(
|
// In this case replace the section's title.
|
||||||
[getStr("flexbox.itemSizing.itemBaseSizeFromContent")]);
|
title = getStr("flexbox.itemSizing.itemContentSize");
|
||||||
}
|
}
|
||||||
|
|
||||||
const className = "section base";
|
const className = "section base";
|
||||||
return (
|
return (
|
||||||
dom.li({ className: className + (property ? "" : " no-property") },
|
dom.li({ className: className + (property ? "" : " no-property") },
|
||||||
dom.span({ className: "name" },
|
dom.span({ className: "name" },
|
||||||
getStr("flexbox.itemSizing.baseSizeSectionHeader"),
|
title,
|
||||||
property
|
property
|
||||||
),
|
),
|
||||||
this.renderSize(mainBaseSize),
|
this.renderSize(mainBaseSize)
|
||||||
reason
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -120,7 +119,6 @@ class FlexItemSizingProperties extends PureComponent {
|
||||||
mainBaseSize,
|
mainBaseSize,
|
||||||
mainFinalSize,
|
mainFinalSize,
|
||||||
lineGrowthState,
|
lineGrowthState,
|
||||||
clampState,
|
|
||||||
} = flexItemSizing;
|
} = flexItemSizing;
|
||||||
|
|
||||||
// Don't display anything if all interesting sizes are 0.
|
// Don't display anything if all interesting sizes are 0.
|
||||||
|
@ -139,7 +137,6 @@ class FlexItemSizingProperties extends PureComponent {
|
||||||
const computedFlexGrow = computedStyle.flexGrow;
|
const computedFlexGrow = computedStyle.flexGrow;
|
||||||
const definedFlexShrink = properties["flex-shrink"];
|
const definedFlexShrink = properties["flex-shrink"];
|
||||||
const computedFlexShrink = computedStyle.flexShrink;
|
const computedFlexShrink = computedStyle.flexShrink;
|
||||||
const wasClamped = clampState !== "unclamped";
|
|
||||||
|
|
||||||
const reasons = [];
|
const reasons = [];
|
||||||
|
|
||||||
|
@ -159,35 +156,15 @@ class FlexItemSizingProperties extends PureComponent {
|
||||||
|
|
||||||
let property = null;
|
let property = null;
|
||||||
|
|
||||||
if (grew) {
|
if (grew && definedFlexGrow && computedFlexGrow) {
|
||||||
// If the item grew.
|
// If the item grew it's normally because it was set to grow (flex-grow is non 0).
|
||||||
if (definedFlexGrow) {
|
property = this.renderCssProperty("flex-grow", definedFlexGrow);
|
||||||
// It's normally because it was set to grow (flex-grow is non 0).
|
} else if (shrank && definedFlexShrink && computedFlexShrink) {
|
||||||
property = this.renderCssProperty("flex-grow", definedFlexGrow);
|
// If the item shrank it's either because flex-shrink is non 0.
|
||||||
}
|
property = this.renderCssProperty("flex-shrink", definedFlexShrink);
|
||||||
|
} else if (shrank && computedFlexShrink) {
|
||||||
if (wasClamped && clampState === "clamped_to_max") {
|
// Or also because it's default value is 1 anyway.
|
||||||
// It may have wanted to grow more than it did, because it was later max-clamped.
|
property = this.renderCssProperty("flex-shrink", computedFlexShrink, true);
|
||||||
reasons.push(getStr("flexbox.itemSizing.growthAttemptButMaxClamped"));
|
|
||||||
} else if (wasClamped && clampState === "clamped_to_min") {
|
|
||||||
// Or it may have wanted to grow less, but was later min-clamped to a larger size.
|
|
||||||
reasons.push(getStr("flexbox.itemSizing.growthAttemptButMinClamped"));
|
|
||||||
}
|
|
||||||
} else if (shrank) {
|
|
||||||
// If the item shrank.
|
|
||||||
if (definedFlexShrink && computedFlexShrink) {
|
|
||||||
// It's either because flex-shrink is non 0.
|
|
||||||
property = this.renderCssProperty("flex-shrink", definedFlexShrink);
|
|
||||||
} else if (computedFlexShrink) {
|
|
||||||
// Or also because it's default value is 1 anyway.
|
|
||||||
property = this.renderCssProperty("flex-shrink", computedFlexShrink, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wasClamped) {
|
|
||||||
// It might have wanted to shrink more (to accomodate all items) but couldn't
|
|
||||||
// because it was later min-clamped.
|
|
||||||
reasons.push(getStr("flexbox.itemSizing.shrinkAttemptWhenClamped"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't display the section at all if there's nothing useful to show users.
|
// Don't display the section at all if there's nothing useful to show users.
|
||||||
|
@ -208,14 +185,24 @@ class FlexItemSizingProperties extends PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMinimumSizeSection({ clampState, mainMinSize }, properties, dimension) {
|
renderMinimumSizeSection(flexItemSizing, properties, dimension) {
|
||||||
|
const { clampState, mainMinSize, mainDeltaSize } = flexItemSizing;
|
||||||
|
const grew = mainDeltaSize > 0;
|
||||||
|
const shrank = mainDeltaSize < 0;
|
||||||
|
const minDimensionValue = properties[`min-${dimension}`];
|
||||||
|
|
||||||
// We only display the minimum size when the item actually violates that size during
|
// We only display the minimum size when the item actually violates that size during
|
||||||
// layout & is clamped.
|
// layout & is clamped.
|
||||||
if (clampState !== "clamped_to_min") {
|
if (clampState !== "clamped_to_min") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const minDimensionValue = properties[`min-${dimension}`];
|
const reasons = [];
|
||||||
|
if (grew || shrank) {
|
||||||
|
// The item may have wanted to grow less, but was min-clamped to a larger size.
|
||||||
|
// Or the item may have wanted to shrink more but was min-clamped to a larger size.
|
||||||
|
reasons.push(getStr("flexbox.itemSizing.clampedToMin"));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
dom.li({ className: "section min" },
|
dom.li({ className: "section min" },
|
||||||
|
@ -223,17 +210,26 @@ class FlexItemSizingProperties extends PureComponent {
|
||||||
getStr("flexbox.itemSizing.minSizeSectionHeader"),
|
getStr("flexbox.itemSizing.minSizeSectionHeader"),
|
||||||
this.renderCssProperty(`min-${dimension}`, minDimensionValue)
|
this.renderCssProperty(`min-${dimension}`, minDimensionValue)
|
||||||
),
|
),
|
||||||
this.renderSize(mainMinSize)
|
this.renderSize(mainMinSize),
|
||||||
|
this.renderReasons(reasons)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMaximumSizeSection({ clampState, mainMaxSize }, properties, dimension) {
|
renderMaximumSizeSection(flexItemSizing, properties, dimension) {
|
||||||
|
const { clampState, mainMaxSize, mainDeltaSize } = flexItemSizing;
|
||||||
|
const grew = mainDeltaSize > 0;
|
||||||
|
const maxDimensionValue = properties[`max-${dimension}`];
|
||||||
|
|
||||||
if (clampState !== "clamped_to_max") {
|
if (clampState !== "clamped_to_max") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxDimensionValue = properties[`max-${dimension}`];
|
const reasons = [];
|
||||||
|
if (grew) {
|
||||||
|
// The item may have wanted to grow more than it did, because it was max-clamped.
|
||||||
|
reasons.push(getStr("flexbox.itemSizing.clampedToMax"));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
dom.li({ className: "section max" },
|
dom.li({ className: "section max" },
|
||||||
|
@ -241,7 +237,8 @@ class FlexItemSizingProperties extends PureComponent {
|
||||||
getStr("flexbox.itemSizing.maxSizeSectionHeader"),
|
getStr("flexbox.itemSizing.maxSizeSectionHeader"),
|
||||||
this.renderCssProperty(`max-${dimension}`, maxDimensionValue)
|
this.renderCssProperty(`max-${dimension}`, maxDimensionValue)
|
||||||
),
|
),
|
||||||
this.renderSize(mainMaxSize)
|
this.renderSize(mainMaxSize),
|
||||||
|
this.renderReasons(reasons)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,6 @@ add_task(async function() {
|
||||||
const allSections = [...flexSizingContainer.querySelectorAll(".section .name")];
|
const allSections = [...flexSizingContainer.querySelectorAll(".section .name")];
|
||||||
|
|
||||||
is(allSections.length, 2, "There are 2 parts in the sizing section");
|
is(allSections.length, 2, "There are 2 parts in the sizing section");
|
||||||
is(allSections[0].textContent, "Base Size", "The first part is the base size");
|
is(allSections[0].textContent, "Content Size", "The first part is the content size");
|
||||||
is(allSections[1].textContent, "Final Size", "The second part is the final size");
|
is(allSections[1].textContent, "Final Size", "The second part is the final size");
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,9 +27,9 @@ add_task(async function() {
|
||||||
ok(outlineContainer.style.gridTemplateColumns.includes("[final-end max]"),
|
ok(outlineContainer.style.gridTemplateColumns.includes("[final-end max]"),
|
||||||
"The final and max points are at the same position");
|
"The final and max points are at the same position");
|
||||||
|
|
||||||
info("Check that the flexibility sizing section displays the right info");
|
info("Check that the maximum sizing section displays the right info");
|
||||||
const reasons = [...sizingContainer.querySelectorAll(".reasons li")];
|
const reasons = [...sizingContainer.querySelectorAll(".reasons li")];
|
||||||
const expectedReason = getStr("flexbox.itemSizing.growthAttemptButMaxClamped");
|
const expectedReason = getStr("flexbox.itemSizing.clampedToMax");
|
||||||
ok(reasons.some(r => r.textContent === expectedReason),
|
ok(reasons.some(r => r.textContent === expectedReason),
|
||||||
"The 'wanted to grow but was clamped' reason was found");
|
"The clampedToMax reason was found");
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,13 +25,6 @@ flexbox.noFlexboxeOnThisPage=Select a Flex container or item to continue.
|
||||||
# properties in the Flexbox panel.
|
# properties in the Flexbox panel.
|
||||||
flexbox.flexContainerProperties=Flex Container Properties
|
flexbox.flexContainerProperties=Flex Container Properties
|
||||||
|
|
||||||
# LOCALIZATION NOTE (flexbox.contentWidth, flexbox.contentHeight, flexbox.finalWidth,
|
|
||||||
# flexbox.finalHeight): Labels for the flex item sizing properties in the Flexbox panel.
|
|
||||||
flexbox.contentWidth=Content width:
|
|
||||||
flexbox.contentHeight=Content height:
|
|
||||||
flexbox.finalWidth=Final width:
|
|
||||||
flexbox.finalHeight=Final height:
|
|
||||||
|
|
||||||
# LOCALIZATION NOTE (flexbox.itemSizing.baseSizeSectionHeader): Header label displayed
|
# LOCALIZATION NOTE (flexbox.itemSizing.baseSizeSectionHeader): Header label displayed
|
||||||
# at the start of the flex item sizing Base Size section.
|
# at the start of the flex item sizing Base Size section.
|
||||||
flexbox.itemSizing.baseSizeSectionHeader=Base Size
|
flexbox.itemSizing.baseSizeSectionHeader=Base Size
|
||||||
|
@ -52,36 +45,26 @@ flexbox.itemSizing.maxSizeSectionHeader=Maximum Size
|
||||||
# the start of the flex item sizing Final Size section.
|
# the start of the flex item sizing Final Size section.
|
||||||
flexbox.itemSizing.finalSizeSectionHeader=Final Size
|
flexbox.itemSizing.finalSizeSectionHeader=Final Size
|
||||||
|
|
||||||
# LOCALIZATION NOTE (flexbox.itemSizing.itemBaseSizeFromContent): Label shown in the flex
|
# LOCALIZATION NOTE (flexbox.itemSizing.itemContentSize): Label shown in the flex item
|
||||||
# item sizing panel. It tells users that a given item’s base size was calculated from its
|
# sizing panel. It tells users that a given item’s base size was calculated from its
|
||||||
# content size when unconstrained.
|
# content size when unconstrained.
|
||||||
flexbox.itemSizing.itemBaseSizeFromContent=The item’s content size when unconstrained.
|
flexbox.itemSizing.itemContentSize=Content Size
|
||||||
|
|
||||||
# LOCALIZATION NOTE (flexbox.itemSizing.itemMinSizeFromItemMinContent): Label shown in the
|
# LOCALIZATION NOTE (flexbox.itemSizing.clampedToMax): Label shown in the flexbox item
|
||||||
# flex item sizing panel. It tells users that a given item’s minimum size is coming from
|
# sizing panel. It tells users that a given item attempted to grow but ended up being
|
||||||
# its min-content size.
|
# clamped to a smaller max size.
|
||||||
flexbox.itemSizing.itemMinSizeFromItemMinContent=This is the element’s minimum content size.
|
|
||||||
|
|
||||||
# LOCALIZATION NOTE (flexbox.itemSizing.growthAttemptButMaxClamped): Label shown in the
|
|
||||||
# flexbox item sizing panel. It tells users that a given item attempted to grow by a
|
|
||||||
# certain amount but ended up being clamped to a smaller max size.
|
|
||||||
# (Note that clamp is a common word in flexbox terminology. It refers to constraining an
|
# (Note that clamp is a common word in flexbox terminology. It refers to constraining an
|
||||||
# item's size to some defined min/max-width/height set on the element, even though there
|
# item's size to some defined min/max-width/height set on the element, even though there
|
||||||
# might have been room for it to grow, or reason for it to shrink more).
|
# might have been room for it to grow, or reason for it to shrink more).
|
||||||
flexbox.itemSizing.growthAttemptButMaxClamped=The item wanted to grow more, but it was clamped to its maximum size.
|
flexbox.itemSizing.clampedToMax=The item was clamped to its maximum size.
|
||||||
|
|
||||||
# LOCALIZATION NOTE (flexbox.itemSizing.growthAttemptButMinClamped): Label shown in the
|
# LOCALIZATION NOTE (flexbox.itemSizing.clampedToMin): Label shown in the flexbox item
|
||||||
# flexbox item sizing panel. It tells users that a given item attempted to grow by a
|
# sizing panel. It tells users that a given item attempted to grow but ended up being
|
||||||
# certain amount but ended up being clamped to a larger min size.
|
# clamped to a larger min size.
|
||||||
# (Note that clamp is a common word in flexbox terminology. It refers to constraining an
|
# (Note that clamp is a common word in flexbox terminology. It refers to constraining an
|
||||||
# item's size to some defined min/max-width/height set on the element, even though there
|
# item's size to some defined min/max-width/height set on the element, even though there
|
||||||
# might have been room for it to grow, or reason for it to shrink more).
|
# might have been room for it to grow, or reason for it to shrink more).
|
||||||
flexbox.itemSizing.growthAttemptButMinClamped=The item wanted to grow less, but it was clamped to its minimum size.
|
flexbox.itemSizing.clampedToMin=The item was clamped to its minimum size.
|
||||||
|
|
||||||
# LOCALIZATION NOTE (flexbox.itemSizing.shrinkAttemptWhenClamped): Label shown in the
|
|
||||||
# flexbox item sizing panel. It tells users that a given item attempted to shrink by a
|
|
||||||
# certain amount but ended up being clamped by a min size.
|
|
||||||
flexbox.itemSizing.shrinkAttemptWhenClamped=The item wanted to shrink, but it was clamped.
|
|
||||||
|
|
||||||
# LOCALIZATION NOTE (flexbox.itemSizing.setToGrow): Label shown in the flex item sizing
|
# LOCALIZATION NOTE (flexbox.itemSizing.setToGrow): Label shown in the flex item sizing
|
||||||
# panel. It tells users that a given item was set to grow.
|
# panel. It tells users that a given item was set to grow.
|
||||||
|
|
|
@ -43,14 +43,16 @@ function waitUntilState(store, predicate) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait until a particular action has been emitted by the store.
|
* Wait until a particular action has been emitted by the store.
|
||||||
* @param Store store
|
* @param {Store} store
|
||||||
* The Redux store being used.
|
* The Redux store being used.
|
||||||
* @param string actionType
|
* @param {String} actionType
|
||||||
* The expected action to wait for.
|
* The expected action to wait for.
|
||||||
|
* @param {Number} count
|
||||||
|
* Number of times to expect the action to occur. Default is once.
|
||||||
* @return Promise
|
* @return Promise
|
||||||
* Resolved once the expected action is emitted by the store.
|
* Resolved once the expected action is emitted by the store.
|
||||||
*/
|
*/
|
||||||
function waitUntilAction(store, actionType) {
|
function waitUntilAction(store, actionType, count = 1) {
|
||||||
const deferred = defer();
|
const deferred = defer();
|
||||||
const unsubscribe = store.subscribe(check);
|
const unsubscribe = store.subscribe(check);
|
||||||
const history = store.history;
|
const history = store.history;
|
||||||
|
@ -61,8 +63,11 @@ function waitUntilAction(store, actionType) {
|
||||||
const action = history[index++];
|
const action = history[index++];
|
||||||
if (action && action.type === actionType) {
|
if (action && action.type === actionType) {
|
||||||
info(`Found action "${actionType}"`);
|
info(`Found action "${actionType}"`);
|
||||||
unsubscribe();
|
count--;
|
||||||
deferred.resolve(store.getState());
|
if (count === 0) {
|
||||||
|
unsubscribe();
|
||||||
|
deferred.resolve(store.getState());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,8 @@
|
||||||
padding-left: calc(var(--diff-level-offset) * 4);
|
padding-left: calc(var(--diff-level-offset) * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar-panel-changes .rule .selector,
|
#sidebar-panel-changes .rule .selector:not(.diff-remove):not(.diff-add),
|
||||||
#sidebar-panel-changes .rule .bracket-close {
|
#sidebar-panel-changes .rule .bracket-close:not(.diff-remove):not(.diff-add) {
|
||||||
margin-left: calc(-1 * var(--diff-level-offset) + 5px);
|
margin-left: calc(-1 * var(--diff-level-offset) + 5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,10 +164,11 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
// Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
|
// Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
|
||||||
this.clearCache();
|
this.clearCache();
|
||||||
|
|
||||||
|
this.axes = null;
|
||||||
|
this.crossAxisDirection = null;
|
||||||
this.flexData = null;
|
this.flexData = null;
|
||||||
this.mainAxisDirection = null;
|
this.mainAxisDirection = null;
|
||||||
this.crossAxisDirection = null;
|
this.transform = null;
|
||||||
this.axes = null;
|
|
||||||
|
|
||||||
AutoRefreshHighlighter.prototype.destroy.call(this);
|
AutoRefreshHighlighter.prototype.destroy.call(this);
|
||||||
}
|
}
|
||||||
|
@ -337,6 +338,10 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
this.justifyContentValue = this.computedStyle.justifyContent;
|
this.justifyContentValue = this.computedStyle.justifyContent;
|
||||||
const newJustifyContent = this.justifyContentValue;
|
const newJustifyContent = this.justifyContentValue;
|
||||||
|
|
||||||
|
const oldTransform = this.transformValue;
|
||||||
|
this.transformValue = this.computedStyle.transform;
|
||||||
|
const newTransform = this.transformValue;
|
||||||
|
|
||||||
return hasMoved ||
|
return hasMoved ||
|
||||||
hasFlexDataChanged ||
|
hasFlexDataChanged ||
|
||||||
oldAlignItems !== newAlignItems ||
|
oldAlignItems !== newAlignItems ||
|
||||||
|
@ -344,7 +349,8 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
oldFlexWrap !== newFlexWrap ||
|
oldFlexWrap !== newFlexWrap ||
|
||||||
oldJustifyContent !== newJustifyContent ||
|
oldJustifyContent !== newJustifyContent ||
|
||||||
oldCrossAxisDirection !== newCrossAxisDirection ||
|
oldCrossAxisDirection !== newCrossAxisDirection ||
|
||||||
oldMainAxisDirection !== newMainAxisDirection;
|
oldMainAxisDirection !== newMainAxisDirection ||
|
||||||
|
oldTransform !== newTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hide() {
|
_hide() {
|
||||||
|
@ -530,8 +536,8 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
this.ctx.strokeStyle = this.color;
|
this.ctx.strokeStyle = this.color;
|
||||||
this.ctx.fillStyle = this.getFlexContainerPattern(devicePixelRatio);
|
this.ctx.fillStyle = this.getFlexContainerPattern(devicePixelRatio);
|
||||||
|
|
||||||
const { bounds } = this.currentQuads.content[0];
|
const { clientWidth, clientHeight } = this.currentNode;
|
||||||
drawRect(this.ctx, 0, 0, bounds.width, bounds.height, this.currentMatrix);
|
drawRect(this.ctx, 0, 0, clientWidth, clientHeight, this.currentMatrix);
|
||||||
|
|
||||||
// Find current angle of outer flex element by measuring the angle of two arbitrary
|
// Find current angle of outer flex element by measuring the angle of two arbitrary
|
||||||
// points, then rotate canvas, so the hash pattern stays 45deg to the boundary.
|
// points, then rotate canvas, so the hash pattern stays 45deg to the boundary.
|
||||||
|
@ -556,6 +562,7 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
const zoom = getCurrentZoom(this.win);
|
const zoom = getCurrentZoom(this.win);
|
||||||
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
||||||
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
||||||
|
const containerOffsets = getNodeRect(this.currentNode);
|
||||||
|
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
this.ctx.translate(offset - canvasX, offset - canvasY);
|
this.ctx.translate(offset - canvasX, offset - canvasY);
|
||||||
|
@ -563,21 +570,18 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
this.ctx.lineWidth = lineWidth;
|
this.ctx.lineWidth = lineWidth;
|
||||||
this.ctx.strokeStyle = this.color;
|
this.ctx.strokeStyle = this.color;
|
||||||
|
|
||||||
const { bounds } = this.currentQuads.content[0];
|
|
||||||
|
|
||||||
for (const flexLine of this.flexData.lines) {
|
for (const flexLine of this.flexData.lines) {
|
||||||
for (const flexItem of flexLine.items) {
|
for (const flexItem of flexLine.items) {
|
||||||
const quads = flexItem.quads;
|
const offsets = getNodeRect(flexItem.node);
|
||||||
if (!quads.length) {
|
|
||||||
|
if (!offsets) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the flex item bounds relative to the current quads.
|
const left = offsets.left - containerOffsets.left;
|
||||||
const { bounds: flexItemBounds } = quads[0];
|
const top = offsets.top - containerOffsets.top;
|
||||||
const left = Math.round(flexItemBounds.left / zoom - bounds.left);
|
const right = offsets.right - containerOffsets.left;
|
||||||
const top = Math.round(flexItemBounds.top / zoom - bounds.top);
|
const bottom = offsets.bottom - containerOffsets.top;
|
||||||
const right = Math.round(flexItemBounds.right / zoom - bounds.left);
|
|
||||||
const bottom = Math.round(flexItemBounds.bottom / zoom - bounds.top);
|
|
||||||
|
|
||||||
clearRect(this.ctx, left, top, right, bottom, this.currentMatrix);
|
clearRect(this.ctx, left, top, right, bottom, this.currentMatrix);
|
||||||
drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
|
drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
|
||||||
|
@ -599,15 +603,14 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
const zoom = getCurrentZoom(this.win);
|
const zoom = getCurrentZoom(this.win);
|
||||||
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
||||||
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
||||||
|
const { clientWidth, clientHeight } = this.currentNode;
|
||||||
|
const options = { matrix: this.currentMatrix };
|
||||||
|
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
this.ctx.translate(offset - canvasX, offset - canvasY);
|
this.ctx.translate(offset - canvasX, offset - canvasY);
|
||||||
this.ctx.lineWidth = lineWidth;
|
this.ctx.lineWidth = lineWidth;
|
||||||
this.ctx.strokeStyle = this.color;
|
this.ctx.strokeStyle = this.color;
|
||||||
|
|
||||||
const { bounds } = this.currentQuads.content[0];
|
|
||||||
const options = { matrix: this.currentMatrix };
|
|
||||||
|
|
||||||
for (const flexLine of this.flexData.lines) {
|
for (const flexLine of this.flexData.lines) {
|
||||||
const { crossStart, crossSize } = flexLine;
|
const { crossStart, crossSize } = flexLine;
|
||||||
|
|
||||||
|
@ -616,56 +619,56 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
case "horizontal-lr vertical-bt":
|
case "horizontal-lr vertical-bt":
|
||||||
case "horizontal-rl vertical-tb":
|
case "horizontal-rl vertical-tb":
|
||||||
case "horizontal-rl vertical-bt":
|
case "horizontal-rl vertical-bt":
|
||||||
clearRect(this.ctx, 0, crossStart, bounds.width, crossStart + crossSize,
|
clearRect(this.ctx, 0, crossStart, clientWidth, crossStart + crossSize,
|
||||||
this.currentMatrix);
|
this.currentMatrix);
|
||||||
|
|
||||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||||
if (crossStart != 0) {
|
if (crossStart != 0) {
|
||||||
drawLine(this.ctx, 0, crossStart, bounds.width, crossStart, options);
|
drawLine(this.ctx, 0, crossStart, clientWidth, crossStart, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid drawing the end flex line when they overlap with the flex container.
|
// Avoid drawing the end flex line when they overlap with the flex container.
|
||||||
if (bounds.height - crossStart - crossSize >= lineWidth) {
|
if (clientHeight - crossStart - crossSize >= lineWidth) {
|
||||||
drawLine(this.ctx, 0, crossStart + crossSize, bounds.width,
|
drawLine(this.ctx, 0, crossStart + crossSize, clientWidth,
|
||||||
crossStart + crossSize, options);
|
crossStart + crossSize, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "vertical-tb horizontal-lr":
|
case "vertical-tb horizontal-lr":
|
||||||
case "vertical-bt horizontal-rl":
|
case "vertical-bt horizontal-rl":
|
||||||
clearRect(this.ctx, crossStart, 0, crossStart + crossSize, bounds.height,
|
clearRect(this.ctx, crossStart, 0, crossStart + crossSize, clientHeight,
|
||||||
this.currentMatrix);
|
this.currentMatrix);
|
||||||
|
|
||||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||||
if (crossStart != 0) {
|
if (crossStart != 0) {
|
||||||
drawLine(this.ctx, crossStart, 0, crossStart, bounds.height, options);
|
drawLine(this.ctx, crossStart, 0, crossStart, clientHeight, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid drawing the end flex line when they overlap with the flex container.
|
// Avoid drawing the end flex line when they overlap with the flex container.
|
||||||
if (bounds.width - crossStart - crossSize >= lineWidth) {
|
if (clientWidth - crossStart - crossSize >= lineWidth) {
|
||||||
drawLine(this.ctx, crossStart + crossSize, 0, crossStart + crossSize,
|
drawLine(this.ctx, crossStart + crossSize, 0, crossStart + crossSize,
|
||||||
bounds.height, options);
|
clientHeight, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "vertical-bt horizontal-lr":
|
case "vertical-bt horizontal-lr":
|
||||||
case "vertical-tb horizontal-rl":
|
case "vertical-tb horizontal-rl":
|
||||||
clearRect(this.ctx, bounds.width - crossStart, 0,
|
clearRect(this.ctx, clientWidth - crossStart, 0,
|
||||||
bounds.width - crossStart - crossSize, bounds.height, this.currentMatrix);
|
clientWidth - crossStart - crossSize, clientHeight, this.currentMatrix);
|
||||||
|
|
||||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||||
if (crossStart != 0) {
|
if (crossStart != 0) {
|
||||||
drawLine(this.ctx, bounds.width - crossStart, 0, bounds.width - crossStart,
|
drawLine(this.ctx, clientWidth - crossStart, 0, clientWidth - crossStart,
|
||||||
bounds.height, options);
|
clientHeight, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid drawing the end flex line when they overlap with the flex container.
|
// Avoid drawing the end flex line when they overlap with the flex container.
|
||||||
if (bounds.width - crossStart - crossSize >= lineWidth) {
|
if (clientWidth - crossStart - crossSize >= lineWidth) {
|
||||||
drawLine(this.ctx, bounds.width - crossStart - crossSize, 0,
|
drawLine(this.ctx, clientWidth - crossStart - crossSize, 0,
|
||||||
bounds.width - crossStart - crossSize, bounds.height, options);
|
clientWidth - crossStart - crossSize, clientHeight, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -683,27 +686,27 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoom = getCurrentZoom(this.win);
|
const lineWidth = getDisplayPixelRatio(this.win);
|
||||||
const { bounds } = this.currentQuads.content[0];
|
const { clientWidth, clientHeight } = this.currentNode;
|
||||||
|
const containerOffsets = getNodeRect(this.currentNode);
|
||||||
|
|
||||||
// Draw a justify content pattern over the whole flex container.
|
// Draw a justify content pattern over the whole flex container.
|
||||||
this.drawJustifyContent(0, 0, bounds.width, bounds.height, this.currentMatrix);
|
this.drawJustifyContent(0, 0, clientWidth, clientHeight);
|
||||||
|
|
||||||
for (const flexLine of this.flexData.lines) {
|
for (const flexLine of this.flexData.lines) {
|
||||||
const { crossStart, crossSize } = flexLine;
|
const { crossStart, crossSize } = flexLine;
|
||||||
|
|
||||||
for (const flexItem of flexLine.items) {
|
for (const flexItem of flexLine.items) {
|
||||||
const quads = flexItem.quads;
|
const offsets = getNodeRect(flexItem.node);
|
||||||
if (!quads.length) {
|
|
||||||
|
if (!offsets) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the flex item bounds relative to the current quads.
|
const left = offsets.left - containerOffsets.left;
|
||||||
const { bounds: flexItemBounds } = quads[0];
|
const top = offsets.top - containerOffsets.top;
|
||||||
const left = Math.round(flexItemBounds.left / zoom - bounds.left);
|
const right = offsets.right - containerOffsets.left;
|
||||||
const top = Math.round(flexItemBounds.top / zoom - bounds.top);
|
const bottom = offsets.bottom - containerOffsets.top;
|
||||||
const right = Math.round(flexItemBounds.right / zoom - bounds.left);
|
|
||||||
const bottom = Math.round(flexItemBounds.bottom / zoom - bounds.top);
|
|
||||||
|
|
||||||
// Clear a rectangular are covering the alignment container.
|
// Clear a rectangular are covering the alignment container.
|
||||||
switch (this.axes) {
|
switch (this.axes) {
|
||||||
|
@ -711,18 +714,22 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
case "horizontal-lr vertical-bt":
|
case "horizontal-lr vertical-bt":
|
||||||
case "horizontal-rl vertical-tb":
|
case "horizontal-rl vertical-tb":
|
||||||
case "horizontal-rl vertical-bt":
|
case "horizontal-rl vertical-bt":
|
||||||
clearRect(this.ctx, left, Math.round(crossStart) + 2, right,
|
clearRect(this.ctx,
|
||||||
Math.round(crossStart + crossSize) - 2, this.currentMatrix);
|
left, Math.round(crossStart) + 2 * lineWidth, right,
|
||||||
|
Math.round(crossStart + crossSize) - 2 * lineWidth, this.currentMatrix);
|
||||||
break;
|
break;
|
||||||
case "vertical-tb horizontal-lr":
|
case "vertical-tb horizontal-lr":
|
||||||
case "vertical-bt horizontal-rl":
|
case "vertical-bt horizontal-rl":
|
||||||
clearRect(this.ctx, Math.round(crossStart) + 1, top,
|
clearRect(this.ctx,
|
||||||
Math.round(crossStart + crossSize), bottom, this.currentMatrix);
|
Math.round(crossStart) + lineWidth * 2, top,
|
||||||
|
Math.round(crossStart + crossSize) - lineWidth, bottom, this.currentMatrix);
|
||||||
break;
|
break;
|
||||||
case "vertical-bt horizontal-lr":
|
case "vertical-bt horizontal-lr":
|
||||||
case "vertical-tb horizontal-rl":
|
case "vertical-tb horizontal-rl":
|
||||||
clearRect(this.ctx, Math.round(bounds.width - crossStart - crossSize) + 1,
|
clearRect(this.ctx,
|
||||||
top, Math.round(bounds.width - crossStart), bottom, this.currentMatrix);
|
Math.round(clientWidth - crossStart - crossSize) + lineWidth * 2, top,
|
||||||
|
Math.round(clientWidth - crossStart) - lineWidth, bottom,
|
||||||
|
this.currentMatrix);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -888,4 +895,55 @@ function compareFlexData(oldFlexData, newFlexData) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the untransformed coordinates for a node.
|
||||||
|
*
|
||||||
|
* @param {DOMNode} node
|
||||||
|
* The node for which the coordinates are to be returned.
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
* {
|
||||||
|
* left: left, // The absolute left coordinates of the node.
|
||||||
|
* top: top, // The absolute top coordinates of the node.
|
||||||
|
* right: right, // The absolute right coordinates of the node.
|
||||||
|
* bottom: bottom, // The absolute left coordinates of the node.
|
||||||
|
* width: width, // The width of the node.
|
||||||
|
* height, // The Height of the node.
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
function getNodeRect(node) {
|
||||||
|
if (node.nodeType === node.TEXT_NODE) {
|
||||||
|
// For now ignore text node flex items because we cannot get the
|
||||||
|
// untransformed position and dimensions of a text node
|
||||||
|
// (see https://bugzil.la/1505079).
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const win = node.ownerGlobal;
|
||||||
|
const style = win.getComputedStyle(node);
|
||||||
|
const borderLeft = parseInt(style.borderLeftWidth, 10) || 0;
|
||||||
|
const borderTop = parseInt(style.borderTopWidth, 10) || 0;
|
||||||
|
const width = node.offsetWidth;
|
||||||
|
const height = node.offsetHeight;
|
||||||
|
|
||||||
|
let left = 0;
|
||||||
|
let top = 0;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
left += node.offsetLeft - node.scrollLeft + node.clientLeft;
|
||||||
|
top += node.offsetTop - node.scrollTop + node.clientTop;
|
||||||
|
|
||||||
|
node = node.offsetParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
left: left - borderLeft,
|
||||||
|
top: top - borderTop,
|
||||||
|
right: left + width - borderLeft,
|
||||||
|
bottom: top + height - borderTop,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
exports.FlexboxHighlighter = FlexboxHighlighter;
|
exports.FlexboxHighlighter = FlexboxHighlighter;
|
||||||
|
|
|
@ -1071,6 +1071,72 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
||||||
return ancestors;
|
return ancestors;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an object with information about this rule used for tracking changes.
|
||||||
|
* It will be decorated with information about a CSS change before being tracked.
|
||||||
|
*
|
||||||
|
* It contains:
|
||||||
|
* - the rule selector (or generated selectror for inline styles)
|
||||||
|
* - the rule's host stylesheet (or element for inline styles)
|
||||||
|
* - the rule's ancestor rules (@media, @supports, @keyframes), if any
|
||||||
|
* - the rule's position within its ancestor tree, if any
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
get metadata() {
|
||||||
|
const data = {};
|
||||||
|
// Collect information about the rule's ancestors (@media, @supports, @keyframes).
|
||||||
|
// Used to show context for this change in the UI and to match the rule for undo/redo.
|
||||||
|
data.ancestors = this.ancestorRules.map(rule => {
|
||||||
|
return {
|
||||||
|
// Rule type as number defined by CSSRule.type (ex: 4, 7, 12)
|
||||||
|
// @see https://developer.mozilla.org/en-US/docs/Web/API/CSSRule
|
||||||
|
type: rule.rawRule.type,
|
||||||
|
// Rule type as human-readable string (ex: "@media", "@supports", "@keyframes")
|
||||||
|
typeName: CSSRuleTypeName[rule.rawRule.type],
|
||||||
|
// Conditions of @media and @supports rules (ex: "min-width: 1em")
|
||||||
|
conditionText: rule.rawRule.conditionText,
|
||||||
|
// Name of @keyframes rule; refrenced by the animation-name CSS property.
|
||||||
|
name: rule.rawRule.name,
|
||||||
|
// Selector of individual @keyframe rule within a @keyframes rule (ex: 0%, 100%).
|
||||||
|
keyText: rule.rawRule.keyText,
|
||||||
|
// Array with the indexes of this rule and its ancestors within the CSS rule tree.
|
||||||
|
ruleIndex: rule._ruleIndex,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// For changes in element style attributes, generate a unique selector.
|
||||||
|
if (this.type === ELEMENT_STYLE) {
|
||||||
|
// findCssSelector() fails on XUL documents. Catch and silently ignore that error.
|
||||||
|
try {
|
||||||
|
data.selector = findCssSelector(this.rawNode);
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
data.source = {
|
||||||
|
type: "element",
|
||||||
|
// Used to differentiate between elements which match the same generated selector
|
||||||
|
// but live in different documents (ex: host document and iframe).
|
||||||
|
href: this.rawNode.baseURI,
|
||||||
|
// Element style attributes don't have a rule index; use the generated selector.
|
||||||
|
index: data.selector,
|
||||||
|
};
|
||||||
|
data.ruleIndex = 0;
|
||||||
|
} else {
|
||||||
|
data.selector = (this.type === CSSRule.KEYFRAME_RULE)
|
||||||
|
? this.rawRule.keyText
|
||||||
|
: this.rawRule.selectorText;
|
||||||
|
data.source = {
|
||||||
|
type: "stylesheet",
|
||||||
|
href: this.sheetActor.href,
|
||||||
|
index: this.sheetActor.styleSheetIndex,
|
||||||
|
};
|
||||||
|
// Used to differentiate between changes to rules with identical selectors.
|
||||||
|
data.ruleIndex = this._ruleIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
|
||||||
getDocument: function(sheet) {
|
getDocument: function(sheet) {
|
||||||
if (sheet.ownerNode) {
|
if (sheet.ownerNode) {
|
||||||
return sheet.ownerNode.nodeType == sheet.ownerNode.DOCUMENT_NODE ?
|
return sheet.ownerNode.nodeType == sheet.ownerNode.DOCUMENT_NODE ?
|
||||||
|
@ -1346,7 +1412,7 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the changes before applying them so we have access to the previous values.
|
// Log the changes before applying them so we have access to the previous values.
|
||||||
modifications.map(mod => this.logChange(mod));
|
modifications.map(mod => this.logDeclarationChange(mod));
|
||||||
|
|
||||||
if (this.type === ELEMENT_STYLE) {
|
if (this.type === ELEMENT_STYLE) {
|
||||||
// For element style rules, set the node's style attribute.
|
// For element style rules, set the node's style attribute.
|
||||||
|
@ -1406,7 +1472,7 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
||||||
const tempElement = document.createElementNS(XHTML_NS, "div");
|
const tempElement = document.createElementNS(XHTML_NS, "div");
|
||||||
|
|
||||||
for (const mod of modifications) {
|
for (const mod of modifications) {
|
||||||
this.logChange(mod);
|
this.logDeclarationChange(mod);
|
||||||
if (mod.type === "set") {
|
if (mod.type === "set") {
|
||||||
tempElement.style.setProperty(mod.name, mod.value, mod.priority || "");
|
tempElement.style.setProperty(mod.name, mod.value, mod.priority || "");
|
||||||
this.rawStyle.setProperty(mod.name,
|
this.rawStyle.setProperty(mod.name,
|
||||||
|
@ -1482,13 +1548,13 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take an object with instructions to modify a CSS declaration and emit a
|
* Take an object with instructions to modify a CSS declaration and log an object with
|
||||||
* "track-change" event with normalized metadata which describes the change.
|
* normalized metadata which describes the change in the context of this rule.
|
||||||
*
|
*
|
||||||
* @param {Object} change
|
* @param {Object} change
|
||||||
* Data about a modification to a rule. @see |modifyProperties()|
|
* Data about a modification to a rule. @see |modifyProperties()|
|
||||||
*/
|
*/
|
||||||
logChange(change) {
|
logDeclarationChange(change) {
|
||||||
// Destructure properties from the previous CSS declaration at this index, if any,
|
// Destructure properties from the previous CSS declaration at this index, if any,
|
||||||
// to new variable names to indicate the previous state.
|
// to new variable names to indicate the previous state.
|
||||||
let {
|
let {
|
||||||
|
@ -1503,59 +1569,11 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
||||||
// Append the "!important" string if defined in the previous priority flag.
|
// Append the "!important" string if defined in the previous priority flag.
|
||||||
prevValue = (prevValue && prevPriority) ? `${prevValue} !important` : prevValue;
|
prevValue = (prevValue && prevPriority) ? `${prevValue} !important` : prevValue;
|
||||||
|
|
||||||
// Metadata about a change.
|
const data = this.metadata;
|
||||||
const data = {};
|
|
||||||
// Collect information about the rule's ancestors (@media, @supports, @keyframes).
|
|
||||||
// Used to show context for this change in the UI and to match the rule for undo/redo.
|
|
||||||
data.ancestors = this.ancestorRules.map(rule => {
|
|
||||||
return {
|
|
||||||
// Rule type as number defined by CSSRule.type (ex: 4, 7, 12)
|
|
||||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/CSSRule
|
|
||||||
type: rule.rawRule.type,
|
|
||||||
// Rule type as human-readable string (ex: "@media", "@supports", "@keyframes")
|
|
||||||
typeName: CSSRuleTypeName[rule.rawRule.type],
|
|
||||||
// Conditions of @media and @supports rules (ex: "min-width: 1em")
|
|
||||||
conditionText: rule.rawRule.conditionText,
|
|
||||||
// Name of @keyframes rule; refrenced by the animation-name CSS property.
|
|
||||||
name: rule.rawRule.name,
|
|
||||||
// Selector of individual @keyframe rule within a @keyframes rule (ex: 0%, 100%).
|
|
||||||
keyText: rule.rawRule.keyText,
|
|
||||||
// Array with the indexes of this rule and its ancestors within the CSS rule tree.
|
|
||||||
ruleIndex: rule._ruleIndex,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// For changes in element style attributes, generate a unique selector.
|
|
||||||
if (this.type === ELEMENT_STYLE) {
|
|
||||||
// findCssSelector() fails on XUL documents. Catch and silently ignore that error.
|
|
||||||
try {
|
|
||||||
data.selector = findCssSelector(this.rawNode);
|
|
||||||
} catch (err) {}
|
|
||||||
|
|
||||||
data.source = {
|
|
||||||
type: "element",
|
|
||||||
// Used to differentiate between elements which match the same generated selector
|
|
||||||
// but live in different documents (ex: host document and iframe).
|
|
||||||
href: this.rawNode.baseURI,
|
|
||||||
// Element style attributes don't have a rule index; use the generated selector.
|
|
||||||
index: data.selector,
|
|
||||||
};
|
|
||||||
data.ruleIndex = 0;
|
|
||||||
} else {
|
|
||||||
data.selector = (this.type === CSSRule.KEYFRAME_RULE)
|
|
||||||
? this.rawRule.keyText
|
|
||||||
: this.rawRule.selectorText;
|
|
||||||
data.source = {
|
|
||||||
type: "stylesheet",
|
|
||||||
href: this.sheetActor.href,
|
|
||||||
index: this.sheetActor.styleSheetIndex,
|
|
||||||
};
|
|
||||||
// Used to differentiate between changes to rules with identical selectors.
|
|
||||||
data.ruleIndex = this._ruleIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (change.type) {
|
switch (change.type) {
|
||||||
case "set":
|
case "set":
|
||||||
|
data.type = prevValue ? "declaration-add" : "declaration-update";
|
||||||
// If `change.newName` is defined, use it because the property is being renamed.
|
// If `change.newName` is defined, use it because the property is being renamed.
|
||||||
// Otherwise, a new declaration is being created or the value of an existing
|
// Otherwise, a new declaration is being created or the value of an existing
|
||||||
// declaration is being updated. In that case, use the provided `change.name`.
|
// declaration is being updated. In that case, use the provided `change.name`.
|
||||||
|
@ -1566,11 +1584,11 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
||||||
// Otherwise, use the incoming value string.
|
// Otherwise, use the incoming value string.
|
||||||
const value = change.newName ? prevValue : newValue;
|
const value = change.newName ? prevValue : newValue;
|
||||||
|
|
||||||
data.add = { property: name, value };
|
data.add = { [name]: value };
|
||||||
// If there is a previous value, log its removal together with the previous
|
// If there is a previous value, log its removal together with the previous
|
||||||
// property name. Using the previous name handles the case for renaming a property
|
// property name. Using the previous name handles the case for renaming a property
|
||||||
// and is harmless when updating an existing value (the name stays the same).
|
// and is harmless when updating an existing value (the name stays the same).
|
||||||
data.remove = prevValue ? { property: prevName, value: prevValue } : null;
|
data.remove = prevValue ? { [prevName]: prevValue } : null;
|
||||||
|
|
||||||
// When toggling a declaration from OFF to ON, if not renaming the property,
|
// When toggling a declaration from OFF to ON, if not renaming the property,
|
||||||
// do not mark the previous declaration for removal, otherwise the add and
|
// do not mark the previous declaration for removal, otherwise the add and
|
||||||
|
@ -1583,14 +1601,52 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "remove":
|
case "remove":
|
||||||
|
data.type = "declaration-remove";
|
||||||
data.add = null;
|
data.add = null;
|
||||||
data.remove = { property: change.name, value: prevValue };
|
data.remove = { [change.name]: prevValue };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackChangeEmitter.trackChange(data);
|
TrackChangeEmitter.trackChange(data);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for tracking CSS changes. Logs the change of this rule's selector as
|
||||||
|
* two operations: a rule removal, including its CSS declarations, using the old
|
||||||
|
* selector and a rule addition using the new selector.
|
||||||
|
*
|
||||||
|
* @param {String} oldSelector
|
||||||
|
* This rule's previous selector.
|
||||||
|
* @param {String} newSelector
|
||||||
|
* This rule's new selector.
|
||||||
|
*/
|
||||||
|
logSelectorChange(oldSelector, newSelector) {
|
||||||
|
// Build a collection of CSS declarations existing on this rule.
|
||||||
|
const declarations = this._declarations.reduce((acc, decl) => {
|
||||||
|
acc[decl.name] = decl.priority ? decl.value + " !important" : decl.value;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// Logging two distinct operations to remove the old rule and add a new one.
|
||||||
|
// TODO: Make TrackChangeEmitter support transactions so these two operations are
|
||||||
|
// grouped together when implementing undo/redo.
|
||||||
|
TrackChangeEmitter.trackChange({
|
||||||
|
...this.metadata,
|
||||||
|
type: "rule-remove",
|
||||||
|
add: null,
|
||||||
|
remove: declarations,
|
||||||
|
selector: oldSelector,
|
||||||
|
});
|
||||||
|
|
||||||
|
TrackChangeEmitter.trackChange({
|
||||||
|
...this.metadata,
|
||||||
|
type: "rule-add",
|
||||||
|
add: declarations,
|
||||||
|
remove: null,
|
||||||
|
selector: newSelector,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls modifySelector2() which needs to be kept around for backwards compatibility.
|
* Calls modifySelector2() which needs to be kept around for backwards compatibility.
|
||||||
* TODO: Once Firefox 64 is no longer supported, inline that mehtod's content,
|
* TODO: Once Firefox 64 is no longer supported, inline that mehtod's content,
|
||||||
|
@ -1628,11 +1684,14 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
||||||
return { ruleProps: null, isMatching: true };
|
return { ruleProps: null, isMatching: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The rule's previous selector is lost after calling _addNewSelector(). Save it now.
|
||||||
|
const oldValue = this.rawRule.selectorText;
|
||||||
let selectorPromise = this._addNewSelector(value, editAuthored);
|
let selectorPromise = this._addNewSelector(value, editAuthored);
|
||||||
|
|
||||||
if (editAuthored) {
|
if (editAuthored) {
|
||||||
selectorPromise = selectorPromise.then((newCssRule) => {
|
selectorPromise = selectorPromise.then((newCssRule) => {
|
||||||
if (newCssRule) {
|
if (newCssRule) {
|
||||||
|
this.logSelectorChange(oldValue, value);
|
||||||
const style = this.pageStyle._styleRef(newCssRule);
|
const style = this.pageStyle._styleRef(newCssRule);
|
||||||
// See the comment in |form| to understand this.
|
// See the comment in |form| to understand this.
|
||||||
return style.getAuthoredCssText().then(() => newCssRule);
|
return style.getAuthoredCssText().then(() => newCssRule);
|
||||||
|
|
|
@ -85,6 +85,7 @@ var DebuggerServer = {
|
||||||
this._nextConnID = 0;
|
this._nextConnID = 0;
|
||||||
|
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
this._onSocketListenerAccepted = this._onSocketListenerAccepted.bind(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
get protocol() {
|
get protocol() {
|
||||||
|
@ -225,6 +226,7 @@ var DebuggerServer = {
|
||||||
* SocketListeners. This is called by a SocketListener after it is opened.
|
* SocketListeners. This is called by a SocketListener after it is opened.
|
||||||
*/
|
*/
|
||||||
_addListener(listener) {
|
_addListener(listener) {
|
||||||
|
listener.on("accepted", this._onSocketListenerAccepted);
|
||||||
this._listeners.push(listener);
|
this._listeners.push(listener);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -233,7 +235,16 @@ var DebuggerServer = {
|
||||||
* SocketListeners. This is called by a SocketListener after it is closed.
|
* SocketListeners. This is called by a SocketListener after it is closed.
|
||||||
*/
|
*/
|
||||||
_removeListener(listener) {
|
_removeListener(listener) {
|
||||||
|
// Remove connections that were accepted in the listener.
|
||||||
|
for (const connID of Object.getOwnPropertyNames(this._connections)) {
|
||||||
|
const connection = this._connections[connID];
|
||||||
|
if (connection.isAcceptedBy(listener)) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._listeners = this._listeners.filter(l => l !== listener);
|
this._listeners = this._listeners.filter(l => l !== listener);
|
||||||
|
listener.off("accepted", this._onSocketListenerAccepted);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -254,6 +265,10 @@ var DebuggerServer = {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onSocketListenerAccepted(transport, listener) {
|
||||||
|
this._onConnection(transport, null, false, listener);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new connection to the local debugger speaking over a fake
|
* Creates a new connection to the local debugger speaking over a fake
|
||||||
* transport. This connection results in straightforward calls to the onPacket
|
* transport. This connection results in straightforward calls to the onPacket
|
||||||
|
@ -863,7 +878,7 @@ var DebuggerServer = {
|
||||||
* that all our actors have names beginning with |forwardingPrefix + '/'|.
|
* that all our actors have names beginning with |forwardingPrefix + '/'|.
|
||||||
* In particular, the root actor's name will be |forwardingPrefix + '/root'|.
|
* In particular, the root actor's name will be |forwardingPrefix + '/root'|.
|
||||||
*/
|
*/
|
||||||
_onConnection(transport, forwardingPrefix, noRootActor = false) {
|
_onConnection(transport, forwardingPrefix, noRootActor = false, socketListener = null) {
|
||||||
let connID;
|
let connID;
|
||||||
if (forwardingPrefix) {
|
if (forwardingPrefix) {
|
||||||
connID = forwardingPrefix + "/";
|
connID = forwardingPrefix + "/";
|
||||||
|
@ -875,7 +890,7 @@ var DebuggerServer = {
|
||||||
connID = "server" + loader.id + ".conn" + this._nextConnID++ + ".";
|
connID = "server" + loader.id + ".conn" + this._nextConnID++ + ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
const conn = new DebuggerServerConnection(connID, transport);
|
const conn = new DebuggerServerConnection(connID, transport, socketListener);
|
||||||
this._connections[connID] = conn;
|
this._connections[connID] = conn;
|
||||||
|
|
||||||
// Create a root actor for the connection and send the hello packet.
|
// Create a root actor for the connection and send the hello packet.
|
||||||
|
@ -974,12 +989,16 @@ exports.DebuggerServer = DebuggerServer;
|
||||||
* with prefix.
|
* with prefix.
|
||||||
* @param transport transport
|
* @param transport transport
|
||||||
* Packet transport for the debugging protocol.
|
* Packet transport for the debugging protocol.
|
||||||
|
* @param socketListener SocketListener
|
||||||
|
* SocketListener which accepted the transport.
|
||||||
|
* If this is null, the transport is not that was accepted by SocketListener.
|
||||||
*/
|
*/
|
||||||
function DebuggerServerConnection(prefix, transport) {
|
function DebuggerServerConnection(prefix, transport, socketListener) {
|
||||||
this._prefix = prefix;
|
this._prefix = prefix;
|
||||||
this._transport = transport;
|
this._transport = transport;
|
||||||
this._transport.hooks = this;
|
this._transport.hooks = this;
|
||||||
this._nextID = 1;
|
this._nextID = 1;
|
||||||
|
this._socketListener = socketListener;
|
||||||
|
|
||||||
this._actorPool = new Pool(this);
|
this._actorPool = new Pool(this);
|
||||||
this._extraPools = [this._actorPool];
|
this._extraPools = [this._actorPool];
|
||||||
|
@ -1225,6 +1244,17 @@ DebuggerServerConnection.prototype = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns whether the connection was accepted by passed SocketListener.
|
||||||
|
*
|
||||||
|
* @param {SocketListener} socketListener
|
||||||
|
* @return {Boolean} return true if this connection was accepted by socketListener,
|
||||||
|
* else returns false.
|
||||||
|
*/
|
||||||
|
isAcceptedBy(socketListener) {
|
||||||
|
return this._socketListener === socketListener;
|
||||||
|
},
|
||||||
|
|
||||||
/* Forwarding packets to other transports based on actor name prefixes. */
|
/* Forwarding packets to other transports based on actor name prefixes. */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -32,6 +32,7 @@ loader.lazyRequireGetter(this, "Authenticators",
|
||||||
"devtools/shared/security/auth", true);
|
"devtools/shared/security/auth", true);
|
||||||
loader.lazyRequireGetter(this, "AuthenticationResult",
|
loader.lazyRequireGetter(this, "AuthenticationResult",
|
||||||
"devtools/shared/security/auth", true);
|
"devtools/shared/security/auth", true);
|
||||||
|
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
|
||||||
|
|
||||||
DevToolsUtils.defineLazyGetter(this, "nsFile", () => {
|
DevToolsUtils.defineLazyGetter(this, "nsFile", () => {
|
||||||
return CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
|
return CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
|
||||||
|
@ -366,7 +367,9 @@ function _storeCertOverride(s, host, port) {
|
||||||
* This helps contain and organize the parts of the server that may differ or
|
* This helps contain and organize the parts of the server that may differ or
|
||||||
* are particular to one given listener mechanism vs. another.
|
* are particular to one given listener mechanism vs. another.
|
||||||
*/
|
*/
|
||||||
function SocketListener() {}
|
function SocketListener() {
|
||||||
|
EventEmitter.decorate(this);
|
||||||
|
}
|
||||||
|
|
||||||
SocketListener.prototype = {
|
SocketListener.prototype = {
|
||||||
|
|
||||||
|
@ -544,11 +547,17 @@ SocketListener.prototype = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onAllowedConnection(transport) {
|
||||||
|
dumpn("onAllowedConnection, transport: " + transport);
|
||||||
|
this.emit("accepted", transport, this);
|
||||||
|
},
|
||||||
|
|
||||||
// nsIServerSocketListener implementation
|
// nsIServerSocketListener implementation
|
||||||
|
|
||||||
onSocketAccepted:
|
onSocketAccepted:
|
||||||
DevToolsUtils.makeInfallible(function(socket, socketTransport) {
|
DevToolsUtils.makeInfallible(function(socket, socketTransport) {
|
||||||
new ServerSocketConnection(this, socketTransport);
|
const connection = new ServerSocketConnection(this, socketTransport);
|
||||||
|
connection.once("allowed", this.onAllowedConnection.bind(this));
|
||||||
}, "SocketListener.onSocketAccepted"),
|
}, "SocketListener.onSocketAccepted"),
|
||||||
|
|
||||||
onStopListening: function(socket, status) {
|
onStopListening: function(socket, status) {
|
||||||
|
@ -572,6 +581,7 @@ function ServerSocketConnection(listener, socketTransport) {
|
||||||
this._listener = listener;
|
this._listener = listener;
|
||||||
this._socketTransport = socketTransport;
|
this._socketTransport = socketTransport;
|
||||||
this._handle();
|
this._handle();
|
||||||
|
EventEmitter.decorate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerSocketConnection.prototype = {
|
ServerSocketConnection.prototype = {
|
||||||
|
@ -628,15 +638,17 @@ ServerSocketConnection.prototype = {
|
||||||
* the connection is denied. If the entire process resolves successfully,
|
* the connection is denied. If the entire process resolves successfully,
|
||||||
* the connection is finally handed off to the |DebuggerServer|.
|
* the connection is finally handed off to the |DebuggerServer|.
|
||||||
*/
|
*/
|
||||||
_handle() {
|
async _handle() {
|
||||||
dumpn("Debugging connection starting authentication on " + this.address);
|
dumpn("Debugging connection starting authentication on " + this.address);
|
||||||
const self = this;
|
try {
|
||||||
(async function() {
|
this._listenForTLSHandshake();
|
||||||
self._listenForTLSHandshake();
|
await this._createTransport();
|
||||||
await self._createTransport();
|
await this._awaitTLSHandshake();
|
||||||
await self._awaitTLSHandshake();
|
await this._authenticate();
|
||||||
await self._authenticate();
|
this.allow();
|
||||||
})().then(() => this.allow()).catch(e => this.deny(e));
|
} catch (e) {
|
||||||
|
this.deny(e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -774,7 +786,7 @@ ServerSocketConnection.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dumpn("Debugging connection allowed on " + this.address);
|
dumpn("Debugging connection allowed on " + this.address);
|
||||||
DebuggerServer._onConnection(this._transport);
|
this.emit("allowed", this._transport);
|
||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -148,19 +148,23 @@ void KeyboardEvent::GetInitDict(KeyboardEventInit& aParam)
|
||||||
|
|
||||||
bool
|
bool
|
||||||
KeyboardEvent::ShouldUseSameValueForCharCodeAndKeyCode(
|
KeyboardEvent::ShouldUseSameValueForCharCodeAndKeyCode(
|
||||||
|
const WidgetKeyboardEvent& aWidgetKeyboardEvent,
|
||||||
CallerType aCallerType) const
|
CallerType aCallerType) const
|
||||||
{
|
{
|
||||||
// - If this event is initialized by JS, we don't need to return same value
|
// - If this event is initialized by JS, we don't need to return same value
|
||||||
// for keyCode and charCode since they can be initialized separately.
|
// for keyCode and charCode since they can be initialized separately.
|
||||||
// - If this is not a keypress event, we shouldn't return same value for
|
// - If this is not a keypress event, we shouldn't return same value for
|
||||||
// keyCode and charCode.
|
// keyCode and charCode.
|
||||||
|
// - If we need to return legacy keyCode and charCode values for the web
|
||||||
|
// app due to in the blacklist.
|
||||||
// - If this event is referred by default handler, i.e., the caller is
|
// - If this event is referred by default handler, i.e., the caller is
|
||||||
// system or this event is now in the system group, we don't need to use
|
// system or this event is now in the system group, we don't need to use
|
||||||
// hack for web-compat.
|
// hack for web-compat.
|
||||||
if (mInitializedByJS ||
|
if (mInitializedByJS ||
|
||||||
mEvent->mMessage != eKeyPress ||
|
aWidgetKeyboardEvent.mMessage != eKeyPress ||
|
||||||
|
aWidgetKeyboardEvent.mUseLegacyKeyCodeAndCharCodeValues ||
|
||||||
aCallerType == CallerType::System ||
|
aCallerType == CallerType::System ||
|
||||||
mEvent->mFlags.mInSystemGroup) {
|
aWidgetKeyboardEvent.mFlags.mInSystemGroup) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +197,8 @@ KeyboardEvent::CharCode(CallerType aCallerType)
|
||||||
// value.
|
// value.
|
||||||
|
|
||||||
if (widgetKeyboardEvent->mKeyNameIndex != KEY_NAME_INDEX_USE_STRING &&
|
if (widgetKeyboardEvent->mKeyNameIndex != KEY_NAME_INDEX_USE_STRING &&
|
||||||
ShouldUseSameValueForCharCodeAndKeyCode(aCallerType)) {
|
ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent,
|
||||||
|
aCallerType)) {
|
||||||
return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
|
return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +231,8 @@ KeyboardEvent::KeyCode(CallerType aCallerType)
|
||||||
// for keyCode value if this is a "keypress" event.
|
// for keyCode value if this is a "keypress" event.
|
||||||
|
|
||||||
if (widgetKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
|
if (widgetKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
|
||||||
ShouldUseSameValueForCharCodeAndKeyCode(aCallerType)) {
|
ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent,
|
||||||
|
aCallerType)) {
|
||||||
return widgetKeyboardEvent->mCharCode;
|
return widgetKeyboardEvent->mCharCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,9 @@ private:
|
||||||
* ShouldUseSameValueForCharCodeAndKeyCode() returns true if KeyCode() and
|
* ShouldUseSameValueForCharCodeAndKeyCode() returns true if KeyCode() and
|
||||||
* CharCode() should return same value.
|
* CharCode() should return same value.
|
||||||
*/
|
*/
|
||||||
bool ShouldUseSameValueForCharCodeAndKeyCode(CallerType aCallerType) const;
|
bool ShouldUseSameValueForCharCodeAndKeyCode(
|
||||||
|
const WidgetKeyboardEvent& aKeyboardEvent,
|
||||||
|
CallerType aCallerType) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
|
@ -10,6 +10,11 @@
|
||||||
* liability, trademark and document use rules apply.
|
* liability, trademark and document use rules apply.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
enum AutomationRate {
|
||||||
|
"a-rate",
|
||||||
|
"k-rate"
|
||||||
|
};
|
||||||
|
|
||||||
[Pref="dom.webaudio.enabled"]
|
[Pref="dom.webaudio.enabled"]
|
||||||
interface AudioParam {
|
interface AudioParam {
|
||||||
|
|
||||||
|
|
|
@ -712,6 +712,56 @@ TextEditor::DeleteSelectionAsAction(EDirection aDirection,
|
||||||
return NS_ERROR_NOT_INITIALIZED;
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is an existing selection when an extended delete is requested,
|
||||||
|
// platforms that use "caret-style" caret positioning collapse the
|
||||||
|
// selection to the start and then create a new selection.
|
||||||
|
// Platforms that use "selection-style" caret positioning just delete the
|
||||||
|
// existing selection without extending it.
|
||||||
|
if (!SelectionRefPtr()->IsCollapsed()) {
|
||||||
|
switch (aDirection) {
|
||||||
|
case eNextWord:
|
||||||
|
case ePreviousWord:
|
||||||
|
case eToBeginningOfLine:
|
||||||
|
case eToEndOfLine: {
|
||||||
|
if (mCaretStyle != 1) {
|
||||||
|
aDirection = eNone;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ErrorResult error;
|
||||||
|
SelectionRefPtr()->CollapseToStart(error);
|
||||||
|
if (NS_WARN_IF(error.Failed())) {
|
||||||
|
return error.StealNSResult();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Selection is still NOT collapsed, it does not important removing
|
||||||
|
// range of the operation since we'll remove the selected content. However,
|
||||||
|
// information of direction (backward or forward) may be important for
|
||||||
|
// web apps. E.g., web apps may want to mark selected range as "deleted"
|
||||||
|
// and move caret before or after the range. Therefore, we should forget
|
||||||
|
// only the range information but keep range information. See discussion
|
||||||
|
// of the spec issue for the detail:
|
||||||
|
// https://github.com/w3c/input-events/issues/82
|
||||||
|
if (!SelectionRefPtr()->IsCollapsed()) {
|
||||||
|
switch (editAction) {
|
||||||
|
case EditAction::eDeleteWordBackward:
|
||||||
|
case EditAction::eDeleteToBeginningOfSoftLine:
|
||||||
|
editActionData.UpdateEditAction(EditAction::eDeleteBackward);
|
||||||
|
break;
|
||||||
|
case EditAction::eDeleteWordForward:
|
||||||
|
case EditAction::eDeleteToEndOfSoftLine:
|
||||||
|
editActionData.UpdateEditAction(EditAction::eDeleteForward);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// delete placeholder txns merge.
|
// delete placeholder txns merge.
|
||||||
AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::DeleteTxnName);
|
AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::DeleteTxnName);
|
||||||
nsresult rv = DeleteSelectionAsSubAction(aDirection, aStripWrappers);
|
nsresult rv = DeleteSelectionAsSubAction(aDirection, aStripWrappers);
|
||||||
|
@ -737,33 +787,6 @@ TextEditor::DeleteSelectionAsSubAction(EDirection aDirection,
|
||||||
// Protect the edit rules object from dying
|
// Protect the edit rules object from dying
|
||||||
RefPtr<TextEditRules> rules(mRules);
|
RefPtr<TextEditRules> rules(mRules);
|
||||||
|
|
||||||
// If there is an existing selection when an extended delete is requested,
|
|
||||||
// platforms that use "caret-style" caret positioning collapse the
|
|
||||||
// selection to the start and then create a new selection.
|
|
||||||
// Platforms that use "selection-style" caret positioning just delete the
|
|
||||||
// existing selection without extending it.
|
|
||||||
if (!SelectionRefPtr()->IsCollapsed()) {
|
|
||||||
switch (aDirection) {
|
|
||||||
case eNextWord:
|
|
||||||
case ePreviousWord:
|
|
||||||
case eToBeginningOfLine:
|
|
||||||
case eToEndOfLine: {
|
|
||||||
if (mCaretStyle != 1) {
|
|
||||||
aDirection = eNone;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ErrorResult error;
|
|
||||||
SelectionRefPtr()->CollapseToStart(error);
|
|
||||||
if (NS_WARN_IF(error.Failed())) {
|
|
||||||
return error.StealNSResult();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
|
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
|
||||||
*this,
|
*this,
|
||||||
EditSubAction::eDeleteSelectedContent,
|
EditSubAction::eDeleteSelectedContent,
|
||||||
|
|
|
@ -1557,7 +1557,9 @@ impl Device {
|
||||||
debug_assert!(self.inside_frame);
|
debug_assert!(self.inside_frame);
|
||||||
debug_assert!(dst.width >= src.width);
|
debug_assert!(dst.width >= src.width);
|
||||||
debug_assert!(dst.height >= src.height);
|
debug_assert!(dst.height >= src.height);
|
||||||
|
debug_assert!(dst.layer_count >= src.layer_count);
|
||||||
|
|
||||||
|
// Note that zip() truncates to the shorter of the two iterators.
|
||||||
let rect = DeviceIntRect::new(DeviceIntPoint::zero(), src.get_dimensions().to_i32());
|
let rect = DeviceIntRect::new(DeviceIntPoint::zero(), src.get_dimensions().to_i32());
|
||||||
for (read_fbo, draw_fbo) in src.fbos.iter().zip(&dst.fbos) {
|
for (read_fbo, draw_fbo) in src.fbos.iter().zip(&dst.fbos) {
|
||||||
self.bind_read_target_impl(*read_fbo);
|
self.bind_read_target_impl(*read_fbo);
|
||||||
|
|
|
@ -110,48 +110,138 @@ pub enum TextureUpdateSource {
|
||||||
Bytes { data: Arc<Vec<u8>> },
|
Bytes { data: Arc<Vec<u8>> },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Command to allocate, reallocate, or free a texture for the texture cache.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TextureUpdateOp {
|
pub struct TextureCacheAllocation {
|
||||||
Create {
|
/// The virtual ID (i.e. distinct from device ID) of the texture.
|
||||||
width: u32,
|
pub id: CacheTextureId,
|
||||||
height: u32,
|
/// Details corresponding to the operation in question.
|
||||||
format: ImageFormat,
|
pub kind: TextureCacheAllocationKind,
|
||||||
filter: TextureFilter,
|
}
|
||||||
render_target: Option<RenderTargetInfo>,
|
|
||||||
layer_count: i32,
|
/// Information used when allocating / reallocating.
|
||||||
},
|
#[derive(Debug)]
|
||||||
Update {
|
pub struct TextureCacheAllocInfo {
|
||||||
rect: DeviceUintRect,
|
pub width: u32,
|
||||||
stride: Option<u32>,
|
pub height: u32,
|
||||||
offset: u32,
|
pub layer_count: i32,
|
||||||
layer_index: i32,
|
pub format: ImageFormat,
|
||||||
source: TextureUpdateSource,
|
pub filter: TextureFilter,
|
||||||
},
|
}
|
||||||
|
|
||||||
|
/// Sub-operation-specific information for allocation operations.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TextureCacheAllocationKind {
|
||||||
|
/// Performs an initial texture allocation.
|
||||||
|
Alloc(TextureCacheAllocInfo),
|
||||||
|
/// Reallocates the texture. The existing live texture with the same id
|
||||||
|
/// will be deallocated and its contents blitted over. The new size must
|
||||||
|
/// be greater than the old size.
|
||||||
|
Realloc(TextureCacheAllocInfo),
|
||||||
|
/// Frees the texture and the corresponding cache ID.
|
||||||
Free,
|
Free,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Command to update the contents of the texture cache.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TextureUpdate {
|
pub struct TextureCacheUpdate {
|
||||||
pub id: CacheTextureId,
|
pub id: CacheTextureId,
|
||||||
pub op: TextureUpdateOp,
|
pub rect: DeviceUintRect,
|
||||||
|
pub stride: Option<u32>,
|
||||||
|
pub offset: u32,
|
||||||
|
pub layer_index: i32,
|
||||||
|
pub source: TextureUpdateSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Atomic set of commands to manipulate the texture cache, generated on the
|
||||||
|
/// RenderBackend thread and executed on the Renderer thread.
|
||||||
|
///
|
||||||
|
/// The list of allocation operations is processed before the updates. This is
|
||||||
|
/// important to allow coalescing of certain allocation operations.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TextureUpdateList {
|
pub struct TextureUpdateList {
|
||||||
pub updates: Vec<TextureUpdate>,
|
/// Commands to alloc/realloc/free the textures. Processed first.
|
||||||
|
pub allocations: Vec<TextureCacheAllocation>,
|
||||||
|
/// Commands to update the contents of the textures. Processed second.
|
||||||
|
pub updates: Vec<TextureCacheUpdate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureUpdateList {
|
impl TextureUpdateList {
|
||||||
|
/// Mints a new `TextureUpdateList`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
TextureUpdateList {
|
TextureUpdateList {
|
||||||
|
allocations: Vec::new(),
|
||||||
updates: Vec::new(),
|
updates: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pushes an update operation onto the list.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push(&mut self, update: TextureUpdate) {
|
pub fn push_update(&mut self, update: TextureCacheUpdate) {
|
||||||
self.updates.push(update);
|
self.updates.push(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pushes an allocation operation onto the list.
|
||||||
|
pub fn push_alloc(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
|
||||||
|
debug_assert!(!self.allocations.iter().any(|x| x.id == id));
|
||||||
|
self.allocations.push(TextureCacheAllocation {
|
||||||
|
id,
|
||||||
|
kind: TextureCacheAllocationKind::Alloc(info),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes a reallocation operation onto the list, potentially coalescing
|
||||||
|
/// with previous operations.
|
||||||
|
pub fn push_realloc(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
|
||||||
|
self.debug_assert_coalesced(id);
|
||||||
|
|
||||||
|
// Coallesce this realloc into a previous alloc or realloc, if available.
|
||||||
|
if let Some(cur) = self.allocations.iter_mut().find(|x| x.id == id) {
|
||||||
|
match cur.kind {
|
||||||
|
TextureCacheAllocationKind::Alloc(ref mut i) => *i = info,
|
||||||
|
TextureCacheAllocationKind::Realloc(ref mut i) => *i = info,
|
||||||
|
TextureCacheAllocationKind::Free => panic!("Reallocating freed texture"),
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.allocations.push(TextureCacheAllocation {
|
||||||
|
id,
|
||||||
|
kind: TextureCacheAllocationKind::Realloc(info),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes a free operation onto the list, potentially coalescing with
|
||||||
|
/// previous operations.
|
||||||
|
pub fn push_free(&mut self, id: CacheTextureId) {
|
||||||
|
self.debug_assert_coalesced(id);
|
||||||
|
|
||||||
|
// Drop any unapplied updates to the to-be-freed texture.
|
||||||
|
self.updates.retain(|x| x.id != id);
|
||||||
|
|
||||||
|
// Drop any allocations for it as well. If we happen to be allocating and
|
||||||
|
// freeing in the same batch, we can collapse them to a no-op.
|
||||||
|
let idx = self.allocations.iter().position(|x| x.id == id);
|
||||||
|
let removed_kind = idx.map(|i| self.allocations.remove(i).kind);
|
||||||
|
match removed_kind {
|
||||||
|
Some(TextureCacheAllocationKind::Alloc(..)) => { /* no-op! */ },
|
||||||
|
Some(TextureCacheAllocationKind::Free) => panic!("Double free"),
|
||||||
|
Some(TextureCacheAllocationKind::Realloc(..)) | None => {
|
||||||
|
self.allocations.push(TextureCacheAllocation {
|
||||||
|
id,
|
||||||
|
kind: TextureCacheAllocationKind::Free,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_assert_coalesced(&self, id: CacheTextureId) {
|
||||||
|
debug_assert!(
|
||||||
|
self.allocations.iter().filter(|x| x.id == id).count() <= 1,
|
||||||
|
"Allocations should have been coalesced",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps a tiling::Frame, but conceptually could hold more information
|
/// Wraps a tiling::Frame, but conceptually could hold more information
|
||||||
|
|
|
@ -53,8 +53,8 @@ use gpu_cache::GpuDebugChunk;
|
||||||
use gpu_glyph_renderer::GpuGlyphRenderer;
|
use gpu_glyph_renderer::GpuGlyphRenderer;
|
||||||
use gpu_types::ScalingInstance;
|
use gpu_types::ScalingInstance;
|
||||||
use internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
|
use internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
|
||||||
use internal_types::{CacheTextureId, DebugOutput, FastHashMap, RenderedDocument, ResultMsg};
|
use internal_types::{CacheTextureId, DebugOutput, FastHashMap, LayerIndex, RenderedDocument, ResultMsg};
|
||||||
use internal_types::{LayerIndex, TextureUpdateList, TextureUpdateOp, TextureUpdateSource};
|
use internal_types::{TextureCacheAllocationKind, TextureCacheUpdate, TextureUpdateList, TextureUpdateSource};
|
||||||
use internal_types::{RenderTargetInfo, SavedTargetIndex};
|
use internal_types::{RenderTargetInfo, SavedTargetIndex};
|
||||||
use prim_store::DeferredResolve;
|
use prim_store::DeferredResolve;
|
||||||
use profiler::{BackendProfileCounters, FrameProfileCounters,
|
use profiler::{BackendProfileCounters, FrameProfileCounters,
|
||||||
|
@ -2800,90 +2800,92 @@ impl Renderer {
|
||||||
let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
|
let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
|
||||||
|
|
||||||
for update_list in pending_texture_updates.drain(..) {
|
for update_list in pending_texture_updates.drain(..) {
|
||||||
for update in update_list.updates {
|
for allocation in update_list.allocations {
|
||||||
match update.op {
|
let is_realloc = matches!(allocation.kind, TextureCacheAllocationKind::Realloc(..));
|
||||||
TextureUpdateOp::Create {
|
match allocation.kind {
|
||||||
width,
|
TextureCacheAllocationKind::Alloc(info) |
|
||||||
height,
|
TextureCacheAllocationKind::Realloc(info) => {
|
||||||
layer_count,
|
|
||||||
format,
|
|
||||||
filter,
|
|
||||||
render_target,
|
|
||||||
} => {
|
|
||||||
// Create a new native texture, as requested by the texture cache.
|
// Create a new native texture, as requested by the texture cache.
|
||||||
//
|
//
|
||||||
// Ensure no PBO is bound when creating the texture storage,
|
// Ensure no PBO is bound when creating the texture storage,
|
||||||
// or GL will attempt to read data from there.
|
// or GL will attempt to read data from there.
|
||||||
let texture = self.device.create_texture(
|
let texture = self.device.create_texture(
|
||||||
TextureTarget::Array,
|
TextureTarget::Array,
|
||||||
format,
|
info.format,
|
||||||
width,
|
info.width,
|
||||||
height,
|
info.height,
|
||||||
filter,
|
info.filter,
|
||||||
render_target,
|
// This needs to be a render target because some render
|
||||||
layer_count,
|
// tasks get rendered into the texture cache.
|
||||||
);
|
Some(RenderTargetInfo { has_depth: false }),
|
||||||
self.texture_resolver.texture_cache_map.insert(update.id, texture);
|
info.layer_count,
|
||||||
}
|
|
||||||
TextureUpdateOp::Update {
|
|
||||||
rect,
|
|
||||||
source,
|
|
||||||
stride,
|
|
||||||
layer_index,
|
|
||||||
offset,
|
|
||||||
} => {
|
|
||||||
let texture = &self.texture_resolver.texture_cache_map[&update.id];
|
|
||||||
let mut uploader = self.device.upload_texture(
|
|
||||||
texture,
|
|
||||||
&self.texture_cache_upload_pbo,
|
|
||||||
0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let bytes_uploaded = match source {
|
let old = self.texture_resolver.texture_cache_map.insert(allocation.id, texture);
|
||||||
TextureUpdateSource::Bytes { data } => {
|
assert_eq!(old.is_some(), is_realloc, "Renderer and RenderBackend disagree");
|
||||||
|
if let Some(old) = old {
|
||||||
|
self.device.blit_renderable_texture(
|
||||||
|
self.texture_resolver.texture_cache_map.get_mut(&allocation.id).unwrap(),
|
||||||
|
&old
|
||||||
|
);
|
||||||
|
self.device.delete_texture(old);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TextureCacheAllocationKind::Free => {
|
||||||
|
let texture = self.texture_resolver.texture_cache_map.remove(&allocation.id).unwrap();
|
||||||
|
self.device.delete_texture(texture);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for update in update_list.updates {
|
||||||
|
let TextureCacheUpdate { id, rect, stride, offset, layer_index, source } = update;
|
||||||
|
let texture = &self.texture_resolver.texture_cache_map[&id];
|
||||||
|
let mut uploader = self.device.upload_texture(
|
||||||
|
texture,
|
||||||
|
&self.texture_cache_upload_pbo,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let bytes_uploaded = match source {
|
||||||
|
TextureUpdateSource::Bytes { data } => {
|
||||||
|
uploader.upload(
|
||||||
|
rect, layer_index, stride,
|
||||||
|
&data[offset as usize ..],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TextureUpdateSource::External { id, channel_index } => {
|
||||||
|
let handler = self.external_image_handler
|
||||||
|
.as_mut()
|
||||||
|
.expect("Found external image, but no handler set!");
|
||||||
|
// The filter is only relevant for NativeTexture external images.
|
||||||
|
let size = match handler.lock(id, channel_index, ImageRendering::Auto).source {
|
||||||
|
ExternalImageSource::RawData(data) => {
|
||||||
uploader.upload(
|
uploader.upload(
|
||||||
rect, layer_index, stride,
|
rect, layer_index, stride,
|
||||||
&data[offset as usize ..],
|
&data[offset as usize ..],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TextureUpdateSource::External { id, channel_index } => {
|
ExternalImageSource::Invalid => {
|
||||||
let handler = self.external_image_handler
|
// Create a local buffer to fill the pbo.
|
||||||
.as_mut()
|
let bpp = texture.get_format().bytes_per_pixel();
|
||||||
.expect("Found external image, but no handler set!");
|
let width = stride.unwrap_or(rect.size.width * bpp);
|
||||||
// The filter is only relevant for NativeTexture external images.
|
let total_size = width * rect.size.height;
|
||||||
let size = match handler.lock(id, channel_index, ImageRendering::Auto).source {
|
// WR haven't support RGBAF32 format in texture_cache, so
|
||||||
ExternalImageSource::RawData(data) => {
|
// we use u8 type here.
|
||||||
uploader.upload(
|
let dummy_data: Vec<u8> = vec![255; total_size as usize];
|
||||||
rect, layer_index, stride,
|
uploader.upload(rect, layer_index, stride, &dummy_data)
|
||||||
&data[offset as usize ..],
|
}
|
||||||
)
|
ExternalImageSource::NativeTexture(eid) => {
|
||||||
}
|
panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id);
|
||||||
ExternalImageSource::Invalid => {
|
|
||||||
// Create a local buffer to fill the pbo.
|
|
||||||
let bpp = texture.get_format().bytes_per_pixel();
|
|
||||||
let width = stride.unwrap_or(rect.size.width * bpp);
|
|
||||||
let total_size = width * rect.size.height;
|
|
||||||
// WR haven't support RGBAF32 format in texture_cache, so
|
|
||||||
// we use u8 type here.
|
|
||||||
let dummy_data: Vec<u8> = vec![255; total_size as usize];
|
|
||||||
uploader.upload(rect, layer_index, stride, &dummy_data)
|
|
||||||
}
|
|
||||||
ExternalImageSource::NativeTexture(eid) => {
|
|
||||||
panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
handler.unlock(id, channel_index);
|
|
||||||
size
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
handler.unlock(id, channel_index);
|
||||||
|
size
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.profile_counters.texture_data_uploaded.add(bytes_uploaded >> 10);
|
self.profile_counters.texture_data_uploaded.add(bytes_uploaded >> 10);
|
||||||
}
|
|
||||||
TextureUpdateOp::Free => {
|
|
||||||
let texture = self.texture_resolver.texture_cache_map.remove(&update.id).unwrap();
|
|
||||||
self.device.delete_texture(texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle};
|
||||||
use gpu_cache::{GpuCache, GpuCacheHandle};
|
use gpu_cache::{GpuCache, GpuCacheHandle};
|
||||||
use gpu_types::{ImageSource, UvRectKind};
|
use gpu_types::{ImageSource, UvRectKind};
|
||||||
use internal_types::{CacheTextureId, LayerIndex, TextureUpdateList, TextureUpdateSource};
|
use internal_types::{CacheTextureId, LayerIndex, TextureUpdateList, TextureUpdateSource};
|
||||||
use internal_types::{RenderTargetInfo, TextureSource, TextureUpdate, TextureUpdateOp};
|
use internal_types::{TextureSource, TextureCacheAllocInfo, TextureCacheUpdate};
|
||||||
use profiler::{ResourceProfileCounter, TextureCacheProfileCounters};
|
use profiler::{ResourceProfileCounter, TextureCacheProfileCounters};
|
||||||
use render_backend::FrameId;
|
use render_backend::FrameId;
|
||||||
use resource_cache::CacheItem;
|
use resource_cache::CacheItem;
|
||||||
|
@ -19,7 +19,7 @@ use std::cmp;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
// The size of each region (page) in a texture layer.
|
/// The size of each region/layer in shared cache texture arrays.
|
||||||
const TEXTURE_REGION_DIMENSIONS: u32 = 512;
|
const TEXTURE_REGION_DIMENSIONS: u32 = 512;
|
||||||
|
|
||||||
// Items in the texture cache can either be standalone textures,
|
// Items in the texture cache can either be standalone textures,
|
||||||
|
@ -33,9 +33,7 @@ enum EntryKind {
|
||||||
// Origin within the texture layer where this item exists.
|
// Origin within the texture layer where this item exists.
|
||||||
origin: DeviceUintPoint,
|
origin: DeviceUintPoint,
|
||||||
// The layer index of the texture array.
|
// The layer index of the texture array.
|
||||||
layer_index: u16,
|
layer_index: usize,
|
||||||
// The region that this entry belongs to in the layer.
|
|
||||||
region_index: u16,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,15 +189,99 @@ impl EvictionNotice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A set of lazily allocated, fixed size, texture arrays for each format the
|
||||||
|
/// texture cache supports.
|
||||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||||
pub struct TextureCache {
|
struct SharedTextures {
|
||||||
// A lazily allocated, fixed size, texture array for
|
|
||||||
// each format the texture cache supports.
|
|
||||||
array_rgba8_nearest: TextureArray,
|
array_rgba8_nearest: TextureArray,
|
||||||
array_a8_linear: TextureArray,
|
array_a8_linear: TextureArray,
|
||||||
array_a16_linear: TextureArray,
|
array_a16_linear: TextureArray,
|
||||||
array_rgba8_linear: TextureArray,
|
array_rgba8_linear: TextureArray,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SharedTextures {
|
||||||
|
/// Mints a new set of shared textures.
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
// Used primarily for cached shadow masks. There can be lots of
|
||||||
|
// these on some pages like francine, but most pages don't use it
|
||||||
|
// much.
|
||||||
|
array_a8_linear: TextureArray::new(
|
||||||
|
ImageFormat::R8,
|
||||||
|
TextureFilter::Linear,
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
// Used for experimental hdr yuv texture support, but not used in
|
||||||
|
// production Firefox.
|
||||||
|
array_a16_linear: TextureArray::new(
|
||||||
|
ImageFormat::R16,
|
||||||
|
TextureFilter::Linear,
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
// The primary cache for images, glyphs, etc.
|
||||||
|
array_rgba8_linear: TextureArray::new(
|
||||||
|
ImageFormat::BGRA8,
|
||||||
|
TextureFilter::Linear,
|
||||||
|
16 * 4,
|
||||||
|
),
|
||||||
|
// Used for image-rendering: crisp. This is mostly favicons, which
|
||||||
|
// are small. Some other images use it too, but those tend to be
|
||||||
|
// larger than 512x512 and thus don't use the shared cache anyway.
|
||||||
|
//
|
||||||
|
// Even though most of the buckets will be sparsely-used, we still
|
||||||
|
// need a few to account for different favicon sizes. 4 seems enough
|
||||||
|
// in practice, though we might also be able to get away with 2 or 3.
|
||||||
|
array_rgba8_nearest: TextureArray::new(
|
||||||
|
ImageFormat::BGRA8,
|
||||||
|
TextureFilter::Nearest,
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears each texture in the set, with the given set of pending updates.
|
||||||
|
fn clear(&mut self, updates: &mut TextureUpdateList) {
|
||||||
|
self.array_a8_linear.clear(updates);
|
||||||
|
self.array_a16_linear.clear(updates);
|
||||||
|
self.array_rgba8_linear.clear(updates);
|
||||||
|
self.array_rgba8_nearest.clear(updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable borrow for the shared texture array matching the parameters.
|
||||||
|
fn select(&mut self, format: ImageFormat, filter: TextureFilter) -> &mut TextureArray {
|
||||||
|
match (format, filter) {
|
||||||
|
(ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear,
|
||||||
|
(ImageFormat::R16, TextureFilter::Linear) => &mut self.array_a16_linear,
|
||||||
|
(ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
|
||||||
|
(ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
|
||||||
|
(_, _) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// General-purpose manager for images in GPU memory. This includes images,
|
||||||
|
/// rasterized glyphs, rasterized blobs, cached render tasks, etc.
|
||||||
|
///
|
||||||
|
/// The texture cache is owned and managed by the RenderBackend thread, and
|
||||||
|
/// produces a series of commands to manipulate the textures on the Renderer
|
||||||
|
/// thread. These commands are executed before any rendering is performed for
|
||||||
|
/// a given frame.
|
||||||
|
///
|
||||||
|
/// Entries in the texture cache are not guaranteed to live past the end of the
|
||||||
|
/// frame in which they are requested, and may be evicted. The API supports
|
||||||
|
/// querying whether an entry is still available.
|
||||||
|
///
|
||||||
|
/// The TextureCache is different from the GpuCache in that the former stores
|
||||||
|
/// images, whereas the latter stores data and parameters for use in the shaders.
|
||||||
|
/// This means that the texture cache can be visualized, which is a good way to
|
||||||
|
/// understand how it works. Enabling gfx.webrender.debug.texture-cache shows a
|
||||||
|
/// live view of its contents in Firefox.
|
||||||
|
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||||
|
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||||
|
pub struct TextureCache {
|
||||||
|
/// Set of texture arrays in different formats used for the shared cache.
|
||||||
|
shared_textures: SharedTextures,
|
||||||
|
|
||||||
// Maximum texture size supported by hardware.
|
// Maximum texture size supported by hardware.
|
||||||
max_texture_size: u32,
|
max_texture_size: u32,
|
||||||
|
@ -207,8 +289,9 @@ pub struct TextureCache {
|
||||||
// The next unused virtual texture ID. Monotonically increasing.
|
// The next unused virtual texture ID. Monotonically increasing.
|
||||||
next_id: CacheTextureId,
|
next_id: CacheTextureId,
|
||||||
|
|
||||||
// A list of updates that need to be applied to the
|
// A list of allocations and updates that need to be
|
||||||
// texture cache in the rendering thread this frame.
|
// applied to the texture cache in the rendering thread
|
||||||
|
// this frame.
|
||||||
#[cfg_attr(all(feature = "serde", any(feature = "capture", feature = "replay")), serde(skip))]
|
#[cfg_attr(all(feature = "serde", any(feature = "capture", feature = "replay")), serde(skip))]
|
||||||
pending_updates: TextureUpdateList,
|
pending_updates: TextureUpdateList,
|
||||||
|
|
||||||
|
@ -233,46 +316,8 @@ pub struct TextureCache {
|
||||||
impl TextureCache {
|
impl TextureCache {
|
||||||
pub fn new(max_texture_size: u32) -> Self {
|
pub fn new(max_texture_size: u32) -> Self {
|
||||||
TextureCache {
|
TextureCache {
|
||||||
|
shared_textures: SharedTextures::new(),
|
||||||
max_texture_size,
|
max_texture_size,
|
||||||
// Used primarily for cached shadow masks. There can be lots of
|
|
||||||
// these on some pages like francine, but most pages don't use it
|
|
||||||
// much.
|
|
||||||
array_a8_linear: TextureArray::new(
|
|
||||||
ImageFormat::R8,
|
|
||||||
TextureFilter::Linear,
|
|
||||||
1024,
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
// Used for experimental hdr yuv texture support, but not used in
|
|
||||||
// production Firefox.
|
|
||||||
array_a16_linear: TextureArray::new(
|
|
||||||
ImageFormat::R16,
|
|
||||||
TextureFilter::Linear,
|
|
||||||
1024,
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
// The primary cache for images, glyphs, etc.
|
|
||||||
array_rgba8_linear: TextureArray::new(
|
|
||||||
ImageFormat::BGRA8,
|
|
||||||
TextureFilter::Linear,
|
|
||||||
2048,
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
// Used for image-rendering: crisp. This is mostly favicons, which
|
|
||||||
// are small. Some other images use it too, but those tend to be
|
|
||||||
// larger than 512x512 and thus don't use the shared cache anyway.
|
|
||||||
//
|
|
||||||
// Ideally we'd use 512 as the dimensions here, since we don't really
|
|
||||||
// need more. But once a page gets something of a given size bucket
|
|
||||||
// assigned to it, all further allocations need to be of that size.
|
|
||||||
// So using 1024 gives us 4 buckets instead of 1, which in practice
|
|
||||||
// is probably enough.
|
|
||||||
array_rgba8_nearest: TextureArray::new(
|
|
||||||
ImageFormat::BGRA8,
|
|
||||||
TextureFilter::Nearest,
|
|
||||||
1024,
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
next_id: CacheTextureId(1),
|
next_id: CacheTextureId(1),
|
||||||
pending_updates: TextureUpdateList::new(),
|
pending_updates: TextureUpdateList::new(),
|
||||||
frame_id: FrameId::invalid(),
|
frame_id: FrameId::invalid(),
|
||||||
|
@ -307,33 +352,7 @@ impl TextureCache {
|
||||||
|
|
||||||
assert!(self.entries.len() == 0);
|
assert!(self.entries.len() == 0);
|
||||||
|
|
||||||
if let Some(texture_id) = self.array_a8_linear.clear() {
|
self.shared_textures.clear(&mut self.pending_updates);
|
||||||
self.pending_updates.push(TextureUpdate {
|
|
||||||
id: texture_id,
|
|
||||||
op: TextureUpdateOp::Free,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(texture_id) = self.array_a16_linear.clear() {
|
|
||||||
self.pending_updates.push(TextureUpdate {
|
|
||||||
id: texture_id,
|
|
||||||
op: TextureUpdateOp::Free,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(texture_id) = self.array_rgba8_linear.clear() {
|
|
||||||
self.pending_updates.push(TextureUpdate {
|
|
||||||
id: texture_id,
|
|
||||||
op: TextureUpdateOp::Free,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(texture_id) = self.array_rgba8_nearest.clear() {
|
|
||||||
self.pending_updates.push(TextureUpdate {
|
|
||||||
id: texture_id,
|
|
||||||
op: TextureUpdateOp::Free,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_frame(&mut self, frame_id: FrameId) {
|
pub fn begin_frame(&mut self, frame_id: FrameId) {
|
||||||
|
@ -343,13 +362,13 @@ impl TextureCache {
|
||||||
pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
|
pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
|
||||||
self.expire_old_standalone_entries();
|
self.expire_old_standalone_entries();
|
||||||
|
|
||||||
self.array_a8_linear
|
self.shared_textures.array_a8_linear
|
||||||
.update_profile(&mut texture_cache_profile.pages_a8_linear);
|
.update_profile(&mut texture_cache_profile.pages_a8_linear);
|
||||||
self.array_a16_linear
|
self.shared_textures.array_a16_linear
|
||||||
.update_profile(&mut texture_cache_profile.pages_a16_linear);
|
.update_profile(&mut texture_cache_profile.pages_a16_linear);
|
||||||
self.array_rgba8_linear
|
self.shared_textures.array_rgba8_linear
|
||||||
.update_profile(&mut texture_cache_profile.pages_rgba8_linear);
|
.update_profile(&mut texture_cache_profile.pages_rgba8_linear);
|
||||||
self.array_rgba8_nearest
|
self.shared_textures.array_rgba8_nearest
|
||||||
.update_profile(&mut texture_cache_profile.pages_rgba8_nearest);
|
.update_profile(&mut texture_cache_profile.pages_rgba8_nearest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,7 +480,7 @@ impl TextureCache {
|
||||||
} => (layer_index, origin),
|
} => (layer_index, origin),
|
||||||
};
|
};
|
||||||
|
|
||||||
let op = TextureUpdate::new_update(
|
let op = TextureCacheUpdate::new_update(
|
||||||
data,
|
data,
|
||||||
&descriptor,
|
&descriptor,
|
||||||
origin,
|
origin,
|
||||||
|
@ -470,7 +489,7 @@ impl TextureCache {
|
||||||
layer_index as i32,
|
layer_index as i32,
|
||||||
dirty_rect,
|
dirty_rect,
|
||||||
);
|
);
|
||||||
self.pending_updates.push(op);
|
self.pending_updates.push_update(op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,24 +497,10 @@ impl TextureCache {
|
||||||
fn get_region_mut(&mut self,
|
fn get_region_mut(&mut self,
|
||||||
format: ImageFormat,
|
format: ImageFormat,
|
||||||
filter: TextureFilter,
|
filter: TextureFilter,
|
||||||
region_index: u16
|
layer_index: usize,
|
||||||
) -> &mut TextureRegion {
|
) -> &mut TextureRegion {
|
||||||
let texture_array = match (format, filter) {
|
let texture_array = self.shared_textures.select(format, filter);
|
||||||
(ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear,
|
&mut texture_array.regions[layer_index]
|
||||||
(ImageFormat::R16, TextureFilter::Linear) => &mut self.array_a16_linear,
|
|
||||||
(ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
|
|
||||||
(ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
|
|
||||||
(ImageFormat::RGBAF32, _) |
|
|
||||||
(ImageFormat::RG8, _) |
|
|
||||||
(ImageFormat::RGBAI32, _) |
|
|
||||||
(ImageFormat::R8, TextureFilter::Nearest) |
|
|
||||||
(ImageFormat::R8, TextureFilter::Trilinear) |
|
|
||||||
(ImageFormat::R16, TextureFilter::Nearest) |
|
|
||||||
(ImageFormat::R16, TextureFilter::Trilinear) |
|
|
||||||
(ImageFormat::BGRA8, TextureFilter::Trilinear) => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
&mut texture_array.regions[region_index as usize]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a given texture handle has a valid allocation
|
// Check if a given texture handle has a valid allocation
|
||||||
|
@ -691,22 +696,18 @@ impl TextureCache {
|
||||||
match entry.kind {
|
match entry.kind {
|
||||||
EntryKind::Standalone { .. } => {
|
EntryKind::Standalone { .. } => {
|
||||||
// This is a standalone texture allocation. Free it directly.
|
// This is a standalone texture allocation. Free it directly.
|
||||||
self.pending_updates.push(TextureUpdate {
|
self.pending_updates.push_free(entry.texture_id);
|
||||||
id: entry.texture_id,
|
|
||||||
op: TextureUpdateOp::Free,
|
|
||||||
});
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
EntryKind::Cache {
|
EntryKind::Cache {
|
||||||
origin,
|
origin,
|
||||||
region_index,
|
layer_index,
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
// Free the block in the given region.
|
// Free the block in the given region.
|
||||||
let region = self.get_region_mut(
|
let region = self.get_region_mut(
|
||||||
entry.format,
|
entry.format,
|
||||||
entry.filter,
|
entry.filter,
|
||||||
region_index
|
layer_index,
|
||||||
);
|
);
|
||||||
region.free(origin);
|
region.free(origin);
|
||||||
Some(region)
|
Some(region)
|
||||||
|
@ -722,43 +723,26 @@ impl TextureCache {
|
||||||
user_data: [f32; 3],
|
user_data: [f32; 3],
|
||||||
uv_rect_kind: UvRectKind,
|
uv_rect_kind: UvRectKind,
|
||||||
) -> Option<CacheEntry> {
|
) -> Option<CacheEntry> {
|
||||||
// Work out which cache it goes in, based on format.
|
// Mutably borrow the correct texture.
|
||||||
let texture_array = match (descriptor.format, filter) {
|
let texture_array = self.shared_textures.select(descriptor.format, filter);
|
||||||
(ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear,
|
|
||||||
(ImageFormat::R16, TextureFilter::Linear) => &mut self.array_a16_linear,
|
|
||||||
(ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
|
|
||||||
(ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
|
|
||||||
(ImageFormat::RGBAF32, _) |
|
|
||||||
(ImageFormat::RGBAI32, _) |
|
|
||||||
(ImageFormat::R8, TextureFilter::Nearest) |
|
|
||||||
(ImageFormat::R8, TextureFilter::Trilinear) |
|
|
||||||
(ImageFormat::R16, TextureFilter::Nearest) |
|
|
||||||
(ImageFormat::R16, TextureFilter::Trilinear) |
|
|
||||||
(ImageFormat::BGRA8, TextureFilter::Trilinear) |
|
|
||||||
(ImageFormat::RG8, _) => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Lazy initialize this texture array if required.
|
// Lazy initialize this texture array if required.
|
||||||
if texture_array.texture_id.is_none() {
|
if texture_array.texture_id.is_none() {
|
||||||
|
assert!(texture_array.regions.is_empty());
|
||||||
let texture_id = self.next_id;
|
let texture_id = self.next_id;
|
||||||
self.next_id.0 += 1;
|
self.next_id.0 += 1;
|
||||||
|
|
||||||
let update_op = TextureUpdate {
|
let info = TextureCacheAllocInfo {
|
||||||
id: texture_id,
|
width: TEXTURE_REGION_DIMENSIONS,
|
||||||
op: TextureUpdateOp::Create {
|
height: TEXTURE_REGION_DIMENSIONS,
|
||||||
width: texture_array.dimensions,
|
format: descriptor.format,
|
||||||
height: texture_array.dimensions,
|
filter: texture_array.filter,
|
||||||
format: descriptor.format,
|
layer_count: 1,
|
||||||
filter: texture_array.filter,
|
|
||||||
// This needs to be a render target because some render
|
|
||||||
// tasks get rendered into the texture cache.
|
|
||||||
render_target: Some(RenderTargetInfo { has_depth: false }),
|
|
||||||
layer_count: texture_array.layer_count as i32,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
self.pending_updates.push(update_op);
|
self.pending_updates.push_alloc(texture_id, info);
|
||||||
|
|
||||||
texture_array.texture_id = Some(texture_id);
|
texture_array.texture_id = Some(texture_id);
|
||||||
|
texture_array.regions.push(TextureRegion::new(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the allocation. This can fail and return None
|
// Do the allocation. This can fail and return None
|
||||||
|
@ -824,6 +808,7 @@ impl TextureCache {
|
||||||
|
|
||||||
// If it's allowed in the cache, see if there is a spot for it.
|
// If it's allowed in the cache, see if there is a spot for it.
|
||||||
if allowed_in_shared_cache {
|
if allowed_in_shared_cache {
|
||||||
|
|
||||||
new_cache_entry = self.allocate_from_shared_cache(
|
new_cache_entry = self.allocate_from_shared_cache(
|
||||||
&descriptor,
|
&descriptor,
|
||||||
filter,
|
filter,
|
||||||
|
@ -831,10 +816,36 @@ impl TextureCache {
|
||||||
uv_rect_kind,
|
uv_rect_kind,
|
||||||
);
|
);
|
||||||
|
|
||||||
// If we failed to allocate in the shared cache, run an
|
// If we failed to allocate in the shared cache, make some space
|
||||||
// eviction cycle, and then try to allocate again.
|
// and then try to allocate again. If we still have room to grow
|
||||||
|
// the cache, we do that. Otherwise, we evict.
|
||||||
|
//
|
||||||
|
// We should improve this logic to support some degree of eviction
|
||||||
|
// before the cache fills up eintirely.
|
||||||
if new_cache_entry.is_none() {
|
if new_cache_entry.is_none() {
|
||||||
self.expire_old_shared_entries(&descriptor);
|
let reallocated = {
|
||||||
|
let texture_array = self.shared_textures.select(descriptor.format, filter);
|
||||||
|
let num_regions = texture_array.regions.len();
|
||||||
|
if num_regions < texture_array.max_layer_count {
|
||||||
|
// We have room to grow.
|
||||||
|
let info = TextureCacheAllocInfo {
|
||||||
|
width: TEXTURE_REGION_DIMENSIONS,
|
||||||
|
height: TEXTURE_REGION_DIMENSIONS,
|
||||||
|
format: descriptor.format,
|
||||||
|
filter: texture_array.filter,
|
||||||
|
layer_count: (num_regions + 1) as i32,
|
||||||
|
};
|
||||||
|
self.pending_updates.push_realloc(texture_array.texture_id.unwrap(), info);
|
||||||
|
texture_array.regions.push(TextureRegion::new(num_regions));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !reallocated {
|
||||||
|
// Out of room. Evict.
|
||||||
|
self.expire_old_shared_entries(&descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
new_cache_entry = self.allocate_from_shared_cache(
|
new_cache_entry = self.allocate_from_shared_cache(
|
||||||
&descriptor,
|
&descriptor,
|
||||||
|
@ -852,20 +863,15 @@ impl TextureCache {
|
||||||
let texture_id = self.next_id;
|
let texture_id = self.next_id;
|
||||||
self.next_id.0 += 1;
|
self.next_id.0 += 1;
|
||||||
|
|
||||||
// Create an update operation to allocate device storage
|
// Push a command to allocate device storage of the right size / format.
|
||||||
// of the right size / format.
|
let info = TextureCacheAllocInfo {
|
||||||
let update_op = TextureUpdate {
|
width: descriptor.size.width,
|
||||||
id: texture_id,
|
height: descriptor.size.height,
|
||||||
op: TextureUpdateOp::Create {
|
format: descriptor.format,
|
||||||
width: descriptor.size.width,
|
filter,
|
||||||
height: descriptor.size.height,
|
layer_count: 1,
|
||||||
format: descriptor.format,
|
|
||||||
filter,
|
|
||||||
render_target: Some(RenderTargetInfo { has_depth: false }),
|
|
||||||
layer_count: 1,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
self.pending_updates.push(update_op);
|
self.pending_updates.push_alloc(texture_id, info);
|
||||||
|
|
||||||
new_cache_entry = Some(CacheEntry::new_standalone(
|
new_cache_entry = Some(CacheEntry::new_standalone(
|
||||||
texture_id,
|
texture_id,
|
||||||
|
@ -976,28 +982,25 @@ impl TextureLocation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A region is a sub-rect of a texture array layer.
|
/// A region corresponds to a layer in a shared cache texture.
|
||||||
// All allocations within a region are of the same size.
|
///
|
||||||
|
/// All allocations within a region are of the same size.
|
||||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||||
struct TextureRegion {
|
struct TextureRegion {
|
||||||
layer_index: i32,
|
layer_index: usize,
|
||||||
region_size: u32,
|
|
||||||
slab_size: SlabSize,
|
slab_size: SlabSize,
|
||||||
free_slots: Vec<TextureLocation>,
|
free_slots: Vec<TextureLocation>,
|
||||||
total_slot_count: usize,
|
total_slot_count: usize,
|
||||||
origin: DeviceUintPoint,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureRegion {
|
impl TextureRegion {
|
||||||
fn new(region_size: u32, layer_index: i32, origin: DeviceUintPoint) -> Self {
|
fn new(layer_index: usize) -> Self {
|
||||||
TextureRegion {
|
TextureRegion {
|
||||||
layer_index,
|
layer_index,
|
||||||
region_size,
|
|
||||||
slab_size: SlabSize::invalid(),
|
slab_size: SlabSize::invalid(),
|
||||||
free_slots: Vec::new(),
|
free_slots: Vec::new(),
|
||||||
total_slot_count: 0,
|
total_slot_count: 0,
|
||||||
origin,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1007,8 +1010,8 @@ impl TextureRegion {
|
||||||
debug_assert!(self.free_slots.is_empty());
|
debug_assert!(self.free_slots.is_empty());
|
||||||
|
|
||||||
self.slab_size = slab_size;
|
self.slab_size = slab_size;
|
||||||
let slots_per_x_axis = self.region_size / self.slab_size.width;
|
let slots_per_x_axis = TEXTURE_REGION_DIMENSIONS / self.slab_size.width;
|
||||||
let slots_per_y_axis = self.region_size / self.slab_size.height;
|
let slots_per_y_axis = TEXTURE_REGION_DIMENSIONS / self.slab_size.height;
|
||||||
|
|
||||||
// Add each block to a freelist.
|
// Add each block to a freelist.
|
||||||
for y in 0 .. slots_per_y_axis {
|
for y in 0 .. slots_per_y_axis {
|
||||||
|
@ -1038,16 +1041,16 @@ impl TextureRegion {
|
||||||
|
|
||||||
self.free_slots.pop().map(|location| {
|
self.free_slots.pop().map(|location| {
|
||||||
DeviceUintPoint::new(
|
DeviceUintPoint::new(
|
||||||
self.origin.x + self.slab_size.width * location.0 as u32,
|
self.slab_size.width * location.0 as u32,
|
||||||
self.origin.y + self.slab_size.height * location.1 as u32,
|
self.slab_size.height * location.1 as u32,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free a block in this region.
|
// Free a block in this region.
|
||||||
fn free(&mut self, point: DeviceUintPoint) {
|
fn free(&mut self, point: DeviceUintPoint) {
|
||||||
let x = (point.x - self.origin.x) / self.slab_size.width;
|
let x = point.x / self.slab_size.width;
|
||||||
let y = (point.y - self.origin.y) / self.slab_size.height;
|
let y = point.y / self.slab_size.height;
|
||||||
self.free_slots.push(TextureLocation::new(x, y));
|
self.free_slots.push(TextureLocation::new(x, y));
|
||||||
|
|
||||||
// If this region is completely unused, deinit it
|
// If this region is completely unused, deinit it
|
||||||
|
@ -1059,17 +1062,14 @@ impl TextureRegion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A texture array contains a number of texture layers, where
|
/// A texture array contains a number of texture layers, where each layer
|
||||||
// each layer contains one or more regions that can act
|
/// contains a region that can act as a slab allocator.
|
||||||
// as slab allocators.
|
|
||||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||||
struct TextureArray {
|
struct TextureArray {
|
||||||
filter: TextureFilter,
|
filter: TextureFilter,
|
||||||
dimensions: u32,
|
max_layer_count: usize,
|
||||||
layer_count: usize,
|
|
||||||
format: ImageFormat,
|
format: ImageFormat,
|
||||||
is_allocated: bool,
|
|
||||||
regions: Vec<TextureRegion>,
|
regions: Vec<TextureRegion>,
|
||||||
texture_id: Option<CacheTextureId>,
|
texture_id: Option<CacheTextureId>,
|
||||||
}
|
}
|
||||||
|
@ -1078,31 +1078,30 @@ impl TextureArray {
|
||||||
fn new(
|
fn new(
|
||||||
format: ImageFormat,
|
format: ImageFormat,
|
||||||
filter: TextureFilter,
|
filter: TextureFilter,
|
||||||
dimensions: u32,
|
max_layer_count: usize,
|
||||||
layer_count: usize,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
TextureArray {
|
TextureArray {
|
||||||
format,
|
format,
|
||||||
filter,
|
filter,
|
||||||
dimensions,
|
max_layer_count,
|
||||||
layer_count,
|
|
||||||
is_allocated: false,
|
|
||||||
regions: Vec::new(),
|
regions: Vec::new(),
|
||||||
texture_id: None,
|
texture_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) -> Option<CacheTextureId> {
|
fn clear(&mut self, updates: &mut TextureUpdateList) {
|
||||||
self.is_allocated = false;
|
|
||||||
self.regions.clear();
|
self.regions.clear();
|
||||||
self.texture_id.take()
|
if let Some(id) = self.texture_id.take() {
|
||||||
|
updates.push_free(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_profile(&self, counter: &mut ResourceProfileCounter) {
|
fn update_profile(&self, counter: &mut ResourceProfileCounter) {
|
||||||
if self.is_allocated {
|
let layer_count = self.regions.len();
|
||||||
let size = self.layer_count as u32 * self.dimensions *
|
if layer_count != 0 {
|
||||||
self.dimensions * self.format.bytes_per_pixel();
|
let size = layer_count as u32 * TEXTURE_REGION_DIMENSIONS *
|
||||||
counter.set(self.layer_count as usize, size as usize);
|
TEXTURE_REGION_DIMENSIONS * self.format.bytes_per_pixel();
|
||||||
|
counter.set(layer_count as usize, size as usize);
|
||||||
} else {
|
} else {
|
||||||
counter.set(0, 0);
|
counter.set(0, 0);
|
||||||
}
|
}
|
||||||
|
@ -1116,31 +1115,6 @@ impl TextureArray {
|
||||||
frame_id: FrameId,
|
frame_id: FrameId,
|
||||||
uv_rect_kind: UvRectKind,
|
uv_rect_kind: UvRectKind,
|
||||||
) -> Option<CacheEntry> {
|
) -> Option<CacheEntry> {
|
||||||
// Lazily allocate the regions if not already created.
|
|
||||||
// This means that very rarely used image formats can be
|
|
||||||
// added but won't allocate a cache if never used.
|
|
||||||
if !self.is_allocated {
|
|
||||||
debug_assert!(self.dimensions % TEXTURE_REGION_DIMENSIONS == 0);
|
|
||||||
let regions_per_axis = self.dimensions / TEXTURE_REGION_DIMENSIONS;
|
|
||||||
for layer_index in 0 .. self.layer_count {
|
|
||||||
for y in 0 .. regions_per_axis {
|
|
||||||
for x in 0 .. regions_per_axis {
|
|
||||||
let origin = DeviceUintPoint::new(
|
|
||||||
x * TEXTURE_REGION_DIMENSIONS,
|
|
||||||
y * TEXTURE_REGION_DIMENSIONS,
|
|
||||||
);
|
|
||||||
let region = TextureRegion::new(
|
|
||||||
TEXTURE_REGION_DIMENSIONS,
|
|
||||||
layer_index as i32,
|
|
||||||
origin
|
|
||||||
);
|
|
||||||
self.regions.push(region);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.is_allocated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quantize the size of the allocation to select a region to
|
// Quantize the size of the allocation to select a region to
|
||||||
// allocate from.
|
// allocate from.
|
||||||
let slab_size = SlabSize::new(size);
|
let slab_size = SlabSize::new(size);
|
||||||
|
@ -1164,8 +1138,7 @@ impl TextureArray {
|
||||||
} else if region.slab_size == slab_size {
|
} else if region.slab_size == slab_size {
|
||||||
if let Some(location) = region.alloc() {
|
if let Some(location) = region.alloc() {
|
||||||
entry_kind = Some(EntryKind::Cache {
|
entry_kind = Some(EntryKind::Cache {
|
||||||
layer_index: region.layer_index as u16,
|
layer_index: region.layer_index,
|
||||||
region_index: i as u16,
|
|
||||||
origin: location,
|
origin: location,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -1180,8 +1153,7 @@ impl TextureArray {
|
||||||
region.init(slab_size);
|
region.init(slab_size);
|
||||||
entry_kind = region.alloc().map(|location| {
|
entry_kind = region.alloc().map(|location| {
|
||||||
EntryKind::Cache {
|
EntryKind::Cache {
|
||||||
layer_index: region.layer_index as u16,
|
layer_index: region.layer_index,
|
||||||
region_index: empty_region_index as u16,
|
|
||||||
origin: location,
|
origin: location,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1206,8 +1178,8 @@ impl TextureArray {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureUpdate {
|
impl TextureCacheUpdate {
|
||||||
// Constructs a TextureUpdate operation to be passed to the
|
// Constructs a TextureCacheUpdate operation to be passed to the
|
||||||
// rendering thread in order to do an upload to the right
|
// rendering thread in order to do an upload to the right
|
||||||
// location in the texture cache.
|
// location in the texture cache.
|
||||||
fn new_update(
|
fn new_update(
|
||||||
|
@ -1218,7 +1190,7 @@ impl TextureUpdate {
|
||||||
texture_id: CacheTextureId,
|
texture_id: CacheTextureId,
|
||||||
layer_index: i32,
|
layer_index: i32,
|
||||||
dirty_rect: Option<DeviceUintRect>,
|
dirty_rect: Option<DeviceUintRect>,
|
||||||
) -> TextureUpdate {
|
) -> TextureCacheUpdate {
|
||||||
let source = match data {
|
let source = match data {
|
||||||
ImageData::Blob(..) => {
|
ImageData::Blob(..) => {
|
||||||
panic!("The vector image should have been rasterized.");
|
panic!("The vector image should have been rasterized.");
|
||||||
|
@ -1248,7 +1220,8 @@ impl TextureUpdate {
|
||||||
let stride = descriptor.compute_stride();
|
let stride = descriptor.compute_stride();
|
||||||
let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x * descriptor.format.bytes_per_pixel();
|
let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x * descriptor.format.bytes_per_pixel();
|
||||||
|
|
||||||
TextureUpdateOp::Update {
|
TextureCacheUpdate {
|
||||||
|
id: texture_id,
|
||||||
rect: DeviceUintRect::new(
|
rect: DeviceUintRect::new(
|
||||||
DeviceUintPoint::new(origin.x + dirty.origin.x, origin.y + dirty.origin.y),
|
DeviceUintPoint::new(origin.x + dirty.origin.x, origin.y + dirty.origin.y),
|
||||||
DeviceUintSize::new(
|
DeviceUintSize::new(
|
||||||
|
@ -1263,7 +1236,8 @@ impl TextureUpdate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
TextureUpdateOp::Update {
|
TextureCacheUpdate {
|
||||||
|
id: texture_id,
|
||||||
rect: DeviceUintRect::new(origin, size),
|
rect: DeviceUintRect::new(origin, size),
|
||||||
source,
|
source,
|
||||||
stride: descriptor.stride,
|
stride: descriptor.stride,
|
||||||
|
@ -1273,10 +1247,7 @@ impl TextureUpdate {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TextureUpdate {
|
update_op
|
||||||
id: texture_id,
|
|
||||||
op: update_op,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
46ee0ffd00da268faf04e3263ad41733f58a6573
|
ab887f2ed4d5d378bb7536b1d721bff45c0ad0e6
|
||||||
|
|
|
@ -5506,7 +5506,10 @@ GlobalLexicals(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (!JS_GetPropertyById(cx, globalLexical, id, &val)) {
|
if (!JS_GetPropertyById(cx, globalLexical, id, &val)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!JS_SetPropertyById(cx, res, id, val)) {
|
if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!JS_DefinePropertyById(cx, res, id, val, JSPROP_ENUMERATE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,15 @@ const y = 2;
|
||||||
var z = 3;
|
var z = 3;
|
||||||
|
|
||||||
var obj = globalLexicals();
|
var obj = globalLexicals();
|
||||||
|
assertEq(Object.keys(obj).length >= 3, true);
|
||||||
assertEq(obj.Foo, Foo);
|
assertEq(obj.Foo, Foo);
|
||||||
assertEq(obj.x, 1);
|
assertEq(obj.x, 1);
|
||||||
assertEq(obj.y, 2);
|
assertEq(obj.y, 2);
|
||||||
assertEq("z" in obj, false);
|
assertEq("z" in obj, false);
|
||||||
|
|
||||||
|
assertEq("uninit" in obj, false);
|
||||||
|
let uninit;
|
||||||
|
|
||||||
// It's just a copy.
|
// It's just a copy.
|
||||||
obj.x = 2;
|
obj.x = 2;
|
||||||
assertEq(x, 1);
|
assertEq(x, 1);
|
||||||
|
|
|
@ -204,6 +204,7 @@ class CompileInfo
|
||||||
: script_(script), fun_(fun), osrPc_(osrPc),
|
: script_(script), fun_(fun), osrPc_(osrPc),
|
||||||
analysisMode_(analysisMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
|
analysisMode_(analysisMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
|
||||||
hadOverflowBailout_(script->hadOverflowBailout()),
|
hadOverflowBailout_(script->hadOverflowBailout()),
|
||||||
|
hadFrequentBailouts_(script->hadFrequentBailouts()),
|
||||||
mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
|
mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
|
||||||
inlineScriptTree_(inlineScriptTree)
|
inlineScriptTree_(inlineScriptTree)
|
||||||
{
|
{
|
||||||
|
@ -256,7 +257,7 @@ class CompileInfo
|
||||||
explicit CompileInfo(unsigned nlocals)
|
explicit CompileInfo(unsigned nlocals)
|
||||||
: script_(nullptr), fun_(nullptr), osrPc_(nullptr),
|
: script_(nullptr), fun_(nullptr), osrPc_(nullptr),
|
||||||
analysisMode_(Analysis_None), scriptNeedsArgsObj_(false), hadOverflowBailout_(false),
|
analysisMode_(Analysis_None), scriptNeedsArgsObj_(false), hadOverflowBailout_(false),
|
||||||
mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr),
|
hadFrequentBailouts_(false), mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr),
|
||||||
needsBodyEnvironmentObject_(false), funNeedsSomeEnvironmentObject_(false)
|
needsBodyEnvironmentObject_(false), funNeedsSomeEnvironmentObject_(false)
|
||||||
{
|
{
|
||||||
nimplicit_ = 0;
|
nimplicit_ = 0;
|
||||||
|
@ -561,6 +562,9 @@ class CompileInfo
|
||||||
bool hadOverflowBailout() const {
|
bool hadOverflowBailout() const {
|
||||||
return hadOverflowBailout_;
|
return hadOverflowBailout_;
|
||||||
}
|
}
|
||||||
|
bool hadFrequentBailouts() const {
|
||||||
|
return hadFrequentBailouts_;
|
||||||
|
}
|
||||||
bool mayReadFrameArgsDirectly() const {
|
bool mayReadFrameArgsDirectly() const {
|
||||||
return mayReadFrameArgsDirectly_;
|
return mayReadFrameArgsDirectly_;
|
||||||
}
|
}
|
||||||
|
@ -585,6 +589,7 @@ class CompileInfo
|
||||||
// Record the state of previous bailouts in order to prevent compiling the
|
// Record the state of previous bailouts in order to prevent compiling the
|
||||||
// same function identically the next time.
|
// same function identically the next time.
|
||||||
bool hadOverflowBailout_;
|
bool hadOverflowBailout_;
|
||||||
|
bool hadFrequentBailouts_;
|
||||||
|
|
||||||
bool mayReadFrameArgsDirectly_;
|
bool mayReadFrameArgsDirectly_;
|
||||||
|
|
||||||
|
|
|
@ -1503,8 +1503,7 @@ OptimizeMIR(MIRGenerator* mir)
|
||||||
// LICM can hoist instructions from conditional branches and trigger
|
// LICM can hoist instructions from conditional branches and trigger
|
||||||
// repeated bailouts. Disable it if this script is known to bailout
|
// repeated bailouts. Disable it if this script is known to bailout
|
||||||
// frequently.
|
// frequently.
|
||||||
JSScript* script = mir->info().script();
|
if (!mir->info().hadFrequentBailouts()) {
|
||||||
if (!script || !script->hadFrequentBailouts()) {
|
|
||||||
if (!LICM(mir, graph)) {
|
if (!LICM(mir, graph)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,14 +402,6 @@ class JSFunction : public js::NativeObject
|
||||||
flags_ |= NEW_SCRIPT_CLEARED;
|
flags_ |= NEW_SCRIPT_CLEARED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAsyncKind(js::FunctionAsyncKind asyncKind) {
|
|
||||||
if (isInterpretedLazy()) {
|
|
||||||
lazyScript()->setAsyncKind(asyncKind);
|
|
||||||
} else {
|
|
||||||
nonLazyScript()->setAsyncKind(asyncKind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun,
|
static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun,
|
||||||
js::MutableHandleValue v);
|
js::MutableHandleValue v);
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ JSScript::ensureHasAnalyzedArgsUsage(JSContext* cx)
|
||||||
inline bool
|
inline bool
|
||||||
JSScript::isDebuggee() const
|
JSScript::isDebuggee() const
|
||||||
{
|
{
|
||||||
return realm_->debuggerObservesAllExecution() || bitFields_.hasDebugScript_;
|
return realm_->debuggerObservesAllExecution() || hasDebugScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
|
|
|
@ -335,6 +335,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
||||||
HandleScriptSourceObject sourceObjectArg, HandleFunction fun,
|
HandleScriptSourceObject sourceObjectArg, HandleFunction fun,
|
||||||
MutableHandleScript scriptp)
|
MutableHandleScript scriptp)
|
||||||
{
|
{
|
||||||
|
using ImmutableFlags = JSScript::ImmutableFlags;
|
||||||
|
|
||||||
/* NB: Keep this in sync with CopyScript. */
|
/* NB: Keep this in sync with CopyScript. */
|
||||||
|
|
||||||
enum ScriptBits {
|
enum ScriptBits {
|
||||||
|
@ -610,19 +612,19 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
||||||
scriptp.set(script);
|
scriptp.set(script);
|
||||||
|
|
||||||
if (scriptBits & (1 << Strict)) {
|
if (scriptBits & (1 << Strict)) {
|
||||||
script->bitFields_.strict_ = true;
|
script->setFlag(ImmutableFlags::Strict);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << ExplicitUseStrict)) {
|
if (scriptBits & (1 << ExplicitUseStrict)) {
|
||||||
script->bitFields_.explicitUseStrict_ = true;
|
script->setFlag(ImmutableFlags::ExplicitUseStrict);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << ContainsDynamicNameAccess)) {
|
if (scriptBits & (1 << ContainsDynamicNameAccess)) {
|
||||||
script->bitFields_.bindingsAccessedDynamically_ = true;
|
script->setFlag(ImmutableFlags::BindingsAccessedDynamically);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << FunHasExtensibleScope)) {
|
if (scriptBits & (1 << FunHasExtensibleScope)) {
|
||||||
script->bitFields_.funHasExtensibleScope_ = true;
|
script->setFlag(ImmutableFlags::FunHasExtensibleScope);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << FunHasAnyAliasedFormal)) {
|
if (scriptBits & (1 << FunHasAnyAliasedFormal)) {
|
||||||
script->bitFields_.funHasAnyAliasedFormal_ = true;
|
script->setFlag(ImmutableFlags::FunHasAnyAliasedFormal);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << ArgumentsHasVarBinding)) {
|
if (scriptBits & (1 << ArgumentsHasVarBinding)) {
|
||||||
script->setArgumentsHasVarBinding();
|
script->setArgumentsHasVarBinding();
|
||||||
|
@ -631,43 +633,43 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
||||||
script->setNeedsArgsObj(true);
|
script->setNeedsArgsObj(true);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << HasMappedArgsObj)) {
|
if (scriptBits & (1 << HasMappedArgsObj)) {
|
||||||
script->bitFields_.hasMappedArgsObj_ = true;
|
script->setFlag(ImmutableFlags::HasMappedArgsObj);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << FunctionHasThisBinding)) {
|
if (scriptBits & (1 << FunctionHasThisBinding)) {
|
||||||
script->bitFields_.functionHasThisBinding_ = true;
|
script->setFlag(ImmutableFlags::FunctionHasThisBinding);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << FunctionHasExtraBodyVarScope)) {
|
if (scriptBits & (1 << FunctionHasExtraBodyVarScope)) {
|
||||||
script->bitFields_.functionHasExtraBodyVarScope_ = true;
|
script->setFlag(ImmutableFlags::FunctionHasExtraBodyVarScope);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << HasSingleton)) {
|
if (scriptBits & (1 << HasSingleton)) {
|
||||||
script->bitFields_.hasSingletons_ = true;
|
script->setFlag(ImmutableFlags::HasSingletons);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << TreatAsRunOnce)) {
|
if (scriptBits & (1 << TreatAsRunOnce)) {
|
||||||
script->bitFields_.treatAsRunOnce_ = true;
|
script->setFlag(ImmutableFlags::TreatAsRunOnce);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << HasNonSyntacticScope)) {
|
if (scriptBits & (1 << HasNonSyntacticScope)) {
|
||||||
script->bitFields_.hasNonSyntacticScope_ = true;
|
script->setFlag(ImmutableFlags::HasNonSyntacticScope);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << HasInnerFunctions)) {
|
if (scriptBits & (1 << HasInnerFunctions)) {
|
||||||
script->bitFields_.hasInnerFunctions_ = true;
|
script->setFlag(ImmutableFlags::HasInnerFunctions);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << NeedsHomeObject)) {
|
if (scriptBits & (1 << NeedsHomeObject)) {
|
||||||
script->bitFields_.needsHomeObject_ = true;
|
script->setFlag(ImmutableFlags::NeedsHomeObject);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << IsDerivedClassConstructor)) {
|
if (scriptBits & (1 << IsDerivedClassConstructor)) {
|
||||||
script->bitFields_.isDerivedClassConstructor_ = true;
|
script->setFlag(ImmutableFlags::IsDerivedClassConstructor);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << IsDefaultClassConstructor)) {
|
if (scriptBits & (1 << IsDefaultClassConstructor)) {
|
||||||
script->bitFields_.isDefaultClassConstructor_ = true;
|
script->setFlag(ImmutableFlags::IsDefaultClassConstructor);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << IsGenerator)) {
|
if (scriptBits & (1 << IsGenerator)) {
|
||||||
script->setGeneratorKind(GeneratorKind::Generator);
|
script->setFlag(ImmutableFlags::IsGenerator);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << IsAsync)) {
|
if (scriptBits & (1 << IsAsync)) {
|
||||||
script->setAsyncKind(FunctionAsyncKind::AsyncFunction);
|
script->setFlag(ImmutableFlags::IsAsync);
|
||||||
}
|
}
|
||||||
if (scriptBits & (1 << HasRest)) {
|
if (scriptBits & (1 << HasRest)) {
|
||||||
script->setHasRest();
|
script->setFlag(ImmutableFlags::HasRest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1109,7 +1111,7 @@ JSScript::setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start,
|
||||||
column_ = column;
|
column_ = column;
|
||||||
// Since this script has been changed to point into the user's source, we
|
// Since this script has been changed to point into the user's source, we
|
||||||
// can clear its self-hosted flag, allowing Debugger to see it.
|
// can clear its self-hosted flag, allowing Debugger to see it.
|
||||||
bitFields_.selfHosted_ = false;
|
clearFlag(ImmutableFlags::SelfHosted);
|
||||||
}
|
}
|
||||||
|
|
||||||
js::ScriptSourceObject&
|
js::ScriptSourceObject&
|
||||||
|
@ -1186,7 +1188,7 @@ JSScript::initScriptCounts(JSContext* cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// safe to set this; we can't fail after this point.
|
// safe to set this; we can't fail after this point.
|
||||||
bitFields_.hasScriptCounts_ = true;
|
setFlag(MutableFlags::HasScriptCounts);
|
||||||
|
|
||||||
// Enable interrupts in any interpreter frames running on this script. This
|
// Enable interrupts in any interpreter frames running on this script. This
|
||||||
// is used to let the interpreter increment the PCCounts, if present.
|
// is used to let the interpreter increment the PCCounts, if present.
|
||||||
|
@ -1412,7 +1414,7 @@ JSScript::getIonCounts()
|
||||||
void
|
void
|
||||||
JSScript::clearHasScriptCounts()
|
JSScript::clearHasScriptCounts()
|
||||||
{
|
{
|
||||||
bitFields_.hasScriptCounts_ = false;
|
clearFlag(MutableFlags::HasScriptCounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1421,7 +1423,7 @@ JSScript::releaseScriptCounts(ScriptCounts* counts)
|
||||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||||
*counts = std::move(*p->value().get());
|
*counts = std::move(*p->value().get());
|
||||||
realm()->scriptCountsMap->remove(p);
|
realm()->scriptCountsMap->remove(p);
|
||||||
bitFields_.hasScriptCounts_ = false;
|
clearHasScriptCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -3300,10 +3302,10 @@ JSScript::Create(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record compile options that get checked at runtime.
|
// Record compile options that get checked at runtime.
|
||||||
script->bitFields_.noScriptRval_ = options.noScriptRval;
|
script->setFlag(ImmutableFlags::NoScriptRval, options.noScriptRval);
|
||||||
script->bitFields_.selfHosted_ = options.selfHostingMode;
|
script->setFlag(ImmutableFlags::SelfHosted, options.selfHostingMode);
|
||||||
script->bitFields_.treatAsRunOnce_ = options.isRunOnce;
|
script->setFlag(ImmutableFlags::TreatAsRunOnce, options.isRunOnce);
|
||||||
script->bitFields_.hideScriptFromDebugger_ = options.hideScriptFromDebugger;
|
script->setFlag(ImmutableFlags::HideScriptFromDebugger, options.hideScriptFromDebugger);
|
||||||
|
|
||||||
if (cx->runtime()->lcovOutput().isEnabled()) {
|
if (cx->runtime()->lcovOutput().isEnabled()) {
|
||||||
if (!script->initScriptName(cx)) {
|
if (!script->initScriptName(cx)) {
|
||||||
|
@ -3449,9 +3451,9 @@ JSScript::initFromFunctionBox(HandleScript script, frontend::FunctionBox* funbox
|
||||||
fun->setScript(script);
|
fun->setScript(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
script->bitFields_.funHasExtensibleScope_ = funbox->hasExtensibleScope();
|
script->setFlag(ImmutableFlags::FunHasExtensibleScope, funbox->hasExtensibleScope());
|
||||||
script->bitFields_.needsHomeObject_ = funbox->needsHomeObject();
|
script->setFlag(ImmutableFlags::NeedsHomeObject, funbox->needsHomeObject());
|
||||||
script->bitFields_.isDerivedClassConstructor_ = funbox->isDerivedClassConstructor();
|
script->setFlag(ImmutableFlags::IsDerivedClassConstructor, funbox->isDerivedClassConstructor());
|
||||||
|
|
||||||
if (funbox->argumentsHasLocalBinding()) {
|
if (funbox->argumentsHasLocalBinding()) {
|
||||||
script->setArgumentsHasVarBinding();
|
script->setArgumentsHasVarBinding();
|
||||||
|
@ -3461,37 +3463,35 @@ JSScript::initFromFunctionBox(HandleScript script, frontend::FunctionBox* funbox
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
|
MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
|
||||||
}
|
}
|
||||||
script->bitFields_.hasMappedArgsObj_ = funbox->hasMappedArgsObj();
|
script->setFlag(ImmutableFlags::HasMappedArgsObj, funbox->hasMappedArgsObj());
|
||||||
|
|
||||||
script->bitFields_.functionHasThisBinding_ = funbox->hasThisBinding();
|
script->setFlag(ImmutableFlags::FunctionHasThisBinding, funbox->hasThisBinding());
|
||||||
script->bitFields_.functionHasExtraBodyVarScope_ = funbox->hasExtraBodyVarScope();
|
script->setFlag(ImmutableFlags::FunctionHasExtraBodyVarScope, funbox->hasExtraBodyVarScope());
|
||||||
|
|
||||||
script->funLength_ = funbox->length;
|
script->funLength_ = funbox->length;
|
||||||
|
|
||||||
script->setGeneratorKind(funbox->generatorKind());
|
script->setFlag(ImmutableFlags::IsGenerator, funbox->isGenerator());
|
||||||
script->setAsyncKind(funbox->asyncKind());
|
script->setFlag(ImmutableFlags::IsAsync, funbox->isAsync());
|
||||||
if (funbox->hasRest()) {
|
script->setFlag(ImmutableFlags::HasRest, funbox->hasRest());
|
||||||
script->setHasRest();
|
|
||||||
}
|
|
||||||
|
|
||||||
PositionalFormalParameterIter fi(script);
|
PositionalFormalParameterIter fi(script);
|
||||||
while (fi && !fi.closedOver()) {
|
while (fi && !fi.closedOver()) {
|
||||||
fi++;
|
fi++;
|
||||||
}
|
}
|
||||||
script->bitFields_.funHasAnyAliasedFormal_ = !!fi;
|
script->setFlag(ImmutableFlags::FunHasAnyAliasedFormal, !!fi);
|
||||||
|
|
||||||
script->setHasInnerFunctions(funbox->hasInnerFunctions());
|
script->setFlag(ImmutableFlags::HasInnerFunctions, funbox->hasInnerFunctions());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
JSScript::initFromModuleContext(HandleScript script)
|
JSScript::initFromModuleContext(HandleScript script)
|
||||||
{
|
{
|
||||||
script->bitFields_.funHasExtensibleScope_ = false;
|
script->clearFlag(ImmutableFlags::FunHasExtensibleScope);
|
||||||
script->bitFields_.needsHomeObject_ = false;
|
script->clearFlag(ImmutableFlags::NeedsHomeObject);
|
||||||
script->bitFields_.isDerivedClassConstructor_ = false;
|
script->clearFlag(ImmutableFlags::IsDerivedClassConstructor);
|
||||||
script->funLength_ = 0;
|
script->funLength_ = 0;
|
||||||
|
|
||||||
script->setGeneratorKind(GeneratorKind::NotGenerator);
|
script->clearFlag(ImmutableFlags::IsGenerator);
|
||||||
|
|
||||||
// Since modules are only run once, mark the script so that initializers
|
// Since modules are only run once, mark the script so that initializers
|
||||||
// created within it may be given more precise types.
|
// created within it may be given more precise types.
|
||||||
|
@ -3571,16 +3571,17 @@ JSScript::fullyInitFromEmitter(JSContext* cx, HandleScript script, frontend::Byt
|
||||||
bce->resumeOffsetList.finish(data->resumeOffsets(), prologueLength);
|
bce->resumeOffsetList.finish(data->resumeOffsets(), prologueLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
script->bitFields_.strict_ = bce->sc->strict();
|
script->setFlag(ImmutableFlags::Strict, bce->sc->strict());
|
||||||
script->bitFields_.explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
|
script->setFlag(ImmutableFlags::ExplicitUseStrict, bce->sc->hasExplicitUseStrict());
|
||||||
script->bitFields_.bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
|
script->setFlag(ImmutableFlags::BindingsAccessedDynamically,
|
||||||
script->bitFields_.hasSingletons_ = bce->hasSingletons;
|
bce->sc->bindingsAccessedDynamically());
|
||||||
|
script->setFlag(ImmutableFlags::HasSingletons, bce->hasSingletons);
|
||||||
|
|
||||||
script->nfixed_ = bce->maxFixedSlots;
|
script->nfixed_ = bce->maxFixedSlots;
|
||||||
script->nslots_ = nslots;
|
script->nslots_ = nslots;
|
||||||
script->bodyScopeIndex_ = bce->bodyScopeIndex;
|
script->bodyScopeIndex_ = bce->bodyScopeIndex;
|
||||||
script->bitFields_.hasNonSyntacticScope_ =
|
script->setFlag(ImmutableFlags::HasNonSyntacticScope,
|
||||||
bce->outermostScope()->hasOnChain(ScopeKind::NonSyntactic);
|
bce->outermostScope()->hasOnChain(ScopeKind::NonSyntactic));
|
||||||
|
|
||||||
// There shouldn't be any fallible operation after initFromFunctionBox,
|
// There shouldn't be any fallible operation after initFromFunctionBox,
|
||||||
// JSFunction::hasUncompletedScript relies on the fact that the existence
|
// JSFunction::hasUncompletedScript relies on the fact that the existence
|
||||||
|
@ -4017,6 +4018,8 @@ bool
|
||||||
js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
|
js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
|
||||||
MutableHandle<GCVector<Scope*>> scopes)
|
MutableHandle<GCVector<Scope*>> scopes)
|
||||||
{
|
{
|
||||||
|
using ImmutableFlags = JSScript::ImmutableFlags;
|
||||||
|
|
||||||
if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
|
if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
|
||||||
JS_ReportErrorASCII(cx, "No cloning toplevel run-once scripts");
|
JS_ReportErrorASCII(cx, "No cloning toplevel run-once scripts");
|
||||||
return false;
|
return false;
|
||||||
|
@ -4129,25 +4132,25 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
|
||||||
dst->setNeedsArgsObj(src->needsArgsObj());
|
dst->setNeedsArgsObj(src->needsArgsObj());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dst->bitFields_.hasMappedArgsObj_ = src->hasMappedArgsObj();
|
dst->setFlag(ImmutableFlags::HasMappedArgsObj, src->hasMappedArgsObj());
|
||||||
dst->bitFields_.functionHasThisBinding_ = src->functionHasThisBinding();
|
dst->setFlag(ImmutableFlags::FunctionHasThisBinding, src->functionHasThisBinding());
|
||||||
dst->bitFields_.functionHasExtraBodyVarScope_ = src->functionHasExtraBodyVarScope();
|
dst->setFlag(ImmutableFlags::FunctionHasExtraBodyVarScope, src->functionHasExtraBodyVarScope());
|
||||||
dst->bitFields_.strict_ = src->strict();
|
dst->setFlag(ImmutableFlags::Strict, src->strict());
|
||||||
dst->bitFields_.explicitUseStrict_ = src->explicitUseStrict();
|
dst->setFlag(ImmutableFlags::ExplicitUseStrict, src->explicitUseStrict());
|
||||||
dst->bitFields_.hasNonSyntacticScope_ = scopes[0]->hasOnChain(ScopeKind::NonSyntactic);
|
dst->setFlag(ImmutableFlags::HasNonSyntacticScope, scopes[0]->hasOnChain(ScopeKind::NonSyntactic));
|
||||||
dst->bitFields_.bindingsAccessedDynamically_ = src->bindingsAccessedDynamically();
|
dst->setFlag(ImmutableFlags::BindingsAccessedDynamically, src->bindingsAccessedDynamically());
|
||||||
dst->bitFields_.funHasExtensibleScope_ = src->funHasExtensibleScope();
|
dst->setFlag(ImmutableFlags::FunHasExtensibleScope, src->funHasExtensibleScope());
|
||||||
dst->bitFields_.funHasAnyAliasedFormal_ = src->funHasAnyAliasedFormal();
|
dst->setFlag(ImmutableFlags::FunHasAnyAliasedFormal, src->funHasAnyAliasedFormal());
|
||||||
dst->bitFields_.hasSingletons_ = src->hasSingletons();
|
dst->setFlag(ImmutableFlags::HasSingletons, src->hasSingletons());
|
||||||
dst->bitFields_.treatAsRunOnce_ = src->treatAsRunOnce();
|
dst->setFlag(ImmutableFlags::TreatAsRunOnce, src->treatAsRunOnce());
|
||||||
dst->bitFields_.hasInnerFunctions_ = src->hasInnerFunctions();
|
dst->setFlag(ImmutableFlags::HasInnerFunctions, src->hasInnerFunctions());
|
||||||
dst->setGeneratorKind(src->generatorKind());
|
dst->setFlag(ImmutableFlags::IsGenerator, src->isGenerator());
|
||||||
dst->bitFields_.isDerivedClassConstructor_ = src->isDerivedClassConstructor();
|
dst->setFlag(ImmutableFlags::IsDerivedClassConstructor, src->isDerivedClassConstructor());
|
||||||
dst->bitFields_.needsHomeObject_ = src->needsHomeObject();
|
dst->setFlag(ImmutableFlags::NeedsHomeObject, src->needsHomeObject());
|
||||||
dst->bitFields_.isDefaultClassConstructor_ = src->isDefaultClassConstructor();
|
dst->setFlag(ImmutableFlags::IsDefaultClassConstructor, src->isDefaultClassConstructor());
|
||||||
dst->bitFields_.isAsync_ = src->bitFields_.isAsync_;
|
dst->setFlag(ImmutableFlags::IsAsync, src->isAsync());
|
||||||
dst->bitFields_.hasRest_ = src->bitFields_.hasRest_;
|
dst->setFlag(ImmutableFlags::HasRest, src->hasRest());
|
||||||
dst->bitFields_.hideScriptFromDebugger_ = src->bitFields_.hideScriptFromDebugger_;
|
dst->setFlag(ImmutableFlags::HideScriptFromDebugger, src->hideScriptFromDebugger());
|
||||||
|
|
||||||
{
|
{
|
||||||
auto array = dst->data_->scopes();
|
auto array = dst->data_->scopes();
|
||||||
|
@ -4294,7 +4297,7 @@ js::CloneScriptIntoFunction(JSContext* cx, HandleScope enclosingScope, HandleFun
|
||||||
DebugScript*
|
DebugScript*
|
||||||
JSScript::debugScript()
|
JSScript::debugScript()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(bitFields_.hasDebugScript_);
|
MOZ_ASSERT(hasDebugScript());
|
||||||
DebugScriptMap* map = realm()->debugScriptMap.get();
|
DebugScriptMap* map = realm()->debugScriptMap.get();
|
||||||
MOZ_ASSERT(map);
|
MOZ_ASSERT(map);
|
||||||
DebugScriptMap::Ptr p = map->lookup(this);
|
DebugScriptMap::Ptr p = map->lookup(this);
|
||||||
|
@ -4305,21 +4308,21 @@ JSScript::debugScript()
|
||||||
DebugScript*
|
DebugScript*
|
||||||
JSScript::releaseDebugScript()
|
JSScript::releaseDebugScript()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(bitFields_.hasDebugScript_);
|
MOZ_ASSERT(hasDebugScript());
|
||||||
DebugScriptMap* map = realm()->debugScriptMap.get();
|
DebugScriptMap* map = realm()->debugScriptMap.get();
|
||||||
MOZ_ASSERT(map);
|
MOZ_ASSERT(map);
|
||||||
DebugScriptMap::Ptr p = map->lookup(this);
|
DebugScriptMap::Ptr p = map->lookup(this);
|
||||||
MOZ_ASSERT(p);
|
MOZ_ASSERT(p);
|
||||||
DebugScript* debug = p->value().release();
|
DebugScript* debug = p->value().release();
|
||||||
map->remove(p);
|
map->remove(p);
|
||||||
bitFields_.hasDebugScript_ = false;
|
clearFlag(MutableFlags::HasDebugScript);
|
||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSScript::destroyDebugScript(FreeOp* fop)
|
JSScript::destroyDebugScript(FreeOp* fop)
|
||||||
{
|
{
|
||||||
if (bitFields_.hasDebugScript_) {
|
if (hasDebugScript()) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
|
for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
|
||||||
if (BreakpointSite* site = getBreakpointSite(pc)) {
|
if (BreakpointSite* site = getBreakpointSite(pc)) {
|
||||||
|
@ -4336,7 +4339,7 @@ JSScript::destroyDebugScript(FreeOp* fop)
|
||||||
bool
|
bool
|
||||||
JSScript::ensureHasDebugScript(JSContext* cx)
|
JSScript::ensureHasDebugScript(JSContext* cx)
|
||||||
{
|
{
|
||||||
if (bitFields_.hasDebugScript_) {
|
if (hasDebugScript()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4361,7 +4364,7 @@ JSScript::ensureHasDebugScript(JSContext* cx)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bitFields_.hasDebugScript_ = true; // safe to set this; we can't fail after this point
|
setFlag(MutableFlags::HasDebugScript); // safe to set this; we can't fail after this point
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ensure that any Interpret() instances running on this script have
|
* Ensure that any Interpret() instances running on this script have
|
||||||
|
@ -4642,16 +4645,16 @@ JSScript::innermostScope(jsbytecode* pc)
|
||||||
void
|
void
|
||||||
JSScript::setArgumentsHasVarBinding()
|
JSScript::setArgumentsHasVarBinding()
|
||||||
{
|
{
|
||||||
bitFields_.argsHasVarBinding_ = true;
|
setFlag(ImmutableFlags::ArgsHasVarBinding);
|
||||||
bitFields_.needsArgsAnalysis_ = true;
|
setFlag(MutableFlags::NeedsArgsAnalysis);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSScript::setNeedsArgsObj(bool needsArgsObj)
|
JSScript::setNeedsArgsObj(bool needsArgsObj)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
|
MOZ_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
|
||||||
bitFields_.needsArgsAnalysis_ = false;
|
clearFlag(MutableFlags::NeedsArgsAnalysis);
|
||||||
bitFields_.needsArgsObj_ = needsArgsObj;
|
setFlag(MutableFlags::NeedsArgsObj, needsArgsObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -4720,7 +4723,7 @@ JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script)
|
||||||
MOZ_ASSERT(!script->isGenerator());
|
MOZ_ASSERT(!script->isGenerator());
|
||||||
MOZ_ASSERT(!script->isAsync());
|
MOZ_ASSERT(!script->isAsync());
|
||||||
|
|
||||||
script->bitFields_.needsArgsObj_ = true;
|
script->setFlag(MutableFlags::NeedsArgsObj);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we can't invalidate baseline scripts, set a flag that's checked from
|
* Since we can't invalidate baseline scripts, set a flag that's checked from
|
||||||
|
@ -5098,7 +5101,7 @@ JSScript::AutoDelazify::holdScript(JS::HandleFunction fun)
|
||||||
JSAutoRealm ar(cx_, fun);
|
JSAutoRealm ar(cx_, fun);
|
||||||
script_ = JSFunction::getOrCreateScript(cx_, fun);
|
script_ = JSFunction::getOrCreateScript(cx_, fun);
|
||||||
if (script_) {
|
if (script_) {
|
||||||
oldDoNotRelazify_ = script_->bitFields_.doNotRelazify_;
|
oldDoNotRelazify_ = script_->hasFlag(MutableFlags::DoNotRelazify);
|
||||||
script_->setDoNotRelazify(true);
|
script_->setDoNotRelazify(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1634,6 +1634,143 @@ class JSScript : public js::gc::TenuredCell
|
||||||
mozilla::Atomic<uint32_t, mozilla::Relaxed,
|
mozilla::Atomic<uint32_t, mozilla::Relaxed,
|
||||||
mozilla::recordreplay::Behavior::DontPreserve> warmUpCount = {};
|
mozilla::recordreplay::Behavior::DontPreserve> warmUpCount = {};
|
||||||
|
|
||||||
|
// Immutable flags should not be modified after this script has been
|
||||||
|
// initialized. These flags should likely be preserved when serializing
|
||||||
|
// (XDR) or copying (CopyScript) this script.
|
||||||
|
enum class ImmutableFlags : uint32_t {
|
||||||
|
// No need for result value of last expression statement.
|
||||||
|
NoScriptRval = 1 << 0,
|
||||||
|
|
||||||
|
// Code is in strict mode.
|
||||||
|
Strict = 1 << 1,
|
||||||
|
|
||||||
|
// Code has "use strict"; explicitly.
|
||||||
|
ExplicitUseStrict = 1 << 2,
|
||||||
|
|
||||||
|
// True if the script has a non-syntactic scope on its dynamic scope chain.
|
||||||
|
// That is, there are objects about which we know nothing between the
|
||||||
|
// outermost syntactic scope and the global.
|
||||||
|
HasNonSyntacticScope = 1 << 3,
|
||||||
|
|
||||||
|
// See Parser::selfHostingMode.
|
||||||
|
SelfHosted = 1 << 4,
|
||||||
|
|
||||||
|
// See FunctionBox.
|
||||||
|
BindingsAccessedDynamically = 1 << 5,
|
||||||
|
FunHasExtensibleScope = 1 << 6,
|
||||||
|
|
||||||
|
// True if any formalIsAliased(i).
|
||||||
|
FunHasAnyAliasedFormal = 1 << 7,
|
||||||
|
|
||||||
|
// Script has singleton objects.
|
||||||
|
HasSingletons = 1 << 8,
|
||||||
|
|
||||||
|
FunctionHasThisBinding = 1 << 9,
|
||||||
|
FunctionHasExtraBodyVarScope = 1 << 10,
|
||||||
|
|
||||||
|
// Whether the arguments object for this script, if it needs one, should be
|
||||||
|
// mapped (alias formal parameters).
|
||||||
|
HasMappedArgsObj = 1 << 11,
|
||||||
|
|
||||||
|
// Script contains inner functions. Used to check if we can relazify the
|
||||||
|
// script.
|
||||||
|
HasInnerFunctions = 1 << 12,
|
||||||
|
|
||||||
|
NeedsHomeObject = 1 << 13,
|
||||||
|
|
||||||
|
IsDerivedClassConstructor = 1 << 14,
|
||||||
|
IsDefaultClassConstructor = 1 << 15,
|
||||||
|
|
||||||
|
// Script is a lambda to treat as running once or a global or eval script
|
||||||
|
// that will only run once. Which one it is can be disambiguated by
|
||||||
|
// checking whether function() is null.
|
||||||
|
TreatAsRunOnce = 1 << 16,
|
||||||
|
|
||||||
|
// 'this', 'arguments' and f.apply() are used. This is likely to be a wrapper.
|
||||||
|
IsLikelyConstructorWrapper = 1 << 17,
|
||||||
|
|
||||||
|
// Set if the debugger's onNewScript hook has not yet been called.
|
||||||
|
HideScriptFromDebugger = 1 << 18,
|
||||||
|
|
||||||
|
// Set if this function is a generator function or async generator.
|
||||||
|
IsGenerator = 1 << 19,
|
||||||
|
|
||||||
|
// Set if this function is an async function or async generator.
|
||||||
|
IsAsync = 1 << 20,
|
||||||
|
|
||||||
|
// Set if this function has a rest parameter.
|
||||||
|
HasRest = 1 << 21,
|
||||||
|
|
||||||
|
// See comments below.
|
||||||
|
ArgsHasVarBinding = 1 << 22,
|
||||||
|
};
|
||||||
|
uint32_t immutableFlags_ = 0;
|
||||||
|
|
||||||
|
// Mutable flags typically store information about runtime or deoptimization
|
||||||
|
// behavior of this script.
|
||||||
|
enum class MutableFlags : uint32_t {
|
||||||
|
// Have warned about uses of undefined properties in this script.
|
||||||
|
WarnedAboutUndefinedProp = 1 << 0,
|
||||||
|
|
||||||
|
// If treatAsRunOnce, whether script has executed.
|
||||||
|
HasRunOnce = 1 << 1,
|
||||||
|
|
||||||
|
// Script has been reused for a clone.
|
||||||
|
HasBeenCloned = 1 << 2,
|
||||||
|
|
||||||
|
// Script came from eval(), and is still active.
|
||||||
|
IsActiveEval = 1 << 3,
|
||||||
|
|
||||||
|
// Script came from eval(), and is in eval cache.
|
||||||
|
IsCachedEval = 1 << 4,
|
||||||
|
|
||||||
|
// Script has an entry in Realm::scriptCountsMap.
|
||||||
|
HasScriptCounts = 1 << 5,
|
||||||
|
|
||||||
|
// Script has an entry in Realm::debugScriptMap.
|
||||||
|
HasDebugScript = 1 << 6,
|
||||||
|
|
||||||
|
// Freeze constraints for stack type sets have been generated.
|
||||||
|
HasFreezeConstraints = 1 << 7,
|
||||||
|
|
||||||
|
// Generation for this script's TypeScript. If out of sync with the
|
||||||
|
// TypeZone's generation, the TypeScript needs to be swept.
|
||||||
|
TypesGeneration = 1 << 8,
|
||||||
|
|
||||||
|
// Do not relazify this script. This is used by the relazify() testing
|
||||||
|
// function for scripts that are on the stack and also by the AutoDelazify
|
||||||
|
// RAII class. Usually we don't relazify functions in compartments with
|
||||||
|
// scripts on the stack, but the relazify() testing function overrides that,
|
||||||
|
// and sometimes we're working with a cross-compartment function and need to
|
||||||
|
// keep it from relazifying.
|
||||||
|
DoNotRelazify = 1 << 9,
|
||||||
|
|
||||||
|
// IonMonkey compilation hints.
|
||||||
|
|
||||||
|
// Script has had hoisted bounds checks fail.
|
||||||
|
FailedBoundsCheck = 1 << 10,
|
||||||
|
|
||||||
|
// Script has had hoisted shape guard fail.
|
||||||
|
FailedShapeGuard = 1 << 11,
|
||||||
|
|
||||||
|
HadFrequentBailouts = 1 << 12,
|
||||||
|
HadOverflowBailout = 1 << 13,
|
||||||
|
|
||||||
|
// Explicitly marked as uninlineable.
|
||||||
|
Uninlineable = 1 << 14,
|
||||||
|
|
||||||
|
// Idempotent cache has triggered invalidation.
|
||||||
|
InvalidatedIdempotentCache = 1 << 15,
|
||||||
|
|
||||||
|
// Lexical check did fail and bail out.
|
||||||
|
FailedLexicalCheck = 1 << 16,
|
||||||
|
|
||||||
|
// See comments below.
|
||||||
|
NeedsArgsAnalysis = 1 << 17,
|
||||||
|
NeedsArgsObj = 1 << 18,
|
||||||
|
};
|
||||||
|
uint32_t mutableFlags_ = 0;
|
||||||
|
|
||||||
// 16-bit fields.
|
// 16-bit fields.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1648,144 +1785,6 @@ class JSScript : public js::gc::TenuredCell
|
||||||
/* Number of type sets used in this script for dynamic type monitoring. */
|
/* Number of type sets used in this script for dynamic type monitoring. */
|
||||||
uint16_t nTypeSets_ = 0;
|
uint16_t nTypeSets_ = 0;
|
||||||
|
|
||||||
private:
|
|
||||||
struct BitFields
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Bit-fields can't have member initializers til C++2a, i.e. probably
|
|
||||||
* C++20, so we can't initialize these to zero in place. Instead we
|
|
||||||
* braced-init this to all zeroes in the JSScript constructor, then
|
|
||||||
* custom-assign particular bit-fields in the constructor body.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// No need for result value of last expression statement.
|
|
||||||
bool noScriptRval_ : 1;
|
|
||||||
|
|
||||||
// Code is in strict mode.
|
|
||||||
bool strict_ : 1;
|
|
||||||
|
|
||||||
// Code has "use strict"; explicitly.
|
|
||||||
bool explicitUseStrict_ : 1;
|
|
||||||
|
|
||||||
// True if the script has a non-syntactic scope on its dynamic scope chain.
|
|
||||||
// That is, there are objects about which we know nothing between the
|
|
||||||
// outermost syntactic scope and the global.
|
|
||||||
bool hasNonSyntacticScope_ : 1;
|
|
||||||
|
|
||||||
// see Parser::selfHostingMode.
|
|
||||||
bool selfHosted_ : 1;
|
|
||||||
|
|
||||||
// See FunctionBox.
|
|
||||||
bool bindingsAccessedDynamically_ : 1;
|
|
||||||
bool funHasExtensibleScope_ : 1;
|
|
||||||
|
|
||||||
// True if any formalIsAliased(i).
|
|
||||||
bool funHasAnyAliasedFormal_ : 1;
|
|
||||||
|
|
||||||
// Have warned about uses of undefined properties in this script.
|
|
||||||
bool warnedAboutUndefinedProp_ : 1;
|
|
||||||
|
|
||||||
// Script has singleton objects.
|
|
||||||
bool hasSingletons_ : 1;
|
|
||||||
|
|
||||||
// Script is a lambda to treat as running once or a global or eval script
|
|
||||||
// that will only run once. Which one it is can be disambiguated by
|
|
||||||
// checking whether function() is null.
|
|
||||||
bool treatAsRunOnce_ : 1;
|
|
||||||
|
|
||||||
// If treatAsRunOnce, whether script has executed.
|
|
||||||
bool hasRunOnce_ : 1;
|
|
||||||
|
|
||||||
// Script has been reused for a clone.
|
|
||||||
bool hasBeenCloned_ : 1;
|
|
||||||
|
|
||||||
// Script came from eval(), and is still active.
|
|
||||||
bool isActiveEval_ : 1;
|
|
||||||
|
|
||||||
// Script came from eval(), and is in eval cache.
|
|
||||||
bool isCachedEval_ : 1;
|
|
||||||
|
|
||||||
// 'this', 'arguments' and f.apply() are used. This is likely to be a wrapper.
|
|
||||||
bool isLikelyConstructorWrapper_ : 1;
|
|
||||||
|
|
||||||
// IonMonkey compilation hints.
|
|
||||||
|
|
||||||
/* Script has had hoisted bounds checks fail. */
|
|
||||||
bool failedBoundsCheck_ : 1;
|
|
||||||
|
|
||||||
/* Script has had hoisted shape guard fail. */
|
|
||||||
bool failedShapeGuard_ : 1;
|
|
||||||
|
|
||||||
bool hadFrequentBailouts_ : 1;
|
|
||||||
bool hadOverflowBailout_ : 1;
|
|
||||||
|
|
||||||
/* Explicitly marked as uninlineable. */
|
|
||||||
bool uninlineable_ : 1;
|
|
||||||
|
|
||||||
// Idempotent cache has triggered invalidation.
|
|
||||||
bool invalidatedIdempotentCache_ : 1;
|
|
||||||
|
|
||||||
// Lexical check did fail and bail out.
|
|
||||||
bool failedLexicalCheck_ : 1;
|
|
||||||
|
|
||||||
// Script has an entry in Realm::scriptCountsMap.
|
|
||||||
bool hasScriptCounts_ : 1;
|
|
||||||
|
|
||||||
// Script has an entry in Realm::debugScriptMap.
|
|
||||||
bool hasDebugScript_ : 1;
|
|
||||||
|
|
||||||
// Freeze constraints for stack type sets have been generated.
|
|
||||||
bool hasFreezeConstraints_ : 1;
|
|
||||||
|
|
||||||
/* See comments below. */
|
|
||||||
bool argsHasVarBinding_ : 1;
|
|
||||||
bool needsArgsAnalysis_ : 1;
|
|
||||||
bool needsArgsObj_ : 1;
|
|
||||||
bool functionHasThisBinding_ : 1;
|
|
||||||
bool functionHasExtraBodyVarScope_ : 1;
|
|
||||||
|
|
||||||
// Whether the arguments object for this script, if it needs one, should be
|
|
||||||
// mapped (alias formal parameters).
|
|
||||||
bool hasMappedArgsObj_ : 1;
|
|
||||||
|
|
||||||
// Generation for this script's TypeScript. If out of sync with the
|
|
||||||
// TypeZone's generation, the TypeScript needs to be swept.
|
|
||||||
//
|
|
||||||
// This should be a uint32 but is instead a bool so that MSVC packs it
|
|
||||||
// correctly.
|
|
||||||
bool typesGeneration_ : 1;
|
|
||||||
|
|
||||||
// Do not relazify this script. This is used by the relazify() testing
|
|
||||||
// function for scripts that are on the stack and also by the AutoDelazify
|
|
||||||
// RAII class. Usually we don't relazify functions in compartments with
|
|
||||||
// scripts on the stack, but the relazify() testing function overrides that,
|
|
||||||
// and sometimes we're working with a cross-compartment function and need to
|
|
||||||
// keep it from relazifying.
|
|
||||||
bool doNotRelazify_ : 1;
|
|
||||||
|
|
||||||
// Script contains inner functions. Used to check if we can relazify the
|
|
||||||
// script.
|
|
||||||
bool hasInnerFunctions_ : 1;
|
|
||||||
|
|
||||||
bool needsHomeObject_ : 1;
|
|
||||||
|
|
||||||
bool isDerivedClassConstructor_ : 1;
|
|
||||||
bool isDefaultClassConstructor_ : 1;
|
|
||||||
|
|
||||||
// True if this function is a generator function or async generator.
|
|
||||||
bool isGenerator_ : 1;
|
|
||||||
|
|
||||||
// True if this function is an async function or async generator.
|
|
||||||
bool isAsync_ : 1;
|
|
||||||
|
|
||||||
bool hasRest_ : 1;
|
|
||||||
|
|
||||||
// True if the debugger's onNewScript hook has not yet been called.
|
|
||||||
bool hideScriptFromDebugger_ : 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
BitFields bitFields_ = {}; // Zero-initialize bitfield flags.
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// End of fields. Start methods.
|
// End of fields. Start methods.
|
||||||
//
|
//
|
||||||
|
@ -1844,6 +1843,44 @@ class JSScript : public js::gc::TenuredCell
|
||||||
void assertValidJumpTargets() const;
|
void assertValidJumpTargets() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// MutableFlags accessors.
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool hasFlag(MutableFlags flag) const {
|
||||||
|
return mutableFlags_ & uint32_t(flag);
|
||||||
|
}
|
||||||
|
void setFlag(MutableFlags flag) {
|
||||||
|
mutableFlags_ |= uint32_t(flag);
|
||||||
|
}
|
||||||
|
void setFlag(MutableFlags flag, bool b) {
|
||||||
|
if (b) {
|
||||||
|
setFlag(flag);
|
||||||
|
} else {
|
||||||
|
clearFlag(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void clearFlag(MutableFlags flag) {
|
||||||
|
mutableFlags_ &= ~uint32_t(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImmutableFlags accessors.
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool hasFlag(ImmutableFlags flag) const {
|
||||||
|
return immutableFlags_ & uint32_t(flag);
|
||||||
|
}
|
||||||
|
void setFlag(ImmutableFlags flag) {
|
||||||
|
immutableFlags_ |= uint32_t(flag);
|
||||||
|
}
|
||||||
|
void setFlag(ImmutableFlags flag, bool b) {
|
||||||
|
if (b) {
|
||||||
|
setFlag(flag);
|
||||||
|
} else {
|
||||||
|
clearFlag(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void clearFlag(ImmutableFlags flag) {
|
||||||
|
immutableFlags_ &= ~uint32_t(flag);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline JSPrincipals* principals();
|
inline JSPrincipals* principals();
|
||||||
|
|
||||||
|
@ -1998,113 +2035,117 @@ class JSScript : public js::gc::TenuredCell
|
||||||
}
|
}
|
||||||
|
|
||||||
bool noScriptRval() const {
|
bool noScriptRval() const {
|
||||||
return bitFields_.noScriptRval_;
|
return hasFlag(ImmutableFlags::NoScriptRval);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool strict() const {
|
bool strict() const {
|
||||||
return bitFields_.strict_;
|
return hasFlag(ImmutableFlags::Strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool explicitUseStrict() const { return bitFields_.explicitUseStrict_; }
|
bool explicitUseStrict() const { return hasFlag(ImmutableFlags::ExplicitUseStrict); }
|
||||||
|
|
||||||
bool hasNonSyntacticScope() const {
|
bool hasNonSyntacticScope() const {
|
||||||
return bitFields_.hasNonSyntacticScope_;
|
return hasFlag(ImmutableFlags::HasNonSyntacticScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool selfHosted() const { return bitFields_.selfHosted_; }
|
bool selfHosted() const { return hasFlag(ImmutableFlags::SelfHosted); }
|
||||||
bool bindingsAccessedDynamically() const { return bitFields_.bindingsAccessedDynamically_; }
|
bool bindingsAccessedDynamically() const {
|
||||||
|
return hasFlag(ImmutableFlags::BindingsAccessedDynamically);
|
||||||
|
}
|
||||||
bool funHasExtensibleScope() const {
|
bool funHasExtensibleScope() const {
|
||||||
return bitFields_.funHasExtensibleScope_;
|
return hasFlag(ImmutableFlags::FunHasExtensibleScope);
|
||||||
}
|
}
|
||||||
bool funHasAnyAliasedFormal() const {
|
bool funHasAnyAliasedFormal() const {
|
||||||
return bitFields_.funHasAnyAliasedFormal_;
|
return hasFlag(ImmutableFlags::FunHasAnyAliasedFormal);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasSingletons() const { return bitFields_.hasSingletons_; }
|
bool hasSingletons() const { return hasFlag(ImmutableFlags::HasSingletons); }
|
||||||
bool treatAsRunOnce() const {
|
bool treatAsRunOnce() const {
|
||||||
return bitFields_.treatAsRunOnce_;
|
return hasFlag(ImmutableFlags::TreatAsRunOnce);
|
||||||
}
|
}
|
||||||
bool hasRunOnce() const { return bitFields_.hasRunOnce_; }
|
bool hasRunOnce() const { return hasFlag(MutableFlags::HasRunOnce); }
|
||||||
bool hasBeenCloned() const { return bitFields_.hasBeenCloned_; }
|
bool hasBeenCloned() const { return hasFlag(MutableFlags::HasBeenCloned); }
|
||||||
|
|
||||||
void setTreatAsRunOnce() { bitFields_.treatAsRunOnce_ = true; }
|
void setTreatAsRunOnce() { setFlag(ImmutableFlags::TreatAsRunOnce); }
|
||||||
void setHasRunOnce() { bitFields_.hasRunOnce_ = true; }
|
void setHasRunOnce() { setFlag(MutableFlags::HasRunOnce); }
|
||||||
void setHasBeenCloned() { bitFields_.hasBeenCloned_ = true; }
|
void setHasBeenCloned() { setFlag(MutableFlags::HasBeenCloned); }
|
||||||
|
|
||||||
bool isActiveEval() const { return bitFields_.isActiveEval_; }
|
bool isActiveEval() const { return hasFlag(MutableFlags::IsActiveEval); }
|
||||||
bool isCachedEval() const { return bitFields_.isCachedEval_; }
|
bool isCachedEval() const { return hasFlag(MutableFlags::IsCachedEval); }
|
||||||
|
|
||||||
void cacheForEval() {
|
void cacheForEval() {
|
||||||
MOZ_ASSERT(isActiveEval());
|
MOZ_ASSERT(isActiveEval());
|
||||||
MOZ_ASSERT(!isCachedEval());
|
MOZ_ASSERT(!isCachedEval());
|
||||||
bitFields_.isActiveEval_ = false;
|
clearFlag(MutableFlags::IsActiveEval);
|
||||||
bitFields_.isCachedEval_ = true;
|
setFlag(MutableFlags::IsCachedEval);
|
||||||
// IsEvalCacheCandidate will make sure that there's nothing in this
|
// IsEvalCacheCandidate will make sure that there's nothing in this
|
||||||
// script that would prevent reexecution even if isRunOnce is
|
// script that would prevent reexecution even if isRunOnce is
|
||||||
// true. So just pretend like we never ran this script.
|
// true. So just pretend like we never ran this script.
|
||||||
bitFields_.hasRunOnce_ = false;
|
clearFlag(MutableFlags::HasRunOnce);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uncacheForEval() {
|
void uncacheForEval() {
|
||||||
MOZ_ASSERT(isCachedEval());
|
MOZ_ASSERT(isCachedEval());
|
||||||
MOZ_ASSERT(!isActiveEval());
|
MOZ_ASSERT(!isActiveEval());
|
||||||
bitFields_.isCachedEval_ = false;
|
clearFlag(MutableFlags::IsCachedEval);
|
||||||
bitFields_.isActiveEval_ = true;
|
setFlag(MutableFlags::IsActiveEval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setActiveEval() { bitFields_.isActiveEval_ = true; }
|
void setActiveEval() { setFlag(MutableFlags::IsActiveEval); }
|
||||||
|
|
||||||
bool isLikelyConstructorWrapper() const {
|
bool isLikelyConstructorWrapper() const {
|
||||||
return bitFields_.isLikelyConstructorWrapper_;
|
return hasFlag(ImmutableFlags::IsLikelyConstructorWrapper);
|
||||||
|
}
|
||||||
|
void setLikelyConstructorWrapper() {
|
||||||
|
setFlag(ImmutableFlags::IsLikelyConstructorWrapper);
|
||||||
}
|
}
|
||||||
void setLikelyConstructorWrapper() { bitFields_.isLikelyConstructorWrapper_ = true; }
|
|
||||||
|
|
||||||
bool failedBoundsCheck() const {
|
bool failedBoundsCheck() const {
|
||||||
return bitFields_.failedBoundsCheck_;
|
return hasFlag(MutableFlags::FailedBoundsCheck);
|
||||||
}
|
}
|
||||||
bool failedShapeGuard() const {
|
bool failedShapeGuard() const {
|
||||||
return bitFields_.failedShapeGuard_;
|
return hasFlag(MutableFlags::FailedShapeGuard);
|
||||||
}
|
}
|
||||||
bool hadFrequentBailouts() const {
|
bool hadFrequentBailouts() const {
|
||||||
return bitFields_.hadFrequentBailouts_;
|
return hasFlag(MutableFlags::HadFrequentBailouts);
|
||||||
}
|
}
|
||||||
bool hadOverflowBailout() const {
|
bool hadOverflowBailout() const {
|
||||||
return bitFields_.hadOverflowBailout_;
|
return hasFlag(MutableFlags::HadOverflowBailout);
|
||||||
}
|
}
|
||||||
bool uninlineable() const {
|
bool uninlineable() const {
|
||||||
return bitFields_.uninlineable_;
|
return hasFlag(MutableFlags::Uninlineable);
|
||||||
}
|
}
|
||||||
bool invalidatedIdempotentCache() const {
|
bool invalidatedIdempotentCache() const {
|
||||||
return bitFields_.invalidatedIdempotentCache_;
|
return hasFlag(MutableFlags::InvalidatedIdempotentCache);
|
||||||
}
|
}
|
||||||
bool failedLexicalCheck() const {
|
bool failedLexicalCheck() const {
|
||||||
return bitFields_.failedLexicalCheck_;
|
return hasFlag(MutableFlags::FailedLexicalCheck);
|
||||||
}
|
}
|
||||||
bool isDefaultClassConstructor() const {
|
bool isDefaultClassConstructor() const {
|
||||||
return bitFields_.isDefaultClassConstructor_;
|
return hasFlag(ImmutableFlags::IsDefaultClassConstructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setFailedBoundsCheck() { bitFields_.failedBoundsCheck_ = true; }
|
void setFailedBoundsCheck() { setFlag(MutableFlags::FailedBoundsCheck); }
|
||||||
void setFailedShapeGuard() { bitFields_.failedShapeGuard_ = true; }
|
void setFailedShapeGuard() { setFlag(MutableFlags::FailedShapeGuard); }
|
||||||
void setHadFrequentBailouts() { bitFields_.hadFrequentBailouts_ = true; }
|
void setHadFrequentBailouts() { setFlag(MutableFlags::HadFrequentBailouts); }
|
||||||
void setHadOverflowBailout() { bitFields_.hadOverflowBailout_ = true; }
|
void setHadOverflowBailout() { setFlag(MutableFlags::HadOverflowBailout); }
|
||||||
void setUninlineable() { bitFields_.uninlineable_ = true; }
|
void setUninlineable() { setFlag(MutableFlags::Uninlineable); }
|
||||||
void setInvalidatedIdempotentCache() { bitFields_.invalidatedIdempotentCache_ = true; }
|
void setInvalidatedIdempotentCache() { setFlag(MutableFlags::InvalidatedIdempotentCache); }
|
||||||
void setFailedLexicalCheck() { bitFields_.failedLexicalCheck_ = true; }
|
void setFailedLexicalCheck() { setFlag(MutableFlags::FailedLexicalCheck); }
|
||||||
void setIsDefaultClassConstructor() { bitFields_.isDefaultClassConstructor_ = true; }
|
void setIsDefaultClassConstructor() { setFlag(ImmutableFlags::IsDefaultClassConstructor); }
|
||||||
|
|
||||||
bool hasScriptCounts() const { return bitFields_.hasScriptCounts_; }
|
bool hasScriptCounts() const { return hasFlag(MutableFlags::HasScriptCounts); }
|
||||||
bool hasScriptName();
|
bool hasScriptName();
|
||||||
|
|
||||||
bool hasFreezeConstraints() const { return bitFields_.hasFreezeConstraints_; }
|
bool hasFreezeConstraints() const { return hasFlag(MutableFlags::HasFreezeConstraints); }
|
||||||
void setHasFreezeConstraints() { bitFields_.hasFreezeConstraints_ = true; }
|
void setHasFreezeConstraints() { setFlag(MutableFlags::HasFreezeConstraints); }
|
||||||
|
|
||||||
bool warnedAboutUndefinedProp() const { return bitFields_.warnedAboutUndefinedProp_; }
|
bool warnedAboutUndefinedProp() const { return hasFlag(MutableFlags::WarnedAboutUndefinedProp); }
|
||||||
void setWarnedAboutUndefinedProp() { bitFields_.warnedAboutUndefinedProp_ = true; }
|
void setWarnedAboutUndefinedProp() { setFlag(MutableFlags::WarnedAboutUndefinedProp); }
|
||||||
|
|
||||||
/* See ContextFlags::funArgumentsHasLocalBinding comment. */
|
/* See ContextFlags::funArgumentsHasLocalBinding comment. */
|
||||||
bool argumentsHasVarBinding() const {
|
bool argumentsHasVarBinding() const {
|
||||||
return bitFields_.argsHasVarBinding_;
|
return hasFlag(ImmutableFlags::ArgsHasVarBinding);
|
||||||
}
|
}
|
||||||
void setArgumentsHasVarBinding();
|
void setArgumentsHasVarBinding();
|
||||||
bool argumentsAliasesFormals() const {
|
bool argumentsAliasesFormals() const {
|
||||||
|
@ -2112,52 +2153,36 @@ class JSScript : public js::gc::TenuredCell
|
||||||
}
|
}
|
||||||
|
|
||||||
js::GeneratorKind generatorKind() const {
|
js::GeneratorKind generatorKind() const {
|
||||||
return bitFields_.isGenerator_ ? js::GeneratorKind::Generator : js::GeneratorKind::NotGenerator;
|
return isGenerator() ? js::GeneratorKind::Generator : js::GeneratorKind::NotGenerator;
|
||||||
}
|
|
||||||
bool isGenerator() const { return bitFields_.isGenerator_; }
|
|
||||||
void setGeneratorKind(js::GeneratorKind kind) {
|
|
||||||
// A script only gets its generator kind set as part of initialization,
|
|
||||||
// so it can only transition from not being a generator.
|
|
||||||
MOZ_ASSERT(!isGenerator());
|
|
||||||
bitFields_.isGenerator_ = kind == js::GeneratorKind::Generator;
|
|
||||||
}
|
}
|
||||||
|
bool isGenerator() const { return hasFlag(ImmutableFlags::IsGenerator); }
|
||||||
|
|
||||||
js::FunctionAsyncKind asyncKind() const {
|
js::FunctionAsyncKind asyncKind() const {
|
||||||
return bitFields_.isAsync_
|
return isAsync()
|
||||||
? js::FunctionAsyncKind::AsyncFunction
|
? js::FunctionAsyncKind::AsyncFunction
|
||||||
: js::FunctionAsyncKind::SyncFunction;
|
: js::FunctionAsyncKind::SyncFunction;
|
||||||
}
|
}
|
||||||
bool isAsync() const {
|
bool isAsync() const {
|
||||||
return bitFields_.isAsync_;
|
return hasFlag(ImmutableFlags::IsAsync);
|
||||||
}
|
|
||||||
|
|
||||||
void setAsyncKind(js::FunctionAsyncKind kind) {
|
|
||||||
bitFields_.isAsync_ = kind == js::FunctionAsyncKind::AsyncFunction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasRest() const {
|
bool hasRest() const {
|
||||||
return bitFields_.hasRest_;
|
return hasFlag(ImmutableFlags::HasRest);
|
||||||
}
|
|
||||||
void setHasRest() {
|
|
||||||
bitFields_.hasRest_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hideScriptFromDebugger() const {
|
bool hideScriptFromDebugger() const {
|
||||||
return bitFields_.hideScriptFromDebugger_;
|
return hasFlag(ImmutableFlags::HideScriptFromDebugger);
|
||||||
}
|
}
|
||||||
void clearHideScriptFromDebugger() {
|
void clearHideScriptFromDebugger() {
|
||||||
bitFields_.hideScriptFromDebugger_ = false;
|
clearFlag(ImmutableFlags::HideScriptFromDebugger);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setNeedsHomeObject() {
|
|
||||||
bitFields_.needsHomeObject_ = true;
|
|
||||||
}
|
|
||||||
bool needsHomeObject() const {
|
bool needsHomeObject() const {
|
||||||
return bitFields_.needsHomeObject_;
|
return hasFlag(ImmutableFlags::NeedsHomeObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDerivedClassConstructor() const {
|
bool isDerivedClassConstructor() const {
|
||||||
return bitFields_.isDerivedClassConstructor_;
|
return hasFlag(ImmutableFlags::IsDerivedClassConstructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2170,21 +2195,21 @@ class JSScript : public js::gc::TenuredCell
|
||||||
* maintain the invariant that needsArgsObj is only called after the script
|
* maintain the invariant that needsArgsObj is only called after the script
|
||||||
* has been analyzed.
|
* has been analyzed.
|
||||||
*/
|
*/
|
||||||
bool analyzedArgsUsage() const { return !bitFields_.needsArgsAnalysis_; }
|
bool analyzedArgsUsage() const { return !hasFlag(MutableFlags::NeedsArgsAnalysis); }
|
||||||
inline bool ensureHasAnalyzedArgsUsage(JSContext* cx);
|
inline bool ensureHasAnalyzedArgsUsage(JSContext* cx);
|
||||||
bool needsArgsObj() const {
|
bool needsArgsObj() const {
|
||||||
MOZ_ASSERT(analyzedArgsUsage());
|
MOZ_ASSERT(analyzedArgsUsage());
|
||||||
return bitFields_.needsArgsObj_;
|
return hasFlag(MutableFlags::NeedsArgsObj);
|
||||||
}
|
}
|
||||||
void setNeedsArgsObj(bool needsArgsObj);
|
void setNeedsArgsObj(bool needsArgsObj);
|
||||||
static bool argumentsOptimizationFailed(JSContext* cx, js::HandleScript script);
|
static bool argumentsOptimizationFailed(JSContext* cx, js::HandleScript script);
|
||||||
|
|
||||||
bool hasMappedArgsObj() const {
|
bool hasMappedArgsObj() const {
|
||||||
return bitFields_.hasMappedArgsObj_;
|
return hasFlag(ImmutableFlags::HasMappedArgsObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool functionHasThisBinding() const {
|
bool functionHasThisBinding() const {
|
||||||
return bitFields_.functionHasThisBinding_;
|
return hasFlag(ImmutableFlags::FunctionHasThisBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2200,24 +2225,20 @@ class JSScript : public js::gc::TenuredCell
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t typesGeneration() const {
|
uint32_t typesGeneration() const {
|
||||||
return (uint32_t) bitFields_.typesGeneration_;
|
return uint32_t(hasFlag(MutableFlags::TypesGeneration));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTypesGeneration(uint32_t generation) {
|
void setTypesGeneration(uint32_t generation) {
|
||||||
MOZ_ASSERT(generation <= 1);
|
MOZ_ASSERT(generation <= 1);
|
||||||
bitFields_.typesGeneration_ = (bool) generation;
|
setFlag(MutableFlags::TypesGeneration, bool(generation));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDoNotRelazify(bool b) {
|
void setDoNotRelazify(bool b) {
|
||||||
bitFields_.doNotRelazify_ = b;
|
setFlag(MutableFlags::DoNotRelazify, b);
|
||||||
}
|
|
||||||
|
|
||||||
void setHasInnerFunctions(bool b) {
|
|
||||||
bitFields_.hasInnerFunctions_ = b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasInnerFunctions() const {
|
bool hasInnerFunctions() const {
|
||||||
return bitFields_.hasInnerFunctions_;
|
return hasFlag(ImmutableFlags::HasInnerFunctions);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasAnyIonScript() const {
|
bool hasAnyIonScript() const {
|
||||||
|
@ -2282,11 +2303,11 @@ class JSScript : public js::gc::TenuredCell
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isRelazifiable() const {
|
bool isRelazifiable() const {
|
||||||
return (selfHosted() || lazyScript) && !bitFields_.hasInnerFunctions_ && !types_ &&
|
return (selfHosted() || lazyScript) && !hasInnerFunctions() && !types_ &&
|
||||||
!isGenerator() && !isAsync() &&
|
!isGenerator() && !isAsync() &&
|
||||||
!isDefaultClassConstructor() &&
|
!isDefaultClassConstructor() &&
|
||||||
!hasBaselineScript() && !hasAnyIonScript() &&
|
!hasBaselineScript() && !hasAnyIonScript() &&
|
||||||
!bitFields_.doNotRelazify_;
|
!hasFlag(MutableFlags::DoNotRelazify);
|
||||||
}
|
}
|
||||||
void setLazyScript(js::LazyScript* lazy) {
|
void setLazyScript(js::LazyScript* lazy) {
|
||||||
lazyScript = lazy;
|
lazyScript = lazy;
|
||||||
|
@ -2416,8 +2437,9 @@ class JSScript : public js::gc::TenuredCell
|
||||||
}
|
}
|
||||||
|
|
||||||
bool functionHasExtraBodyVarScope() const {
|
bool functionHasExtraBodyVarScope() const {
|
||||||
MOZ_ASSERT_IF(bitFields_.functionHasExtraBodyVarScope_, functionHasParameterExprs());
|
bool res = hasFlag(ImmutableFlags::FunctionHasExtraBodyVarScope);
|
||||||
return bitFields_.functionHasExtraBodyVarScope_;
|
MOZ_ASSERT_IF(res, functionHasParameterExprs());
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
js::VarScope* functionExtraBodyVarScope() const {
|
js::VarScope* functionExtraBodyVarScope() const {
|
||||||
|
@ -2658,9 +2680,13 @@ class JSScript : public js::gc::TenuredCell
|
||||||
js::DebugScript* releaseDebugScript();
|
js::DebugScript* releaseDebugScript();
|
||||||
void destroyDebugScript(js::FreeOp* fop);
|
void destroyDebugScript(js::FreeOp* fop);
|
||||||
|
|
||||||
|
bool hasDebugScript() const {
|
||||||
|
return hasFlag(MutableFlags::HasDebugScript);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool hasBreakpointsAt(jsbytecode* pc);
|
bool hasBreakpointsAt(jsbytecode* pc);
|
||||||
bool hasAnyBreakpointsOrStepMode() { return bitFields_.hasDebugScript_; }
|
bool hasAnyBreakpointsOrStepMode() { return hasDebugScript(); }
|
||||||
|
|
||||||
// See comment above 'debugMode' in Realm.h for explanation of
|
// See comment above 'debugMode' in Realm.h for explanation of
|
||||||
// invariants of debuggee compartments, scripts, and frames.
|
// invariants of debuggee compartments, scripts, and frames.
|
||||||
|
@ -2668,7 +2694,7 @@ class JSScript : public js::gc::TenuredCell
|
||||||
|
|
||||||
js::BreakpointSite* getBreakpointSite(jsbytecode* pc)
|
js::BreakpointSite* getBreakpointSite(jsbytecode* pc)
|
||||||
{
|
{
|
||||||
return bitFields_.hasDebugScript_ ? debugScript()->breakpoints[pcToOffset(pc)] : nullptr;
|
return hasDebugScript() ? debugScript()->breakpoints[pcToOffset(pc)] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
js::BreakpointSite* getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc);
|
js::BreakpointSite* getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc);
|
||||||
|
@ -2686,10 +2712,10 @@ class JSScript : public js::gc::TenuredCell
|
||||||
bool incrementStepModeCount(JSContext* cx);
|
bool incrementStepModeCount(JSContext* cx);
|
||||||
void decrementStepModeCount(js::FreeOp* fop);
|
void decrementStepModeCount(js::FreeOp* fop);
|
||||||
|
|
||||||
bool stepModeEnabled() { return bitFields_.hasDebugScript_ && !!debugScript()->stepMode; }
|
bool stepModeEnabled() { return hasDebugScript() && !!debugScript()->stepMode; }
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
uint32_t stepModeCount() { return bitFields_.hasDebugScript_ ? debugScript()->stepMode : 0; }
|
uint32_t stepModeCount() { return hasDebugScript() ? debugScript()->stepMode : 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void finalize(js::FreeOp* fop);
|
void finalize(js::FreeOp* fop);
|
||||||
|
|
|
@ -4886,7 +4886,7 @@ JSScript::sweepTypes(const js::AutoSweepTypeScript& sweep)
|
||||||
|
|
||||||
// Freeze constraints on stack type sets need to be regenerated the
|
// Freeze constraints on stack type sets need to be regenerated the
|
||||||
// next time the script is analyzed.
|
// next time the script is analyzed.
|
||||||
bitFields_.hasFreezeConstraints_ = false;
|
clearFlag(MutableFlags::HasFreezeConstraints);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -4902,7 +4902,7 @@ JSScript::sweepTypes(const js::AutoSweepTypeScript& sweep)
|
||||||
if (zone()->types.hadOOMSweepingTypes()) {
|
if (zone()->types.hadOOMSweepingTypes()) {
|
||||||
// It's possible we OOM'd while copying freeze constraints, so they
|
// It's possible we OOM'd while copying freeze constraints, so they
|
||||||
// need to be regenerated.
|
// need to be regenerated.
|
||||||
bitFields_.hasFreezeConstraints_ = false;
|
clearFlag(MutableFlags::HasFreezeConstraints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -826,7 +826,8 @@ PresShell::PresShell()
|
||||||
, mHasHandledUserInput(false)
|
, mHasHandledUserInput(false)
|
||||||
#ifdef NIGHTLY_BUILD
|
#ifdef NIGHTLY_BUILD
|
||||||
, mForceDispatchKeyPressEventsForNonPrintableKeys(false)
|
, mForceDispatchKeyPressEventsForNonPrintableKeys(false)
|
||||||
, mInitializedForceDispatchKeyPressEventsForNonPrintableKeys(false)
|
, mForceUseLegacyKeyCodeAndCharCodeValues(false)
|
||||||
|
, mInitializedWithKeyPressEventDispatchingBlacklist(false)
|
||||||
#endif // #ifdef NIGHTLY_BUILD
|
#endif // #ifdef NIGHTLY_BUILD
|
||||||
{
|
{
|
||||||
MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
|
MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
|
||||||
|
@ -7919,7 +7920,8 @@ GetDocumentURIToCompareWithBlacklist(PresShell& aPresShell)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
DispatchKeyPressEventsEvenForNonPrintableKeys(nsIURI* aURI)
|
IsURIInBlacklistPref(nsIURI* aURI,
|
||||||
|
const char* aBlacklistPrefName)
|
||||||
{
|
{
|
||||||
if (!aURI) {
|
if (!aURI) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -7940,11 +7942,8 @@ DispatchKeyPressEventsEvenForNonPrintableKeys(nsIURI* aURI)
|
||||||
|
|
||||||
// The black list is comma separated domain list. Each item may start with
|
// The black list is comma separated domain list. Each item may start with
|
||||||
// "*.". If starts with "*.", it matches any sub-domains.
|
// "*.". If starts with "*.", it matches any sub-domains.
|
||||||
static const char* kPrefNameOfBlackList =
|
|
||||||
"dom.keyboardevent.keypress.hack.dispatch_non_printable_keys";
|
|
||||||
|
|
||||||
nsAutoCString blackList;
|
nsAutoCString blackList;
|
||||||
Preferences::GetCString(kPrefNameOfBlackList, blackList);
|
Preferences::GetCString(aBlacklistPrefName, blackList);
|
||||||
if (blackList.IsEmpty()) {
|
if (blackList.IsEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -8016,8 +8015,7 @@ PresShell::DispatchEventToDOM(WidgetEvent* aEvent,
|
||||||
if (aEvent->IsBlockedForFingerprintingResistance()) {
|
if (aEvent->IsBlockedForFingerprintingResistance()) {
|
||||||
aEvent->mFlags.mOnlySystemGroupDispatchInContent = true;
|
aEvent->mFlags.mOnlySystemGroupDispatchInContent = true;
|
||||||
#ifdef NIGHTLY_BUILD
|
#ifdef NIGHTLY_BUILD
|
||||||
} else if (aEvent->mMessage == eKeyPress &&
|
} else if (aEvent->mMessage == eKeyPress) {
|
||||||
aEvent->mFlags.mOnlySystemGroupDispatchInContent) {
|
|
||||||
// If eKeyPress event is marked as not dispatched in the default event
|
// If eKeyPress event is marked as not dispatched in the default event
|
||||||
// group in web content, it's caused by non-printable key or key
|
// group in web content, it's caused by non-printable key or key
|
||||||
// combination. In this case, UI Events declares that browsers
|
// combination. In this case, UI Events declares that browsers
|
||||||
|
@ -8025,15 +8023,26 @@ PresShell::DispatchEventToDOM(WidgetEvent* aEvent,
|
||||||
// broken with this strict behavior due to historical issue.
|
// broken with this strict behavior due to historical issue.
|
||||||
// Therefore, we need to keep dispatching keypress event for such keys
|
// Therefore, we need to keep dispatching keypress event for such keys
|
||||||
// even with breaking the standard.
|
// even with breaking the standard.
|
||||||
if (!mInitializedForceDispatchKeyPressEventsForNonPrintableKeys) {
|
// Similarly, the other browsers sets non-zero value of keyCode or
|
||||||
mInitializedForceDispatchKeyPressEventsForNonPrintableKeys = true;
|
// charCode of keypress event to the other. Therefore, we should
|
||||||
|
// behave so, however, some web apps may be broken. On such web apps,
|
||||||
|
// we should keep using legacy our behavior.
|
||||||
|
if (!mInitializedWithKeyPressEventDispatchingBlacklist) {
|
||||||
|
mInitializedWithKeyPressEventDispatchingBlacklist = true;
|
||||||
nsCOMPtr<nsIURI> uri = GetDocumentURIToCompareWithBlacklist(*this);
|
nsCOMPtr<nsIURI> uri = GetDocumentURIToCompareWithBlacklist(*this);
|
||||||
mForceDispatchKeyPressEventsForNonPrintableKeys =
|
mForceDispatchKeyPressEventsForNonPrintableKeys =
|
||||||
DispatchKeyPressEventsEvenForNonPrintableKeys(uri);
|
IsURIInBlacklistPref(uri,
|
||||||
|
"dom.keyboardevent.keypress.hack.dispatch_non_printable_keys");
|
||||||
|
mForceUseLegacyKeyCodeAndCharCodeValues =
|
||||||
|
IsURIInBlacklistPref(uri,
|
||||||
|
"dom.keyboardevent.keypress.hack.use_legacy_keycode_and_charcode");
|
||||||
}
|
}
|
||||||
if (mForceDispatchKeyPressEventsForNonPrintableKeys) {
|
if (mForceDispatchKeyPressEventsForNonPrintableKeys) {
|
||||||
aEvent->mFlags.mOnlySystemGroupDispatchInContent = false;
|
aEvent->mFlags.mOnlySystemGroupDispatchInContent = false;
|
||||||
}
|
}
|
||||||
|
if (mForceUseLegacyKeyCodeAndCharCodeValues) {
|
||||||
|
aEvent->AsKeyboardEvent()->mUseLegacyKeyCodeAndCharCodeValues = true;
|
||||||
|
}
|
||||||
#endif // #ifdef NIGHTLY_BUILD
|
#endif // #ifdef NIGHTLY_BUILD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -871,8 +871,14 @@ private:
|
||||||
// Whether we should dispatch keypress events even for non-printable keys
|
// Whether we should dispatch keypress events even for non-printable keys
|
||||||
// for keeping backward compatibility.
|
// for keeping backward compatibility.
|
||||||
bool mForceDispatchKeyPressEventsForNonPrintableKeys : 1;
|
bool mForceDispatchKeyPressEventsForNonPrintableKeys : 1;
|
||||||
// Whether mForceDispatchKeyPressEventsForNonPrintableKeys is initialized.
|
// Whether we should set keyCode or charCode value of keypress events whose
|
||||||
bool mInitializedForceDispatchKeyPressEventsForNonPrintableKeys : 1;
|
// value is zero to the other value or not. When this is set to true, we
|
||||||
|
// should keep using legacy keyCode and charCode values (i.e., one of them
|
||||||
|
// is always 0).
|
||||||
|
bool mForceUseLegacyKeyCodeAndCharCodeValues : 1;
|
||||||
|
// Whether mForceDispatchKeyPressEventsForNonPrintableKeys and
|
||||||
|
// mForceUseLegacyKeyCodeAndCharCodeValues are initialized.
|
||||||
|
bool mInitializedWithKeyPressEventDispatchingBlacklist : 1;
|
||||||
#endif // #ifdef NIGHTLY_BUILD
|
#endif // #ifdef NIGHTLY_BUILD
|
||||||
|
|
||||||
static bool sDisableNonTestMouseEvents;
|
static bool sDisableNonTestMouseEvents;
|
||||||
|
|
|
@ -2645,6 +2645,7 @@ CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
|
||||||
static bool
|
static bool
|
||||||
FrameParticipatesIn3DContext(nsIFrame* aAncestor, nsIFrame* aDescendant) {
|
FrameParticipatesIn3DContext(nsIFrame* aAncestor, nsIFrame* aDescendant) {
|
||||||
MOZ_ASSERT(aAncestor != aDescendant);
|
MOZ_ASSERT(aAncestor != aDescendant);
|
||||||
|
MOZ_ASSERT(aAncestor->GetContent() != aDescendant->GetContent());
|
||||||
MOZ_ASSERT(aAncestor->Extend3DContext());
|
MOZ_ASSERT(aAncestor->Extend3DContext());
|
||||||
|
|
||||||
nsIFrame* ancestor = aAncestor->FirstContinuation();
|
nsIFrame* ancestor = aAncestor->FirstContinuation();
|
||||||
|
@ -2666,14 +2667,13 @@ FrameParticipatesIn3DContext(nsIFrame* aAncestor, nsIFrame* aDescendant) {
|
||||||
static bool
|
static bool
|
||||||
ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem)
|
ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem)
|
||||||
{
|
{
|
||||||
nsIFrame* transformFrame;
|
auto type = aItem->GetType();
|
||||||
if (aItem->GetType() == DisplayItemType::TYPE_TRANSFORM ||
|
if (type != DisplayItemType::TYPE_TRANSFORM &&
|
||||||
aItem->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
|
type != DisplayItemType::TYPE_PERSPECTIVE) {
|
||||||
transformFrame = aItem->Frame();
|
|
||||||
} else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (aAncestor == transformFrame) {
|
nsIFrame* transformFrame = aItem->Frame();
|
||||||
|
if (aAncestor->GetContent() == transformFrame->GetContent()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return FrameParticipatesIn3DContext(aAncestor, transformFrame);
|
return FrameParticipatesIn3DContext(aAncestor, transformFrame);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<style>
|
||||||
|
* { -webkit-transform-style: preserve-3d }
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function go() {
|
||||||
|
a.append("x");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<body onload=go()>
|
||||||
|
<svg overflow="auto">
|
||||||
|
<use xlink:href="#b" style="-webkit-transform-style: flat"/>
|
||||||
|
<use id="b" xlink:href="#a">
|
||||||
|
<text id="a">
|
|
@ -15,3 +15,4 @@ load 1465305-1.html
|
||||||
load 1468124-1.html
|
load 1468124-1.html
|
||||||
load 1469472.html
|
load 1469472.html
|
||||||
load 1477831-1.html
|
load 1477831-1.html
|
||||||
|
load 1504033.html
|
||||||
|
|
|
@ -172,7 +172,7 @@ fuzzy(0-16,0-69) fuzzy-if(skiaContent,0-95,0-2206) == attachment-local-clipping-
|
||||||
fuzzy(0-80,0-500) fuzzy-if(skiaContent,0-109,0-908) == attachment-local-clipping-image-6.html attachment-local-clipping-image-6-ref.html
|
fuzzy(0-80,0-500) fuzzy-if(skiaContent,0-109,0-908) == attachment-local-clipping-image-6.html attachment-local-clipping-image-6-ref.html
|
||||||
|
|
||||||
fuzzy-if(skiaContent,0-1,0-8) fuzzy-if(webrender,0-1,0-84) == background-multiple-with-border-radius.html background-multiple-with-border-radius-ref.html
|
fuzzy-if(skiaContent,0-1,0-8) fuzzy-if(webrender,0-1,0-84) == background-multiple-with-border-radius.html background-multiple-with-border-radius-ref.html
|
||||||
fuzzy-if(webrender,73-93,49600-49600) == background-repeat-large-area.html background-repeat-large-area-ref.html
|
fuzzy-if(webrender,10-93,49600-49600) == background-repeat-large-area.html background-repeat-large-area-ref.html
|
||||||
|
|
||||||
fuzzy(0-30,0-474) fuzzy-if(skiaContent,0-31,0-474) == background-tiling-zoom-1.html background-tiling-zoom-1-ref.html
|
fuzzy(0-30,0-474) fuzzy-if(skiaContent,0-31,0-474) == background-tiling-zoom-1.html background-tiling-zoom-1-ref.html
|
||||||
|
|
||||||
|
|
|
@ -1391,7 +1391,7 @@ random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 495385-4.html 495385-4-r
|
||||||
== 501257-1a.html 501257-1-ref.html
|
== 501257-1a.html 501257-1-ref.html
|
||||||
== 501257-1b.html 501257-1-ref.html
|
== 501257-1b.html 501257-1-ref.html
|
||||||
== 501257-1.xhtml 501257-1-ref.xhtml
|
== 501257-1.xhtml 501257-1-ref.xhtml
|
||||||
fuzzy-if(webrender,2-6,39042-97456) == 501627-1.html 501627-1-ref.html # Bug 1481664
|
fuzzy-if(webrender,0-6,0-97456) == 501627-1.html 501627-1-ref.html # Bug 1481664
|
||||||
== 502288-1.html 502288-1-ref.html
|
== 502288-1.html 502288-1-ref.html
|
||||||
fuzzy-if(gtkWidget,0-1,0-2) == 502447-1.html 502447-1-ref.html #Bug 1315834
|
fuzzy-if(gtkWidget,0-1,0-2) == 502447-1.html 502447-1-ref.html #Bug 1315834
|
||||||
== 502795-1.html 502795-1-ref.html
|
== 502795-1.html 502795-1-ref.html
|
||||||
|
@ -1772,7 +1772,7 @@ fuzzy-if(asyncPan,0-190,0-510) fuzzy-if(asyncPan&&!layersGPUAccelerated,0-102,0-
|
||||||
== 827577-1b.html 827577-1-ref.html
|
== 827577-1b.html 827577-1-ref.html
|
||||||
== 827799-1.html about:blank
|
== 827799-1.html about:blank
|
||||||
== 829958.html 829958-ref.html
|
== 829958.html 829958-ref.html
|
||||||
fuzzy-if(webrender&>kWidget,1-2,44000-152400) == 836844-1.html 836844-1-ref.html
|
fuzzy-if(webrender&>kWidget,0-2,0-152400) == 836844-1.html 836844-1-ref.html
|
||||||
== 841192-1.html 841192-1-ref.html
|
== 841192-1.html 841192-1-ref.html
|
||||||
== 844178.html 844178-ref.html
|
== 844178.html 844178-ref.html
|
||||||
fuzzy-if(OSX,0-1,0-364) fuzzy-if(skiaContent,0-1,0-320) == 846144-1.html 846144-1-ref.html
|
fuzzy-if(OSX,0-1,0-364) fuzzy-if(skiaContent,0-1,0-320) == 846144-1.html 846144-1-ref.html
|
||||||
|
|
|
@ -5,7 +5,7 @@ fuzzy-if(webrender,9-9,4780-5120) == blur.html blur-ref.html
|
||||||
== blur.svg blur-ref.svg
|
== blur.svg blur-ref.svg
|
||||||
== blur-calc.html blur-calc-ref.html
|
== blur-calc.html blur-calc-ref.html
|
||||||
== blur-calc-negative.html blur-calc-negative-ref.html
|
== blur-calc-negative.html blur-calc-negative-ref.html
|
||||||
fuzzy-if(cocoaWidget&&webrender,1-1,1-1) skip-if(d2d) == blur-cap-large-radius-on-software.html blur-cap-large-radius-on-software-ref.html
|
fuzzy-if(cocoaWidget&&webrender,0-1,0-1) skip-if(d2d) == blur-cap-large-radius-on-software.html blur-cap-large-radius-on-software-ref.html
|
||||||
fuzzy-if(webrender,9-9,4780-5120) == blur-em-radius.html blur-em-radius-ref.html
|
fuzzy-if(webrender,9-9,4780-5120) == blur-em-radius.html blur-em-radius-ref.html
|
||||||
== blur-invalid-radius.html blur-invalid-radius-ref.html
|
== blur-invalid-radius.html blur-invalid-radius-ref.html
|
||||||
fuzzy-if(webrender,9-9,4780-5120) == blur-rem-radius.html blur-rem-radius-ref.html
|
fuzzy-if(webrender,9-9,4780-5120) == blur-rem-radius.html blur-rem-radius-ref.html
|
||||||
|
|
|
@ -122,7 +122,7 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
||||||
* @return This Builder instance.
|
* @return This Builder instance.
|
||||||
*/
|
*/
|
||||||
public @NonNull Builder webFontsEnabled(final boolean flag) {
|
public @NonNull Builder webFontsEnabled(final boolean flag) {
|
||||||
mSettings.mWebFonts.set(flag);
|
mSettings.mWebFonts.set(flag ? 1 : 0);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,8 +368,8 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
||||||
"javascript.enabled", true);
|
"javascript.enabled", true);
|
||||||
/* package */ Pref<Boolean> mRemoteDebugging = new Pref<Boolean>(
|
/* package */ Pref<Boolean> mRemoteDebugging = new Pref<Boolean>(
|
||||||
"devtools.debugger.remote-enabled", false);
|
"devtools.debugger.remote-enabled", false);
|
||||||
/* package */ Pref<Boolean> mWebFonts = new Pref<Boolean>(
|
/* package */ Pref<Integer> mWebFonts = new Pref<Integer>(
|
||||||
"browser.display.use_document_fonts", true);
|
"browser.display.use_document_fonts", 1);
|
||||||
/* package */ Pref<Integer> mCookieBehavior = new Pref<Integer>(
|
/* package */ Pref<Integer> mCookieBehavior = new Pref<Integer>(
|
||||||
"network.cookie.cookieBehavior", COOKIE_ACCEPT_ALL);
|
"network.cookie.cookieBehavior", COOKIE_ACCEPT_ALL);
|
||||||
/* package */ Pref<Integer> mCookieLifetime = new Pref<Integer>(
|
/* package */ Pref<Integer> mCookieLifetime = new Pref<Integer>(
|
||||||
|
@ -541,7 +541,7 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
||||||
* @return Whether web fonts support is enabled.
|
* @return Whether web fonts support is enabled.
|
||||||
*/
|
*/
|
||||||
public boolean getWebFontsEnabled() {
|
public boolean getWebFontsEnabled() {
|
||||||
return mWebFonts.get();
|
return mWebFonts.get() != 0 ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -551,7 +551,7 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
||||||
* @return This GeckoRuntimeSettings instance.
|
* @return This GeckoRuntimeSettings instance.
|
||||||
*/
|
*/
|
||||||
public @NonNull GeckoRuntimeSettings setWebFontsEnabled(final boolean flag) {
|
public @NonNull GeckoRuntimeSettings setWebFontsEnabled(final boolean flag) {
|
||||||
mWebFonts.set(flag);
|
mWebFonts.set(flag ? 1 : 0);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -197,11 +197,17 @@ VARCACHE_PREF(
|
||||||
|
|
||||||
// If this is true, "keypress" event's keyCode value and charCode value always
|
// If this is true, "keypress" event's keyCode value and charCode value always
|
||||||
// become same if the event is not created/initialized by JS.
|
// become same if the event is not created/initialized by JS.
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
|
# define PREF_VALUE true
|
||||||
|
#else
|
||||||
|
# define PREF_VALUE false
|
||||||
|
#endif
|
||||||
VARCACHE_PREF(
|
VARCACHE_PREF(
|
||||||
"dom.keyboardevent.keypress.set_keycode_and_charcode_to_same_value",
|
"dom.keyboardevent.keypress.set_keycode_and_charcode_to_same_value",
|
||||||
dom_keyboardevent_keypress_set_keycode_and_charcode_to_same_value,
|
dom_keyboardevent_keypress_set_keycode_and_charcode_to_same_value,
|
||||||
bool, false
|
bool, PREF_VALUE
|
||||||
)
|
)
|
||||||
|
#undef PREF_VALUE
|
||||||
|
|
||||||
// NOTE: This preference is used in unit tests. If it is removed or its default
|
// NOTE: This preference is used in unit tests. If it is removed or its default
|
||||||
// value changes, please update test_sharedMap_var_caches.js accordingly.
|
// value changes, please update test_sharedMap_var_caches.js accordingly.
|
||||||
|
|
|
@ -231,6 +231,16 @@ pref("dom.keyboardevent.keypress.hack.dispatch_non_printable_keys",
|
||||||
pref("dom.keyboardevent.keypress.dispatch_non_printable_keys_only_system_group_in_content", false);
|
pref("dom.keyboardevent.keypress.dispatch_non_printable_keys_only_system_group_in_content", false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
|
// Blacklist of domains of web apps which handle keyCode and charCode of
|
||||||
|
// keypress events with a path only for Firefox (i.e., broken if we set
|
||||||
|
// non-zero keyCode or charCode value to the other). The format is exactly
|
||||||
|
// same as "dom.keyboardevent.keypress.hack.dispatch_non_printable_keys". So,
|
||||||
|
// check its explanation for the detail.
|
||||||
|
pref("dom.keyboardevent.keypress.hack.use_legacy_keycode_and_charcode",
|
||||||
|
"docs.google.com,www.rememberthemilk.com");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Whether the WebMIDI API is enabled
|
// Whether the WebMIDI API is enabled
|
||||||
pref("dom.webmidi.enabled", false);
|
pref("dom.webmidi.enabled", false);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
# coding=utf8
|
||||||
|
|
||||||
|
# Any copyright is dedicated to the Public Domain.
|
||||||
|
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import fluent.syntax.ast as FTL
|
||||||
|
from fluent.migrate.helpers import transforms_from, MESSAGE_REFERENCE
|
||||||
|
from fluent.migrate import COPY
|
||||||
|
|
||||||
|
def migrate(ctx):
|
||||||
|
"""Bug 1491679 - Convert a subset of the strings in extensions.dtd to using Fluent for localization, part {index}."""
|
||||||
|
|
||||||
|
ctx.add_transforms(
|
||||||
|
"toolkit/toolkit/about/aboutAddons.ftl",
|
||||||
|
"toolkit/toolkit/about/aboutAddons.ftl",
|
||||||
|
transforms_from(
|
||||||
|
"""
|
||||||
|
extensions-warning-safe-mode-label =
|
||||||
|
.value = { COPY(from_path, "warning.safemode.label") }
|
||||||
|
extensions-warning-check-compatibility-label =
|
||||||
|
.value = { COPY(from_path, "warning.checkcompatibility.label") }
|
||||||
|
extensions-warning-check-compatibility-enable =
|
||||||
|
.label = { COPY(from_path, "warning.checkcompatibility.enable.label") }
|
||||||
|
.tooltiptext = { COPY(from_path, "warning.checkcompatibility.enable.tooltip") }
|
||||||
|
extensions-warning-update-security-label =
|
||||||
|
.value = { COPY(from_path, "warning.updatesecurity.label") }
|
||||||
|
extensions-warning-update-security-enable =
|
||||||
|
.label = { COPY(from_path, "warning.updatesecurity.enable.label") }
|
||||||
|
.tooltiptext = { COPY(from_path, "warning.updatesecurity.enable.tooltip") }
|
||||||
|
extensions-updates-check-for-updates =
|
||||||
|
.label = { COPY(from_path, "updates.checkForUpdates.label") }
|
||||||
|
.accesskey = { COPY(from_path, "updates.checkForUpdates.accesskey") }
|
||||||
|
extensions-updates-view-updates =
|
||||||
|
.label = { COPY(from_path, "updates.viewUpdates.label") }
|
||||||
|
.accesskey = { COPY(from_path, "updates.viewUpdates.accesskey") }
|
||||||
|
extensions-updates-update-addons-automatically =
|
||||||
|
.label = { COPY(from_path, "updates.updateAddonsAutomatically.label") }
|
||||||
|
.accesskey = { COPY(from_path, "updates.updateAddonsAutomatically.accesskey") }
|
||||||
|
extensions-updates-reset-updates-to-automatic =
|
||||||
|
.label = { COPY(from_path, "updates.resetUpdatesToAutomatic.label") }
|
||||||
|
.accesskey = { COPY(from_path, "updates.resetUpdatesToAutomatic.accesskey") }
|
||||||
|
extensions-updates-reset-updates-to-manual =
|
||||||
|
.label = { COPY(from_path, "updates.resetUpdatesToManual.label") }
|
||||||
|
.accesskey = { COPY(from_path, "updates.resetUpdatesToManual.accesskey") }
|
||||||
|
extensions-updates-updating =
|
||||||
|
.value = { COPY(from_path, "updates.updating.label") }
|
||||||
|
extensions-updates-installed =
|
||||||
|
.value = { COPY(from_path, "updates.installed.label") }
|
||||||
|
extensions-updates-downloaded =
|
||||||
|
.value = { COPY(from_path, "updates.downloaded.label") }
|
||||||
|
extensions-updates-restart =
|
||||||
|
.label = { COPY(from_path, "updates.restart.label") }
|
||||||
|
extensions-updates-none-found =
|
||||||
|
.value = { COPY(from_path, "updates.noneFound.label") }
|
||||||
|
extensions-updates-manual-updates-found =
|
||||||
|
.label = { COPY(from_path, "updates.manualUpdatesFound.label") }
|
||||||
|
extensions-updates-update-selected =
|
||||||
|
.label = { COPY(from_path, "updates.updateSelected.label") }
|
||||||
|
.tooltiptext = { COPY(from_path, "updates.updateSelected.tooltip") }
|
||||||
|
""", from_path="toolkit/chrome/mozapps/extensions/extensions.dtd"))
|
||||||
|
|
||||||
|
ctx.add_transforms(
|
||||||
|
"toolkit/toolkit/about/aboutAddons.ftl",
|
||||||
|
"toolkit/toolkit/about/aboutAddons.ftl",
|
||||||
|
[
|
||||||
|
FTL.Message(
|
||||||
|
id=FTL.Identifier("extensions-view-discover"),
|
||||||
|
attributes=[
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("name"),
|
||||||
|
value=COPY(
|
||||||
|
"toolkit/chrome/mozapps/extensions/extensions.dtd",
|
||||||
|
"view.discover.label"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("tooltiptext"),
|
||||||
|
value=FTL.Pattern(
|
||||||
|
elements=[
|
||||||
|
FTL.Placeable(
|
||||||
|
expression=MESSAGE_REFERENCE("extensions-view-discover.name")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
FTL.Message(
|
||||||
|
id=FTL.Identifier("extensions-view-recent-updates"),
|
||||||
|
attributes=[
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("name"),
|
||||||
|
value=COPY(
|
||||||
|
"toolkit/chrome/mozapps/extensions/extensions.dtd",
|
||||||
|
"view.recentUpdates.label"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("tooltiptext"),
|
||||||
|
value=FTL.Pattern(
|
||||||
|
elements=[
|
||||||
|
FTL.Placeable(
|
||||||
|
expression=MESSAGE_REFERENCE("extensions-view-recent-updates.name")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
FTL.Message(
|
||||||
|
id=FTL.Identifier("extensions-view-available-updates"),
|
||||||
|
attributes=[
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("name"),
|
||||||
|
value=COPY(
|
||||||
|
"toolkit/chrome/mozapps/extensions/extensions.dtd",
|
||||||
|
"view.availableUpdates.label"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("tooltiptext"),
|
||||||
|
value=FTL.Pattern(
|
||||||
|
elements=[
|
||||||
|
FTL.Placeable(
|
||||||
|
expression=MESSAGE_REFERENCE("extensions-view-available-updates.name")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
FTL.Message(
|
||||||
|
id=FTL.Identifier("extensions-warning-safe-mode-container"),
|
||||||
|
attributes=[
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("tooltiptext"),
|
||||||
|
value=FTL.Pattern(
|
||||||
|
elements=[
|
||||||
|
FTL.Placeable(
|
||||||
|
expression=MESSAGE_REFERENCE("extensions-warning-safe-mode-label.value")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
FTL.Message(
|
||||||
|
id=FTL.Identifier("extensions-warning-check-compatibility-container"),
|
||||||
|
attributes=[
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("tooltiptext"),
|
||||||
|
value=FTL.Pattern(
|
||||||
|
elements=[
|
||||||
|
FTL.Placeable(
|
||||||
|
expression=MESSAGE_REFERENCE("extensions-warning-check-compatibility-label.value")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
FTL.Message(
|
||||||
|
id=FTL.Identifier("extensions-warning-update-security-container"),
|
||||||
|
attributes=[
|
||||||
|
FTL.Attribute(
|
||||||
|
id=FTL.Identifier("tooltiptext"),
|
||||||
|
value=FTL.Pattern(
|
||||||
|
elements=[
|
||||||
|
FTL.Placeable(
|
||||||
|
expression=MESSAGE_REFERENCE("extensions-warning-update-security-label.value")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
|
@ -7733,6 +7733,42 @@
|
||||||
"description": "a testing histogram; not meant to be touched",
|
"description": "a testing histogram; not meant to be touched",
|
||||||
"operating_systems": ["mac"]
|
"operating_systems": ["mac"]
|
||||||
},
|
},
|
||||||
|
"TELEMETRY_TEST_MAIN_ONLY": {
|
||||||
|
"record_in_processes": ["main"],
|
||||||
|
"alert_emails": ["telemetry-client-dev@mozilla.com"],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "linear",
|
||||||
|
"low": 1,
|
||||||
|
"high": 2147483646,
|
||||||
|
"n_buckets": 10,
|
||||||
|
"bug_numbers": [1498164],
|
||||||
|
"description": "a testing histogram; not meant to be touched",
|
||||||
|
"record_into_store": ["main"]
|
||||||
|
},
|
||||||
|
"TELEMETRY_TEST_SYNC_ONLY": {
|
||||||
|
"record_in_processes": ["main"],
|
||||||
|
"alert_emails": ["telemetry-client-dev@mozilla.com"],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "linear",
|
||||||
|
"low": 1,
|
||||||
|
"high": 2147483646,
|
||||||
|
"n_buckets": 10,
|
||||||
|
"bug_numbers": [1498164],
|
||||||
|
"description": "a testing histogram; not meant to be touched",
|
||||||
|
"record_into_store": ["sync"]
|
||||||
|
},
|
||||||
|
"TELEMETRY_TEST_MULTIPLE_STORES": {
|
||||||
|
"record_in_processes": ["main"],
|
||||||
|
"alert_emails": ["telemetry-client-dev@mozilla.com"],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "linear",
|
||||||
|
"low": 1,
|
||||||
|
"high": 2147483646,
|
||||||
|
"n_buckets": 10,
|
||||||
|
"bug_numbers": [1498164],
|
||||||
|
"description": "a testing histogram; not meant to be touched",
|
||||||
|
"record_into_store": ["main", "sync"]
|
||||||
|
},
|
||||||
"STARTUP_CRASH_DETECTED": {
|
"STARTUP_CRASH_DETECTED": {
|
||||||
"record_in_processes": ["main", "content"],
|
"record_in_processes": ["main", "content"],
|
||||||
"expires_in_version": "never",
|
"expires_in_version": "never",
|
||||||
|
@ -10847,10 +10883,12 @@
|
||||||
},
|
},
|
||||||
"SSL_CERT_VERIFICATION_ERRORS": {
|
"SSL_CERT_VERIFICATION_ERRORS": {
|
||||||
"record_in_processes": ["main", "content"],
|
"record_in_processes": ["main", "content"],
|
||||||
"alert_emails": ["seceng@mozilla.org"],
|
"alert_emails": ["jhofmann@mozilla.com", "rtestard@mozilla.com", "seceng@mozilla.org"],
|
||||||
"expires_in_version": "default",
|
"expires_in_version": "never",
|
||||||
"kind": "enumerated",
|
"kind": "enumerated",
|
||||||
"n_values": 100,
|
"n_values": 100,
|
||||||
|
"bug_numbers": [1503572],
|
||||||
|
"releaseChannelCollection": "opt-out",
|
||||||
"description": "If certificate verification failed in a TLS handshake, what was the error? (see MapCertErrorToProbeValue in security/manager/ssl/SSLServerCertVerification.cpp and the values in security/pkix/include/pkix/Result.h)"
|
"description": "If certificate verification failed in a TLS handshake, what was the error? (see MapCertErrorToProbeValue in security/manager/ssl/SSLServerCertVerification.cpp and the values in security/pkix/include/pkix/Result.h)"
|
||||||
},
|
},
|
||||||
"SSL_CT_POLICY_COMPLIANCE_OF_EV_CERTS": {
|
"SSL_CT_POLICY_COMPLIANCE_OF_EV_CERTS": {
|
||||||
|
|
|
@ -2472,6 +2472,49 @@ telemetry.test:
|
||||||
- fennec
|
- fennec
|
||||||
- geckoview
|
- geckoview
|
||||||
|
|
||||||
|
main_only:
|
||||||
|
bug_numbers:
|
||||||
|
- 1498164
|
||||||
|
description: >
|
||||||
|
This is a test uint type with only the main store.
|
||||||
|
expires: never
|
||||||
|
kind: uint
|
||||||
|
notification_emails:
|
||||||
|
- telemetry-client-dev@mozilla.com
|
||||||
|
record_in_processes:
|
||||||
|
- 'main'
|
||||||
|
record_into_store:
|
||||||
|
- 'main'
|
||||||
|
|
||||||
|
sync_only:
|
||||||
|
bug_numbers:
|
||||||
|
- 1498164
|
||||||
|
description: >
|
||||||
|
This is a test uint type with only the sync store.
|
||||||
|
expires: never
|
||||||
|
kind: uint
|
||||||
|
notification_emails:
|
||||||
|
- telemetry-client-dev@mozilla.com
|
||||||
|
record_in_processes:
|
||||||
|
- 'main'
|
||||||
|
record_into_store:
|
||||||
|
- 'sync'
|
||||||
|
|
||||||
|
multiple_stores:
|
||||||
|
bug_numbers:
|
||||||
|
- 1498164
|
||||||
|
description: >
|
||||||
|
This is a test uint type with multiple stores.
|
||||||
|
expires: never
|
||||||
|
kind: uint
|
||||||
|
notification_emails:
|
||||||
|
- telemetry-client-dev@mozilla.com
|
||||||
|
record_in_processes:
|
||||||
|
- 'main'
|
||||||
|
record_into_store:
|
||||||
|
- 'main'
|
||||||
|
- 'sync'
|
||||||
|
|
||||||
# NOTE: Please don't add new definitions below this point. Consider adding
|
# NOTE: Please don't add new definitions below this point. Consider adding
|
||||||
# them earlier in the file and leave the telemetry.test category as the last
|
# them earlier in the file and leave the telemetry.test category as the last
|
||||||
# one for readability.
|
# one for readability.
|
||||||
|
|
|
@ -20,10 +20,12 @@ banner = """/* This file is auto-generated, see gen_histogram_data.py. */
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def print_array_entry(output, histogram, name_index, exp_index, label_index,
|
def print_array_entry(output, histogram, name_index, exp_index,
|
||||||
label_count, key_index, key_count):
|
label_index, label_count,
|
||||||
|
key_index, key_count,
|
||||||
|
store_index, store_count):
|
||||||
if histogram.record_on_os(buildconfig.substs["OS_TARGET"]):
|
if histogram.record_on_os(buildconfig.substs["OS_TARGET"]):
|
||||||
print(" { %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %s, %s },"
|
print(" { %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %s, %s, %s },"
|
||||||
% (histogram.low(),
|
% (histogram.low(),
|
||||||
histogram.high(),
|
histogram.high(),
|
||||||
histogram.n_buckets(),
|
histogram.n_buckets(),
|
||||||
|
@ -31,8 +33,10 @@ def print_array_entry(output, histogram, name_index, exp_index, label_index,
|
||||||
exp_index,
|
exp_index,
|
||||||
label_count,
|
label_count,
|
||||||
key_count,
|
key_count,
|
||||||
|
store_count,
|
||||||
label_index,
|
label_index,
|
||||||
key_index,
|
key_index,
|
||||||
|
store_index,
|
||||||
"true" if histogram.keyed() else "false",
|
"true" if histogram.keyed() else "false",
|
||||||
histogram.nsITelemetry_kind(),
|
histogram.nsITelemetry_kind(),
|
||||||
histogram.dataset(),
|
histogram.dataset(),
|
||||||
|
@ -42,10 +46,13 @@ def print_array_entry(output, histogram, name_index, exp_index, label_index,
|
||||||
|
|
||||||
def write_histogram_table(output, histograms):
|
def write_histogram_table(output, histograms):
|
||||||
string_table = StringTable()
|
string_table = StringTable()
|
||||||
|
|
||||||
label_table = []
|
label_table = []
|
||||||
label_count = 0
|
label_count = 0
|
||||||
keys_table = []
|
keys_table = []
|
||||||
keys_count = 0
|
keys_count = 0
|
||||||
|
store_table = []
|
||||||
|
total_store_count = 0
|
||||||
|
|
||||||
print("constexpr HistogramInfo gHistogramInfos[] = {", file=output)
|
print("constexpr HistogramInfo gHistogramInfos[] = {", file=output)
|
||||||
for histogram in histograms:
|
for histogram in histograms:
|
||||||
|
@ -66,8 +73,19 @@ def write_histogram_table(output, histograms):
|
||||||
keys_table.append((histogram.name(), string_table.stringIndexes(keys)))
|
keys_table.append((histogram.name(), string_table.stringIndexes(keys)))
|
||||||
keys_count += len(keys)
|
keys_count += len(keys)
|
||||||
|
|
||||||
|
stores = histogram.record_into_store()
|
||||||
|
store_index = 0
|
||||||
|
if stores == ["main"]:
|
||||||
|
# if count == 1 && offset == UINT16_MAX -> only main store
|
||||||
|
store_index = 'UINT16_MAX'
|
||||||
|
else:
|
||||||
|
store_index = total_store_count
|
||||||
|
store_table.append((histogram.name(), string_table.stringIndexes(stores)))
|
||||||
|
total_store_count += len(stores)
|
||||||
|
|
||||||
print_array_entry(output, histogram, name_index, exp_index,
|
print_array_entry(output, histogram, name_index, exp_index,
|
||||||
label_index, len(labels), key_index, len(keys))
|
label_index, len(labels), key_index, len(keys),
|
||||||
|
store_index, len(stores))
|
||||||
print("};\n", file=output)
|
print("};\n", file=output)
|
||||||
|
|
||||||
strtab_name = "gHistogramStringTable"
|
strtab_name = "gHistogramStringTable"
|
||||||
|
@ -95,6 +113,18 @@ def write_histogram_table(output, histograms):
|
||||||
print("};", file=output)
|
print("};", file=output)
|
||||||
static_assert(output, "sizeof(gHistogramKeyTable) <= UINT16_MAX", "index overflow")
|
static_assert(output, "sizeof(gHistogramKeyTable) <= UINT16_MAX", "index overflow")
|
||||||
|
|
||||||
|
store_table_name = "gHistogramStoresTable"
|
||||||
|
print("\n#if defined(_MSC_VER) && !defined(__clang__)", file=output)
|
||||||
|
print("const uint32_t {}[] = {{".format(store_table_name), file=output)
|
||||||
|
print("#else", file=output)
|
||||||
|
print("constexpr uint32_t {}[] = {{".format(store_table_name), file=output)
|
||||||
|
print("#endif", file=output)
|
||||||
|
for name, indexes in store_table:
|
||||||
|
print("/* %s */ %s," % (name, ", ".join(map(str, indexes))), file=output)
|
||||||
|
print("};", file=output)
|
||||||
|
static_assert(output, "sizeof(%s) <= UINT16_MAX" % store_table_name,
|
||||||
|
"index overflow")
|
||||||
|
|
||||||
|
|
||||||
# Write out static asserts for histogram data. We'd prefer to perform
|
# Write out static asserts for histogram data. We'd prefer to perform
|
||||||
# these checks in this script itself, but since several histograms
|
# these checks in this script itself, but since several histograms
|
||||||
|
|
|
@ -35,7 +35,7 @@ file_footer = """\
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def write_scalar_info(scalar, output, name_index, expiration_index):
|
def write_scalar_info(scalar, output, name_index, expiration_index, store_index, store_count):
|
||||||
"""Writes a scalar entry to the output file.
|
"""Writes a scalar entry to the output file.
|
||||||
|
|
||||||
:param scalar: a ScalarType instance describing the scalar.
|
:param scalar: a ScalarType instance describing the scalar.
|
||||||
|
@ -47,14 +47,16 @@ def write_scalar_info(scalar, output, name_index, expiration_index):
|
||||||
if cpp_guard:
|
if cpp_guard:
|
||||||
print("#if defined(%s)" % cpp_guard, file=output)
|
print("#if defined(%s)" % cpp_guard, file=output)
|
||||||
|
|
||||||
print(" {{ {}, {}, {}, {}, {}, {}, {} }},"
|
print(" {{ {}, {}, {}, {}, {}, {}, {}, {}, {} }},"
|
||||||
.format(scalar.nsITelemetry_kind,
|
.format(scalar.nsITelemetry_kind,
|
||||||
name_index,
|
name_index,
|
||||||
expiration_index,
|
expiration_index,
|
||||||
scalar.dataset,
|
scalar.dataset,
|
||||||
" | ".join(scalar.record_in_processes_enum),
|
" | ".join(scalar.record_in_processes_enum),
|
||||||
"true" if scalar.keyed else "false",
|
"true" if scalar.keyed else "false",
|
||||||
" | ".join(scalar.products_enum)),
|
" | ".join(scalar.products_enum),
|
||||||
|
store_count,
|
||||||
|
store_index),
|
||||||
file=output)
|
file=output)
|
||||||
|
|
||||||
if cpp_guard:
|
if cpp_guard:
|
||||||
|
@ -69,14 +71,28 @@ def write_scalar_tables(scalars, output):
|
||||||
"""
|
"""
|
||||||
string_table = StringTable()
|
string_table = StringTable()
|
||||||
|
|
||||||
|
store_table = []
|
||||||
|
total_store_count = 0
|
||||||
|
|
||||||
print("const ScalarInfo gScalars[] = {", file=output)
|
print("const ScalarInfo gScalars[] = {", file=output)
|
||||||
for s in scalars:
|
for s in scalars:
|
||||||
# We add both the scalar label and the expiration string to the strings
|
# We add both the scalar label and the expiration string to the strings
|
||||||
# table.
|
# table.
|
||||||
name_index = string_table.stringIndex(s.label)
|
name_index = string_table.stringIndex(s.label)
|
||||||
exp_index = string_table.stringIndex(s.expires)
|
exp_index = string_table.stringIndex(s.expires)
|
||||||
|
|
||||||
|
stores = s.record_into_store
|
||||||
|
store_index = 0
|
||||||
|
if stores == ["main"]:
|
||||||
|
# if count == 1 && offset == UINT16_MAX -> only main store
|
||||||
|
store_index = 'UINT16_MAX'
|
||||||
|
else:
|
||||||
|
store_index = total_store_count
|
||||||
|
store_table.append((s.label, string_table.stringIndexes(stores)))
|
||||||
|
total_store_count += len(stores)
|
||||||
|
|
||||||
# Write the scalar info entry.
|
# Write the scalar info entry.
|
||||||
write_scalar_info(s, output, name_index, exp_index)
|
write_scalar_info(s, output, name_index, exp_index, store_index, len(stores))
|
||||||
print("};", file=output)
|
print("};", file=output)
|
||||||
|
|
||||||
string_table_name = "gScalarsStringTable"
|
string_table_name = "gScalarsStringTable"
|
||||||
|
@ -84,6 +100,18 @@ def write_scalar_tables(scalars, output):
|
||||||
static_assert(output, "sizeof(%s) <= UINT32_MAX" % string_table_name,
|
static_assert(output, "sizeof(%s) <= UINT32_MAX" % string_table_name,
|
||||||
"index overflow")
|
"index overflow")
|
||||||
|
|
||||||
|
store_table_name = "gScalarStoresTable"
|
||||||
|
print("\n#if defined(_MSC_VER) && !defined(__clang__)", file=output)
|
||||||
|
print("const uint32_t {}[] = {{".format(store_table_name), file=output)
|
||||||
|
print("#else", file=output)
|
||||||
|
print("constexpr uint32_t {}[] = {{".format(store_table_name), file=output)
|
||||||
|
print("#endif", file=output)
|
||||||
|
for name, indexes in store_table:
|
||||||
|
print("/* %s */ %s," % (name, ", ".join(map(str, indexes))), file=output)
|
||||||
|
print("};", file=output)
|
||||||
|
static_assert(output, "sizeof(%s) <= UINT16_MAX" % store_table_name,
|
||||||
|
"index overflow")
|
||||||
|
|
||||||
|
|
||||||
def parse_scalar_definitions(filenames):
|
def parse_scalar_definitions(filenames):
|
||||||
if len(filenames) > 1:
|
if len(filenames) > 1:
|
||||||
|
|
|
@ -36,6 +36,7 @@ ALWAYS_ALLOWED_KEYS = [
|
||||||
'bug_numbers',
|
'bug_numbers',
|
||||||
'keys',
|
'keys',
|
||||||
'record_in_processes',
|
'record_in_processes',
|
||||||
|
'record_into_store',
|
||||||
'products',
|
'products',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -141,6 +142,7 @@ definition is a dict-like object that must contain at least the keys:
|
||||||
self._expiration = definition.get('expires_in_version')
|
self._expiration = definition.get('expires_in_version')
|
||||||
self._labels = definition.get('labels', [])
|
self._labels = definition.get('labels', [])
|
||||||
self._record_in_processes = definition.get('record_in_processes')
|
self._record_in_processes = definition.get('record_in_processes')
|
||||||
|
self._record_into_store = definition.get('record_into_store', ['main'])
|
||||||
self._products = definition.get('products', ["all"])
|
self._products = definition.get('products', ["all"])
|
||||||
self._operating_systems = definition.get('operating_systems', ["all"])
|
self._operating_systems = definition.get('operating_systems', ["all"])
|
||||||
|
|
||||||
|
@ -232,6 +234,10 @@ the histogram."""
|
||||||
|
|
||||||
return canonical_os in os
|
return canonical_os in os
|
||||||
|
|
||||||
|
def record_into_store(self):
|
||||||
|
"""Get the non-empty list of stores to record into"""
|
||||||
|
return self._record_into_store
|
||||||
|
|
||||||
def ranges(self):
|
def ranges(self):
|
||||||
"""Return an array of lower bounds for each bucket in the histogram."""
|
"""Return an array of lower bounds for each bucket in the histogram."""
|
||||||
bucket_fns = {
|
bucket_fns = {
|
||||||
|
@ -303,6 +309,7 @@ the histogram."""
|
||||||
self.check_record_in_processes(name, definition)
|
self.check_record_in_processes(name, definition)
|
||||||
self.check_products(name, definition)
|
self.check_products(name, definition)
|
||||||
self.check_operating_systems(name, definition)
|
self.check_operating_systems(name, definition)
|
||||||
|
self.check_record_into_store(name, definition)
|
||||||
|
|
||||||
def check_name(self, name):
|
def check_name(self, name):
|
||||||
if '#' in name:
|
if '#' in name:
|
||||||
|
@ -415,7 +422,7 @@ the histogram."""
|
||||||
field = 'operating_systems'
|
field = 'operating_systems'
|
||||||
operating_systems = definition.get(field)
|
operating_systems = definition.get(field)
|
||||||
|
|
||||||
DOC_URL = HISTOGRAMS_DOC_URL + "#operating_systems"
|
DOC_URL = HISTOGRAMS_DOC_URL + "#operating-systems"
|
||||||
|
|
||||||
if not operating_systems:
|
if not operating_systems:
|
||||||
# operating_systems is optional
|
# operating_systems is optional
|
||||||
|
@ -426,6 +433,23 @@ the histogram."""
|
||||||
ParserError('Histogram "%s" has unknown operating system "%s" in %s.\n%s' %
|
ParserError('Histogram "%s" has unknown operating system "%s" in %s.\n%s' %
|
||||||
(name, operating_system, field, DOC_URL)).handle_later()
|
(name, operating_system, field, DOC_URL)).handle_later()
|
||||||
|
|
||||||
|
def check_record_into_store(self, name, definition):
|
||||||
|
if not self._strict_type_checks:
|
||||||
|
return
|
||||||
|
|
||||||
|
field = 'record_into_store'
|
||||||
|
DOC_URL = HISTOGRAMS_DOC_URL + "#record-into-store"
|
||||||
|
|
||||||
|
if field not in definition:
|
||||||
|
# record_into_store is optional
|
||||||
|
return
|
||||||
|
|
||||||
|
record_into_store = definition.get(field)
|
||||||
|
# record_into_store should not be empty
|
||||||
|
if not record_into_store:
|
||||||
|
ParserError('Histogram "%s" has empty list of stores, which is not allowed.\n%s' %
|
||||||
|
(name, DOC_URL)).handle_later()
|
||||||
|
|
||||||
def check_keys_field(self, name, definition):
|
def check_keys_field(self, name, definition):
|
||||||
keys = definition.get('keys')
|
keys = definition.get('keys')
|
||||||
if not self._strict_type_checks or keys is None:
|
if not self._strict_type_checks or keys is None:
|
||||||
|
@ -512,6 +536,7 @@ the histogram."""
|
||||||
"keys": basestring,
|
"keys": basestring,
|
||||||
"products": basestring,
|
"products": basestring,
|
||||||
"operating_systems": basestring,
|
"operating_systems": basestring,
|
||||||
|
"record_into_store": basestring,
|
||||||
}
|
}
|
||||||
|
|
||||||
# For the server-side, where _strict_type_checks==False, we want to
|
# For the server-side, where _strict_type_checks==False, we want to
|
||||||
|
|
|
@ -111,6 +111,7 @@ class ScalarType:
|
||||||
'release_channel_collection': basestring,
|
'release_channel_collection': basestring,
|
||||||
'keyed': bool,
|
'keyed': bool,
|
||||||
'products': list,
|
'products': list,
|
||||||
|
'record_into_store': list,
|
||||||
}
|
}
|
||||||
|
|
||||||
# The types for the data within the fields that hold lists.
|
# The types for the data within the fields that hold lists.
|
||||||
|
@ -119,6 +120,7 @@ class ScalarType:
|
||||||
'notification_emails': basestring,
|
'notification_emails': basestring,
|
||||||
'record_in_processes': basestring,
|
'record_in_processes': basestring,
|
||||||
'products': basestring,
|
'products': basestring,
|
||||||
|
'record_into_store': basestring,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Concatenate the required and optional field definitions.
|
# Concatenate the required and optional field definitions.
|
||||||
|
@ -329,6 +331,11 @@ class ScalarType:
|
||||||
"""Get the cpp guard for this scalar"""
|
"""Get the cpp guard for this scalar"""
|
||||||
return self._definition.get('cpp_guard')
|
return self._definition.get('cpp_guard')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def record_into_store(self):
|
||||||
|
"""Get the list of stores this probe should be recorded into"""
|
||||||
|
return self._definition.get('record_into_store', ['main'])
|
||||||
|
|
||||||
|
|
||||||
def load_scalars(filename, strict_type_checks=True):
|
def load_scalars(filename, strict_type_checks=True):
|
||||||
"""Parses a YAML file containing the scalar definition.
|
"""Parses a YAML file containing the scalar definition.
|
||||||
|
|
|
@ -49,6 +49,8 @@ struct BaseScalarInfo {
|
||||||
struct ScalarInfo : BaseScalarInfo {
|
struct ScalarInfo : BaseScalarInfo {
|
||||||
uint32_t name_offset;
|
uint32_t name_offset;
|
||||||
uint32_t expiration_offset;
|
uint32_t expiration_offset;
|
||||||
|
uint32_t store_count;
|
||||||
|
uint16_t store_offset;
|
||||||
|
|
||||||
// In order to cleanly support dynamic scalars in TelemetryScalar.cpp, we need
|
// In order to cleanly support dynamic scalars in TelemetryScalar.cpp, we need
|
||||||
// to use virtual functions for |name| and |expiration|, as they won't be looked
|
// to use virtual functions for |name| and |expiration|, as they won't be looked
|
||||||
|
@ -59,7 +61,8 @@ struct ScalarInfo : BaseScalarInfo {
|
||||||
ScalarInfo(uint32_t aKind, uint32_t aNameOffset, uint32_t aExpirationOffset,
|
ScalarInfo(uint32_t aKind, uint32_t aNameOffset, uint32_t aExpirationOffset,
|
||||||
uint32_t aDataset,
|
uint32_t aDataset,
|
||||||
mozilla::Telemetry::Common::RecordedProcessType aRecordInProcess,
|
mozilla::Telemetry::Common::RecordedProcessType aRecordInProcess,
|
||||||
bool aKeyed, mozilla::Telemetry::Common::SupportedProduct aProducts)
|
bool aKeyed, mozilla::Telemetry::Common::SupportedProduct aProducts,
|
||||||
|
uint32_t aStoreCount, uint16_t aStoreOffset)
|
||||||
: BaseScalarInfo(aKind, aDataset, aRecordInProcess, aKeyed, aProducts)
|
: BaseScalarInfo(aKind, aDataset, aRecordInProcess, aKeyed, aProducts)
|
||||||
, name_offset(aNameOffset)
|
, name_offset(aNameOffset)
|
||||||
, expiration_offset(aExpirationOffset)
|
, expiration_offset(aExpirationOffset)
|
||||||
|
|
|
@ -140,8 +140,10 @@ struct HistogramInfo {
|
||||||
uint32_t expiration_offset;
|
uint32_t expiration_offset;
|
||||||
uint32_t label_count;
|
uint32_t label_count;
|
||||||
uint32_t key_count;
|
uint32_t key_count;
|
||||||
|
uint32_t store_count;
|
||||||
uint16_t label_index;
|
uint16_t label_index;
|
||||||
uint16_t key_index;
|
uint16_t key_index;
|
||||||
|
uint16_t store_index;
|
||||||
bool keyed;
|
bool keyed;
|
||||||
uint8_t histogramType;
|
uint8_t histogramType;
|
||||||
uint8_t dataset;
|
uint8_t dataset;
|
||||||
|
|
|
@ -235,6 +235,16 @@ Optional. This field is a list of products this histogram can be recorded on. Cu
|
||||||
|
|
||||||
If this field is left out it defaults to ``all``.
|
If this field is left out it defaults to ``all``.
|
||||||
|
|
||||||
|
``record_into_store``
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This field is not yet ready for use.
|
||||||
|
|
||||||
|
Optional. This field is a list of stores this histogram should be recorded into.
|
||||||
|
If this field is left out it defaults to ``[main]``.
|
||||||
|
|
||||||
Changing a histogram
|
Changing a histogram
|
||||||
====================
|
====================
|
||||||
Changing histogram declarations after the histogram has been released is tricky. Many tools (like `the aggregator <https://github.com/mozilla/python_mozaggregator>`_) assume histograms don't change. The current recommended procedure is to change the name of the histogram.
|
Changing histogram declarations after the histogram has been released is tricky. Many tools (like `the aggregator <https://github.com/mozilla/python_mozaggregator>`_) assume histograms don't change. The current recommended procedure is to change the name of the histogram.
|
||||||
|
|
|
@ -173,6 +173,8 @@ Optional Fields
|
||||||
- ``geckoview``
|
- ``geckoview``
|
||||||
- ``all`` (record on all products)
|
- ``all`` (record on all products)
|
||||||
|
|
||||||
|
- ``record_into_store``: A list of stores this scalar should be recorded into. It defaults to ``[main]`` (*Note: This field is not yet ready to use*).
|
||||||
|
|
||||||
String type restrictions
|
String type restrictions
|
||||||
------------------------
|
------------------------
|
||||||
To prevent abuses, the content of a string scalar is limited to 50 characters in length. Trying
|
To prevent abuses, the content of a string scalar is limited to 50 characters in length. Trying
|
||||||
|
|
|
@ -1033,7 +1033,6 @@
|
||||||
"SSL_AUTH_RSA_KEY_SIZE_FULL",
|
"SSL_AUTH_RSA_KEY_SIZE_FULL",
|
||||||
"SSL_BYTES_BEFORE_CERT_CALLBACK",
|
"SSL_BYTES_BEFORE_CERT_CALLBACK",
|
||||||
"SSL_CERT_ERROR_OVERRIDES",
|
"SSL_CERT_ERROR_OVERRIDES",
|
||||||
"SSL_CERT_VERIFICATION_ERRORS",
|
|
||||||
"SSL_CIPHER_SUITE_FULL",
|
"SSL_CIPHER_SUITE_FULL",
|
||||||
"SSL_CIPHER_SUITE_RESUMED",
|
"SSL_CIPHER_SUITE_RESUMED",
|
||||||
"SSL_HANDSHAKE_TYPE",
|
"SSL_HANDSHAKE_TYPE",
|
||||||
|
@ -1363,7 +1362,6 @@
|
||||||
"TRANSLATED_PAGES_BY_LANGUAGE",
|
"TRANSLATED_PAGES_BY_LANGUAGE",
|
||||||
"MOZ_SQLITE_OTHER_WRITE_B",
|
"MOZ_SQLITE_OTHER_WRITE_B",
|
||||||
"LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS",
|
"LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS",
|
||||||
"SSL_CERT_VERIFICATION_ERRORS",
|
|
||||||
"FX_SESSION_RESTORE_NUMBER_OF_WINDOWS_RESTORED",
|
"FX_SESSION_RESTORE_NUMBER_OF_WINDOWS_RESTORED",
|
||||||
"MOZ_SQLITE_PLACES_WRITE_MS",
|
"MOZ_SQLITE_PLACES_WRITE_MS",
|
||||||
"FX_THUMBNAILS_BG_CAPTURE_CANVAS_DRAW_TIME_MS",
|
"FX_THUMBNAILS_BG_CAPTURE_CANVAS_DRAW_TIME_MS",
|
||||||
|
|
|
@ -42,7 +42,7 @@ add_task(async function test_recording() {
|
||||||
"pre_content_spawn_expiration": {
|
"pre_content_spawn_expiration": {
|
||||||
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
||||||
keyed: false,
|
keyed: false,
|
||||||
release_channel_collection: true,
|
record_on_release: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,17 +65,17 @@ add_task(async function test_recording() {
|
||||||
"post_content_spawn": {
|
"post_content_spawn": {
|
||||||
kind: Ci.nsITelemetry.SCALAR_TYPE_BOOLEAN,
|
kind: Ci.nsITelemetry.SCALAR_TYPE_BOOLEAN,
|
||||||
keyed: false,
|
keyed: false,
|
||||||
release_channel_collection: false,
|
record_on_release: false,
|
||||||
},
|
},
|
||||||
"post_content_spawn_keyed": {
|
"post_content_spawn_keyed": {
|
||||||
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
||||||
keyed: true,
|
keyed: true,
|
||||||
release_channel_collection: true,
|
record_on_release: true,
|
||||||
},
|
},
|
||||||
"pre_content_spawn_expiration": {
|
"pre_content_spawn_expiration": {
|
||||||
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
||||||
keyed: false,
|
keyed: false,
|
||||||
release_channel_collection: true,
|
record_on_release: true,
|
||||||
expired: true,
|
expired: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -40,6 +40,7 @@ class TestParser(unittest.TestCase):
|
||||||
self.assertTrue(hist.expiration(), "never")
|
self.assertTrue(hist.expiration(), "never")
|
||||||
self.assertTrue(hist.kind(), "boolean")
|
self.assertTrue(hist.kind(), "boolean")
|
||||||
self.assertTrue(hist.record_in_processes, ["main", "content"])
|
self.assertTrue(hist.record_in_processes, ["main", "content"])
|
||||||
|
self.assertTrue(hist.record_into_store, ["main"])
|
||||||
|
|
||||||
def test_missing_bug_numbers(self):
|
def test_missing_bug_numbers(self):
|
||||||
SAMPLE_HISTOGRAM = {
|
SAMPLE_HISTOGRAM = {
|
||||||
|
@ -325,6 +326,50 @@ class TestParser(unittest.TestCase):
|
||||||
|
|
||||||
parse_histograms.whitelists = None
|
parse_histograms.whitelists = None
|
||||||
|
|
||||||
|
def test_multistore(self):
|
||||||
|
SAMPLE_HISTOGRAM = {
|
||||||
|
"TEST_VALID_HISTOGRAM": {
|
||||||
|
"record_in_processes": ["main", "content"],
|
||||||
|
"alert_emails": ["team@mozilla.xyz"],
|
||||||
|
"bug_numbers": [1383793],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "boolean",
|
||||||
|
"description": "Test histogram",
|
||||||
|
"record_into_store": ["main", "sync"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
histograms = load_histogram(SAMPLE_HISTOGRAM)
|
||||||
|
parse_histograms.load_whitelist()
|
||||||
|
|
||||||
|
hist = parse_histograms.Histogram('TEST_VALID_HISTOGRAM',
|
||||||
|
histograms['TEST_VALID_HISTOGRAM'],
|
||||||
|
strict_type_checks=True)
|
||||||
|
|
||||||
|
ParserError.exit_func()
|
||||||
|
self.assertTrue(hist.expiration(), "never")
|
||||||
|
self.assertTrue(hist.kind(), "boolean")
|
||||||
|
self.assertTrue(hist.record_into_store, ["main", "sync"])
|
||||||
|
|
||||||
|
def test_multistore_empty(self):
|
||||||
|
SAMPLE_HISTOGRAM = {
|
||||||
|
"TEST_HISTOGRAM_EMPTY_MULTISTORE": {
|
||||||
|
"record_in_processes": ["main", "content"],
|
||||||
|
"alert_emails": ["team@mozilla.xyz"],
|
||||||
|
"bug_numbers": [1383793],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "boolean",
|
||||||
|
"description": "Test histogram",
|
||||||
|
"record_into_store": [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
histograms = load_histogram(SAMPLE_HISTOGRAM)
|
||||||
|
parse_histograms.load_whitelist()
|
||||||
|
|
||||||
|
parse_histograms.Histogram('TEST_HISTOGRAM_EMPTY_MULTISTORE',
|
||||||
|
histograms['TEST_HISTOGRAM_EMPTY_MULTISTORE'],
|
||||||
|
strict_type_checks=True)
|
||||||
|
self.assertRaises(SystemExit, ParserError.exit_func)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
mozunit.main()
|
mozunit.main()
|
||||||
|
|
|
@ -53,6 +53,7 @@ bug_numbers:
|
||||||
SAMPLE_SCALAR_INVALID_ADDRESSES = """
|
SAMPLE_SCALAR_INVALID_ADDRESSES = """
|
||||||
description: A nice one-line description.
|
description: A nice one-line description.
|
||||||
expires: never
|
expires: never
|
||||||
|
record_in_processes:
|
||||||
- 'main'
|
- 'main'
|
||||||
kind: uint
|
kind: uint
|
||||||
notification_emails:
|
notification_emails:
|
||||||
|
@ -68,6 +69,71 @@ bug_numbers:
|
||||||
|
|
||||||
self.assertRaises(SystemExit, ParserError.exit_func)
|
self.assertRaises(SystemExit, ParserError.exit_func)
|
||||||
|
|
||||||
|
def test_multistore_default(self):
|
||||||
|
SAMPLE_SCALAR = """
|
||||||
|
description: A nice one-line description.
|
||||||
|
expires: never
|
||||||
|
record_in_processes:
|
||||||
|
- 'main'
|
||||||
|
kind: uint
|
||||||
|
notification_emails:
|
||||||
|
- test01@mozilla.com
|
||||||
|
bug_numbers:
|
||||||
|
- 12345
|
||||||
|
"""
|
||||||
|
scalar = load_scalar(SAMPLE_SCALAR)
|
||||||
|
sclr = parse_scalars.ScalarType("CATEGORY",
|
||||||
|
"PROVE",
|
||||||
|
scalar,
|
||||||
|
strict_type_checks=True)
|
||||||
|
ParserError.exit_func()
|
||||||
|
|
||||||
|
self.assertEqual(sclr.record_into_store, ["main"])
|
||||||
|
|
||||||
|
def test_multistore_extended(self):
|
||||||
|
SAMPLE_SCALAR = """
|
||||||
|
description: A nice one-line description.
|
||||||
|
expires: never
|
||||||
|
record_in_processes:
|
||||||
|
- 'main'
|
||||||
|
kind: uint
|
||||||
|
notification_emails:
|
||||||
|
- test01@mozilla.com
|
||||||
|
bug_numbers:
|
||||||
|
- 12345
|
||||||
|
record_into_store:
|
||||||
|
- main
|
||||||
|
- sync
|
||||||
|
"""
|
||||||
|
scalar = load_scalar(SAMPLE_SCALAR)
|
||||||
|
sclr = parse_scalars.ScalarType("CATEGORY",
|
||||||
|
"PROVE",
|
||||||
|
scalar,
|
||||||
|
strict_type_checks=True)
|
||||||
|
ParserError.exit_func()
|
||||||
|
|
||||||
|
self.assertEqual(sclr.record_into_store, ["main", "sync"])
|
||||||
|
|
||||||
|
def test_multistore_empty(self):
|
||||||
|
SAMPLE_SCALAR = """
|
||||||
|
description: A nice one-line description.
|
||||||
|
expires: never
|
||||||
|
record_in_processes:
|
||||||
|
- 'main'
|
||||||
|
kind: uint
|
||||||
|
notification_emails:
|
||||||
|
- test01@mozilla.com
|
||||||
|
bug_numbers:
|
||||||
|
- 12345
|
||||||
|
record_into_store: []
|
||||||
|
"""
|
||||||
|
scalar = load_scalar(SAMPLE_SCALAR)
|
||||||
|
parse_scalars.ScalarType("CATEGORY",
|
||||||
|
"PROVE",
|
||||||
|
scalar,
|
||||||
|
strict_type_checks=True)
|
||||||
|
self.assertRaises(SystemExit, ParserError.exit_func)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
mozunit.main()
|
mozunit.main()
|
||||||
|
|
|
@ -2,49 +2,6 @@
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
- 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/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
<!-- global warnings -->
|
|
||||||
<!ENTITY warning.safemode.label "All add-ons have been disabled by safe mode.">
|
|
||||||
<!ENTITY warning.checkcompatibility.label "Add-on compatibility checking is disabled. You may have incompatible add-ons.">
|
|
||||||
<!ENTITY warning.checkcompatibility.enable.label "Enable">
|
|
||||||
<!ENTITY warning.checkcompatibility.enable.tooltip "Enable add-on compatibility checking">
|
|
||||||
<!ENTITY warning.updatesecurity.label "Add-on update security checking is disabled. You may be compromised by updates.">
|
|
||||||
<!ENTITY warning.updatesecurity.enable.label "Enable">
|
|
||||||
<!ENTITY warning.updatesecurity.enable.tooltip "Enable add-on update security checking">
|
|
||||||
|
|
||||||
<!-- categories / views -->
|
|
||||||
<!ENTITY view.discover.label "Get Add-ons">
|
|
||||||
<!ENTITY view.recentUpdates.label "Recent Updates">
|
|
||||||
<!ENTITY view.availableUpdates.label "Available Updates">
|
|
||||||
|
|
||||||
<!-- addon updates -->
|
|
||||||
<!ENTITY updates.checkForUpdates.label "Check for Updates">
|
|
||||||
<!ENTITY updates.checkForUpdates.accesskey "C">
|
|
||||||
<!ENTITY updates.viewUpdates.label "View Recent Updates">
|
|
||||||
<!ENTITY updates.viewUpdates.accesskey "V">
|
|
||||||
<!-- LOCALIZATION NOTE (updates.updateAddonsAutomatically.label): This menu item
|
|
||||||
is a checkbox that toggles the default global behavior for add-on update
|
|
||||||
checking. -->
|
|
||||||
<!ENTITY updates.updateAddonsAutomatically.label "Update Add-ons Automatically">
|
|
||||||
<!ENTITY updates.updateAddonsAutomatically.accesskey "A">
|
|
||||||
<!-- LOCALIZATION NOTE (updates.resetUpdatesToAutomatic.label, updates.resetUpdatesToManual.label):
|
|
||||||
Specific addons can have custom update checking behaviors ("Manually",
|
|
||||||
"Automatically", "Use default global behavior"). These menu items reset the
|
|
||||||
update checking behavior for all add-ons to the default global behavior
|
|
||||||
(which itself is either "Automatically" or "Manually", controlled by the
|
|
||||||
updates.updateAddonsAutomatically.label menu item). -->
|
|
||||||
<!ENTITY updates.resetUpdatesToAutomatic.label "Reset All Add-ons to Update Automatically">
|
|
||||||
<!ENTITY updates.resetUpdatesToAutomatic.accesskey "R">
|
|
||||||
<!ENTITY updates.resetUpdatesToManual.label "Reset All Add-ons to Update Manually">
|
|
||||||
<!ENTITY updates.resetUpdatesToManual.accesskey "R">
|
|
||||||
<!ENTITY updates.updating.label "Updating add-ons">
|
|
||||||
<!ENTITY updates.installed.label "Your add-ons have been updated.">
|
|
||||||
<!ENTITY updates.downloaded.label "Your add-on updates have been downloaded.">
|
|
||||||
<!ENTITY updates.restart.label "Restart now to complete installation">
|
|
||||||
<!ENTITY updates.noneFound.label "No updates found">
|
|
||||||
<!ENTITY updates.manualUpdatesFound.label "View Available Updates">
|
|
||||||
<!ENTITY updates.updateSelected.label "Install Updates">
|
|
||||||
<!ENTITY updates.updateSelected.tooltip "Install available updates in this list">
|
|
||||||
|
|
||||||
<!-- addon actions -->
|
<!-- addon actions -->
|
||||||
<!ENTITY cmd.enableAddon.label "Enable">
|
<!ENTITY cmd.enableAddon.label "Enable">
|
||||||
<!ENTITY cmd.enableAddon.accesskey "E">
|
<!ENTITY cmd.enableAddon.accesskey "E">
|
||||||
|
|
|
@ -189,3 +189,89 @@ legacy-extensions =
|
||||||
|
|
||||||
legacy-extensions-description =
|
legacy-extensions-description =
|
||||||
These extensions do not meet current { -brand-short-name } standards so they have been deactivated. <label data-l10n-name="legacy-learn-more">Learn about the changes to add-ons</label>
|
These extensions do not meet current { -brand-short-name } standards so they have been deactivated. <label data-l10n-name="legacy-learn-more">Learn about the changes to add-ons</label>
|
||||||
|
|
||||||
|
extensions-view-discover =
|
||||||
|
.name = Get Add-ons
|
||||||
|
.tooltiptext = { extensions-view-discover.name }
|
||||||
|
|
||||||
|
extensions-view-recent-updates =
|
||||||
|
.name = Recent Updates
|
||||||
|
.tooltiptext = { extensions-view-recent-updates.name }
|
||||||
|
|
||||||
|
extensions-view-available-updates =
|
||||||
|
.name = Available Updates
|
||||||
|
.tooltiptext = { extensions-view-available-updates.name }
|
||||||
|
|
||||||
|
## These are global warnings
|
||||||
|
|
||||||
|
extensions-warning-safe-mode-label =
|
||||||
|
.value = All add-ons have been disabled by safe mode.
|
||||||
|
extensions-warning-safe-mode-container =
|
||||||
|
.tooltiptext = { extensions-warning-safe-mode-label.value }
|
||||||
|
|
||||||
|
extensions-warning-check-compatibility-label =
|
||||||
|
.value = Add-on compatibility checking is disabled. You may have incompatible add-ons.
|
||||||
|
extensions-warning-check-compatibility-container =
|
||||||
|
.tooltiptext = { extensions-warning-check-compatibility-label.value }
|
||||||
|
|
||||||
|
extensions-warning-check-compatibility-enable =
|
||||||
|
.label = Enable
|
||||||
|
.tooltiptext = Enable add-on compatibility checking
|
||||||
|
|
||||||
|
extensions-warning-update-security-label =
|
||||||
|
.value = Add-on update security checking is disabled. You may be compromised by updates.
|
||||||
|
extensions-warning-update-security-container =
|
||||||
|
.tooltiptext = { extensions-warning-update-security-label.value }
|
||||||
|
|
||||||
|
extensions-warning-update-security-enable =
|
||||||
|
.label = Enable
|
||||||
|
.tooltiptext = Enable add-on update security checking
|
||||||
|
|
||||||
|
## Strings connected to add-on updates
|
||||||
|
|
||||||
|
extensions-updates-check-for-updates =
|
||||||
|
.label = Check for Updates
|
||||||
|
.accesskey = C
|
||||||
|
|
||||||
|
extensions-updates-view-updates =
|
||||||
|
.label = View Recent Updates
|
||||||
|
.accesskey = V
|
||||||
|
|
||||||
|
# This menu item is a checkbox that toggles the default global behavior for
|
||||||
|
# add-on update checking.
|
||||||
|
|
||||||
|
extensions-updates-update-addons-automatically =
|
||||||
|
.label = Update Add-ons Automatically
|
||||||
|
.accesskey = A
|
||||||
|
|
||||||
|
## Specific add-ons can have custom update checking behaviors ("Manually",
|
||||||
|
## "Automatically", "Use default global behavior"). These menu items reset the
|
||||||
|
## update checking behavior for all add-ons to the default global behavior
|
||||||
|
## (which itself is either "Automatically" or "Manually", controlled by the
|
||||||
|
## extensions-updates-update-addons-automatically.label menu item).
|
||||||
|
|
||||||
|
extensions-updates-reset-updates-to-automatic =
|
||||||
|
.label = Reset All Add-ons to Update Automatically
|
||||||
|
.accesskey = R
|
||||||
|
|
||||||
|
extensions-updates-reset-updates-to-manual =
|
||||||
|
.label = Reset All Add-ons to Update Manually
|
||||||
|
.accesskey = R
|
||||||
|
|
||||||
|
## Status messages displayed when updating add-ons
|
||||||
|
|
||||||
|
extensions-updates-updating =
|
||||||
|
.value = Updating add-ons
|
||||||
|
extensions-updates-installed =
|
||||||
|
.value = Your add-ons have been updated.
|
||||||
|
extensions-updates-downloaded =
|
||||||
|
.value = Your add-on updates have been downloaded.
|
||||||
|
extensions-updates-restart =
|
||||||
|
.label = Restart now to complete installation
|
||||||
|
extensions-updates-none-found =
|
||||||
|
.value = No updates found
|
||||||
|
extensions-updates-manual-updates-found =
|
||||||
|
.label = View Available Updates
|
||||||
|
extensions-updates-update-selected =
|
||||||
|
.label = Install Updates
|
||||||
|
.tooltiptext = Install available updates in this list
|
||||||
|
|
|
@ -128,20 +128,23 @@
|
||||||
<richlistbox id="categories" flex="1">
|
<richlistbox id="categories" flex="1">
|
||||||
<richlistitem id="category-discover" value="addons://discover/"
|
<richlistitem id="category-discover" value="addons://discover/"
|
||||||
class="category"
|
class="category"
|
||||||
name="&view.discover.label;" priority="1000"
|
data-l10n-id="extensions-view-discover"
|
||||||
tooltiptext="&view.discover.label;"/>
|
data-l10n-attrs="name"
|
||||||
|
priority="1000"/>
|
||||||
<richlistitem id="category-legacy" value="addons://legacy/"
|
<richlistitem id="category-legacy" value="addons://legacy/"
|
||||||
class="category" priority="20000"
|
class="category" priority="20000"
|
||||||
disabled="true"/>
|
disabled="true"/>
|
||||||
<richlistitem id="category-availableUpdates" value="addons://updates/available"
|
<richlistitem id="category-availableUpdates" value="addons://updates/available"
|
||||||
class="category"
|
class="category"
|
||||||
name="&view.availableUpdates.label;" priority="100000"
|
data-l10n-id="extensions-view-available-updates"
|
||||||
tooltiptext="&view.availableUpdates.label;"
|
data-l10n-attrs="name"
|
||||||
disabled="true"/>
|
disabled="true"/>
|
||||||
<richlistitem id="category-recentUpdates" value="addons://updates/recent"
|
<richlistitem id="category-recentUpdates" value="addons://updates/recent"
|
||||||
class="category"
|
class="category"
|
||||||
name="&view.recentUpdates.label;" priority="101000"
|
data-l10n-id="extensions-view-recent-updates"
|
||||||
tooltiptext="&view.recentUpdates.label;" disabled="true"/>
|
data-l10n-attrs="name"
|
||||||
|
priority="101000"
|
||||||
|
disabled="true"/>
|
||||||
</richlistbox>
|
</richlistbox>
|
||||||
|
|
||||||
<spacer flex="1"/>
|
<spacer flex="1"/>
|
||||||
|
@ -225,18 +228,19 @@
|
||||||
<hbox id="updates-container" align="center">
|
<hbox id="updates-container" align="center">
|
||||||
<image class="spinner"/>
|
<image class="spinner"/>
|
||||||
<label id="updates-noneFound" hidden="true"
|
<label id="updates-noneFound" hidden="true"
|
||||||
value="&updates.noneFound.label;"/>
|
data-l10n-id="extensions-updates-none-found"/>
|
||||||
<button id="updates-manualUpdatesFound-btn" class="button-link"
|
<button id="updates-manualUpdatesFound-btn" class="button-link"
|
||||||
hidden="true" label="&updates.manualUpdatesFound.label;"
|
hidden="true"
|
||||||
|
data-l10n-id="extensions-updates-manual-updates-found"
|
||||||
command="cmd_goToAvailableUpdates"/>
|
command="cmd_goToAvailableUpdates"/>
|
||||||
<label id="updates-progress" hidden="true"
|
<label id="updates-progress" hidden="true"
|
||||||
value="&updates.updating.label;"/>
|
data-l10n-id="extensions-updates-updating"/>
|
||||||
<label id="updates-installed" hidden="true"
|
<label id="updates-installed" hidden="true"
|
||||||
value="&updates.installed.label;"/>
|
data-l10n-id="extensions-updates-installed"/>
|
||||||
<label id="updates-downloaded" hidden="true"
|
<label id="updates-downloaded" hidden="true"
|
||||||
value="&updates.downloaded.label;"/>
|
data-l10n-id="extensions-updates-downloaded"/>
|
||||||
<button id="updates-restart-btn" class="button-link" hidden="true"
|
<button id="updates-restart-btn" class="button-link" hidden="true"
|
||||||
label="&updates.restart.label;"
|
data-l10n-id="extensions-updates-restart"
|
||||||
command="cmd_restartApp"/>
|
command="cmd_restartApp"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
||||||
|
@ -244,12 +248,10 @@
|
||||||
data-l10n-id="tools-menu">
|
data-l10n-id="tools-menu">
|
||||||
<menupopup id="utils-menu">
|
<menupopup id="utils-menu">
|
||||||
<menuitem id="utils-updateNow"
|
<menuitem id="utils-updateNow"
|
||||||
label="&updates.checkForUpdates.label;"
|
data-l10n-id="extensions-updates-check-for-updates"
|
||||||
accesskey="&updates.checkForUpdates.accesskey;"
|
|
||||||
command="cmd_findAllUpdates"/>
|
command="cmd_findAllUpdates"/>
|
||||||
<menuitem id="utils-viewUpdates"
|
<menuitem id="utils-viewUpdates"
|
||||||
label="&updates.viewUpdates.label;"
|
data-l10n-id="extensions-updates-view-updates"
|
||||||
accesskey="&updates.viewUpdates.accesskey;"
|
|
||||||
command="cmd_goToRecentUpdates"/>
|
command="cmd_goToRecentUpdates"/>
|
||||||
<menuseparator id="utils-installFromFile-separator"/>
|
<menuseparator id="utils-installFromFile-separator"/>
|
||||||
<menuitem id="utils-installFromFile"
|
<menuitem id="utils-installFromFile"
|
||||||
|
@ -260,17 +262,14 @@
|
||||||
command="cmd_debugAddons"/>
|
command="cmd_debugAddons"/>
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
<menuitem id="utils-autoUpdateDefault"
|
<menuitem id="utils-autoUpdateDefault"
|
||||||
label="&updates.updateAddonsAutomatically.label;"
|
data-l10n-id="extensions-updates-update-addons-automatically"
|
||||||
accesskey="&updates.updateAddonsAutomatically.accesskey;"
|
|
||||||
type="checkbox" autocheck="false"
|
type="checkbox" autocheck="false"
|
||||||
command="cmd_toggleAutoUpdateDefault"/>
|
command="cmd_toggleAutoUpdateDefault"/>
|
||||||
<menuitem id="utils-resetAddonUpdatesToAutomatic"
|
<menuitem id="utils-resetAddonUpdatesToAutomatic"
|
||||||
label="&updates.resetUpdatesToAutomatic.label;"
|
data-l10n-id="extensions-updates-reset-updates-to-automatic"
|
||||||
accesskey="&updates.resetUpdatesToAutomatic.accesskey;"
|
|
||||||
command="cmd_resetAddonAutoUpdate"/>
|
command="cmd_resetAddonAutoUpdate"/>
|
||||||
<menuitem id="utils-resetAddonUpdatesToManual"
|
<menuitem id="utils-resetAddonUpdatesToManual"
|
||||||
label="&updates.resetUpdatesToManual.label;"
|
data-l10n-id="extensions-updates-reset-updates-to-manual"
|
||||||
accesskey="&updates.resetUpdatesToManual.accesskey;"
|
|
||||||
command="cmd_resetAddonAutoUpdate"/>
|
command="cmd_resetAddonAutoUpdate"/>
|
||||||
</menupopup>
|
</menupopup>
|
||||||
</toolbarbutton>
|
</toolbarbutton>
|
||||||
|
@ -310,30 +309,28 @@
|
||||||
<!-- global warnings -->
|
<!-- global warnings -->
|
||||||
<hbox class="global-warning" flex="1">
|
<hbox class="global-warning" flex="1">
|
||||||
<hbox class="global-warning-safemode" flex="1" align="center"
|
<hbox class="global-warning-safemode" flex="1" align="center"
|
||||||
tooltiptext="&warning.safemode.label;">
|
data-l10n-id="extensions-warning-safe-mode-container">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.safemode.label;"/>
|
data-l10n-id="extensions-warning-safe-mode-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
||||||
tooltiptext="&warning.checkcompatibility.label;">
|
data-l10n-id="extensions-warning-check-compatibility-container">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.checkcompatibility.label;"/>
|
data-l10n-id="extensions-warning-check-compatibility-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<button class="button-link global-warning-checkcompatibility"
|
<button class="button-link global-warning-checkcompatibility"
|
||||||
label="&warning.checkcompatibility.enable.label;"
|
data-l10n-id="extensions-warning-check-compatibility-enable"
|
||||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
|
||||||
command="cmd_enableCheckCompatibility"/>
|
command="cmd_enableCheckCompatibility"/>
|
||||||
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
||||||
tooltiptext="&warning.updatesecurity.label;">
|
data-l10n-id="extensions-warning-update-security-container">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.updatesecurity.label;"/>
|
data-l10n-id="extensions-warning-update-security-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<button class="button-link global-warning-updatesecurity"
|
<button class="button-link global-warning-updatesecurity"
|
||||||
label="&warning.updatesecurity.enable.label;"
|
data-l10n-id="extensions-warning-update-security-enable"
|
||||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
|
||||||
command="cmd_enableUpdateSecurity"/>
|
command="cmd_enableUpdateSecurity"/>
|
||||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||||
</hbox>
|
</hbox>
|
||||||
|
@ -370,30 +367,28 @@
|
||||||
<!-- global warnings -->
|
<!-- global warnings -->
|
||||||
<hbox class="global-warning" flex="1">
|
<hbox class="global-warning" flex="1">
|
||||||
<hbox class="global-warning-safemode" flex="1" align="center"
|
<hbox class="global-warning-safemode" flex="1" align="center"
|
||||||
tooltiptext="&warning.safemode.label;">
|
data-l10n-id="extensions-warning-safe-mode-container">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.safemode.label;"/>
|
data-l10n-id="extensions-warning-safe-mode-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
||||||
tooltiptext="&warning.checkcompatibility.label;">
|
data-l10n-id="extensions-warning-check-compatibility-label">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.checkcompatibility.label;"/>
|
data-l10n-id="extensions-warning-check-compatibility-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<button class="button-link global-warning-checkcompatibility"
|
<button class="button-link global-warning-checkcompatibility"
|
||||||
label="&warning.checkcompatibility.enable.label;"
|
data-l10n-id="extensions-warning-check-compatibility-enable"
|
||||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
|
||||||
command="cmd_enableCheckCompatibility"/>
|
command="cmd_enableCheckCompatibility"/>
|
||||||
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
||||||
tooltiptext="&warning.updatesecurity.label;">
|
data-l10n-id="extensions-warning-update-security-label">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.updatesecurity.label;"/>
|
data-l10n-id="extensions-warning-update-security-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<button class="button-link global-warning-updatesecurity"
|
<button class="button-link global-warning-updatesecurity"
|
||||||
label="&warning.updatesecurity.enable.label;"
|
data-l10n-id="extensions-warning-update-security-enable"
|
||||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
|
||||||
command="cmd_enableUpdateSecurity"/>
|
command="cmd_enableUpdateSecurity"/>
|
||||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||||
</hbox>
|
</hbox>
|
||||||
|
@ -411,8 +406,7 @@
|
||||||
</vbox>
|
</vbox>
|
||||||
<hbox id="update-actions" pack="center">
|
<hbox id="update-actions" pack="center">
|
||||||
<button id="update-selected-btn" hidden="true"
|
<button id="update-selected-btn" hidden="true"
|
||||||
label="&updates.updateSelected.label;"
|
data-l10n-id="extensions-updates-update-selected"/>
|
||||||
tooltiptext="&updates.updateSelected.tooltip;"/>
|
|
||||||
</hbox>
|
</hbox>
|
||||||
<richlistbox id="updates-list" class="list" flex="1"/>
|
<richlistbox id="updates-list" class="list" flex="1"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
@ -423,30 +417,28 @@
|
||||||
<!-- global warnings -->
|
<!-- global warnings -->
|
||||||
<hbox class="global-warning-container global-warning">
|
<hbox class="global-warning-container global-warning">
|
||||||
<hbox class="global-warning-safemode" flex="1" align="center"
|
<hbox class="global-warning-safemode" flex="1" align="center"
|
||||||
tooltiptext="&warning.safemode.label;">
|
data-l10n-id="extensions-warning-safe-mode-container">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.safemode.label;"/>
|
data-l10n-id="extensions-warning-safe-mode-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
||||||
tooltiptext="&warning.checkcompatibility.label;">
|
data-l10n-id="extensions-warning-check-compatibility-container">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.checkcompatibility.label;"/>
|
data-l10n-id="extensions-warning-check-compatibility-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<button class="button-link global-warning-checkcompatibility"
|
<button class="button-link global-warning-checkcompatibility"
|
||||||
label="&warning.checkcompatibility.enable.label;"
|
data-l10n-id="extensions-warning-check-compatibility-enable"
|
||||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
|
||||||
command="cmd_enableCheckCompatibility"/>
|
command="cmd_enableCheckCompatibility"/>
|
||||||
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
||||||
tooltiptext="&warning.updatesecurity.label;">
|
data-l10n-id="extensions-warning-update-security-container">
|
||||||
<image class="warning-icon"/>
|
<image class="warning-icon"/>
|
||||||
<label class="global-warning-text" flex="1" crop="end"
|
<label class="global-warning-text" flex="1" crop="end"
|
||||||
value="&warning.updatesecurity.label;"/>
|
data-l10n-id="extensions-warning-update-security-label"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<button class="button-link global-warning-updatesecurity"
|
<button class="button-link global-warning-updatesecurity"
|
||||||
label="&warning.updatesecurity.enable.label;"
|
data-l10n-id="extensions-warning-update-security-label"
|
||||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
|
||||||
command="cmd_enableUpdateSecurity"/>
|
command="cmd_enableUpdateSecurity"/>
|
||||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
|
@ -86,7 +86,7 @@ getId(const char *bin_name)
|
||||||
using namespace google_breakpad;
|
using namespace google_breakpad;
|
||||||
|
|
||||||
PageAllocator allocator;
|
PageAllocator allocator;
|
||||||
auto_wasteful_vector<uint8_t, sizeof(MDGUID)> identifier(&allocator);
|
auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier(&allocator);
|
||||||
|
|
||||||
#if defined(GP_OS_android)
|
#if defined(GP_OS_android)
|
||||||
if (nsDependentCString(bin_name).Find("!/") != kNotFound) {
|
if (nsDependentCString(bin_name).Find("!/") != kNotFound) {
|
||||||
|
|
|
@ -166,6 +166,7 @@ protected:
|
||||||
, mIsComposing(false)
|
, mIsComposing(false)
|
||||||
, mIsSynthesizedByTIP(false)
|
, mIsSynthesizedByTIP(false)
|
||||||
, mMaybeSkippableInRemoteProcess(true)
|
, mMaybeSkippableInRemoteProcess(true)
|
||||||
|
, mUseLegacyKeyCodeAndCharCodeValues(false)
|
||||||
, mEditCommandsForSingleLineEditorInitialized(false)
|
, mEditCommandsForSingleLineEditorInitialized(false)
|
||||||
, mEditCommandsForMultiLineEditorInitialized(false)
|
, mEditCommandsForMultiLineEditorInitialized(false)
|
||||||
, mEditCommandsForRichTextEditorInitialized(false)
|
, mEditCommandsForRichTextEditorInitialized(false)
|
||||||
|
@ -195,6 +196,7 @@ public:
|
||||||
, mIsComposing(false)
|
, mIsComposing(false)
|
||||||
, mIsSynthesizedByTIP(false)
|
, mIsSynthesizedByTIP(false)
|
||||||
, mMaybeSkippableInRemoteProcess(true)
|
, mMaybeSkippableInRemoteProcess(true)
|
||||||
|
, mUseLegacyKeyCodeAndCharCodeValues(false)
|
||||||
, mEditCommandsForSingleLineEditorInitialized(false)
|
, mEditCommandsForSingleLineEditorInitialized(false)
|
||||||
, mEditCommandsForMultiLineEditorInitialized(false)
|
, mEditCommandsForMultiLineEditorInitialized(false)
|
||||||
, mEditCommandsForRichTextEditorInitialized(false)
|
, mEditCommandsForRichTextEditorInitialized(false)
|
||||||
|
@ -400,6 +402,10 @@ public:
|
||||||
// Don't refer this member directly when you need to check this.
|
// Don't refer this member directly when you need to check this.
|
||||||
// Use CanSkipInRemoteProcess() instead.
|
// Use CanSkipInRemoteProcess() instead.
|
||||||
bool mMaybeSkippableInRemoteProcess;
|
bool mMaybeSkippableInRemoteProcess;
|
||||||
|
// Indicates whether the event should return legacy keyCode value and
|
||||||
|
// charCode value to web apps (one of them is always 0) or not, when it's
|
||||||
|
// an eKeyPress event.
|
||||||
|
bool mUseLegacyKeyCodeAndCharCodeValues;
|
||||||
|
|
||||||
bool CanSkipInRemoteProcess() const
|
bool CanSkipInRemoteProcess() const
|
||||||
{
|
{
|
||||||
|
@ -681,6 +687,8 @@ public:
|
||||||
#endif
|
#endif
|
||||||
mIsSynthesizedByTIP = aEvent.mIsSynthesizedByTIP;
|
mIsSynthesizedByTIP = aEvent.mIsSynthesizedByTIP;
|
||||||
mMaybeSkippableInRemoteProcess = aEvent.mMaybeSkippableInRemoteProcess;
|
mMaybeSkippableInRemoteProcess = aEvent.mMaybeSkippableInRemoteProcess;
|
||||||
|
mUseLegacyKeyCodeAndCharCodeValues =
|
||||||
|
aEvent.mUseLegacyKeyCodeAndCharCodeValues;
|
||||||
|
|
||||||
// Don't copy mEditCommandsFor*Editor because it may require a lot of
|
// Don't copy mEditCommandsFor*Editor because it may require a lot of
|
||||||
// memory space. For example, if the event is dispatched but grabbed by
|
// memory space. For example, if the event is dispatched but grabbed by
|
||||||
|
|
Загрузка…
Ссылка в новой задаче