зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound. CLOSED TREE
This commit is contained in:
Коммит
ec50e0e5af
|
@ -288,15 +288,15 @@ window:not([chromehidden~="toolbar"]) #nav-bar[nonemptyoverflow] > .overflow-but
|
|||
|
||||
%ifdef MENUBAR_CAN_AUTOHIDE
|
||||
#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
|
||||
%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
|
||||
#main-window:not([chromemargin]) .titlebar-buttonbox-container,
|
||||
#main-window[inFullscreen] .titlebar-buttonbox-container,
|
||||
#main-window[inFullscreen] .titlebar-placeholder,
|
||||
#main-window:not([tabsintitlebar]) .titlebar-placeholder {
|
||||
#main-window[inFullscreen] .titlebar-spacer,
|
||||
#main-window:not([tabsintitlebar]) .titlebar-spacer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -760,7 +760,7 @@ xmlns="http://www.w3.org/1999/xhtml"
|
|||
<spacer flex="1000"/>
|
||||
|
||||
<hbox id="TabsToolbar-customization-target" flex="1">
|
||||
<hbox class="titlebar-placeholder" type="pre-tabs"
|
||||
<hbox class="titlebar-spacer" type="pre-tabs"
|
||||
skipintoolbarset="true"/>
|
||||
|
||||
<tabs id="tabbrowser-tabs"
|
||||
|
@ -791,7 +791,7 @@ xmlns="http://www.w3.org/1999/xhtml"
|
|||
tooltiptext="&listAllTabs.label;"
|
||||
removable="false"/>
|
||||
|
||||
<hbox class="titlebar-placeholder" type="post-tabs"
|
||||
<hbox class="titlebar-spacer" type="post-tabs"
|
||||
ordinal="1000"
|
||||
skipintoolbarset="true"/>
|
||||
</hbox>
|
||||
|
|
|
@ -843,7 +843,7 @@ var CustomizableUIInternal = {
|
|||
// 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
|
||||
// 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)) &&
|
||||
node.getAttribute("skipintoolbarset") != "true") {
|
||||
if (this.isWidgetRemovable(node)) {
|
||||
|
|
|
@ -1027,7 +1027,8 @@ BrowserGlue.prototype = {
|
|||
name: gBrowserBundle.GetStringFromName("lightTheme.name"),
|
||||
description: gBrowserBundle.GetStringFromName("lightTheme.description"),
|
||||
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",
|
||||
popup: "#fff",
|
||||
popup_text: "#0c0c0d",
|
||||
|
@ -1045,6 +1046,7 @@ BrowserGlue.prototype = {
|
|||
description: gBrowserBundle.GetStringFromName("darkTheme.description"),
|
||||
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/dark.icon.svg",
|
||||
textcolor: "rgb(249, 249, 250)",
|
||||
icon_color: "rgb(249, 249, 250, 0.7)",
|
||||
accentcolor: "hsl(240, 5%, 5%)",
|
||||
popup: "#4a4a4f",
|
||||
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.
|
||||
* See nsWindow::TopLevelWindowUseARGBVisual() for details. */
|
||||
@media (-moz-gtk-csd-transparent-background) {
|
||||
:root[tabsintitlebar]:not(:-moz-lwtheme) {
|
||||
:root[tabsintitlebar][sizemode="normal"]:not(:-moz-lwtheme) {
|
||||
background-color: transparent;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
|
|
@ -4,14 +4,3 @@
|
|||
|
||||
%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
|
||||
border there */
|
||||
@media (-moz-mac-yosemite-theme: 0) {
|
||||
.titlebar-placeholder[type="fullscreen-button"] {
|
||||
.titlebar-spacer[type="fullscreen-button"] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
--toolbar-non-lwt-bgcolor: var(--toolbar-bgcolor);
|
||||
--toolbar-non-lwt-textcolor: var(--lwt-text-color);
|
||||
--toolbar-non-lwt-bgimage: none;
|
||||
|
||||
--toolbarbutton-icon-fill-opacity: .7;
|
||||
}
|
||||
|
||||
:root:-moz-lwtheme-brighttext {
|
||||
|
|
|
@ -629,20 +629,20 @@
|
|||
|
||||
/* Drag space */
|
||||
|
||||
.titlebar-placeholder[type="pre-tabs"],
|
||||
.titlebar-placeholder[type="post-tabs"] {
|
||||
.titlebar-spacer[type="pre-tabs"],
|
||||
.titlebar-spacer[type="post-tabs"] {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.titlebar-placeholder[type="post-tabs"] {
|
||||
.titlebar-spacer[type="post-tabs"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tab separators */
|
||||
|
||||
.titlebar-placeholder[type="pre-tabs"] {
|
||||
.titlebar-spacer[type="pre-tabs"] {
|
||||
border-inline-end: 1px solid var(--lwt-background-tab-separator-color, currentColor);
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
|
|
@ -59,9 +59,9 @@
|
|||
}
|
||||
|
||||
@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 {
|
||||
--toolbarbutton-icon-fill-opacity: 1;
|
||||
--lwt-toolbarbutton-icon-fill: white;
|
||||
}
|
||||
|
||||
/* Make the menubar text readable on aero glass (copied from browser-aero.css). */
|
||||
|
|
|
@ -26,7 +26,7 @@ class CSSDeclaration extends PureComponent {
|
|||
render() {
|
||||
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-value theme-fg-color1"}, value),
|
||||
|
|
|
@ -35,11 +35,21 @@ class ChangesApp extends PureComponent {
|
|||
|
||||
renderDeclarations(remove = {}, add = {}) {
|
||||
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]) => {
|
||||
return CSSDeclaration({ className: "level diff-add", property, value });
|
||||
return CSSDeclaration({
|
||||
key: "add-" + property,
|
||||
className: "level diff-add",
|
||||
property,
|
||||
value,
|
||||
});
|
||||
});
|
||||
|
||||
return [removals, additions];
|
||||
|
@ -55,13 +65,21 @@ class ChangesApp extends PureComponent {
|
|||
// Mark this rule as rendered so we don't render it again.
|
||||
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(
|
||||
{
|
||||
key: ruleId,
|
||||
className: "rule",
|
||||
},
|
||||
dom.div(
|
||||
{
|
||||
className: "level selector",
|
||||
className: `level selector ${diffClass}`,
|
||||
title: selector,
|
||||
},
|
||||
selector,
|
||||
|
@ -73,7 +91,7 @@ class ChangesApp extends PureComponent {
|
|||
}),
|
||||
// Render any changed CSS declarations.
|
||||
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(
|
||||
{
|
||||
key: sourceId,
|
||||
className: "source devtools-monospace",
|
||||
open: true,
|
||||
},
|
||||
|
|
|
@ -133,6 +133,7 @@ function removeRule(ruleId, rules) {
|
|||
* rules: {
|
||||
* <ruleId>: {
|
||||
* 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.
|
||||
* parent: // <ruleId> of the parent rule
|
||||
* add: {
|
||||
|
@ -166,9 +167,12 @@ const reducers = {
|
|||
state = cloneState(state);
|
||||
|
||||
const { type, href, index } = change.source;
|
||||
const { selector, ancestors, ruleIndex } = change;
|
||||
const { selector, ancestors, ruleIndex, type: changeType } = change;
|
||||
const sourceId = getSourceHash(change.source);
|
||||
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.
|
||||
const source = Object.assign({}, state[sourceId], { type, href, index });
|
||||
|
@ -178,37 +182,41 @@ const reducers = {
|
|||
let rule = rules[ruleId];
|
||||
if (!rule) {
|
||||
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.
|
||||
const add = Object.assign({}, rule.add);
|
||||
// Copy or create collection of all CSS declarations ever removed from this rule.
|
||||
const remove = Object.assign({}, rule.remove);
|
||||
|
||||
if (change.remove && change.remove.property) {
|
||||
// Track the remove operation only if the property was not previously introduced
|
||||
// by an add operation. This ensures repeated changes of the same property
|
||||
// register as a single remove operation of its original value.
|
||||
if (!add[change.remove.property]) {
|
||||
remove[change.remove.property] = change.remove.value;
|
||||
}
|
||||
if (hasRemove) {
|
||||
Object.entries(change.remove).forEach(([property, value]) => {
|
||||
// Track the remove operation only if the property was not previously introduced
|
||||
// by an add operation. This ensures repeated changes of the same property
|
||||
// register as a single remove operation of its original value.
|
||||
if (!add[property]) {
|
||||
remove[property] = value;
|
||||
}
|
||||
|
||||
// Delete any previous add operation which would be canceled out by this remove.
|
||||
if (add[change.remove.property] === change.remove.value) {
|
||||
delete add[change.remove.property];
|
||||
}
|
||||
// Delete any previous add operation which would be canceled out by this remove.
|
||||
if (add[property] === value) {
|
||||
delete add[property];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (change.add && change.add.property) {
|
||||
add[change.add.property] = change.add.value;
|
||||
}
|
||||
if (hasAdd) {
|
||||
Object.entries(change.add).forEach(([property, value]) => {
|
||||
add[property] = value;
|
||||
|
||||
const property = change.add && change.add.property ||
|
||||
change.remove && change.remove.property;
|
||||
|
||||
// Remove tracked operations if they cancel each other out.
|
||||
if (add[property] === remove[property]) {
|
||||
delete add[property];
|
||||
delete remove[property];
|
||||
// Remove previously tracked declarations if they cancel each other out.
|
||||
if (add[property] === remove[property]) {
|
||||
delete add[property];
|
||||
delete remove[property];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove information about the rule if none its declarations changed.
|
||||
|
|
|
@ -15,3 +15,4 @@ support-files =
|
|||
[browser_changes_declaration_disable.js]
|
||||
[browser_changes_declaration_edit_value.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 dimensionValue = properties[dimension];
|
||||
|
||||
let title = getStr("flexbox.itemSizing.baseSizeSectionHeader");
|
||||
let property = null;
|
||||
let reason = null;
|
||||
|
||||
if (flexBasisValue) {
|
||||
// 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);
|
||||
} else {
|
||||
// Finally, if nothing is set, then the base size is the max-content size.
|
||||
reason = this.renderReasons(
|
||||
[getStr("flexbox.itemSizing.itemBaseSizeFromContent")]);
|
||||
// In this case replace the section's title.
|
||||
title = getStr("flexbox.itemSizing.itemContentSize");
|
||||
}
|
||||
|
||||
const className = "section base";
|
||||
return (
|
||||
dom.li({ className: className + (property ? "" : " no-property") },
|
||||
dom.span({ className: "name" },
|
||||
getStr("flexbox.itemSizing.baseSizeSectionHeader"),
|
||||
title,
|
||||
property
|
||||
),
|
||||
this.renderSize(mainBaseSize),
|
||||
reason
|
||||
this.renderSize(mainBaseSize)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -120,7 +119,6 @@ class FlexItemSizingProperties extends PureComponent {
|
|||
mainBaseSize,
|
||||
mainFinalSize,
|
||||
lineGrowthState,
|
||||
clampState,
|
||||
} = flexItemSizing;
|
||||
|
||||
// Don't display anything if all interesting sizes are 0.
|
||||
|
@ -139,7 +137,6 @@ class FlexItemSizingProperties extends PureComponent {
|
|||
const computedFlexGrow = computedStyle.flexGrow;
|
||||
const definedFlexShrink = properties["flex-shrink"];
|
||||
const computedFlexShrink = computedStyle.flexShrink;
|
||||
const wasClamped = clampState !== "unclamped";
|
||||
|
||||
const reasons = [];
|
||||
|
||||
|
@ -159,35 +156,15 @@ class FlexItemSizingProperties extends PureComponent {
|
|||
|
||||
let property = null;
|
||||
|
||||
if (grew) {
|
||||
// If the item grew.
|
||||
if (definedFlexGrow) {
|
||||
// It's normally because it was set to grow (flex-grow is non 0).
|
||||
property = this.renderCssProperty("flex-grow", definedFlexGrow);
|
||||
}
|
||||
|
||||
if (wasClamped && clampState === "clamped_to_max") {
|
||||
// It may have wanted to grow more than it did, because it was later max-clamped.
|
||||
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"));
|
||||
}
|
||||
if (grew && definedFlexGrow && computedFlexGrow) {
|
||||
// If the item grew it's normally because it was set to grow (flex-grow is non 0).
|
||||
property = this.renderCssProperty("flex-grow", definedFlexGrow);
|
||||
} else if (shrank && definedFlexShrink && computedFlexShrink) {
|
||||
// If the item shrank it's either because flex-shrink is non 0.
|
||||
property = this.renderCssProperty("flex-shrink", definedFlexShrink);
|
||||
} else if (shrank && computedFlexShrink) {
|
||||
// Or also because it's default value is 1 anyway.
|
||||
property = this.renderCssProperty("flex-shrink", computedFlexShrink, true);
|
||||
}
|
||||
|
||||
// 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
|
||||
// layout & is clamped.
|
||||
if (clampState !== "clamped_to_min") {
|
||||
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 (
|
||||
dom.li({ className: "section min" },
|
||||
|
@ -223,17 +210,26 @@ class FlexItemSizingProperties extends PureComponent {
|
|||
getStr("flexbox.itemSizing.minSizeSectionHeader"),
|
||||
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") {
|
||||
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 (
|
||||
dom.li({ className: "section max" },
|
||||
|
@ -241,7 +237,8 @@ class FlexItemSizingProperties extends PureComponent {
|
|||
getStr("flexbox.itemSizing.maxSizeSectionHeader"),
|
||||
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")];
|
||||
|
||||
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");
|
||||
});
|
||||
|
|
|
@ -27,9 +27,9 @@ add_task(async function() {
|
|||
ok(outlineContainer.style.gridTemplateColumns.includes("[final-end max]"),
|
||||
"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 expectedReason = getStr("flexbox.itemSizing.growthAttemptButMaxClamped");
|
||||
const expectedReason = getStr("flexbox.itemSizing.clampedToMax");
|
||||
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.
|
||||
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
|
||||
# at the start of the flex item sizing Base Size section.
|
||||
flexbox.itemSizing.baseSizeSectionHeader=Base Size
|
||||
|
@ -52,36 +45,26 @@ flexbox.itemSizing.maxSizeSectionHeader=Maximum Size
|
|||
# the start of the flex item sizing Final Size section.
|
||||
flexbox.itemSizing.finalSizeSectionHeader=Final Size
|
||||
|
||||
# LOCALIZATION NOTE (flexbox.itemSizing.itemBaseSizeFromContent): Label shown in the flex
|
||||
# item sizing panel. It tells users that a given item’s base size was calculated from its
|
||||
# LOCALIZATION NOTE (flexbox.itemSizing.itemContentSize): Label shown in the flex item
|
||||
# sizing panel. It tells users that a given item’s base size was calculated from its
|
||||
# content size when unconstrained.
|
||||
flexbox.itemSizing.itemBaseSizeFromContent=The item’s content size when unconstrained.
|
||||
flexbox.itemSizing.itemContentSize=Content Size
|
||||
|
||||
# LOCALIZATION NOTE (flexbox.itemSizing.itemMinSizeFromItemMinContent): Label shown in the
|
||||
# flex item sizing panel. It tells users that a given item’s minimum size is coming from
|
||||
# its min-content size.
|
||||
flexbox.itemSizing.itemMinSizeFromItemMinContent=This is the element’s minimum content size.
|
||||
|
||||
# LOCALIZATION NOTE (flexbox.itemSizing.growthAttemptButMaxClamped): Label shown in the
|
||||
# flexbox item sizing panel. It tells users that a given item attempted to grow by a
|
||||
# certain amount but ended up being clamped to a smaller max size.
|
||||
# LOCALIZATION NOTE (flexbox.itemSizing.clampedToMax): Label shown in the flexbox item
|
||||
# sizing panel. It tells users that a given item attempted to grow 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
|
||||
# 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).
|
||||
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
|
||||
# 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 larger min size.
|
||||
# LOCALIZATION NOTE (flexbox.itemSizing.clampedToMin): Label shown in the flexbox item
|
||||
# sizing panel. It tells users that a given item attempted to grow but ended up being
|
||||
# clamped to a larger min size.
|
||||
# (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
|
||||
# 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.
|
||||
|
||||
# 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.
|
||||
flexbox.itemSizing.clampedToMin=The item was clamped to its minimum size.
|
||||
|
||||
# LOCALIZATION NOTE (flexbox.itemSizing.setToGrow): Label shown in the flex item sizing
|
||||
# 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.
|
||||
* @param Store store
|
||||
* @param {Store} store
|
||||
* The Redux store being used.
|
||||
* @param string actionType
|
||||
* @param {String} actionType
|
||||
* The expected action to wait for.
|
||||
* @param {Number} count
|
||||
* Number of times to expect the action to occur. Default is once.
|
||||
* @return Promise
|
||||
* Resolved once the expected action is emitted by the store.
|
||||
*/
|
||||
function waitUntilAction(store, actionType) {
|
||||
function waitUntilAction(store, actionType, count = 1) {
|
||||
const deferred = defer();
|
||||
const unsubscribe = store.subscribe(check);
|
||||
const history = store.history;
|
||||
|
@ -61,8 +63,11 @@ function waitUntilAction(store, actionType) {
|
|||
const action = history[index++];
|
||||
if (action && action.type === actionType) {
|
||||
info(`Found action "${actionType}"`);
|
||||
unsubscribe();
|
||||
deferred.resolve(store.getState());
|
||||
count--;
|
||||
if (count === 0) {
|
||||
unsubscribe();
|
||||
deferred.resolve(store.getState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@
|
|||
padding-left: calc(var(--diff-level-offset) * 4);
|
||||
}
|
||||
|
||||
#sidebar-panel-changes .rule .selector,
|
||||
#sidebar-panel-changes .rule .bracket-close {
|
||||
#sidebar-panel-changes .rule .selector:not(.diff-remove):not(.diff-add),
|
||||
#sidebar-panel-changes .rule .bracket-close:not(.diff-remove):not(.diff-add) {
|
||||
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).
|
||||
this.clearCache();
|
||||
|
||||
this.axes = null;
|
||||
this.crossAxisDirection = null;
|
||||
this.flexData = null;
|
||||
this.mainAxisDirection = null;
|
||||
this.crossAxisDirection = null;
|
||||
this.axes = null;
|
||||
this.transform = null;
|
||||
|
||||
AutoRefreshHighlighter.prototype.destroy.call(this);
|
||||
}
|
||||
|
@ -337,6 +338,10 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
this.justifyContentValue = this.computedStyle.justifyContent;
|
||||
const newJustifyContent = this.justifyContentValue;
|
||||
|
||||
const oldTransform = this.transformValue;
|
||||
this.transformValue = this.computedStyle.transform;
|
||||
const newTransform = this.transformValue;
|
||||
|
||||
return hasMoved ||
|
||||
hasFlexDataChanged ||
|
||||
oldAlignItems !== newAlignItems ||
|
||||
|
@ -344,7 +349,8 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
oldFlexWrap !== newFlexWrap ||
|
||||
oldJustifyContent !== newJustifyContent ||
|
||||
oldCrossAxisDirection !== newCrossAxisDirection ||
|
||||
oldMainAxisDirection !== newMainAxisDirection;
|
||||
oldMainAxisDirection !== newMainAxisDirection ||
|
||||
oldTransform !== newTransform;
|
||||
}
|
||||
|
||||
_hide() {
|
||||
|
@ -530,8 +536,8 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
this.ctx.strokeStyle = this.color;
|
||||
this.ctx.fillStyle = this.getFlexContainerPattern(devicePixelRatio);
|
||||
|
||||
const { bounds } = this.currentQuads.content[0];
|
||||
drawRect(this.ctx, 0, 0, bounds.width, bounds.height, this.currentMatrix);
|
||||
const { clientWidth, clientHeight } = this.currentNode;
|
||||
drawRect(this.ctx, 0, 0, clientWidth, clientHeight, this.currentMatrix);
|
||||
|
||||
// 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.
|
||||
|
@ -556,6 +562,7 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
const zoom = getCurrentZoom(this.win);
|
||||
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
||||
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
||||
const containerOffsets = getNodeRect(this.currentNode);
|
||||
|
||||
this.ctx.save();
|
||||
this.ctx.translate(offset - canvasX, offset - canvasY);
|
||||
|
@ -563,21 +570,18 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
this.ctx.lineWidth = lineWidth;
|
||||
this.ctx.strokeStyle = this.color;
|
||||
|
||||
const { bounds } = this.currentQuads.content[0];
|
||||
|
||||
for (const flexLine of this.flexData.lines) {
|
||||
for (const flexItem of flexLine.items) {
|
||||
const quads = flexItem.quads;
|
||||
if (!quads.length) {
|
||||
const offsets = getNodeRect(flexItem.node);
|
||||
|
||||
if (!offsets) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust the flex item bounds relative to the current quads.
|
||||
const { bounds: flexItemBounds } = quads[0];
|
||||
const left = Math.round(flexItemBounds.left / zoom - bounds.left);
|
||||
const top = Math.round(flexItemBounds.top / zoom - bounds.top);
|
||||
const right = Math.round(flexItemBounds.right / zoom - bounds.left);
|
||||
const bottom = Math.round(flexItemBounds.bottom / zoom - bounds.top);
|
||||
const left = offsets.left - containerOffsets.left;
|
||||
const top = offsets.top - containerOffsets.top;
|
||||
const right = offsets.right - containerOffsets.left;
|
||||
const bottom = offsets.bottom - containerOffsets.top;
|
||||
|
||||
clearRect(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 canvasX = Math.round(this._canvasPosition.x * 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.translate(offset - canvasX, offset - canvasY);
|
||||
this.ctx.lineWidth = lineWidth;
|
||||
this.ctx.strokeStyle = this.color;
|
||||
|
||||
const { bounds } = this.currentQuads.content[0];
|
||||
const options = { matrix: this.currentMatrix };
|
||||
|
||||
for (const flexLine of this.flexData.lines) {
|
||||
const { crossStart, crossSize } = flexLine;
|
||||
|
||||
|
@ -616,56 +619,56 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
case "horizontal-lr vertical-bt":
|
||||
case "horizontal-rl vertical-tb":
|
||||
case "horizontal-rl vertical-bt":
|
||||
clearRect(this.ctx, 0, crossStart, bounds.width, crossStart + crossSize,
|
||||
clearRect(this.ctx, 0, crossStart, clientWidth, crossStart + crossSize,
|
||||
this.currentMatrix);
|
||||
|
||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||
if (crossStart != 0) {
|
||||
drawLine(this.ctx, 0, crossStart, bounds.width, crossStart, options);
|
||||
drawLine(this.ctx, 0, crossStart, clientWidth, crossStart, options);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
// Avoid drawing the end flex line when they overlap with the flex container.
|
||||
if (bounds.height - crossStart - crossSize >= lineWidth) {
|
||||
drawLine(this.ctx, 0, crossStart + crossSize, bounds.width,
|
||||
if (clientHeight - crossStart - crossSize >= lineWidth) {
|
||||
drawLine(this.ctx, 0, crossStart + crossSize, clientWidth,
|
||||
crossStart + crossSize, options);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
break;
|
||||
case "vertical-tb horizontal-lr":
|
||||
case "vertical-bt horizontal-rl":
|
||||
clearRect(this.ctx, crossStart, 0, crossStart + crossSize, bounds.height,
|
||||
clearRect(this.ctx, crossStart, 0, crossStart + crossSize, clientHeight,
|
||||
this.currentMatrix);
|
||||
|
||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||
if (crossStart != 0) {
|
||||
drawLine(this.ctx, crossStart, 0, crossStart, bounds.height, options);
|
||||
drawLine(this.ctx, crossStart, 0, crossStart, clientHeight, options);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
// 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,
|
||||
bounds.height, options);
|
||||
clientHeight, options);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
break;
|
||||
case "vertical-bt horizontal-lr":
|
||||
case "vertical-tb horizontal-rl":
|
||||
clearRect(this.ctx, bounds.width - crossStart, 0,
|
||||
bounds.width - crossStart - crossSize, bounds.height, this.currentMatrix);
|
||||
clearRect(this.ctx, clientWidth - crossStart, 0,
|
||||
clientWidth - crossStart - crossSize, clientHeight, this.currentMatrix);
|
||||
|
||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||
if (crossStart != 0) {
|
||||
drawLine(this.ctx, bounds.width - crossStart, 0, bounds.width - crossStart,
|
||||
bounds.height, options);
|
||||
drawLine(this.ctx, clientWidth - crossStart, 0, clientWidth - crossStart,
|
||||
clientHeight, options);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
// Avoid drawing the end flex line when they overlap with the flex container.
|
||||
if (bounds.width - crossStart - crossSize >= lineWidth) {
|
||||
drawLine(this.ctx, bounds.width - crossStart - crossSize, 0,
|
||||
bounds.width - crossStart - crossSize, bounds.height, options);
|
||||
if (clientWidth - crossStart - crossSize >= lineWidth) {
|
||||
drawLine(this.ctx, clientWidth - crossStart - crossSize, 0,
|
||||
clientWidth - crossStart - crossSize, clientHeight, options);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
break;
|
||||
|
@ -683,27 +686,27 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
return;
|
||||
}
|
||||
|
||||
const zoom = getCurrentZoom(this.win);
|
||||
const { bounds } = this.currentQuads.content[0];
|
||||
const lineWidth = getDisplayPixelRatio(this.win);
|
||||
const { clientWidth, clientHeight } = this.currentNode;
|
||||
const containerOffsets = getNodeRect(this.currentNode);
|
||||
|
||||
// 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) {
|
||||
const { crossStart, crossSize } = flexLine;
|
||||
|
||||
for (const flexItem of flexLine.items) {
|
||||
const quads = flexItem.quads;
|
||||
if (!quads.length) {
|
||||
const offsets = getNodeRect(flexItem.node);
|
||||
|
||||
if (!offsets) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust the flex item bounds relative to the current quads.
|
||||
const { bounds: flexItemBounds } = quads[0];
|
||||
const left = Math.round(flexItemBounds.left / zoom - bounds.left);
|
||||
const top = Math.round(flexItemBounds.top / zoom - bounds.top);
|
||||
const right = Math.round(flexItemBounds.right / zoom - bounds.left);
|
||||
const bottom = Math.round(flexItemBounds.bottom / zoom - bounds.top);
|
||||
const left = offsets.left - containerOffsets.left;
|
||||
const top = offsets.top - containerOffsets.top;
|
||||
const right = offsets.right - containerOffsets.left;
|
||||
const bottom = offsets.bottom - containerOffsets.top;
|
||||
|
||||
// Clear a rectangular are covering the alignment container.
|
||||
switch (this.axes) {
|
||||
|
@ -711,18 +714,22 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
|||
case "horizontal-lr vertical-bt":
|
||||
case "horizontal-rl vertical-tb":
|
||||
case "horizontal-rl vertical-bt":
|
||||
clearRect(this.ctx, left, Math.round(crossStart) + 2, right,
|
||||
Math.round(crossStart + crossSize) - 2, this.currentMatrix);
|
||||
clearRect(this.ctx,
|
||||
left, Math.round(crossStart) + 2 * lineWidth, right,
|
||||
Math.round(crossStart + crossSize) - 2 * lineWidth, this.currentMatrix);
|
||||
break;
|
||||
case "vertical-tb horizontal-lr":
|
||||
case "vertical-bt horizontal-rl":
|
||||
clearRect(this.ctx, Math.round(crossStart) + 1, top,
|
||||
Math.round(crossStart + crossSize), bottom, this.currentMatrix);
|
||||
clearRect(this.ctx,
|
||||
Math.round(crossStart) + lineWidth * 2, top,
|
||||
Math.round(crossStart + crossSize) - lineWidth, bottom, this.currentMatrix);
|
||||
break;
|
||||
case "vertical-bt horizontal-lr":
|
||||
case "vertical-tb horizontal-rl":
|
||||
clearRect(this.ctx, Math.round(bounds.width - crossStart - crossSize) + 1,
|
||||
top, Math.round(bounds.width - crossStart), bottom, this.currentMatrix);
|
||||
clearRect(this.ctx,
|
||||
Math.round(clientWidth - crossStart - crossSize) + lineWidth * 2, top,
|
||||
Math.round(clientWidth - crossStart) - lineWidth, bottom,
|
||||
this.currentMatrix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -888,4 +895,55 @@ function compareFlexData(oldFlexData, newFlexData) {
|
|||
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;
|
||||
|
|
|
@ -1071,6 +1071,72 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
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) {
|
||||
if (sheet.ownerNode) {
|
||||
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.
|
||||
modifications.map(mod => this.logChange(mod));
|
||||
modifications.map(mod => this.logDeclarationChange(mod));
|
||||
|
||||
if (this.type === ELEMENT_STYLE) {
|
||||
// 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");
|
||||
|
||||
for (const mod of modifications) {
|
||||
this.logChange(mod);
|
||||
this.logDeclarationChange(mod);
|
||||
if (mod.type === "set") {
|
||||
tempElement.style.setProperty(mod.name, mod.value, mod.priority || "");
|
||||
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
|
||||
* "track-change" event with normalized metadata which describes the change.
|
||||
* Take an object with instructions to modify a CSS declaration and log an object with
|
||||
* normalized metadata which describes the change in the context of this rule.
|
||||
*
|
||||
* @param {Object} change
|
||||
* 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,
|
||||
// to new variable names to indicate the previous state.
|
||||
let {
|
||||
|
@ -1503,59 +1569,11 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
// Append the "!important" string if defined in the previous priority flag.
|
||||
prevValue = (prevValue && prevPriority) ? `${prevValue} !important` : prevValue;
|
||||
|
||||
// Metadata about a change.
|
||||
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;
|
||||
}
|
||||
const data = this.metadata;
|
||||
|
||||
switch (change.type) {
|
||||
case "set":
|
||||
data.type = prevValue ? "declaration-add" : "declaration-update";
|
||||
// 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
|
||||
// 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.
|
||||
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
|
||||
// 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).
|
||||
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,
|
||||
// do not mark the previous declaration for removal, otherwise the add and
|
||||
|
@ -1583,14 +1601,52 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
|
|||
break;
|
||||
|
||||
case "remove":
|
||||
data.type = "declaration-remove";
|
||||
data.add = null;
|
||||
data.remove = { property: change.name, value: prevValue };
|
||||
data.remove = { [change.name]: prevValue };
|
||||
break;
|
||||
}
|
||||
|
||||
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.
|
||||
* 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 };
|
||||
}
|
||||
|
||||
// The rule's previous selector is lost after calling _addNewSelector(). Save it now.
|
||||
const oldValue = this.rawRule.selectorText;
|
||||
let selectorPromise = this._addNewSelector(value, editAuthored);
|
||||
|
||||
if (editAuthored) {
|
||||
selectorPromise = selectorPromise.then((newCssRule) => {
|
||||
if (newCssRule) {
|
||||
this.logSelectorChange(oldValue, value);
|
||||
const style = this.pageStyle._styleRef(newCssRule);
|
||||
// See the comment in |form| to understand this.
|
||||
return style.getAuthoredCssText().then(() => newCssRule);
|
||||
|
|
|
@ -85,6 +85,7 @@ var DebuggerServer = {
|
|||
this._nextConnID = 0;
|
||||
|
||||
this._initialized = true;
|
||||
this._onSocketListenerAccepted = this._onSocketListenerAccepted.bind(this);
|
||||
},
|
||||
|
||||
get protocol() {
|
||||
|
@ -225,6 +226,7 @@ var DebuggerServer = {
|
|||
* SocketListeners. This is called by a SocketListener after it is opened.
|
||||
*/
|
||||
_addListener(listener) {
|
||||
listener.on("accepted", this._onSocketListenerAccepted);
|
||||
this._listeners.push(listener);
|
||||
},
|
||||
|
||||
|
@ -233,7 +235,16 @@ var DebuggerServer = {
|
|||
* SocketListeners. This is called by a SocketListener after it is closed.
|
||||
*/
|
||||
_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);
|
||||
listener.off("accepted", this._onSocketListenerAccepted);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -254,6 +265,10 @@ var DebuggerServer = {
|
|||
return true;
|
||||
},
|
||||
|
||||
_onSocketListenerAccepted(transport, listener) {
|
||||
this._onConnection(transport, null, false, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new connection to the local debugger speaking over a fake
|
||||
* 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 + '/'|.
|
||||
* 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;
|
||||
if (forwardingPrefix) {
|
||||
connID = forwardingPrefix + "/";
|
||||
|
@ -875,7 +890,7 @@ var DebuggerServer = {
|
|||
connID = "server" + loader.id + ".conn" + this._nextConnID++ + ".";
|
||||
}
|
||||
|
||||
const conn = new DebuggerServerConnection(connID, transport);
|
||||
const conn = new DebuggerServerConnection(connID, transport, socketListener);
|
||||
this._connections[connID] = conn;
|
||||
|
||||
// Create a root actor for the connection and send the hello packet.
|
||||
|
@ -974,12 +989,16 @@ exports.DebuggerServer = DebuggerServer;
|
|||
* with prefix.
|
||||
* @param transport transport
|
||||
* 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._transport = transport;
|
||||
this._transport.hooks = this;
|
||||
this._nextID = 1;
|
||||
this._socketListener = socketListener;
|
||||
|
||||
this._actorPool = new Pool(this);
|
||||
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. */
|
||||
|
||||
/*
|
||||
|
|
|
@ -32,6 +32,7 @@ loader.lazyRequireGetter(this, "Authenticators",
|
|||
"devtools/shared/security/auth", true);
|
||||
loader.lazyRequireGetter(this, "AuthenticationResult",
|
||||
"devtools/shared/security/auth", true);
|
||||
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "nsFile", () => {
|
||||
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
|
||||
* are particular to one given listener mechanism vs. another.
|
||||
*/
|
||||
function SocketListener() {}
|
||||
function SocketListener() {
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
SocketListener.prototype = {
|
||||
|
||||
|
@ -544,11 +547,17 @@ SocketListener.prototype = {
|
|||
};
|
||||
},
|
||||
|
||||
onAllowedConnection(transport) {
|
||||
dumpn("onAllowedConnection, transport: " + transport);
|
||||
this.emit("accepted", transport, this);
|
||||
},
|
||||
|
||||
// nsIServerSocketListener implementation
|
||||
|
||||
onSocketAccepted:
|
||||
DevToolsUtils.makeInfallible(function(socket, socketTransport) {
|
||||
new ServerSocketConnection(this, socketTransport);
|
||||
const connection = new ServerSocketConnection(this, socketTransport);
|
||||
connection.once("allowed", this.onAllowedConnection.bind(this));
|
||||
}, "SocketListener.onSocketAccepted"),
|
||||
|
||||
onStopListening: function(socket, status) {
|
||||
|
@ -572,6 +581,7 @@ function ServerSocketConnection(listener, socketTransport) {
|
|||
this._listener = listener;
|
||||
this._socketTransport = socketTransport;
|
||||
this._handle();
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
ServerSocketConnection.prototype = {
|
||||
|
@ -628,15 +638,17 @@ ServerSocketConnection.prototype = {
|
|||
* the connection is denied. If the entire process resolves successfully,
|
||||
* the connection is finally handed off to the |DebuggerServer|.
|
||||
*/
|
||||
_handle() {
|
||||
async _handle() {
|
||||
dumpn("Debugging connection starting authentication on " + this.address);
|
||||
const self = this;
|
||||
(async function() {
|
||||
self._listenForTLSHandshake();
|
||||
await self._createTransport();
|
||||
await self._awaitTLSHandshake();
|
||||
await self._authenticate();
|
||||
})().then(() => this.allow()).catch(e => this.deny(e));
|
||||
try {
|
||||
this._listenForTLSHandshake();
|
||||
await this._createTransport();
|
||||
await this._awaitTLSHandshake();
|
||||
await this._authenticate();
|
||||
this.allow();
|
||||
} catch (e) {
|
||||
this.deny(e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -774,7 +786,7 @@ ServerSocketConnection.prototype = {
|
|||
return;
|
||||
}
|
||||
dumpn("Debugging connection allowed on " + this.address);
|
||||
DebuggerServer._onConnection(this._transport);
|
||||
this.emit("allowed", this._transport);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
|
|
|
@ -148,19 +148,23 @@ void KeyboardEvent::GetInitDict(KeyboardEventInit& aParam)
|
|||
|
||||
bool
|
||||
KeyboardEvent::ShouldUseSameValueForCharCodeAndKeyCode(
|
||||
const WidgetKeyboardEvent& aWidgetKeyboardEvent,
|
||||
CallerType aCallerType) const
|
||||
{
|
||||
// - 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.
|
||||
// - If this is not a keypress event, we shouldn't return same value for
|
||||
// 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
|
||||
// system or this event is now in the system group, we don't need to use
|
||||
// hack for web-compat.
|
||||
if (mInitializedByJS ||
|
||||
mEvent->mMessage != eKeyPress ||
|
||||
aWidgetKeyboardEvent.mMessage != eKeyPress ||
|
||||
aWidgetKeyboardEvent.mUseLegacyKeyCodeAndCharCodeValues ||
|
||||
aCallerType == CallerType::System ||
|
||||
mEvent->mFlags.mInSystemGroup) {
|
||||
aWidgetKeyboardEvent.mFlags.mInSystemGroup) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -193,7 +197,8 @@ KeyboardEvent::CharCode(CallerType aCallerType)
|
|||
// value.
|
||||
|
||||
if (widgetKeyboardEvent->mKeyNameIndex != KEY_NAME_INDEX_USE_STRING &&
|
||||
ShouldUseSameValueForCharCodeAndKeyCode(aCallerType)) {
|
||||
ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent,
|
||||
aCallerType)) {
|
||||
return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
|
||||
}
|
||||
|
||||
|
@ -226,7 +231,8 @@ KeyboardEvent::KeyCode(CallerType aCallerType)
|
|||
// for keyCode value if this is a "keypress" event.
|
||||
|
||||
if (widgetKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
|
||||
ShouldUseSameValueForCharCodeAndKeyCode(aCallerType)) {
|
||||
ShouldUseSameValueForCharCodeAndKeyCode(*widgetKeyboardEvent,
|
||||
aCallerType)) {
|
||||
return widgetKeyboardEvent->mCharCode;
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,9 @@ private:
|
|||
* ShouldUseSameValueForCharCodeAndKeyCode() returns true if KeyCode() and
|
||||
* CharCode() should return same value.
|
||||
*/
|
||||
bool ShouldUseSameValueForCharCodeAndKeyCode(CallerType aCallerType) const;
|
||||
bool ShouldUseSameValueForCharCodeAndKeyCode(
|
||||
const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
CallerType aCallerType) const;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
enum AutomationRate {
|
||||
"a-rate",
|
||||
"k-rate"
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled"]
|
||||
interface AudioParam {
|
||||
|
||||
|
|
|
@ -712,6 +712,56 @@ TextEditor::DeleteSelectionAsAction(EDirection aDirection,
|
|||
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.
|
||||
AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::DeleteTxnName);
|
||||
nsresult rv = DeleteSelectionAsSubAction(aDirection, aStripWrappers);
|
||||
|
@ -737,33 +787,6 @@ TextEditor::DeleteSelectionAsSubAction(EDirection aDirection,
|
|||
// Protect the edit rules object from dying
|
||||
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(
|
||||
*this,
|
||||
EditSubAction::eDeleteSelectedContent,
|
||||
|
|
|
@ -1557,7 +1557,9 @@ impl Device {
|
|||
debug_assert!(self.inside_frame);
|
||||
debug_assert!(dst.width >= src.width);
|
||||
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());
|
||||
for (read_fbo, draw_fbo) in src.fbos.iter().zip(&dst.fbos) {
|
||||
self.bind_read_target_impl(*read_fbo);
|
||||
|
|
|
@ -110,48 +110,138 @@ pub enum TextureUpdateSource {
|
|||
Bytes { data: Arc<Vec<u8>> },
|
||||
}
|
||||
|
||||
/// Command to allocate, reallocate, or free a texture for the texture cache.
|
||||
#[derive(Debug)]
|
||||
pub enum TextureUpdateOp {
|
||||
Create {
|
||||
width: u32,
|
||||
height: u32,
|
||||
format: ImageFormat,
|
||||
filter: TextureFilter,
|
||||
render_target: Option<RenderTargetInfo>,
|
||||
layer_count: i32,
|
||||
},
|
||||
Update {
|
||||
rect: DeviceUintRect,
|
||||
stride: Option<u32>,
|
||||
offset: u32,
|
||||
layer_index: i32,
|
||||
source: TextureUpdateSource,
|
||||
},
|
||||
pub struct TextureCacheAllocation {
|
||||
/// The virtual ID (i.e. distinct from device ID) of the texture.
|
||||
pub id: CacheTextureId,
|
||||
/// Details corresponding to the operation in question.
|
||||
pub kind: TextureCacheAllocationKind,
|
||||
}
|
||||
|
||||
/// Information used when allocating / reallocating.
|
||||
#[derive(Debug)]
|
||||
pub struct TextureCacheAllocInfo {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub layer_count: i32,
|
||||
pub format: ImageFormat,
|
||||
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,
|
||||
}
|
||||
|
||||
/// Command to update the contents of the texture cache.
|
||||
#[derive(Debug)]
|
||||
pub struct TextureUpdate {
|
||||
pub struct TextureCacheUpdate {
|
||||
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)]
|
||||
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 {
|
||||
/// Mints a new `TextureUpdateList`.
|
||||
pub fn new() -> Self {
|
||||
TextureUpdateList {
|
||||
allocations: Vec::new(),
|
||||
updates: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes an update operation onto the list.
|
||||
#[inline]
|
||||
pub fn push(&mut self, update: TextureUpdate) {
|
||||
pub fn push_update(&mut self, update: TextureCacheUpdate) {
|
||||
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
|
||||
|
|
|
@ -53,8 +53,8 @@ use gpu_cache::GpuDebugChunk;
|
|||
use gpu_glyph_renderer::GpuGlyphRenderer;
|
||||
use gpu_types::ScalingInstance;
|
||||
use internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
|
||||
use internal_types::{CacheTextureId, DebugOutput, FastHashMap, RenderedDocument, ResultMsg};
|
||||
use internal_types::{LayerIndex, TextureUpdateList, TextureUpdateOp, TextureUpdateSource};
|
||||
use internal_types::{CacheTextureId, DebugOutput, FastHashMap, LayerIndex, RenderedDocument, ResultMsg};
|
||||
use internal_types::{TextureCacheAllocationKind, TextureCacheUpdate, TextureUpdateList, TextureUpdateSource};
|
||||
use internal_types::{RenderTargetInfo, SavedTargetIndex};
|
||||
use prim_store::DeferredResolve;
|
||||
use profiler::{BackendProfileCounters, FrameProfileCounters,
|
||||
|
@ -2800,90 +2800,92 @@ impl Renderer {
|
|||
let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
|
||||
|
||||
for update_list in pending_texture_updates.drain(..) {
|
||||
for update in update_list.updates {
|
||||
match update.op {
|
||||
TextureUpdateOp::Create {
|
||||
width,
|
||||
height,
|
||||
layer_count,
|
||||
format,
|
||||
filter,
|
||||
render_target,
|
||||
} => {
|
||||
for allocation in update_list.allocations {
|
||||
let is_realloc = matches!(allocation.kind, TextureCacheAllocationKind::Realloc(..));
|
||||
match allocation.kind {
|
||||
TextureCacheAllocationKind::Alloc(info) |
|
||||
TextureCacheAllocationKind::Realloc(info) => {
|
||||
// Create a new native texture, as requested by the texture cache.
|
||||
//
|
||||
// Ensure no PBO is bound when creating the texture storage,
|
||||
// or GL will attempt to read data from there.
|
||||
let texture = self.device.create_texture(
|
||||
TextureTarget::Array,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
filter,
|
||||
render_target,
|
||||
layer_count,
|
||||
);
|
||||
self.texture_resolver.texture_cache_map.insert(update.id, texture);
|
||||
}
|
||||
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,
|
||||
info.format,
|
||||
info.width,
|
||||
info.height,
|
||||
info.filter,
|
||||
// This needs to be a render target because some render
|
||||
// tasks get rendered into the texture cache.
|
||||
Some(RenderTargetInfo { has_depth: false }),
|
||||
info.layer_count,
|
||||
);
|
||||
|
||||
let bytes_uploaded = match source {
|
||||
TextureUpdateSource::Bytes { data } => {
|
||||
let old = self.texture_resolver.texture_cache_map.insert(allocation.id, texture);
|
||||
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(
|
||||
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(
|
||||
rect, layer_index, stride,
|
||||
&data[offset as usize ..],
|
||||
)
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
self.profile_counters.texture_data_uploaded.add(bytes_uploaded >> 10);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle};
|
|||
use gpu_cache::{GpuCache, GpuCacheHandle};
|
||||
use gpu_types::{ImageSource, UvRectKind};
|
||||
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 render_backend::FrameId;
|
||||
use resource_cache::CacheItem;
|
||||
|
@ -19,7 +19,7 @@ use std::cmp;
|
|||
use std::mem;
|
||||
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;
|
||||
|
||||
// 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: DeviceUintPoint,
|
||||
// The layer index of the texture array.
|
||||
layer_index: u16,
|
||||
// The region that this entry belongs to in the layer.
|
||||
region_index: u16,
|
||||
layer_index: usize,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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 = "replay", derive(Deserialize))]
|
||||
pub struct TextureCache {
|
||||
// A lazily allocated, fixed size, texture array for
|
||||
// each format the texture cache supports.
|
||||
struct SharedTextures {
|
||||
array_rgba8_nearest: TextureArray,
|
||||
array_a8_linear: TextureArray,
|
||||
array_a16_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.
|
||||
max_texture_size: u32,
|
||||
|
@ -207,8 +289,9 @@ pub struct TextureCache {
|
|||
// The next unused virtual texture ID. Monotonically increasing.
|
||||
next_id: CacheTextureId,
|
||||
|
||||
// A list of updates that need to be applied to the
|
||||
// texture cache in the rendering thread this frame.
|
||||
// A list of allocations and updates that need to be
|
||||
// applied to the texture cache in the rendering thread
|
||||
// this frame.
|
||||
#[cfg_attr(all(feature = "serde", any(feature = "capture", feature = "replay")), serde(skip))]
|
||||
pending_updates: TextureUpdateList,
|
||||
|
||||
|
@ -233,46 +316,8 @@ pub struct TextureCache {
|
|||
impl TextureCache {
|
||||
pub fn new(max_texture_size: u32) -> Self {
|
||||
TextureCache {
|
||||
shared_textures: SharedTextures::new(),
|
||||
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),
|
||||
pending_updates: TextureUpdateList::new(),
|
||||
frame_id: FrameId::invalid(),
|
||||
|
@ -307,33 +352,7 @@ impl TextureCache {
|
|||
|
||||
assert!(self.entries.len() == 0);
|
||||
|
||||
if let Some(texture_id) = self.array_a8_linear.clear() {
|
||||
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,
|
||||
});
|
||||
}
|
||||
self.shared_textures.clear(&mut self.pending_updates);
|
||||
}
|
||||
|
||||
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) {
|
||||
self.expire_old_standalone_entries();
|
||||
|
||||
self.array_a8_linear
|
||||
self.shared_textures.array_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);
|
||||
self.array_rgba8_linear
|
||||
self.shared_textures.array_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);
|
||||
}
|
||||
|
||||
|
@ -461,7 +480,7 @@ impl TextureCache {
|
|||
} => (layer_index, origin),
|
||||
};
|
||||
|
||||
let op = TextureUpdate::new_update(
|
||||
let op = TextureCacheUpdate::new_update(
|
||||
data,
|
||||
&descriptor,
|
||||
origin,
|
||||
|
@ -470,7 +489,7 @@ impl TextureCache {
|
|||
layer_index as i32,
|
||||
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,
|
||||
format: ImageFormat,
|
||||
filter: TextureFilter,
|
||||
region_index: u16
|
||||
layer_index: usize,
|
||||
) -> &mut TextureRegion {
|
||||
let texture_array = 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,
|
||||
(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]
|
||||
let texture_array = self.shared_textures.select(format, filter);
|
||||
&mut texture_array.regions[layer_index]
|
||||
}
|
||||
|
||||
// Check if a given texture handle has a valid allocation
|
||||
|
@ -691,22 +696,18 @@ impl TextureCache {
|
|||
match entry.kind {
|
||||
EntryKind::Standalone { .. } => {
|
||||
// This is a standalone texture allocation. Free it directly.
|
||||
self.pending_updates.push(TextureUpdate {
|
||||
id: entry.texture_id,
|
||||
op: TextureUpdateOp::Free,
|
||||
});
|
||||
self.pending_updates.push_free(entry.texture_id);
|
||||
None
|
||||
}
|
||||
EntryKind::Cache {
|
||||
origin,
|
||||
region_index,
|
||||
..
|
||||
layer_index,
|
||||
} => {
|
||||
// Free the block in the given region.
|
||||
let region = self.get_region_mut(
|
||||
entry.format,
|
||||
entry.filter,
|
||||
region_index
|
||||
layer_index,
|
||||
);
|
||||
region.free(origin);
|
||||
Some(region)
|
||||
|
@ -722,43 +723,26 @@ impl TextureCache {
|
|||
user_data: [f32; 3],
|
||||
uv_rect_kind: UvRectKind,
|
||||
) -> Option<CacheEntry> {
|
||||
// Work out which cache it goes in, based on format.
|
||||
let texture_array = match (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!(),
|
||||
};
|
||||
// Mutably borrow the correct texture.
|
||||
let texture_array = self.shared_textures.select(descriptor.format, filter);
|
||||
|
||||
// Lazy initialize this texture array if required.
|
||||
if texture_array.texture_id.is_none() {
|
||||
assert!(texture_array.regions.is_empty());
|
||||
let texture_id = self.next_id;
|
||||
self.next_id.0 += 1;
|
||||
|
||||
let update_op = TextureUpdate {
|
||||
id: texture_id,
|
||||
op: TextureUpdateOp::Create {
|
||||
width: texture_array.dimensions,
|
||||
height: texture_array.dimensions,
|
||||
format: descriptor.format,
|
||||
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,
|
||||
},
|
||||
let info = TextureCacheAllocInfo {
|
||||
width: TEXTURE_REGION_DIMENSIONS,
|
||||
height: TEXTURE_REGION_DIMENSIONS,
|
||||
format: descriptor.format,
|
||||
filter: texture_array.filter,
|
||||
layer_count: 1,
|
||||
};
|
||||
self.pending_updates.push(update_op);
|
||||
self.pending_updates.push_alloc(texture_id, info);
|
||||
|
||||
texture_array.texture_id = Some(texture_id);
|
||||
texture_array.regions.push(TextureRegion::new(0));
|
||||
}
|
||||
|
||||
// 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 allowed_in_shared_cache {
|
||||
|
||||
new_cache_entry = self.allocate_from_shared_cache(
|
||||
&descriptor,
|
||||
filter,
|
||||
|
@ -831,10 +816,36 @@ impl TextureCache {
|
|||
uv_rect_kind,
|
||||
);
|
||||
|
||||
// If we failed to allocate in the shared cache, run an
|
||||
// eviction cycle, and then try to allocate again.
|
||||
// If we failed to allocate in the shared cache, make some space
|
||||
// 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() {
|
||||
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(
|
||||
&descriptor,
|
||||
|
@ -852,20 +863,15 @@ impl TextureCache {
|
|||
let texture_id = self.next_id;
|
||||
self.next_id.0 += 1;
|
||||
|
||||
// Create an update operation to allocate device storage
|
||||
// of the right size / format.
|
||||
let update_op = TextureUpdate {
|
||||
id: texture_id,
|
||||
op: TextureUpdateOp::Create {
|
||||
width: descriptor.size.width,
|
||||
height: descriptor.size.height,
|
||||
format: descriptor.format,
|
||||
filter,
|
||||
render_target: Some(RenderTargetInfo { has_depth: false }),
|
||||
layer_count: 1,
|
||||
},
|
||||
// Push a command to allocate device storage of the right size / format.
|
||||
let info = TextureCacheAllocInfo {
|
||||
width: descriptor.size.width,
|
||||
height: descriptor.size.height,
|
||||
format: descriptor.format,
|
||||
filter,
|
||||
layer_count: 1,
|
||||
};
|
||||
self.pending_updates.push(update_op);
|
||||
self.pending_updates.push_alloc(texture_id, info);
|
||||
|
||||
new_cache_entry = Some(CacheEntry::new_standalone(
|
||||
texture_id,
|
||||
|
@ -976,28 +982,25 @@ impl TextureLocation {
|
|||
}
|
||||
}
|
||||
|
||||
// A region is a sub-rect of a texture array layer.
|
||||
// All allocations within a region are of the same size.
|
||||
/// A region corresponds to a layer in a shared cache texture.
|
||||
///
|
||||
/// All allocations within a region are of the same size.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
struct TextureRegion {
|
||||
layer_index: i32,
|
||||
region_size: u32,
|
||||
layer_index: usize,
|
||||
slab_size: SlabSize,
|
||||
free_slots: Vec<TextureLocation>,
|
||||
total_slot_count: usize,
|
||||
origin: DeviceUintPoint,
|
||||
}
|
||||
|
||||
impl TextureRegion {
|
||||
fn new(region_size: u32, layer_index: i32, origin: DeviceUintPoint) -> Self {
|
||||
fn new(layer_index: usize) -> Self {
|
||||
TextureRegion {
|
||||
layer_index,
|
||||
region_size,
|
||||
slab_size: SlabSize::invalid(),
|
||||
free_slots: Vec::new(),
|
||||
total_slot_count: 0,
|
||||
origin,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1007,8 +1010,8 @@ impl TextureRegion {
|
|||
debug_assert!(self.free_slots.is_empty());
|
||||
|
||||
self.slab_size = slab_size;
|
||||
let slots_per_x_axis = self.region_size / self.slab_size.width;
|
||||
let slots_per_y_axis = self.region_size / self.slab_size.height;
|
||||
let slots_per_x_axis = TEXTURE_REGION_DIMENSIONS / self.slab_size.width;
|
||||
let slots_per_y_axis = TEXTURE_REGION_DIMENSIONS / self.slab_size.height;
|
||||
|
||||
// Add each block to a freelist.
|
||||
for y in 0 .. slots_per_y_axis {
|
||||
|
@ -1038,16 +1041,16 @@ impl TextureRegion {
|
|||
|
||||
self.free_slots.pop().map(|location| {
|
||||
DeviceUintPoint::new(
|
||||
self.origin.x + self.slab_size.width * location.0 as u32,
|
||||
self.origin.y + self.slab_size.height * location.1 as u32,
|
||||
self.slab_size.width * location.0 as u32,
|
||||
self.slab_size.height * location.1 as u32,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Free a block in this region.
|
||||
fn free(&mut self, point: DeviceUintPoint) {
|
||||
let x = (point.x - self.origin.x) / self.slab_size.width;
|
||||
let y = (point.y - self.origin.y) / self.slab_size.height;
|
||||
let x = point.x / self.slab_size.width;
|
||||
let y = point.y / self.slab_size.height;
|
||||
self.free_slots.push(TextureLocation::new(x, y));
|
||||
|
||||
// If this region is completely unused, deinit it
|
||||
|
@ -1059,17 +1062,14 @@ impl TextureRegion {
|
|||
}
|
||||
}
|
||||
|
||||
// A texture array contains a number of texture layers, where
|
||||
// each layer contains one or more regions that can act
|
||||
// as slab allocators.
|
||||
/// A texture array contains a number of texture layers, where each layer
|
||||
/// contains a region that can act as a slab allocator.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
struct TextureArray {
|
||||
filter: TextureFilter,
|
||||
dimensions: u32,
|
||||
layer_count: usize,
|
||||
max_layer_count: usize,
|
||||
format: ImageFormat,
|
||||
is_allocated: bool,
|
||||
regions: Vec<TextureRegion>,
|
||||
texture_id: Option<CacheTextureId>,
|
||||
}
|
||||
|
@ -1078,31 +1078,30 @@ impl TextureArray {
|
|||
fn new(
|
||||
format: ImageFormat,
|
||||
filter: TextureFilter,
|
||||
dimensions: u32,
|
||||
layer_count: usize,
|
||||
max_layer_count: usize,
|
||||
) -> Self {
|
||||
TextureArray {
|
||||
format,
|
||||
filter,
|
||||
dimensions,
|
||||
layer_count,
|
||||
is_allocated: false,
|
||||
max_layer_count,
|
||||
regions: Vec::new(),
|
||||
texture_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) -> Option<CacheTextureId> {
|
||||
self.is_allocated = false;
|
||||
fn clear(&mut self, updates: &mut TextureUpdateList) {
|
||||
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) {
|
||||
if self.is_allocated {
|
||||
let size = self.layer_count as u32 * self.dimensions *
|
||||
self.dimensions * self.format.bytes_per_pixel();
|
||||
counter.set(self.layer_count as usize, size as usize);
|
||||
let layer_count = self.regions.len();
|
||||
if layer_count != 0 {
|
||||
let size = layer_count as u32 * TEXTURE_REGION_DIMENSIONS *
|
||||
TEXTURE_REGION_DIMENSIONS * self.format.bytes_per_pixel();
|
||||
counter.set(layer_count as usize, size as usize);
|
||||
} else {
|
||||
counter.set(0, 0);
|
||||
}
|
||||
|
@ -1116,31 +1115,6 @@ impl TextureArray {
|
|||
frame_id: FrameId,
|
||||
uv_rect_kind: UvRectKind,
|
||||
) -> 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
|
||||
// allocate from.
|
||||
let slab_size = SlabSize::new(size);
|
||||
|
@ -1164,8 +1138,7 @@ impl TextureArray {
|
|||
} else if region.slab_size == slab_size {
|
||||
if let Some(location) = region.alloc() {
|
||||
entry_kind = Some(EntryKind::Cache {
|
||||
layer_index: region.layer_index as u16,
|
||||
region_index: i as u16,
|
||||
layer_index: region.layer_index,
|
||||
origin: location,
|
||||
});
|
||||
break;
|
||||
|
@ -1180,8 +1153,7 @@ impl TextureArray {
|
|||
region.init(slab_size);
|
||||
entry_kind = region.alloc().map(|location| {
|
||||
EntryKind::Cache {
|
||||
layer_index: region.layer_index as u16,
|
||||
region_index: empty_region_index as u16,
|
||||
layer_index: region.layer_index,
|
||||
origin: location,
|
||||
}
|
||||
});
|
||||
|
@ -1206,8 +1178,8 @@ impl TextureArray {
|
|||
}
|
||||
}
|
||||
|
||||
impl TextureUpdate {
|
||||
// Constructs a TextureUpdate operation to be passed to the
|
||||
impl TextureCacheUpdate {
|
||||
// Constructs a TextureCacheUpdate operation to be passed to the
|
||||
// rendering thread in order to do an upload to the right
|
||||
// location in the texture cache.
|
||||
fn new_update(
|
||||
|
@ -1218,7 +1190,7 @@ impl TextureUpdate {
|
|||
texture_id: CacheTextureId,
|
||||
layer_index: i32,
|
||||
dirty_rect: Option<DeviceUintRect>,
|
||||
) -> TextureUpdate {
|
||||
) -> TextureCacheUpdate {
|
||||
let source = match data {
|
||||
ImageData::Blob(..) => {
|
||||
panic!("The vector image should have been rasterized.");
|
||||
|
@ -1248,7 +1220,8 @@ impl TextureUpdate {
|
|||
let stride = descriptor.compute_stride();
|
||||
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(
|
||||
DeviceUintPoint::new(origin.x + dirty.origin.x, origin.y + dirty.origin.y),
|
||||
DeviceUintSize::new(
|
||||
|
@ -1263,7 +1236,8 @@ impl TextureUpdate {
|
|||
}
|
||||
}
|
||||
None => {
|
||||
TextureUpdateOp::Update {
|
||||
TextureCacheUpdate {
|
||||
id: texture_id,
|
||||
rect: DeviceUintRect::new(origin, size),
|
||||
source,
|
||||
stride: descriptor.stride,
|
||||
|
@ -1273,10 +1247,7 @@ impl TextureUpdate {
|
|||
}
|
||||
};
|
||||
|
||||
TextureUpdate {
|
||||
id: texture_id,
|
||||
op: update_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)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,15 @@ const y = 2;
|
|||
var z = 3;
|
||||
|
||||
var obj = globalLexicals();
|
||||
assertEq(Object.keys(obj).length >= 3, true);
|
||||
assertEq(obj.Foo, Foo);
|
||||
assertEq(obj.x, 1);
|
||||
assertEq(obj.y, 2);
|
||||
assertEq("z" in obj, false);
|
||||
|
||||
assertEq("uninit" in obj, false);
|
||||
let uninit;
|
||||
|
||||
// It's just a copy.
|
||||
obj.x = 2;
|
||||
assertEq(x, 1);
|
||||
|
|
|
@ -204,6 +204,7 @@ class CompileInfo
|
|||
: script_(script), fun_(fun), osrPc_(osrPc),
|
||||
analysisMode_(analysisMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
|
||||
hadOverflowBailout_(script->hadOverflowBailout()),
|
||||
hadFrequentBailouts_(script->hadFrequentBailouts()),
|
||||
mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
|
||||
inlineScriptTree_(inlineScriptTree)
|
||||
{
|
||||
|
@ -256,7 +257,7 @@ class CompileInfo
|
|||
explicit CompileInfo(unsigned nlocals)
|
||||
: script_(nullptr), fun_(nullptr), osrPc_(nullptr),
|
||||
analysisMode_(Analysis_None), scriptNeedsArgsObj_(false), hadOverflowBailout_(false),
|
||||
mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr),
|
||||
hadFrequentBailouts_(false), mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr),
|
||||
needsBodyEnvironmentObject_(false), funNeedsSomeEnvironmentObject_(false)
|
||||
{
|
||||
nimplicit_ = 0;
|
||||
|
@ -561,6 +562,9 @@ class CompileInfo
|
|||
bool hadOverflowBailout() const {
|
||||
return hadOverflowBailout_;
|
||||
}
|
||||
bool hadFrequentBailouts() const {
|
||||
return hadFrequentBailouts_;
|
||||
}
|
||||
bool mayReadFrameArgsDirectly() const {
|
||||
return mayReadFrameArgsDirectly_;
|
||||
}
|
||||
|
@ -585,6 +589,7 @@ class CompileInfo
|
|||
// Record the state of previous bailouts in order to prevent compiling the
|
||||
// same function identically the next time.
|
||||
bool hadOverflowBailout_;
|
||||
bool hadFrequentBailouts_;
|
||||
|
||||
bool mayReadFrameArgsDirectly_;
|
||||
|
||||
|
|
|
@ -1503,8 +1503,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
// LICM can hoist instructions from conditional branches and trigger
|
||||
// repeated bailouts. Disable it if this script is known to bailout
|
||||
// frequently.
|
||||
JSScript* script = mir->info().script();
|
||||
if (!script || !script->hadFrequentBailouts()) {
|
||||
if (!mir->info().hadFrequentBailouts()) {
|
||||
if (!LICM(mir, graph)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -402,14 +402,6 @@ class JSFunction : public js::NativeObject
|
|||
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,
|
||||
js::MutableHandleValue v);
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ JSScript::ensureHasAnalyzedArgsUsage(JSContext* cx)
|
|||
inline bool
|
||||
JSScript::isDebuggee() const
|
||||
{
|
||||
return realm_->debuggerObservesAllExecution() || bitFields_.hasDebugScript_;
|
||||
return realm_->debuggerObservesAllExecution() || hasDebugScript();
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -335,6 +335,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
HandleScriptSourceObject sourceObjectArg, HandleFunction fun,
|
||||
MutableHandleScript scriptp)
|
||||
{
|
||||
using ImmutableFlags = JSScript::ImmutableFlags;
|
||||
|
||||
/* NB: Keep this in sync with CopyScript. */
|
||||
|
||||
enum ScriptBits {
|
||||
|
@ -610,19 +612,19 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
scriptp.set(script);
|
||||
|
||||
if (scriptBits & (1 << Strict)) {
|
||||
script->bitFields_.strict_ = true;
|
||||
script->setFlag(ImmutableFlags::Strict);
|
||||
}
|
||||
if (scriptBits & (1 << ExplicitUseStrict)) {
|
||||
script->bitFields_.explicitUseStrict_ = true;
|
||||
script->setFlag(ImmutableFlags::ExplicitUseStrict);
|
||||
}
|
||||
if (scriptBits & (1 << ContainsDynamicNameAccess)) {
|
||||
script->bitFields_.bindingsAccessedDynamically_ = true;
|
||||
script->setFlag(ImmutableFlags::BindingsAccessedDynamically);
|
||||
}
|
||||
if (scriptBits & (1 << FunHasExtensibleScope)) {
|
||||
script->bitFields_.funHasExtensibleScope_ = true;
|
||||
script->setFlag(ImmutableFlags::FunHasExtensibleScope);
|
||||
}
|
||||
if (scriptBits & (1 << FunHasAnyAliasedFormal)) {
|
||||
script->bitFields_.funHasAnyAliasedFormal_ = true;
|
||||
script->setFlag(ImmutableFlags::FunHasAnyAliasedFormal);
|
||||
}
|
||||
if (scriptBits & (1 << ArgumentsHasVarBinding)) {
|
||||
script->setArgumentsHasVarBinding();
|
||||
|
@ -631,43 +633,43 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
script->setNeedsArgsObj(true);
|
||||
}
|
||||
if (scriptBits & (1 << HasMappedArgsObj)) {
|
||||
script->bitFields_.hasMappedArgsObj_ = true;
|
||||
script->setFlag(ImmutableFlags::HasMappedArgsObj);
|
||||
}
|
||||
if (scriptBits & (1 << FunctionHasThisBinding)) {
|
||||
script->bitFields_.functionHasThisBinding_ = true;
|
||||
script->setFlag(ImmutableFlags::FunctionHasThisBinding);
|
||||
}
|
||||
if (scriptBits & (1 << FunctionHasExtraBodyVarScope)) {
|
||||
script->bitFields_.functionHasExtraBodyVarScope_ = true;
|
||||
script->setFlag(ImmutableFlags::FunctionHasExtraBodyVarScope);
|
||||
}
|
||||
if (scriptBits & (1 << HasSingleton)) {
|
||||
script->bitFields_.hasSingletons_ = true;
|
||||
script->setFlag(ImmutableFlags::HasSingletons);
|
||||
}
|
||||
if (scriptBits & (1 << TreatAsRunOnce)) {
|
||||
script->bitFields_.treatAsRunOnce_ = true;
|
||||
script->setFlag(ImmutableFlags::TreatAsRunOnce);
|
||||
}
|
||||
if (scriptBits & (1 << HasNonSyntacticScope)) {
|
||||
script->bitFields_.hasNonSyntacticScope_ = true;
|
||||
script->setFlag(ImmutableFlags::HasNonSyntacticScope);
|
||||
}
|
||||
if (scriptBits & (1 << HasInnerFunctions)) {
|
||||
script->bitFields_.hasInnerFunctions_ = true;
|
||||
script->setFlag(ImmutableFlags::HasInnerFunctions);
|
||||
}
|
||||
if (scriptBits & (1 << NeedsHomeObject)) {
|
||||
script->bitFields_.needsHomeObject_ = true;
|
||||
script->setFlag(ImmutableFlags::NeedsHomeObject);
|
||||
}
|
||||
if (scriptBits & (1 << IsDerivedClassConstructor)) {
|
||||
script->bitFields_.isDerivedClassConstructor_ = true;
|
||||
script->setFlag(ImmutableFlags::IsDerivedClassConstructor);
|
||||
}
|
||||
if (scriptBits & (1 << IsDefaultClassConstructor)) {
|
||||
script->bitFields_.isDefaultClassConstructor_ = true;
|
||||
script->setFlag(ImmutableFlags::IsDefaultClassConstructor);
|
||||
}
|
||||
if (scriptBits & (1 << IsGenerator)) {
|
||||
script->setGeneratorKind(GeneratorKind::Generator);
|
||||
script->setFlag(ImmutableFlags::IsGenerator);
|
||||
}
|
||||
if (scriptBits & (1 << IsAsync)) {
|
||||
script->setAsyncKind(FunctionAsyncKind::AsyncFunction);
|
||||
script->setFlag(ImmutableFlags::IsAsync);
|
||||
}
|
||||
if (scriptBits & (1 << HasRest)) {
|
||||
script->setHasRest();
|
||||
script->setFlag(ImmutableFlags::HasRest);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1109,7 +1111,7 @@ JSScript::setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start,
|
|||
column_ = column;
|
||||
// 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.
|
||||
bitFields_.selfHosted_ = false;
|
||||
clearFlag(ImmutableFlags::SelfHosted);
|
||||
}
|
||||
|
||||
js::ScriptSourceObject&
|
||||
|
@ -1186,7 +1188,7 @@ JSScript::initScriptCounts(JSContext* cx)
|
|||
}
|
||||
|
||||
// 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
|
||||
// is used to let the interpreter increment the PCCounts, if present.
|
||||
|
@ -1412,7 +1414,7 @@ JSScript::getIonCounts()
|
|||
void
|
||||
JSScript::clearHasScriptCounts()
|
||||
{
|
||||
bitFields_.hasScriptCounts_ = false;
|
||||
clearFlag(MutableFlags::HasScriptCounts);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1421,7 +1423,7 @@ JSScript::releaseScriptCounts(ScriptCounts* counts)
|
|||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
*counts = std::move(*p->value().get());
|
||||
realm()->scriptCountsMap->remove(p);
|
||||
bitFields_.hasScriptCounts_ = false;
|
||||
clearHasScriptCounts();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3300,10 +3302,10 @@ JSScript::Create(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
}
|
||||
|
||||
// Record compile options that get checked at runtime.
|
||||
script->bitFields_.noScriptRval_ = options.noScriptRval;
|
||||
script->bitFields_.selfHosted_ = options.selfHostingMode;
|
||||
script->bitFields_.treatAsRunOnce_ = options.isRunOnce;
|
||||
script->bitFields_.hideScriptFromDebugger_ = options.hideScriptFromDebugger;
|
||||
script->setFlag(ImmutableFlags::NoScriptRval, options.noScriptRval);
|
||||
script->setFlag(ImmutableFlags::SelfHosted, options.selfHostingMode);
|
||||
script->setFlag(ImmutableFlags::TreatAsRunOnce, options.isRunOnce);
|
||||
script->setFlag(ImmutableFlags::HideScriptFromDebugger, options.hideScriptFromDebugger);
|
||||
|
||||
if (cx->runtime()->lcovOutput().isEnabled()) {
|
||||
if (!script->initScriptName(cx)) {
|
||||
|
@ -3449,9 +3451,9 @@ JSScript::initFromFunctionBox(HandleScript script, frontend::FunctionBox* funbox
|
|||
fun->setScript(script);
|
||||
}
|
||||
|
||||
script->bitFields_.funHasExtensibleScope_ = funbox->hasExtensibleScope();
|
||||
script->bitFields_.needsHomeObject_ = funbox->needsHomeObject();
|
||||
script->bitFields_.isDerivedClassConstructor_ = funbox->isDerivedClassConstructor();
|
||||
script->setFlag(ImmutableFlags::FunHasExtensibleScope, funbox->hasExtensibleScope());
|
||||
script->setFlag(ImmutableFlags::NeedsHomeObject, funbox->needsHomeObject());
|
||||
script->setFlag(ImmutableFlags::IsDerivedClassConstructor, funbox->isDerivedClassConstructor());
|
||||
|
||||
if (funbox->argumentsHasLocalBinding()) {
|
||||
script->setArgumentsHasVarBinding();
|
||||
|
@ -3461,37 +3463,35 @@ JSScript::initFromFunctionBox(HandleScript script, frontend::FunctionBox* funbox
|
|||
} else {
|
||||
MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
|
||||
}
|
||||
script->bitFields_.hasMappedArgsObj_ = funbox->hasMappedArgsObj();
|
||||
script->setFlag(ImmutableFlags::HasMappedArgsObj, funbox->hasMappedArgsObj());
|
||||
|
||||
script->bitFields_.functionHasThisBinding_ = funbox->hasThisBinding();
|
||||
script->bitFields_.functionHasExtraBodyVarScope_ = funbox->hasExtraBodyVarScope();
|
||||
script->setFlag(ImmutableFlags::FunctionHasThisBinding, funbox->hasThisBinding());
|
||||
script->setFlag(ImmutableFlags::FunctionHasExtraBodyVarScope, funbox->hasExtraBodyVarScope());
|
||||
|
||||
script->funLength_ = funbox->length;
|
||||
|
||||
script->setGeneratorKind(funbox->generatorKind());
|
||||
script->setAsyncKind(funbox->asyncKind());
|
||||
if (funbox->hasRest()) {
|
||||
script->setHasRest();
|
||||
}
|
||||
script->setFlag(ImmutableFlags::IsGenerator, funbox->isGenerator());
|
||||
script->setFlag(ImmutableFlags::IsAsync, funbox->isAsync());
|
||||
script->setFlag(ImmutableFlags::HasRest, funbox->hasRest());
|
||||
|
||||
PositionalFormalParameterIter fi(script);
|
||||
while (fi && !fi.closedOver()) {
|
||||
fi++;
|
||||
}
|
||||
script->bitFields_.funHasAnyAliasedFormal_ = !!fi;
|
||||
script->setFlag(ImmutableFlags::FunHasAnyAliasedFormal, !!fi);
|
||||
|
||||
script->setHasInnerFunctions(funbox->hasInnerFunctions());
|
||||
script->setFlag(ImmutableFlags::HasInnerFunctions, funbox->hasInnerFunctions());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
JSScript::initFromModuleContext(HandleScript script)
|
||||
{
|
||||
script->bitFields_.funHasExtensibleScope_ = false;
|
||||
script->bitFields_.needsHomeObject_ = false;
|
||||
script->bitFields_.isDerivedClassConstructor_ = false;
|
||||
script->clearFlag(ImmutableFlags::FunHasExtensibleScope);
|
||||
script->clearFlag(ImmutableFlags::NeedsHomeObject);
|
||||
script->clearFlag(ImmutableFlags::IsDerivedClassConstructor);
|
||||
script->funLength_ = 0;
|
||||
|
||||
script->setGeneratorKind(GeneratorKind::NotGenerator);
|
||||
script->clearFlag(ImmutableFlags::IsGenerator);
|
||||
|
||||
// Since modules are only run once, mark the script so that initializers
|
||||
// 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);
|
||||
}
|
||||
|
||||
script->bitFields_.strict_ = bce->sc->strict();
|
||||
script->bitFields_.explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
|
||||
script->bitFields_.bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
|
||||
script->bitFields_.hasSingletons_ = bce->hasSingletons;
|
||||
script->setFlag(ImmutableFlags::Strict, bce->sc->strict());
|
||||
script->setFlag(ImmutableFlags::ExplicitUseStrict, bce->sc->hasExplicitUseStrict());
|
||||
script->setFlag(ImmutableFlags::BindingsAccessedDynamically,
|
||||
bce->sc->bindingsAccessedDynamically());
|
||||
script->setFlag(ImmutableFlags::HasSingletons, bce->hasSingletons);
|
||||
|
||||
script->nfixed_ = bce->maxFixedSlots;
|
||||
script->nslots_ = nslots;
|
||||
script->bodyScopeIndex_ = bce->bodyScopeIndex;
|
||||
script->bitFields_.hasNonSyntacticScope_ =
|
||||
bce->outermostScope()->hasOnChain(ScopeKind::NonSyntactic);
|
||||
script->setFlag(ImmutableFlags::HasNonSyntacticScope,
|
||||
bce->outermostScope()->hasOnChain(ScopeKind::NonSyntactic));
|
||||
|
||||
// There shouldn't be any fallible operation after initFromFunctionBox,
|
||||
// JSFunction::hasUncompletedScript relies on the fact that the existence
|
||||
|
@ -4017,6 +4018,8 @@ bool
|
|||
js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
|
||||
MutableHandle<GCVector<Scope*>> scopes)
|
||||
{
|
||||
using ImmutableFlags = JSScript::ImmutableFlags;
|
||||
|
||||
if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
|
||||
JS_ReportErrorASCII(cx, "No cloning toplevel run-once scripts");
|
||||
return false;
|
||||
|
@ -4129,25 +4132,25 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
|
|||
dst->setNeedsArgsObj(src->needsArgsObj());
|
||||
}
|
||||
}
|
||||
dst->bitFields_.hasMappedArgsObj_ = src->hasMappedArgsObj();
|
||||
dst->bitFields_.functionHasThisBinding_ = src->functionHasThisBinding();
|
||||
dst->bitFields_.functionHasExtraBodyVarScope_ = src->functionHasExtraBodyVarScope();
|
||||
dst->bitFields_.strict_ = src->strict();
|
||||
dst->bitFields_.explicitUseStrict_ = src->explicitUseStrict();
|
||||
dst->bitFields_.hasNonSyntacticScope_ = scopes[0]->hasOnChain(ScopeKind::NonSyntactic);
|
||||
dst->bitFields_.bindingsAccessedDynamically_ = src->bindingsAccessedDynamically();
|
||||
dst->bitFields_.funHasExtensibleScope_ = src->funHasExtensibleScope();
|
||||
dst->bitFields_.funHasAnyAliasedFormal_ = src->funHasAnyAliasedFormal();
|
||||
dst->bitFields_.hasSingletons_ = src->hasSingletons();
|
||||
dst->bitFields_.treatAsRunOnce_ = src->treatAsRunOnce();
|
||||
dst->bitFields_.hasInnerFunctions_ = src->hasInnerFunctions();
|
||||
dst->setGeneratorKind(src->generatorKind());
|
||||
dst->bitFields_.isDerivedClassConstructor_ = src->isDerivedClassConstructor();
|
||||
dst->bitFields_.needsHomeObject_ = src->needsHomeObject();
|
||||
dst->bitFields_.isDefaultClassConstructor_ = src->isDefaultClassConstructor();
|
||||
dst->bitFields_.isAsync_ = src->bitFields_.isAsync_;
|
||||
dst->bitFields_.hasRest_ = src->bitFields_.hasRest_;
|
||||
dst->bitFields_.hideScriptFromDebugger_ = src->bitFields_.hideScriptFromDebugger_;
|
||||
dst->setFlag(ImmutableFlags::HasMappedArgsObj, src->hasMappedArgsObj());
|
||||
dst->setFlag(ImmutableFlags::FunctionHasThisBinding, src->functionHasThisBinding());
|
||||
dst->setFlag(ImmutableFlags::FunctionHasExtraBodyVarScope, src->functionHasExtraBodyVarScope());
|
||||
dst->setFlag(ImmutableFlags::Strict, src->strict());
|
||||
dst->setFlag(ImmutableFlags::ExplicitUseStrict, src->explicitUseStrict());
|
||||
dst->setFlag(ImmutableFlags::HasNonSyntacticScope, scopes[0]->hasOnChain(ScopeKind::NonSyntactic));
|
||||
dst->setFlag(ImmutableFlags::BindingsAccessedDynamically, src->bindingsAccessedDynamically());
|
||||
dst->setFlag(ImmutableFlags::FunHasExtensibleScope, src->funHasExtensibleScope());
|
||||
dst->setFlag(ImmutableFlags::FunHasAnyAliasedFormal, src->funHasAnyAliasedFormal());
|
||||
dst->setFlag(ImmutableFlags::HasSingletons, src->hasSingletons());
|
||||
dst->setFlag(ImmutableFlags::TreatAsRunOnce, src->treatAsRunOnce());
|
||||
dst->setFlag(ImmutableFlags::HasInnerFunctions, src->hasInnerFunctions());
|
||||
dst->setFlag(ImmutableFlags::IsGenerator, src->isGenerator());
|
||||
dst->setFlag(ImmutableFlags::IsDerivedClassConstructor, src->isDerivedClassConstructor());
|
||||
dst->setFlag(ImmutableFlags::NeedsHomeObject, src->needsHomeObject());
|
||||
dst->setFlag(ImmutableFlags::IsDefaultClassConstructor, src->isDefaultClassConstructor());
|
||||
dst->setFlag(ImmutableFlags::IsAsync, src->isAsync());
|
||||
dst->setFlag(ImmutableFlags::HasRest, src->hasRest());
|
||||
dst->setFlag(ImmutableFlags::HideScriptFromDebugger, src->hideScriptFromDebugger());
|
||||
|
||||
{
|
||||
auto array = dst->data_->scopes();
|
||||
|
@ -4294,7 +4297,7 @@ js::CloneScriptIntoFunction(JSContext* cx, HandleScope enclosingScope, HandleFun
|
|||
DebugScript*
|
||||
JSScript::debugScript()
|
||||
{
|
||||
MOZ_ASSERT(bitFields_.hasDebugScript_);
|
||||
MOZ_ASSERT(hasDebugScript());
|
||||
DebugScriptMap* map = realm()->debugScriptMap.get();
|
||||
MOZ_ASSERT(map);
|
||||
DebugScriptMap::Ptr p = map->lookup(this);
|
||||
|
@ -4305,21 +4308,21 @@ JSScript::debugScript()
|
|||
DebugScript*
|
||||
JSScript::releaseDebugScript()
|
||||
{
|
||||
MOZ_ASSERT(bitFields_.hasDebugScript_);
|
||||
MOZ_ASSERT(hasDebugScript());
|
||||
DebugScriptMap* map = realm()->debugScriptMap.get();
|
||||
MOZ_ASSERT(map);
|
||||
DebugScriptMap::Ptr p = map->lookup(this);
|
||||
MOZ_ASSERT(p);
|
||||
DebugScript* debug = p->value().release();
|
||||
map->remove(p);
|
||||
bitFields_.hasDebugScript_ = false;
|
||||
clearFlag(MutableFlags::HasDebugScript);
|
||||
return debug;
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::destroyDebugScript(FreeOp* fop)
|
||||
{
|
||||
if (bitFields_.hasDebugScript_) {
|
||||
if (hasDebugScript()) {
|
||||
#ifdef DEBUG
|
||||
for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
|
||||
if (BreakpointSite* site = getBreakpointSite(pc)) {
|
||||
|
@ -4336,7 +4339,7 @@ JSScript::destroyDebugScript(FreeOp* fop)
|
|||
bool
|
||||
JSScript::ensureHasDebugScript(JSContext* cx)
|
||||
{
|
||||
if (bitFields_.hasDebugScript_) {
|
||||
if (hasDebugScript()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4361,7 +4364,7 @@ JSScript::ensureHasDebugScript(JSContext* cx)
|
|||
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
|
||||
|
@ -4642,16 +4645,16 @@ JSScript::innermostScope(jsbytecode* pc)
|
|||
void
|
||||
JSScript::setArgumentsHasVarBinding()
|
||||
{
|
||||
bitFields_.argsHasVarBinding_ = true;
|
||||
bitFields_.needsArgsAnalysis_ = true;
|
||||
setFlag(ImmutableFlags::ArgsHasVarBinding);
|
||||
setFlag(MutableFlags::NeedsArgsAnalysis);
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::setNeedsArgsObj(bool needsArgsObj)
|
||||
{
|
||||
MOZ_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
|
||||
bitFields_.needsArgsAnalysis_ = false;
|
||||
bitFields_.needsArgsObj_ = needsArgsObj;
|
||||
clearFlag(MutableFlags::NeedsArgsAnalysis);
|
||||
setFlag(MutableFlags::NeedsArgsObj, needsArgsObj);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -4720,7 +4723,7 @@ JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script)
|
|||
MOZ_ASSERT(!script->isGenerator());
|
||||
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
|
||||
|
@ -5098,7 +5101,7 @@ JSScript::AutoDelazify::holdScript(JS::HandleFunction fun)
|
|||
JSAutoRealm ar(cx_, fun);
|
||||
script_ = JSFunction::getOrCreateScript(cx_, fun);
|
||||
if (script_) {
|
||||
oldDoNotRelazify_ = script_->bitFields_.doNotRelazify_;
|
||||
oldDoNotRelazify_ = script_->hasFlag(MutableFlags::DoNotRelazify);
|
||||
script_->setDoNotRelazify(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1634,6 +1634,143 @@ class JSScript : public js::gc::TenuredCell
|
|||
mozilla::Atomic<uint32_t, mozilla::Relaxed,
|
||||
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.
|
||||
|
||||
/**
|
||||
|
@ -1648,144 +1785,6 @@ class JSScript : public js::gc::TenuredCell
|
|||
/* Number of type sets used in this script for dynamic type monitoring. */
|
||||
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.
|
||||
//
|
||||
|
@ -1844,6 +1843,44 @@ class JSScript : public js::gc::TenuredCell
|
|||
void assertValidJumpTargets() const;
|
||||
#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:
|
||||
inline JSPrincipals* principals();
|
||||
|
||||
|
@ -1998,113 +2035,117 @@ class JSScript : public js::gc::TenuredCell
|
|||
}
|
||||
|
||||
bool noScriptRval() const {
|
||||
return bitFields_.noScriptRval_;
|
||||
return hasFlag(ImmutableFlags::NoScriptRval);
|
||||
}
|
||||
|
||||
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 {
|
||||
return bitFields_.hasNonSyntacticScope_;
|
||||
return hasFlag(ImmutableFlags::HasNonSyntacticScope);
|
||||
}
|
||||
|
||||
bool selfHosted() const { return bitFields_.selfHosted_; }
|
||||
bool bindingsAccessedDynamically() const { return bitFields_.bindingsAccessedDynamically_; }
|
||||
bool selfHosted() const { return hasFlag(ImmutableFlags::SelfHosted); }
|
||||
bool bindingsAccessedDynamically() const {
|
||||
return hasFlag(ImmutableFlags::BindingsAccessedDynamically);
|
||||
}
|
||||
bool funHasExtensibleScope() const {
|
||||
return bitFields_.funHasExtensibleScope_;
|
||||
return hasFlag(ImmutableFlags::FunHasExtensibleScope);
|
||||
}
|
||||
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 {
|
||||
return bitFields_.treatAsRunOnce_;
|
||||
return hasFlag(ImmutableFlags::TreatAsRunOnce);
|
||||
}
|
||||
bool hasRunOnce() const { return bitFields_.hasRunOnce_; }
|
||||
bool hasBeenCloned() const { return bitFields_.hasBeenCloned_; }
|
||||
bool hasRunOnce() const { return hasFlag(MutableFlags::HasRunOnce); }
|
||||
bool hasBeenCloned() const { return hasFlag(MutableFlags::HasBeenCloned); }
|
||||
|
||||
void setTreatAsRunOnce() { bitFields_.treatAsRunOnce_ = true; }
|
||||
void setHasRunOnce() { bitFields_.hasRunOnce_ = true; }
|
||||
void setHasBeenCloned() { bitFields_.hasBeenCloned_ = true; }
|
||||
void setTreatAsRunOnce() { setFlag(ImmutableFlags::TreatAsRunOnce); }
|
||||
void setHasRunOnce() { setFlag(MutableFlags::HasRunOnce); }
|
||||
void setHasBeenCloned() { setFlag(MutableFlags::HasBeenCloned); }
|
||||
|
||||
bool isActiveEval() const { return bitFields_.isActiveEval_; }
|
||||
bool isCachedEval() const { return bitFields_.isCachedEval_; }
|
||||
bool isActiveEval() const { return hasFlag(MutableFlags::IsActiveEval); }
|
||||
bool isCachedEval() const { return hasFlag(MutableFlags::IsCachedEval); }
|
||||
|
||||
void cacheForEval() {
|
||||
MOZ_ASSERT(isActiveEval());
|
||||
MOZ_ASSERT(!isCachedEval());
|
||||
bitFields_.isActiveEval_ = false;
|
||||
bitFields_.isCachedEval_ = true;
|
||||
clearFlag(MutableFlags::IsActiveEval);
|
||||
setFlag(MutableFlags::IsCachedEval);
|
||||
// IsEvalCacheCandidate will make sure that there's nothing in this
|
||||
// script that would prevent reexecution even if isRunOnce is
|
||||
// true. So just pretend like we never ran this script.
|
||||
bitFields_.hasRunOnce_ = false;
|
||||
clearFlag(MutableFlags::HasRunOnce);
|
||||
}
|
||||
|
||||
void uncacheForEval() {
|
||||
MOZ_ASSERT(isCachedEval());
|
||||
MOZ_ASSERT(!isActiveEval());
|
||||
bitFields_.isCachedEval_ = false;
|
||||
bitFields_.isActiveEval_ = true;
|
||||
clearFlag(MutableFlags::IsCachedEval);
|
||||
setFlag(MutableFlags::IsActiveEval);
|
||||
}
|
||||
|
||||
void setActiveEval() { bitFields_.isActiveEval_ = true; }
|
||||
void setActiveEval() { setFlag(MutableFlags::IsActiveEval); }
|
||||
|
||||
bool isLikelyConstructorWrapper() const {
|
||||
return bitFields_.isLikelyConstructorWrapper_;
|
||||
return hasFlag(ImmutableFlags::IsLikelyConstructorWrapper);
|
||||
}
|
||||
void setLikelyConstructorWrapper() {
|
||||
setFlag(ImmutableFlags::IsLikelyConstructorWrapper);
|
||||
}
|
||||
void setLikelyConstructorWrapper() { bitFields_.isLikelyConstructorWrapper_ = true; }
|
||||
|
||||
bool failedBoundsCheck() const {
|
||||
return bitFields_.failedBoundsCheck_;
|
||||
return hasFlag(MutableFlags::FailedBoundsCheck);
|
||||
}
|
||||
bool failedShapeGuard() const {
|
||||
return bitFields_.failedShapeGuard_;
|
||||
return hasFlag(MutableFlags::FailedShapeGuard);
|
||||
}
|
||||
bool hadFrequentBailouts() const {
|
||||
return bitFields_.hadFrequentBailouts_;
|
||||
return hasFlag(MutableFlags::HadFrequentBailouts);
|
||||
}
|
||||
bool hadOverflowBailout() const {
|
||||
return bitFields_.hadOverflowBailout_;
|
||||
return hasFlag(MutableFlags::HadOverflowBailout);
|
||||
}
|
||||
bool uninlineable() const {
|
||||
return bitFields_.uninlineable_;
|
||||
return hasFlag(MutableFlags::Uninlineable);
|
||||
}
|
||||
bool invalidatedIdempotentCache() const {
|
||||
return bitFields_.invalidatedIdempotentCache_;
|
||||
return hasFlag(MutableFlags::InvalidatedIdempotentCache);
|
||||
}
|
||||
bool failedLexicalCheck() const {
|
||||
return bitFields_.failedLexicalCheck_;
|
||||
return hasFlag(MutableFlags::FailedLexicalCheck);
|
||||
}
|
||||
bool isDefaultClassConstructor() const {
|
||||
return bitFields_.isDefaultClassConstructor_;
|
||||
return hasFlag(ImmutableFlags::IsDefaultClassConstructor);
|
||||
}
|
||||
|
||||
void setFailedBoundsCheck() { bitFields_.failedBoundsCheck_ = true; }
|
||||
void setFailedShapeGuard() { bitFields_.failedShapeGuard_ = true; }
|
||||
void setHadFrequentBailouts() { bitFields_.hadFrequentBailouts_ = true; }
|
||||
void setHadOverflowBailout() { bitFields_.hadOverflowBailout_ = true; }
|
||||
void setUninlineable() { bitFields_.uninlineable_ = true; }
|
||||
void setInvalidatedIdempotentCache() { bitFields_.invalidatedIdempotentCache_ = true; }
|
||||
void setFailedLexicalCheck() { bitFields_.failedLexicalCheck_ = true; }
|
||||
void setIsDefaultClassConstructor() { bitFields_.isDefaultClassConstructor_ = true; }
|
||||
void setFailedBoundsCheck() { setFlag(MutableFlags::FailedBoundsCheck); }
|
||||
void setFailedShapeGuard() { setFlag(MutableFlags::FailedShapeGuard); }
|
||||
void setHadFrequentBailouts() { setFlag(MutableFlags::HadFrequentBailouts); }
|
||||
void setHadOverflowBailout() { setFlag(MutableFlags::HadOverflowBailout); }
|
||||
void setUninlineable() { setFlag(MutableFlags::Uninlineable); }
|
||||
void setInvalidatedIdempotentCache() { setFlag(MutableFlags::InvalidatedIdempotentCache); }
|
||||
void setFailedLexicalCheck() { setFlag(MutableFlags::FailedLexicalCheck); }
|
||||
void setIsDefaultClassConstructor() { setFlag(ImmutableFlags::IsDefaultClassConstructor); }
|
||||
|
||||
bool hasScriptCounts() const { return bitFields_.hasScriptCounts_; }
|
||||
bool hasScriptCounts() const { return hasFlag(MutableFlags::HasScriptCounts); }
|
||||
bool hasScriptName();
|
||||
|
||||
bool hasFreezeConstraints() const { return bitFields_.hasFreezeConstraints_; }
|
||||
void setHasFreezeConstraints() { bitFields_.hasFreezeConstraints_ = true; }
|
||||
bool hasFreezeConstraints() const { return hasFlag(MutableFlags::HasFreezeConstraints); }
|
||||
void setHasFreezeConstraints() { setFlag(MutableFlags::HasFreezeConstraints); }
|
||||
|
||||
bool warnedAboutUndefinedProp() const { return bitFields_.warnedAboutUndefinedProp_; }
|
||||
void setWarnedAboutUndefinedProp() { bitFields_.warnedAboutUndefinedProp_ = true; }
|
||||
bool warnedAboutUndefinedProp() const { return hasFlag(MutableFlags::WarnedAboutUndefinedProp); }
|
||||
void setWarnedAboutUndefinedProp() { setFlag(MutableFlags::WarnedAboutUndefinedProp); }
|
||||
|
||||
/* See ContextFlags::funArgumentsHasLocalBinding comment. */
|
||||
bool argumentsHasVarBinding() const {
|
||||
return bitFields_.argsHasVarBinding_;
|
||||
return hasFlag(ImmutableFlags::ArgsHasVarBinding);
|
||||
}
|
||||
void setArgumentsHasVarBinding();
|
||||
bool argumentsAliasesFormals() const {
|
||||
|
@ -2112,52 +2153,36 @@ class JSScript : public js::gc::TenuredCell
|
|||
}
|
||||
|
||||
js::GeneratorKind generatorKind() const {
|
||||
return bitFields_.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;
|
||||
return isGenerator() ? js::GeneratorKind::Generator : js::GeneratorKind::NotGenerator;
|
||||
}
|
||||
bool isGenerator() const { return hasFlag(ImmutableFlags::IsGenerator); }
|
||||
|
||||
js::FunctionAsyncKind asyncKind() const {
|
||||
return bitFields_.isAsync_
|
||||
return isAsync()
|
||||
? js::FunctionAsyncKind::AsyncFunction
|
||||
: js::FunctionAsyncKind::SyncFunction;
|
||||
}
|
||||
bool isAsync() const {
|
||||
return bitFields_.isAsync_;
|
||||
}
|
||||
|
||||
void setAsyncKind(js::FunctionAsyncKind kind) {
|
||||
bitFields_.isAsync_ = kind == js::FunctionAsyncKind::AsyncFunction;
|
||||
return hasFlag(ImmutableFlags::IsAsync);
|
||||
}
|
||||
|
||||
bool hasRest() const {
|
||||
return bitFields_.hasRest_;
|
||||
}
|
||||
void setHasRest() {
|
||||
bitFields_.hasRest_ = true;
|
||||
return hasFlag(ImmutableFlags::HasRest);
|
||||
}
|
||||
|
||||
bool hideScriptFromDebugger() const {
|
||||
return bitFields_.hideScriptFromDebugger_;
|
||||
return hasFlag(ImmutableFlags::HideScriptFromDebugger);
|
||||
}
|
||||
void clearHideScriptFromDebugger() {
|
||||
bitFields_.hideScriptFromDebugger_ = false;
|
||||
clearFlag(ImmutableFlags::HideScriptFromDebugger);
|
||||
}
|
||||
|
||||
void setNeedsHomeObject() {
|
||||
bitFields_.needsHomeObject_ = true;
|
||||
}
|
||||
bool needsHomeObject() const {
|
||||
return bitFields_.needsHomeObject_;
|
||||
return hasFlag(ImmutableFlags::NeedsHomeObject);
|
||||
}
|
||||
|
||||
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
|
||||
* has been analyzed.
|
||||
*/
|
||||
bool analyzedArgsUsage() const { return !bitFields_.needsArgsAnalysis_; }
|
||||
bool analyzedArgsUsage() const { return !hasFlag(MutableFlags::NeedsArgsAnalysis); }
|
||||
inline bool ensureHasAnalyzedArgsUsage(JSContext* cx);
|
||||
bool needsArgsObj() const {
|
||||
MOZ_ASSERT(analyzedArgsUsage());
|
||||
return bitFields_.needsArgsObj_;
|
||||
return hasFlag(MutableFlags::NeedsArgsObj);
|
||||
}
|
||||
void setNeedsArgsObj(bool needsArgsObj);
|
||||
static bool argumentsOptimizationFailed(JSContext* cx, js::HandleScript script);
|
||||
|
||||
bool hasMappedArgsObj() const {
|
||||
return bitFields_.hasMappedArgsObj_;
|
||||
return hasFlag(ImmutableFlags::HasMappedArgsObj);
|
||||
}
|
||||
|
||||
bool functionHasThisBinding() const {
|
||||
return bitFields_.functionHasThisBinding_;
|
||||
return hasFlag(ImmutableFlags::FunctionHasThisBinding);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2200,24 +2225,20 @@ class JSScript : public js::gc::TenuredCell
|
|||
}
|
||||
|
||||
uint32_t typesGeneration() const {
|
||||
return (uint32_t) bitFields_.typesGeneration_;
|
||||
return uint32_t(hasFlag(MutableFlags::TypesGeneration));
|
||||
}
|
||||
|
||||
void setTypesGeneration(uint32_t generation) {
|
||||
MOZ_ASSERT(generation <= 1);
|
||||
bitFields_.typesGeneration_ = (bool) generation;
|
||||
setFlag(MutableFlags::TypesGeneration, bool(generation));
|
||||
}
|
||||
|
||||
void setDoNotRelazify(bool b) {
|
||||
bitFields_.doNotRelazify_ = b;
|
||||
}
|
||||
|
||||
void setHasInnerFunctions(bool b) {
|
||||
bitFields_.hasInnerFunctions_ = b;
|
||||
setFlag(MutableFlags::DoNotRelazify, b);
|
||||
}
|
||||
|
||||
bool hasInnerFunctions() const {
|
||||
return bitFields_.hasInnerFunctions_;
|
||||
return hasFlag(ImmutableFlags::HasInnerFunctions);
|
||||
}
|
||||
|
||||
bool hasAnyIonScript() const {
|
||||
|
@ -2282,11 +2303,11 @@ class JSScript : public js::gc::TenuredCell
|
|||
}
|
||||
|
||||
bool isRelazifiable() const {
|
||||
return (selfHosted() || lazyScript) && !bitFields_.hasInnerFunctions_ && !types_ &&
|
||||
return (selfHosted() || lazyScript) && !hasInnerFunctions() && !types_ &&
|
||||
!isGenerator() && !isAsync() &&
|
||||
!isDefaultClassConstructor() &&
|
||||
!hasBaselineScript() && !hasAnyIonScript() &&
|
||||
!bitFields_.doNotRelazify_;
|
||||
!hasFlag(MutableFlags::DoNotRelazify);
|
||||
}
|
||||
void setLazyScript(js::LazyScript* lazy) {
|
||||
lazyScript = lazy;
|
||||
|
@ -2416,8 +2437,9 @@ class JSScript : public js::gc::TenuredCell
|
|||
}
|
||||
|
||||
bool functionHasExtraBodyVarScope() const {
|
||||
MOZ_ASSERT_IF(bitFields_.functionHasExtraBodyVarScope_, functionHasParameterExprs());
|
||||
return bitFields_.functionHasExtraBodyVarScope_;
|
||||
bool res = hasFlag(ImmutableFlags::FunctionHasExtraBodyVarScope);
|
||||
MOZ_ASSERT_IF(res, functionHasParameterExprs());
|
||||
return res;
|
||||
}
|
||||
|
||||
js::VarScope* functionExtraBodyVarScope() const {
|
||||
|
@ -2658,9 +2680,13 @@ class JSScript : public js::gc::TenuredCell
|
|||
js::DebugScript* releaseDebugScript();
|
||||
void destroyDebugScript(js::FreeOp* fop);
|
||||
|
||||
bool hasDebugScript() const {
|
||||
return hasFlag(MutableFlags::HasDebugScript);
|
||||
}
|
||||
|
||||
public:
|
||||
bool hasBreakpointsAt(jsbytecode* pc);
|
||||
bool hasAnyBreakpointsOrStepMode() { return bitFields_.hasDebugScript_; }
|
||||
bool hasAnyBreakpointsOrStepMode() { return hasDebugScript(); }
|
||||
|
||||
// See comment above 'debugMode' in Realm.h for explanation of
|
||||
// invariants of debuggee compartments, scripts, and frames.
|
||||
|
@ -2668,7 +2694,7 @@ class JSScript : public js::gc::TenuredCell
|
|||
|
||||
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);
|
||||
|
@ -2686,10 +2712,10 @@ class JSScript : public js::gc::TenuredCell
|
|||
bool incrementStepModeCount(JSContext* cx);
|
||||
void decrementStepModeCount(js::FreeOp* fop);
|
||||
|
||||
bool stepModeEnabled() { return bitFields_.hasDebugScript_ && !!debugScript()->stepMode; }
|
||||
bool stepModeEnabled() { return hasDebugScript() && !!debugScript()->stepMode; }
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t stepModeCount() { return bitFields_.hasDebugScript_ ? debugScript()->stepMode : 0; }
|
||||
uint32_t stepModeCount() { return hasDebugScript() ? debugScript()->stepMode : 0; }
|
||||
#endif
|
||||
|
||||
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
|
||||
// next time the script is analyzed.
|
||||
bitFields_.hasFreezeConstraints_ = false;
|
||||
clearFlag(MutableFlags::HasFreezeConstraints);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -4902,7 +4902,7 @@ JSScript::sweepTypes(const js::AutoSweepTypeScript& sweep)
|
|||
if (zone()->types.hadOOMSweepingTypes()) {
|
||||
// It's possible we OOM'd while copying freeze constraints, so they
|
||||
// need to be regenerated.
|
||||
bitFields_.hasFreezeConstraints_ = false;
|
||||
clearFlag(MutableFlags::HasFreezeConstraints);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -826,7 +826,8 @@ PresShell::PresShell()
|
|||
, mHasHandledUserInput(false)
|
||||
#ifdef NIGHTLY_BUILD
|
||||
, mForceDispatchKeyPressEventsForNonPrintableKeys(false)
|
||||
, mInitializedForceDispatchKeyPressEventsForNonPrintableKeys(false)
|
||||
, mForceUseLegacyKeyCodeAndCharCodeValues(false)
|
||||
, mInitializedWithKeyPressEventDispatchingBlacklist(false)
|
||||
#endif // #ifdef NIGHTLY_BUILD
|
||||
{
|
||||
MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
|
||||
|
@ -7919,7 +7920,8 @@ GetDocumentURIToCompareWithBlacklist(PresShell& aPresShell)
|
|||
}
|
||||
|
||||
static bool
|
||||
DispatchKeyPressEventsEvenForNonPrintableKeys(nsIURI* aURI)
|
||||
IsURIInBlacklistPref(nsIURI* aURI,
|
||||
const char* aBlacklistPrefName)
|
||||
{
|
||||
if (!aURI) {
|
||||
return false;
|
||||
|
@ -7940,11 +7942,8 @@ DispatchKeyPressEventsEvenForNonPrintableKeys(nsIURI* aURI)
|
|||
|
||||
// The black list is comma separated domain list. Each item may start with
|
||||
// "*.". If starts with "*.", it matches any sub-domains.
|
||||
static const char* kPrefNameOfBlackList =
|
||||
"dom.keyboardevent.keypress.hack.dispatch_non_printable_keys";
|
||||
|
||||
nsAutoCString blackList;
|
||||
Preferences::GetCString(kPrefNameOfBlackList, blackList);
|
||||
Preferences::GetCString(aBlacklistPrefName, blackList);
|
||||
if (blackList.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -8016,8 +8015,7 @@ PresShell::DispatchEventToDOM(WidgetEvent* aEvent,
|
|||
if (aEvent->IsBlockedForFingerprintingResistance()) {
|
||||
aEvent->mFlags.mOnlySystemGroupDispatchInContent = true;
|
||||
#ifdef NIGHTLY_BUILD
|
||||
} else if (aEvent->mMessage == eKeyPress &&
|
||||
aEvent->mFlags.mOnlySystemGroupDispatchInContent) {
|
||||
} else if (aEvent->mMessage == eKeyPress) {
|
||||
// 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
|
||||
// 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.
|
||||
// Therefore, we need to keep dispatching keypress event for such keys
|
||||
// even with breaking the standard.
|
||||
if (!mInitializedForceDispatchKeyPressEventsForNonPrintableKeys) {
|
||||
mInitializedForceDispatchKeyPressEventsForNonPrintableKeys = true;
|
||||
// Similarly, the other browsers sets non-zero value of keyCode or
|
||||
// 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);
|
||||
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) {
|
||||
aEvent->mFlags.mOnlySystemGroupDispatchInContent = false;
|
||||
}
|
||||
if (mForceUseLegacyKeyCodeAndCharCodeValues) {
|
||||
aEvent->AsKeyboardEvent()->mUseLegacyKeyCodeAndCharCodeValues = true;
|
||||
}
|
||||
#endif // #ifdef NIGHTLY_BUILD
|
||||
}
|
||||
|
||||
|
|
|
@ -871,8 +871,14 @@ private:
|
|||
// Whether we should dispatch keypress events even for non-printable keys
|
||||
// for keeping backward compatibility.
|
||||
bool mForceDispatchKeyPressEventsForNonPrintableKeys : 1;
|
||||
// Whether mForceDispatchKeyPressEventsForNonPrintableKeys is initialized.
|
||||
bool mInitializedForceDispatchKeyPressEventsForNonPrintableKeys : 1;
|
||||
// Whether we should set keyCode or charCode value of keypress events whose
|
||||
// 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
|
||||
|
||||
static bool sDisableNonTestMouseEvents;
|
||||
|
|
|
@ -2645,6 +2645,7 @@ CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
|
|||
static bool
|
||||
FrameParticipatesIn3DContext(nsIFrame* aAncestor, nsIFrame* aDescendant) {
|
||||
MOZ_ASSERT(aAncestor != aDescendant);
|
||||
MOZ_ASSERT(aAncestor->GetContent() != aDescendant->GetContent());
|
||||
MOZ_ASSERT(aAncestor->Extend3DContext());
|
||||
|
||||
nsIFrame* ancestor = aAncestor->FirstContinuation();
|
||||
|
@ -2666,14 +2667,13 @@ FrameParticipatesIn3DContext(nsIFrame* aAncestor, nsIFrame* aDescendant) {
|
|||
static bool
|
||||
ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem)
|
||||
{
|
||||
nsIFrame* transformFrame;
|
||||
if (aItem->GetType() == DisplayItemType::TYPE_TRANSFORM ||
|
||||
aItem->GetType() == DisplayItemType::TYPE_PERSPECTIVE) {
|
||||
transformFrame = aItem->Frame();
|
||||
} else {
|
||||
auto type = aItem->GetType();
|
||||
if (type != DisplayItemType::TYPE_TRANSFORM &&
|
||||
type != DisplayItemType::TYPE_PERSPECTIVE) {
|
||||
return false;
|
||||
}
|
||||
if (aAncestor == transformFrame) {
|
||||
nsIFrame* transformFrame = aItem->Frame();
|
||||
if (aAncestor->GetContent() == transformFrame->GetContent()) {
|
||||
return true;
|
||||
}
|
||||
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 1469472.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-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
|
||||
|
||||
|
|
|
@ -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-1b.html 501257-1-ref.html
|
||||
== 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
|
||||
fuzzy-if(gtkWidget,0-1,0-2) == 502447-1.html 502447-1-ref.html #Bug 1315834
|
||||
== 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
|
||||
== 827799-1.html about:blank
|
||||
== 829958.html 829958-ref.html
|
||||
fuzzy-if(webrender&>kWidget,1-2,44000-152400) == 836844-1.html 836844-1-ref.html
|
||||
fuzzy-if(webrender&>kWidget,0-2,0-152400) == 836844-1.html 836844-1-ref.html
|
||||
== 841192-1.html 841192-1-ref.html
|
||||
== 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
|
||||
|
|
|
@ -5,7 +5,7 @@ fuzzy-if(webrender,9-9,4780-5120) == blur.html blur-ref.html
|
|||
== blur.svg blur-ref.svg
|
||||
== blur-calc.html blur-calc-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
|
||||
== 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
|
||||
|
|
|
@ -122,7 +122,7 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
* @return This Builder instance.
|
||||
*/
|
||||
public @NonNull Builder webFontsEnabled(final boolean flag) {
|
||||
mSettings.mWebFonts.set(flag);
|
||||
mSettings.mWebFonts.set(flag ? 1 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -368,8 +368,8 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
"javascript.enabled", true);
|
||||
/* package */ Pref<Boolean> mRemoteDebugging = new Pref<Boolean>(
|
||||
"devtools.debugger.remote-enabled", false);
|
||||
/* package */ Pref<Boolean> mWebFonts = new Pref<Boolean>(
|
||||
"browser.display.use_document_fonts", true);
|
||||
/* package */ Pref<Integer> mWebFonts = new Pref<Integer>(
|
||||
"browser.display.use_document_fonts", 1);
|
||||
/* package */ Pref<Integer> mCookieBehavior = new Pref<Integer>(
|
||||
"network.cookie.cookieBehavior", COOKIE_ACCEPT_ALL);
|
||||
/* package */ Pref<Integer> mCookieLifetime = new Pref<Integer>(
|
||||
|
@ -541,7 +541,7 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
* @return Whether web fonts support is enabled.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public @NonNull GeckoRuntimeSettings setWebFontsEnabled(final boolean flag) {
|
||||
mWebFonts.set(flag);
|
||||
mWebFonts.set(flag ? 1 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,11 +197,17 @@ VARCACHE_PREF(
|
|||
|
||||
// If this is true, "keypress" event's keyCode value and charCode value always
|
||||
// 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(
|
||||
"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
|
||||
// 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);
|
||||
#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
|
||||
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",
|
||||
"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": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"expires_in_version": "never",
|
||||
|
@ -10847,10 +10883,12 @@
|
|||
},
|
||||
"SSL_CERT_VERIFICATION_ERRORS": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"alert_emails": ["seceng@mozilla.org"],
|
||||
"expires_in_version": "default",
|
||||
"alert_emails": ["jhofmann@mozilla.com", "rtestard@mozilla.com", "seceng@mozilla.org"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"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)"
|
||||
},
|
||||
"SSL_CT_POLICY_COMPLIANCE_OF_EV_CERTS": {
|
||||
|
|
|
@ -2472,6 +2472,49 @@ telemetry.test:
|
|||
- fennec
|
||||
- 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
|
||||
# them earlier in the file and leave the telemetry.test category as the last
|
||||
# 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,
|
||||
label_count, key_index, key_count):
|
||||
def print_array_entry(output, histogram, name_index, exp_index,
|
||||
label_index, label_count,
|
||||
key_index, key_count,
|
||||
store_index, store_count):
|
||||
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.high(),
|
||||
histogram.n_buckets(),
|
||||
|
@ -31,8 +33,10 @@ def print_array_entry(output, histogram, name_index, exp_index, label_index,
|
|||
exp_index,
|
||||
label_count,
|
||||
key_count,
|
||||
store_count,
|
||||
label_index,
|
||||
key_index,
|
||||
store_index,
|
||||
"true" if histogram.keyed() else "false",
|
||||
histogram.nsITelemetry_kind(),
|
||||
histogram.dataset(),
|
||||
|
@ -42,10 +46,13 @@ def print_array_entry(output, histogram, name_index, exp_index, label_index,
|
|||
|
||||
def write_histogram_table(output, histograms):
|
||||
string_table = StringTable()
|
||||
|
||||
label_table = []
|
||||
label_count = 0
|
||||
keys_table = []
|
||||
keys_count = 0
|
||||
store_table = []
|
||||
total_store_count = 0
|
||||
|
||||
print("constexpr HistogramInfo gHistogramInfos[] = {", file=output)
|
||||
for histogram in histograms:
|
||||
|
@ -66,8 +73,19 @@ def write_histogram_table(output, histograms):
|
|||
keys_table.append((histogram.name(), string_table.stringIndexes(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,
|
||||
label_index, len(labels), key_index, len(keys))
|
||||
label_index, len(labels), key_index, len(keys),
|
||||
store_index, len(stores))
|
||||
print("};\n", file=output)
|
||||
|
||||
strtab_name = "gHistogramStringTable"
|
||||
|
@ -95,6 +113,18 @@ def write_histogram_table(output, histograms):
|
|||
print("};", file=output)
|
||||
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
|
||||
# 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.
|
||||
|
||||
: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:
|
||||
print("#if defined(%s)" % cpp_guard, file=output)
|
||||
|
||||
print(" {{ {}, {}, {}, {}, {}, {}, {} }},"
|
||||
print(" {{ {}, {}, {}, {}, {}, {}, {}, {}, {} }},"
|
||||
.format(scalar.nsITelemetry_kind,
|
||||
name_index,
|
||||
expiration_index,
|
||||
scalar.dataset,
|
||||
" | ".join(scalar.record_in_processes_enum),
|
||||
"true" if scalar.keyed else "false",
|
||||
" | ".join(scalar.products_enum)),
|
||||
" | ".join(scalar.products_enum),
|
||||
store_count,
|
||||
store_index),
|
||||
file=output)
|
||||
|
||||
if cpp_guard:
|
||||
|
@ -69,14 +71,28 @@ def write_scalar_tables(scalars, output):
|
|||
"""
|
||||
string_table = StringTable()
|
||||
|
||||
store_table = []
|
||||
total_store_count = 0
|
||||
|
||||
print("const ScalarInfo gScalars[] = {", file=output)
|
||||
for s in scalars:
|
||||
# We add both the scalar label and the expiration string to the strings
|
||||
# table.
|
||||
name_index = string_table.stringIndex(s.label)
|
||||
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_scalar_info(s, output, name_index, exp_index)
|
||||
write_scalar_info(s, output, name_index, exp_index, store_index, len(stores))
|
||||
print("};", file=output)
|
||||
|
||||
string_table_name = "gScalarsStringTable"
|
||||
|
@ -84,6 +100,18 @@ def write_scalar_tables(scalars, output):
|
|||
static_assert(output, "sizeof(%s) <= UINT32_MAX" % string_table_name,
|
||||
"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):
|
||||
if len(filenames) > 1:
|
||||
|
|
|
@ -36,6 +36,7 @@ ALWAYS_ALLOWED_KEYS = [
|
|||
'bug_numbers',
|
||||
'keys',
|
||||
'record_in_processes',
|
||||
'record_into_store',
|
||||
'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._labels = definition.get('labels', [])
|
||||
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._operating_systems = definition.get('operating_systems', ["all"])
|
||||
|
||||
|
@ -232,6 +234,10 @@ the histogram."""
|
|||
|
||||
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):
|
||||
"""Return an array of lower bounds for each bucket in the histogram."""
|
||||
bucket_fns = {
|
||||
|
@ -303,6 +309,7 @@ the histogram."""
|
|||
self.check_record_in_processes(name, definition)
|
||||
self.check_products(name, definition)
|
||||
self.check_operating_systems(name, definition)
|
||||
self.check_record_into_store(name, definition)
|
||||
|
||||
def check_name(self, name):
|
||||
if '#' in name:
|
||||
|
@ -415,7 +422,7 @@ the histogram."""
|
|||
field = 'operating_systems'
|
||||
operating_systems = definition.get(field)
|
||||
|
||||
DOC_URL = HISTOGRAMS_DOC_URL + "#operating_systems"
|
||||
DOC_URL = HISTOGRAMS_DOC_URL + "#operating-systems"
|
||||
|
||||
if not operating_systems:
|
||||
# operating_systems is optional
|
||||
|
@ -426,6 +433,23 @@ the histogram."""
|
|||
ParserError('Histogram "%s" has unknown operating system "%s" in %s.\n%s' %
|
||||
(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):
|
||||
keys = definition.get('keys')
|
||||
if not self._strict_type_checks or keys is None:
|
||||
|
@ -512,6 +536,7 @@ the histogram."""
|
|||
"keys": basestring,
|
||||
"products": basestring,
|
||||
"operating_systems": basestring,
|
||||
"record_into_store": basestring,
|
||||
}
|
||||
|
||||
# For the server-side, where _strict_type_checks==False, we want to
|
||||
|
|
|
@ -111,6 +111,7 @@ class ScalarType:
|
|||
'release_channel_collection': basestring,
|
||||
'keyed': bool,
|
||||
'products': list,
|
||||
'record_into_store': list,
|
||||
}
|
||||
|
||||
# The types for the data within the fields that hold lists.
|
||||
|
@ -119,6 +120,7 @@ class ScalarType:
|
|||
'notification_emails': basestring,
|
||||
'record_in_processes': basestring,
|
||||
'products': basestring,
|
||||
'record_into_store': basestring,
|
||||
}
|
||||
|
||||
# Concatenate the required and optional field definitions.
|
||||
|
@ -329,6 +331,11 @@ class ScalarType:
|
|||
"""Get the cpp guard for this scalar"""
|
||||
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):
|
||||
"""Parses a YAML file containing the scalar definition.
|
||||
|
|
|
@ -49,6 +49,8 @@ struct BaseScalarInfo {
|
|||
struct ScalarInfo : BaseScalarInfo {
|
||||
uint32_t name_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
|
||||
// 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,
|
||||
uint32_t aDataset,
|
||||
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)
|
||||
, name_offset(aNameOffset)
|
||||
, expiration_offset(aExpirationOffset)
|
||||
|
|
|
@ -140,8 +140,10 @@ struct HistogramInfo {
|
|||
uint32_t expiration_offset;
|
||||
uint32_t label_count;
|
||||
uint32_t key_count;
|
||||
uint32_t store_count;
|
||||
uint16_t label_index;
|
||||
uint16_t key_index;
|
||||
uint16_t store_index;
|
||||
bool keyed;
|
||||
uint8_t histogramType;
|
||||
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``.
|
||||
|
||||
``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 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``
|
||||
- ``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
|
||||
------------------------
|
||||
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_BYTES_BEFORE_CERT_CALLBACK",
|
||||
"SSL_CERT_ERROR_OVERRIDES",
|
||||
"SSL_CERT_VERIFICATION_ERRORS",
|
||||
"SSL_CIPHER_SUITE_FULL",
|
||||
"SSL_CIPHER_SUITE_RESUMED",
|
||||
"SSL_HANDSHAKE_TYPE",
|
||||
|
@ -1363,7 +1362,6 @@
|
|||
"TRANSLATED_PAGES_BY_LANGUAGE",
|
||||
"MOZ_SQLITE_OTHER_WRITE_B",
|
||||
"LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS",
|
||||
"SSL_CERT_VERIFICATION_ERRORS",
|
||||
"FX_SESSION_RESTORE_NUMBER_OF_WINDOWS_RESTORED",
|
||||
"MOZ_SQLITE_PLACES_WRITE_MS",
|
||||
"FX_THUMBNAILS_BG_CAPTURE_CANVAS_DRAW_TIME_MS",
|
||||
|
|
|
@ -42,7 +42,7 @@ add_task(async function test_recording() {
|
|||
"pre_content_spawn_expiration": {
|
||||
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
||||
keyed: false,
|
||||
release_channel_collection: true,
|
||||
record_on_release: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -65,17 +65,17 @@ add_task(async function test_recording() {
|
|||
"post_content_spawn": {
|
||||
kind: Ci.nsITelemetry.SCALAR_TYPE_BOOLEAN,
|
||||
keyed: false,
|
||||
release_channel_collection: false,
|
||||
record_on_release: false,
|
||||
},
|
||||
"post_content_spawn_keyed": {
|
||||
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
||||
keyed: true,
|
||||
release_channel_collection: true,
|
||||
record_on_release: true,
|
||||
},
|
||||
"pre_content_spawn_expiration": {
|
||||
kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
|
||||
keyed: false,
|
||||
release_channel_collection: true,
|
||||
record_on_release: true,
|
||||
expired: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -40,6 +40,7 @@ class TestParser(unittest.TestCase):
|
|||
self.assertTrue(hist.expiration(), "never")
|
||||
self.assertTrue(hist.kind(), "boolean")
|
||||
self.assertTrue(hist.record_in_processes, ["main", "content"])
|
||||
self.assertTrue(hist.record_into_store, ["main"])
|
||||
|
||||
def test_missing_bug_numbers(self):
|
||||
SAMPLE_HISTOGRAM = {
|
||||
|
@ -325,6 +326,50 @@ class TestParser(unittest.TestCase):
|
|||
|
||||
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__':
|
||||
mozunit.main()
|
||||
|
|
|
@ -53,6 +53,7 @@ bug_numbers:
|
|||
SAMPLE_SCALAR_INVALID_ADDRESSES = """
|
||||
description: A nice one-line description.
|
||||
expires: never
|
||||
record_in_processes:
|
||||
- 'main'
|
||||
kind: uint
|
||||
notification_emails:
|
||||
|
@ -68,6 +69,71 @@ bug_numbers:
|
|||
|
||||
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__':
|
||||
mozunit.main()
|
||||
|
|
|
@ -2,49 +2,6 @@
|
|||
- 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/. -->
|
||||
|
||||
<!-- 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 -->
|
||||
<!ENTITY cmd.enableAddon.label "Enable">
|
||||
<!ENTITY cmd.enableAddon.accesskey "E">
|
||||
|
|
|
@ -189,3 +189,89 @@ legacy-extensions =
|
|||
|
||||
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>
|
||||
|
||||
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">
|
||||
<richlistitem id="category-discover" value="addons://discover/"
|
||||
class="category"
|
||||
name="&view.discover.label;" priority="1000"
|
||||
tooltiptext="&view.discover.label;"/>
|
||||
data-l10n-id="extensions-view-discover"
|
||||
data-l10n-attrs="name"
|
||||
priority="1000"/>
|
||||
<richlistitem id="category-legacy" value="addons://legacy/"
|
||||
class="category" priority="20000"
|
||||
disabled="true"/>
|
||||
<richlistitem id="category-availableUpdates" value="addons://updates/available"
|
||||
class="category"
|
||||
name="&view.availableUpdates.label;" priority="100000"
|
||||
tooltiptext="&view.availableUpdates.label;"
|
||||
data-l10n-id="extensions-view-available-updates"
|
||||
data-l10n-attrs="name"
|
||||
disabled="true"/>
|
||||
<richlistitem id="category-recentUpdates" value="addons://updates/recent"
|
||||
class="category"
|
||||
name="&view.recentUpdates.label;" priority="101000"
|
||||
tooltiptext="&view.recentUpdates.label;" disabled="true"/>
|
||||
data-l10n-id="extensions-view-recent-updates"
|
||||
data-l10n-attrs="name"
|
||||
priority="101000"
|
||||
disabled="true"/>
|
||||
</richlistbox>
|
||||
|
||||
<spacer flex="1"/>
|
||||
|
@ -225,18 +228,19 @@
|
|||
<hbox id="updates-container" align="center">
|
||||
<image class="spinner"/>
|
||||
<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"
|
||||
hidden="true" label="&updates.manualUpdatesFound.label;"
|
||||
hidden="true"
|
||||
data-l10n-id="extensions-updates-manual-updates-found"
|
||||
command="cmd_goToAvailableUpdates"/>
|
||||
<label id="updates-progress" hidden="true"
|
||||
value="&updates.updating.label;"/>
|
||||
data-l10n-id="extensions-updates-updating"/>
|
||||
<label id="updates-installed" hidden="true"
|
||||
value="&updates.installed.label;"/>
|
||||
data-l10n-id="extensions-updates-installed"/>
|
||||
<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"
|
||||
label="&updates.restart.label;"
|
||||
data-l10n-id="extensions-updates-restart"
|
||||
command="cmd_restartApp"/>
|
||||
</hbox>
|
||||
|
||||
|
@ -244,12 +248,10 @@
|
|||
data-l10n-id="tools-menu">
|
||||
<menupopup id="utils-menu">
|
||||
<menuitem id="utils-updateNow"
|
||||
label="&updates.checkForUpdates.label;"
|
||||
accesskey="&updates.checkForUpdates.accesskey;"
|
||||
data-l10n-id="extensions-updates-check-for-updates"
|
||||
command="cmd_findAllUpdates"/>
|
||||
<menuitem id="utils-viewUpdates"
|
||||
label="&updates.viewUpdates.label;"
|
||||
accesskey="&updates.viewUpdates.accesskey;"
|
||||
data-l10n-id="extensions-updates-view-updates"
|
||||
command="cmd_goToRecentUpdates"/>
|
||||
<menuseparator id="utils-installFromFile-separator"/>
|
||||
<menuitem id="utils-installFromFile"
|
||||
|
@ -260,17 +262,14 @@
|
|||
command="cmd_debugAddons"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="utils-autoUpdateDefault"
|
||||
label="&updates.updateAddonsAutomatically.label;"
|
||||
accesskey="&updates.updateAddonsAutomatically.accesskey;"
|
||||
data-l10n-id="extensions-updates-update-addons-automatically"
|
||||
type="checkbox" autocheck="false"
|
||||
command="cmd_toggleAutoUpdateDefault"/>
|
||||
<menuitem id="utils-resetAddonUpdatesToAutomatic"
|
||||
label="&updates.resetUpdatesToAutomatic.label;"
|
||||
accesskey="&updates.resetUpdatesToAutomatic.accesskey;"
|
||||
data-l10n-id="extensions-updates-reset-updates-to-automatic"
|
||||
command="cmd_resetAddonAutoUpdate"/>
|
||||
<menuitem id="utils-resetAddonUpdatesToManual"
|
||||
label="&updates.resetUpdatesToManual.label;"
|
||||
accesskey="&updates.resetUpdatesToManual.accesskey;"
|
||||
data-l10n-id="extensions-updates-reset-updates-to-manual"
|
||||
command="cmd_resetAddonAutoUpdate"/>
|
||||
</menupopup>
|
||||
</toolbarbutton>
|
||||
|
@ -310,30 +309,28 @@
|
|||
<!-- global warnings -->
|
||||
<hbox class="global-warning" flex="1">
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.safemode.label;"/>
|
||||
data-l10n-id="extensions-warning-safe-mode-label"/>
|
||||
</hbox>
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.checkcompatibility.label;"/>
|
||||
data-l10n-id="extensions-warning-check-compatibility-label"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-checkcompatibility"
|
||||
label="&warning.checkcompatibility.enable.label;"
|
||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
||||
data-l10n-id="extensions-warning-check-compatibility-enable"
|
||||
command="cmd_enableCheckCompatibility"/>
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.updatesecurity.label;"/>
|
||||
data-l10n-id="extensions-warning-update-security-label"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-updatesecurity"
|
||||
label="&warning.updatesecurity.enable.label;"
|
||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
||||
data-l10n-id="extensions-warning-update-security-enable"
|
||||
command="cmd_enableUpdateSecurity"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
|
@ -370,30 +367,28 @@
|
|||
<!-- global warnings -->
|
||||
<hbox class="global-warning" flex="1">
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.safemode.label;"/>
|
||||
data-l10n-id="extensions-warning-safe-mode-label"/>
|
||||
</hbox>
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.checkcompatibility.label;"/>
|
||||
data-l10n-id="extensions-warning-check-compatibility-label"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-checkcompatibility"
|
||||
label="&warning.checkcompatibility.enable.label;"
|
||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
||||
data-l10n-id="extensions-warning-check-compatibility-enable"
|
||||
command="cmd_enableCheckCompatibility"/>
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.updatesecurity.label;"/>
|
||||
data-l10n-id="extensions-warning-update-security-label"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-updatesecurity"
|
||||
label="&warning.updatesecurity.enable.label;"
|
||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
||||
data-l10n-id="extensions-warning-update-security-enable"
|
||||
command="cmd_enableUpdateSecurity"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
|
@ -411,8 +406,7 @@
|
|||
</vbox>
|
||||
<hbox id="update-actions" pack="center">
|
||||
<button id="update-selected-btn" hidden="true"
|
||||
label="&updates.updateSelected.label;"
|
||||
tooltiptext="&updates.updateSelected.tooltip;"/>
|
||||
data-l10n-id="extensions-updates-update-selected"/>
|
||||
</hbox>
|
||||
<richlistbox id="updates-list" class="list" flex="1"/>
|
||||
</vbox>
|
||||
|
@ -423,30 +417,28 @@
|
|||
<!-- global warnings -->
|
||||
<hbox class="global-warning-container global-warning">
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.safemode.label;"/>
|
||||
data-l10n-id="extensions-warning-safe-mode-label"/>
|
||||
</hbox>
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.checkcompatibility.label;"/>
|
||||
data-l10n-id="extensions-warning-check-compatibility-label"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-checkcompatibility"
|
||||
label="&warning.checkcompatibility.enable.label;"
|
||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
||||
data-l10n-id="extensions-warning-check-compatibility-enable"
|
||||
command="cmd_enableCheckCompatibility"/>
|
||||
<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"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.updatesecurity.label;"/>
|
||||
data-l10n-id="extensions-warning-update-security-label"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-updatesecurity"
|
||||
label="&warning.updatesecurity.enable.label;"
|
||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
||||
data-l10n-id="extensions-warning-update-security-label"
|
||||
command="cmd_enableUpdateSecurity"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
|
|
|
@ -86,7 +86,7 @@ getId(const char *bin_name)
|
|||
using namespace google_breakpad;
|
||||
|
||||
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 (nsDependentCString(bin_name).Find("!/") != kNotFound) {
|
||||
|
|
|
@ -166,6 +166,7 @@ protected:
|
|||
, mIsComposing(false)
|
||||
, mIsSynthesizedByTIP(false)
|
||||
, mMaybeSkippableInRemoteProcess(true)
|
||||
, mUseLegacyKeyCodeAndCharCodeValues(false)
|
||||
, mEditCommandsForSingleLineEditorInitialized(false)
|
||||
, mEditCommandsForMultiLineEditorInitialized(false)
|
||||
, mEditCommandsForRichTextEditorInitialized(false)
|
||||
|
@ -195,6 +196,7 @@ public:
|
|||
, mIsComposing(false)
|
||||
, mIsSynthesizedByTIP(false)
|
||||
, mMaybeSkippableInRemoteProcess(true)
|
||||
, mUseLegacyKeyCodeAndCharCodeValues(false)
|
||||
, mEditCommandsForSingleLineEditorInitialized(false)
|
||||
, mEditCommandsForMultiLineEditorInitialized(false)
|
||||
, mEditCommandsForRichTextEditorInitialized(false)
|
||||
|
@ -400,6 +402,10 @@ public:
|
|||
// Don't refer this member directly when you need to check this.
|
||||
// Use CanSkipInRemoteProcess() instead.
|
||||
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
|
||||
{
|
||||
|
@ -681,6 +687,8 @@ public:
|
|||
#endif
|
||||
mIsSynthesizedByTIP = aEvent.mIsSynthesizedByTIP;
|
||||
mMaybeSkippableInRemoteProcess = aEvent.mMaybeSkippableInRemoteProcess;
|
||||
mUseLegacyKeyCodeAndCharCodeValues =
|
||||
aEvent.mUseLegacyKeyCodeAndCharCodeValues;
|
||||
|
||||
// Don't copy mEditCommandsFor*Editor because it may require a lot of
|
||||
// memory space. For example, if the event is dispatched but grabbed by
|
||||
|
|
Загрузка…
Ссылка в новой задаче