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

This commit is contained in:
Sebastian Hengst 2017-08-14 11:59:40 +02:00
commit 87c58fd561
91 changed files with 1887 additions and 1181 deletions

View File

@ -18,6 +18,7 @@ var gSearchResultsPane = {
this.searchInput = document.getElementById("searchInput");
this.searchInput.hidden = !Services.prefs.getBoolPref("browser.preferences.search");
if (!this.searchInput.hidden) {
this.searchInput.addEventListener("input", this);
this.searchInput.addEventListener("command", this);
window.addEventListener("DOMContentLoaded", () => {
this.searchInput.focus();
@ -145,7 +146,8 @@ var gSearchResultsPane = {
let nodeStartIndex = null;
// Determining the start and end node to highlight from
nodeSizes.forEach(function(lengthNodes, index) {
for (let index = 0; index < nodeSizes.length; index++) {
let lengthNodes = nodeSizes[index];
// Determining the start node
if (!startNode && lengthNodes >= startValue) {
startNode = textNodes[index];
@ -164,7 +166,7 @@ var gSearchResultsPane = {
endValue -= nodeSizes[index - 1];
}
}
});
}
let range = document.createRange();
range.setStart(startNode, startValue);
range.setEnd(endNode, endValue);
@ -207,8 +209,15 @@ var gSearchResultsPane = {
* @param String event
* to search for filted query in
*/
searchFunction(event) {
this.query = event.target.value.trim().toLowerCase();
async searchFunction(event) {
let query = event.target.value.trim().toLowerCase();
if (this.query == query) {
return;
}
let subQuery = this.query && query.indexOf(this.query) !== -1;
this.query = query;
this.getFindSelection(window).removeAllRanges();
this.removeAllSearchTooltips();
this.removeAllSearchMenuitemIndicators();
@ -230,13 +239,40 @@ var gSearchResultsPane = {
let rootPreferencesChildren = document
.querySelectorAll("#mainPrefPane > *:not([data-hidden-from-search])");
// Showing all the children to bind JS, Access Keys, etc
if (subQuery) {
// Since the previous query is a subset of the current query,
// there is no need to check elements that is hidden already.
rootPreferencesChildren =
Array.prototype.filter.call(rootPreferencesChildren, el => !el.hidden);
}
// Mark all the children to check be visible to bind JS, Access Keys, etc,
// but don't really show them by setting their visibility to hidden in CSS.
for (let i = 0; i < rootPreferencesChildren.length; i++) {
rootPreferencesChildren[i].hidden = false;
rootPreferencesChildren[i].classList.add("visually-hidden");
}
let ts = performance.now();
let FRAME_THRESHOLD = 1000 / 60;
// Showing or Hiding specific section depending on if words in query are found
for (let i = 0; i < rootPreferencesChildren.length; i++) {
if (performance.now() - ts > FRAME_THRESHOLD) {
// Creating tooltips for all the instances found
for (let anchorNode of this.listSearchTooltips) {
this.createSearchTooltip(anchorNode, this.query);
}
// It hides Search Results header so turning it on
srHeader.hidden = false;
srHeader.classList.remove("visually-hidden");
ts = await new Promise(resolve => window.requestAnimationFrame(resolve));
if (query !== this.query) {
return;
}
}
rootPreferencesChildren[i].classList.remove("visually-hidden");
if (!rootPreferencesChildren[i].classList.contains("header") &&
!rootPreferencesChildren[i].classList.contains("subcategory") &&
!rootPreferencesChildren[i].classList.contains("no-results-message") &&
@ -249,6 +285,7 @@ var gSearchResultsPane = {
}
// It hides Search Results header so turning it on
srHeader.hidden = false;
srHeader.classList.remove("visually-hidden");
if (!resultsFound) {
let noResultsEl = document.querySelector(".no-results-message");
@ -266,7 +303,9 @@ var gSearchResultsPane = {
strings.getFormattedString("searchResults.needHelp2", [helpUrl, brandName]);
} else {
// Creating tooltips for all the instances found
this.listSearchTooltips.forEach((anchorNode) => this.createSearchTooltip(anchorNode, this.query));
for (let anchorNode of this.listSearchTooltips) {
this.createSearchTooltip(anchorNode, this.query);
}
// Implant search telemetry probe after user stops typing for a while
if (this.query.length >= 2) {
@ -280,6 +319,8 @@ var gSearchResultsPane = {
// Going back to General when cleared
gotoPref("paneGeneral");
}
window.dispatchEvent(new CustomEvent("PreferencesSearchCompleted", { detail: query }));
},
/**
@ -409,6 +450,9 @@ var gSearchResultsPane = {
* Word or words that are being searched for
*/
createSearchTooltip(anchorNode, query) {
if (anchorNode.tooltipNode) {
return;
}
let searchTooltip = anchorNode.ownerDocument.createElement("span");
searchTooltip.setAttribute("class", "search-tooltip");
searchTooltip.textContent = query;
@ -439,7 +483,10 @@ var gSearchResultsPane = {
searchTooltip.parentElement.classList.remove("search-tooltip-parent");
searchTooltip.remove();
}
this.listSearchTooltips.forEach((anchorNode) => anchorNode.tooltipNode.remove());
for (let anchorNode of this.listSearchTooltips) {
anchorNode.tooltipNode.remove();
anchorNode.tooltipNode = null;
}
this.listSearchTooltips.clear();
},
@ -447,7 +494,9 @@ var gSearchResultsPane = {
* Remove all indicators on menuitem.
*/
removeAllSearchMenuitemIndicators() {
this.listSearchMenuitemIndicators.forEach((node) => node.removeAttribute("indicator"));
for (let node of this.listSearchMenuitemIndicators) {
node.removeAttribute("indicator");
}
this.listSearchMenuitemIndicators.clear();
}
}

View File

@ -240,6 +240,7 @@ function search(aQuery, aAttribute, aSubquery, aSubAttribute) {
element.hidden = true;
}
}
element.classList.remove("visually-hidden");
}
let keysets = mainPrefPane.getElementsByTagName("keyset");

View File

@ -15,10 +15,15 @@ skip-if = !updater
[browser_bug731866.js]
[browser_search_within_preferences_1.js]
[browser_search_within_preferences_2.js]
[browser_search_within_preferences_command.js]
[browser_search_subdialogs_within_preferences_1.js]
[browser_search_subdialogs_within_preferences_2.js]
[browser_search_subdialogs_within_preferences_3.js]
[browser_search_subdialogs_within_preferences_4.js]
[browser_search_subdialogs_within_preferences_5.js]
[browser_search_subdialogs_within_preferences_6.js]
[browser_search_subdialogs_within_preferences_7.js]
[browser_search_subdialogs_within_preferences_8.js]
[browser_bug795764_cachedisabled.js]
[browser_bug1018066_resetScrollPosition.js]
[browser_bug1020245_openPreferences_to_paneContent.js]

View File

@ -12,7 +12,7 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("Set Home Page", "startupGroup");
await evaluateSearchResults("Set Home Page", "startupGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
@ -21,33 +21,6 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("Choose languages", "languagesGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Fonts" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("Text Encoding", "fontsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Colors" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("Link Colors", "fontsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Exceptions - Saved Logins" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("sites will not be saved", "passwordsGroup");
await evaluateSearchResults("Choose languages", "languagesGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -15,7 +15,7 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("sites are stored", "passwordsGroup");
await evaluateSearchResults("sites are stored", "passwordsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
@ -24,24 +24,6 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("disabled Tracking Protection", "trackingGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Block Lists" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("block Web elements", "trackingGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Allowed Sites - Pop-ups" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("open pop-up windows", "permissionsGroup");
await evaluateSearchResults("disabled Tracking Protection", "trackingGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -15,7 +15,7 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("allowed to install add-ons", "permissionsGroup");
await evaluateSearchResults("allowed to install add-ons", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
@ -24,33 +24,6 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("identify these certificate authorities", "certSelection");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Device Manager" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("Security Modules and Devices", "certSelection");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Connection Settings" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("Use system proxy settings", "connectionGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Settings - Site Data" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("store site data on your computer", "siteDataGroup");
await evaluateSearchResults("identify these certificate authorities", "certSelection");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -12,7 +12,7 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("updates have been installed", "updateApp");
await evaluateSearchResults("updates have been installed", "updateApp");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
@ -21,33 +21,6 @@ add_task(async function() {
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("set location permissions", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Camera Permissions" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("set camera permissions", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Microphone Permissions" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("set microphone permissions", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Notification Permissions" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
evaluateSearchResults("set notifications permissions", "permissionsGroup");
await evaluateSearchResults("set location permissions", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -0,0 +1,35 @@
/*
* This file contains tests for the Preferences search bar.
*/
// Enabling Searching functionatily. Will display search bar form this testcase forward.
add_task(async function() {
await SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", true]]});
});
/**
* Test for searching for the "Fonts" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("Text Encoding", "fontsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Colors" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("Link Colors", "fontsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Exceptions - Saved Logins" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("sites will not be saved", "passwordsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -0,0 +1,29 @@
/*
* This file contains tests for the Preferences search bar.
*/
// Enabling Searching functionatily. Will display search bar form this testcase forward.
add_task(async function() {
await SpecialPowers.pushPrefEnv({"set": [
["browser.preferences.search", true],
["privacy.trackingprotection.ui.enabled", true]
]});
});
/**
* Test for searching for the "Block Lists" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("block Web elements", "trackingGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Allowed Sites - Pop-ups" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("open pop-up windows", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -0,0 +1,38 @@
/*
* This file contains tests for the Preferences search bar.
*/
// Enabling Searching functionatily. Will display search bar form this testcase forward.
add_task(async function() {
await SpecialPowers.pushPrefEnv({"set": [
["browser.preferences.search", true],
["browser.storageManager.enabled", true]
]});
});
/**
* Test for searching for the "Device Manager" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("Security Modules and Devices", "certSelection");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Connection Settings" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("Use system proxy settings", "connectionGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Settings - Site Data" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("store site data on your computer", "siteDataGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -0,0 +1,35 @@
/*
* This file contains tests for the Preferences search bar.
*/
// Enabling Searching functionatily. Will display search bar form this testcase forward.
add_task(async function() {
await SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", true]]});
});
/**
* Test for searching for the "Camera Permissions" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("set camera permissions", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Microphone Permissions" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("set microphone permissions", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Notification Permissions" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
await evaluateSearchResults("set notifications permissions", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -3,7 +3,7 @@
* This file contains tests for the Preferences search bar.
*/
requestLongerTimeout(2);
requestLongerTimeout(6);
/**
* Tests to see if search bar is being hidden when pref is turned off
@ -49,8 +49,11 @@ add_task(async function() {
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
searchInput.value = "password";
searchInput.doCommand();
let query = "password";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
let categoriesList = gBrowser.contentDocument.getElementById("categories");
@ -59,8 +62,13 @@ add_task(async function() {
is(child.selected, false, "No other panel should be selected");
}
// Takes search off
searchInput.value = "";
searchInput.doCommand();
searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == "");
let count = query.length;
while (count--) {
EventUtils.sendKey("BACK_SPACE");
}
await searchCompletedPromise;
// Checks if back to generalPane
for (let i = 0; i < categoriesList.childElementCount; i++) {
@ -87,8 +95,11 @@ add_task(async function() {
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
searchInput.value = "password";
searchInput.doCommand();
let query = "password";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane");
@ -106,8 +117,13 @@ add_task(async function() {
}
// Takes search off
searchInput.value = "";
searchInput.doCommand();
searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == "");
let count = query.length;
while (count--) {
EventUtils.sendKey("BACK_SPACE");
}
await searchCompletedPromise;
// Checks if back to generalPane
for (let i = 0; i < mainPrefTag.childElementCount; i++) {
@ -155,14 +171,22 @@ add_task(async function() {
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
searchInput.value = "coach";
searchInput.doCommand();
let query = "coach";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
is_element_visible(noResultsEl, "Should be in search results");
// Takes search off
searchInput.value = "";
searchInput.doCommand();
searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == "");
let count = query.length;
while (count--) {
EventUtils.sendKey("BACK_SPACE");
}
await searchCompletedPromise;
is_element_hidden(noResultsEl, "Should not be in search results");
@ -184,12 +208,20 @@ add_task(async function() {
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
searchInput.value = "password";
searchInput.doCommand();
let query = "password";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
// Takes search off
searchInput.value = "";
searchInput.doCommand();
searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == "");
let count = query.length;
while (count--) {
EventUtils.sendKey("BACK_SPACE");
}
await searchCompletedPromise;
// Checks if back to normal
is_element_visible(generalPane, "Should be in generalPane");
@ -214,8 +246,11 @@ add_task(async function() {
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
searchInput.value = "site data";
searchInput.doCommand();
let query = "site data";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane");
@ -223,8 +258,13 @@ add_task(async function() {
is_element_hidden(child, "Should be hidden in search results");
// Takes search off
searchInput.value = "";
searchInput.doCommand();
searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == "");
let count = query.length;
while (count--) {
EventUtils.sendKey("BACK_SPACE");
}
await searchCompletedPromise;
// Checks if back to normal
is_element_visible(generalPane, "Should be in generalPane");
@ -242,8 +282,11 @@ add_task(async function() {
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
searchInput.value = "password";
searchInput.doCommand();
let query = "password";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
let privacyCategory = gBrowser.contentDocument.getElementById("category-privacy");
privacyCategory.click();

View File

@ -31,8 +31,11 @@ add_task(async function() {
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
searchInput.value = "Create Account";
searchInput.doCommand();
let query = "Create Account";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane");
for (let i = 0; i < mainPrefTag.childElementCount; i++) {
@ -51,8 +54,11 @@ add_task(async function() {
// Performs search.
searchInput.focus();
searchInput.value = "Forget this Email";
searchInput.doCommand();
query = "Forget this Email";
searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
let noResultsEl = gBrowser.contentDocument.querySelector(".no-results-message");
is_element_visible(noResultsEl, "Should be reporting no results");

View File

@ -0,0 +1,37 @@
"use strict";
/**
* Test for "command" event on search input (when user clicks the x button)
*/
add_task(async function() {
await SpecialPowers.pushPrefEnv({"set": [["browser.storageManager.enabled", false]]});
await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
let generalPane = gBrowser.contentDocument.getElementById("generalCategory");
is_element_hidden(generalPane, "Should not be in general");
// Performs search
let searchInput = gBrowser.contentDocument.getElementById("searchInput");
is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
"Search input should be focused when visiting preferences");
let query = "x";
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
EventUtils.sendString(query);
await searchCompletedPromise;
is_element_hidden(generalPane, "Should not be in generalPane");
// Takes search off with "command"
searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == "");
searchInput.value = "";
searchInput.doCommand();
await searchCompletedPromise;
// Checks if back to normal
is_element_visible(generalPane, "Should be in generalPane");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -227,14 +227,16 @@ function assertSitesListed(doc, hosts) {
is(removeAllBtn.disabled, false, "Should enable the removeAllBtn button");
}
function evaluateSearchResults(keyword, searchReults) {
async function evaluateSearchResults(keyword, searchReults) {
searchReults = Array.isArray(searchReults) ? searchReults : [searchReults];
searchReults.push("header-searchResults");
let searchInput = gBrowser.contentDocument.getElementById("searchInput");
searchInput.focus();
searchInput.value = keyword;
searchInput.doCommand();
let searchCompletedPromise = BrowserTestUtils.waitForEvent(
gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == keyword);
EventUtils.sendString(keyword);
await searchCompletedPromise;
let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane");
for (let i = 0; i < mainPrefTag.childElementCount; i++) {

View File

@ -7,5 +7,6 @@ support-files =
[browser_onboarding_notification_2.js]
[browser_onboarding_notification_3.js]
[browser_onboarding_notification_4.js]
[browser_onboarding_select_default_tour.js]
[browser_onboarding_tours.js]
[browser_onboarding_tourset.js]

View File

@ -0,0 +1,114 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const OVERLAY_ICON_ID = "#onboarding-overlay-button";
const PRIVATE_BROWSING_TOUR_ID = "#onboarding-tour-private-browsing";
const ADDONS_TOUR_ID = "#onboarding-tour-addons";
const CUSTOMIZE_TOUR_ID = "#onboarding-tour-customize";
const CLASS_ACTIVE = "onboarding-active";
add_task(async function test_default_tour_open_the_right_page() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [
["browser.onboarding.tour-type", "new"],
["browser.onboarding.tourset-version", 1],
["browser.onboarding.seen-tourset-version", 1],
["browser.onboarding.newtour", "private,addons,customize"],
]});
let tab = await openTab(ABOUT_NEWTAB_URL);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await BrowserTestUtils.synthesizeMouseAtCenter(OVERLAY_ICON_ID, {}, tab.linkedBrowser);
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
info("Make sure the default tour is active and open the right page");
let { activeNavItemId, activePageId } = await getCurrentActiveTour(tab.linkedBrowser);
is(`#${activeNavItemId}`, PRIVATE_BROWSING_TOUR_ID, "default tour is active");
is(activePageId, "onboarding-tour-private-browsing-page", "default tour page is shown");
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_first_tour_is_active_by_default() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [
["browser.onboarding.tour-type", "new"],
["browser.onboarding.tourset-version", 1],
["browser.onboarding.seen-tourset-version", 1],
["browser.onboarding.newtour", "private,addons,customize"],
]});
let tab = await openTab(ABOUT_NEWTAB_URL);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await BrowserTestUtils.synthesizeMouseAtCenter(OVERLAY_ICON_ID, {}, tab.linkedBrowser);
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
info("Make sure the default tour is selected");
let doc = content && content.document;
let dom = doc.querySelector(PRIVATE_BROWSING_TOUR_ID);
ok(dom.classList.contains(CLASS_ACTIVE), "default tour is selected");
let dom2 = doc.querySelector(ADDONS_TOUR_ID);
ok(!dom2.classList.contains(CLASS_ACTIVE), "none default tour should not be selected");
let dom3 = doc.querySelector(CUSTOMIZE_TOUR_ID);
ok(!dom3.classList.contains(CLASS_ACTIVE), "none default tour should not be selected");
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_select_first_uncomplete_tour() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [
["browser.onboarding.tour-type", "new"],
["browser.onboarding.tourset-version", 1],
["browser.onboarding.seen-tourset-version", 1],
["browser.onboarding.newtour", "private,addons,customize"],
]});
setTourCompletedState("onboarding-tour-private-browsing", true);
let tab = await openTab(ABOUT_NEWTAB_URL);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await BrowserTestUtils.synthesizeMouseAtCenter(OVERLAY_ICON_ID, {}, tab.linkedBrowser);
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
info("Make sure the first uncomplete tour is selected");
let doc = content && content.document;
let dom = doc.querySelector(PRIVATE_BROWSING_TOUR_ID);
ok(!dom.classList.contains(CLASS_ACTIVE), "the first tour is set completed and should not be selected");
let dom2 = doc.querySelector(ADDONS_TOUR_ID);
ok(dom2.classList.contains(CLASS_ACTIVE), "the first uncomplete tour is selected");
let dom3 = doc.querySelector(CUSTOMIZE_TOUR_ID);
ok(!dom3.classList.contains(CLASS_ACTIVE), "other tour should not be selected");
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_select_first_tour_when_all_tours_are_complete() {
resetOnboardingDefaultState();
await SpecialPowers.pushPrefEnv({set: [
["browser.onboarding.tour-type", "new"],
["browser.onboarding.tourset-version", 1],
["browser.onboarding.seen-tourset-version", 1],
["browser.onboarding.newtour", "private,addons,customize"],
]});
setTourCompletedState("onboarding-tour-private-browsing", true);
setTourCompletedState("onboarding-tour-addons", true);
setTourCompletedState("onboarding-tour-customize", true);
let tab = await openTab(ABOUT_NEWTAB_URL);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await BrowserTestUtils.synthesizeMouseAtCenter(OVERLAY_ICON_ID, {}, tab.linkedBrowser);
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
info("Make sure the first tour is selected when all tours are completed");
let doc = content && content.document;
let dom = doc.querySelector(PRIVATE_BROWSING_TOUR_ID);
ok(dom.classList.contains(CLASS_ACTIVE), "should be selected when all tours are completed");
let dom2 = doc.querySelector(ADDONS_TOUR_ID);
ok(!dom2.classList.contains(CLASS_ACTIVE), "other tour should not be selected");
let dom3 = doc.querySelector(CUSTOMIZE_TOUR_ID);
ok(!dom3.classList.contains(CLASS_ACTIVE), "other tour should not be selected");
await BrowserTestUtils.removeTab(tab);
});

View File

@ -721,6 +721,10 @@ groupbox {
position: relative;
}
.visually-hidden {
visibility: hidden;
}
menulist[indicator=true] > menupopup menuitem:not([image]) > .menu-iconic-left {
display: -moz-box;
min-width: auto; /* Override the min-width defined in menu.css */

View File

@ -4143,19 +4143,40 @@ Element::SetCustomElementData(CustomElementData* aData)
MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
size_t
Element::SizeOfExcludingThis(SizeOfState& aState) const
void
Element::AddSizeOfExcludingThis(SizeOfState& aState, nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
size_t n = FragmentOrElement::SizeOfExcludingThis(aState);
FragmentOrElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
// Measure mServoData. We use ServoElementMallocSizeOf rather than
// |aState.mMallocSizeOf| to distinguish in DMD's output the memory
// measured within Servo code.
if (mServoData.Get()) {
n += Servo_Element_SizeOfExcludingThis(ServoElementMallocSizeOf,
&aState.mSeenPtrs, this);
if (HasServoData()) {
// Measure mServoData, excluding the ComputedValues. This measurement
// counts towards the element's size. We use ServoElementMallocSizeOf
// rather thang |aState.mMallocSizeOf| to better distinguish in DMD's
// output the memory measured within Servo code.
*aNodeSize +=
Servo_Element_SizeOfExcludingThisAndCVs(ServoElementMallocSizeOf,
&aState.mSeenPtrs, this);
// Now measure just the ComputedValues (and style structs) under
// mServoData. This counts towards the relevant fields in |aSizes|.
RefPtr<ServoStyleContext> sc;
if (Servo_Element_HasPrimaryComputedValues(this)) {
sc = Servo_Element_GetPrimaryComputedValues(this).Consume();
if (!aState.HaveSeenPtr(sc.get())) {
sc->AddSizeOfIncludingThis(aState, aSizes, /* isDOM = */ true);
}
for (size_t i = 0; i < nsCSSPseudoElements::kEagerPseudoCount; i++) {
if (Servo_Element_HasPseudoComputedValues(this, i)) {
sc = Servo_Element_GetPseudoComputedValues(this, i).Consume();
if (!aState.HaveSeenPtr(sc.get())) {
sc->AddSizeOfIncludingThis(aState, aSizes, /* isDOM = */ true);
}
}
}
}
}
return n;
}
struct DirtyDescendantsBit {

View File

@ -205,7 +205,7 @@ public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ELEMENT_IID)
NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;

View File

@ -2502,19 +2502,18 @@ FragmentOrElement::FireNodeRemovedForChildren()
}
}
size_t
FragmentOrElement::SizeOfExcludingThis(SizeOfState& aState) const
void
FragmentOrElement::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
size_t n = 0;
n += nsIContent::SizeOfExcludingThis(aState);
n += mAttrsAndChildren.SizeOfExcludingThis(aState.mMallocSizeOf);
nsIContent::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
*aNodeSize += mAttrsAndChildren.SizeOfExcludingThis(aState.mMallocSizeOf);
nsDOMSlots* slots = GetExistingDOMSlots();
if (slots) {
n += slots->SizeOfIncludingThis(aState.mMallocSizeOf);
*aNodeSize += slots->SizeOfIncludingThis(aState.mMallocSizeOf);
}
return n;
}
void

View File

@ -118,7 +118,7 @@ public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS
// nsINode interface methods
virtual uint32_t GetChildCount() const override;

View File

@ -12434,26 +12434,25 @@ nsDocument::GetVisibilityState(nsAString& aState)
}
/* virtual */ void
nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aSizes) const
{
aWindowSizes.mDOMOtherSize +=
nsINode::SizeOfExcludingThis(aWindowSizes.mState);
nsINode::AddSizeOfExcludingThis(aSizes.mState, aSizes.mStyleSizes,
&aSizes.mDOMOtherSize);
if (mPresShell) {
mPresShell->AddSizeOfIncludingThis(aWindowSizes);
mPresShell->AddSizeOfIncludingThis(aSizes);
}
aWindowSizes.mPropertyTablesSize +=
mPropertyTable.SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
aSizes.mPropertyTablesSize +=
mPropertyTable.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
for (uint32_t i = 0, count = mExtraPropertyTables.Length();
i < count; ++i) {
aWindowSizes.mPropertyTablesSize +=
mExtraPropertyTables[i]->SizeOfIncludingThis(
aWindowSizes.mState.mMallocSizeOf);
aSizes.mPropertyTablesSize +=
mExtraPropertyTables[i]->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
}
if (EventListenerManager* elm = GetExistingListenerManager()) {
aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
aSizes.mDOMEventListenersCount += elm->ListenerCount();
}
// Measurement of the following members may be added later if DMD finds it
@ -12484,10 +12483,12 @@ SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<StyleSheet>>& aSheets,
return n;
}
size_t
nsDocument::SizeOfExcludingThis(SizeOfState& aState) const
void
nsDocument::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
// This SizeOfExcludingThis() overrides the one from nsINode. But
// This AddSizeOfExcludingThis() overrides the one from nsINode. But
// nsDocuments can only appear at the top of the DOM tree, and we use the
// specialized DocAddSizeOfExcludingThis() in that case. So this should never
// be called.
@ -12497,40 +12498,48 @@ nsDocument::SizeOfExcludingThis(SizeOfState& aState) const
void
nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
{
nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
for (nsIContent* node = nsINode::GetFirstChild();
node;
node = node->GetNextNode(this))
{
size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes.mState);
size_t* p;
size_t nodeSize = 0;
node->AddSizeOfIncludingThis(aWindowSizes.mState, aWindowSizes.mStyleSizes,
&nodeSize);
// This is where we transfer the nodeSize obtained from
// nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
switch (node->NodeType()) {
case nsIDOMNode::ELEMENT_NODE:
p = &aWindowSizes.mDOMElementNodesSize;
aWindowSizes.mDOMElementNodesSize += nodeSize;
break;
case nsIDOMNode::TEXT_NODE:
p = &aWindowSizes.mDOMTextNodesSize;
aWindowSizes.mDOMTextNodesSize += nodeSize;
break;
case nsIDOMNode::CDATA_SECTION_NODE:
p = &aWindowSizes.mDOMCDATANodesSize;
aWindowSizes.mDOMCDATANodesSize += nodeSize;
break;
case nsIDOMNode::COMMENT_NODE:
p = &aWindowSizes.mDOMCommentNodesSize;
aWindowSizes.mDOMCommentNodesSize += nodeSize;
break;
default:
p = &aWindowSizes.mDOMOtherSize;
aWindowSizes.mDOMOtherSize += nodeSize;
break;
}
*p += nodeSize;
if (EventListenerManager* elm = node->GetExistingListenerManager()) {
aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
}
}
// IMPORTANT: for our ComputedValues measurements, we want to measure
// ComputedValues accessible from DOM elements before ComputedValues not
// accessible from DOM elements (i.e. accessible only from the frame tree).
//
// Therefore, the measurement of the nsIDocument superclass must happen after
// the measurement of DOM nodes (above), because nsIDocument contains the
// PresShell, which contains the frame tree.
nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
aWindowSizes.mStyleSheetsSize +=
SizeOfOwnedSheetArrayExcludingThis(mStyleSheets,
aWindowSizes.mState.mMallocSizeOf);

View File

@ -367,7 +367,7 @@ public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS
virtual void Reset(nsIChannel *aChannel, nsILoadGroup *aLoadGroup) override;
virtual void ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,

View File

@ -1109,11 +1109,12 @@ nsGenericDOMDataNode::GetAttributeChangeHint(const nsIAtom* aAttribute,
return nsChangeHint(0);
}
size_t
nsGenericDOMDataNode::SizeOfExcludingThis(SizeOfState& aState) const
void
nsGenericDOMDataNode::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
size_t n = nsIContent::SizeOfExcludingThis(aState);
n += mText.SizeOfExcludingThis(aState.mMallocSizeOf);
return n;
nsIContent::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
*aNodeSize += mText.SizeOfExcludingThis(aState.mMallocSizeOf);
}

View File

@ -67,7 +67,7 @@ class nsGenericDOMDataNode : public nsIContent
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS
explicit nsGenericDOMDataNode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
explicit nsGenericDOMDataNode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);

View File

@ -2569,13 +2569,13 @@ nsINode::GetAccessibleNode()
return nullptr;
}
size_t
nsINode::SizeOfExcludingThis(SizeOfState& aState) const
void
nsINode::AddSizeOfExcludingThis(SizeOfState& aState, nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
size_t n = 0;
EventListenerManager* elm = GetExistingListenerManager();
if (elm) {
n += elm->SizeOfIncludingThis(aState.mMallocSizeOf);
*aNodeSize += elm->SizeOfIncludingThis(aState.mMallocSizeOf);
}
// Measurement of the following members may be added later if DMD finds it is
@ -2586,7 +2586,6 @@ nsINode::SizeOfExcludingThis(SizeOfState& aState) const
// The following members are not measured:
// - mParent, mNextSibling, mPreviousSibling, mFirstChild: because they're
// non-owning
return n;
}
#define EVENT(name_, id_, type_, struct_) \

View File

@ -17,9 +17,9 @@
#include "nsNodeInfoManager.h" // for use in NodePrincipal()
#include "nsPropertyTable.h" // for typedefs
#include "nsTObserverArray.h" // for member
#include "nsWindowSizes.h" // for nsStyleSizes
#include "mozilla/ErrorResult.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/SizeOfState.h" // for SizeOfState
#include "mozilla/dom/EventTarget.h" // for base class
#include "js/TypeDecls.h" // for Handle, Value, JSObject, JSContext
#include "mozilla/dom/DOMString.h"
@ -264,13 +264,13 @@ private:
};
// This should be used for any nsINode sub-class that has fields of its own
// that it needs to measure; any sub-class that doesn't use it will inherit
// SizeOfExcludingThis from its super-class. SizeOfIncludingThis() need not be
// defined, it is inherited from nsINode.
// This macro isn't actually specific to nodes, and bug 956400 will move it into MFBT.
#define NS_DECL_SIZEOF_EXCLUDING_THIS \
virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) \
const override;
// that it needs to measure; any sub-class that doesn't use it will inherit
// AddSizeOfExcludingThis from its super-class. AddSizeOfIncludingThis() need
// not be defined, it is inherited from nsINode.
#define NS_DECL_ADDSIZEOFEXCLUDINGTHIS \
virtual void AddSizeOfExcludingThis(mozilla::SizeOfState& aState, \
nsStyleSizes& aSizes, \
size_t* aNodeSize) const override;
// Categories of node properties
// 0 is global.
@ -305,6 +305,10 @@ public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)
// The |aNodeSize| outparam on this function is where the actual node size
// value is put. It gets added to the appropriate value within |aSizes| by
// nsDocument::DocAddSizeOfExcludingThis().
//
// Among the sub-classes that inherit (directly or indirectly) from nsINode,
// measurement of the following members may be added later if DMD finds it is
// worthwhile:
@ -328,15 +332,20 @@ public:
// The following members don't need to be measured:
// - nsIContent: mPrimaryFrame, because it's non-owning and measured elsewhere
//
virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) const;
virtual void AddSizeOfExcludingThis(mozilla::SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const;
// SizeOfIncludingThis doesn't need to be overridden by sub-classes because
// sub-classes of nsINode are guaranteed to be laid out in memory in such a
// way that |this| points to the start of the allocated object, even in
// methods of nsINode's sub-classes, so aState.mMallocSizeOf(this) is always
// safe to call no matter which object it was invoked on.
virtual size_t SizeOfIncludingThis(mozilla::SizeOfState& aState) const {
return aState.mMallocSizeOf(this) + SizeOfExcludingThis(aState);
virtual void AddSizeOfIncludingThis(mozilla::SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const {
*aNodeSize += aState.mMallocSizeOf(this);
AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
}
friend class nsNodeUtils;

View File

@ -384,7 +384,7 @@ CollectWindowReports(nsGlobalWindow *aWindow,
aWindowTotalSizes->mArenaSizes.mStyleContexts
+= windowSizes.mArenaSizes.mStyleContexts;
REPORT_SIZE("/layout/style-structs", windowSizes.mArenaSizes.mStyleStructs,
REPORT_SIZE("/layout/gecko-style-structs", windowSizes.mArenaSizes.mStyleStructs,
"Memory used by style structs within a window.");
aWindowTotalSizes->mArenaSizes.mStyleStructs
+= windowSizes.mArenaSizes.mStyleStructs;
@ -429,19 +429,17 @@ CollectWindowReports(nsGlobalWindow *aWindow,
js::MemoryReportingSundriesThreshold();
size_t frameSundriesSize = 0;
#define FRAME_ID(classname, ...) \
{ \
size_t frameSize \
= windowSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname); \
if (frameSize < FRAME_SUNDRIES_THRESHOLD) { \
frameSundriesSize += frameSize; \
} else { \
REPORT_SIZE("/layout/frames/" # classname, frameSize, \
"Memory used by frames of " \
"type " #classname " within a window."); \
} \
aWindowTotalSizes->mArenaSizes.NS_ARENA_SIZES_FIELD(classname) \
+= frameSize; \
#define FRAME_ID(classname, ...) \
{ \
size_t size = windowSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname); \
if (size < FRAME_SUNDRIES_THRESHOLD) { \
frameSundriesSize += size; \
} else { \
REPORT_SIZE("/layout/frames/" # classname, size, \
"Memory used by frames of " \
"type " #classname " within a window."); \
} \
aWindowTotalSizes->mArenaSizes.NS_ARENA_SIZES_FIELD(classname) += size; \
}
#define ABSTRACT_FRAME_ID(...)
#include "nsFrameIdList.h"
@ -454,6 +452,50 @@ CollectWindowReports(nsGlobalWindow *aWindow,
"to be shown individually.");
}
// There are many different kinds of style structs, but it is likely that
// only a few matter. Implement a cutoff so we don't bloat about:memory with
// many uninteresting entries.
const size_t STYLE_SUNDRIES_THRESHOLD =
js::MemoryReportingSundriesThreshold();
size_t styleSundriesSize = 0;
#define STYLE_STRUCT(name_, cb_) \
{ \
size_t size = windowSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_); \
if (size < STYLE_SUNDRIES_THRESHOLD) { \
styleSundriesSize += size; \
} else { \
REPORT_SIZE("/layout/servo-style-structs/" # name_, size, \
"Memory used by the " #name_ " Servo style structs " \
"within a window."); \
} \
aWindowTotalSizes->mStyleSizes.NS_STYLE_SIZES_FIELD(name_) += size; \
}
#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
if (styleSundriesSize > 0) {
REPORT_SIZE("/layout/servo-style-structs/sundries", styleSundriesSize,
"The sum of all memory used by Servo style structs which were "
"too small to be shown individually.");
}
REPORT_SIZE("/layout/computed-values/dom",
windowSizes.mStyleSizes.mComputedValuesDom,
"Memory used by ComputedValues objects accessible from DOM "
"elements.");
aWindowTotalSizes->mStyleSizes.mComputedValuesDom +=
windowSizes.mStyleSizes.mComputedValuesDom;
REPORT_SIZE("/layout/computed-values/non-dom",
windowSizes.mStyleSizes.mComputedValuesNonDom,
"Memory used by ComputedValues objects not accessible from DOM "
"elements.");
aWindowTotalSizes->mStyleSizes.mComputedValuesNonDom +=
windowSizes.mStyleSizes.mComputedValuesNonDom;
#undef REPORT_SIZE
#undef REPORT_COUNT
}
@ -575,9 +617,9 @@ nsWindowMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
windowTotalSizes.mArenaSizes.mStyleContexts,
"This is the sum of all windows' 'layout/style-contexts' numbers.");
REPORT("window-objects/layout/style-structs",
REPORT("window-objects/layout/gecko-style-structs",
windowTotalSizes.mArenaSizes.mStyleStructs,
"This is the sum of all windows' 'layout/style-structs' numbers.");
"This is the sum of all windows' 'layout/gecko-style-structs' numbers.");
REPORT("window-objects/layout/style-sets", windowTotalSizes.mLayoutStyleSetsSize,
"This is the sum of all windows' 'layout/style-sets' numbers.");
@ -603,6 +645,24 @@ nsWindowMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
"Memory used for layout frames within windows. "
"This is the sum of all windows' 'layout/frames/' numbers.");
size_t styleTotal = 0;
#define STYLE_STRUCT(name_, cb_) \
styleTotal += windowTotalSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_);
#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
REPORT("window-objects/layout/servo-style-structs", styleTotal,
"Memory used for style structs within windows. This is the sum of "
"all windows' 'layout/servo-style-structs/' numbers.");
REPORT("window-objects/layout/computed-values",
windowTotalSizes.mStyleSizes.mComputedValuesDom +
windowTotalSizes.mStyleSizes.mComputedValuesNonDom,
"This is the sum of all windows' 'layout/computed-values/' "
"numbers.");
#undef REPORT
return NS_OK;

View File

@ -36,6 +36,11 @@ public:
size_t mOther;
};
#define ZERO_SIZE(kind, mSize) mSize(0),
#define ADD_TO_TAB_SIZES(kind, mSize) aSizes->add(nsTabSizes::kind, mSize);
#define ADD_TO_TOTAL_SIZE(kind, mSize) total += mSize;
#define DECL_SIZE(kind, mSize) size_t mSize;
#define NS_ARENA_SIZES_FIELD(classname) mArena##classname
struct nsArenaSizes {
@ -47,24 +52,24 @@ struct nsArenaSizes {
nsArenaSizes()
:
#define ZERO_SIZE(kind, mSize) mSize(0),
FOR_EACH_SIZE(ZERO_SIZE)
#undef ZERO_SIZE
#define FRAME_ID(classname, ...) NS_ARENA_SIZES_FIELD(classname)(),
#define FRAME_ID(classname, ...) \
NS_ARENA_SIZES_FIELD(classname)(0),
#define ABSTRACT_FRAME_ID(...)
#include "nsFrameIdList.h"
#undef FRAME_ID
#undef ABSTRACT_FRAME_ID
dummy()
{}
void addToTabSizes(nsTabSizes *sizes) const
void addToTabSizes(nsTabSizes* aSizes) const
{
#define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
#undef ADD_TO_TAB_SIZES
#define FRAME_ID(classname, ...) \
sizes->add(nsTabSizes::Other, NS_ARENA_SIZES_FIELD(classname));
aSizes->add(nsTabSizes::Other, NS_ARENA_SIZES_FIELD(classname));
#define ABSTRACT_FRAME_ID(...)
#include "nsFrameIdList.h"
#undef FRAME_ID
@ -74,28 +79,95 @@ struct nsArenaSizes {
size_t getTotalSize() const
{
size_t total = 0;
#define ADD_TO_TOTAL_SIZE(kind, mSize) total += mSize;
FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
#undef ADD_TO_TOTAL_SIZE
#define FRAME_ID(classname, ...) \
total += NS_ARENA_SIZES_FIELD(classname);
#define ABSTRACT_FRAME_ID(...)
#include "nsFrameIdList.h"
#undef FRAME_ID
#undef ABSTRACT_FRAME_ID
return total;
}
#define DECL_SIZE(kind, mSize) size_t mSize;
FOR_EACH_SIZE(DECL_SIZE)
#undef DECL_SIZE
#define FRAME_ID(classname, ...) size_t NS_ARENA_SIZES_FIELD(classname);
#define FRAME_ID(classname, ...) \
size_t NS_ARENA_SIZES_FIELD(classname);
#define ABSTRACT_FRAME_ID(...)
#include "nsFrameIdList.h"
#undef FRAME_ID
#undef ABSTRACT_FRAME_ID
int dummy; // present just to absorb the trailing comma from FRAME_ID in the
// constructor
// Present just to absorb the trailing comma in the constructor.
int dummy;
#undef FOR_EACH_SIZE
};
#define NS_STYLE_SIZES_FIELD(name_) mStyle##name_
struct nsStyleSizes
{
#define FOR_EACH_SIZE(macro) \
macro(Style, mComputedValuesDom) \
macro(Style, mComputedValuesNonDom)
nsStyleSizes()
:
FOR_EACH_SIZE(ZERO_SIZE)
#define STYLE_STRUCT(name_, cb_) \
NS_STYLE_SIZES_FIELD(name_)(0),
#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
dummy()
{}
void addToTabSizes(nsTabSizes* aSizes) const
{
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
#define STYLE_STRUCT(name_, cb_) \
aSizes->add(nsTabSizes::Style, NS_STYLE_SIZES_FIELD(name_));
#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
}
size_t getTotalSize() const
{
size_t total = 0;
FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
#define STYLE_STRUCT(name_, cb_) \
total += NS_STYLE_SIZES_FIELD(name_);
#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
return total;
}
FOR_EACH_SIZE(DECL_SIZE)
#define STYLE_STRUCT(name_, cb_) \
size_t NS_STYLE_SIZES_FIELD(name_);
#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
// Present just to absorb the trailing comma in the constructor.
int dummy;
#undef FOR_EACH_SIZE
};
@ -122,43 +194,50 @@ class nsWindowSizes
public:
explicit nsWindowSizes(mozilla::SizeOfState& aState)
:
#define ZERO_SIZE(kind, mSize) mSize(0),
FOR_EACH_SIZE(ZERO_SIZE)
#undef ZERO_SIZE
mDOMEventTargetsCount(0),
mDOMEventListenersCount(0),
mArenaSizes(),
mStyleSizes(),
mState(aState)
{}
void addToTabSizes(nsTabSizes *sizes) const {
#define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
void addToTabSizes(nsTabSizes* aSizes) const {
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
#undef ADD_TO_TAB_SIZES
mArenaSizes.addToTabSizes(sizes);
mArenaSizes.addToTabSizes(aSizes);
mStyleSizes.addToTabSizes(aSizes);
}
size_t getTotalSize() const
{
size_t total = 0;
#define ADD_TO_TOTAL_SIZE(kind, mSize) total += mSize;
FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
#undef ADD_TO_TOTAL_SIZE
total += mArenaSizes.getTotalSize();
total += mStyleSizes.getTotalSize();
return total;
}
#define DECL_SIZE(kind, mSize) size_t mSize;
FOR_EACH_SIZE(DECL_SIZE);
#undef DECL_SIZE
uint32_t mDOMEventTargetsCount;
uint32_t mDOMEventListenersCount;
nsArenaSizes mArenaSizes;
// This is Stylo-only because in Gecko these style structs are stored in the
// nsPresArena, and so are measured as part of that.
nsStyleSizes mStyleSizes;
mozilla::SizeOfState& mState;
#undef FOR_EACH_SIZE
};
#undef ZERO_SIZE
#undef ADD_TO_TAB_SIZES
#undef ADD_TO_TOTAL_SIZE
#undef DECL_SIZE
#endif // nsWindowSizes_h

View File

@ -405,11 +405,13 @@ HTMLAnchorElement::IntrinsicState() const
return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
}
size_t
HTMLAnchorElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
void
HTMLAnchorElement::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
Link::SizeOfExcludingThis(aState);
nsGenericHTMLElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
*aNodeSize += Link::SizeOfExcludingThis(aState);
}
} // namespace dom

View File

@ -48,8 +48,7 @@ public:
// nsIDOMHTMLAnchorElement
NS_DECL_NSIDOMHTMLANCHORELEMENT
// DOM memory reporter participant
NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,

View File

@ -219,11 +219,13 @@ HTMLAreaElement::IntrinsicState() const
return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
}
size_t
HTMLAreaElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
void
HTMLAreaElement::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
Link::SizeOfExcludingThis(aState);
nsGenericHTMLElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
*aNodeSize += Link::SizeOfExcludingThis(aState);
}
JSObject*

View File

@ -36,8 +36,7 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLAreaElement,
nsGenericHTMLElement)
// DOM memory reporter participant
NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS
virtual int32_t TabIndexDefault() override;

View File

@ -508,11 +508,13 @@ HTMLLinkElement::IntrinsicState() const
return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
}
size_t
HTMLLinkElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
void
HTMLLinkElement::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
Link::SizeOfExcludingThis(aState);
nsGenericHTMLElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
*aNodeSize += Link::SizeOfExcludingThis(aState);
}
JSObject*

View File

@ -36,8 +36,7 @@ public:
// nsIDOMHTMLLinkElement
NS_DECL_NSIDOMHTMLLINKELEMENT
// DOM memory reporter participant
NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS
void LinkAdded();
void LinkRemoved();

View File

@ -406,10 +406,11 @@ AudioDestinationNode::DestroyMediaStream()
void
AudioDestinationNode::NotifyMainThreadStreamFinished()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mStream->IsFinished());
if (mIsOffline) {
NS_DispatchToCurrentThread(
AbstractMainThread()->Dispatch(
NewRunnableMethod("dom::AudioDestinationNode::FireOfflineCompletionEvent",
this,
&AudioDestinationNode::FireOfflineCompletionEvent));

View File

@ -45,11 +45,13 @@ SVGPathElement::SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeIn
//----------------------------------------------------------------------
// memory reporting methods
size_t
SVGPathElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
void
SVGPathElement::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
return SVGPathElementBase::SizeOfExcludingThis(aState) +
mD.SizeOfExcludingThis(aState.mMallocSizeOf);
SVGPathElementBase::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
*aNodeSize += mD.SizeOfExcludingThis(aState.mMallocSizeOf);
}
//----------------------------------------------------------------------

View File

@ -35,8 +35,7 @@ protected:
explicit SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
public:
// DOM memory reporter participant
NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS
// nsIContent interface
NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override;

View File

@ -2121,16 +2121,6 @@ MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
namespace xpc {
static size_t
SizeOfTreeIncludingThis(nsINode* tree, SizeOfState& aState)
{
size_t n = tree->SizeOfIncludingThis(aState);
for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
n += child->SizeOfIncludingThis(aState);
return n;
}
class OrphanReporter : public JS::ObjectPrivateVisitor
{
public:
@ -2154,12 +2144,28 @@ class OrphanReporter : public JS::ObjectPrivateVisitor
// and then record its root so we don't measure it again.
nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
if (orphanTree && !mState.HaveSeenPtr(orphanTree.get())) {
n += SizeOfTreeIncludingThis(orphanTree, mState);
n += SizeOfTreeIncludingThis(orphanTree);
}
}
return n;
}
size_t SizeOfTreeIncludingThis(nsINode* tree)
{
size_t nodeSize = 0;
nsStyleSizes sizes;
tree->AddSizeOfIncludingThis(mState, sizes, &nodeSize);
for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
child->AddSizeOfIncludingThis(mState, sizes, &nodeSize);
// We combine the node size with nsStyleSizes here. It's not ideal, but
// it's hard to get the style structs measurements out to
// nsWindowMemoryReporter. Also, we drop mServoData in
// UnbindFromTree(), so in theory any non-in-tree element won't have
// any style data to measure.
return nodeSize + sizes.getTotalSize();
}
private:
SizeOfState mState;
};

View File

@ -11045,8 +11045,7 @@ PresShell::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
if (rootFrame) {
aSizes.mLayoutFramePropertiesSize +=
rootFrame->SizeOfFramePropertiesForTree(mallocSizeOf);
rootFrame->AddSizeOfExcludingThisForTree(aSizes);
}
}

View File

@ -10701,22 +10701,29 @@ nsFrame::HasCSSTransitions()
return collection && collection->mAnimations.Length() > 0;
}
size_t
nsIFrame::SizeOfFramePropertiesForTree(MallocSizeOf aMallocSizeOf) const
void
nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const
{
size_t result = 0;
aSizes.mLayoutFramePropertiesSize +=
mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
result += mProperties.SizeOfExcludingThis(aMallocSizeOf);
// We don't do this for Gecko because this stuff is stored in the nsPresArena
// and so measured elsewhere.
if (mStyleContext->IsServo()) {
ServoStyleContext* sc = mStyleContext->AsServo();
if (!aSizes.mState.HaveSeenPtr(sc)) {
sc->AddSizeOfIncludingThis(aSizes.mState, aSizes.mStyleSizes,
/* isDOM = */ false);
}
}
FrameChildListIterator iter(this);
while (!iter.IsDone()) {
for (const nsIFrame* f : iter.CurrentList()) {
result += f->SizeOfFramePropertiesForTree(aMallocSizeOf);
f->AddSizeOfExcludingThisForTree(aSizes);
}
iter.Next();
}
return result;
}
// Box layout debugging

View File

@ -88,6 +88,7 @@ class nsIContent;
class nsContainerFrame;
class nsPlaceholderFrame;
class nsStyleChangeList;
class nsWindowSizes;
struct nsPeekOffsetStruct;
struct nsPoint;
@ -3541,8 +3542,10 @@ public:
mProperties.DeleteAll(this);
}
// Reports size of the FrameProperties for this frame and its descendants
size_t SizeOfFramePropertiesForTree(mozilla::MallocSizeOf aMallocSizeOf) const;
// nsIFrames themselves are in the nsPresArena, and so are not measured here.
// Instead, this measures heap-allocated things hanging off the nsIFrame, and
// likewise for its descendants.
void AddSizeOfExcludingThisForTree(nsWindowSizes& aWindowSizes) const;
/**
* Return true if and only if this frame obeys visibility:hidden.

View File

@ -20,8 +20,19 @@
// Element data
SERVO_BINDING_FUNC(Servo_Element_ClearData, void, RawGeckoElementBorrowed node)
SERVO_BINDING_FUNC(Servo_Element_SizeOfExcludingThis, size_t, mozilla::MallocSizeOf,
mozilla::SeenPtrs* seen_ptrs, RawGeckoElementBorrowed node)
SERVO_BINDING_FUNC(Servo_Element_SizeOfExcludingThisAndCVs, size_t,
mozilla::MallocSizeOf, mozilla::SeenPtrs* seen_ptrs,
RawGeckoElementBorrowed node)
SERVO_BINDING_FUNC(Servo_Element_HasPrimaryComputedValues, bool,
RawGeckoElementBorrowed node)
SERVO_BINDING_FUNC(Servo_Element_GetPrimaryComputedValues,
ServoStyleContextStrong,
RawGeckoElementBorrowed node)
SERVO_BINDING_FUNC(Servo_Element_HasPseudoComputedValues, bool,
RawGeckoElementBorrowed node, size_t index)
SERVO_BINDING_FUNC(Servo_Element_GetPseudoComputedValues,
ServoStyleContextStrong,
RawGeckoElementBorrowed node, size_t index)
// Styleset and Stylesheet management
SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetContentsStrong,

View File

@ -235,6 +235,46 @@ ServoComputedData::GetStyleVariables() const
"called");
}
MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleStructsMallocSizeOf)
void
ServoComputedData::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes) const
{
// XXX WARNING: GetStyleFoo() returns an nsStyleFoo pointer. This nsStyleFoo
// sits within a servo_arc::Arc, i.e. it is preceded by a word-sized
// refcount. So this pointer is an interior pointer. To get the start address
// of the heap block we move the pointer back by one word. For this to work,
// two things must be true.
//
// - The layout of servo_arc::Arc must stay the same.
//
// - The alignment of each nsStyleFoo must not be greater than the size of a
// word (otherwise padding might be inserted between the refcount and the
// struct in the servo_arc::Arc).
//
// In the long run a better solution here is for mozjemalloc to provide a
// function that converts an interior pointer to a start pointer (bug
// 1389305), but that's not available right now.
//
// Also, we use ServoStyleStructsMallocSizeOf rather than
// |aState.mMallocSizeOf| to better distinguish in DMD's output the memory
// measured here.
#define STYLE_STRUCT(name_, cb_) \
static_assert(alignof(nsStyle##name_) <= sizeof(size_t), \
"alignment will break AddSizeOfExcludingThis()"); \
const char* p##name_ = reinterpret_cast<const char*>(GetStyle##name_()); \
p##name_ -= sizeof(size_t); \
if (!aState.HaveSeenPtr(p##name_)) { \
aSizes.NS_STYLE_SIZES_FIELD(name_) += \
ServoStyleStructsMallocSizeOf(p##name_); \
}
#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
}
void
Gecko_ServoStyleContext_Destroy(ServoStyleContext* aContext)
{

View File

@ -7,7 +7,10 @@
#ifndef mozilla_ServoStyleContext_h
#define mozilla_ServoStyleContext_h
#include "nsIMemoryReporter.h"
#include "nsStyleContext.h"
#include "nsWindowSizes.h"
#include <algorithm>
namespace mozilla {
@ -15,6 +18,8 @@ namespace dom {
class Element;
} // namespace dom
MOZ_DEFINE_MALLOC_SIZE_OF(ServoComputedValuesMallocSizeOf)
class ServoStyleContext final : public nsStyleContext
{
public:
@ -95,6 +100,29 @@ public:
*/
inline void ResolveSameStructsAs(const ServoStyleContext* aOther);
void AddSizeOfIncludingThis(SizeOfState& aState, nsStyleSizes& aSizes,
bool aIsDOM) const
{
// XXX WARNING: similar to ServoComputedData::AddSizeOfExcludingThis(),
// but here we need to step back 4 or 8 bytes to get past the servo_arc::Arc
// refcount to the base pointer.
static_assert(alignof(ServoStyleContext) == 4 ||
alignof(ServoStyleContext) == 8,
"alignment will break AddSizeOfExcludingThis()");
const char* p = reinterpret_cast<const char*>(this);
p -= std::max(sizeof(size_t), alignof(ServoStyleContext));
// We use ServoComputedValuesMallocSizeOf rather than
// |aState.mMallocSizeOf| to better distinguish in DMD's output the memory
// measured here.
if (aIsDOM) {
aSizes.mComputedValuesDom += ServoComputedValuesMallocSizeOf(p);
} else {
aSizes.mComputedValuesNonDom += ServoComputedValuesMallocSizeOf(p);
}
mSource.AddSizeOfExcludingThis(aState, aSizes);
}
private:
nsPresContext* mPresContext;
ServoComputedData mSource;

View File

@ -18,9 +18,12 @@
* so don't add significant include dependencies to this file.
*/
struct nsStyleSizes;
struct ServoNodeData;
namespace mozilla {
class SizeOfState;
/*
* Replaced types. These get mapped to associated Servo types in bindgen.
*/
@ -230,6 +233,9 @@ public:
#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
const nsStyleVariables* GetStyleVariables() const;
void AddSizeOfExcludingThis(mozilla::SizeOfState& aState,
nsStyleSizes& aSizes) const;
private:
mozilla::ServoCustomPropertiesMap custom_properties;
mozilla::ServoWritingMode writing_mode;

View File

@ -87,6 +87,9 @@ public:
static bool IsCSS2PseudoElement(nsIAtom *aAtom);
// This must match EAGER_PSEUDO_COUNT in Rust code.
static const size_t kEagerPseudoCount = 4;
static bool IsEagerlyCascadedInServo(const Type aType)
{
return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_CSS2);

View File

@ -74,7 +74,6 @@ var skippedProperties = {
"box-shadow": true, // Bug 1387973
"caret-color": true, // Bug 1387951
"clip": true, // Bug 1387951
"clip-path": true, // Bug 1387952
"column-count": true, // Bug 1387939
"-moz-image-region": true, // Bug 1387951
"order": true, // Bug 1387939
@ -687,11 +686,15 @@ var clipPathTests = [
// none to shape
{ start: "none",
end: "circle(500px at 500px 500px) border-box",
expected: ["circle", ["500px at calc(500px + 0%) calc(500px + 0%)"], "border-box"]
expected:
isServo ? ["circle", ["500px at 500px 500px"], "border-box"]
: ["circle", ["500px at calc(500px + 0%) calc(500px + 0%)"], "border-box"]
},
{ start: "none",
end: "ellipse(500px 500px at 500px 500px) border-box",
expected: ["ellipse", ["500px 500px at calc(500px + 0%) calc(500px + 0%)"], "border-box"]
expected:
isServo ? ["ellipse", ["500px 500px at 500px 500px"], "border-box"]
: ["ellipse", ["500px 500px at calc(500px + 0%) calc(500px + 0%)"], "border-box"]
},
{ start: "none",
end: "polygon(evenodd, 500px 500px, 500px 500px) border-box",
@ -708,11 +711,15 @@ var clipPathTests = [
expected: ["ellipse", ["200px 200px at 50% 50%"]] },
{ start: "circle(100px at 100px 100px) border-box",
end: "circle(500px at 500px 500px) border-box",
expected: ["circle", ["200px at calc(200px + 0%) calc(200px + 0%)"], "border-box"]
expected:
isServo ? ["circle", ["200px at 200px 200px"], "border-box"]
: ["circle", ["200px at calc(200px + 0%) calc(200px + 0%)"], "border-box"]
},
{ start: "ellipse(100px 100px at 100px 100px) border-box",
end: "ellipse(500px 500px at 500px 500px) border-box",
expected: ["ellipse", ["200px 200px at calc(200px + 0%) calc(200px + 0%)"], "border-box"]
expected:
isServo ? ["ellipse", ["200px 200px at 200px 200px"], "border-box"]
: ["ellipse", ["200px 200px at calc(200px + 0%) calc(200px + 0%)"], "border-box"]
},
{ start: "polygon(evenodd, 100px 100px, 100px 100px) border-box",
end: "polygon(evenodd, 500px 500px, 500px 500px) border-box",
@ -783,11 +790,15 @@ var clipPathTests = [
// mismatching boxes
{ start: "circle(100px at 100px 100px) border-box",
end: "circle(500px at 500px 500px) content-box",
expected: ["circle", ["500px at calc(500px + 0%) calc(500px + 0%)"], "content-box"]
expected:
isServo ? ["circle", ["500px at 500px 500px"], "content-box"]
: ["circle", ["500px at calc(500px + 0%) calc(500px + 0%)"], "content-box"]
},
{ start: "ellipse(100px 100px at 100px 100px) border-box",
end: "ellipse(500px 500px at 500px 500px) content-box",
expected: ["ellipse", ["500px 500px at calc(500px + 0%) calc(500px + 0%)"], "content-box"]
expected:
isServo ? ["ellipse", ["500px 500px at 500px 500px"], "content-box"]
: ["ellipse", ["500px 500px at calc(500px + 0%) calc(500px + 0%)"], "content-box"]
},
{ start: "polygon(evenodd, 100px 100px, 100px 100px) border-box",
end: "polygon(evenodd, 500px 500px, 500px 500px) content-box",
@ -800,7 +811,9 @@ var clipPathTests = [
// mismatching functions
{ start: "circle(100px at 100px 100px) border-box",
end: "ellipse(500px 500px at 500px 500px) border-box",
expected: ["ellipse", ["500px 500px at calc(500px + 0%) calc(500px + 0%)"], "border-box"]
expected:
isServo ? ["ellipse", ["500px 500px at 500px 500px"], "border-box"]
: ["ellipse", ["500px 500px at calc(500px + 0%) calc(500px + 0%)"], "border-box"]
},
{ start: "inset(0px round 20px)", end: "ellipse(500px 500px)",
expected: ["ellipse", ["500px 500px at 50% 50%"]]

View File

@ -1677,7 +1677,9 @@ ReportHelper(const void* aPtr, bool aReportedOnAlloc)
} else {
// We have no record of the block. It must be a bogus pointer. This should
// be extremely rare because Report() is almost always called in
// conjunction with a malloc_size_of-style function.
// conjunction with a malloc_size_of-style function. Print a message so
// that we get some feedback.
StatusMsg("Unknown pointer %p\n", aPtr);
}
}

View File

@ -404,6 +404,26 @@ getUserMedia.blockedCameraAccess = Camera has been blocked.
getUserMedia.blockedMicrophoneAccess = Microphone has been blocked.
getUserMedia.blockedCameraAndMicrophoneAccess = Camera and microphone have been blocked.
# LOCALIZATION NOTE (userContextPersonal.label,
# userContextWork.label,
# userContextShopping.label,
# userContextBanking.label,
# userContextNone.label):
# These strings specify the four predefined contexts included in support of the
# Contextual Identity / Containers project. Each context is meant to represent
# the context that the user is in when interacting with the site. Different
# contexts will store cookies and other information from those sites in
# different, isolated locations. You can enable the feature by typing
# about:config in the URL bar and changing privacy.userContext.enabled to true.
# Once enabled, you can open a new tab in a specific context by clicking
# File > New Container Tab > (1 of 4 contexts). Once opened, you will see these
# strings on the right-hand side of the URL bar.
# In android this will be only exposed by web extensions
userContextPersonal.label = Personal
userContextWork.label = Work
userContextBanking.label = Banking
userContextShopping.label = Shopping
# LOCALIZATION NOTE (readerMode.toolbarTip):
# Tip shown to users the first time we hide the reader mode toolbar.
readerMode.toolbarTip=Tap the screen to show reader options

View File

@ -16,7 +16,7 @@ use shared_lock::StylesheetGuards;
use std::fmt;
use std::ops::{Deref, DerefMut};
#[cfg(feature = "gecko")]
use stylesheets::{MallocSizeOfWithRepeats, SizeOfState};
use stylesheets::SizeOfState;
bitflags! {
flags RestyleFlags: u8 {
@ -261,6 +261,17 @@ impl ElementStyles {
pub fn is_display_none(&self) -> bool {
self.primary().get_box().clone_display() == display::T::none
}
#[cfg(feature = "gecko")]
fn malloc_size_of_children_excluding_cvs(&self, _state: &mut SizeOfState) -> usize {
// As the method name suggests, we don't measures the ComputedValues
// here, because they are measured on the C++ side.
// XXX: measure the EagerPseudoArray itself, but not the ComputedValues
// within it.
0
}
}
// We manually implement Debug for ElementStyles so that we can avoid the
@ -273,20 +284,6 @@ impl fmt::Debug for ElementStyles {
}
}
#[cfg(feature = "gecko")]
impl MallocSizeOfWithRepeats for ElementStyles {
fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize {
let mut n = 0;
if let Some(ref primary) = self.primary {
n += primary.malloc_size_of_children(state)
};
// We may measure more fields in the future if DMD says it's worth it.
n
}
}
/// Style system data associated with an Element.
///
/// In Gecko, this hangs directly off the Element. Servo, this is embedded
@ -436,12 +433,11 @@ impl ElementData {
pub fn clear_restyle_flags_and_damage(&mut self) {
self.restyle.clear_restyle_flags_and_damage();
}
}
#[cfg(feature = "gecko")]
impl MallocSizeOfWithRepeats for ElementData {
fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize {
let n = self.styles.malloc_size_of_children(state);
/// Measures memory usage.
#[cfg(feature = "gecko")]
pub fn malloc_size_of_children_excluding_cvs(&self, state: &mut SizeOfState) -> usize {
let n = self.styles.malloc_size_of_children_excluding_cvs(state);
// We may measure more fields in the future if DMD says it's worth it.

View File

@ -1915,11 +1915,26 @@ extern "C" {
pub fn Servo_Element_ClearData(node: RawGeckoElementBorrowed);
}
extern "C" {
pub fn Servo_Element_SizeOfExcludingThis(arg1: MallocSizeOf,
seen_ptrs: *mut SeenPtrs,
node: RawGeckoElementBorrowed)
pub fn Servo_Element_SizeOfExcludingThisAndCVs(malloc_size_of: MallocSizeOf,
seen_ptrs: *mut SeenPtrs,
node: RawGeckoElementBorrowed)
-> usize;
}
extern "C" {
pub fn Servo_Element_HasPrimaryComputedValues(element: RawGeckoElementBorrowed) -> bool;
}
extern "C" {
pub fn Servo_Element_GetPrimaryComputedValues(element: RawGeckoElementBorrowed)
-> ServoStyleContextStrong;
}
extern "C" {
pub fn Servo_Element_HasPseudoComputedValues(element: RawGeckoElementBorrowed, index: usize)
-> bool;
}
extern "C" {
pub fn Servo_Element_GetPseudoComputedValues(element: RawGeckoElementBorrowed, index: usize)
-> ServoStyleContextStrong;
}
extern "C" {
pub fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader,
gecko_stylesheet:

View File

@ -84,7 +84,7 @@ macro_rules! define_keyword_type {
($name: ident, $css: expr) => {
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, PartialEq, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)]
pub struct $name;
impl $crate::properties::animated_properties::Animatable for $name {

View File

@ -60,7 +60,6 @@ use selector_parser::PseudoElement;
use servo_arc::{Arc, RawOffsetArc};
use std::mem::{forget, uninitialized, transmute, zeroed};
use std::{cmp, ops, ptr};
use stylesheets::{MallocSizeOfWithRepeats, SizeOfState};
use values::{self, Auto, CustomIdent, Either, KeyframesName};
use values::computed::{NonNegativeAu, ToComputedValue, Percentage};
use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
@ -369,18 +368,6 @@ impl ComputedValuesInner {
}
}
impl MallocSizeOfWithRepeats for ComputedValues {
fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize {
let mut n = 0;
if let Some(ref raw_offset_arc) = *self.get_raw_visited_style() {
n += raw_offset_arc.with_arc(|a: &Arc<ComputedValues>| {
a.malloc_size_of_children(state)
})
}
n
}
}
<%def name="declare_style_struct(style_struct)">
pub use ::gecko_bindings::structs::mozilla::Gecko${style_struct.gecko_name} as ${style_struct.gecko_struct_name};
impl ${style_struct.gecko_struct_name} {

View File

@ -119,8 +119,11 @@
use values::computed::ComputedVecIter;
/// The computed value, effectively a list of single values.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq)]
% if need_animatable or animation_value_type == "ComputedValue":
#[derive(ComputeSquaredDistance)]
% endif
pub struct T(
% if allow_empty and allow_empty != "NotInitial":
pub Vec<single_value::T>,
@ -142,16 +145,6 @@
fn add(&self, other: &Self) -> Result<Self, ()> {
self.0.add(&other.0).map(T)
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_squared_distance(&other.0)
}
}
impl ToAnimatedZero for T {
@ -956,63 +949,6 @@
%>
</%def>
/// Macro for defining Animatable trait for tuple struct which has Option<T>,
/// e.g. struct T(pub Option<Au>).
<%def name="impl_animatable_for_option_tuple(value_for_none)">
impl Animatable for T {
#[inline]
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-> Result<Self, ()> {
match (self, other) {
(&T(Some(ref this)), &T(Some(ref other))) => {
Ok(T(this.add_weighted(other, self_portion, other_portion).ok()))
},
(&T(Some(ref this)), &T(None)) => {
Ok(T(this.add_weighted(&${value_for_none}, self_portion, other_portion).ok()))
},
(&T(None), &T(Some(ref other))) => {
Ok(T(${value_for_none}.add_weighted(other, self_portion, other_portion).ok()))
},
(&T(None), &T(None)) => {
Ok(T(None))
},
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&T(Some(ref this)), &T(Some(ref other))) => {
this.compute_distance(other)
},
(&T(Some(ref value)), &T(None)) |
(&T(None), &T(Some(ref value)))=> {
value.compute_distance(&${value_for_none})
},
(&T(None), &T(None)) => {
Ok(0.0)
},
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&T(Some(ref this)), &T(Some(ref other))) => {
this.compute_squared_distance(other)
},
(&T(Some(ref value)), &T(None)) |
(&T(None), &T(Some(ref value))) => {
value.compute_squared_distance(&${value_for_none})
},
(&T(None), &T(None)) => {
Ok(0.0)
},
}
}
}
</%def>
// Define property that supports prefixed intrinsic size keyword values for gecko.
// E.g. -moz-max-content, -moz-min-content, etc.
<%def name="gecko_size_type(name, length_type, initial_value, logical, **kwargs)">

View File

@ -49,6 +49,7 @@ use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToC
use values::computed::{NonNegativeAu, NonNegativeNumber, PositiveIntegerOrAuto};
use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal};
use values::computed::length::NonNegativeLengthOrPercentage;
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::{GreaterThanOrEqualToOne, NonNegative};
use values::generics::effects::Filter;
use values::generics::position as generic_position;
@ -83,16 +84,6 @@ pub trait Animatable: Sized {
fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
self.add_weighted(other, count as f64, 1.0)
}
/// Compute distance between a value and another for a given property.
fn compute_distance(&self, _other: &Self) -> Result<f64, ()> { Err(()) }
/// In order to compute the Euclidean distance of a list or property value with multiple
/// components, we need to compute squared distance for each element, so the vector can sum it
/// and then get its squared root as the distance.
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_distance(other).map(|d| d * d)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
@ -741,28 +732,31 @@ impl Animatable for AnimationValue {
}
}
}
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
impl ComputeSquaredDistance for AnimationValue {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
% for prop in data.longhands:
% if prop.animatable:
% if prop.animation_value_type != "discrete":
(&AnimationValue::${prop.camel_case}(ref from),
&AnimationValue::${prop.camel_case}(ref to)) => {
from.compute_distance(to)
},
% else:
(&AnimationValue::${prop.camel_case}(ref _from),
&AnimationValue::${prop.camel_case}(ref _to)) => {
Err(())
},
% endif
% endif
% if prop.animatable:
% if prop.animation_value_type != "discrete":
(&AnimationValue::${prop.camel_case}(ref this), &AnimationValue::${prop.camel_case}(ref other)) => {
this.compute_squared_distance(other)
},
% else:
(&AnimationValue::${prop.camel_case}(_), &AnimationValue::${prop.camel_case}(_)) => {
Err(())
},
% endif
% endif
% endfor
_ => {
panic!("Expected compute_distance of computed values of the same \
property, got: {:?}, {:?}", self, other);
}
panic!(
"computed values should be of the same property, got: {:?}, {:?}",
self,
other
);
},
}
}
}
@ -802,22 +796,21 @@ macro_rules! repeated_vec_impl {
me.add_weighted(you, self_portion, other_portion)
}).collect()
}
}
impl<T> ComputeSquaredDistance for $ty
where
T: ComputeSquaredDistance + RepeatableListAnimatable,
{
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
// If the length of either list is zero, the least common multiple is undefined.
if cmp::min(self.len(), other.len()) < 1 {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
if self.is_empty() || other.is_empty() {
return Err(());
}
use num_integer::lcm;
let len = lcm(self.len(), other.len());
self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| {
me.compute_squared_distance(you)
self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(this, other)| {
this.compute_squared_distance(other)
}).sum()
}
})*
@ -832,11 +825,6 @@ impl Animatable for Au {
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32))
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
}
impl <T> Animatable for Option<T>
@ -852,28 +840,6 @@ impl <T> Animatable for Option<T>
_ => Err(()),
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&Some(ref this), &Some(ref other)) => {
this.compute_distance(other)
},
(&None, &None) => Ok(0.0),
_ => Err(()),
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&Some(ref this), &Some(ref other)) => {
this.compute_squared_distance(other)
},
(&None, &None) => Ok(0.0),
_ => Err(()),
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
@ -882,11 +848,6 @@ impl Animatable for f32 {
fn add_weighted(&self, other: &f32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32)
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok((*self - *other).abs() as f64)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
@ -895,11 +856,6 @@ impl Animatable for f64 {
fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
Ok(*self * self_portion + *other * other_portion)
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok((*self - *other).abs())
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-integer
@ -908,11 +864,6 @@ impl Animatable for i32 {
fn add_weighted(&self, other: &i32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
Ok((*self as f64 * self_portion + *other as f64 * other_portion).round() as i32)
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok((*self - *other).abs() as f64)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
@ -934,13 +885,6 @@ impl Animatable for Angle {
}
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
// Use the formula for calculating the distance between angles defined in SVG:
// https://www.w3.org/TR/SVG/animate.html#complexDistances
Ok((self.radians64() - other.radians64()).abs())
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-percentage
@ -949,11 +893,6 @@ impl Animatable for Percentage {
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
Ok(Percentage((self.0 as f64 * self_portion + other.0 as f64 * other_portion) as f32))
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok((self.0 as f64 - other.0 as f64).abs())
}
}
impl ToAnimatedZero for Percentage {
@ -977,14 +916,12 @@ impl Animatable for Visibility {
_ => Err(()),
}
}
}
impl ComputeSquaredDistance for Visibility {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
if *self == *other {
Ok(0.0)
} else {
Ok(1.0)
}
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(SquaredDistance::Value(if *self == *other { 0. } else { 1. }))
}
}
@ -1003,16 +940,6 @@ impl<T: Animatable + Copy> Animatable for Size2D<T> {
Ok(Size2D::new(width, height))
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.compute_squared_distance(other)?.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.width.compute_squared_distance(&other.width)? + self.height.compute_squared_distance(&other.height)?)
}
}
impl<T: Animatable + Copy> Animatable for Point2D<T> {
@ -1044,15 +971,20 @@ impl Animatable for VerticalAlign {
_ => Err(()),
}
}
}
impl ComputeSquaredDistance for VerticalAlign {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(VerticalAlign::LengthOrPercentage(ref this),
VerticalAlign::LengthOrPercentage(ref other)) => {
this.compute_distance(other)
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&VerticalAlign::LengthOrPercentage(ref this), &VerticalAlign::LengthOrPercentage(ref other)) => {
this.compute_squared_distance(other)
},
_ => {
// FIXME(nox): Should this return `Ok(SquaredDistance::Value(0.))`
// if `self` and `other` are the same keyword value?
Err(())
},
_ => Err(()),
}
}
}
@ -1087,18 +1019,6 @@ impl Animatable for CalcLengthOrPercentage {
let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?;
Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode))
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sq| sq.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
let length_diff = (self.unclamped_length().0 - other.unclamped_length().0) as f64;
let percentage_diff = (self.percentage() - other.percentage()) as f64;
Ok(length_diff * length_diff + percentage_diff * percentage_diff)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
@ -1131,48 +1051,6 @@ impl Animatable for LengthOrPercentage {
}
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentage::Length(ref this),
LengthOrPercentage::Length(ref other)) => {
this.compute_distance(other)
},
(LengthOrPercentage::Percentage(ref this),
LengthOrPercentage::Percentage(ref other)) => {
this.compute_distance(other)
},
(this, other) => {
let this: CalcLengthOrPercentage = From::from(this);
let other: CalcLengthOrPercentage = From::from(other);
this.compute_distance(&other)
}
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentage::Length(ref this),
LengthOrPercentage::Length(ref other)) => {
let diff = (this.0 - other.0) as f64;
Ok(diff * diff)
},
(LengthOrPercentage::Percentage(ref this),
LengthOrPercentage::Percentage(ref other)) => {
let diff = this.0 as f64 - other.0 as f64;
Ok(diff * diff)
},
(this, other) => {
let this: CalcLengthOrPercentage = From::from(this);
let other: CalcLengthOrPercentage = From::from(other);
let length_diff = (this.unclamped_length().0 - other.unclamped_length().0) as f64;
let percentage_diff = (this.percentage() - other.percentage()) as f64;
Ok(length_diff * length_diff + percentage_diff * percentage_diff)
}
}
}
}
impl ToAnimatedZero for LengthOrPercentage {
@ -1210,53 +1088,6 @@ impl Animatable for LengthOrPercentageOrAuto {
}
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentageOrAuto::Length(ref this),
LengthOrPercentageOrAuto::Length(ref other)) => {
this.compute_distance(other)
},
(LengthOrPercentageOrAuto::Percentage(ref this),
LengthOrPercentageOrAuto::Percentage(ref other)) => {
this.compute_distance(other)
},
(this, other) => {
// If one of the element is Auto, Option<> will be None, and the returned distance is Err(())
let this: Option<CalcLengthOrPercentage> = From::from(this);
let other: Option<CalcLengthOrPercentage> = From::from(other);
this.compute_distance(&other)
}
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentageOrAuto::Length(ref this),
LengthOrPercentageOrAuto::Length(ref other)) => {
let diff = (this.0 - other.0) as f64;
Ok(diff * diff)
},
(LengthOrPercentageOrAuto::Percentage(ref this),
LengthOrPercentageOrAuto::Percentage(ref other)) => {
let diff = this.0 as f64 - other.0 as f64;
Ok(diff * diff)
},
(this, other) => {
let this: Option<CalcLengthOrPercentage> = From::from(this);
let other: Option<CalcLengthOrPercentage> = From::from(other);
if let (Some(this), Some(other)) = (this, other) {
let length_diff = (this.unclamped_length().0 - other.unclamped_length().0) as f64;
let percentage_diff = (this.percentage() - other.percentage()) as f64;
Ok(length_diff * length_diff + percentage_diff * percentage_diff)
} else {
Err(())
}
}
}
}
}
impl ToAnimatedZero for LengthOrPercentageOrAuto {
@ -1301,26 +1132,6 @@ impl Animatable for LengthOrPercentageOrNone {
},
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentageOrNone::Length(ref this),
LengthOrPercentageOrNone::Length(ref other)) => {
this.compute_distance(other)
},
(LengthOrPercentageOrNone::Percentage(ref this),
LengthOrPercentageOrNone::Percentage(ref other)) => {
this.compute_distance(other)
},
(this, other) => {
// If one of the element is Auto, Option<> will be None, and the returned distance is Err(())
let this = <Option<CalcLengthOrPercentage>>::from(this);
let other = <Option<CalcLengthOrPercentage>>::from(other);
this.compute_distance(&other)
},
}
}
}
impl ToAnimatedZero for LengthOrPercentageOrNone {
@ -1350,17 +1161,6 @@ impl Animatable for MozLength {
_ => Err(()),
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(MozLength::LengthOrPercentageOrAuto(ref this),
MozLength::LengthOrPercentageOrAuto(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
}
impl ToAnimatedZero for MozLength {
@ -1388,17 +1188,6 @@ impl Animatable for MaxLength {
_ => Err(()),
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(MaxLength::LengthOrPercentageOrNone(ref this),
MaxLength::LengthOrPercentageOrNone(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
}
impl ToAnimatedZero for MaxLength {
@ -1417,13 +1206,6 @@ impl Animatable for FontWeight {
let weight = (weight.max(100.).min(900.) / 100.).round() * 100.;
Ok(FontWeight(weight as u16))
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
let a = self.0 as f64;
let b = other.0 as f64;
a.compute_distance(&b)
}
}
impl ToAnimatedZero for FontWeight {
@ -1447,12 +1229,12 @@ impl Animatable for FontStretch {
Ok(result.into())
}
}
impl ComputeSquaredDistance for FontStretch {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
let from = f64::from(*self);
let to = f64::from(*other);
from.compute_distance(&to)
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
f64::from(*self).compute_squared_distance(&(*other).into())
}
}
@ -1500,17 +1282,6 @@ impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H,
vertical: self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?,
})
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.horizontal.compute_squared_distance(&other.horizontal)? +
self.vertical.compute_squared_distance(&other.vertical)?)
}
}
impl<H, V> ToAnimatedZero for generic_position::Position<H, V>
@ -1542,22 +1313,6 @@ impl Animatable for ClipRect {
left: self.left.add_weighted(&other.left, self_portion, other_portion)?,
})
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
let list = [
self.top.compute_distance(&other.top)?,
self.right.compute_distance(&other.right)?,
self.bottom.compute_distance(&other.bottom)?,
self.left.compute_distance(&other.left)?
];
Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff))
}
}
impl ToAnimatedZero for ClipRect {
@ -1719,11 +1474,17 @@ fn add_weighted_transform_lists(from_list: &[TransformOperation],
}
}
(&TransformOperation::Perspective(fd),
&TransformOperation::Perspective(_td)) => {
&TransformOperation::Perspective(td)) => {
let mut fd_matrix = ComputedMatrix::identity();
let mut td_matrix = ComputedMatrix::identity();
fd_matrix.m43 = -1. / fd.to_f32_px();
td_matrix.m43 = -1. / _td.to_f32_px();
if fd.0 > 0 {
fd_matrix.m34 = -1. / fd.to_f32_px();
}
if td.0 > 0 {
td_matrix.m34 = -1. / td.to_f32_px();
}
let sum = fd_matrix.add_weighted(&td_matrix, self_portion, other_portion)
.unwrap();
result.push(TransformOperation::Matrix(sum));
@ -2641,6 +2402,14 @@ impl Animatable for TransformList {
}
}
impl ComputeSquaredDistance for TransformList {
#[inline]
fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {
// FIXME: This should be implemented.
Err(())
}
}
impl ToAnimatedZero for TransformList {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
@ -2666,32 +2435,6 @@ impl<T, U> Animatable for Either<T, U>
}
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&Either::First(ref this), &Either::First(ref other)) => {
this.compute_distance(other)
},
(&Either::Second(ref this), &Either::Second(ref other)) => {
this.compute_distance(other)
},
_ => Err(())
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&Either::First(ref this), &Either::First(ref other)) => {
this.compute_squared_distance(other)
},
(&Either::Second(ref this), &Either::Second(ref other)) => {
this.compute_squared_distance(other)
},
_ => Err(())
}
}
}
impl<A, B> ToAnimatedZero for Either<A, B>
@ -2789,28 +2532,14 @@ impl Animatable for IntermediateRGBA {
Ok(IntermediateRGBA::new(red, green, blue, alpha))
}
}
}
impl ComputeSquaredDistance for IntermediateRGBA {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sq| sq.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
let start = [ self.alpha,
self.red * self.alpha,
self.green * self.alpha,
self.blue * self.alpha ];
let end = [ other.alpha,
other.red * other.alpha,
other.green * other.alpha,
other.blue * other.alpha ];
let diff = start.iter().zip(&end)
.fold(0.0f64, |n, (&a, &b)| {
let diff = (a - b) as f64;
n + diff * diff
});
Ok(diff)
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
let start = [ self.alpha, self.red * self.alpha, self.green * self.alpha, self.blue * self.alpha ];
let end = [ other.alpha, other.red * other.alpha, other.green * other.alpha, other.blue * other.alpha ];
start.iter().zip(&end).map(|(this, other)| this.compute_squared_distance(other)).sum()
}
}
@ -2930,31 +2659,35 @@ impl Animatable for IntermediateColor {
})
}
}
}
impl ComputeSquaredDistance for IntermediateColor {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sq| sq.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
// All comments in add_weighted also applies here.
if self.foreground_ratio == other.foreground_ratio {
if self.is_currentcolor() {
Ok(0.)
Ok(SquaredDistance::Value(0.))
} else {
self.color.compute_squared_distance(&other.color)
}
} else if self.is_currentcolor() && other.is_numeric() {
Ok(IntermediateRGBA::transparent().compute_squared_distance(&other.color)? + 1.)
Ok(
IntermediateRGBA::transparent().compute_squared_distance(&other.color)? +
SquaredDistance::Value(1.),
)
} else if self.is_numeric() && other.is_currentcolor() {
Ok(self.color.compute_squared_distance(&IntermediateRGBA::transparent())? + 1.)
Ok(
self.color.compute_squared_distance(&IntermediateRGBA::transparent())? +
SquaredDistance::Value(1.),
)
} else {
let self_color = self.effective_intermediate_rgba();
let other_color = other.effective_intermediate_rgba();
let dist = self_color.compute_squared_distance(&other_color)?;
let ratio_diff = (self.foreground_ratio - other.foreground_ratio) as f64;
Ok(dist + ratio_diff * ratio_diff)
Ok(
self_color.compute_squared_distance(&other_color)? +
self.foreground_ratio.compute_squared_distance(&other.foreground_ratio)?,
)
}
}
}
@ -2978,16 +2711,15 @@ impl Animatable for IntermediateSVGPaint {
fallback: self.fallback.add_weighted(&other.fallback, self_portion, other_portion)?,
})
}
}
impl ComputeSquaredDistance for IntermediateSVGPaint {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sq| sq.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.kind.compute_squared_distance(&other.kind)? +
self.fallback.compute_squared_distance(&other.fallback)?)
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(
self.kind.compute_squared_distance(&other.kind)? +
self.fallback.compute_squared_distance(&other.fallback)?,
)
}
}
@ -3016,16 +2748,20 @@ impl Animatable for IntermediateSVGPaintKind {
_ => Err(())
}
}
}
impl ComputeSquaredDistance for IntermediateSVGPaintKind {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&SVGPaintKind::Color(ref self_color), &SVGPaintKind::Color(ref other_color)) => {
self_color.compute_distance(other_color)
(&SVGPaintKind::Color(ref this), &SVGPaintKind::Color(ref other)) => {
this.compute_squared_distance(other)
}
(&SVGPaintKind::None, &SVGPaintKind::None) |
(&SVGPaintKind::ContextFill, &SVGPaintKind::ContextFill) |
(&SVGPaintKind::ContextStroke, &SVGPaintKind::ContextStroke)=> Ok(0.0),
(&SVGPaintKind::ContextStroke, &SVGPaintKind::ContextStroke) => {
Ok(SquaredDistance::Value(0.))
},
_ => Err(())
}
}
@ -3060,16 +2796,6 @@ impl<LengthType> Animatable for SVGLength<LengthType>
}
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => {
this.compute_distance(other)
}
_ => Err(())
}
}
}
impl<LengthType> ToAnimatedZero for SVGLength<LengthType> where LengthType : ToAnimatedZero {
@ -3097,16 +2823,6 @@ impl<LengthType> Animatable for SVGStrokeDashArray<LengthType>
}
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
this.compute_distance(other)
}
_ => Err(())
}
}
}
impl<LengthType> ToAnimatedZero for SVGStrokeDashArray<LengthType>
@ -3138,16 +2854,6 @@ impl<OpacityType> Animatable for SVGOpacity<OpacityType>
}
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => {
this.compute_distance(other)
}
_ => Err(())
}
}
}
impl<OpacityType> ToAnimatedZero for SVGOpacity<OpacityType>
@ -3246,9 +2952,7 @@ fn add_weighted_filter_function(from: Option<<&AnimatedFilter>,
}
}
fn compute_filter_square_distance(from: &AnimatedFilter,
to: &AnimatedFilter)
-> Result<f64, ()> {
fn compute_filter_square_distance(from: &AnimatedFilter, to: &AnimatedFilter) -> Result<SquaredDistance, ()> {
match (from, to) {
% for func in FILTER_FUNCTIONS :
(&Filter::${func}(f),
@ -3296,29 +3000,24 @@ impl Animatable for AnimatedFilterList {
fn add(&self, other: &Self) -> Result<Self, ()> {
Ok(AnimatedFilterList(self.0.iter().chain(other.0.iter()).cloned().collect()))
}
}
impl ComputeSquaredDistance for AnimatedFilterList {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
use itertools::{EitherOrBoth, Itertools};
let mut square_distance: f64 = 0.0;
for it in self.0.iter().zip_longest(other.0.iter()) {
square_distance += match it {
self.0.iter().zip_longest(other.0.iter()).map(|it| {
match it {
EitherOrBoth::Both(from, to) => {
compute_filter_square_distance(&from, &to)?
compute_filter_square_distance(&from, &to)
},
EitherOrBoth::Left(list) | EitherOrBoth::Right(list)=> {
let none = add_weighted_filter_function(Some(list), Some(list), 0.0, 0.0)?;
compute_filter_square_distance(&none, &list)?
compute_filter_square_distance(&none, &list)
},
};
}
Ok(square_distance)
}
}).sum()
}
}
@ -3382,11 +3081,6 @@ impl<T> Animatable for NonNegative<T>
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
self.0.add_weighted(&other.0, self_portion, other_portion).map(NonNegative::<T>)
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
}
impl<T> ToAnimatedZero for NonNegative<T>
@ -3405,11 +3099,6 @@ impl<T> Animatable for GreaterThanOrEqualToOne<T>
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
self.0.add_weighted(&other.0, self_portion, other_portion).map(GreaterThanOrEqualToOne::<T>)
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
}
impl<T> ToAnimatedZero for GreaterThanOrEqualToOne<T>

View File

@ -485,7 +485,7 @@ ${helpers.single_keyword_system("font-variant-caps",
///
/// However, system fonts may provide other values. Pango
/// may provide 350, 380, and 1000 (on top of the existing values), for example.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, Eq, Hash, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
pub struct T(pub u16);
@ -1118,6 +1118,7 @@ ${helpers.single_keyword_system("font-variant-caps",
use properties::animated_properties::Animatable;
use values::CSSFloat;
use values::animated::{ToAnimatedValue, ToAnimatedZero};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Copy, Clone, Debug, PartialEq, ToCss)]
@ -1145,12 +1146,13 @@ ${helpers.single_keyword_system("font-variant-caps",
_ => Err(()),
}
}
}
impl ComputeSquaredDistance for T {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(T::Number(ref number), T::Number(ref other)) =>
number.compute_distance(other),
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&T::Number(ref this), &T::Number(ref other)) => this.compute_squared_distance(other),
_ => Err(()),
}
}

View File

@ -31,7 +31,7 @@ ${helpers.single_keyword("caption-side", "top bottom",
use values::computed::NonNegativeAu;
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToCss)]
pub struct T {
pub horizontal: NonNegativeAu,
pub vertical: NonNegativeAu,
@ -49,17 +49,6 @@ ${helpers.single_keyword("caption-side", "top bottom",
self_portion, other_portion)?,
})
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.horizontal.compute_squared_distance(&other.horizontal)? +
self.vertical.compute_squared_distance(&other.vertical)?)
}
}
impl ToAnimatedZero for T {

View File

@ -14,6 +14,7 @@ use values::Impossible;
use values::animated::{ToAnimatedValue, ToAnimatedZero};
use values::computed::{Angle, NonNegativeNumber};
use values::computed::length::{Length, NonNegativeLength};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::effects::BoxShadow as GenericBoxShadow;
use values::generics::effects::Filter as GenericFilter;
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
@ -102,6 +103,14 @@ where
}
}
impl<S> ComputeSquaredDistance for ShadowList<S> {
#[inline]
fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {
// FIXME: This should be implemented.
Err(())
}
}
impl<S> ToAnimatedZero for ShadowList<S> {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
@ -140,14 +149,11 @@ impl Animatable for BoxShadow {
inset: self.inset,
})
}
}
impl ComputeSquaredDistance for BoxShadow {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
if self.inset != other.inset {
return Err(());
}
@ -219,21 +225,6 @@ impl Animatable for SimpleShadow {
blur: blur,
})
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(
self.color.compute_squared_distance(&other.color)? +
self.horizontal.compute_squared_distance(&other.horizontal)? +
self.vertical.compute_squared_distance(&other.vertical)? +
self.blur.compute_squared_distance(&other.blur)?
)
}
}
impl ToAnimatedZero for SimpleShadow {

View File

@ -30,27 +30,6 @@ impl Animatable for BackgroundSize {
_ => Err(()),
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(
&GenericBackgroundSize::Explicit { width: self_width, height: self_height },
&GenericBackgroundSize::Explicit { width: other_width, height: other_height },
) => {
Ok(
self_width.compute_squared_distance(&other_width)? +
self_height.compute_squared_distance(&other_height)?
)
}
_ => Err(()),
}
}
}
impl ToAnimatedZero for BackgroundSize {

View File

@ -12,6 +12,7 @@ use style_traits::values::specified::AllowedLengthType;
use super::{Number, ToComputedValue, Context, Percentage};
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
use values::computed::{NonNegativeAu, NonNegativeNumber};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::NonNegative;
use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
use values::specified::length::ViewportPercentageLength;
@ -71,6 +72,18 @@ pub struct CalcLengthOrPercentage {
pub percentage: Option<Percentage>,
}
impl ComputeSquaredDistance for CalcLengthOrPercentage {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
// FIXME(nox): This looks incorrect to me, to add a distance between lengths
// with a distance between percentages.
Ok(
self.unclamped_length().compute_squared_distance(&other.unclamped_length())? +
self.percentage().compute_squared_distance(&other.percentage())?,
)
}
}
impl CalcLengthOrPercentage {
/// Returns a new `CalcLengthOrPercentage`.
#[inline]
@ -257,6 +270,23 @@ pub enum LengthOrPercentage {
Calc(CalcLengthOrPercentage),
}
impl ComputeSquaredDistance for LengthOrPercentage {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&LengthOrPercentage::Length(ref this), &LengthOrPercentage::Length(ref other)) => {
this.compute_squared_distance(other)
},
(&LengthOrPercentage::Percentage(ref this), &LengthOrPercentage::Percentage(ref other)) => {
this.compute_squared_distance(other)
},
(this, other) => {
CalcLengthOrPercentage::compute_squared_distance(&(*this).into(), &(*other).into())
}
}
}
}
impl From<Au> for LengthOrPercentage {
#[inline]
fn from(length: Au) -> Self {
@ -382,6 +412,23 @@ pub enum LengthOrPercentageOrAuto {
Calc(CalcLengthOrPercentage),
}
impl ComputeSquaredDistance for LengthOrPercentageOrAuto {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&LengthOrPercentageOrAuto::Length(ref this), &LengthOrPercentageOrAuto::Length(ref other)) => {
this.compute_squared_distance(other)
},
(&LengthOrPercentageOrAuto::Percentage(ref this), &LengthOrPercentageOrAuto::Percentage(ref other)) => {
this.compute_squared_distance(other)
},
(this, other) => {
<Option<CalcLengthOrPercentage>>::compute_squared_distance(&(*this).into(), &(*other).into())
}
}
}
}
impl LengthOrPercentageOrAuto {
/// Returns true if the computed value is absolute 0 or 0%.
///
@ -460,6 +507,23 @@ pub enum LengthOrPercentageOrNone {
None,
}
impl ComputeSquaredDistance for LengthOrPercentageOrNone {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&LengthOrPercentageOrNone::Length(ref this), &LengthOrPercentageOrNone::Length(ref other)) => {
this.compute_squared_distance(other)
},
(&LengthOrPercentageOrNone::Percentage(ref this), &LengthOrPercentageOrNone::Percentage(ref other)) => {
this.compute_squared_distance(other)
},
(this, other) => {
<Option<CalcLengthOrPercentage>>::compute_squared_distance(&(*this).into(), &(*other).into())
}
}
}
}
impl LengthOrPercentageOrNone {
/// Returns the used value.
pub fn to_used_value(&self, containing_length: Au) -> Option<Au> {
@ -607,6 +671,22 @@ pub enum MozLength {
ExtremumLength(ExtremumLength),
}
impl ComputeSquaredDistance for MozLength {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&MozLength::LengthOrPercentageOrAuto(ref this), &MozLength::LengthOrPercentageOrAuto(ref other)) => {
this.compute_squared_distance(other)
},
_ => {
// FIXME(nox): Should this return `Ok(SquaredDistance::Value(1.))`
// when `self` and `other` are the same extremum value?
Err(())
},
}
}
}
impl MozLength {
/// Returns the `auto` value.
pub fn auto() -> Self {
@ -651,6 +731,22 @@ pub enum MaxLength {
ExtremumLength(ExtremumLength),
}
impl ComputeSquaredDistance for MaxLength {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(&MaxLength::LengthOrPercentageOrNone(ref this), &MaxLength::LengthOrPercentageOrNone(ref other)) => {
this.compute_squared_distance(other)
},
_ => {
// FIXME(nox): Should this return `Ok(SquaredDistance::Value(1.))`
// when `self` and `other` are the same extremum value?
Err(())
},
}
}
}
impl MaxLength {
/// Returns the `none` value.
pub fn none() -> Self {

View File

@ -22,6 +22,7 @@ use std::fmt;
use std::sync::Arc;
use style_traits::ToCss;
use super::{CSSFloat, CSSInteger};
use super::distance::{ComputeSquaredDistance, SquaredDistance};
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
@ -338,6 +339,15 @@ pub enum Angle {
Turn(CSSFloat),
}
impl ComputeSquaredDistance for Angle {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
// Use the formula for calculating the distance between angles defined in SVG:
// https://www.w3.org/TR/SVG/animate.html#complexDistances
self.radians64().compute_squared_distance(&other.radians64())
}
}
impl Angle {
/// Construct a computed `Angle` value from a radian amount.
pub fn from_radians(radians: CSSFloat) -> Self {
@ -533,9 +543,9 @@ pub type LengthOrPercentageOrNumber = Either<Number, LengthOrPercentage>;
/// NonNegativeLengthOrPercentage | NonNegativeNumber
pub type NonNegativeLengthOrPercentageOrNumber = Either<NonNegativeNumber, NonNegativeLengthOrPercentage>;
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, Eq, PartialEq)]
/// A computed cliprect for clip and image-region
pub struct ClipRect {
pub top: Option<Au>,
@ -649,7 +659,7 @@ impl From<Au> for NonNegativeAu {
}
/// A computed `<percentage>` value.
#[derive(Clone, Copy, Debug, Default, PartialEq, HasViewportPercentage)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, Default, HasViewportPercentage, PartialEq)]
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
pub struct Percentage(pub CSSFloat);

View File

@ -45,22 +45,6 @@ impl Animatable for LineHeight {
_ => Err(()),
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(GenericLineHeight::Length(ref this), GenericLineHeight::Length(ref other)) => {
this.compute_distance(other)
},
(GenericLineHeight::Number(ref this), GenericLineHeight::Number(ref other)) => {
this.compute_distance(other)
},
(GenericLineHeight::Normal, GenericLineHeight::Normal) => Ok(0.),
#[cfg(feature = "gecko")]
(GenericLineHeight::MozBlockHeight, GenericLineHeight::MozBlockHeight) => Ok(0.),
_ => Err(()),
}
}
}
impl ToAnimatedZero for LineHeight {

View File

@ -37,20 +37,6 @@ impl Animatable for TransformOrigin {
self.depth.add_weighted(&other.depth, self_portion, other_portion)?,
))
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(f64::sqrt)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(
self.horizontal.compute_squared_distance(&other.horizontal)? +
self.vertical.compute_squared_distance(&other.vertical)? +
self.depth.compute_squared_distance(&other.depth)?
)
}
}
impl ToAnimatedZero for TransformOrigin {

View File

@ -0,0 +1,124 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Machinery to compute distances between animatable values.
use app_units::Au;
use euclid::Size2D;
use std::iter::Sum;
use std::ops::Add;
/// A trait to compute squared distances between two animatable values.
pub trait ComputeSquaredDistance {
/// Computes the squared distance between two animatable values.
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()>;
}
/// A distance between two animatable values.
#[derive(Clone, Copy, Debug)]
pub enum SquaredDistance {
/// Represented as the square root of the squared distance.
Sqrt(f64),
/// Represented as the squared distance itself.
Value(f64),
}
impl ComputeSquaredDistance for u16 {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(SquaredDistance::Sqrt(((*self as f64) - (*other as f64)).abs()))
}
}
impl ComputeSquaredDistance for i32 {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(SquaredDistance::Sqrt((*self - *other).abs() as f64))
}
}
impl ComputeSquaredDistance for f32 {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(SquaredDistance::Sqrt((*self - *other).abs() as f64))
}
}
impl ComputeSquaredDistance for f64 {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(SquaredDistance::Sqrt((*self - *other).abs()))
}
}
impl ComputeSquaredDistance for Au {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
self.0.compute_squared_distance(&other.0)
}
}
impl<T> ComputeSquaredDistance for Option<T>
where T: ComputeSquaredDistance
{
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self.as_ref(), other.as_ref()) {
(Some(this), Some(other)) => this.compute_squared_distance(other),
(None, None) => Ok(SquaredDistance::Value(0.)),
_ => Err(()),
}
}
}
impl<T> ComputeSquaredDistance for Size2D<T>
where T: ComputeSquaredDistance
{
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(self.width.compute_squared_distance(&other.width)? + self.height.compute_squared_distance(&other.height)?)
}
}
impl SquaredDistance {
/// Returns the square root of this squared distance.
pub fn sqrt(self) -> f64 {
match self {
SquaredDistance::Sqrt(this) => this,
SquaredDistance::Value(this) => this.sqrt(),
}
}
}
impl From<SquaredDistance> for f64 {
#[inline]
fn from(distance: SquaredDistance) -> Self {
match distance {
SquaredDistance::Sqrt(this) => this * this,
SquaredDistance::Value(this) => this,
}
}
}
impl Add for SquaredDistance {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
SquaredDistance::Value(f64::from(self) + f64::from(rhs))
}
}
impl Sum for SquaredDistance {
fn sum<I>(mut iter: I) -> Self
where
I: Iterator<Item = Self>,
{
let first = match iter.next() {
Some(first) => first,
None => return SquaredDistance::Value(0.),
};
iter.fold(first, Add::add)
}
}

View File

@ -5,7 +5,7 @@
//! Generic types for CSS values related to backgrounds.
/// A generic value for the `background-size` property.
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum BackgroundSize<LengthOrPercentageOrAuto> {
/// `<width> <height>`

View File

@ -10,6 +10,7 @@ use std::fmt;
use style_traits::{HasViewportPercentage, ToCss};
use values::animated::ToAnimatedZero;
use values::computed::ComputedValueAsSpecified;
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::border::BorderRadius;
use values::generics::position::Position;
use values::generics::rect::Rect;
@ -54,7 +55,7 @@ pub enum ShapeSource<BasicShape, ReferenceBox, Url> {
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Debug, PartialEq, ToComputedValue, ToCss)]
pub enum BasicShape<H, V, LengthOrPercentage> {
Inset(InsetRect<LengthOrPercentage>),
Circle(Circle<H, V, LengthOrPercentage>),
@ -65,7 +66,7 @@ pub enum BasicShape<H, V, LengthOrPercentage> {
/// https://drafts.csswg.org/css-shapes/#funcdef-inset
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
#[derive(Clone, ComputeSquaredDistance, Debug, PartialEq, ToComputedValue)]
pub struct InsetRect<LengthOrPercentage> {
pub rect: Rect<LengthOrPercentage>,
pub round: Option<BorderRadius<LengthOrPercentage>>,
@ -74,7 +75,7 @@ pub struct InsetRect<LengthOrPercentage> {
/// https://drafts.csswg.org/css-shapes/#funcdef-circle
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue)]
pub struct Circle<H, V, LengthOrPercentage> {
pub position: Position<H, V>,
pub radius: ShapeRadius<LengthOrPercentage>,
@ -83,7 +84,7 @@ pub struct Circle<H, V, LengthOrPercentage> {
/// https://drafts.csswg.org/css-shapes/#funcdef-ellipse
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue)]
pub struct Ellipse<H, V, LengthOrPercentage> {
pub position: Position<H, V>,
pub semiaxis_x: ShapeRadius<LengthOrPercentage>,
@ -93,7 +94,7 @@ pub struct Ellipse<H, V, LengthOrPercentage> {
/// https://drafts.csswg.org/css-shapes/#typedef-shape-radius
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
pub enum ShapeRadius<LengthOrPercentage> {
Length(LengthOrPercentage),
ClosestSide,
@ -144,20 +145,16 @@ where
_ => Err(()),
}
}
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(
&ShapeSource::Shape(ref this, ref this_box),
&ShapeSource::Shape(ref other, ref other_box),
) if this_box == other_box => {
this.compute_distance(other)
},
_ => Err(()),
}
}
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
// FIXME(nox): Implement ComputeSquaredDistance for T types and stop
// using PartialEq here, this will let us derive this impl.
impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U>
where
B: ComputeSquaredDistance,
T: PartialEq,
{
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
(
&ShapeSource::Shape(ref this, ref this_box),
@ -209,42 +206,6 @@ where
_ => Err(()),
}
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => {
this.compute_distance(other)
},
(&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => {
this.compute_distance(other)
},
(&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => {
this.compute_distance(other)
},
(&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => {
this.compute_squared_distance(other)
},
(&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => {
this.compute_squared_distance(other)
},
(&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => {
this.compute_squared_distance(other)
},
(&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => {
this.compute_squared_distance(other)
},
_ => Err(()),
}
}
}
impl<L> Animatable for InsetRect<L>
@ -261,17 +222,6 @@ where
let round = self.round.add_weighted(&other.round, self_portion, other_portion)?;
Ok(InsetRect { rect, round })
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.compute_squared_distance(other)?.sqrt())
}
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(
self.rect.compute_squared_distance(&other.rect)? +
self.round.compute_squared_distance(&other.round)?,
)
}
}
impl<L> ToCss for InsetRect<L>
@ -304,17 +254,6 @@ where
let radius = self.radius.add_weighted(&other.radius, self_portion, other_portion)?;
Ok(Circle { position, radius })
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.compute_squared_distance(other)?.sqrt())
}
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(
self.position.compute_squared_distance(&other.position)? +
self.radius.compute_squared_distance(&other.radius)?,
)
}
}
impl<H, V, L> Animatable for Ellipse<H, V, L>
@ -334,18 +273,6 @@ where
let semiaxis_y = self.semiaxis_y.add_weighted(&other.semiaxis_y, self_portion, other_portion)?;
Ok(Ellipse { position, semiaxis_x, semiaxis_y })
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.compute_squared_distance(other)?.sqrt())
}
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(
self.position.compute_squared_distance(&other.position)? +
self.semiaxis_x.compute_squared_distance(&other.semiaxis_x)? +
self.semiaxis_y.compute_squared_distance(&other.semiaxis_y)?,
)
}
}
impl<L> Animatable for ShapeRadius<L>
@ -365,24 +292,6 @@ where
_ => Err(()),
}
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => {
this.compute_squared_distance(other)
},
_ => Err(()),
}
}
}
impl<L> Default for ShapeRadius<L> {
@ -413,12 +322,13 @@ where
}).collect::<Result<Vec<_>, _>>()?;
Ok(Polygon { fill: self.fill, coordinates })
}
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.compute_squared_distance(other)?.sqrt())
}
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
impl<L> ComputeSquaredDistance for Polygon<L>
where
L: ComputeSquaredDistance,
{
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
if self.fill != other.fill {
return Err(());
}
@ -426,9 +336,10 @@ where
return Err(());
}
self.coordinates.iter().zip(other.coordinates.iter()).map(|(this, other)| {
let x = this.0.compute_squared_distance(&other.0)?;
let y = this.1.compute_squared_distance(&other.1)?;
Ok(x + y)
Ok(
this.0.compute_squared_distance(&other.0)? +
this.1.compute_squared_distance(&other.1)?,
)
}).sum()
}
}

View File

@ -36,7 +36,7 @@ pub struct BorderImageSlice<NumberOrPercentage> {
///
/// https://drafts.csswg.org/css-backgrounds-3/#border-radius
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
pub struct BorderRadius<LengthOrPercentage> {
/// The top left radius.
pub top_left: BorderCornerRadius<LengthOrPercentage>,
@ -48,9 +48,9 @@ pub struct BorderRadius<LengthOrPercentage> {
pub bottom_left: BorderCornerRadius<LengthOrPercentage>,
}
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
/// A generic value for `border-*-radius` longhand properties.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
pub struct BorderCornerRadius<L>(pub Size2D<L>);
impl<N> From<N> for BorderImageSlice<N>
@ -129,19 +129,6 @@ where
let bl = self.bottom_left.add_weighted(&other.bottom_left, self_portion, other_portion)?;
Ok(BorderRadius::new(tl, tr, br, bl))
}
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(self.compute_squared_distance(other)?.sqrt())
}
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(
self.top_left.compute_squared_distance(&other.top_left)? +
self.top_right.compute_squared_distance(&other.top_right)? +
self.bottom_right.compute_squared_distance(&other.bottom_right)? +
self.bottom_left.compute_squared_distance(&other.bottom_left)?,
)
}
}
impl<L> ToCss for BorderRadius<L>
@ -189,16 +176,6 @@ where
) -> Result<Self, ()> {
Ok(BorderCornerRadius(self.0.add_weighted(&other.0, self_portion, other_portion)?))
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_squared_distance(&other.0)
}
}
impl<L> ToCss for BorderCornerRadius<L>

View File

@ -65,7 +65,7 @@ pub enum Filter<Angle, Factor, Length, DropShadow> {
/// Contrary to the canonical order from the spec, the color is serialised
/// first, like in Gecko and Webkit.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue, ToCss)]
pub struct SimpleShadow<Color, SizeLength, ShapeLength> {
/// Color.
pub color: Color,

View File

@ -267,11 +267,13 @@ impl ToCss for FontSettingTagFloat {
}
/// A wrapper of Non-negative values.
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage)]
#[derive(PartialEq, PartialOrd, ToComputedValue, ToCss)]
pub struct NonNegative<T>(pub T);
/// A wrapper of greater-than-or-equal-to-one values.
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage)]
#[derive(PartialEq, PartialOrd, ToComputedValue, ToCss)]
pub struct GreaterThanOrEqualToOne<T>(pub T);

View File

@ -5,9 +5,9 @@
//! Generic types for CSS handling of specified and computed values of
//! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position)
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
pub struct Position<H, V> {
/// The horizontal component of position.
pub horizontal: H,

View File

@ -12,7 +12,7 @@ use style_traits::{ToCss, ParseError};
/// A CSS value made of four components, where its `ToCss` impl will try to
/// serialize as few components as possible, like for example in `border-width`.
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Rect<T>(pub T, pub T, pub T, pub T);

View File

@ -97,7 +97,8 @@ impl<ColorType: Parse, UrlPaintServer: Parse> Parse for SVGPaint<ColorType, UrlP
/// An SVG length value supports `context-value` in addition to length.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, HasViewportPercentage, ToAnimatedValue, ToComputedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)]
#[derive(HasViewportPercentage, ToAnimatedValue, ToComputedValue, ToCss)]
pub enum SVGLength<LengthType> {
/// `<length> | <percentage> | <number>`
Length(LengthType),
@ -107,7 +108,7 @@ pub enum SVGLength<LengthType> {
/// Generic value for stroke-dasharray.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, HasViewportPercentage, ToAnimatedValue, ToComputedValue)]
#[derive(Clone, ComputeSquaredDistance, Debug, PartialEq, HasViewportPercentage, ToAnimatedValue, ToComputedValue)]
pub enum SVGStrokeDashArray<LengthType> {
/// `[ <length> | <percentage> | <number> ]#`
Values(Vec<LengthType>),
@ -141,7 +142,7 @@ impl<LengthType> ToCss for SVGStrokeDashArray<LengthType> where LengthType: ToCs
/// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
/// addition to opacity value.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, HasViewportPercentage, ToComputedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, HasViewportPercentage, ToComputedValue, ToCss)]
pub enum SVGOpacity<OpacityType> {
/// `<opacity-value>`
Opacity(OpacityType),

View File

@ -10,6 +10,7 @@ use parser::ParserContext;
use properties::animated_properties::Animatable;
use style_traits::ParseError;
use values::animated::ToAnimatedZero;
use values::distance::{ComputeSquaredDistance, SquaredDistance};
/// A generic value for the `initial-letter` property.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -84,13 +85,18 @@ impl<Value> Animatable for Spacing<Value>
let other = other.value().unwrap_or(&zero);
this.add_weighted(other, self_portion, other_portion).map(Spacing::Value)
}
}
impl<V> ComputeSquaredDistance for Spacing<V>
where
V: ComputeSquaredDistance + From<Au>,
{
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
let zero = Value::from(Au(0));
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
let zero = V::from(Au(0));
let this = self.value().unwrap_or(&zero);
let other = other.value().unwrap_or(&zero);
this.compute_distance(other)
this.compute_squared_distance(other)
}
}
@ -104,7 +110,7 @@ where
/// A generic value for the `line-height` property.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue, ToCss)]
pub enum LineHeight<Number, LengthOrPercentage> {
/// `normal`
Normal,

View File

@ -24,7 +24,7 @@ pub struct Matrix<T, U = T> {
/// A generic transform origin.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
pub struct TransformOrigin<H, V, Depth> {
/// The horizontal origin.
pub horizontal: H,

View File

@ -19,6 +19,7 @@ use style_traits::{ToCss, ParseError, StyleParseError};
pub mod animated;
pub mod computed;
pub mod distance;
pub mod generics;
pub mod specified;
@ -51,7 +52,8 @@ impl Parse for Impossible {
/// A struct representing one of two kinds of values.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToAnimatedValue, ToComputedValue, ToCss)]
#[derive(Clone, ComputeSquaredDistance, Copy, HasViewportPercentage, PartialEq)]
#[derive(ToAnimatedValue, ToComputedValue, ToCss)]
pub enum Either<A, B> {
/// The first value.
First(A),

View File

@ -0,0 +1,117 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use quote;
use std::borrow::Cow;
use syn;
use synstructure;
pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.clone();
for param in &input.generics.ty_params {
where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into())))
}
let variants = variants(&input);
let mut match_body = quote!();
match_body.append_all(variants.iter().map(|variant| {
let name = match input.body {
syn::Body::Struct(_) => Cow::Borrowed(&input.ident),
syn::Body::Enum(_) => {
Cow::Owned(syn::Ident::from(format!("{}::{}", input.ident, variant.ident)))
},
};
let (this_pattern, this_info) = synstructure::match_pattern(
&name,
&variant.data,
&synstructure::BindOpts::with_prefix(
synstructure::BindStyle::Ref,
"this".to_owned(),
),
);
let (other_pattern, other_info) = synstructure::match_pattern(
&name,
&variant.data,
&synstructure::BindOpts::with_prefix(
synstructure::BindStyle::Ref,
"other".to_owned(),
),
);
let sum = if this_info.is_empty() {
quote! { ::values::distance::SquaredDistance::Value(0.) }
} else {
let mut sum = quote!();
sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| {
where_clause.predicates.push(where_predicate(this.field.ty.clone()));
quote! {
::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)?
}
}), "+");
sum
};
quote! {
(&#this_pattern, &#other_pattern) => {
Ok(#sum)
}
}
}));
if variants.len() > 1 {
match_body = quote! { #match_body, _ => Err(()), };
}
quote! {
impl #impl_generics ::values::distance::ComputeSquaredDistance for #name #ty_generics #where_clause {
#[allow(unused_variables, unused_imports)]
#[inline]
fn compute_squared_distance(
&self,
other: &Self,
) -> Result<::values::distance::SquaredDistance, ()> {
match (self, other) {
#match_body
}
}
}
}
}
fn variants(input: &syn::DeriveInput) -> Cow<[syn::Variant]> {
match input.body {
syn::Body::Enum(ref variants) => (&**variants).into(),
syn::Body::Struct(ref data) => {
vec![syn::Variant {
ident: input.ident.clone(),
attrs: input.attrs.clone(),
data: data.clone(),
discriminant: None,
}].into()
},
}
}
fn where_predicate(ty: syn::Ty) -> syn::WherePredicate {
syn::WherePredicate::BoundPredicate(
syn::WhereBoundPredicate {
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: vec![syn::TyParamBound::Trait(
syn::PolyTraitRef {
bound_lifetimes: vec![],
trait_ref: syn::Path {
global: true,
segments: vec![
"values".into(),
"distance".into(),
"ComputeSquaredDistance".into(),
],
},
},
syn::TraitBoundModifier::None,
)],
},
)
}

View File

@ -9,11 +9,18 @@ extern crate synstructure;
use proc_macro::TokenStream;
mod compute_squared_distance;
mod has_viewport_percentage;
mod to_animated_value;
mod to_computed_value;
mod to_css;
#[proc_macro_derive(ComputeSquaredDistance)]
pub fn derive_compute_squared_distance(stream: TokenStream) -> TokenStream {
let input = syn::parse_derive_input(&stream.to_string()).unwrap();
compute_squared_distance::derive(input).to_string().parse().unwrap()
}
#[proc_macro_derive(HasViewportPercentage)]
pub fn derive_has_viewport_percentage(stream: TokenStream) -> TokenStream {
let input = syn::parse_derive_input(&stream.to_string()).unwrap();

View File

@ -115,10 +115,8 @@ use style::string_cache::Atom;
use style::style_adjuster::StyleAdjuster;
use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocSizeOfWithGuard};
use style::stylesheets::{MallocSizeOfWithRepeats, MediaRule};
use style::stylesheets::{NamespaceRule, Origin, PageRule, SizeOfState, StyleRule, SupportsRule};
use style::stylesheets::StylesheetContents;
use style::stylesheets::StylesheetInDocument;
use style::stylesheets::{MediaRule, NamespaceRule, Origin, PageRule, SizeOfState, StyleRule};
use style::stylesheets::{StylesheetContents, StylesheetInDocument, SupportsRule};
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
use style::stylesheets::supports_rule::parse_condition_or_declaration;
@ -131,6 +129,7 @@ use style::traversal_flags::{TraversalFlags, self};
use style::values::{CustomIdent, KeyframesName};
use style::values::animated::ToAnimatedZero;
use style::values::computed::Context;
use style::values::distance::ComputeSquaredDistance;
use style_traits::{PARSING_MODE_DEFAULT, ToCss};
use super::error_reporter::ErrorReporter;
use super::stylesheet_loader::StylesheetLoader;
@ -380,7 +379,7 @@ pub extern "C" fn Servo_AnimationValues_ComputeDistance(from: RawServoAnimationV
-> f64 {
let from_value = AnimationValue::as_arc(&from);
let to_value = AnimationValue::as_arc(&to);
from_value.compute_distance(to_value).unwrap_or(0.0)
from_value.compute_squared_distance(to_value).map(|d| d.sqrt()).unwrap_or(0.0)
}
#[no_mangle]
@ -758,9 +757,9 @@ pub extern "C" fn Servo_Element_ClearData(element: RawGeckoElementBorrowed) {
}
#[no_mangle]
pub extern "C" fn Servo_Element_SizeOfExcludingThis(malloc_size_of: MallocSizeOf,
seen_ptrs: *mut SeenPtrs,
element: RawGeckoElementBorrowed) -> usize {
pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(malloc_size_of: MallocSizeOf,
seen_ptrs: *mut SeenPtrs,
element: RawGeckoElementBorrowed) -> usize {
let malloc_size_of = malloc_size_of.unwrap();
let element = GeckoElement(element);
let borrow = element.borrow_data();
@ -769,12 +768,49 @@ pub extern "C" fn Servo_Element_SizeOfExcludingThis(malloc_size_of: MallocSizeOf
malloc_size_of: malloc_size_of,
seen_ptrs: seen_ptrs,
};
(*data).malloc_size_of_children(&mut state)
(*data).malloc_size_of_children_excluding_cvs(&mut state)
} else {
0
}
}
#[no_mangle]
pub extern "C" fn Servo_Element_HasPrimaryComputedValues(element: RawGeckoElementBorrowed) -> bool
{
let element = GeckoElement(element);
let data = element.borrow_data().expect("Looking for CVs on unstyled element");
data.has_styles()
}
#[no_mangle]
pub extern "C" fn Servo_Element_GetPrimaryComputedValues(element: RawGeckoElementBorrowed)
-> ServoStyleContextStrong
{
let element = GeckoElement(element);
let data = element.borrow_data().expect("Getting CVs on unstyled element");
assert!(data.has_styles(), "Getting CVs on unstyled element");
data.styles.primary().clone().into()
}
#[no_mangle]
pub extern "C" fn Servo_Element_HasPseudoComputedValues(element: RawGeckoElementBorrowed,
index: usize) -> bool
{
let element = GeckoElement(element);
let data = element.borrow_data().expect("Looking for CVs on unstyled element");
data.styles.pseudos.as_array()[index].is_some()
}
#[no_mangle]
pub extern "C" fn Servo_Element_GetPseudoComputedValues(element: RawGeckoElementBorrowed,
index: usize) -> ServoStyleContextStrong
{
let element = GeckoElement(element);
let data = element.borrow_data().expect("Getting CVs that aren't present");
data.styles.pseudos.as_array()[index].as_ref().expect("Getting CVs that aren't present")
.clone().into()
}
#[no_mangle]
pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyleSheetContentsStrong {
let global_style_data = &*GLOBAL_STYLE_DATA;

View File

@ -330453,6 +330453,12 @@
{}
]
],
"css/css-fonts-3/test_datafont_same_origin.html": [
[
"/css/css-fonts-3/test_datafont_same_origin.html",
{}
]
],
"css/css-fonts-3/test_font_family_parsing.html": [
[
"/css/css-fonts-3/test_font_family_parsing.html",
@ -510454,6 +510460,10 @@
"2a8f7afbb6ccfc4084534c36bd4cfeebcbae4194",
"manual"
],
"css/css-fonts-3/test_datafont_same_origin.html": [
"a793f06cceb92b34dc27728307995774a5d95b63",
"testharness"
],
"css/css-fonts-3/test_font_family_parsing.html": [
"fa926a9e86823cddea6d9f1418c752cf04e341ad",
"testharness"
@ -573043,7 +573053,7 @@
"support"
],
"cssom/stylesheet-same-origin.sub.html": [
"719c525b1af3b6b46dfeeb0627034d799bab50b5",
"c8112887da9ee152a9ebe64988fd29cb2c27b9ba",
"testharness"
],
"cssom/support/1x1-green.png": [
@ -574339,11 +574349,11 @@
"support"
],
"dom/nodes/Document-createEvent.html": [
"9274ffffe1b08dee78b64ffc70582957f0386bca",
"9d128acd63f3e972334d9a034e0c222495fd927e",
"testharness"
],
"dom/nodes/Document-createEvent.js": [
"6e7d9350e2fd9ffad36fddd4d90438d620c7dbf3",
"5a8ecc8e5627e340eb10fdc91675968dc9b8fb98",
"support"
],
"dom/nodes/Document-createProcessingInstruction-xhtml.xhtml": [
@ -579755,7 +579765,7 @@
"testharness"
],
"html/browsers/browsing-the-web/history-traversal/PopStateEvent.html": [
"2a7ed0827fc61af7b3bdd238577887aff1902ea7",
"5a9c575a86adbbbca30734992b4d80c22f3973a1",
"testharness"
],
"html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html": [
@ -596407,7 +596417,7 @@
"testharness"
],
"intersection-observer/timestamp.html": [
"a7084b2a0f359115a1862f3c0891ace8143dba83",
"b9bf8d472d7751ec4a1ebee925d12668bedeee7a",
"testharness"
],
"intersection-observer/unclipped-root.html": [
@ -625023,7 +625033,7 @@
"wdspec"
],
"webdriver/tests/actions/mouse.py": [
"51ba095d3d754e30154c20b2910830c6d3e3410c",
"d7af66e45c8e306b4258b92c54ed86073fd21c8d",
"wdspec"
],
"webdriver/tests/actions/sequence.py": [
@ -625047,7 +625057,7 @@
"support"
],
"webdriver/tests/actions/support/test_actions_wdspec.html": [
"a5a1cbce9f0ef66c19065cb8ba761c8fd27cc4ac",
"94b6f474bb4417cf2c06cb5fc6042c01bde98aa2",
"support"
],
"webdriver/tests/conftest.py": [
@ -639415,7 +639425,7 @@
"testharness"
],
"workers/Worker_dispatchEvent_ErrorEvent.htm": [
"a1100df5a79ed7b484a8d5c5746bd646a165242b",
"a27efcba6fcdbb34bb07ac8553a6bbfa04761008",
"testharness"
],
"workers/Worker_script_mimetype.htm": [

View File

@ -1,8 +1,5 @@
[unitless-length.html]
type: testharness
expected:
if debug and stylo and (os == "linux"): TIMEOUT
if debug and stylo and (os == "mac"): TIMEOUT
[calc(2 * 2px) (quirks)]
expected:
if stylo: PASS

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset=utf-8>
<title>data:font same-origin test</title>
<link rel="author" title="Henry Chang" href="mailto:hchang@mozilla.com">
<link rel="help" href="http://www.w3.org/TR/css-fonts-3/#font-prop" />
<meta name="assert" content="tests data:font would be treated same origin." />
<script type="text/javascript" src="/resources/testharness.js"></script>
<script type="text/javascript" src="/resources/testharnessreport.js"></script>
<style type="text/css">
@font-face {
font-family: 'DataFont';
src: url(data:font/opentype;base64,AAEAAAANAIAAAwBQRkZUTU6u6MkAAAXcAAAAHE9TLzJWYWQKAAABWAAAAFZjbWFwAA8D7wAAAcAAAAFCY3Z0IAAhAnkAAAMEAAAABGdhc3D//wADAAAF1AAAAAhnbHlmCC6aTwAAAxQAAACMaGVhZO8ooBcAAADcAAAANmhoZWEIkAV9AAABFAAAACRobXR4EZQAhQAAAbAAAAAQbG9jYQBwAFQAAAMIAAAACm1heHAASQA9AAABOAAAACBuYW1lehAVOgAAA6AAAAIHcG9zdP+uADUAAAWoAAAAKgABAAAAAQAAMhPyuV8PPPUACwPoAAAAAMU4Lm0AAAAAxTgubQAh/5wFeAK8AAAACAACAAAAAAAAAAEAAAK8/5wAWgXcAAAAAAV4AAEAAAAAAAAAAAAAAAAAAAAEAAEAAAAEAAwAAwAAAAAAAgAAAAEAAQAAAEAALgAAAAAAAQXcAfQABQAAAooCvAAAAIwCigK8AAAB4AAxAQIAAAIABgkAAAAAAAAAAAABAAAAAAAAAAAAAAAAUGZFZABAAEEAQQMg/zgAWgK8AGQAAAABAAAAAAAABdwAIQAAAAAF3AAABdwAZAAAAAMAAAADAAAAHAABAAAAAAA8AAMAAQAAABwABAAgAAAABAAEAAEAAABB//8AAABB////wgABAAAAAAAAAQYAAAEAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAnkAAAAqACoAKgBGAAAAAgAhAAABKgKaAAMABwAusQEALzyyBwQA7TKxBgXcPLIDAgDtMgCxAwAvPLIFBADtMrIHBgH8PLIBAgDtMjMRIREnMxEjIQEJ6MfHApr9ZiECWAAAAwBk/5wFeAK8AAMABwALAAABNSEVATUhFQE1IRUB9AH0/UQDhPu0BRQB9MjI/tTIyP7UyMgAAAAAAA4ArgABAAAAAAAAACYATgABAAAAAAABAAUAgQABAAAAAAACAAYAlQABAAAAAAADACEA4AABAAAAAAAEAAUBDgABAAAAAAAFABABNgABAAAAAAAGAAUBUwADAAEECQAAAEwAAAADAAEECQABAAoAdQADAAEECQACAAwAhwADAAEECQADAEIAnAADAAEECQAEAAoBAgADAAEECQAFACABFAADAAEECQAGAAoBRwBDAG8AcAB5AHIAaQBnAGgAdAAgACgAYwApACAAMgAwADAAOAAgAE0AbwB6AGkAbABsAGEAIABDAG8AcgBwAG8AcgBhAHQAaQBvAG4AAENvcHlyaWdodCAoYykgMjAwOCBNb3ppbGxhIENvcnBvcmF0aW9uAABNAGEAcgBrAEEAAE1hcmtBAABNAGUAZABpAHUAbQAATWVkaXVtAABGAG8AbgB0AEYAbwByAGcAZQAgADIALgAwACAAOgAgAE0AYQByAGsAQQAgADoAIAA1AC0AMQAxAC0AMgAwADAAOAAARm9udEZvcmdlIDIuMCA6IE1hcmtBIDogNS0xMS0yMDA4AABNAGEAcgBrAEEAAE1hcmtBAABWAGUAcgBzAGkAbwBuACAAMAAwADEALgAwADAAMAAgAABWZXJzaW9uIDAwMS4wMDAgAABNAGEAcgBrAEEAAE1hcmtBAAAAAgAAAAAAAP+DADIAAAABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAQACACQAAAAAAAH//wACAAAAAQAAAADEPovuAAAAAMU4Lm0AAAAAxTgubQ==);
}
</style>
</head>
<body>
<div id="log"></div>
<pre id="display"></pre>
<style type="text/css" id="testbox"></style>
<script type="text/javascript">
async_test(function(t) {
var text = document.createElement('p');
// Cross-domain font will not load according to [1] so we try to apply
// data:font to this text and see if the font can be loaded.
// [1] https://www.w3.org/TR/css-fonts-3/#same-origin-restriction
text.style = 'font-family: DataFont';
text.innerHTML = "This text should trigger 'TestFont' to load.";
document.body.appendChild(text);
document.fonts.onloadingdone = function (fontFaceSetEvent) {
assert_equals(fontFaceSetEvent.fontfaces.length, 1, "Same origin font should be loaded.");
t.done();
};
document.fonts.onloadingerror = function (fontFaceSetEvent) {
assert_unreached("data:font is not same origin!");
};
}, "Test if data:font would be treated same origin.")
</script>
</body>
</html>

View File

@ -8,10 +8,12 @@
<link id="crossorigin" href="http://www1.{{host}}:{{ports[http][1]}}/stylesheet-same-origin.css" rel="stylesheet">
<link id="sameorigin" href="stylesheet-same-origin.css" rel="stylesheet">
<link id="sameorigindata" href="data:text/css,.green-text{color:rgb(0, 255, 0)}" rel="stylesheet">
<script>
var crossorigin = document.getElementById("crossorigin").sheet;
var sameorigin = document.getElementById("sameorigin").sheet;
var sameorigindata = document.getElementById("sameorigindata").sheet;
test(function() {
assert_throws("SecurityError",
@ -32,13 +34,22 @@
"Cross origin stylesheet.deleteRule should throw SecurityError.");
}, "Origin-clean check in cross-origin CSSOM Stylesheets");
function doOriginCleanCheck(sheet, name) {
assert_equals(sheet.cssRules.length, 1, name + " stylesheet.cssRules should be accessible.");
sheet.insertRule("#test { margin: 10px; }", 1);
assert_equals(sheet.cssRules.length, 2, name + " stylesheet.insertRule should be accessible.");
sheet.deleteRule(0);
assert_equals(sheet.cssRules.length, 1, name + " stylesheet.deleteRule should be accessible.");
}
test(function() {
assert_equals(sameorigin.cssRules.length, 1, "Same origin stylesheet.cssRules should be accessible.");
sameorigin.insertRule("#test { margin: 10px; }", 1);
assert_equals(sameorigin.cssRules.length, 2, "Same origin stylesheet.insertRule should be accessible.");
sameorigin.deleteRule(0);
assert_equals(sameorigin.cssRules.length, 1, "Same origin stylesheet.deleteRule should be accessible.");
doOriginCleanCheck(sameorigin, "Same-origin");
}, "Origin-clean check in same-origin CSSOM Stylesheets");
test(function() {
doOriginCleanCheck(sameorigindata, "data:css");
}, "Origin-clean check in data:css CSSOM Stylesheets");
</script>
</head>
<body>

View File

@ -216,6 +216,8 @@ _ContextualIdentityService.prototype = {
this._identities.push(identity);
this.saveSoon();
Services.obs.notifyObservers(this.getIdentityObserverOutput(identity),
"contextual-identity-created");
return Cu.cloneInto(identity, {});
},
@ -232,8 +234,9 @@ _ContextualIdentityService.prototype = {
delete identity.l10nID;
delete identity.accessKey;
Services.obs.notifyObservers(null, "contextual-identity-updated", userContextId);
this.saveSoon();
Services.obs.notifyObservers(this.getIdentityObserverOutput(identity),
"contextual-identity-updated");
}
return !!identity;
@ -250,13 +253,26 @@ _ContextualIdentityService.prototype = {
Services.obs.notifyObservers(null, "clear-origin-attributes-data",
JSON.stringify({ userContextId }));
let deletedOutput = this.getIdentityObserverOutput(this.getPublicIdentityFromId(userContextId));
this._identities.splice(index, 1);
this._openedIdentities.delete(userContextId);
this.saveSoon();
Services.obs.notifyObservers(deletedOutput, "contextual-identity-deleted");
return true;
},
getIdentityObserverOutput(identity) {
let wrappedJSObject = {
name: this.getUserContextLabel(identity.userContextId),
icon: identity.icon,
color: identity.color,
userContextId: identity.userContextId,
};
return {wrappedJSObject};
},
ensureDataReady() {
if (this._dataReady) {
return;

View File

@ -1,12 +1,13 @@
"use strict";
do_get_profile();
const profileDir = do_get_profile();
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/ContextualIdentityService.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
const TEST_STORE_FILE_NAME = "test-containers.json";
const TEST_STORE_FILE_PATH = OS.Path.join(profileDir.path, "test-containers.json");
let cis;
@ -14,7 +15,7 @@ let cis;
add_task(function() {
ok(!!ContextualIdentityService, "ContextualIdentityService exists");
cis = ContextualIdentityService.createNewInstanceForTesting(TEST_STORE_FILE_NAME);
cis = ContextualIdentityService.createNewInstanceForTesting(TEST_STORE_FILE_PATH);
ok(!!cis, "We have our instance of ContextualIdentityService");
equal(cis.getPublicIdentities().length, 4, "By default, 4 containers.");

View File

@ -1,3 +1,4 @@
[DEFAULT]
firefox-appdir = browser
[test_basic.js]

View File

@ -24,34 +24,38 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
*/
function runMinidumpAnalyzer(minidumpPath) {
return new Promise((resolve, reject) => {
const binSuffix = AppConstants.platform === "win" ? ".exe" : "";
const exeName = "minidump-analyzer" + binSuffix;
try {
const binSuffix = AppConstants.platform === "win" ? ".exe" : "";
const exeName = "minidump-analyzer" + binSuffix;
let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile);
let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile);
if (AppConstants.platform === "macosx") {
exe.append("crashreporter.app");
exe.append("Contents");
exe.append("MacOS");
}
exe.append(exeName);
let args = [ minidumpPath ];
let process = Cc["@mozilla.org/process/util;1"]
.createInstance(Ci.nsIProcess);
process.init(exe);
process.startHidden = true;
process.runAsync(args, args.length, (subject, topic, data) => {
switch (topic) {
case "process-finished":
resolve();
break;
default:
reject(topic);
break;
if (AppConstants.platform === "macosx") {
exe.append("crashreporter.app");
exe.append("Contents");
exe.append("MacOS");
}
});
exe.append(exeName);
let args = [ minidumpPath ];
let process = Cc["@mozilla.org/process/util;1"]
.createInstance(Ci.nsIProcess);
process.init(exe);
process.startHidden = true;
process.runAsync(args, args.length, (subject, topic, data) => {
switch (topic) {
case "process-finished":
resolve();
break;
default:
reject(new Error("Unexpected topic received " + topic));
break;
}
});
} catch (e) {
Cu.reportError(e);
}
});
}
@ -65,21 +69,26 @@ function runMinidumpAnalyzer(minidumpPath) {
*/
function computeMinidumpHash(minidumpPath) {
return (async function() {
let minidumpData = await OS.File.read(minidumpPath);
let hasher = Cc["@mozilla.org/security/hash;1"]
.createInstance(Ci.nsICryptoHash);
hasher.init(hasher.SHA256);
hasher.update(minidumpData, minidumpData.length);
try {
let minidumpData = await OS.File.read(minidumpPath);
let hasher = Cc["@mozilla.org/security/hash;1"]
.createInstance(Ci.nsICryptoHash);
hasher.init(hasher.SHA256);
hasher.update(minidumpData, minidumpData.length);
let hashBin = hasher.finish(false);
let hash = "";
let hashBin = hasher.finish(false);
let hash = "";
for (let i = 0; i < hashBin.length; i++) {
// Every character in the hash string contains a byte of the hash data
hash += ("0" + hashBin.charCodeAt(i).toString(16)).slice(-2);
for (let i = 0; i < hashBin.length; i++) {
// Every character in the hash string contains a byte of the hash data
hash += ("0" + hashBin.charCodeAt(i).toString(16)).slice(-2);
}
return hash;
} catch (e) {
Cu.reportError(e);
return null;
}
return hash;
})();
}
@ -94,10 +103,15 @@ function computeMinidumpHash(minidumpPath) {
*/
function processExtraFile(extraPath) {
return (async function() {
let decoder = new TextDecoder();
let extraData = await OS.File.read(extraPath);
try {
let decoder = new TextDecoder();
let extraData = await OS.File.read(extraPath);
return parseKeyValuePairs(decoder.decode(extraData));
return parseKeyValuePairs(decoder.decode(extraData));
} catch (e) {
Cu.reportError(e);
return {};
}
})();
}
@ -115,7 +129,7 @@ CrashService.prototype = Object.freeze({
Ci.nsIObserver,
]),
addCrash(processType, crashType, id) {
async addCrash(processType, crashType, id) {
switch (processType) {
case Ci.nsICrashService.PROCESS_TYPE_MAIN:
processType = Services.crashmanager.PROCESS_TYPE_MAIN;
@ -147,30 +161,23 @@ CrashService.prototype = Object.freeze({
throw new Error("Unrecognized CRASH_TYPE: " + crashType);
}
let blocker = (async function() {
let metadata = {};
let hash = null;
let cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
.getService(Components.interfaces.nsICrashReporter);
let minidumpPath = cr.getMinidumpForID(id).path;
let extraPath = cr.getExtraFileForID(id).path;
let metadata = {};
let hash = null;
try {
let cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
.getService(Components.interfaces.nsICrashReporter);
let minidumpPath = cr.getMinidumpForID(id).path;
let extraPath = cr.getExtraFileForID(id).path;
await runMinidumpAnalyzer(minidumpPath);
metadata = await processExtraFile(extraPath);
hash = await computeMinidumpHash(minidumpPath);
await runMinidumpAnalyzer(minidumpPath);
metadata = await processExtraFile(extraPath);
hash = await computeMinidumpHash(minidumpPath);
} catch (e) {
Cu.reportError(e);
}
if (hash) {
metadata.MinidumpSha256Hash = hash;
}
if (hash) {
metadata.MinidumpSha256Hash = hash;
}
await Services.crashmanager.addCrash(processType, crashType, id,
new Date(), metadata);
})();
let blocker = Services.crashmanager.addCrash(processType, crashType, id,
new Date(), metadata);
AsyncShutdown.profileBeforeChange.addBlocker(
"CrashService waiting for content crash ping to be sent", blocker
@ -178,7 +185,7 @@ CrashService.prototype = Object.freeze({
blocker.then(AsyncShutdown.profileBeforeChange.removeBlocker(blocker));
return blocker;
await blocker;
},
observe(subject, topic, data) {

View File

@ -19,6 +19,18 @@ const convertIdentity = identity => {
return result;
};
const convertIdentityFromObserver = wrappedIdentity => {
let identity = wrappedIdentity.wrappedJSObject;
let result = {
name: identity.name,
icon: identity.icon,
color: identity.color,
cookieStoreId: getCookieStoreIdForContainer(identity.userContextId),
};
return result;
};
this.contextualIdentities = class extends ExtensionAPI {
getAPI(context) {
let self = {
@ -126,6 +138,40 @@ this.contextualIdentities = class extends ExtensionAPI {
return Promise.resolve(convertedIdentity);
},
onCreated: new EventManager(context, "contextualIdentities.onCreated", fire => {
let observer = (subject, topic) => {
fire.async({contextualIdentity: convertIdentityFromObserver(subject)});
};
Services.obs.addObserver(observer, "contextual-identity-created");
return () => {
Services.obs.removeObserver(observer, "contextual-identity-created");
};
}).api(),
onUpdated: new EventManager(context, "contextualIdentities.onUpdated", fire => {
let observer = (subject, topic) => {
fire.async({contextualIdentity: convertIdentityFromObserver(subject)});
};
Services.obs.addObserver(observer, "contextual-identity-updated");
return () => {
Services.obs.removeObserver(observer, "contextual-identity-updated");
};
}).api(),
onRemoved: new EventManager(context, "contextualIdentities.onRemoved", fire => {
let observer = (subject, topic) => {
fire.async({contextualIdentity: convertIdentityFromObserver(subject)});
};
Services.obs.addObserver(observer, "contextual-identity-deleted");
return () => {
Services.obs.removeObserver(observer, "contextual-identity-deleted");
};
}).api(),
},
};

View File

@ -118,6 +118,50 @@
}
]
}
],
"events": [
{
"name": "onUpdated",
"type": "function",
"description": "Fired when a container is updated.",
"parameters": [
{
"type": "object",
"name": "changeInfo",
"properties": {
"contextualIdentity": {"$ref": "ContextualIdentity", "description": "Contextual identity that has been updated"}
}
}
]
},
{
"name": "onCreated",
"type": "function",
"description": "Fired when a new container is created.",
"parameters": [
{
"type": "object",
"name": "changeInfo",
"properties": {
"contextualIdentity": {"$ref": "ContextualIdentity", "description": "Contextual identity that has been created"}
}
}
]
},
{
"name": "onRemoved",
"type": "function",
"description": "Fired when a container is removed.",
"parameters": [
{
"type": "object",
"name": "changeInfo",
"properties": {
"contextualIdentity": {"$ref": "ContextualIdentity", "description": "Contextual identity that has been removed"}
}
}
]
}
]
}
]

View File

@ -61,6 +61,73 @@ add_task(async function test_contextualIdentity_no_containers() {
Services.prefs.clearUserPref("privacy.userContext.enabled");
});
add_task(async function test_contextualIdentity_events() {
async function backgroundScript() {
function createOneTimeListener(type) {
return new Promise((resolve, reject) => {
try {
browser.test.assertTrue(type in browser.contextualIdentities, `Found API object browser.contextualIdentities.${type}`);
const listener = (change) => {
browser.test.assertTrue("contextualIdentity" in change, `Found identity in change`);
browser.contextualIdentities[type].removeListener(listener);
resolve(change);
};
browser.contextualIdentities[type].addListener(listener);
} catch (e) {
reject(e);
}
});
}
function assertExpected(expected, container) {
for (let key of Object.keys(container)) {
browser.test.assertTrue(key in expected, `found property ${key}`);
browser.test.assertEq(expected[key], container[key], `property value for ${key} is correct`);
}
browser.test.assertEq(Object.keys(expected).length, Object.keys(container).length, "all expected properties found");
}
let onCreatePromise = createOneTimeListener("onCreated");
let containerObj = {name: "foobar", color: "red", icon: "icon"};
let ci = await browser.contextualIdentities.create(containerObj);
browser.test.assertTrue(!!ci, "We have an identity");
const onCreateListenerResponse = await onCreatePromise;
const cookieStoreId = ci.cookieStoreId;
assertExpected(onCreateListenerResponse.contextualIdentity, Object.assign(containerObj, {cookieStoreId}));
let onUpdatedPromise = createOneTimeListener("onUpdated");
let updateContainerObj = {name: "testing", color: "blue", icon: "thing"};
ci = await browser.contextualIdentities.update(cookieStoreId, updateContainerObj);
browser.test.assertTrue(!!ci, "We have an update identity");
const onUpdatedListenerResponse = await onUpdatedPromise;
assertExpected(onUpdatedListenerResponse.contextualIdentity, Object.assign(updateContainerObj, {cookieStoreId}));
let onRemovePromise = createOneTimeListener("onRemoved");
ci = await browser.contextualIdentities.remove(updateContainerObj.cookieStoreId);
browser.test.assertTrue(!!ci, "We have an remove identity");
const onRemoveListenerResponse = await onRemovePromise;
assertExpected(onRemoveListenerResponse.contextualIdentity, Object.assign(updateContainerObj, {cookieStoreId}));
browser.test.notifyPass("contextualIdentities_events");
}
let extension = ExtensionTestUtils.loadExtension({
background: `(${backgroundScript})()`,
manifest: {
permissions: ["contextualIdentities"],
},
});
Services.prefs.setBoolPref("privacy.userContext.enabled", true);
await extension.startup();
await extension.awaitFinish("contextualIdentities_events");
await extension.unload();
Services.prefs.clearUserPref("privacy.userContext.enabled");
});
add_task(async function test_contextualIdentity_with_permissions() {
async function backgroundScript() {
let ci = await browser.contextualIdentities.get("foobar");