Merge mozilla-inbound to mozilla-central. a=merge

This commit is contained in:
Daniel Varga 2019-01-16 06:49:11 +02:00
Родитель 2e0a18921c 994713f905
Коммит 55006aaaec
40 изменённых файлов: 877 добавлений и 341 удалений

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

@ -63,11 +63,18 @@
padding-inline-start: 30px;
}
#prefs > tr.deleted > td.cell-name {
font-weight: bold;
opacity: 0.4;
}
.cell-value {
white-space: pre-wrap;
word-break: break-all;
}
td.cell-value > form > input {
td.cell-value > form > input[type="text"],
td.cell-value > form > input[type="number"] {
-moz-appearance: textfield;
width: 100%;
box-sizing: border-box;

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

@ -35,7 +35,7 @@
</div>
<div class="button-container">
<button class="primary" onclick="alterWarningState(); loadPrefs();"
<button class="primary" onclick="onWarningButtonClick();"
data-l10n-id="about-config-warning-button"></button>
</div>
</div>

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

@ -2,30 +2,64 @@
* 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/. */
ChromeUtils.import("resource://gre/modules/DeferredTask.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Preferences.jsm");
const SEARCH_TIMEOUT_MS = 500;
let gDefaultBranch = Services.prefs.getDefaultBranch("");
let gPrefArray;
let gFilterPrefsTask = new DeferredTask(() => filterPrefs(), SEARCH_TIMEOUT_MS);
/**
* Maps the name of each preference in the back-end to its PrefRow object,
* separating the preferences that actually exist. This is as an optimization to
* avoid querying the preferences service each time the list is filtered.
*/
let gExistingPrefs = new Map();
let gDeletedPrefs = new Map();
/**
* Maps each row element currently in the table to its PrefRow object.
*/
let gElementToPrefMap = new WeakMap();
/**
* Reference to the PrefRow currently being edited, if any.
*/
let gPrefInEdit = null;
/**
* Lowercase substring that should be contained in the preference name.
*/
let gFilterString = null;
class PrefRow {
constructor(name) {
this.name = name;
this.value = true;
this.refreshValue();
this.editing = false;
this.element = document.createElement("tr");
this.element.setAttribute("aria-label", this.name);
this._setupElement();
gElementToPrefMap.set(this.element, this);
}
refreshValue() {
this.hasDefaultValue = prefHasDefaultValue(this.name);
this.hasUserValue = Services.prefs.prefHasUserValue(this.name);
this.hasDefaultValue = this.hasUserValue ? prefHasDefaultValue(this.name)
: true;
this.isLocked = Services.prefs.prefIsLocked(this.name);
// If this preference has been deleted, we keep its last known value.
if (!this.exists) {
gExistingPrefs.delete(this.name);
gDeletedPrefs.set(this.name, this);
return;
}
gExistingPrefs.set(this.name, this);
gDeletedPrefs.delete(this.name);
try {
// This can throw for locked preferences without a default value.
this.value = Preferences.get(this.name);
@ -42,6 +76,18 @@ class PrefRow {
}
}
get type() {
return this.value.constructor.name;
}
get exists() {
return this.hasDefaultValue || this.hasUserValue;
}
get matchesFilter() {
return !gFilterString || this.name.toLowerCase().includes(gFilterString);
}
_setupElement() {
this.element.textContent = "";
let nameCell = document.createElement("td");
@ -73,9 +119,15 @@ class PrefRow {
refreshElement() {
this.element.classList.toggle("has-user-value", !!this.hasUserValue);
this.element.classList.toggle("locked", !!this.isLocked);
if (!this.editing) {
this.valueCell.textContent = this.value;
if (this.value.constructor.name == "Boolean") {
this.element.classList.toggle("deleted", !this.exists);
if (this.exists && !this.editing) {
// We need to place the text inside a "span" element to ensure that the
// text copied to the clipboard includes all whitespace.
let span = document.createElement("span");
span.textContent = this.value;
this.valueCell.textContent = "";
this.valueCell.append(span);
if (this.type == "Boolean") {
document.l10n.setAttributes(this.editButton, "about-config-pref-toggle");
this.editButton.className = "button-toggle";
} else {
@ -91,20 +143,52 @@ class PrefRow {
let form = document.createElement("form");
form.addEventListener("submit", event => event.preventDefault());
form.id = "form-edit";
this.inputField = document.createElement("input");
this.inputField.value = this.value;
if (this.value.constructor.name == "Number") {
this.inputField.type = "number";
this.inputField.required = true;
this.inputField.min = -2147483648;
this.inputField.max = 2147483647;
if (this.editing) {
this.inputField = document.createElement("input");
this.inputField.value = this.value;
if (this.type == "Number") {
this.inputField.type = "number";
this.inputField.required = true;
this.inputField.min = -2147483648;
this.inputField.max = 2147483647;
} else {
this.inputField.type = "text";
}
form.appendChild(this.inputField);
document.l10n.setAttributes(this.editButton, "about-config-pref-save");
this.editButton.className = "primary button-save";
} else {
this.inputField.type = "text";
delete this.inputField;
for (let type of ["Boolean", "Number", "String"]) {
let radio = document.createElement("input");
radio.type = "radio";
radio.name = "type";
radio.value = type;
radio.checked = this.type == type;
form.appendChild(radio);
let radioLabel = document.createElement("span");
radioLabel.textContent = type;
form.appendChild(radioLabel);
}
form.addEventListener("click", event => {
if (event.target.name != "type") {
return;
}
let type = event.target.value;
if (this.type != type) {
if (type == "Boolean") {
this.value = true;
} else if (type == "Number") {
this.value = 0;
} else {
this.value = "";
}
}
});
document.l10n.setAttributes(this.editButton, "about-config-pref-add");
this.editButton.className = "button-add";
}
form.appendChild(this.inputField);
this.valueCell.appendChild(form);
document.l10n.setAttributes(this.editButton, "about-config-pref-save");
this.editButton.className = "primary button-save";
this.editButton.setAttribute("form", "form-edit");
}
this.editButton.disabled = this.isLocked;
@ -139,7 +223,7 @@ class PrefRow {
}
save() {
if (this.value.constructor.name == "Number") {
if (this.type == "Number") {
if (!this.inputField.reportValidity()) {
return;
}
@ -159,19 +243,37 @@ class PrefRow {
}
}
function getPrefName(prefRow) {
return prefRow.getAttribute("aria-label");
let gPrefObserver = {
observe(subject, topic, data) {
let pref = gExistingPrefs.get(data) || gDeletedPrefs.get(data);
if (pref) {
pref.refreshValue();
if (!pref.editing) {
pref.refreshElement();
}
return;
}
let newPref = new PrefRow(data);
if (newPref.matchesFilter) {
document.getElementById("prefs").appendChild(newPref.element);
}
},
};
if (!Preferences.get("browser.aboutConfig.showWarning")) {
// When showing the filtered preferences directly, remove the warning elements
// immediately to prevent flickering, but wait to filter the preferences until
// the value of the textbox has been restored from previous sessions.
document.addEventListener("DOMContentLoaded", loadPrefs, { once: true });
window.addEventListener("load", filterPrefs, { once: true });
}
document.addEventListener("DOMContentLoaded", () => {
if (!Preferences.get("browser.aboutConfig.showWarning")) {
loadPrefs();
}
}, { once: true });
function alterWarningState() {
function onWarningButtonClick() {
Services.prefs.setBoolPref("browser.aboutConfig.showWarning",
document.getElementById("showWarningNextTime").checked);
loadPrefs();
filterPrefs();
}
function loadPrefs() {
@ -187,110 +289,82 @@ function loadPrefs() {
prefs.id = "prefs";
document.body.appendChild(prefs);
gPrefArray = Services.prefs.getChildList("").map(name => new PrefRow(name));
for (let name of Services.prefs.getChildList("")) {
new PrefRow(name);
}
gPrefArray.sort((a, b) => a.name > b.name);
search.addEventListener("keypress", e => {
if (e.key == "Enter") {
filterPrefs();
search.addEventListener("keypress", event => {
switch (event.key) {
case "Escape":
search.value = "";
// Fall through.
case "Enter":
gFilterPrefsTask.disarm();
filterPrefs();
}
});
search.addEventListener("input", () => {
// We call "disarm" to restart the timer at every input.
gFilterPrefsTask.disarm();
gFilterPrefsTask.arm();
});
prefs.addEventListener("click", event => {
if (event.target.localName != "button") {
return;
}
let prefRow = event.target.closest("tr");
let prefName = getPrefName(prefRow);
let pref = gPrefArray.find(p => p.name == prefName);
let pref = gElementToPrefMap.get(event.target.closest("tr"));
let button = event.target.closest("button");
if (button.classList.contains("button-reset")) {
// Reset pref and update gPrefArray.
Services.prefs.clearUserPref(prefName);
pref.refreshValue();
pref.refreshElement();
pref.editButton.focus();
} else if (button.classList.contains("add-true")) {
addNewPref(prefRow.firstChild.innerHTML, true);
} else if (button.classList.contains("add-false")) {
addNewPref(prefRow.firstChild.innerHTML, false);
} else if (button.classList.contains("add-Number") ||
button.classList.contains("add-String")) {
addNewPref(prefRow.firstChild.innerHTML,
button.classList.contains("add-Number") ? 0 : "").edit();
if (button.classList.contains("button-add")) {
Preferences.set(pref.name, pref.value);
if (pref.type != "Boolean") {
pref.edit();
}
} else if (button.classList.contains("button-toggle")) {
// Toggle the pref and update gPrefArray.
Services.prefs.setBoolPref(prefName, !pref.value);
pref.refreshValue();
pref.refreshElement();
Services.prefs.setBoolPref(pref.name, !pref.value);
} else if (button.classList.contains("button-edit")) {
pref.edit();
} else if (button.classList.contains("button-save")) {
pref.save();
} else {
Services.prefs.clearUserPref(prefName);
gPrefArray.splice(gPrefArray.findIndex(p => p.name == prefName), 1);
prefRow.remove();
// This is "button-reset" or "button-delete".
pref.editing = false;
Services.prefs.clearUserPref(pref.name);
pref.editButton.focus();
}
});
filterPrefs();
Services.prefs.addObserver("", gPrefObserver);
window.addEventListener("unload", () => {
Services.prefs.removeObserver("", gPrefObserver);
}, { once: true });
}
function filterPrefs() {
if (gPrefInEdit) {
gPrefInEdit.endEdit();
}
gDeletedPrefs.clear();
let substring = document.getElementById("search").value.trim();
document.getElementById("prefs").textContent = "";
if (substring && !gPrefArray.some(pref => pref.name == substring)) {
document.getElementById("prefs").appendChild(createNewPrefFragment(substring));
let searchName = document.getElementById("search").value.trim();
gFilterString = searchName.toLowerCase();
let prefArray = [...gExistingPrefs.values()];
if (gFilterString) {
prefArray = prefArray.filter(pref => pref.matchesFilter);
}
prefArray.sort((a, b) => a.name > b.name);
if (searchName && !gExistingPrefs.has(searchName)) {
prefArray.push(new PrefRow(searchName));
}
let fragment = createPrefsFragment(gPrefArray.filter(pref => pref.name.includes(substring)));
document.getElementById("prefs").appendChild(fragment);
}
function createPrefsFragment(prefArray) {
let prefsElement = document.getElementById("prefs");
prefsElement.textContent = "";
let fragment = document.createDocumentFragment();
for (let pref of prefArray) {
fragment.appendChild(pref.element);
}
return fragment;
}
function createNewPrefFragment(name) {
let fragment = document.createDocumentFragment();
let row = document.createElement("tr");
row.classList.add("has-user-value");
row.setAttribute("aria-label", name);
let nameCell = document.createElement("td");
nameCell.className = "cell-name";
nameCell.append(name);
row.appendChild(nameCell);
let valueCell = document.createElement("td");
valueCell.classList.add("cell-value");
let guideText = document.createElement("span");
document.l10n.setAttributes(guideText, "about-config-pref-add");
valueCell.appendChild(guideText);
for (let item of ["true", "false", "Number", "String"]) {
let optionBtn = document.createElement("button");
optionBtn.textContent = item;
optionBtn.classList.add("add-" + item);
valueCell.appendChild(optionBtn);
}
row.appendChild(valueCell);
let editCell = document.createElement("td");
row.appendChild(editCell);
let buttonCell = document.createElement("td");
row.appendChild(buttonCell);
fragment.appendChild(row);
return fragment;
prefsElement.appendChild(fragment);
}
function prefHasDefaultValue(name) {
@ -309,12 +383,3 @@ function prefHasDefaultValue(name) {
} catch (ex) {}
return false;
}
function addNewPref(name, value) {
Preferences.set(name, value);
let pref = new PrefRow(name);
gPrefArray.push(pref);
gPrefArray.sort((a, b) => a.name > b.name);
filterPrefs();
return pref;
}

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

@ -13,7 +13,7 @@ about-config-title = about:config
about-config-search =
.placeholder = Search
about-config-pref-add = Add as:
about-config-pref-add = Add
about-config-pref-toggle = Toggle
about-config-pref-edit = Edit
about-config-pref-save = Save

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

@ -3,10 +3,14 @@ support-files =
head.js
[browser_basic.js]
[browser_warning.js]
[browser_clipboard.js]
skip-if = debug # Bug 1507747
subsuite = clipboard
[browser_edit.js]
skip-if = debug # Bug 1507747
[browser_locked.js]
[browser_observe.js]
skip-if = debug # Bug 1507747
[browser_search.js]
skip-if = debug # Bug 1507747
[browser_locked.js]
[browser_warning.js]

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

@ -0,0 +1,92 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["test.aboutconfig.copy.false", false],
["test.aboutconfig.copy.number", 10],
["test.aboutconfig.copy.spaces.1", " "],
["test.aboutconfig.copy.spaces.2", " "],
["test.aboutconfig.copy.spaces.3", " "],
["test.aboutconfig.copy.string", "010.5"],
],
});
});
add_task(async function test_copy() {
await AboutConfigTest.withNewTab(async function() {
for (let [name, expectedString] of [
[PREF_BOOLEAN_DEFAULT_TRUE, "true"],
[PREF_BOOLEAN_USERVALUE_TRUE, "true"],
[PREF_STRING_DEFAULT_EMPTY, ""],
["test.aboutconfig.copy.false", "false"],
["test.aboutconfig.copy.number", "10"],
["test.aboutconfig.copy.spaces.1", " "],
["test.aboutconfig.copy.spaces.2", " "],
["test.aboutconfig.copy.spaces.3", " "],
["test.aboutconfig.copy.string", "010.5"],
]) {
// Limit the number of preferences shown so all the rows are visible.
this.search(name);
let row = this.getRow(name);
// Triple click at any location in the name cell should select the name.
await BrowserTestUtils.synthesizeMouseAtCenter(row.nameCell,
{ clickCount: 3 },
this.browser);
Assert.ok(row.nameCell.contains(this.window.getSelection().anchorNode));
await SimpleTest.promiseClipboardChange(name, async () => {
await BrowserTestUtils.synthesizeKey("c", { accelKey: true },
this.browser);
});
// Triple click at any location in the value cell should select the value.
await BrowserTestUtils.synthesizeMouseAtCenter(row.valueCell,
{ clickCount: 3 },
this.browser);
let selection = this.window.getSelection();
Assert.ok(row.valueCell.contains(selection.anchorNode));
// The selection is never collapsed because of the <span> element, and
// this makes sure that an empty string can be copied.
Assert.ok(!selection.isCollapsed);
await SimpleTest.promiseClipboardChange(expectedString, async () => {
await BrowserTestUtils.synthesizeKey("c", { accelKey: true },
this.browser);
});
}
});
});
add_task(async function test_copy_multiple() {
await AboutConfigTest.withNewTab(async function() {
// Lines are separated by a single LF character on all platforms.
let expectedString = "test.aboutconfig.copy.false\tfalse\t\n" +
"test.aboutconfig.copy.number\t10\t\n" +
"test.aboutconfig.copy.spaces.1\t \t\n" +
"test.aboutconfig.copy.spaces.2\t \t\n" +
"test.aboutconfig.copy.spaces.3\t \t\n" +
"test.aboutconfig.copy.string\t010.5";
this.search("test.aboutconfig.copy.");
let startRow = this.getRow("test.aboutconfig.copy.false");
let endRow = this.getRow("test.aboutconfig.copy.string");
let { width, height } = endRow.valueCell.getBoundingClientRect();
// Drag from the top left of the first row to the bottom right of the last.
await BrowserTestUtils.synthesizeMouse(startRow.nameCell, 1, 1,
{ type: "mousedown" }, this.browser);
await BrowserTestUtils.synthesizeMouse(endRow.valueCell,
width - 1, height - 1,
{ type: "mousemove" }, this.browser);
await BrowserTestUtils.synthesizeMouse(endRow.valueCell,
width - 1, height - 1,
{ type: "mouseup" }, this.browser);
await SimpleTest.promiseClipboardChange(expectedString, async () => {
await BrowserTestUtils.synthesizeKey("c", { accelKey: true },
this.browser);
});
});
});

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

@ -18,36 +18,71 @@ add_task(async function setup() {
});
add_task(async function test_add_user_pref() {
await AboutConfigTest.withNewTab(async function() {
Assert.ok(!Services.prefs.getChildList("").find(pref => pref == "testPref"));
Assert.equal(Services.prefs.getPrefType(PREF_NEW),
Ci.nsIPrefBranch.PREF_INVALID);
for (let [buttonSelector, expectedValue] of [
[".add-true", true],
[".add-false", false],
[".add-Number", 0],
[".add-String", ""],
await AboutConfigTest.withNewTab(async function() {
// The row for a new preference appears when searching for its name.
Assert.ok(!this.getRow(PREF_NEW));
this.search(PREF_NEW);
let row = this.getRow(PREF_NEW);
Assert.ok(row.hasClass("deleted"));
for (let [radioIndex, expectedValue, expectedEditingMode] of [
[0, true, false],
[1, 0, true],
[2, "", true],
]) {
this.search("testPref");
this.document.querySelector("#prefs button" + buttonSelector).click();
Assert.ok(Services.prefs.getChildList("").find(pref => pref == "testPref"));
Assert.ok(Preferences.get("testPref") === expectedValue);
this.document.querySelector("#prefs button[data-l10n-id='about-config-pref-delete']").click();
// Adding the preference should set the default for the data type.
row.element.querySelectorAll("input")[radioIndex].click();
row.editColumnButton.click();
Assert.ok(!row.hasClass("deleted"));
Assert.ok(Preferences.get(PREF_NEW) === expectedValue);
// Number and String preferences should be in edit mode.
Assert.equal(!!row.valueInput, expectedEditingMode);
// Repeat the search to verify that the preference remains.
this.search(PREF_NEW);
row = this.getRow(PREF_NEW);
Assert.ok(!row.hasClass("deleted"));
Assert.ok(!row.valueInput);
// Reset the preference, then continue by adding a different type.
row.resetColumnButton.click();
Assert.equal(Services.prefs.getPrefType(PREF_NEW),
Ci.nsIPrefBranch.PREF_INVALID);
}
});
});
add_task(async function test_delete_user_pref() {
Services.prefs.setBoolPref("userAddedPref", true);
await AboutConfigTest.withNewTab(async function() {
let row = this.getRow("userAddedPref");
row.resetColumnButton.click();
Assert.ok(!this.getRow("userAddedPref"));
Assert.ok(!Services.prefs.getChildList("").includes("userAddedPref"));
for (let [radioIndex, testValue] of [
[0, false],
[1, -1],
[2, "value"],
]) {
Preferences.set(PREF_NEW, testValue);
await AboutConfigTest.withNewTab(async function() {
// Deleting the preference should keep the row.
let row = this.getRow(PREF_NEW);
row.resetColumnButton.click();
Assert.ok(row.hasClass("deleted"));
Assert.equal(Services.prefs.getPrefType(PREF_NEW),
Ci.nsIPrefBranch.PREF_INVALID);
// Search for nothing to test gPrefArray
this.search();
Assert.ok(!this.getRow("userAddedPref"));
});
// Re-adding the preference should keep the same value.
Assert.ok(row.element.querySelectorAll("input")[radioIndex].checked);
row.editColumnButton.click();
Assert.ok(!row.hasClass("deleted"));
Assert.ok(Preferences.get(PREF_NEW) === testValue);
// Searching again after deleting should remove the row.
row.resetColumnButton.click();
this.search();
Assert.ok(!this.getRow(PREF_NEW));
});
}
});
add_task(async function test_reset_user_pref() {
@ -148,11 +183,8 @@ add_task(async function test_modify() {
row.editColumnButton.click();
Assert.equal(row.valueInput.value, Preferences.get(prefName));
row.resetColumnButton.click();
if (willDelete) {
Assert.ok(!this.getRow(prefName));
} else {
Assert.ok(!row.hasClass("has-user-value"));
}
Assert.ok(!row.hasClass("has-user-value"));
Assert.equal(row.hasClass("deleted"), willDelete);
}
});
});

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

@ -0,0 +1,140 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["test.aboutconfig.modify.boolean", true],
["test.aboutconfig.modify.number", 1337],
["test.aboutconfig.modify.string", "the answer to the life the universe and everything"],
],
});
registerCleanupFunction(() => {
Services.prefs.clearUserPref(PREF_BOOLEAN_DEFAULT_TRUE);
Services.prefs.clearUserPref(PREF_NUMBER_DEFAULT_ZERO);
Services.prefs.clearUserPref(PREF_STRING_DEFAULT_EMPTY);
});
});
add_task(async function test_observe_add_user_pref() {
Assert.equal(Services.prefs.getPrefType(PREF_NEW),
Ci.nsIPrefBranch.PREF_INVALID);
await AboutConfigTest.withNewTab(async function() {
for (let value of [false, true, "", "value", 0, -10]) {
// A row should be added when a new preference is added.
Assert.ok(!this.getRow(PREF_NEW));
Preferences.set(PREF_NEW, value);
let row = this.getRow(PREF_NEW);
Assert.equal(row.value, "" + value);
// The row should stay when the preference is removed.
Preferences.reset(PREF_NEW);
Assert.ok(row.hasClass("deleted"));
// Re-adding the preference from the interface should restore its value.
row.editColumnButton.click();
if (value.constructor.name != "Boolean") {
row.editColumnButton.click();
}
Assert.equal(row.value, "" + value);
Assert.ok(Preferences.get(PREF_NEW) === value);
// Searching again after deleting should remove the row.
Preferences.reset(PREF_NEW);
this.search();
Assert.ok(!this.getRow(PREF_NEW));
// Searching for the preference name should give the ability to add it.
Preferences.reset(PREF_NEW);
this.search(PREF_NEW);
row = this.getRow(PREF_NEW);
Assert.ok(row.hasClass("deleted"));
// The row for adding should be reused if the new preference is added.
Preferences.set(PREF_NEW, value);
Assert.equal(row.value, "" + value);
// If a new preference does not match the filter it is not displayed.
Preferences.reset(PREF_NEW);
this.search(PREF_NEW + ".extra");
Assert.ok(!this.getRow(PREF_NEW));
Preferences.set(PREF_NEW, value);
Assert.ok(!this.getRow(PREF_NEW));
// Resetting the filter should display the new preference.
this.search("");
Assert.equal(this.getRow(PREF_NEW).value, "" + value);
// Reset the preference, then continue by adding a different value.
Preferences.reset(PREF_NEW);
this.search("");
}
});
});
add_task(async function test_observe_delete_user_pref() {
for (let value of [true, "value", -10]) {
Preferences.set(PREF_NEW, value);
await AboutConfigTest.withNewTab(async function() {
// Deleting the preference should keep the row.
let row = this.getRow(PREF_NEW);
Preferences.reset(PREF_NEW);
Assert.ok(row.hasClass("deleted"));
// Searching again should remove the row.
this.search();
Assert.ok(!this.getRow(PREF_NEW));
});
}
});
add_task(async function test_observe_reset_user_pref() {
await SpecialPowers.pushPrefEnv({
"set": [
[PREF_BOOLEAN_DEFAULT_TRUE, false],
],
});
await AboutConfigTest.withNewTab(async function() {
let row = this.getRow(PREF_BOOLEAN_DEFAULT_TRUE);
Preferences.reset(PREF_BOOLEAN_DEFAULT_TRUE);
Assert.ok(!row.hasClass("has-user-value"));
Assert.equal(row.value, "true");
});
});
add_task(async function test_observe_modify() {
await AboutConfigTest.withNewTab(async function() {
for (let [name, value] of [
["test.aboutconfig.modify.boolean", false],
["test.aboutconfig.modify.number", -10],
["test.aboutconfig.modify.string", "value"],
[PREF_BOOLEAN_DEFAULT_TRUE, false],
[PREF_NUMBER_DEFAULT_ZERO, 1],
[PREF_STRING_DEFAULT_EMPTY, "string"],
]) {
let row = this.getRow(name);
Assert.notEqual(row.value, "" + value);
Preferences.set(name, value);
Assert.equal(row.value, "" + value);
if (value.constructor.name == "Boolean") {
continue;
}
// Changing the value or removing while editing should not take effect.
row.editColumnButton.click();
row.valueInput.value = "42";
Preferences.reset(name);
Assert.equal(row.element, this.getRow(name).element);
Assert.equal(row.valueInput.value, "42");
// Saving should store the value even if the preference was modified.
row.editColumnButton.click();
Assert.equal(row.value, "42");
Assert.equal(Preferences.get(name), "42");
}
});
});

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

@ -25,7 +25,7 @@ add_task(async function test_search() {
// Filter a subset of preferences. The "browser.download." branch is
// chosen because it is very unlikely that its preferences would be
// modified by other code during the execution of this test.
this.search("wser.down ");
this.search("Wser.down ");
let filteredPrefArray =
prefArray.filter(pref => pref.includes("wser.down"));
@ -49,8 +49,41 @@ add_task(async function test_search() {
this.search("aJunkValueasdf");
Assert.equal(this.rows.length, 1);
// Test added preferences search returns 2 preferences.
// Two preferences match this filter, and one of those matches exactly.
this.search("test.aboutconfig.a");
Assert.equal(this.rows.length, 2);
// When searching case insensitively, there is an additional row to add a
// new preference with the same name but a different case.
this.search("TEST.aboutconfig.a");
Assert.equal(this.rows.length, 3);
});
});
add_task(async function test_search_delayed() {
await AboutConfigTest.withNewTab(async function() {
let prefs = this.document.getElementById("prefs");
// Prepare the table and the search field for the test.
this.search("test.aboutconfig.a");
Assert.equal(this.rows.length, 2);
// The table is updated in a single microtask, so we don't need to wait for
// specific mutations, we can just continue when the children are updated.
let prefsTableChanged = new Promise(resolve => {
let observer = new MutationObserver(() => {
observer.disconnect();
resolve();
});
observer.observe(prefs, { childList: true });
});
// Add a character and test that the table is not updated immediately.
EventUtils.synthesizeKey("b");
Assert.equal(this.rows.length, 2);
// The table will eventually be updated after a delay.
await prefsTableChanged;
Assert.equal(this.rows.length, 1);
});
});

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

@ -17,6 +17,9 @@ const PREF_STRING_DEFAULT_NOTEMPTY = "accessibility.typeaheadfind.soundURL";
const PREF_STRING_DEFAULT_NOTEMPTY_VALUE = "beep";
const PREF_STRING_LOCALIZED_MISSING = "gecko.handlerService.schemes.irc.1.name";
// Other preference names used in tests.
const PREF_NEW = "test.aboutconfig.new";
class AboutConfigRowTest {
constructor(element) {
this.element = element;
@ -26,24 +29,32 @@ class AboutConfigRowTest {
return this.element.querySelector(selector);
}
get nameCell() {
return this.querySelector("td");
}
get name() {
return this.querySelector("td").textContent;
return this.nameCell.textContent;
}
get valueCell() {
return this.querySelector("td.cell-value");
}
get value() {
return this.querySelector("td.cell-value").textContent;
return this.valueCell.textContent;
}
/**
* Text input field when the row is in edit mode.
*/
get valueInput() {
return this.querySelector("td.cell-value input");
return this.valueCell.querySelector("input");
}
/**
* This is normally "edit" or "toggle" based on the preference type, or "save"
* when the row is in edit mode.
* This is normally "edit" or "toggle" based on the preference type, "save"
* when the row is in edit mode, or "add" when the preference does not exist.
*/
get editColumnButton() {
return this.querySelector("td.cell-edit > button");
@ -74,7 +85,9 @@ class AboutConfigTest {
}
constructor(browser) {
this.browser = browser;
this.document = browser.contentDocument;
this.window = browser.contentWindow;
}
async setupNewTab(options) {

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

@ -54,7 +54,7 @@ skip-if = true || !healthreport # Bug 1185403 for the "true"
[browser_homepages_filter_aboutpreferences.js]
[browser_homepages_use_bookmark.js]
[browser_extension_controlled.js]
skipif = ccov && os == 'win' # bug 1437051
skip-if = ccov && os == 'win' # bug 1437051
[browser_languages_subdialog.js]
[browser_browser_languages_subdialog.js]
[browser_layersacceleration.js]

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

@ -25,6 +25,14 @@
using namespace mozilla;
namespace mozilla {
LazyLogModule gPageLoadLog("PageLoad");
#define PAGELOAD_LOG(args) MOZ_LOG(gPageLoadLog, LogLevel::Debug, args)
#define PAGELOAD_LOG_ENABLED() MOZ_LOG_TEST(gPageLoadLog, LogLevel::Error)
} // namespace mozilla
nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell) {
Clear();
@ -140,6 +148,25 @@ void nsDOMNavigationTiming::NotifyLoadEventEnd() {
mDocShell);
if (IsTopLevelContentDocumentInContentProcess()) {
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active() || PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mLoadEventEnd - mNavigationStart;
TimeDuration duration = mLoadEventEnd - mLoadEventStart;
nsAutoCString spec;
if (mLoadedURI) {
mLoadedURI->GetSpec(spec);
}
nsPrintfCString marker(
"Document %s loaded after %dms, load event duration %dms", spec.get(),
int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds()));
DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
PAGELOAD_LOG(("%s", marker.get()));
profiler_add_marker(
"DocumentLoad",
MakeUnique<TextMarkerPayload>(marker, mNavigationStart, mLoadEventEnd,
docShellId, docShellHistoryId));
}
#endif
Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_END_MS,
mNavigationStart);
}
@ -240,36 +267,6 @@ void nsDOMNavigationTiming::TTITimeoutCallback(nsITimer* aTimer,
self->TTITimeout(aTimer);
}
// Return the max of aT1 and aT2, or the lower of the two if there's more
// than Nms (the window size) between them. In other words, the window
// starts at the lower of aT1 and aT2, and we only want to respect
// timestamps within the window (and pick the max of those).
//
// This approach handles the edge case of a late wakeup: where there was
// more than Nms after one (of aT1 or aT2) without the other, but the other
// happened after Nms and before we woke up. For example, if aT1 was 10
// seconds after aT2, but we woke up late (after aT1) we don't want to
// return aT1 if the window is 5 seconds.
static const TimeStamp& MaxWithinWindowBeginningAtMin(
const TimeStamp& aT1, const TimeStamp& aT2,
const TimeDuration& aWindowSize) {
if (aT2.IsNull()) {
return aT1;
} else if (aT1.IsNull()) {
return aT2;
}
if (aT1 > aT2) {
if ((aT1 - aT2) > aWindowSize) {
return aT2;
}
return aT1;
}
if ((aT2 - aT1) > aWindowSize) {
return aT1;
}
return aT2;
}
#define TTI_WINDOW_SIZE_MS (5 * 1000)
void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) {
@ -281,21 +278,36 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) {
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
TimeStamp lastLongTaskEnded;
mainThread->GetLastLongNonIdleTaskEnd(&lastLongTaskEnded);
if (!lastLongTaskEnded.IsNull()) {
TimeDuration delta = now - lastLongTaskEnded;
if (delta.ToMilliseconds() < TTI_WINDOW_SIZE_MS) {
// Less than 5 seconds since the last long task. Schedule another check
aTimer->InitWithNamedFuncCallback(TTITimeoutCallback, this,
TTI_WINDOW_SIZE_MS,
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
"nsDOMNavigationTiming::TTITimeout");
return;
}
// Window starts at mContentfulPaint; any long task before that is ignored
if (lastLongTaskEnded.IsNull() || lastLongTaskEnded < mContentfulPaint) {
PAGELOAD_LOG(
("no longtask (last was %g ms before ContentfulPaint)",
lastLongTaskEnded.IsNull()
? 0
: (mContentfulPaint - lastLongTaskEnded).ToMilliseconds()));
lastLongTaskEnded = mContentfulPaint;
}
TimeDuration delta = now - lastLongTaskEnded;
PAGELOAD_LOG(("TTI delta: %g ms", delta.ToMilliseconds()));
if (delta.ToMilliseconds() < TTI_WINDOW_SIZE_MS) {
// Less than 5 seconds since the last long task or start of the window.
// Schedule another check.
PAGELOAD_LOG(("TTI: waiting additional %g ms",
(TTI_WINDOW_SIZE_MS + 100) - delta.ToMilliseconds()));
aTimer->InitWithNamedFuncCallback(
TTITimeoutCallback, this,
(TTI_WINDOW_SIZE_MS + 100) -
delta.ToMilliseconds(), // slightly after the window ends
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
"nsDOMNavigationTiming::TTITimeout");
return;
}
// To correctly implement TTI/TTFI as proposed, we'd need to not
// fire it until there are no more than 2 network loads. By the
// proposed definition, without that we're closer to
// TimeToFirstInteractive.
// TimeToFirstInteractive. There are also arguments about what sort
// of loads should qualify.
// XXX check number of network loads, and if > 2 mark to check if loads
// decreases to 2 (or record that point and let the normal timer here
@ -303,15 +315,25 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) {
// TTI has occurred! TTI is either FCP (if there are no longtasks and no
// DCLEnd in the window that starts at FCP), or at the end of the last
// Long Task or DOMContentLoadedEnd (whichever is later).
// Long Task or DOMContentLoadedEnd (whichever is later). lastLongTaskEnded
// is >= FCP here.
if (mTTFI.IsNull()) {
mTTFI = MaxWithinWindowBeginningAtMin(
lastLongTaskEnded, mDOMContentLoadedEventEnd,
TimeDuration::FromMilliseconds(TTI_WINDOW_SIZE_MS));
if (mTTFI.IsNull()) {
mTTFI = mContentfulPaint;
}
// lastLongTaskEnded is >= mContentfulPaint
mTTFI = (mDOMContentLoadedEventEnd.IsNull() ||
lastLongTaskEnded > mDOMContentLoadedEventEnd)
? lastLongTaskEnded
: mDOMContentLoadedEventEnd;
PAGELOAD_LOG(
("TTFI after %dms (LongTask was at %dms, DCL was %dms)",
int((mTTFI - mNavigationStart).ToMilliseconds()),
lastLongTaskEnded.IsNull()
? 0
: int((lastLongTaskEnded - mNavigationStart).ToMilliseconds()),
mDOMContentLoadedEventEnd.IsNull()
? 0
: int((mDOMContentLoadedEventEnd - mNavigationStart)
.ToMilliseconds())));
}
// XXX Implement TTI via check number of network loads, and if > 2 mark
// to check if loads decreases to 2 (or record that point and let the
@ -320,22 +342,23 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) {
mTTITimer = nullptr;
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active()) {
if (profiler_is_active() || PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mTTFI - mNavigationStart;
MOZ_ASSERT(elapsed.ToMilliseconds() > 0);
TimeDuration elapsedLongTask =
lastLongTaskEnded.IsNull() ? 0 : lastLongTaskEnded - mNavigationStart;
nsAutoCString spec;
if (mLoadedURI) {
mLoadedURI->GetSpec(spec);
}
nsPrintfCString marker("TTFI after %dms (LongTask after %dms) for URL %s",
nsPrintfCString marker("TTFI after %dms (LongTask was at %dms) for URL %s",
int(elapsed.ToMilliseconds()),
int(elapsedLongTask.ToMilliseconds()), spec.get());
DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
profiler_add_marker("TTI", MakeUnique<UserTimingMarkerPayload>(
NS_ConvertASCIItoUTF16(marker), mTTFI,
docShellId, docShellHistoryId));
profiler_add_marker(
"TTFI", MakeUnique<TextMarkerPayload>(marker, mNavigationStart, mTTFI,
docShellId, docShellHistoryId));
}
#endif
return;
@ -352,7 +375,7 @@ void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() {
mNonBlankPaint = TimeStamp::Now();
#ifdef MOZ_GECKO_PROFILER
if (profiler_thread_is_being_profiled()) {
if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mNonBlankPaint - mNavigationStart;
nsAutoCString spec;
if (mLoadedURI) {
@ -365,7 +388,12 @@ void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() {
? "foreground tab"
: "this tab was inactive some of the time between navigation start "
"and first non-blank paint");
profiler_add_marker(marker.get());
PAGELOAD_LOG(("%s", marker.get()));
DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
profiler_add_marker(
"FirstNonBlankPaint",
MakeUnique<TextMarkerPayload>(marker, mNavigationStart, mNonBlankPaint,
docShellId, docShellHistoryId));
}
#endif
@ -396,7 +424,7 @@ void nsDOMNavigationTiming::NotifyContentfulPaintForRootContentDocument() {
mContentfulPaint = TimeStamp::Now();
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active()) {
if (profiler_is_active() || PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mContentfulPaint - mNavigationStart;
nsAutoCString spec;
if (mLoadedURI) {
@ -409,7 +437,12 @@ void nsDOMNavigationTiming::NotifyContentfulPaintForRootContentDocument() {
? "foreground tab"
: "this tab was inactive some of the time between navigation start "
"and first non-blank paint");
profiler_add_marker(marker.get());
DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
PAGELOAD_LOG(("%s", marker.get()));
profiler_add_marker("FirstContentfulPaint",
MakeUnique<TextMarkerPayload>(
marker, mNavigationStart, mContentfulPaint,
docShellId, docShellHistoryId));
}
#endif
@ -436,7 +469,7 @@ void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() {
mDOMContentFlushed = TimeStamp::Now();
#ifdef MOZ_GECKO_PROFILER
if (profiler_thread_is_being_profiled()) {
if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mDOMContentFlushed - mNavigationStart;
nsAutoCString spec;
if (mLoadedURI) {
@ -449,7 +482,12 @@ void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() {
? "foreground tab"
: "this tab was inactive some of the time between navigation start "
"and DOMContentFlushed");
profiler_add_marker(marker.get());
DECLARE_DOCSHELL_AND_HISTORY_ID(mDocShell);
PAGELOAD_LOG(("%s", marker.get()));
profiler_add_marker("DOMContentFlushed",
MakeUnique<TextMarkerPayload>(
marker, mNavigationStart, mDOMContentFlushed,
docShellId, docShellHistoryId));
}
#endif
}

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

@ -55,10 +55,6 @@ class MOZ_RAII StackingContextHelper {
// Export the inherited scale
gfx::Size GetInheritedScale() const { return mScale; }
const gfx::Matrix& GetInheritedTransform() const {
return mInheritedTransform;
}
const gfx::Matrix& GetSnappingSurfaceTransform() const {
return mSnappingSurfaceTransform;
}

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

@ -262,7 +262,7 @@ ImgDrawResult ImageResource::GetImageContainerImpl(
return drawResult;
}
void ImageResource::UpdateImageContainer(const Maybe<IntRect>& aDirtyRect) {
bool ImageResource::UpdateImageContainer(const Maybe<IntRect>& aDirtyRect) {
MOZ_ASSERT(NS_IsMainThread());
for (int i = mImageContainers.Length() - 1; i >= 0; --i) {
@ -289,6 +289,8 @@ void ImageResource::UpdateImageContainer(const Maybe<IntRect>& aDirtyRect) {
mImageContainers.RemoveElementAt(i);
}
}
return !mImageContainers.IsEmpty();
}
void ImageResource::ReleaseImageContainer() {

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

@ -368,7 +368,12 @@ class ImageResource : public Image {
uint32_t aFlags,
layers::ImageContainer** aContainer);
void UpdateImageContainer(const Maybe<gfx::IntRect>& aDirtyRect);
/**
* Re-requests the appropriate frames for each image container using
* GetFrameInternal.
* @returns True if any image containers were updated, else false.
*/
bool UpdateImageContainer(const Maybe<gfx::IntRect>& aDirtyRect);
void ReleaseImageContainer();

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

@ -542,7 +542,6 @@ VectorImage::RequestRefresh(const TimeStamp& aTime) {
mSVGDocumentWrapper->TickRefreshDriver();
if (mHasPendingInvalidation) {
mHasPendingInvalidation = false;
SendInvalidationNotifications();
}
}
@ -555,21 +554,30 @@ void VectorImage::SendInvalidationNotifications() {
// notifications there to ensure that there is actually a document observing
// us. Otherwise, the notifications are just wasted effort.
//
// Non-animated images call this method directly from
// Non-animated images post an event to call this method from
// InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never
// called for them. Ordinarily this isn't needed, since we send out
// invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
// SVG document may not be 100% ready to render at that time. In those cases
// we would miss the subsequent invalidations if we didn't send out the
// notifications directly in |InvalidateObservers...|.
// notifications indirectly in |InvalidateObservers...|.
MOZ_ASSERT(mHasPendingInvalidation);
mHasPendingInvalidation = false;
SurfaceCache::RemoveImage(ImageKey(this));
if (UpdateImageContainer(Nothing())) {
// If we have image containers, that means we probably won't get a Draw call
// from the owner since they are using the container. We must assume all
// invalidations need to be handled.
MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
mRenderingObserver->ResumeHonoringInvalidations();
}
if (mProgressTracker) {
SurfaceCache::RemoveImage(ImageKey(this));
mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
GetMaxSizedIntRect());
}
UpdateImageContainer(Nothing());
}
NS_IMETHODIMP_(IntRect)
@ -1465,11 +1473,37 @@ VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
// Invalidation helper method
void VectorImage::InvalidateObserversOnNextRefreshDriverTick() {
if (mHaveAnimations) {
mHasPendingInvalidation = true;
} else {
SendInvalidationNotifications();
if (mHasPendingInvalidation) {
return;
}
mHasPendingInvalidation = true;
// Animated images can wait for the refresh tick.
if (mHaveAnimations) {
return;
}
// Non-animated images won't get the refresh tick, so we should just send an
// invalidation outside the current execution context. We need to defer
// because the layout tree is in the middle of invalidation, and the tree
// state needs to be consistent. Specifically only some of the frames have
// had the NS_FRAME_DESCENDANT_NEEDS_PAINT and/or NS_FRAME_NEEDS_PAINT bits
// set by InvalidateFrameInternal in layout/generic/nsFrame.cpp. These bits
// get cleared when we repaint the SVG into a surface by
// nsIFrame::ClearInvalidationStateBits in nsDisplayList::PaintRoot.
nsCOMPtr<nsIEventTarget> eventTarget;
if (mProgressTracker) {
eventTarget = mProgressTracker->GetEventTarget();
} else {
eventTarget = do_GetMainThread();
}
RefPtr<VectorImage> self(this);
nsCOMPtr<nsIRunnable> ev(NS_NewRunnableFunction(
"VectorImage::SendInvalidationNotifications",
[=]() -> void { self->SendInvalidationNotifications(); }));
eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
}
void VectorImage::PropagateUseCounters(Document* aParentDocument) {

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

@ -4796,6 +4796,10 @@ AbortReasonOr<Ok> IonBuilder::inlineCalls(CallInfo& callInfo,
inlineInfo.popCallStack(inlineBlock);
inlineInfo.setFun(funcDef);
if (callInfo.constructing() && callInfo.getNewTarget() == callInfo.fun()) {
inlineInfo.setNewTarget(funcDef);
}
if (maybeCache) {
// Assign the 'this' value a TypeSet specialized to the groups that
// can generate this inlining target.

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

@ -1456,7 +1456,7 @@ void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) {
} else {
masm.Tst(toWRegister(baab->left()), toWRegister(baab->right()));
}
emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse());
emitBranch(baab->cond(), baab->ifTrue(), baab->ifFalse());
}
void CodeGenerator::visitWasmUint32ToDouble(LWasmUint32ToDouble* lir) {

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

@ -66,8 +66,8 @@ class WptreportHandler(object):
"time": start_time,
})
for result in result["subtests"]:
self.formatter.test_status(result)
for subtest in result["subtests"]:
self.formatter.test_status(subtest)
self.formatter.test_end({
"test": testname,

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

@ -413,7 +413,6 @@ void CompilerEnvironment::computeParameters(Decoder& d,
gcFeatureOptIn == HasGcTypes::True;
bool argBaselineEnabled = args_->baselineEnabled || gcEnabled;
bool argIonEnabled = args_->ionEnabled && !gcEnabled;
bool argTestTiering = args_->testTiering && !gcEnabled;
bool argDebugEnabled = args_->debugEnabled;
uint32_t codeSectionSize = 0;
@ -424,11 +423,9 @@ void CompilerEnvironment::computeParameters(Decoder& d,
}
// Attempt to default to ion if baseline is disabled.
bool baselineEnabled =
BaselineCanCompile() && (argBaselineEnabled || argTestTiering);
bool baselineEnabled = BaselineCanCompile() && argBaselineEnabled;
bool debugEnabled = BaselineCanCompile() && argDebugEnabled;
bool ionEnabled =
IonCanCompile() && (argIonEnabled || !baselineEnabled || argTestTiering);
bool ionEnabled = IonCanCompile() && (argIonEnabled || !baselineEnabled);
#ifdef ENABLE_WASM_CRANELIFT
bool forceCranelift = args_->forceCranelift;
#endif
@ -437,7 +434,7 @@ void CompilerEnvironment::computeParameters(Decoder& d,
MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled);
if (baselineEnabled && ionEnabled && !debugEnabled && CanUseExtraThreads() &&
(TieringBeneficial(codeSectionSize) || argTestTiering)) {
(TieringBeneficial(codeSectionSize) || args_->testTiering)) {
mode_ = CompileMode::Tier1;
tier_ = Tier::Baseline;
} else {

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

@ -6361,8 +6361,6 @@ static SnappedImageDrawingParameters ComputeSnappedImageDrawingParameters(
// Snap even if we have a scale in the context. But don't snap if
// we have something that's not translation+scale, or if the scale flips in
// the X or Y direction, because snapped image drawing can't handle that yet.
// Any changes to this algorithm will need to be reflected in
// ComputeImageContainerDrawingParameters.
if (!currentMatrix.HasNonAxisAlignedTransform() && currentMatrix._11 > 0.0 &&
currentMatrix._22 > 0.0 && aCtx->UserToDevicePixelSnapped(fill, true) &&
aCtx->UserToDevicePixelSnapped(dest, true)) {
@ -6752,46 +6750,17 @@ static ImgDrawResult DrawImageInternal(
}
}
// Attempt to snap pixels, the same as ComputeSnappedImageDrawingParameters.
// Any changes to the algorithm here will need to be reflected there.
bool snapped = false;
gfxSize gfxLayerSize;
const gfx::Matrix& itm = aSc.GetInheritedTransform();
if (!itm.HasNonAxisAlignedTransform() && itm._11 > 0.0 && itm._22 > 0.0) {
gfxRect rect(gfxPoint(aDestRect.X(), aDestRect.Y()),
gfxSize(aDestRect.Width(), aDestRect.Height()));
// Compute our size in layer pixels. We may need to revisit this for Android
// because mobile websites are rarely displayed at a 1:1
// LayoutPixel:ScreenPixel ratio and the snapping here may be insufficient.
const LayerIntSize layerSize =
RoundedToInt(LayerSize(aDestRect.Width() * scaleFactors.width,
aDestRect.Height() * scaleFactors.height));
gfxPoint p1 = ThebesPoint(itm.TransformPoint(ToPoint(rect.TopLeft())));
gfxPoint p2 = ThebesPoint(itm.TransformPoint(ToPoint(rect.TopRight())));
gfxPoint p3 = ThebesPoint(itm.TransformPoint(ToPoint(rect.BottomRight())));
if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
p1.Round();
p3.Round();
rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
std::max(p1.y, p3.y) - rect.Y()));
// An empty size is unacceptable so we ensure our suggested size is at
// least 1 pixel wide/tall.
gfxLayerSize =
gfxSize(std::max(rect.Width(), 1.0), std::max(rect.Height(), 1.0));
snapped = true;
}
}
if (!snapped) {
// Compute our size in layer pixels.
const LayerIntSize layerSize =
RoundedToInt(LayerSize(aDestRect.Width() * scaleFactors.width,
aDestRect.Height() * scaleFactors.height));
// An empty size is unacceptable so we ensure our suggested size is at least
// 1 pixel wide/tall.
gfxLayerSize =
gfxSize(std::max(layerSize.width, 1), std::max(layerSize.height, 1));
}
// An empty size is unacceptable so we ensure our suggested size is at least
// 1 pixel wide/tall.
gfxSize gfxLayerSize =
gfxSize(std::max(layerSize.width, 1), std::max(layerSize.height, 1));
return aImage->OptimalImageSizeForDest(
gfxLayerSize, imgIContainer::FRAME_CURRENT, samplingFilter, aFlags);

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

@ -102,6 +102,14 @@ static nsRect FindScrollAnchoringBoundingRect(const nsIFrame* aScrollFrame,
}
nsRect localRect = aCandidate->GetScrollableOverflowRectRelativeToSelf();
// XXX this isn't correct with non-vertical-tb writing-mode, see bug 1520344
if (localRect.X() < 0) {
localRect.SetBoxX(0, localRect.XMost());
}
if (localRect.Y() < 0) {
localRect.SetBoxY(0, localRect.YMost());
}
nsRect transformed = nsLayoutUtils::TransformFrameRectToAncestor(
aCandidate, localRect, aScrollFrame);
return transformed;

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

@ -432,6 +432,8 @@ ImgDrawResult BulletRenderer::CreateWebRenderCommandsForImage(
aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
LayoutDeviceRect destRect =
LayoutDeviceRect::FromAppUnits(mDest, appUnitsPerDevPixel);
destRect.Round();
Maybe<SVGImageContext> svgContext;
gfx::IntSize decodeSize =
nsLayoutUtils::ComputeImageContainerDrawingParameters(
@ -454,7 +456,7 @@ ImgDrawResult BulletRenderer::CreateWebRenderCommandsForImage(
return drawResult;
}
wr::LayoutRect dest = wr::ToRoundedLayoutRect(destRect);
wr::LayoutRect dest = wr::ToLayoutRect(destRect);
aBuilder.PushImage(dest, dest, !aItem->BackfaceIsHidden(), rendering,
key.value());

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

@ -1618,8 +1618,10 @@ ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer(
size);
const int32_t factor = PresContext()->AppUnitsPerDevPixel();
const LayoutDeviceRect destRect(
LayoutDeviceRect destRect(
LayoutDeviceRect::FromAppUnits(dest, factor));
destRect.Round();
Maybe<SVGImageContext> svgContext;
IntSize decodeSize =
nsLayoutUtils::ComputeImageContainerDrawingParameters(
@ -1896,8 +1898,10 @@ bool nsDisplayImage::CreateWebRenderCommands(
}
const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
const LayoutDeviceRect destRect(
LayoutDeviceRect destRect(
LayoutDeviceRect::FromAppUnits(GetDestRect(), factor));
destRect.Round();
Maybe<SVGImageContext> svgContext;
IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters(
mImage, mFrame, destRect, aSc, flags, svgContext);

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

@ -3553,7 +3553,8 @@ ImgDrawResult nsCSSBorderImageRenderer::CreateWebRenderCommands(
LayoutDeviceRect destRect =
LayoutDeviceRect::FromAppUnits(mArea, appUnitsPerDevPixel);
wr::LayoutRect dest = wr::ToRoundedLayoutRect(destRect);
destRect.Round();
wr::LayoutRect dest = wr::ToLayoutRect(destRect);
wr::LayoutRect clip = dest;
if (!mClip.IsEmpty()) {

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

@ -567,6 +567,9 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
mForFrame->PresContext()->AppUnitsPerDevPixel();
LayoutDeviceRect destRect =
LayoutDeviceRect::FromAppUnits(aDest, appUnitsPerDevPixel);
auto stretchSize = wr::ToLayoutSize(destRect.Size());
destRect.Round();
gfx::IntSize decodeSize =
nsLayoutUtils::ComputeImageContainerDrawingParameters(
mImageContainer, mForFrame, destRect, aSc, containerFlags,
@ -600,8 +603,7 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
appUnitsPerDevPixel);
wr::LayoutRect fill = wr::ToRoundedLayoutRect(fillRect);
wr::LayoutRect roundedDest = wr::ToRoundedLayoutRect(destRect);
auto stretchSize = wr::ToLayoutSize(destRect.Size());
wr::LayoutRect roundedDest = wr::ToLayoutRect(destRect);
// WebRender special cases situations where stretchSize == fillSize to
// infer that it shouldn't use repeat sampling. This makes sure

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

@ -398,6 +398,8 @@ ImgDrawResult nsImageBoxFrame::CreateWebRenderCommands(
const int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
LayoutDeviceRect fillRect =
LayoutDeviceRect::FromAppUnits(dest, appUnitsPerDevPixel);
fillRect.Round();
Maybe<SVGImageContext> svgContext;
gfx::IntSize decodeSize =
nsLayoutUtils::ComputeImageContainerDrawingParameters(
@ -420,7 +422,7 @@ ImgDrawResult nsImageBoxFrame::CreateWebRenderCommands(
if (key.isNothing()) {
return result;
}
wr::LayoutRect fill = wr::ToRoundedLayoutRect(fillRect);
wr::LayoutRect fill = wr::ToLayoutRect(fillRect);
LayoutDeviceSize gapSize(0, 0);
aBuilder.PushImage(fill, fill, !BackfaceIsHidden(),

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

@ -1,7 +1,7 @@
fails-if(Android) == textbox-multiline-noresize.xul textbox-multiline-ref.xul # reference is blank on Android (due to no native theme support?)
!= textbox-multiline-resize.xul textbox-multiline-ref.xul
== popup-explicit-size.xul popup-explicit-size-ref.xul
random-if(Android) fuzzy-if(webrender,128-128,168-168) == image-size.xul image-size-ref.xul
random-if(Android) == image-size.xul image-size-ref.xul
== image-scaling-min-height-1.xul image-scaling-min-height-1-ref.xul
== textbox-text-transform.xul textbox-text-transform-ref.xul

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

@ -3338,7 +3338,7 @@ pref("dom.ipc.plugins.asyncdrawing.enabled", true);
pref("dom.ipc.plugins.forcedirect.enabled", true);
// Enable multi by default.
#if defined(NIGHTLY_BUILD) && !defined(MOZ_ASAN)
#if !defined(MOZ_ASAN)
pref("dom.ipc.processCount", 8);
#else
pref("dom.ipc.processCount", 4);

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

@ -24,14 +24,15 @@ XCODE_LEGACY = ('https://developer.apple.com/downloads/download.action?path=Deve
'xcode_3.2.6_and_ios_sdk_4.3__final/xcode_3.2.6_and_ios_sdk_4.3.dmg')
MACPORTS_URL = {
'13': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.13-HighSierra.pkg',
'12': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.12-Sierra.pkg',
'11': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.11-ElCapitan.pkg',
'10': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.10-Yosemite.pkg',
'9': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.9-Mavericks.pkg',
'8': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.8-MountainLion.pkg',
'7': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.7-Lion.pkg',
'6': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.3-10.6-SnowLeopard.pkg', }
'14': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.14-Mojave.pkg',
'13': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.13-HighSierra.pkg',
'12': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.12-Sierra.pkg',
'11': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.11-ElCapitan.pkg',
'10': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.10-Yosemite.pkg',
'9': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.9-Mavericks.pkg',
'8': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.8-MountainLion.pkg',
'7': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.7-Lion.pkg',
'6': 'https://distfiles.macports.org/MacPorts/MacPorts-2.5.4-10.6-SnowLeopard.pkg', }
RE_CLANG_VERSION = re.compile('Apple (?:clang|LLVM) version (\d+\.\d+)')

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

@ -10,12 +10,13 @@ import concurrent.futures as futures
import logging
import os
from taskgraph.util.taskcluster import list_task_group, cancel_task
from taskgraph.util.taskcluster import (
list_task_group_incomplete_tasks,
cancel_task,
CONCURRENCY,
)
from .registry import register_callback_action
# the maximum number of parallel cancelTask calls to make
CONCURRENCY = 50
logger = logging.getLogger(__name__)
@ -33,11 +34,14 @@ logger = logging.getLogger(__name__)
context=[]
)
def cancel_all_action(parameters, graph_config, input, task_group_id, task_id, task):
def do_cancel_task(task_id):
logger.info('Cancelling task {}'.format(task_id))
cancel_task(task_id, use_proxy=True)
own_task_id = os.environ.get('TASK_ID', '')
to_cancel = [t for t in list_task_group_incomplete_tasks(task_group_id) if t != own_task_id]
logger.info("Cancelling {} tasks".format(len(to_cancel)))
with futures.ThreadPoolExecutor(CONCURRENCY) as e:
cancels_jobs = [
e.submit(cancel_task, t, use_proxy=True)
for t in list_task_group(task_group_id) if t != own_task_id
]
for job in cancels_jobs:
job.result()
cancel_futs = [e.submit(do_cancel_task, t) for t in to_cancel]
for f in futures.as_completed(cancel_futs):
f.result()

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

@ -5,8 +5,6 @@
from __future__ import absolute_import, print_function, unicode_literals
import concurrent.futures as futures
import requests
import requests.adapters
import json
import os
import sys
@ -15,12 +13,10 @@ import logging
from slugid import nice as slugid
from taskgraph.util.parameterization import resolve_timestamps
from taskgraph.util.time import current_json_time
from taskgraph.util.taskcluster import get_session, CONCURRENCY
logger = logging.getLogger(__name__)
# the maximum number of parallel createTask calls to make
CONCURRENCY = 50
# this is set to true for `mach taskgraph action-callback --test`
testing = False
@ -28,16 +24,6 @@ testing = False
def create_tasks(taskgraph, label_to_taskid, params, decision_task_id=None):
taskid_to_label = {t: l for l, t in label_to_taskid.iteritems()}
session = requests.Session()
# Default HTTPAdapter uses 10 connections. Mount custom adapter to increase
# that limit. Connections are established as needed, so using a large value
# should not negatively impact performance.
http_adapter = requests.adapters.HTTPAdapter(pool_connections=CONCURRENCY,
pool_maxsize=CONCURRENCY)
session.mount('https://', http_adapter)
session.mount('http://', http_adapter)
decision_task_id = decision_task_id or os.environ.get('TASK_ID')
# when running as an actual decision task, we use the decision task's
@ -66,6 +52,7 @@ def create_tasks(taskgraph, label_to_taskid, params, decision_task_id=None):
# If `testing` is True, then run without parallelization
concurrency = CONCURRENCY if not testing else 1
session = get_session()
with futures.ThreadPoolExecutor(concurrency) as e:
fs = {}

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

@ -15,7 +15,6 @@ import logging
import taskcluster_urls as liburls
from mozbuild.util import memoize
from requests.packages.urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
from taskgraph.task import Task
logger = logging.getLogger(__name__)
@ -27,6 +26,9 @@ testing = False
# to the production Taskcluster deployment used for CI.
PRODUCTION_TASKCLUSTER_ROOT_URL = 'https://taskcluster.net'
# the maximum number of parallel Taskcluster API calls to make
CONCURRENCY = 50
@memoize
def get_root_url():
@ -48,10 +50,20 @@ def get_root_url():
@memoize
def get_session():
session = requests.Session()
retry = Retry(total=5, backoff_factor=0.1,
status_forcelist=[500, 502, 503, 504])
session.mount('http://', HTTPAdapter(max_retries=retry))
session.mount('https://', HTTPAdapter(max_retries=retry))
# Default HTTPAdapter uses 10 connections. Mount custom adapter to increase
# that limit. Connections are established as needed, so using a large value
# should not negatively impact performance.
http_adapter = requests.adapters.HTTPAdapter(
pool_connections=CONCURRENCY,
pool_maxsize=CONCURRENCY,
max_retries=retry)
session.mount('https://', http_adapter)
session.mount('http://', http_adapter)
return session
@ -60,7 +72,7 @@ def _do_request(url, force_get=False, **kwargs):
if kwargs and not force_get:
response = session.post(url, **kwargs)
else:
response = session.get(url, stream=True)
response = session.get(url, stream=True, **kwargs)
if response.status_code >= 400:
# Consume content before raise_for_status, so that the connection can be
# reused.
@ -268,8 +280,8 @@ def send_email(address, subject, content, link, use_proxy=False):
})
def list_task_group(task_group_id):
"""Generate the tasks in a task group"""
def list_task_group_incomplete_tasks(task_group_id):
"""Generate the incomplete tasks in a task group"""
params = {}
while True:
url = liburls.api(get_root_url(), 'queue', 'v1',

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

@ -990,7 +990,7 @@ SimpleTest.promiseClipboardChange = async function(aExpectedStringOrValidatorFn,
let maxPolls = aTimeout ? aTimeout / 100 : 50;
async function putAndVerify(operationFn, validatorFn, flavor) {
operationFn();
await operationFn();
let data;
for (let i = 0; i < maxPolls; i++) {

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

@ -0,0 +1,2 @@
[timeouts.py]
disabled: if ccov and (os == "win") and (bits == 64) and (version == "10.0.15063"): https://bugzilla.mozilla.org/show_bug.cgi?id=1495002

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

@ -10,6 +10,16 @@ MozElements.RichListBox = class RichListBox extends MozElements.BaseControl {
constructor() {
super();
this.selectedItems = new ChromeNodeList();
this._currentIndex = null;
this._lastKeyTime = 0;
this._incrementalString = "";
this._suppressOnSelect = false;
this._userSelecting = false;
this._selectTimeout = null;
this._currentItem = null;
this._selectionStart = null;
this.addEventListener("keypress", event => {
if (event.altKey || event.metaKey) {
return;
@ -135,17 +145,6 @@ MozElements.RichListBox = class RichListBox extends MozElements.BaseControl {
}
this.setAttribute("allowevents", "true");
this.selectedItems = new ChromeNodeList();
this._currentIndex = null;
this._lastKeyTime = 0;
this._incrementalString = "";
this._suppressOnSelect = false;
this._userSelecting = false;
this._selectTimeout = null;
this._currentItem = null;
this._selectionStart = null;
this._refreshSelection();
}

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

@ -281,8 +281,7 @@ extensions-updates-update-selected =
shortcuts-manage =
.label = Keyboard Shortcuts
shortcuts-empty-message = There are no shortcuts for this extension.
# TODO: Confirm this copy.
shortcuts-no-addons = You don't have any active add-ons.
shortcuts-no-addons = You dont have any extensions enabled.
shortcuts-input =
.placeholder = Type a shortcut

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

@ -97,6 +97,21 @@ void UserTimingMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
}
}
void TextMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime,
UniqueStacks& aUniqueStacks) {
StreamCommonProps("Text", aWriter, aProcessStartTime, aUniqueStacks);
aWriter.StringProperty("name", mText.get());
}
void LogMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime,
UniqueStacks& aUniqueStacks) {
StreamCommonProps("Log", aWriter, aProcessStartTime, aUniqueStacks);
aWriter.StringProperty("name", mText.get());
aWriter.StringProperty("module", mModule.get());
}
void DOMEventMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime,
UniqueStacks& aUniqueStacks) {

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

@ -11,6 +11,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/net/TimingStruct.h"
@ -359,4 +360,53 @@ class LongTaskMarkerPayload : public ProfilerMarkerPayload {
DECL_STREAM_PAYLOAD
};
class TextMarkerPayload : public ProfilerMarkerPayload {
public:
TextMarkerPayload(const nsACString& aText,
const mozilla::TimeStamp& aStartTime)
: ProfilerMarkerPayload(aStartTime, aStartTime), mText(aText) {}
TextMarkerPayload(const nsACString& aText,
const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime)
: ProfilerMarkerPayload(aStartTime, aEndTime), mText(aText) {}
TextMarkerPayload(const nsACString& aText,
const mozilla::TimeStamp& aStartTime,
const mozilla::Maybe<nsID>& aDocShellId,
const mozilla::Maybe<uint32_t>& aDocShellHistoryId)
: ProfilerMarkerPayload(aStartTime, aStartTime, aDocShellId,
aDocShellHistoryId),
mText(aText) {}
TextMarkerPayload(const nsACString& aText,
const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime,
const mozilla::Maybe<nsID>& aDocShellId,
const mozilla::Maybe<uint32_t>& aDocShellHistoryId)
: ProfilerMarkerPayload(aStartTime, aEndTime, aDocShellId,
aDocShellHistoryId),
mText(aText) {}
DECL_STREAM_PAYLOAD
private:
nsCString mText;
};
class LogMarkerPayload : public ProfilerMarkerPayload {
public:
LogMarkerPayload(const char* aModule, const char* aText,
const mozilla::TimeStamp& aStartTime)
: ProfilerMarkerPayload(aStartTime, aStartTime),
mModule(aModule),
mText(aText) {}
DECL_STREAM_PAYLOAD
private:
nsAutoCStringN<32> mModule; // longest known LazyLogModule name is ~24
nsCString mText;
};
#endif // ProfilerMarkerPayload_h

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

@ -22,6 +22,9 @@
#include "nsDebugImpl.h"
#include "NSPRLogModulesParser.h"
#include "LogCommandLineHandler.h"
#ifdef MOZ_GECKO_PROFILER
#include "ProfilerMarkerPayload.h"
#endif
#include "prenv.h"
#ifdef XP_WIN
@ -166,6 +169,7 @@ class LogModuleManager {
mMainThread(PR_GetCurrentThread()),
mSetFromEnv(false),
mAddTimestamp(false),
mAddProfilerMarker(false),
mIsRaw(false),
mIsSync(false),
mRotate(0),
@ -208,6 +212,7 @@ class LogModuleManager {
bool addTimestamp = false;
bool isSync = false;
bool isRaw = false;
bool isMarkers = false;
int32_t rotate = 0;
const char* modules = PR_GetEnv("MOZ_LOG");
if (!modules || !modules[0]) {
@ -230,9 +235,9 @@ class LogModuleManager {
// Need to capture `this` since `sLogModuleManager` is not set until after
// initialization is complete.
NSPRLogModulesParser(
modules,
[this, &shouldAppend, &addTimestamp, &isSync, &isRaw, &rotate](
const char* aName, LogLevel aLevel, int32_t aValue) mutable {
modules, [this, &shouldAppend, &addTimestamp, &isSync, &isRaw, &rotate,
&isMarkers](const char* aName, LogLevel aLevel,
int32_t aValue) mutable {
if (strcmp(aName, "append") == 0) {
shouldAppend = true;
} else if (strcmp(aName, "timestamp") == 0) {
@ -243,6 +248,8 @@ class LogModuleManager {
isRaw = true;
} else if (strcmp(aName, "rotate") == 0) {
rotate = (aValue << 20) / kRotateFilesNumber;
} else if (strcmp(aName, "profilermarkers") == 0) {
isMarkers = true;
} else {
this->CreateOrGetModule(aName)->SetLevel(aLevel);
}
@ -253,6 +260,7 @@ class LogModuleManager {
mIsSync = isSync;
mIsRaw = isRaw;
mRotate = rotate;
mAddProfilerMarker = isMarkers;
if (rotate > 0 && shouldAppend) {
NS_WARNING("MOZ_LOG: when you rotate the log, you cannot use append!");
@ -401,6 +409,14 @@ class LogModuleManager {
charsWritten = strlen(buffToWrite);
}
#ifdef MOZ_GECKO_PROFILER
if (mAddProfilerMarker && profiler_is_active()) {
profiler_add_marker(
"LogMessages",
MakeUnique<LogMarkerPayload>(aName, buffToWrite, TimeStamp::Now()));
}
#endif
// Determine if a newline needs to be appended to the message.
const char* newline = "";
if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') {
@ -524,6 +540,7 @@ class LogModuleManager {
PRThread* mMainThread;
bool mSetFromEnv;
Atomic<bool, Relaxed> mAddTimestamp;
Atomic<bool, Relaxed> mAddProfilerMarker;
Atomic<bool, Relaxed> mIsRaw;
Atomic<bool, Relaxed> mIsSync;
int32_t mRotate;