Merge mozilla-central to mozilla-inbound. CLOSED TREE

This commit is contained in:
Csoregi Natalia 2018-11-07 18:22:28 +02:00
Родитель 76e211c34b ce0127e7c1
Коммит ec50e0e5af
74 изменённых файлов: 1997 добавлений и 1073 удалений

Просмотреть файл

@ -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 items base size was calculated from its # sizing panel. It tells users that a given items base size was calculated from its
# content size when unconstrained. # content size when unconstrained.
flexbox.itemSizing.itemBaseSizeFromContent=The items 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 items 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 elements 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&&gtkWidget,1-2,44000-152400) == 836844-1.html 836844-1-ref.html fuzzy-if(webrender&&gtkWidget,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