Merge autoland to mozilla-central. a=merge

This commit is contained in:
smolnar 2022-06-17 12:38:51 +03:00
commit 7f8c28a578
145 changed files with 4324 additions and 1377 deletions

View File

@ -24,6 +24,8 @@ const kESModuleList = new Set([
/browser\/content\/browser\/firefoxview\.js$/,
/browser\/content\/browser\/recently-closed-tabs\.js$/,
/browser\/content\/browser\/tabs-pickup\.js$/,
/browser\/content\/browser\/helpers\.js$/,
/browser\/content\/browser\/tab-pickup-list\.js$/,
/toolkit\/content\/global\/certviewer\/components\/.*\.js$/,
/toolkit\/content\/global\/certviewer\/.*\.js$/,
/chrome\/pdfjs\/content\/web\/.*\.js$/,

View File

@ -2046,22 +2046,17 @@ var Policies = {
let manifest = {
description: newEngine.Description,
iconURL: newEngine.IconURL ? newEngine.IconURL.href : null,
chrome_settings_overrides: {
search_provider: {
name: newEngine.Name,
// If the encoding is not specified or is falsy, the
// search service will fall back to the default encoding.
encoding: newEngine.Encoding,
search_url: encodeURI(newEngine.URLTemplate),
keyword: newEngine.Alias,
search_url_post_params:
newEngine.Method == "POST"
? newEngine.PostData
: undefined,
suggest_url: newEngine.SuggestURLTemplate,
},
},
name: newEngine.Name,
// If the encoding is not specified or is falsy, the
// search service will fall back to the default encoding.
encoding: newEngine.Encoding,
search_url: encodeURI(newEngine.URLTemplate),
keyword: newEngine.Alias,
search_url_post_params:
newEngine.Method == "POST" ? newEngine.PostData : undefined,
suggest_url: newEngine.SuggestURLTemplate,
};
let engine = Services.search.getEngineByName(newEngine.Name);
if (engine) {
try {

View File

@ -50,7 +50,10 @@ firefoxview-closed-tabs-collapse-button =
firefoxview-closed-tabs-description = Reopen pages youve closed on this device.
firefoxview-closed-tabs-placeholder = History is empty <br/> The next time you close a tab, you can reopen it here.
# refers to the last tab that was used
firefoxview-pickup-tabs-badge = Last active
# Variables:
# $targetURI (string) - URL that will be opened in the new tab
firefoxview-closed-tabs-tab-button =
firefoxview-tabs-list-tab-button =
.title = Open { $targetURI } in a new tab

View File

@ -323,24 +323,160 @@ body > main > aside {
text-align: end;
}
.closed-tab-li-url, .closed-tab-li-time {
.closed-tab-li-url,
.closed-tab-li-time,
.synced-tab-li-device,
.synced-tab-li-url,
.synced-tab-li-time,
.synced-tab-li-url-device {
color: var(--in-content-deemphasized-text);
font-weight: 400;
}
.closed-tab-li-title, .closed-tab-li-url {
.closed-tab-li-title,
.closed-tab-li-url,
.synced-tab-li:not(:first-child) > .synced-tab-li-title,
.synced-tab-li-device,
.synced-tab-li-url-device {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.synced-tabs-list {
padding: 0;
list-style: none;
display: grid;
grid-template-columns: 4fr 4fr;
column-gap: 16px;
row-gap: 8px;
grid-template-areas:
"first second"
"first third";
}
.synced-tab-li {
box-sizing: border-box;
border: 1px solid #CFCFD8;
border-radius: 8px;
padding: 10px;
display: grid;
grid-template-columns: min-content repeat(3, 1fr);
grid-template-rows: auto auto;
grid-template-areas:
"favicon title title title"
"favicon url-device url-device time"
}
.synced-tab-li:hover {
box-shadow: 0px 2px 6px rgba(58, 57, 68, 0.2);
cursor: pointer;
}
.synced-tab-li:not(:first-child) {
align-content: center;
}
@media only screen and (max-width: 60rem) {
.synced-tab-li {
grid-template-areas:
"favicon title title title"
"favicon url-device url-device url-device"
}
.synced-tab-li:not(:first-child) > .synced-tab-li-time {
display: none;
}
}
.synced-tab-li:first-child {
padding-top: 20px;
grid-area: first;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto;
grid-template-areas:
"favicon favicon badge badge"
"title title title title"
"domain domain domain time"
"device device device time";
}
.synced-tab-li:nth-child(2) {
grid-area: second;
}
.synced-tab-li:nth-child(3) {
grid-area: third;
}
.synced-tab-li-url,
.synced-tab-li-device,
.synced-tab-li-time,
.synced-tab-li:not(:first-child) > .synced-tab-li-title {
font-size: .85em;
}
.synced-tab-li-url {
text-decoration-line: underline;
}
.synced-tab-li:first-child > .synced-tab-li-url {
align-self: end;
grid-area: domain;
}
.synced-tab-li-title {
grid-area: title;
font-weight: 500;
}
.synced-tab-li:first-child > .synced-tab-li-title {
color: inherit;
padding-top: 5px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
height: 2.5em;
}
.synced-tab-li-url-device {
grid-area: url-device;
padding-top: 4px
}
.synced-tab-li:first-child > .synced-tab-li-device {
grid-area: device;
margin-top: 5px;
}
.synced-tab-li-time {
grid-area: time;
justify-self: end;
color: var(--in-content-deemphasized-text);
align-self: end;
}
.synced-tab-li:first-child > .synced-tab-li-time {
align-self: center;
}
.synced-tab-li .favicon {
grid-area: favicon;
margin-inline-end: 10px;
}
.synced-tab-li .icon {
vertical-align: bottom;
margin-inline-end: 5px;
}
.icon {
background-position: center center;
background-repeat: no-repeat;
display: inline-block;
-moz-context-properties: fill;
fill: currentColor;
margin-top: 10px;
}
.icon.arrow-down {
@ -355,12 +491,50 @@ body > main > aside {
background-image: url('chrome://browser/skin/history.svg');
}
.icon.phone {
background-image: url('chrome://browser/skin/device-phone.svg');
}
.icon.desktop {
background-image: url('chrome://browser/skin/device-desktop.svg');
}
.icon.tablet {
background-image: url('chrome://browser/skin/device-tablet.svg');
}
.favicon {
background-size: cover;
margin: 2px;
}
.favicon, .icon {
width: 16px;
height: 16px;
}
.last-active-badge {
height: 1.25em;
width: 6em;
background-color: #E3FFF3;
grid-area: badge;
border-radius: 2em;
justify-self: end;
text-align: center;
padding-bottom: 5px;
}
.dot {
height: 8px;
width: 8px;
background-color: #2AC3A2;
border-radius: 50%;
display: inline-block;
}
.badge-text {
font-weight: 400;
font-size: .75em;
letter-spacing: 0.02em;
margin-inline-start: 4px;
color: #000000;
}

View File

@ -17,6 +17,8 @@
<script type="module" src="chrome://browser/content/tabs-pickup.js"></script>
<script type="module" src="chrome://browser/content/firefoxview.js"></script>
<script type="module" src="chrome://browser/content/recently-closed-tabs.js"></script>
<script type="module" src="chrome://browser/content/tab-pickup-list.js"></script>
<script type="module" src="chrome://browser/content/helpers.js"></script>
</head>
<body>
@ -83,9 +85,9 @@
</template>
<template id="synced-tabs-template">
<div class="synced-tabs-container" data-prefix="id:" hidden>
<div class="card">
<h2 data-l10n-id="firefoxview-tabpickup-recenttabs-description"></h2>
</div>
<tab-pickup-list>
<ol hidden="true" class="synced-tabs-list"></ol>
</tab-pickup-list>
<p class="loading-content" data-l10n-id="firefoxview-tabpickup-syncing"></p>
</div>
</template>

View File

@ -0,0 +1,63 @@
/* 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/. */
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(globalThis, {
Services: "resource://gre/modules/Services.jsm",
PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
});
export function formatURIForDisplay(uriString) {
// TODO: Bug 1764816: Make sure we handle file:///, jar:, blob, IP4/IP6 etc. addresses
let uri;
try {
uri = Services.io.newURI(uriString);
} catch (ex) {
return uriString;
}
if (!uri.asciiHost) {
return uriString;
}
let displayHost;
try {
// This might fail if it's an IP address or doesn't have more than 1 part
displayHost = Services.eTLD.getBaseDomain(uri);
} catch (ex) {
return uri.displayHostPort;
}
return displayHost.length ? displayHost : uriString;
}
export function convertTimestamp(timestamp, fluentStrings) {
const relativeTimeFormat = new Services.intl.RelativeTimeFormat(
undefined,
{}
);
const elapsed = Date.now() - timestamp;
// Cutoff of 1.5 minutes + 1 second to determine what text string to display
const nowThresholdMs = 91000;
let formattedTime;
if (elapsed <= nowThresholdMs) {
// Use a different string for very recent timestamps
formattedTime = fluentStrings.formatValueSync(
"firefoxview-just-now-timestamp"
);
} else {
formattedTime = relativeTimeFormat.formatBestUnit(new Date(timestamp));
}
return formattedTime;
}
export function createFaviconElement(image) {
const imageUrl = image
? PlacesUIUtils.getImageURL(image)
: "chrome://global/skin/icons/defaultFavicon.svg";
let favicon = document.createElement("div");
favicon.style.backgroundImage = `url('${imageUrl}')`;
favicon.classList.add("favicon");
return favicon;
}

View File

@ -6,6 +6,8 @@ browser.jar:
content/browser/firefoxview.html
content/browser/firefoxview.js
content/browser/firefoxview.css
content/browser/helpers.js
content/browser/tabs-pickup.js
content/browser/tab-pickup-list.js
content/browser/recently-closed-tabs.js
content/browser/colorway-background.svg (content/colorway-background.svg)

View File

@ -10,10 +10,14 @@ const { XPCOMUtils } = ChromeUtils.import(
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(globalThis, {
SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
});
const relativeTimeFormat = new Services.intl.RelativeTimeFormat(undefined, {});
import {
formatURIForDisplay,
convertTimestamp,
createFaviconElement,
} from "./helpers.js";
const SS_NOTIFY_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed";
function getWindow() {
@ -52,42 +56,6 @@ class RecentlyClosedTabsList extends HTMLElement {
}
}
convertTimestamp(timestamp) {
const elapsed = Date.now() - timestamp;
const nowThresholdMs = 91000;
let formattedTime;
if (elapsed <= nowThresholdMs) {
// Use a different string for very recent timestamps
formattedTime = this.fluentStrings.formatValueSync(
"firefoxview-just-now-timestamp"
);
} else {
formattedTime = relativeTimeFormat.formatBestUnit(new Date(timestamp));
}
return formattedTime;
}
formatURIForDisplay(uriString) {
// TODO: Bug 1764816: Make sure we handle file:///, jar:, blob, IP4/IP6 etc. addresses
let uri;
try {
uri = Services.io.newURI(uriString);
} catch (ex) {
return uriString;
}
if (!uri.asciiHost) {
return uriString;
}
let displayHost;
try {
// This might fail if it's an IP address or doesn't have more than 1 part
displayHost = Services.eTLD.getBaseDomain(uri);
} catch (ex) {
return uri.displayHostPort;
}
return displayHost.length ? displayHost : uriString;
}
getTabStateValue(tab, key) {
let value = "";
const tabEntries = tab.state.entries;
@ -173,18 +141,6 @@ class RecentlyClosedTabsList extends HTMLElement {
}
}
setFavicon(tab) {
const imageUrl = tab.image
? PlacesUIUtils.getImageURL(tab)
: "chrome://global/skin/icons/defaultFavicon.svg";
let favicon = document.createElement("div");
favicon.style.backgroundImage = `url('${imageUrl}')`;
favicon.classList.add("favicon");
favicon.setAttribute("role", "presentation");
return favicon;
}
generateListItem(tab) {
const li = document.createElement("li");
li.classList.add("closed-tab-li");
@ -195,24 +151,25 @@ class RecentlyClosedTabsList extends HTMLElement {
title.textContent = `${tab.title}`;
title.classList.add("closed-tab-li-title");
const favicon = this.setFavicon(tab);
const favicon = createFaviconElement(tab.image);
li.append(favicon);
const targetURI = this.getTabStateValue(tab, "url");
li.dataset.targetURI = targetURI;
document.l10n.setAttributes(li, "firefoxview-closed-tabs-tab-button", {
document.l10n.setAttributes(li, "firefoxview-tabs-list-tab-button", {
targetURI,
});
const url = document.createElement("span");
if (targetURI) {
url.textContent = this.formatURIForDisplay(targetURI);
url.textContent = formatURIForDisplay(targetURI);
url.classList.add("closed-tab-li-url");
}
const time = document.createElement("span");
time.textContent = this.convertTimestamp(tab.closedAt);
time.textContent = convertTimestamp(tab.closedAt, this.fluentStrings);
time.classList.add("closed-tab-li-time");
li.append(title, url, time);

View File

@ -0,0 +1,152 @@
/* 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 strict";
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(globalThis, {
SyncedTabs: "resource://services-sync/SyncedTabs.jsm",
Services: "resource://gre/modules/Services.jsm",
});
import {
formatURIForDisplay,
convertTimestamp,
createFaviconElement,
} from "./helpers.js";
class TabPickupList extends HTMLElement {
constructor() {
super();
this.maxTabsLength = 3;
}
get tabsList() {
return this.querySelector("ol");
}
get fluentStrings() {
if (!this._fluentStrings) {
this._fluentStrings = new Localization(["preview/firefoxView.ftl"], true);
}
return this._fluentStrings;
}
connectedCallback() {
this.addEventListener("click", this);
}
handleEvent(event) {
if (event.type == "click") {
this.openTab(event);
}
}
openTab(event) {
event.preventDefault();
const item = event.target.closest(".synced-tab-li");
window.open(item.dataset.targetURI, "_blank");
}
async getSyncedTabData() {
let tabs = [];
let clients = await SyncedTabs.getTabClients();
for (let client of clients) {
for (let tab of client.tabs) {
tab.device = client.name;
tab.deviceType = client.clientType;
}
tabs = [...tabs, ...client.tabs.reverse()];
}
tabs = tabs
.sort((a, b) => b.lastUsed - a.lastUsed)
.slice(0, this.maxTabsLength);
if (tabs.length) {
this.initiateTabsList(tabs);
} else {
// TODO show empty state placeholder
}
}
initiateTabsList(syncedTabs) {
for (let i = 0; i < syncedTabs.length; i++) {
const li = this.generateListItem(syncedTabs[i], i);
this.tabsList.append(li);
}
this.tabsList.hidden = false;
}
generateListItem(tab, index) {
const li = document.createElement("li");
li.classList.add("synced-tab-li");
li.setAttribute("tabindex", 0);
li.setAttribute("role", "button");
const title = document.createElement("span");
title.textContent = tab.title;
title.classList.add("synced-tab-li-title");
const favicon = createFaviconElement(tab.icon);
const targetURI = tab.url;
li.dataset.targetURI = targetURI;
document.l10n.setAttributes(li, "firefoxview-tabs-list-tab-button", {
targetURI,
});
const time = document.createElement("span");
time.textContent = convertTimestamp(
tab.lastUsed * 1000,
this.fluentStrings
);
time.classList.add("synced-tab-li-time");
const url = document.createElement("span");
const device = document.createElement("span");
const deviceIcon = document.createElement("div");
deviceIcon.classList.add("icon", tab.deviceType);
deviceIcon.setAttribute("role", "presentation");
const deviceText = tab.device;
device.textContent = deviceText;
device.prepend(deviceIcon);
device.title = deviceText;
url.textContent = formatURIForDisplay(tab.url);
url.classList.add("synced-tab-li-url");
device.classList.add("synced-tab-li-device");
// the first list item is diffent from second and third
if (index == 0) {
const badge = this.createBadge();
li.append(favicon, badge, title, url, device, time);
} else {
const urlWithDevice = document.createElement("span");
urlWithDevice.append(url, " • ", device);
urlWithDevice.classList.add("synced-tab-li-url-device");
li.append(favicon, title, urlWithDevice, time);
}
return li;
}
createBadge() {
const badge = document.createElement("div");
const dot = document.createElement("span");
const badgeText = document.createElement("span");
badgeText.setAttribute("data-l10n-id", "firefoxview-pickup-tabs-badge");
badgeText.classList.add("badge-text");
badge.classList.add("last-active-badge");
dot.classList.add("dot");
badge.append(dot, badgeText);
return badge;
}
}
customElements.define("tab-pickup-list", TabPickupList);

View File

@ -261,19 +261,28 @@ class TabsPickupContainer extends HTMLElement {
}
setupElem.hidden = false;
setupElem.selectedViewName = `sync-setup-view${stateIndex}`;
} else {
if (!tabsElem) {
this.appendTemplatedElement(
"synced-tabs-template",
"tabpickup-tabs-container"
);
tabsElem = this.tabsContainerElem;
}
if (setupElem) {
setupElem.hidden = true;
}
return;
}
if (!tabsElem) {
this.appendTemplatedElement(
"synced-tabs-template",
"tabpickup-tabs-container"
);
tabsElem = this.tabsContainerElem;
tabsElem.classList.toggle("loading", stateIndex == 3);
tabsElem.hidden = false;
}
if (setupElem) {
setupElem.hidden = true;
}
tabsElem.hidden = false;
const tabPickupList = document.querySelector("tab-pickup-list");
if (stateIndex == 4 && !tabPickupList.tabsList.hasChildNodes()) {
if (tabsElem) {
tabsElem.classList.toggle("loading", false);
}
tabPickupList.getSyncedTabData();
}
}
}

View File

@ -1254,3 +1254,12 @@ These record the impression and click pings for the Sponsored TopSites.
}
}
```
## Glean "newtab" ping
Unlike the other data collections, this is a
[Glean Ping](https://mozilla.github.io/glean/book/user/pings/index.html)
that batches events and metadata about newtab sessions.
You can find full documentation about this ping and its contents in
[its Glean Dictionary entry](https://dictionary.telemetry.mozilla.org/apps/firefox_desktop/pings/newtab).

View File

@ -8,12 +8,17 @@ const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { actionCreators: ac, actionTypes: at } = ChromeUtils.import(
"resource://activity-stream/common/Actions.jsm"
);
const {
actionCreators: ac,
actionTypes: at,
actionUtils: au,
} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm");
const { shortURL } = ChromeUtils.import(
"resource://activity-stream/lib/ShortURL.jsm"
);
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
const lazy = {};
@ -443,17 +448,21 @@ class PlacesFeed {
];
}
handoffSearchToAwesomebar({ _target, data, meta }) {
handoffSearchToAwesomebar(action) {
const { _target, data, meta } = action;
const searchEngine = this._getDefaultSearchEngine(
lazy.PrivateBrowsingUtils.isBrowserPrivate(_target.browser)
);
const urlBar = _target.browser.ownerGlobal.gURLBar;
let isFirstChange = true;
const newtabSession = AboutNewTab.activityStream.store.feeds
.get("feeds.telemetry")
?.sessions.get(au.getPortIdOfSender(action));
if (!data || !data.text) {
urlBar.setHiddenFocus();
} else {
urlBar.handoff(data.text, searchEngine);
urlBar.handoff(data.text, searchEngine, newtabSession?.session_id);
isFirstChange = false;
}
@ -464,7 +473,7 @@ class PlacesFeed {
if (isFirstChange) {
isFirstChange = false;
urlBar.removeHiddenFocus(true);
urlBar.handoff("", searchEngine);
urlBar.handoff("", searchEngine, newtabSession?.session_id);
this.store.dispatch(
ac.OnlyToOneContent({ type: at.DISABLE_SEARCH }, meta.fromTarget)
);

View File

@ -67,6 +67,7 @@ ChromeUtils.defineModuleGetter(
XPCOMUtils.defineLazyModuleGetters(lazy, {
ExperimentAPI: "resource://nimbus/ExperimentAPI.jsm",
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
TelemetrySession: "resource://gre/modules/TelemetrySession.jsm",
});
@ -188,6 +189,7 @@ class TelemetryFeed {
this._impressionId
);
Services.telemetry.scalarSet("deletion.request.context_id", lazy.contextId);
Glean.newtab.locale.set(Services.locale.appLocaleAsBCP47);
}
handleEvent(event) {
@ -423,6 +425,14 @@ class TelemetryFeed {
this.sendDiscoveryStreamLoadedContent(portID, session);
this.sendDiscoveryStreamImpressions(portID, session);
Glean.newtab.closed.record({ newtab_visit_id: session.session_id });
if (
this.telemetryEnabled &&
(lazy.NimbusFeatures.glean.getVariable("newtabPingEnabled") ?? true)
) {
GleanPings.newtab.submit("newtab_session_end");
}
if (session.perf.visibility_event_rcvd_ts) {
let absNow = this.processStartTs + Cu.now();
session.session_duration = Math.round(
@ -845,9 +855,10 @@ class TelemetryFeed {
handleTopSitesImpressionStats(action) {
const { data } = action;
const { type, position, source } = data;
const { type, position, source, advertiser } = data;
let pingType;
const session = this.sessions.get(au.getPortIdOfSender(action));
if (type === "impression") {
pingType = "topsites-impression";
Services.telemetry.keyedScalarAdd(
@ -855,6 +866,12 @@ class TelemetryFeed {
`${source}_${position}`,
1
);
if (session) {
Glean.topsites.impression.record({
newtab_visit_id: session.session_id,
is_sponsored: !!advertiser,
});
}
} else if (type === "click") {
pingType = "topsites-click";
Services.telemetry.keyedScalarAdd(
@ -862,6 +879,12 @@ class TelemetryFeed {
`${source}_${position}`,
1
);
if (session) {
Glean.topsites.click.record({
newtab_visit_id: session.session_id,
is_sponsored: !!advertiser,
});
}
} else {
Cu.reportError("Unknown ping type for TopSites impression");
return;
@ -1146,6 +1169,17 @@ class TelemetryFeed {
}
Object.assign(session.perf, data);
if (data.visibility_event_rcvd_ts && !session.newtabOpened) {
session.newtabOpened = true;
const source = ONBOARDING_ALLOWED_PAGE_VALUES.includes(session.page)
? session.page
: "other";
Glean.newtab.opened.record({
newtab_visit_id: session.session_id,
source,
});
}
}
uninit() {

View File

@ -0,0 +1,147 @@
# 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/.
# Adding a new metric? We have docs for that!
# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
---
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
$tags:
- 'Firefox :: New Tab Page'
newtab:
locale:
type: string
description: >
The application's locale as of when newtab's TelemetryFeed was init.
Comes from `Services.local.appLocaleAsBCP47`.
Looks like `en-US`.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
expires: 107
send_in_pings:
- newtab
lifetime: application
opened:
type: event
description: >
Recorded when newtab UI is opened via `about:newtab` or `about:home` or
`about:welcome` and has been made visible (see `visibility_event_rcvd_ts`
in
[detect-user-session-start.js](https://searchfox.org/mozilla-central/source/browser/components/newtab/content-src/lib/detect-user-session-start.js)).
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
expires: 107
extra_keys:
newtab_visit_id: &newtab_visit_id
description: >
The id of this newtab visit.
Allows you to separate multiple simultaneous newtabs and
build an event timeline of actions taken from this newtab.
type: string
source:
description: >
The source that opened this newtab.
One of
* `about:newtab`
* `about:home`
* `about:welcome`
* `other`
(See `ONBOARDING_ALLOWED_PAGE_VALUES`).
type: string
send_in_pings:
- newtab
closed:
type: event
description: >
Recorded when newtab UI is closed by
* navigation
* closing the tab
Doesn't mean that the newtab was ever visible to a user.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
expires: 107
extra_keys:
newtab_visit_id: *newtab_visit_id
send_in_pings:
- newtab
topsites:
impression:
type: event
description: >
Recorded when topsite tiles are loaded.
Presently only happens for sponsored tiles.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
expires: 107
extra_keys:
newtab_visit_id: *newtab_visit_id
is_sponsored: &is_sponsored
description: Whether the topsite tile was sponsored.
type: boolean
send_in_pings:
- newtab
click:
type: event
description: >
Recorded when a topsite tile is clicked.
Presently only happens for sponsored tiles.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
expires: 107
extra_keys:
newtab_visit_id: *newtab_visit_id
is_sponsored: *is_sponsored
send_in_pings:
- newtab

View File

@ -0,0 +1,26 @@
# 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/.
---
$schema: moz://mozilla.org/schemas/glean/pings/2-0-0
newtab:
description: |
Newtab-related instrumentation.
Can be disabled via the `newtabPingEnabled` variable of the `glean` Nimbus
feature, or the `browser.newtabpage.ping.enabled` pref.
reasons:
newtab_session_end: |
The newtab session ended.
Could be by navigation, being closed, etc.
include_client_id: true
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
notification_emails:
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- anicholson@mozilla.com
- najiang@mozilla.com

View File

@ -55,6 +55,7 @@ support-files=
[browser_newtab_overrides.js]
[browser_newtab_header.js]
[browser_newtab_last_LinkMenu.js]
[browser_newtab_ping.js]
[browser_newtab_trigger.js]
[browser_open_tab_focus.js]
skip-if = (os == "linux") # Test setup only implemented for OSX and Windows

View File

@ -0,0 +1,172 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
const { ASRouter } = ChromeUtils.import(
"resource://activity-stream/lib/ASRouter.jsm"
);
const { ExperimentFakes } = ChromeUtils.import(
"resource://testing-common/NimbusTestUtils.jsm"
);
let sendTriggerMessageSpy;
add_setup(function() {
let sandbox = sinon.createSandbox();
sendTriggerMessageSpy = sandbox.spy(ASRouter, "sendTriggerMessage");
registerCleanupFunction(() => {
sandbox.restore();
});
});
add_task(async function test_newtab_tab_close_sends_ping() {
await SpecialPowers.pushPrefEnv({
set: [["browser.newtabpage.activity-stream.telemetry", true]],
});
Services.fog.testResetFOG();
sendTriggerMessageSpy.resetHistory();
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:newtab",
false // waitForLoad; about:newtab is cached so this would never resolve
);
await BrowserTestUtils.waitForCondition(
() => sendTriggerMessageSpy.called,
"After about:newtab finishes loading"
);
sendTriggerMessageSpy.resetHistory();
let record = Glean.newtab.opened.testGetValue("newtab");
Assert.equal(record.length, 1, "Should only be one open");
const sessionId = record[0].extra.newtab_visit_id;
Assert.ok(!!sessionId, "newtab_visit_id must be present");
let pingSubmitted = false;
GleanPings.newtab.testBeforeNextSubmit(reason => {
pingSubmitted = true;
Assert.equal(reason, "newtab_session_end");
record = Glean.newtab.closed.testGetValue("newtab");
Assert.equal(record.length, 1, "Should only have one close");
Assert.equal(
record[0].extra.newtab_visit_id,
sessionId,
"Should've closed the session we opened"
);
});
BrowserTestUtils.removeTab(tab);
await BrowserTestUtils.waitForCondition(
() => pingSubmitted,
"We expect the ping to have submitted."
);
await SpecialPowers.popPrefEnv();
});
add_task(async function test_newtab_tab_nav_sends_ping() {
await SpecialPowers.pushPrefEnv({
set: [["browser.newtabpage.activity-stream.telemetry", true]],
});
Services.fog.testResetFOG();
sendTriggerMessageSpy.resetHistory();
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:newtab",
false // waitForLoad; about:newtab is cached so this would never resolve
);
await BrowserTestUtils.waitForCondition(
() => sendTriggerMessageSpy.called,
"After about:newtab finishes loading"
);
sendTriggerMessageSpy.resetHistory();
await BrowserTestUtils.waitForCondition(
() => !!Glean.newtab.opened.testGetValue("newtab"),
"We expect the newtab open to be recorded"
);
let record = Glean.newtab.opened.testGetValue("newtab");
Assert.equal(record.length, 1, "Should only be one open");
const sessionId = record[0].extra.newtab_visit_id;
Assert.ok(!!sessionId, "newtab_visit_id must be present");
let pingSubmitted = false;
GleanPings.newtab.testBeforeNextSubmit(reason => {
pingSubmitted = true;
Assert.equal(reason, "newtab_session_end");
record = Glean.newtab.closed.testGetValue("newtab");
Assert.equal(record.length, 1, "Should only have one close");
Assert.equal(
record[0].extra.newtab_visit_id,
sessionId,
"Should've closed the session we opened"
);
});
BrowserTestUtils.loadURI(tab.linkedBrowser, "about:mozilla");
await BrowserTestUtils.waitForCondition(
() => pingSubmitted,
"We expect the ping to have submitted."
);
BrowserTestUtils.removeTab(tab);
await SpecialPowers.popPrefEnv();
});
add_task(async function test_newtab_doesnt_send_nimbus() {
await SpecialPowers.pushPrefEnv({
set: [["browser.newtabpage.activity-stream.telemetry", true]],
});
let doEnrollmentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "glean",
value: { newtabPingEnabled: false },
});
Services.fog.testResetFOG();
sendTriggerMessageSpy.resetHistory();
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:newtab",
false // waitForLoad; about:newtab is cached so this would never resolve
);
await BrowserTestUtils.waitForCondition(
() => sendTriggerMessageSpy.called,
"After about:newtab finishes loading"
);
sendTriggerMessageSpy.resetHistory();
await BrowserTestUtils.waitForCondition(
() => !!Glean.newtab.opened.testGetValue("newtab"),
"We expect the newtab open to be recorded"
);
let record = Glean.newtab.opened.testGetValue("newtab");
Assert.equal(record.length, 1, "Should only be one open");
const sessionId = record[0].extra.newtab_visit_id;
Assert.ok(!!sessionId, "newtab_visit_id must be present");
GleanPings.newtab.testBeforeNextSubmit(() => {
Assert.ok(false, "Must not submit ping!");
});
BrowserTestUtils.loadURI(tab.linkedBrowser, "about:mozilla");
BrowserTestUtils.removeTab(tab);
await BrowserTestUtils.waitForCondition(() => {
let { sessions } = AboutNewTab.activityStream.store.feeds.get(
"feeds.telemetry"
);
return !Array.from(sessions.entries()).filter(
([k, v]) => v.session_id === sessionId
).length;
}, "Waiting for sessions to clean up.");
// Session ended without a ping being sent. Success!
await doEnrollmentCleanup();
await SpecialPowers.popPrefEnv();
});

View File

@ -106,6 +106,9 @@ describe("PlacesFeed", () => {
PlacesObserver = PlacesFeed.PlacesObserver;
feed = new PlacesFeed();
feed.store = { dispatch: sinon.spy() };
globals.set("AboutNewTab", {
activityStream: { store: { feeds: { get() {} } } },
});
});
afterEach(() => {
globals.restore();
@ -735,16 +738,25 @@ describe("PlacesFeed", () => {
});
});
it("should properly handle handoff with text data passed in", () => {
const sessionId = "decafc0ffee";
sandbox.stub(AboutNewTab.activityStream.store.feeds, "get").returns({
sessions: {
get: () => {
return { session_id: sessionId };
},
},
});
feed.handoffSearchToAwesomebar({
_target: { browser: { ownerGlobal: { gURLBar: fakeUrlBar } } },
data: { text: "foo" },
meta: { fromTarget: {} },
});
assert.calledOnce(fakeUrlBar.handoff);
assert.calledWith(
assert.calledWithExactly(
fakeUrlBar.handoff,
"foo",
global.Services.search.defaultEngine
global.Services.search.defaultEngine,
sessionId
);
assert.notCalled(fakeUrlBar.focus);
assert.notCalled(fakeUrlBar.setHiddenFocus);
@ -770,10 +782,11 @@ describe("PlacesFeed", () => {
meta: { fromTarget: {} },
});
assert.calledOnce(fakeUrlBar.handoff);
assert.calledWith(
assert.calledWithExactly(
fakeUrlBar.handoff,
"foo",
global.Services.search.defaultPrivateEngine
global.Services.search.defaultPrivateEngine,
undefined
);
assert.notCalled(fakeUrlBar.focus);
assert.notCalled(fakeUrlBar.setHiddenFocus);
@ -802,7 +815,8 @@ describe("PlacesFeed", () => {
assert.calledWithExactly(
fakeUrlBar.handoff,
"foo",
global.Services.search.defaultEngine
global.Services.search.defaultEngine,
undefined
);
assert.notCalled(fakeUrlBar.focus);
@ -819,6 +833,45 @@ describe("PlacesFeed", () => {
type: "SHOW_SEARCH",
});
});
it("should properly handoff a newtab session id with no text passed in", () => {
const sessionId = "decafc0ffee";
sandbox.stub(AboutNewTab.activityStream.store.feeds, "get").returns({
sessions: {
get: () => {
return { session_id: sessionId };
},
},
});
feed.handoffSearchToAwesomebar({
_target: { browser: { ownerGlobal: { gURLBar: fakeUrlBar } } },
data: {},
meta: { fromTarget: {} },
});
assert.calledOnce(fakeUrlBar.setHiddenFocus);
assert.notCalled(fakeUrlBar.handoff);
assert.notCalled(feed.store.dispatch);
// Now type a character.
listeners.keydown({ key: "f" });
assert.calledOnce(fakeUrlBar.handoff);
assert.calledWithExactly(
fakeUrlBar.handoff,
"",
global.Services.search.defaultEngine,
sessionId
);
assert.calledOnce(fakeUrlBar.removeHiddenFocus);
assert.calledOnce(feed.store.dispatch);
assert.calledWith(feed.store.dispatch, {
meta: {
from: "ActivityStream:Main",
skipMain: true,
to: "ActivityStream:Content",
toTarget: {},
},
type: "DISABLE_SEARCH",
});
});
});
describe("#observe", () => {

View File

@ -1303,6 +1303,22 @@ describe("TelemetryFeed", () => {
assert.calledOnce(spy);
assert.calledWith(spy, topsites_first_painted_ts);
});
it("should record a Glean newtab.opened event with the correct visit_id when visibility event received", () => {
const session_id = "decafc0ffee";
const page = "about:newtab";
const session = { page, perf: {}, session_id };
const data = { visibility_event_rcvd_ts: 444455 };
sandbox.stub(instance.sessions, "get").returns(session);
sandbox.spy(Glean.newtab.opened, "record");
instance.saveSessionPerfData("port123", data);
assert.calledOnce(Glean.newtab.opened.record);
assert.deepEqual(Glean.newtab.opened.record.firstCall.args[0], {
newtab_visit_id: session_id,
source: page,
});
});
});
describe("#uninit", () => {
it("should call .pingCentre.uninit", () => {
@ -1956,6 +1972,51 @@ describe("TelemetryFeed", () => {
// version
assert.equal(args[3], "1");
});
it("should record a Glean topsites.impression event on an impression event", async () => {
const data = {
type: "impression",
tile_id: 42,
source: "newtab",
position: 1,
reporting_url: "https://test.reporting.net/",
advertiser: "adnoid ads",
};
instance = new TelemetryFeed();
const session_id = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id });
sandbox.spy(Glean.topsites.impression, "record");
await instance.handleTopSitesImpressionStats({ data });
// Event should be recorded
assert.calledOnce(Glean.topsites.impression.record);
assert.calledWith(Glean.topsites.impression.record, {
newtab_visit_id: session_id,
is_sponsored: true,
});
});
it("should record a Glean topsites.click event on a click event", async () => {
const data = {
type: "click",
tile_id: 42,
source: "newtab",
position: 1,
reporting_url: "https://test.reporting.net/",
};
instance = new TelemetryFeed();
const session_id = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id });
sandbox.spy(Glean.topsites.click, "record");
await instance.handleTopSitesImpressionStats({ data });
// Event should be recorded
assert.calledOnce(Glean.topsites.click.record);
assert.calledWith(Glean.topsites.click.record, {
newtab_visit_id: session_id,
is_sponsored: false,
});
});
it("should reportError on unknown pingTypes", async () => {
const data = { type: "unknown_type" };
instance = new TelemetryFeed();

View File

@ -528,6 +528,9 @@ const TEST_GLOBAL = {
off: () => {},
},
NimbusFeatures: {
glean: {
getVariable() {},
},
newtab: {
isEnabled() {},
getVariable() {},
@ -580,6 +583,33 @@ const TEST_GLOBAL = {
},
Logger,
getFxAccountsSingleton() {},
AboutNewTab: {},
Glean: {
newtab: {
opened: {
record() {},
},
closed: {
record() {},
},
locale: {
set() {},
},
},
topsites: {
impression: {
record() {},
},
click: {
record() {},
},
},
},
GleanPings: {
newtab: {
submit() {},
},
},
};
overrider.set(TEST_GLOBAL);

View File

@ -1561,6 +1561,23 @@ var PlacesUIUtils = {
PlacesUIUtils.lastContextMenuTriggerNode = menupopup.triggerNode;
if (Services.prefs.getBoolPref("browser.tabs.loadBookmarksInTabs", false)) {
menupopup.ownerDocument
.getElementById("placesContext_open")
.removeAttribute("default");
menupopup.ownerDocument
.getElementById("placesContext_open:newtab")
.setAttribute("default", "true");
// else clause ensures correct behavior if pref is repeatedly toggled
} else {
menupopup.ownerDocument
.getElementById("placesContext_open:newtab")
.removeAttribute("default");
menupopup.ownerDocument
.getElementById("placesContext_open")
.setAttribute("default", "true");
}
let isManaged = !!menupopup.triggerNode.closest("#managed-bookmarks");
if (isManaged) {
this.managedPlacesContextShowing(event);
@ -1816,8 +1833,8 @@ var PlacesUIUtils = {
}
},
getImageURL(aItem) {
let iconURL = aItem.image;
getImageURL(icon) {
let iconURL = icon;
// don't initiate a connection just to fetch a favicon (see bug 467828)
if (/^https?:/.test(iconURL)) {
iconURL = "moz-anno:favicon:" + iconURL;

View File

@ -90,14 +90,63 @@ let checkContextMenu = async (cbfunc, optionItems, doc = document) => {
children: bookmarksInfo,
});
let contextMenu = await cbfunc(bookmark);
// Open and check the context menu twice, once with
// `browser.tabs.loadBookmarksInTabs` set to true and again with it set to
// false.
for (let loadBookmarksInNewTab of [true, false]) {
info(
`Running checkContextMenu: ` + JSON.stringify({ loadBookmarksInNewTab })
);
for (let item of optionItems) {
OptionItemExists(item, doc);
Services.prefs.setBoolPref(
"browser.tabs.loadBookmarksInTabs",
loadBookmarksInNewTab
);
// When `loadBookmarksInTabs` is true, the usual placesContext_open:newtab
// item is hidden and placesContext_open is shown. The tasks in this test
// assume that `loadBookmarksInTabs` is false, so when a caller expects
// placesContext_open:newtab to appear but not placesContext_open, add it to
// the list of expected items when the pref is set.
let expectedOptionItems = [...optionItems];
if (
loadBookmarksInNewTab &&
optionItems.includes("placesContext_open:newtab") &&
!optionItems.includes("placesContext_open")
) {
expectedOptionItems.push("placesContext_open");
}
// The caller is responsible for opening the menu, via `cbfunc()`.
let contextMenu = await cbfunc(bookmark);
for (let item of expectedOptionItems) {
OptionItemExists(item, doc);
}
OptionsMatchExpected(contextMenu, expectedOptionItems);
// Check the "default" attributes on placesContext_open and
// placesContext_open:newtab.
if (expectedOptionItems.includes("placesContext_open")) {
Assert.equal(
doc.getElementById("placesContext_open").getAttribute("default"),
loadBookmarksInNewTab ? "" : "true",
`placesContext_open has the correct "default" attribute when loadBookmarksInTabs = ${loadBookmarksInNewTab}`
);
}
if (expectedOptionItems.includes("placesContext_open:newtab")) {
Assert.equal(
doc.getElementById("placesContext_open:newtab").getAttribute("default"),
loadBookmarksInNewTab ? "true" : "",
`placesContext_open:newtab has the correct "default" attribute when loadBookmarksInTabs = ${loadBookmarksInNewTab}`
);
}
contextMenu.hidePopup();
}
OptionsMatchExpected(contextMenu, optionItems);
contextMenu.hidePopup();
Services.prefs.clearUserPref("browser.tabs.loadBookmarksInTabs");
await PlacesUtils.bookmarks.eraseEverything();
};
@ -149,7 +198,7 @@ add_task(async function test_bookmark_contextmenu_contents() {
return contextMenu;
}, optionItems);
let tab;
let tabs = [];
let contextMenuOnContent;
await checkContextMenu(async function() {
@ -161,7 +210,11 @@ add_task(async function test_bookmark_contextmenu_contents() {
});
info("Open context menu on about:config");
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:config"
);
tabs.push(tab);
contextMenuOnContent = document.getElementById("contentAreaContextMenu");
const popupShownPromiseOnContent = BrowserTestUtils.waitForEvent(
contextMenuOnContent,
@ -172,6 +225,7 @@ add_task(async function test_bookmark_contextmenu_contents() {
type: "contextmenu",
});
await popupShownPromiseOnContent;
contextMenuOnContent.hidePopup();
info("Check context menu on bookmark");
const toolbarNode = getToolbarNodeForItemGuid(toolbarBookmark.guid);
@ -192,10 +246,11 @@ add_task(async function test_bookmark_contextmenu_contents() {
// We need to do a thorough cleanup to avoid leaking the window of
// 'about:config'.
const tabClosed = BrowserTestUtils.waitForTabClosing(tab);
contextMenuOnContent.hidePopup();
BrowserTestUtils.removeTab(tab);
await tabClosed;
for (let tab of tabs) {
const tabClosed = BrowserTestUtils.waitForTabClosing(tab);
BrowserTestUtils.removeTab(tab);
await tabClosed;
}
});
add_task(async function test_empty_contextmenu_contents() {

View File

@ -174,6 +174,8 @@ class BrowserSearchTelemetryHandler {
* true if this event was generated by a form history result.
* @param {string} [details.alias=null]
* The search engine alias used in the search, if any.
* @param {string} [details.newtabSessionId=undefined]
* The newtab session that prompted this search, if any.
* @throws if source is not in the known sources list.
*/
recordSearch(browser, engine, source, details = {}) {
@ -218,6 +220,17 @@ class BrowserSearchTelemetryHandler {
this._recordSearch(browser, engine, details.url, source);
break;
}
if (["urlbar-handoff", "abouthome", "newtab"].includes(source)) {
Glean.newtabSearch.issued.record({
newtab_visit_id: details.newtabSessionId,
search_access_point: KNOWN_SEARCH_SOURCES.get(source),
telemetry_id: engine.telemetryId,
});
lazy.SearchSERPTelemetry.recordBrowserNewtabSession(
browser,
details.newtabSessionId
);
}
} catch (ex) {
// Catch any errors here, so that search actions are not broken if
// telemetry is broken for some reason.

View File

@ -79,6 +79,10 @@ class TelemetryHandler {
// browser - one of the KNOWN_SEARCH_SOURCES in BrowserSearchTelemetry.
_browserSourceMap = new WeakMap();
// _browserNewtabSessionMap is a map of the newtab session id for particular
// browsers.
_browserNewtabSessionMap = new WeakMap();
constructor() {
this._contentHandler = new ContentHandler({
browserInfoByURL: this._browserInfoByURL,
@ -152,6 +156,19 @@ class TelemetryHandler {
this._browserSourceMap.set(browser, source);
}
/**
* Records the newtab source for particular browsers, in case it needs
* to be associated with a SERP.
*
* @param {browser} browser
* The browser where the search originated.
* @param {string} newtabSessionId
* The sessionId of the newtab session the search originated from.
*/
recordBrowserNewtabSession(browser, newtabSessionId) {
this._browserNewtabSessionMap.set(browser, newtabSessionId);
}
/**
* Handles the TabClose event received from the listeners.
*
@ -163,6 +180,7 @@ class TelemetryHandler {
return;
}
this._browserNewtabSessionMap.delete(event.target.linkedBrowser);
this.stopTrackingBrowser(event.target.linkedBrowser);
}
@ -227,6 +245,7 @@ class TelemetryHandler {
}
let info = this._checkURLForSerpMatch(url);
if (!info) {
this._browserNewtabSessionMap.delete(browser);
this.stopTrackingBrowser(browser);
return;
}
@ -241,6 +260,13 @@ class TelemetryHandler {
this._browserSourceMap.delete(browser);
}
let newtabSessionId;
if (this._browserNewtabSessionMap.has(browser)) {
newtabSessionId = this._browserNewtabSessionMap.get(browser);
// We leave the newtabSessionId in the map for this browser
// until we stop loading SERP pages or the tab is closed.
}
this._reportSerpPage(info, source, url);
let item = this._browserInfoByURL.get(url);
@ -249,12 +275,14 @@ class TelemetryHandler {
item.browsers.set(browser, "no ads reported");
item.count++;
item.source = source;
item.newtabSessionId = newtabSessionId;
} else {
item = this._browserInfoByURL.set(url, {
browsers: new WeakMap().set(browser, "no ads reported"),
info,
count: 1,
source,
newtabSessionId,
});
}
}
@ -770,6 +798,15 @@ class ContentHandler {
1
);
wrappedChannel._adClickRecorded = true;
if (item.newtabSessionId) {
Glean.newtabSearchAd.click.record({
newtab_visit_id: item.newtabSessionId,
search_access_point: item.source,
is_follow_on: item.info.type.endsWith("follow-on"),
is_tagged: item.info.type.startsWith("tagged"),
telemetry_id: item.info.provider,
});
}
} catch (e) {
Cu.reportError(e);
}
@ -827,6 +864,16 @@ class ContentHandler {
);
item.browsers.set(browser, "ad reported");
if (item.newtabSessionId) {
Glean.newtabSearchAd.impression.record({
newtab_visit_id: item.newtabSessionId,
search_access_point: item.source,
is_follow_on: item.info.type.endsWith("follow-on"),
is_tagged: item.info.type.startsWith("tagged"),
telemetry_id: item.info.provider,
});
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -2,7 +2,7 @@
"name": "Ecosia",
"description": "Search Ecosia",
"manifest_version": 2,
"version": "1.0",
"version": "1.1",
"applications": {
"gecko": {
"id": "ecosia@search.mozilla.org"

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/.
# Adding a new metric? We have docs for that!
# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
---
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
$tags:
- 'Firefox :: Search'
newtab.search:
issued:
type: event
description: >
When Firefox was asked to issue a search from a Search Access Point (SAP)
on a newtab page.
Doesn't record searches in Private Browsing Mode unless
`browser.engagement.search_counts.pbm` is set to `true`.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
expires: 107
extra_keys:
newtab_visit_id: &newtab_visit_id
description: >
The id of the newtab visit that originated the search.
Should always be present for handoff searches.
TODO(bug 1774597): for searches done without handoff (e.g. with
`browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar`
set to `false`), the active newtab visit id is unknown.
type: string
search_access_point: &search_access_point
description: >
One of the search access points available on the new tab like
* `urlbar_handoff`
* `about_home`
* `about_newtab`
type: string
telemetry_id: &telemetry_id
description: >
The search engine's `telemetryId`, like `google-b-d`.
This is set to be a telemetry-specific id for app-provided engines,
and is `other-<name>` for others (where `<name>` is the engine's
WebExtension name).
type: string
send_in_pings:
- newtab
newtab.search.ad:
impression:
type: event
description: >
Recorded when a newtab visit resulted in a search that
loaded a Search Engine Result Page (SERP) that contains an ad link.
And the SERP is visible.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
expires: 107
extra_keys:
newtab_visit_id: *newtab_visit_id
search_access_point: *search_access_point
is_follow_on: &is_follow_on
description: >
Whether the preceding search happened on a search results page.
type: boolean
is_tagged: &is_tagged
description: >
Whether the preceding search was tagged with a partner code.
type: boolean
telemetry_id: *telemetry_id
send_in_pings:
- newtab
click:
type: event
description: >
Recorded when an ad link is clicked on a Search Engine Result Page (SERP)
which was loaded by a seach that began on a newtab page.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1766887
data_sensitivity:
- interaction
notification_emails:
- anicholson@mozilla.com
- chutten@mozilla.com
- mmccorquodale@mozilla.com
- najiang@mozilla.com
expires: 107
extra_keys:
newtab_visit_id: *newtab_visit_id
search_access_point: *search_access_point
is_follow_on: *is_follow_on
is_tagged: *is_tagged
telemetry_id: *telemetry_id
send_in_pings:
- newtab

View File

@ -59,6 +59,7 @@ add_task(async function test_abouthome_activitystream_simpleQuery() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
Services.fog.testResetFOG();
let search_hist = TelemetryTestUtils.getAndClearKeyedHistogram(
"SEARCH_COUNTS"
);
@ -122,5 +123,18 @@ add_task(async function test_abouthome_activitystream_simpleQuery() {
{ category: "navigation", method: "search" }
);
// Also also check Glean events.
const record = Glean.newtabSearch.issued.testGetValue();
Assert.ok(!!record, "Must have recorded a search issuance");
Assert.equal(record.length, 1, "One search, one event");
Assert.deepEqual(
{
search_access_point: "about_home",
telemetry_id: "other-MozSearch",
},
record[0].extra,
"Must have recorded the expected information."
);
BrowserTestUtils.removeTab(tab);
});

View File

@ -135,6 +135,7 @@ add_task(async function test_about_newtab() {
// Let's reset the counts.
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
Services.fog.testResetFOG();
let search_hist = TelemetryTestUtils.getAndClearKeyedHistogram(
"SEARCH_COUNTS"
);
@ -191,6 +192,19 @@ add_task(async function test_about_newtab() {
{ category: "navigation", method: "search" }
);
// Also also check Glean events.
const record = Glean.newtabSearch.issued.testGetValue();
Assert.ok(!!record, "Must have recorded a search issuance");
Assert.equal(record.length, 1, "One search, one event");
Assert.deepEqual(
{
search_access_point: "about_newtab",
telemetry_id: "other-MozSearch",
},
record[0].extra,
"Must have recorded the expected information."
);
BrowserTestUtils.removeTab(tab);
await SpecialPowers.popPrefEnv();
});

View File

@ -204,6 +204,7 @@ add_task(async function test_source_urlbar_handoff() {
"urlbar-handoff",
"urlbar_handoff",
async () => {
Services.fog.testResetFOG();
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
BrowserTestUtils.loadURI(tab.linkedBrowser, "about:newtab");
await BrowserTestUtils.browserStopped(tab.linkedBrowser, "about:newtab");
@ -237,6 +238,47 @@ add_task(async function test_source_urlbar_handoff() {
return tab;
},
async () => {
const issueRecords = Glean.newtabSearch.issued.testGetValue();
Assert.ok(!!issueRecords, "Must have recorded a search issuance");
Assert.equal(issueRecords.length, 1, "One search, one event");
const newtabVisitId = issueRecords[0].extra.newtab_visit_id;
Assert.ok(!!newtabVisitId, "Must have a visit id");
Assert.deepEqual(
{
// Yes, this is tautological. But I want to use deepEqual.
newtab_visit_id: newtabVisitId,
search_access_point: "urlbar_handoff",
telemetry_id: "other-Example",
},
issueRecords[0].extra,
"Must have recorded the expected information."
);
const impRecords = Glean.newtabSearchAd.impression.testGetValue();
Assert.equal(impRecords.length, 1, "One impression, one event.");
Assert.deepEqual(
{
newtab_visit_id: newtabVisitId,
search_access_point: "urlbar_handoff",
telemetry_id: "example",
is_tagged: "true",
is_follow_on: "false",
},
impRecords[0].extra,
"Must have recorded the expected information."
);
const clickRecords = Glean.newtabSearchAd.click.testGetValue();
Assert.equal(clickRecords.length, 1, "One click, one event.");
Assert.deepEqual(
{
newtab_visit_id: newtabVisitId,
search_access_point: "urlbar_handoff",
telemetry_id: "example",
is_tagged: "true",
is_follow_on: "false",
},
clickRecords[0].extra,
"Must have recorded the expected information."
);
BrowserTestUtils.removeTab(tab);
}
);

View File

@ -194,7 +194,7 @@ function createEntry(
element.setAttribute("label", aMenuLabel);
if (aClosedTab.image) {
const iconURL = lazy.PlacesUIUtils.getImageURL(aClosedTab);
const iconURL = lazy.PlacesUIUtils.getImageURL(aClosedTab.image);
element.setAttribute("image", iconURL);
}
if (!aIsWindowsFragment) {

View File

@ -775,6 +775,7 @@ class TelemetryEvent {
};
let { queryContext } = this._controller._lastQueryContextWrapper || {};
this._controller.manager.notifyEngagementChange(
this._isPrivate,
"start",
@ -905,6 +906,13 @@ class TelemetryEvent {
);
let { queryContext } = this._controller._lastQueryContextWrapper || {};
if (method === "engagement" && queryContext.results?.[0].autofill) {
// Record autofill impressions upon engagement.
const type = UrlbarUtils.telemetryTypeFromResult(queryContext.results[0]);
Services.telemetry.scalarAdd(`urlbar.impression.${type}`, 1);
}
this._controller.manager.notifyEngagementChange(
this._isPrivate,
method,

View File

@ -723,10 +723,12 @@ class UrlbarInput {
* @param {nsISearchEngine} [searchEngine]
* Optional. If included and the right prefs are set, we will enter search
* mode when handing `searchString` from the fake input to the Urlbar.
* @param {string} newtabSessionId
* Optional. The id of the newtab session that handed off this search.
*
*/
handoff(searchString, searchEngine) {
this._isHandoffSession = true;
handoff(searchString, searchEngine, newtabSessionId) {
this._handoffSession = newtabSessionId;
if (UrlbarPrefs.get("shouldHandOffToSearchMode") && searchEngine) {
this.search(searchString, {
searchEngine,
@ -2350,7 +2352,7 @@ class UrlbarInput {
const isOneOff = this.view.oneOffSearchButtons.eventTargetIsAOneOff(event);
let source = "urlbar";
if (this._isHandoffSession) {
if (this._handoffSession) {
source = "urlbar-handoff";
} else if (this.searchMode && !isOneOff) {
// Without checking !isOneOff, we might record the string
@ -2365,7 +2367,11 @@ class UrlbarInput {
this.window.gBrowser.selectedBrowser,
engine,
source,
{ ...searchActionDetails, isOneOff }
{
...searchActionDetails,
isOneOff,
newtabSessionId: this._handoffSession,
}
);
}
@ -2877,7 +2883,7 @@ class UrlbarInput {
_on_blur(event) {
this.focusedViaMousedown = false;
this._isHandoffSession = false;
this._handoffSession = undefined;
// We cannot count every blur events after a missed engagement as abandoment
// because the user may have clicked on some view element that executes

View File

@ -65,6 +65,27 @@ urlbar.engagement
example by picking a result in the urlbar panel or typing a search term or URL
in the urlbar and pressing the enter key.
urlbar.impression.*
A uint recording the number of impression that was displaying when user picks
any result.
- ``autofill_about``
For about-page type autofill.
- ``autofill_adaptive``
For adaptive history type autofill.
- ``autofill_origin``
For origin type autofill.
- ``autofill_other``
Counts how many times some other type of autofill result that does not have
a specific scalar was shown. This is a fallback that is used when the code is
not properly setting a specific autofill type, and it should not normally be
used. If it appears in the data, it means we need to investigate and fix the
code that is not properly setting a specific autofill type.
- ``autofill_preloaded``
For preloaded site type autofill.
- ``autofill_url``
For url type autofill.
urlbar.tips
This is a keyed scalar whose values are uints and are incremented each time a
tip result is shown, a tip is picked, and a tip's help button is picked. The
@ -215,10 +236,21 @@ urlbar.picked.*
- ``autofill``
An origin or a URL completed the user typed text inline. This was deprecated
in Firefox 102.
- ``autofill_about``
An about-page completed the user typed text inline.
- ``autofill_adaptive``
An adaptive history completed the user typed text inline.
- ``autofill_origin``
An origin completed the user typed text inline.
- ``autofill_other``
Counts how many times some other type of autofill result that does not have
a specific keyed scalar was picked at a given index. This is a fallback that
is used when the code is not properly setting a specific autofill type, and
it should not normally be used. If it appears in the data, it means we need
to investigate and fix the code that is not properly setting a specific
autofill type.
- ``autofill_preloaded``
A preloaded site completed the user typed text inline.
- ``autofill_url``
A URL completed the user typed text inline.
- ``bookmark``

View File

@ -73,31 +73,90 @@ function assertTelemetryResults(histograms, type, index, method) {
* @param {string} searchString
* @param {string} autofilledValue
* The input's expected value after autofill occurs.
* @param {string} unpickResult
* Optional: If true, do not pick any result. Default value is false.
* @param {string} urlToSelect
* Optional: If want to select result except autofill, pass the URL.
*/
async function triggerAutofillAndPickResult(searchString, autofilledValue) {
async function triggerAutofillAndPickResult(
searchString,
autofilledValue,
unpickResult = false,
urlToSelect = null
) {
await BrowserTestUtils.withNewTab("about:blank", async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: searchString,
fireInputEvent: true,
});
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill, "Result is autofill");
Assert.equal(gURLBar.value, autofilledValue, "gURLBar.value");
Assert.equal(gURLBar.selectionStart, searchString.length, "selectionStart");
Assert.equal(gURLBar.selectionEnd, autofilledValue.length, "selectionEnd");
if (urlToSelect) {
for (let row = 0; row < UrlbarTestUtils.getResultCount(window); row++) {
const result = await UrlbarTestUtils.getDetailsOfResultAt(window, row);
if (result.url === urlToSelect) {
UrlbarTestUtils.setSelectedRowIndex(window, row);
break;
}
}
}
if (unpickResult) {
// Close popup without any action.
await UrlbarTestUtils.promisePopupClose(window);
return;
}
let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
EventUtils.synthesizeKey("KEY_Enter");
await loadPromise;
let url = autofilledValue.includes(":")
? autofilledValue
: "http://" + autofilledValue;
let url;
if (urlToSelect) {
url = urlToSelect;
} else {
url = autofilledValue.includes(":")
? autofilledValue
: "http://" + autofilledValue;
}
Assert.equal(gBrowser.currentURI.spec, url, "Loaded URL is correct");
});
}
function createOtherAutofillProvider(searchString, autofilledValue) {
return new UrlbarTestUtils.TestProvider({
priority: Infinity,
type: UrlbarUtils.PROVIDER_TYPE.HEURISTIC,
results: [
Object.assign(
new UrlbarResult(
UrlbarUtils.RESULT_TYPE.URL,
UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
{
title: "Test",
url: "http://example.com/",
}
),
{
heuristic: true,
autofill: {
value: autofilledValue,
selectionStart: searchString.length,
selectionEnd: autofilledValue.length,
// Leave out `type` to trigger "other"
},
}
),
],
});
}
// Allow more time for Mac machines so they don't time out in verify mode.
if (AppConstants.platform == "macosx") {
requestLongerTimeout(3);
@ -106,8 +165,15 @@ if (AppConstants.platform == "macosx") {
add_setup(async function() {
await PlacesUtils.history.clear();
await PlacesUtils.bookmarks.eraseEverything();
await PlacesTestUtils.clearInputHistory();
// Enable local telemetry recording for the duration of the tests.
const originalCanRecord = Services.telemetry.canRecordExtended;
Services.telemetry.canRecordExtended = true;
registerCleanupFunction(async () => {
Services.telemetry.canRecordExtended = originalCanRecord;
await PlacesTestUtils.clearInputHistory();
await PlacesUtils.history.clear();
});
});
@ -116,7 +182,7 @@ add_setup(async function() {
add_task(async function history() {
const testData = [
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/test"],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "ex",
@ -124,7 +190,7 @@ add_task(async function history() {
expected: "autofill_origin",
},
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/test"],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "exa",
@ -132,7 +198,7 @@ add_task(async function history() {
expected: "autofill_adaptive",
},
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/test"],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "exam",
@ -140,7 +206,7 @@ add_task(async function history() {
expected: "autofill_adaptive",
},
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/test"],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "example.com",
@ -148,7 +214,7 @@ add_task(async function history() {
expected: "autofill_adaptive",
},
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/test"],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "example.com/",
@ -156,7 +222,7 @@ add_task(async function history() {
expected: "autofill_adaptive",
},
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/test"],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "example.com/test",
@ -164,7 +230,7 @@ add_task(async function history() {
expected: "autofill_adaptive",
},
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/test", "http://example.org/test"],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "example.org",
@ -172,7 +238,7 @@ add_task(async function history() {
expected: "autofill_origin",
},
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/test", "http://example.com/test/url"],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "example.com/test/",
@ -180,7 +246,7 @@ add_task(async function history() {
expected: "autofill_url",
},
{
adaptiveHistoryPref: true,
useAdaptiveHistory: true,
visitHistory: [{ uri: "http://example.com/test" }],
inputHistory: [
{ uri: "http://example.com/test", input: "http://example.com/test" },
@ -190,7 +256,7 @@ add_task(async function history() {
expected: "autofill_adaptive",
},
{
adaptiveHistoryPref: false,
useAdaptiveHistory: false,
visitHistory: [{ uri: "http://example.com/test" }],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "example",
@ -198,7 +264,7 @@ add_task(async function history() {
expected: "autofill_origin",
},
{
adaptiveHistoryPref: false,
useAdaptiveHistory: false,
visitHistory: [{ uri: "http://example.com/test" }],
inputHistory: [{ uri: "http://example.com/test", input: "exa" }],
userInput: "example.com/te",
@ -208,7 +274,7 @@ add_task(async function history() {
];
for (const {
adaptiveHistoryPref,
useAdaptiveHistory,
visitHistory,
inputHistory,
userInput,
@ -222,7 +288,7 @@ add_task(async function history() {
await UrlbarUtils.addToInputHistory(uri, input);
}
UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", adaptiveHistoryPref);
UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", useAdaptiveHistory);
await triggerAutofillAndPickResult(userInput, autofilled);
@ -235,6 +301,7 @@ add_task(async function history() {
);
UrlbarPrefs.clear("autoFill.adaptiveHistory.enabled");
await PlacesTestUtils.clearInputHistory();
await PlacesUtils.history.clear();
}
});
@ -283,32 +350,7 @@ add_task(async function preloaded() {
add_task(async function other() {
let searchString = "exam";
let autofilledValue = "example.com/";
let provider = new UrlbarTestUtils.TestProvider({
priority: Infinity,
type: UrlbarUtils.PROVIDER_TYPE.HEURISTIC,
results: [
Object.assign(
new UrlbarResult(
UrlbarUtils.RESULT_TYPE.URL,
UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
{
title: "Test",
url: "http://example.com/",
}
),
{
heuristic: true,
autofill: {
value: autofilledValue,
selectionStart: searchString.length,
selectionEnd: autofilledValue.length,
// Leave out `type` to trigger "other"
},
}
),
],
});
let provider = createOtherAutofillProvider(searchString, autofilledValue);
UrlbarProvidersManager.registerProvider(provider);
let histograms = snapshotHistograms();
@ -325,3 +367,214 @@ add_task(async function other() {
await PlacesUtils.history.clear();
UrlbarProvidersManager.unregisterProvider(provider);
});
// Checks impression telemetry.
add_task(async function impression() {
const testData = [
{
description: "Adaptive history autofill and pick it",
useAdaptiveHistory: true,
visitHistory: ["http://example.com/first", "http://example.com/second"],
inputHistory: [{ uri: "http://example.com/first", input: "exa" }],
userInput: "exa",
autofilled: "example.com/first",
expected: "autofill_adaptive",
},
{
description: "Adaptive history autofill but pick another result",
useAdaptiveHistory: true,
visitHistory: ["http://example.com/first", "http://example.com/second"],
inputHistory: [{ uri: "http://example.com/first", input: "exa" }],
userInput: "exa",
urlToSelect: "http://example.com/second",
autofilled: "example.com/first",
expected: "autofill_adaptive",
},
{
description: "Adaptive history autofill but not pick any result",
unpickResult: true,
useAdaptiveHistory: true,
visitHistory: ["http://example.com/first", "http://example.com/second"],
inputHistory: [{ uri: "http://example.com/first", input: "exa" }],
userInput: "exa",
autofilled: "example.com/first",
},
{
description: "Origin autofill and pick it",
visitHistory: ["http://example.com/first", "http://example.com/second"],
userInput: "exa",
autofilled: "example.com/",
expected: "autofill_origin",
},
{
description: "Origin autofill but pick another result",
visitHistory: ["http://example.com/first", "http://example.com/second"],
userInput: "exa",
urlToSelect: "http://example.com/second",
autofilled: "example.com/",
expected: "autofill_origin",
},
{
description: "Origin autofill but not pick any result",
unpickResult: true,
visitHistory: ["http://example.com/first", "http://example.com/second"],
userInput: "exa",
autofilled: "example.com/",
},
{
description: "URL autofill and pick it",
visitHistory: ["http://example.com/first", "http://example.com/second"],
userInput: "example.com/",
autofilled: "example.com/",
expected: "autofill_url",
},
{
description: "URL autofill but pick another result",
visitHistory: ["http://example.com/first", "http://example.com/second"],
userInput: "example.com/",
urlToSelect: "http://example.com/second",
autofilled: "example.com/",
expected: "autofill_url",
},
{
description: "URL autofill but not pick any result",
unpickResult: true,
visitHistory: ["http://example.com/first", "http://example.com/second"],
userInput: "example.com/",
autofilled: "example.com/",
},
{
description: "about page autofill and pick it",
userInput: "about:a",
autofilled: "about:about",
expected: "autofill_about",
},
{
description: "about page autofill but pick another result",
userInput: "about:a",
urlToSelect: "about:addons",
autofilled: "about:about",
expected: "autofill_about",
},
{
description: "about page autofill but not pick any result",
unpickResult: true,
userInput: "about:a",
autofilled: "about:about",
},
{
description: "Preloaded site autofill and pick it",
usePreloadedSite: true,
preloadedSites: [["http://example.com/", "Example"]],
userInput: "exa",
autofilled: "example.com/",
expected: "autofill_preloaded",
},
{
description: "Preloaded site autofill but not pick any result",
unpickResult: true,
usePreloadedSite: true,
preloadedSites: [["http://example.com/", "Example"]],
userInput: "exa",
autofilled: "example.com/",
},
{
description: "Other provider's autofill and pick it",
useOtherProvider: true,
userInput: "example",
autofilled: "example.com/",
expected: "autofill_other",
},
{
description: "Other provider's autofill but not pick any result",
unpickResult: true,
useOtherProvider: true,
userInput: "example",
autofilled: "example.com/",
},
];
for (const {
description,
useAdaptiveHistory = false,
usePreloadedSite = false,
useOtherProvider = false,
unpickResult = false,
visitHistory,
inputHistory,
preloadedSites,
userInput,
select,
autofilled,
expected,
} of testData) {
info(description);
UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", useAdaptiveHistory);
if (usePreloadedSite) {
UrlbarPrefs.set("usepreloadedtopurls.enabled", true);
UrlbarPrefs.set("usepreloadedtopurls.expire_days", 100);
}
let otherProvider;
if (useOtherProvider) {
otherProvider = createOtherAutofillProvider(userInput, autofilled);
UrlbarProvidersManager.registerProvider(otherProvider);
}
if (visitHistory) {
await PlacesTestUtils.addVisits(visitHistory);
}
if (inputHistory) {
for (const { uri, input } of inputHistory) {
await UrlbarUtils.addToInputHistory(uri, input);
}
}
if (preloadedSites) {
UrlbarProviderPreloadedSites.populatePreloadedSiteStorage(preloadedSites);
}
await triggerAutofillAndPickResult(
userInput,
autofilled,
unpickResult,
select
);
const scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
if (unpickResult) {
TelemetryTestUtils.assertScalarUnset(
scalars,
"urlbar.impression.autofill_adaptive"
);
TelemetryTestUtils.assertScalarUnset(
scalars,
"urlbar.impression.autofill_origin"
);
TelemetryTestUtils.assertScalarUnset(
scalars,
"urlbar.impression.autofill_url"
);
TelemetryTestUtils.assertScalarUnset(
scalars,
"urlbar.impression.autofill_about"
);
} else {
TelemetryTestUtils.assertScalar(
scalars,
`urlbar.impression.${expected}`,
1
);
}
UrlbarPrefs.clear("autoFill.adaptiveHistory.enabled");
UrlbarPrefs.clear("usepreloadedtopurls.enabled");
UrlbarPrefs.clear("usepreloadedtopurls.expire_days");
if (otherProvider) {
UrlbarProvidersManager.unregisterProvider(otherProvider);
}
await PlacesTestUtils.clearInputHistory();
await PlacesUtils.history.clear();
}
});

View File

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Web Compatibility Interventions",
"description": "Urgent post-release fixes for web compatibility.",
"version": "102.14.0",
"version": "102.15.0",
"applications": {
"gecko": {
"id": "webcompat@mozilla.org",

View File

@ -71,3 +71,7 @@ if (window.gapi?._pl === undefined) {
zoomableimage: stub,
};
}
for (const e of document.querySelectorAll("ins.adsbygoogle")) {
e.style.maxWidth = "0px";
}

View File

@ -9,44 +9,93 @@ const TEST_URI = `
<style type='text/css'>
div {
color: red;
font-family: "courier";
}
</style>
<div></div>
<div>test</div>
`;
/*
This array will be iterated over in order and the `value` property will be used to
update the value of the first CSS declaration.
This object contains iteration steps to modify various CSS properties of the
test element, keyed by property name,.
Each value is an array which will be iterated over in order and the `value`
property will be used to update the value of the property.
The `add` and `remove` objects hold the expected values of the tracked declarations
shown in the Changes panel. If `add` or `remove` are null, it means we don't expect
any corresponding tracked declaration to show up in the Changes panel.
*/
const VALUE_CHANGE_ITERATIONS = [
// No changes should be tracked if the value did not actually change.
{
value: "red",
add: null,
remove: null,
},
// Changing the priority flag "!important" should be tracked.
{
value: "red !important",
add: { value: "red !important" },
remove: { value: "red" },
},
// Repeated changes should still show the original value as the one removed.
{
value: "blue",
add: { value: "blue" },
remove: { value: "red" },
},
// Restoring the original value should clear tracked changes.
{
value: "red",
add: null,
remove: null,
},
];
const ITERATIONS = {
color: [
// No changes should be tracked if the value did not actually change.
{
value: "red",
add: null,
remove: null,
},
// Changing the priority flag "!important" should be tracked.
{
value: "red !important",
add: { value: "red !important" },
remove: { value: "red" },
},
// Repeated changes should still show the original value as the one removed.
{
value: "blue",
add: { value: "blue" },
remove: { value: "red" },
},
// Restoring the original value should clear tracked changes.
{
value: "red",
add: null,
remove: null,
},
],
"font-family": [
// Set a value with an opening quote, missing the closing one.
// The closing quote should still appear in the "add" value.
{
value: '"ar',
add: { value: '"ar"' },
remove: { value: '"courier"' },
// For some reason we need an additional flush the first time we set a
// value with a quote. Since the ruleview is manually flushed when opened
// openRuleView, we need to pass this information all the way down to the
// setProperty helper.
needsExtraFlush: true,
},
// Add an escaped character
{
value: '"ar\\i',
add: { value: '"ar\\i"' },
remove: { value: '"courier"' },
},
// Add some more text
{
value: '"ar\\ia',
add: { value: '"ar\\ia"' },
remove: { value: '"courier"' },
},
// Remove the backslash
{
value: '"aria',
add: { value: '"aria"' },
remove: { value: '"courier"' },
},
// Add the rest of the text, still no closing quote
{
value: '"arial',
add: { value: '"arial"' },
remove: { value: '"courier"' },
},
// Restoring the original value should clear tracked changes.
{
value: '"courier"',
add: null,
remove: null,
},
],
};
add_task(async function() {
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
@ -54,15 +103,31 @@ add_task(async function() {
const { document: doc, store } = selectChangesView(inspector);
await selectNode("div", inspector);
const prop = getTextProperty(ruleView, 1, { color: "red" });
const colorProp = getTextProperty(ruleView, 1, { color: "red" });
await assertEditValue(ruleView, doc, store, colorProp, ITERATIONS.color);
const fontFamilyProp = getTextProperty(ruleView, 1, {
"font-family": '"courier"',
});
await assertEditValue(
ruleView,
doc,
store,
fontFamilyProp,
ITERATIONS["font-family"]
);
});
async function assertEditValue(ruleView, doc, store, prop, iterations) {
let onTrackChange;
for (const { value, add, remove } of VALUE_CHANGE_ITERATIONS) {
for (const { value, add, needsExtraFlush, remove } of iterations) {
onTrackChange = waitForDispatch(store, "TRACK_CHANGE");
info(`Change the CSS declaration value to ${value}`);
await setProperty(ruleView, prop, value);
await setProperty(ruleView, prop, value, {
flushCount: needsExtraFlush ? 2 : 1,
});
info("Wait for the change to be tracked");
await onTrackChange;
@ -102,4 +167,4 @@ add_task(async function() {
);
}
}
});
}

View File

@ -27,7 +27,7 @@ add_task(async function() {
let prop = rule.textProps[0];
info("Clearing the property value");
await setProperty(view, prop, null, false);
await setProperty(view, prop, null, { blurNewProperty: false });
let newValue = await getRulePropertyValue(0, 0, "background-color");
is(newValue, "", "background-color should have been unset.");
@ -44,7 +44,7 @@ add_task(async function() {
view.styleDocument.activeElement.blur();
info("Clearing the property value");
await setProperty(view, prop, null, false);
await setProperty(view, prop, null, { blurNewProperty: false });
newValue = await getRulePropertyValue(0, 0, "background-color");
is(newValue, "", "color should have been unset.");

View File

@ -353,17 +353,23 @@ var addProperty = async function(
* @param {String} value
* The new value to be used. If null is passed, then the value will be
* deleted
* @param {Boolean} blurNewProperty
* @param {Object} options
* @param {Boolean} options.blurNewProperty
* After the value has been changed, a new property would have been
* focused. This parameter is true by default, and that causes the new
* property to be blurred. Set to false if you don't want this.
* @param {number} options.flushCount
* The ruleview uses a manual flush for tests only, and some properties are
* only updated after several flush. Allow tests to trigger several flushes
* if necessary. Defaults to 1.
*/
var setProperty = async function(
view,
textProp,
value,
blurNewProperty = true
{ blurNewProperty = true, flushCount = 1 } = {}
) {
info("Set property to: " + value);
await focusEditableField(view, textProp.editor.valueSpan);
const onPreview = view.once("ruleview-changed");
@ -374,14 +380,29 @@ var setProperty = async function(
} else {
EventUtils.sendString(value, view.styleWindow);
}
info("Waiting for ruleview-changed after updating property");
view.debounce.flush();
flushCount--;
while (flushCount > 0) {
// Wait for some time before triggering a new flush to let new debounced
// functions queue in-between.
await wait(100);
view.debounce.flush();
flushCount--;
}
await onPreview;
const onValueDone = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
info("Waiting for another ruleview-changed after setting property");
await onValueDone;
if (blurNewProperty) {
info("Force blur on the active element");
view.styleDocument.activeElement.blur();
}
};

View File

@ -142,6 +142,13 @@ TableWidget.prototype = {
editBookmark: null,
scrollIntoViewOnUpdate: null,
/**
* Return true if the table body has a scrollbar.
*/
get hasScrollbar() {
return this.tbody.scrollHeight > this.tbody.clientHeight;
},
/**
* Getter for the headers context menu popup id.
*/

View File

@ -24,19 +24,50 @@ add_task(async function() {
await openStoragePanel();
info("Run the tests with tall DevTools");
await runTests();
await runTests(true);
});
async function runTests() {
async function runTests(tall) {
if (tall) {
// We need to zoom out and a tall storage panel in order to fit more than 50
// items in the table. We do this to ensure that we load enough content to
// show a scrollbar so that we can still use infinite scrolling.
zoom(0.5);
}
gUI.tree.expandAll();
await selectTreeItem(["localStorage", "https://test1.example.org"]);
checkCellLength(ITEMS_PER_PAGE);
await scroll();
checkCellLength(ITEMS_PER_PAGE * 2);
if (tall) {
if (getCellLength() === ITEMS_PER_PAGE) {
await scrollToAddItems();
await waitForStorageData("item-100", "value-100");
}
await scroll();
checkCellLength(ITEMS_PER_PAGE * 3);
if (getCellLength() === ITEMS_PER_PAGE * 2) {
await scrollToAddItems();
await waitForStorageData("item-150", "value-150");
}
if (getCellLength() === ITEMS_PER_PAGE * 3) {
await scrollToAddItems();
await waitForStorageData("item-151", "value-151");
}
} else {
checkCellLength(ITEMS_PER_PAGE);
await scrollToAddItems();
await waitForStorageData("item-100", "value-100");
checkCellLength(ITEMS_PER_PAGE * 2);
await scrollToAddItems();
await waitForStorageData("item-150", "value-150");
checkCellLength(ITEMS_PER_PAGE * 3);
await scrollToAddItems();
await waitForStorageData("item-151", "value-151");
}
is(getCellLength(), 151, "Storage table contains 151 items");
// Check that the columns are sorted in a human readable way (ascending).
checkCellValues("ASC");
@ -46,6 +77,10 @@ async function runTests() {
// Check that the columns are sorted in a human readable way (descending).
checkCellValues("DEC");
if (tall) {
zoom(1);
}
}
function checkCellValues(order) {
@ -54,6 +89,16 @@ function checkCellValues(order) {
];
cells.forEach(function(cell, index, arr) {
const i = order === "ASC" ? index + 1 : arr.length - index;
is(cell.value, `item-${i}`, `Cell value is correct (${order}).`);
is(cell.value, `item-${i}`, `Cell value is "item-${i}" (${order}).`);
});
}
async function scrollToAddItems() {
info(`Scrolling to add ${ITEMS_PER_PAGE} items`);
await scroll();
}
function zoom(zoomValue) {
const bc = BrowsingContext.getFromWindow(gPanelWindow);
bc.fullZoom = zoomValue;
}

View File

@ -1070,7 +1070,7 @@ function getCellLength() {
}
function checkCellLength(len) {
is(getCellLength(), len, `Table should initially display ${len} items`);
is(getCellLength(), len, `Table should contain ${len} items`);
}
async function scroll() {

View File

@ -11,7 +11,7 @@ Bug 1171903 - Storage Inspector endless scrolling
<script type="text/javascript">
"use strict";
for (let i = 1; i < 151; i++) {
for (let i = 1; i < 152; i++) {
localStorage.setItem(`item-${i}`, `value-${i}`);
}
</script>

View File

@ -29,6 +29,11 @@ loader.lazyRequireGetter(
"devtools/client/shared/widgets/TableWidget",
true
);
loader.lazyImporter(
this,
"DeferredTask",
"resource://gre/modules/DeferredTask.jsm"
);
loader.lazyImporter(
this,
"VariablesView",
@ -42,6 +47,9 @@ const REASON = {
UPDATE: "update",
};
// How long we wait to debounce resize events
const LAZY_RESIZE_INTERVAL_MS = 200;
// Maximum length of item name to show in context menu label - will be
// trimmed with ellipsis if it's longer.
const ITEM_NAME_MAX_LENGTH = 32;
@ -143,7 +151,7 @@ class StorageUI {
this.updateObjectSidebar = this.updateObjectSidebar.bind(this);
this.table.on(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);
this.handleScrollEnd = this.handleScrollEnd.bind(this);
this.handleScrollEnd = this.loadMoreItems.bind(this);
this.table.on(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd);
this.editItem = this.editItem.bind(this);
@ -189,6 +197,7 @@ class StorageUI {
this.onRefreshTable = this.onRefreshTable.bind(this);
this.onAddItem = this.onAddItem.bind(this);
this.onCopyItem = this.onCopyItem.bind(this);
this.onPanelWindowResize = this.#onPanelWindowResize.bind(this);
this.onRemoveItem = this.onRemoveItem.bind(this);
this.onRemoveAllFrom = this.onRemoveAllFrom.bind(this);
this.onRemoveAll = this.onRemoveAll.bind(this);
@ -201,6 +210,8 @@ class StorageUI {
this._addButton = this._panelDoc.getElementById("add-button");
this._addButton.addEventListener("click", this.onAddItem);
this._window.addEventListener("resize", this.onPanelWindowResize, true);
this._variableViewPopupCopy = this._panelDoc.getElementById(
"variable-view-popup-copy"
);
@ -413,6 +424,11 @@ class StorageUI {
}
destroy() {
if (this._destroyed) {
return;
}
this._destroyed = true;
const { resourceCommand } = this._toolbox;
resourceCommand.unwatchResources(
[
@ -429,7 +445,7 @@ class StorageUI {
);
this.table.off(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);
this.table.off(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd);
this.table.off(TableWidget.EVENTS.SCROLL_END, this.loadMoreItems);
this.table.off(TableWidget.EVENTS.CELL_EDIT, this.editItem);
this.table.destroy();
@ -443,6 +459,8 @@ class StorageUI {
);
this.sidebarToggleBtn = null;
this._window.removeEventListener("resize", this.#onPanelWindowResize, true);
this._treePopup.removeEventListener(
"popupshowing",
this.onTreePopupShowing
@ -673,6 +691,40 @@ class StorageUI {
}
}
/**
* Debounce the window resize event by calling _onLazyPanelResize() if
* the required amount of time has passed.
*/
#onPanelWindowResize() {
if (this._toolbox.currentToolId !== "storage") {
return;
}
if (!this._lazyResizeHandler) {
this._lazyResizeHandler = new DeferredTask(
this.#onLazyPanelResize.bind(this),
LAZY_RESIZE_INTERVAL_MS,
0
);
}
this._lazyResizeHandler.arm();
}
/**
* If the panel is resized we need to check if we should load the next batch of
* storage items.
*/
async #onLazyPanelResize() {
// We can be called on a closed window or destroyed toolbox because of the
// deferred task.
if (this._window.closed || this._destroyed || this.table.hasScrollbar) {
return;
}
await this.loadMoreItems();
this.emit("storage-resize");
}
/**
* Get a string for a column name automatically choosing whether or not the
* string should be localized.
@ -899,9 +951,13 @@ class StorageUI {
await this.resetColumns(type, host, subType);
}
const { data } = await storage.getStoreObjects(host, names, fetchOpts);
const { data, total } = await storage.getStoreObjects(
host,
names,
fetchOpts
);
if (data.length) {
await this.populateTable(data, reason);
await this.populateTable(data, reason, total);
} else if (reason === REASON.POPULATE) {
await this.clearHeaders();
}
@ -1235,8 +1291,9 @@ class StorageUI {
if (item.length > 2) {
names = [JSON.stringify(item.slice(2))];
}
await this.fetchStorageObjects(type, host, names, REASON.POPULATE);
this.itemOffset = 0;
await this.fetchStorageObjects(type, host, names, REASON.POPULATE);
}
/**
@ -1312,8 +1369,10 @@ class StorageUI {
* Array of objects to be populated in the storage table
* @param {Constant} reason
* See REASON constant at top of file.
* @param {number} totalAvailable
* The total number of items available in the current storage type.
*/
async populateTable(data, reason) {
async populateTable(data, reason, totalAvailable) {
for (const item of data) {
if (item.value) {
item.valueActor = item.value;
@ -1351,6 +1410,14 @@ class StorageUI {
this.shouldLoadMoreItems = true;
}
if (
(reason === REASON.POPULATE || reason === REASON.NEXT_50_ITEMS) &&
this.table.items.size < totalAvailable &&
!this.table.hasScrollbar
) {
await this.loadMoreItems();
}
}
/**
@ -1390,9 +1457,9 @@ class StorageUI {
}
/**
* Handles endless scrolling for the table
* Load the next batch of 50 items
*/
async handleScrollEnd() {
async loadMoreItems() {
if (!this.shouldLoadMoreItems) {
return;
}

View File

@ -8,7 +8,6 @@ const protocol = require("devtools/shared/protocol");
const { getCSSLexer } = require("devtools/shared/css/lexer");
const InspectorUtils = require("InspectorUtils");
const TrackChangeEmitter = require("devtools/server/actors/utils/track-change-emitter");
const {
getRuleText,
getTextAtLineColumn,
@ -93,6 +92,8 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
// this.form().declarations on demand because that would cause needless re-parsing.
this._declarations = [];
this._pendingDeclarationChanges = [];
if (CSSRule.isInstance(item)) {
this.type = item.type;
this.rawRule = item;
@ -459,6 +460,16 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
return decl;
});
// We have computed the new `declarations` array, before forgetting about
// the old declarations compute the CSS changes for pending modifications
// applied by the user. Comparing the old and new declarations arrays
// ensures we only rely on values understood by the engine and not authored
// values. See Bug 1590031.
this._pendingDeclarationChanges.forEach(change =>
this.logDeclarationChange(change, declarations, this._declarations)
);
this._pendingDeclarationChanges = [];
// Cache parsed declarations so we don't needlessly re-parse authoredText every time
// we need to check previous property names and values when tracking changes.
this._declarations = declarations;
@ -711,9 +722,6 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
throw new Error("invalid call to setRuleText");
}
// Log the changes before applying them so we have access to the previous values.
modifications.map(mod => this.logDeclarationChange(mod));
if (this.type === ELEMENT_STYLE) {
// For element style rules, set the node's style attribute.
this.rawNode.setAttributeDevtools("style", newText);
@ -751,6 +759,11 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
this.authoredText = newText;
this.pageStyle.refreshObservedRules();
// Add processed modifications to the _pendingDeclarationChanges array,
// they will be emitted as CSS_CHANGE resources once `declarations` have
// been re-computed in `form`.
this._pendingDeclarationChanges.push(...modifications);
// Returning this updated actor over the protocol will update its corresponding front
// and any references to it.
return this;
@ -794,7 +807,6 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
const tempElement = document.createElementNS(XHTML_NS, "div");
for (const mod of modifications) {
this.logDeclarationChange(mod);
if (mod.type === "set") {
tempElement.style.setProperty(mod.name, mod.value, mod.priority || "");
this.rawStyle.setProperty(
@ -809,6 +821,11 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
this.pageStyle.refreshObservedRules();
// Add processed modifications to the _pendingDeclarationChanges array,
// they will be emitted as CSS_CHANGE resources once `declarations` have
// been re-computed in `form`.
this._pendingDeclarationChanges.push(...modifications);
return this;
},
@ -912,8 +929,12 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
*
* @param {Object} change
* Data about a modification to a declaration. @see |modifyProperties()|
* @param {Object} newDeclarations
* The current declarations array to get the latest values, names...
* @param {Object} oldDeclarations
* The previous declarations array to use to fetch old values, names...
*/
logDeclarationChange(change) {
logDeclarationChange(change, newDeclarations, oldDeclarations) {
// Position of the declaration within its rule.
const index = change.index;
// Destructure properties from the previous CSS declaration at this index, if any,
@ -923,7 +944,9 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
name: prevName,
priority: prevPriority,
commentOffsets,
} = this._declarations[index] || {};
} = oldDeclarations[index] || {};
const { value: currentValue } = newDeclarations[index] || {};
// A declaration is disabled if it has a `commentOffsets` array.
// Here we type coerce the value to a boolean with double-bang (!!)
const prevDisabled = !!commentOffsets;
@ -941,9 +964,12 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
// declaration is being updated. In that case, use the provided `change.name`.
const name = change.newName ? change.newName : change.name;
// Append the "!important" string if defined in the incoming priority flag.
const changeValue = currentValue || change.value;
const newValue = change.priority
? `${change.value} !important`
: change.value;
? `${changeValue} !important`
: changeValue;
// Reuse the previous value string, when the property is renamed.
// Otherwise, use the incoming value string.
const value = change.newName ? prevValue : newValue;

View File

@ -66,30 +66,18 @@ async function addTestEngines() {
// WebExtensions need is only defined for browser/
await Services.search.addPolicyEngine({
description: "urifixup search engine",
chrome_settings_overrides: {
search_provider: {
name: kSearchEngineID,
search_url: kSearchEngineURL,
},
},
name: kSearchEngineID,
search_url: kSearchEngineURL,
});
await Services.search.addPolicyEngine({
description: "urifixup private search engine",
chrome_settings_overrides: {
search_provider: {
name: kPrivateSearchEngineID,
search_url: kPrivateSearchEngineURL,
},
},
name: kPrivateSearchEngineID,
search_url: kPrivateSearchEngineURL,
});
await Services.search.addPolicyEngine({
description: "urifixup POST search engine",
chrome_settings_overrides: {
search_provider: {
name: kPostSearchEngineID,
search_url: kPostSearchEngineURL,
search_url_post_params: kPostSearchEngineData,
},
},
name: kPostSearchEngineID,
search_url: kPostSearchEngineURL,
search_url_post_params: kPostSearchEngineData,
});
}

View File

@ -14778,8 +14778,7 @@ void Document::GetWireframeWithoutFlushing(bool aIncludeNodes,
}
bool drawImage = false;
bool drawColor = false;
ComputedStyle* bgStyle = nullptr;
if (nsCSSRendering::FindBackground(frame, &bgStyle)) {
if (const auto* bgStyle = nsCSSRendering::FindBackground(frame)) {
const nscolor color = nsCSSRendering::DetermineBackgroundColor(
pc, bgStyle, frame, drawImage, drawColor);
if (drawImage &&

View File

@ -183,6 +183,8 @@
#include "mozilla/dom/XRPermissionRequest.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/glean/bindings/Glean.h"
#include "mozilla/glean/bindings/GleanPings.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/fallible.h"
#include "mozilla/gfx/BasePoint.h"

View File

@ -44,8 +44,6 @@
#include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/WindowBinding.h"
#include "mozilla/dom/WindowProxyHolder.h"
#include "mozilla/glean/bindings/Glean.h"
#include "mozilla/glean/bindings/GleanPings.h"
#include "Units.h"
#include "nsCheapSets.h"
#include "mozilla/dom/ImageBitmapBinding.h"
@ -91,6 +89,11 @@ namespace mozilla {
class AbstractThread;
class ErrorResult;
namespace glean {
class Glean;
class GleanPings;
} // namespace glean
namespace hal {
enum class ScreenOrientation : uint32_t;
}

View File

@ -3139,7 +3139,7 @@ void BrowserChild::ReinitRendering() {
// In some cases, like when we create a windowless browser,
// RemoteLayerTreeOwner/BrowserChild is not connected to a compositor.
if (mLayersConnected.isNothing() || !*mLayersConnected) {
if (mLayersConnected.isNothing()) {
return;
}

View File

@ -138,6 +138,7 @@ PerformanceTimingData::PerformanceTimingData(nsITimedChannel* aChannel,
if (aChannel) {
aChannel->GetAsyncOpen(&mAsyncOpen);
aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
aChannel->GetAllRedirectsPassTimingAllowCheck(&mAllRedirectsPassTAO);
aChannel->GetRedirectCount(&mRedirectCount);
aChannel->GetRedirectStart(&mRedirectStart);
aChannel->GetRedirectEnd(&mRedirectEnd);

View File

@ -8,6 +8,12 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { PushRecord } = ChromeUtils.import(
"resource://gre/modules/PushRecord.jsm"
);
const { Preferences } = ChromeUtils.import(
"resource://gre/modules/Preferences.jsm"
);
const lazy = {};
@ -16,11 +22,6 @@ ChromeUtils.defineModuleGetter(
"PushDB",
"resource://gre/modules/PushDB.jsm"
);
ChromeUtils.defineModuleGetter(
lazy,
"PushRecord",
"resource://gre/modules/PushRecord.jsm"
);
ChromeUtils.defineModuleGetter(
lazy,
"PushCrypto",
@ -31,11 +32,6 @@ ChromeUtils.defineModuleGetter(
"EventDispatcher",
"resource://gre/modules/Messaging.jsm"
);
ChromeUtils.defineModuleGetter(
lazy,
"Preferences",
"resource://gre/modules/Preferences.jsm"
);
XPCOMUtils.defineLazyGetter(lazy, "Log", () => {
return ChromeUtils.import(
@ -60,7 +56,7 @@ const kPUSHANDROIDGCMDB_STORE_NAME = "pushAndroidGCM";
const FXA_PUSH_SCOPE = "chrome://fxa-push";
const prefs = new lazy.Preferences("dom.push.");
const prefs = new Preferences("dom.push.");
/**
* The implementation of WebPush push backed by Android's GCM
@ -311,11 +307,11 @@ var PushServiceAndroidGCM = {
};
function PushRecordAndroidGCM(record) {
lazy.PushRecord.call(this, record);
PushRecord.call(this, record);
this.channelID = record.channelID;
}
PushRecordAndroidGCM.prototype = Object.create(lazy.PushRecord.prototype, {
PushRecordAndroidGCM.prototype = Object.create(PushRecord.prototype, {
keyID: {
get() {
return this.channelID;

View File

@ -1562,6 +1562,46 @@ nsresult nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) {
return NS_OK;
}
// https://fetch.spec.whatwg.org/#serializing-a-request-origin
void nsContentSecurityManager::GetSerializedOrigin(
nsIPrincipal* aOrigin, nsIPrincipal* aResourceOrigin,
nsACString& aSerializedOrigin, nsILoadInfo* aLoadInfo) {
// The following for loop performs the
// https://fetch.spec.whatwg.org/#ref-for-concept-request-tainted-origin
nsCOMPtr<nsIPrincipal> lastOrigin;
for (nsIRedirectHistoryEntry* entry : aLoadInfo->RedirectChain()) {
if (!lastOrigin) {
entry->GetPrincipal(getter_AddRefs(lastOrigin));
continue;
}
nsCOMPtr<nsIPrincipal> currentOrigin;
entry->GetPrincipal(getter_AddRefs(currentOrigin));
if (!currentOrigin->Equals(lastOrigin) && !lastOrigin->Equals(aOrigin)) {
return;
}
lastOrigin = currentOrigin;
}
// When the redirectChain is empty, it means this is the first redirect.
// So according to the #serializing-a-request-origin spec, we don't
// have a redirect-tainted origin, so we return the origin of the request
// here.
if (!lastOrigin) {
aOrigin->GetAsciiOrigin(aSerializedOrigin);
return;
}
// Same as above, redirectChain doesn't contain the current redirect,
// so we have to do the check one last time here.
if (lastOrigin->Equals(aResourceOrigin) && !lastOrigin->Equals(aOrigin)) {
return;
}
aOrigin->GetAsciiOrigin(aSerializedOrigin);
}
// ==== nsIContentSecurityManager implementation =====
NS_IMETHODIMP

View File

@ -71,6 +71,10 @@ class nsContentSecurityManager : public nsIContentSecurityManager,
static nsSecurityFlags ComputeSecurityFlags(
mozilla::CORSMode aCORSMode, CORSSecurityMapping aCORSSecurityMapping);
static void GetSerializedOrigin(nsIPrincipal* aOrigin,
nsIPrincipal* aResourceOrigin,
nsACString& aResult, nsILoadInfo* aLoadInfo);
private:
static nsresult CheckChannel(nsIChannel* aChannel);
static nsresult CheckFTPSubresourceLoad(nsIChannel* aChannel);

View File

@ -747,6 +747,56 @@ static EditorDOMPoint GetPointAfterFollowingLineBreakOrAtFollowingBlock(
return point;
}
void AutoRangeArray::ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction aEditSubAction, const Element& aEditingHost) {
// FYI: This is originated in
// https://searchfox.org/mozilla-central/rev/1739f1301d658c9bff544a0a095ab11fca2e549d/editor/libeditor/HTMLEditSubActionHandler.cpp#6712
bool removeSomeRanges = false;
for (OwningNonNull<nsRange>& range : mRanges) {
// Remove non-positioned ranges.
if (MOZ_UNLIKELY(!range->IsPositioned())) {
removeSomeRanges = true;
continue;
}
// If the range is native anonymous subtrees, we must meet a bug of
// `Selection` so that we need to hack here.
if (MOZ_UNLIKELY(range->GetStartContainer()->IsInNativeAnonymousSubtree() ||
range->GetEndContainer()->IsInNativeAnonymousSubtree())) {
EditorRawDOMRange rawRange(range);
if (!rawRange.EnsureNotInNativeAnonymousSubtree()) {
range->Reset();
removeSomeRanges = true;
continue;
}
if (NS_FAILED(
range->SetStartAndEnd(rawRange.StartRef().ToRawRangeBoundary(),
rawRange.EndRef().ToRawRangeBoundary())) ||
MOZ_UNLIKELY(!range->IsPositioned())) {
range->Reset();
removeSomeRanges = true;
continue;
}
}
// Finally, extend the range.
if (NS_FAILED(ExtendRangeToWrapStartAndEndLinesContainingBoundaries(
range, aEditSubAction, aEditingHost))) {
// If we failed to extend the range, we should use the original range
// as-is unless the range is broken at setting the range.
if (NS_WARN_IF(!range->IsPositioned())) {
removeSomeRanges = true;
}
}
}
if (removeSomeRanges) {
for (size_t i : Reversed(IntegerRange(mRanges.Length()))) {
if (!mRanges[i]->IsPositioned()) {
mRanges.RemoveElementAt(i);
}
}
}
}
// static
nsresult AutoRangeArray::ExtendRangeToWrapStartAndEndLinesContainingBoundaries(
nsRange& aRange, EditSubAction aEditSubAction,

View File

@ -345,6 +345,13 @@ class MOZ_STACK_CLASS AutoRangeArray final {
*/
void EnsureRangesInTextNode(const dom::Text& aTextNode);
/**
* Extend ranges to wrap lines to handle block level edit actions such as
* updating the block parent or indent/outdent around the selection.
*/
void ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction aEditSubAction, const dom::Element& aEditingHost);
/**
* Check whether the range is in aEditingHost and both containers of start and
* end boundaries of the range are editable.

View File

@ -57,8 +57,13 @@ nsresult HTMLEditor::SetSelectionToAbsoluteOrStaticAsAction(
return rv;
}
const RefPtr<Element> editingHost = ComputeEditingHost();
if (!editingHost) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
if (aEnabled) {
EditActionResult result = SetSelectionToAbsoluteAsSubAction();
EditActionResult result = SetSelectionToAbsoluteAsSubAction(*editingHost);
NS_WARNING_ASSERTION(
result.Succeeded(),
"HTMLEditor::SetSelectionToAbsoluteAsSubAction() failed");

View File

@ -1481,7 +1481,8 @@ nsresult HTMLEditor::InsertLineBreakAsSubAction() {
return rv;
}
EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction(
const Element& aEditingHost) {
if (NS_WARN_IF(!mInitSucceeded)) {
return EditActionIgnored(NS_ERROR_NOT_INITIALIZED);
}
@ -1550,11 +1551,6 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
}
}
RefPtr<Element> editingHost = ComputeEditingHost();
if (NS_WARN_IF(!editingHost)) {
return EditActionIgnored(NS_ERROR_FAILURE);
}
if (IsMailEditor()) {
const auto pointToSplit = GetFirstSelectionStartPoint<EditorDOMPoint>();
if (NS_WARN_IF(!pointToSplit.IsInContentNode())) {
@ -1567,7 +1563,7 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
// table cell boundaries?
Result<EditorDOMPoint, nsresult> atNewBRElementOrError =
HandleInsertParagraphInMailCiteElement(*mailCiteElement, pointToSplit,
*editingHost);
aEditingHost);
if (MOZ_UNLIKELY(atNewBRElementOrError.isErr())) {
NS_WARNING(
"HTMLEditor::HandleInsertParagraphInMailCiteElement() failed");
@ -1612,11 +1608,11 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
// insert new paragraph nor <br> element.
// XXX Currently, we don't support editing outside <body> element, but Blink
// does it.
if (editingHost->GetParentElement() &&
HTMLEditUtils::IsSimplyEditableNode(*editingHost->GetParentElement()) &&
if (aEditingHost.GetParentElement() &&
HTMLEditUtils::IsSimplyEditableNode(*aEditingHost.GetParentElement()) &&
(!atStartOfSelection.IsInContentNode() ||
!nsContentUtils::ContentIsFlattenedTreeDescendantOf(
atStartOfSelection.ContainerAsContent(), editingHost))) {
atStartOfSelection.ContainerAsContent(), &aEditingHost))) {
return EditActionHandled(NS_ERROR_EDITOR_NO_EDITABLE_RANGE);
}
@ -1644,9 +1640,9 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
else if (!HTMLEditUtils::IsSplittableNode(*editableBlockElement)) {
insertLineBreak =
separator == ParagraphSeparator::br ||
!HTMLEditUtils::CanElementContainParagraph(*editingHost) ||
!HTMLEditUtils::CanElementContainParagraph(aEditingHost) ||
HTMLEditUtils::ShouldInsertLinefeedCharacter(atStartOfSelection,
*editingHost);
aEditingHost);
}
// If the nearest block parent is a single-line container declared in
// the execCommand spec and not the editing host, we should separate the
@ -1675,8 +1671,8 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
// paragraph separator is set to "br" which is Gecko-specific mode.
if (separator != ParagraphSeparator::br &&
HTMLEditUtils::ShouldInsertLinefeedCharacter(atStartOfSelection,
*editingHost)) {
nsresult rv = HandleInsertLinefeed(atStartOfSelection, *editingHost);
aEditingHost)) {
nsresult rv = HandleInsertLinefeed(atStartOfSelection, aEditingHost);
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::HandleInsertLinefeed() failed");
return EditActionResult(rv);
@ -1685,7 +1681,7 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
}
CreateElementResult insertBRElementResult =
HandleInsertBRElement(atStartOfSelection, *editingHost);
HandleInsertBRElement(atStartOfSelection, aEditingHost);
if (insertBRElementResult.isErr()) {
NS_WARNING("HTMLEditor::HandleInsertBRElement() failed");
return EditActionHandled(insertBRElementResult.unwrapErr());
@ -1707,7 +1703,8 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
// Therefore, even if it returns NS_OK, editor might have been destroyed
// at restoring Selection.
nsresult rv = FormatBlockContainerWithTransaction(
MOZ_KnownLive(HTMLEditor::ToParagraphSeparatorTagName(separator)));
MOZ_KnownLive(HTMLEditor::ToParagraphSeparatorTagName(separator)),
aEditingHost);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
NS_WARN_IF(Destroyed())) {
return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
@ -1738,7 +1735,7 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
if (NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(*editableBlockElement))) {
// Didn't create a new block for some reason, fall back to <br>
CreateElementResult insertBRElementResult =
HandleInsertBRElement(atStartOfSelection, *editingHost);
HandleInsertBRElement(atStartOfSelection, aEditingHost);
if (insertBRElementResult.isErr()) {
NS_WARNING("HTMLEditor::HandleInsertBRElement() failed");
return EditActionResult(insertBRElementResult.unwrapErr());
@ -1793,12 +1790,12 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
RefPtr<Element> maybeNonEditableListItem =
HTMLEditUtils::GetClosestAncestorListItemElement(*editableBlockElement,
editingHost);
&aEditingHost);
if (maybeNonEditableListItem &&
HTMLEditUtils::IsSplittableNode(*maybeNonEditableListItem)) {
Result<EditorDOMPoint, nsresult> pointToPutCaretOrError =
HandleInsertParagraphInListItemElement(
*maybeNonEditableListItem, atStartOfSelection, *editingHost);
*maybeNonEditableListItem, atStartOfSelection, aEditingHost);
if (MOZ_UNLIKELY(pointToPutCaretOrError.isErr())) {
if (NS_WARN_IF(pointToPutCaretOrError.unwrapErr() ==
NS_ERROR_EDITOR_DESTROYED)) {
@ -1884,7 +1881,7 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
// If nobody handles this edit action, let's insert new <br> at the selection.
CreateElementResult insertBRElementResult =
HandleInsertBRElement(atStartOfSelection, *editingHost);
HandleInsertBRElement(atStartOfSelection, aEditingHost);
if (insertBRElementResult.isErr()) {
NS_WARNING("HTMLEditor::HandleInsertBRElement() failed");
return EditActionIgnored(insertBRElementResult.unwrapErr());
@ -1898,7 +1895,7 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
}
CreateElementResult HTMLEditor::HandleInsertBRElement(
const EditorDOMPoint& aPointToBreak, Element& aEditingHost) {
const EditorDOMPoint& aPointToBreak, const Element& aEditingHost) {
MOZ_ASSERT(aPointToBreak.IsSet());
MOZ_ASSERT(IsEditActionDataAvailable());
@ -2052,7 +2049,7 @@ CreateElementResult HTMLEditor::HandleInsertBRElement(
}
nsresult HTMLEditor::HandleInsertLinefeed(const EditorDOMPoint& aPointToBreak,
Element& aEditingHost) {
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (NS_WARN_IF(!aPointToBreak.IsSet())) {
@ -2194,7 +2191,7 @@ nsresult HTMLEditor::HandleInsertLinefeed(const EditorDOMPoint& aPointToBreak,
Result<EditorDOMPoint, nsresult>
HTMLEditor::HandleInsertParagraphInMailCiteElement(
Element& aMailCiteElement, const EditorDOMPoint& aPointToSplit,
Element& aEditingHost) {
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aPointToSplit.IsSet());
NS_ASSERTION(!HTMLEditUtils::IsEmptyNode(aMailCiteElement),
@ -3037,9 +3034,13 @@ EditActionResult HTMLEditor::MakeOrChangeListAndListItemAsSubAction(
// ChangeSelectedHardLinesToList() creates AutoSelectionRestorer.
// Therefore, even if it returns NS_OK, editor might have been destroyed
// at restoring Selection.
result = ChangeSelectedHardLinesToList(MOZ_KnownLive(*listTagName),
MOZ_KnownLive(*listItemTagName),
aBulletType, aSelectAllOfCurrentList);
const RefPtr<Element> editingHost = ComputeEditingHost();
if (MOZ_UNLIKELY(!editingHost)) {
return EditActionIgnored(NS_SUCCESS_DOM_NO_OPERATION);
}
result = ChangeSelectedHardLinesToList(
MOZ_KnownLive(*listTagName), MOZ_KnownLive(*listItemTagName), aBulletType,
aSelectAllOfCurrentList, *editingHost);
if (NS_WARN_IF(Destroyed())) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
@ -3051,15 +3052,11 @@ EditActionResult HTMLEditor::MakeOrChangeListAndListItemAsSubAction(
EditActionResult HTMLEditor::ChangeSelectedHardLinesToList(
nsAtom& aListElementTagName, nsAtom& aListItemElementTagName,
const nsAString& aBulletType,
SelectAllOfCurrentList aSelectAllOfCurrentList) {
SelectAllOfCurrentList aSelectAllOfCurrentList,
const Element& aEditingHost) {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
MOZ_ASSERT(!IsSelectionRangeContainerNotContent());
RefPtr<Element> editingHost = ComputeEditingHost();
if (MOZ_UNLIKELY(NS_WARN_IF(!editingHost))) {
return EditActionResult(NS_ERROR_FAILURE);
}
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
@ -3073,11 +3070,11 @@ EditActionResult HTMLEditor::ChangeSelectedHardLinesToList(
} else {
AutoTransactionsConserveSelection dontChangeMySelection(*this);
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
GetSelectionRangesExtendedToHardLineStartAndEnd(
extendedSelectionRanges, EditSubAction::eCreateOrChangeList);
AutoRangeArray extendedSelectionRanges(SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eCreateOrChangeList, aEditingHost);
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
extendedSelectionRanges, arrayOfContents,
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eCreateOrChangeList, CollectNonEditableNodes::No);
if (NS_FAILED(rv)) {
NS_WARNING(
@ -3134,7 +3131,7 @@ EditActionResult HTMLEditor::ChangeSelectedHardLinesToList(
CreateElementResult createNewListElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
aListElementTagName, atStartOfSelection,
BRElementNextToSplitPoint::Keep, *editingHost,
BRElementNextToSplitPoint::Keep, aEditingHost,
// MOZ_CAN_RUN_SCRIPT_BOUNDARY due to bug 1758868
[&newListItemElement, &aListItemElementTagName](
HTMLEditor& aHTMLEditor, Element& aListElement,
@ -3531,7 +3528,7 @@ EditActionResult HTMLEditor::ChangeSelectedHardLinesToList(
CreateElementResult createNewListElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
aListElementTagName, atContent, BRElementNextToSplitPoint::Keep,
*editingHost);
aEditingHost);
if (createNewListElementResult.isErr()) {
NS_WARNING(
nsPrintfCString(
@ -3661,7 +3658,8 @@ EditActionResult HTMLEditor::ChangeSelectedHardLinesToList(
return EditActionHandled();
}
nsresult HTMLEditor::RemoveListAtSelectionAsSubAction() {
nsresult HTMLEditor::RemoveListAtSelectionAsSubAction(
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
EditActionResult result = CanHandleHTMLEditSubAction();
@ -3699,11 +3697,11 @@ nsresult HTMLEditor::RemoveListAtSelectionAsSubAction() {
{
AutoTransactionsConserveSelection dontChangeMySelection(*this);
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
GetSelectionRangesExtendedToHardLineStartAndEnd(
extendedSelectionRanges, EditSubAction::eCreateOrChangeList);
AutoRangeArray extendedSelectionRanges(SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eCreateOrChangeList, aEditingHost);
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
extendedSelectionRanges, arrayOfContents,
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eCreateOrChangeList, CollectNonEditableNodes::No);
if (NS_FAILED(rv)) {
NS_WARNING(
@ -3752,14 +3750,10 @@ nsresult HTMLEditor::RemoveListAtSelectionAsSubAction() {
return NS_OK;
}
nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
nsresult HTMLEditor::FormatBlockContainerWithTransaction(
nsAtom& blockType, const Element& aEditingHost) {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
const RefPtr<Element> editingHost = ComputeEditingHost();
if (MOZ_UNLIKELY(NS_WARN_IF(!editingHost))) {
return NS_ERROR_FAILURE;
}
if (!SelectionRef().IsCollapsed()) {
nsresult rv = MaybeExtendSelectionToHardLineEdgesForBlockEditAction();
if (NS_FAILED(rv)) {
@ -3775,11 +3769,11 @@ nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
{
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
GetSelectionRangesExtendedToHardLineStartAndEnd(
extendedSelectionRanges, EditSubAction::eCreateOrRemoveBlock);
AutoRangeArray extendedSelectionRanges(SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eCreateOrRemoveBlock, aEditingHost);
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
extendedSelectionRanges, arrayOfContents,
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eCreateOrRemoveBlock, CollectNonEditableNodes::Yes);
if (NS_FAILED(rv)) {
NS_WARNING(
@ -3826,7 +3820,7 @@ nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
// which is visually bad.
if (nsCOMPtr<nsIContent> brContent = HTMLEditUtils::GetNextContent(
pointToInsertBlock, {WalkTreeOption::IgnoreNonEditableNode},
editingHost)) {
&aEditingHost)) {
if (brContent && brContent->IsHTMLElement(nsGkAtoms::br)) {
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
nsresult rv = DeleteNodeWithTransaction(*brContent);
@ -3868,7 +3862,7 @@ nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
pointToInsertBlock,
{WalkTreeOption::IgnoreNonEditableNode,
WalkTreeOption::StopAtBlockBoundary},
editingHost)) {
&aEditingHost)) {
if (maybeBRContent->IsHTMLElement(nsGkAtoms::br)) {
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
nsresult rv = DeleteNodeWithTransaction(*maybeBRContent);
@ -3884,7 +3878,7 @@ nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
CreateElementResult createNewBlockElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
blockType, pointToInsertBlock, BRElementNextToSplitPoint::Keep,
*editingHost);
aEditingHost);
if (createNewBlockElementResult.isErr()) {
NS_WARNING(
nsPrintfCString(
@ -3932,7 +3926,7 @@ nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
if (&blockType == nsGkAtoms::blockquote) {
Result<EditorDOMPoint, nsresult> wrapContentsInBlockquoteElementsResult =
WrapContentsInBlockquoteElementsWithTransaction(arrayOfContents,
*editingHost);
aEditingHost);
if (wrapContentsInBlockquoteElementsResult.isErr()) {
NS_WARNING(
"HTMLEditor::WrapContentsInBlockquoteElementsWithTransaction() "
@ -3957,7 +3951,7 @@ nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
}
Result<EditorDOMPoint, nsresult> wrapContentsInBlockElementResult =
CreateOrChangeBlockContainerElement(arrayOfContents, blockType,
*editingHost);
aEditingHost);
if (MOZ_UNLIKELY(wrapContentsInBlockElementResult.isErr())) {
NS_WARNING("HTMLEditor::CreateOrChangeBlockContainerElement() failed");
return wrapContentsInBlockElementResult.unwrapErr();
@ -3996,7 +3990,7 @@ nsresult HTMLEditor::MaybeInsertPaddingBRElementForEmptyLastLineAtSelection() {
return rv;
}
EditActionResult HTMLEditor::IndentAsSubAction() {
EditActionResult HTMLEditor::IndentAsSubAction(const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
AutoPlaceholderBatch treatAsOneTransaction(
@ -4023,7 +4017,7 @@ EditActionResult HTMLEditor::IndentAsSubAction() {
return EditActionIgnored();
}
result |= HandleIndentAtSelection();
result |= HandleIndentAtSelection(aEditingHost);
if (result.Failed() || result.Canceled()) {
NS_WARNING_ASSERTION(result.Succeeded(),
"HTMLEditor::HandleIndentAtSelection() failed");
@ -4182,7 +4176,8 @@ nsresult HTMLEditor::IndentListChild(RefPtr<Element>* aCurList,
return NS_OK;
}
EditActionResult HTMLEditor::HandleIndentAtSelection() {
EditActionResult HTMLEditor::HandleIndentAtSelection(
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!IsSelectionRangeContainerNotContent());
@ -4219,18 +4214,18 @@ EditActionResult HTMLEditor::HandleIndentAtSelection() {
}
if (IsCSSEnabled()) {
nsresult rv = HandleCSSIndentAtSelection();
nsresult rv = HandleCSSIndentAtSelection(aEditingHost);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::HandleCSSIndentAtSelection() failed");
return EditActionHandled(rv);
}
rv = HandleHTMLIndentAtSelection();
rv = HandleHTMLIndentAtSelection(aEditingHost);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::HandleHTMLIndent() failed");
return EditActionHandled(rv);
}
nsresult HTMLEditor::HandleCSSIndentAtSelection() {
nsresult HTMLEditor::HandleCSSIndentAtSelection(const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!IsSelectionRangeContainerNotContent());
@ -4247,7 +4242,7 @@ nsresult HTMLEditor::HandleCSSIndentAtSelection() {
// HandleCSSIndentAtSelectionInternal() creates AutoSelectionRestorer.
// Therefore, even if it returns NS_OK, editor might have been destroyed
// at restoring Selection.
nsresult rv = HandleCSSIndentAtSelectionInternal();
nsresult rv = HandleCSSIndentAtSelectionInternal(aEditingHost);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
@ -4257,15 +4252,11 @@ nsresult HTMLEditor::HandleCSSIndentAtSelection() {
return rv;
}
nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal(
const Element& aEditingHost) {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
MOZ_ASSERT(!IsSelectionRangeContainerNotContent());
RefPtr<Element> editingHost = ComputeEditingHost();
if (MOZ_UNLIKELY(NS_WARN_IF(!editingHost))) {
return NS_ERROR_FAILURE;
}
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
@ -4289,12 +4280,12 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
}
if (arrayOfContents.IsEmpty()) {
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
GetSelectionRangesExtendedToHardLineStartAndEnd(extendedSelectionRanges,
EditSubAction::eIndent);
AutoRangeArray extendedSelectionRanges(SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eIndent, aEditingHost);
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
extendedSelectionRanges, arrayOfContents, EditSubAction::eIndent,
CollectNonEditableNodes::Yes);
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eIndent, CollectNonEditableNodes::Yes);
if (NS_FAILED(rv)) {
NS_WARNING(
"SplitInlinesAndCollectEditTargetNodes(EditSubAction::eIndent, "
@ -4322,7 +4313,7 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
CreateElementResult createNewDivElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
*nsGkAtoms::div, atStartOfSelection,
BRElementNextToSplitPoint::Keep, *editingHost);
BRElementNextToSplitPoint::Keep, aEditingHost);
if (createNewDivElementResult.isErr()) {
NS_WARNING(
"HTMLEditor::InsertElementWithSplittingAncestorsWithTransaction("
@ -4418,7 +4409,7 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
CreateElementResult createNewDivElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
*nsGkAtoms::div, atContent, BRElementNextToSplitPoint::Keep,
*editingHost);
aEditingHost);
if (createNewDivElementResult.isErr()) {
NS_WARNING(
"HTMLEditor::InsertElementWithSplittingAncestorsWithTransaction("
@ -4471,7 +4462,7 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
return NS_OK;
}
nsresult HTMLEditor::HandleHTMLIndentAtSelection() {
nsresult HTMLEditor::HandleHTMLIndentAtSelection(const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!IsSelectionRangeContainerNotContent());
@ -4488,7 +4479,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelection() {
// HandleHTMLIndentAtSelectionInternal() creates AutoSelectionRestorer.
// Therefore, even if it returns NS_OK, editor might have been destroyed
// at restoring Selection.
nsresult rv = HandleHTMLIndentAtSelectionInternal();
nsresult rv = HandleHTMLIndentAtSelectionInternal(aEditingHost);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
@ -4498,14 +4489,10 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelection() {
return rv;
}
nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal(
const Element& aEditingHost) {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
RefPtr<Element> editingHost = ComputeEditingHost();
if (MOZ_UNLIKELY(NS_WARN_IF(!editingHost))) {
return NS_ERROR_FAILURE;
}
AutoSelectionRestorer restoreSelectionLater(*this);
// convert the selection ranges into "promoted" selection ranges:
@ -4513,14 +4500,14 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
// block parent, and then further expands to include any ancestors
// whose children are all in the range
AutoTArray<OwningNonNull<nsRange>, 4> arrayOfRanges;
GetSelectionRangesExtendedToHardLineStartAndEnd(arrayOfRanges,
EditSubAction::eIndent);
AutoRangeArray extendedSelectionRanges(SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eIndent, aEditingHost);
// use these ranges to construct a list of nodes to act on.
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
arrayOfRanges, arrayOfContents, EditSubAction::eIndent,
extendedSelectionRanges.Ranges(), arrayOfContents, EditSubAction::eIndent,
CollectNonEditableNodes::Yes);
if (NS_FAILED(rv)) {
NS_WARNING(
@ -4547,7 +4534,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
CreateElementResult createNewBlockQuoteElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
*nsGkAtoms::blockquote, atStartOfSelection,
BRElementNextToSplitPoint::Keep, *editingHost);
BRElementNextToSplitPoint::Keep, aEditingHost);
if (createNewBlockQuoteElementResult.isErr()) {
NS_WARNING(
"HTMLEditor::InsertElementWithSplittingAncestorsWithTransaction("
@ -4622,7 +4609,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
// to act on that is still inside the same li.
if (RefPtr<Element> listItem =
HTMLEditUtils::GetClosestAncestorListItemElement(content,
editingHost)) {
&aEditingHost)) {
if (indentedLI == listItem) {
// already indented this list item
continue;
@ -4645,7 +4632,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
CreateElementResult createNewListElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
MOZ_KnownLive(*containerName), atListItem,
BRElementNextToSplitPoint::Keep, *editingHost);
BRElementNextToSplitPoint::Keep, aEditingHost);
if (createNewListElementResult.isErr()) {
NS_WARNING(nsPrintfCString("HTMLEditor::"
"InsertElementWithSplittingAncestorsWithTr"
@ -4709,7 +4696,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
CreateElementResult createNewBlockQuoteElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
*nsGkAtoms::blockquote, atContent,
BRElementNextToSplitPoint::Keep, *editingHost);
BRElementNextToSplitPoint::Keep, aEditingHost);
if (createNewBlockQuoteElementResult.isErr()) {
NS_WARNING(
"HTMLEditor::InsertElementWithSplittingAncestorsWithTransaction("
@ -4759,7 +4746,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
return NS_OK;
}
EditActionResult HTMLEditor::OutdentAsSubAction() {
EditActionResult HTMLEditor::OutdentAsSubAction(const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
AutoPlaceholderBatch treatAsOneTransaction(
@ -4786,7 +4773,7 @@ EditActionResult HTMLEditor::OutdentAsSubAction() {
return EditActionIgnored();
}
result |= HandleOutdentAtSelection();
result |= HandleOutdentAtSelection(aEditingHost);
if (result.Failed() || result.Canceled()) {
NS_WARNING_ASSERTION(result.Succeeded(),
"HTMLEditor::HandleOutdentAtSelection() failed");
@ -4806,7 +4793,8 @@ EditActionResult HTMLEditor::OutdentAsSubAction() {
return result.SetResult(rv);
}
EditActionResult HTMLEditor::HandleOutdentAtSelection() {
EditActionResult HTMLEditor::HandleOutdentAtSelection(
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!IsSelectionRangeContainerNotContent());
@ -4821,7 +4809,7 @@ EditActionResult HTMLEditor::HandleOutdentAtSelection() {
// Therefore, even if it returns NS_OK, the editor might have been destroyed
// at restoring Selection.
SplitRangeOffFromNodeResult outdentResult =
HandleOutdentAtSelectionInternal();
HandleOutdentAtSelectionInternal(aEditingHost);
if (NS_WARN_IF(Destroyed())) {
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
@ -4900,7 +4888,8 @@ EditActionResult HTMLEditor::HandleOutdentAtSelection() {
return EditActionHandled();
}
SplitRangeOffFromNodeResult HTMLEditor::HandleOutdentAtSelectionInternal() {
SplitRangeOffFromNodeResult HTMLEditor::HandleOutdentAtSelectionInternal(
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
AutoSelectionRestorer restoreSelectionLater(*this);
@ -4913,12 +4902,12 @@ SplitRangeOffFromNodeResult HTMLEditor::HandleOutdentAtSelectionInternal() {
// in the range
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
{
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
GetSelectionRangesExtendedToHardLineStartAndEnd(extendedSelectionRanges,
EditSubAction::eOutdent);
AutoRangeArray extendedSelectionRanges(SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eOutdent, aEditingHost);
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
extendedSelectionRanges, arrayOfContents, EditSubAction::eOutdent,
CollectNonEditableNodes::Yes);
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eOutdent, CollectNonEditableNodes::Yes);
if (NS_FAILED(rv)) {
NS_WARNING(
"HTMLEditor::SplitInlinesAndCollectEditTargetNodes(EditSubAction::"
@ -5622,7 +5611,8 @@ nsresult HTMLEditor::CreateStyleForInsertText(
return rv;
}
EditActionResult HTMLEditor::AlignAsSubAction(const nsAString& aAlignType) {
EditActionResult HTMLEditor::AlignAsSubAction(const nsAString& aAlignType,
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
AutoPlaceholderBatch treatAsOneTransaction(
@ -5695,7 +5685,7 @@ EditActionResult HTMLEditor::AlignAsSubAction(const nsAString& aAlignType) {
// AlignContentsAtSelection() creates AutoSelectionRestorer. Therefore,
// we need to check whether we've been destroyed or not even if it returns
// NS_OK.
rv = AlignContentsAtSelection(aAlignType);
rv = AlignContentsAtSelection(aAlignType, aEditingHost);
if (NS_WARN_IF(Destroyed())) {
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
}
@ -5717,7 +5707,8 @@ EditActionResult HTMLEditor::AlignAsSubAction(const nsAString& aAlignType) {
return EditActionHandled(rv);
}
nsresult HTMLEditor::AlignContentsAtSelection(const nsAString& aAlignType) {
nsresult HTMLEditor::AlignContentsAtSelection(const nsAString& aAlignType,
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!IsSelectionRangeContainerNotContent());
@ -5729,11 +5720,11 @@ nsresult HTMLEditor::AlignContentsAtSelection(const nsAString& aAlignType) {
// in the range
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
{
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
GetSelectionRangesExtendedToHardLineStartAndEnd(
extendedSelectionRanges, EditSubAction::eSetOrClearAlignment);
AutoRangeArray extendedSelectionRanges(SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eSetOrClearAlignment, aEditingHost);
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
extendedSelectionRanges, arrayOfContents,
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eSetOrClearAlignment, CollectNonEditableNodes::Yes);
if (NS_FAILED(rv)) {
NS_WARNING(
@ -6396,42 +6387,6 @@ nsresult HTMLEditor::MaybeExtendSelectionToHardLineEdgesForBlockEditAction() {
return error.StealNSResult();
}
void HTMLEditor::GetSelectionRangesExtendedToHardLineStartAndEnd(
nsTArray<OwningNonNull<nsRange>>& aOutArrayOfRanges,
EditSubAction aEditSubAction) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aOutArrayOfRanges.IsEmpty());
Element* editingHost = ComputeEditingHost();
const uint32_t rangeCount = SelectionRef().RangeCount();
aOutArrayOfRanges.SetCapacity(rangeCount);
for (const uint32_t i : IntegerRange(rangeCount)) {
MOZ_ASSERT(SelectionRef().RangeCount() == rangeCount);
// Make a new adjusted range to represent the appropriate block content.
// The basic idea is to push out the range endpoints to truly enclose the
// blocks that we will affect. This call alters opRange.
nsRange* selectionRange = SelectionRef().GetRangeAt(i);
MOZ_ASSERT(selectionRange);
EditorDOMRange editorRange(*selectionRange);
if (!editorRange.IsPositioned() ||
!editorRange.EnsureNotInNativeAnonymousSubtree()) {
continue; // ignore ranges which are in orphan fragment which were
// disconnected from native anonymous subtrees
}
RefPtr<nsRange> extendedRange;
if (editingHost) {
extendedRange = AutoRangeArray::
CreateRangeWrappingStartAndEndLinesContainingBoundaries(
editorRange, aEditSubAction, *editingHost);
}
if (!extendedRange) {
extendedRange = selectionRange->CloneRange();
}
aOutArrayOfRanges.AppendElement(std::move(extendedRange));
}
}
template <typename EditorDOMRangeType>
already_AddRefed<nsRange> HTMLEditor::CreateRangeIncludingAdjuscentWhiteSpaces(
const EditorDOMRangeType& aRange) {
@ -7573,7 +7528,7 @@ nsresult HTMLEditor::SplitParagraph(Element& aParentDivOrP,
Result<EditorDOMPoint, nsresult>
HTMLEditor::HandleInsertParagraphInListItemElement(
Element& aListItemElement, const EditorDOMPoint& aPointToSplit,
Element& aEditingHost) {
const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(HTMLEditUtils::IsListItem(&aListItemElement));
@ -10044,7 +9999,8 @@ nsresult HTMLEditor::ChangeMarginStart(Element& aElement,
return rv;
}
EditActionResult HTMLEditor::SetSelectionToAbsoluteAsSubAction() {
EditActionResult HTMLEditor::SetSelectionToAbsoluteAsSubAction(
const Element& aEditingHost) {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
AutoPlaceholderBatch treatAsOneTransaction(
@ -10115,7 +10071,7 @@ EditActionResult HTMLEditor::SetSelectionToAbsoluteAsSubAction() {
RefPtr<Element> divElement;
rv = MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
address_of(divElement));
address_of(divElement), aEditingHost);
// MoveSelectedContentsToDivElementToMakeItAbsolutePosition() may restore
// selection with AutoSelectionRestorer. Therefore, the editor might have
// already been destroyed now.
@ -10155,26 +10111,21 @@ EditActionResult HTMLEditor::SetSelectionToAbsoluteAsSubAction() {
}
nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
RefPtr<Element>* aTargetElement) {
RefPtr<Element>* aTargetElement, const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aTargetElement);
RefPtr<Element> editingHost = ComputeEditingHost();
if (MOZ_UNLIKELY(NS_WARN_IF(!editingHost))) {
return NS_ERROR_FAILURE;
}
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTArray<OwningNonNull<nsRange>, 4> arrayOfRanges;
GetSelectionRangesExtendedToHardLineStartAndEnd(
arrayOfRanges, EditSubAction::eSetPositionToAbsolute);
AutoRangeArray extendedSelectionRanges(SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eSetPositionToAbsolute, aEditingHost);
// Use these ranges to construct a list of nodes to act on.
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
arrayOfRanges, arrayOfContents, EditSubAction::eSetPositionToAbsolute,
CollectNonEditableNodes::Yes);
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eSetPositionToAbsolute, CollectNonEditableNodes::Yes);
if (NS_FAILED(rv)) {
NS_WARNING(
"HTMLEditor::SplitInlinesAndCollectEditTargetNodes("
@ -10200,7 +10151,7 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
CreateElementResult createNewDivElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
*nsGkAtoms::div, atCaret, BRElementNextToSplitPoint::Keep,
*editingHost);
aEditingHost);
if (createNewDivElementResult.isErr()) {
NS_WARNING(
"HTMLEditor::InsertElementWithSplittingAncestorsWithTransaction("
@ -10292,7 +10243,7 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
CreateElementResult createNewDivElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
*nsGkAtoms::div, atContent, BRElementNextToSplitPoint::Keep,
*editingHost);
aEditingHost);
if (createNewDivElementResult.isErr()) {
NS_WARNING(
"HTMLEditor::"
@ -10358,7 +10309,7 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
// because we want to keep indent level of the contents.
if (RefPtr<Element> listItemElement =
HTMLEditUtils::GetClosestAncestorListItemElement(content,
editingHost)) {
&aEditingHost)) {
if (handledListItemElement == listItemElement) {
// Current node has already been moved into the `<div>` element.
continue;
@ -10402,7 +10353,7 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
CreateElementResult createNewDivElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
*nsGkAtoms::div, atContent, BRElementNextToSplitPoint::Keep,
*editingHost);
aEditingHost);
if (createNewDivElementResult.isErr()) {
NS_WARNING(
"HTMLEditor::"
@ -10481,7 +10432,7 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
CreateElementResult createNewDivElementResult =
InsertElementWithSplittingAncestorsWithTransaction(
*nsGkAtoms::div, atContent, BRElementNextToSplitPoint::Keep,
*editingHost);
aEditingHost);
if (createNewDivElementResult.isErr()) {
NS_WARNING(
"HTMLEditor::InsertElementWithSplittingAncestorsWithTransaction("

View File

@ -1257,7 +1257,12 @@ NS_IMETHODIMP HTMLEditor::InsertLineBreak() {
return EditorBase::ToGenericNSResult(rv);
}
EditActionResult result = InsertParagraphSeparatorAsSubAction();
const RefPtr<Element> editingHost = ComputeEditingHost();
if (!editingHost) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
EditActionResult result = InsertParagraphSeparatorAsSubAction(*editingHost);
NS_WARNING_ASSERTION(
result.Succeeded(),
"HTMLEditor::InsertParagraphSeparatorAsSubAction() failed");
@ -1297,7 +1302,12 @@ nsresult HTMLEditor::InsertParagraphSeparatorAsAction(
return EditorBase::ToGenericNSResult(rv);
}
EditActionResult result = InsertParagraphSeparatorAsSubAction();
const RefPtr<Element> editingHost = ComputeEditingHost();
if (!editingHost) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
EditActionResult result = InsertParagraphSeparatorAsSubAction(*editingHost);
NS_WARNING_ASSERTION(
result.Succeeded(),
"HTMLEditor::InsertParagraphSeparatorAsSubAction() failed");
@ -2153,6 +2163,12 @@ nsresult HTMLEditor::SetParagraphFormatAsAction(
return EditorBase::ToGenericNSResult(rv);
}
// TODO: Computing the editing host here makes the `execCommand` in
// docshell/base/crashtests/file_432114-2.xhtml cannot run
// `DOMNodeRemoved` event listener with deleting the bogus <br> element.
// So that it should be rewritten with different mutation event listener
// since we'd like to stop using it.
nsAutoString lowerCaseTagName(aParagraphFormat);
ToLowerCase(lowerCaseTagName);
RefPtr<nsAtom> tagName = NS_Atomize(lowerCaseTagName);
@ -2165,6 +2181,7 @@ nsresult HTMLEditor::SetParagraphFormatAsAction(
"SelectAllOfCurrentList::No) failed");
return EditorBase::ToGenericNSResult(result.Rv());
}
rv = FormatBlockContainerAsSubAction(*tagName);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::FormatBlockContainerAsSubAction() failed");
@ -2573,7 +2590,12 @@ nsresult HTMLEditor::RemoveListAsAction(const nsAString& aListType,
return EditorBase::ToGenericNSResult(rv);
}
rv = RemoveListAtSelectionAsSubAction();
const RefPtr<Element> editingHost = ComputeEditingHost();
if (!editingHost) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
rv = RemoveListAtSelectionAsSubAction(*editingHost);
NS_WARNING_ASSERTION(NS_FAILED(rv),
"HTMLEditor::RemoveListAtSelectionAsSubAction() failed");
return rv;
@ -2642,7 +2664,11 @@ nsresult HTMLEditor::FormatBlockContainerAsSubAction(nsAtom& aTagName) {
// FormatBlockContainerWithTransaction() creates AutoSelectionRestorer.
// Therefore, even if it returns NS_OK, editor might have been destroyed
// at restoring Selection.
rv = FormatBlockContainerWithTransaction(aTagName);
const RefPtr<Element> editingHost = ComputeEditingHost();
if (MOZ_UNLIKELY(!editingHost)) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
rv = FormatBlockContainerWithTransaction(aTagName, *editingHost);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
@ -2673,7 +2699,12 @@ nsresult HTMLEditor::IndentAsAction(nsIPrincipal* aPrincipal) {
return EditorBase::ToGenericNSResult(rv);
}
EditActionResult result = IndentAsSubAction();
const RefPtr<Element> editingHost = ComputeEditingHost();
if (!editingHost) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
EditActionResult result = IndentAsSubAction(*editingHost);
NS_WARNING_ASSERTION(result.Succeeded(),
"HTMLEditor::IndentAsSubAction() failed");
return EditorBase::ToGenericNSResult(result.Rv());
@ -2693,7 +2724,12 @@ nsresult HTMLEditor::OutdentAsAction(nsIPrincipal* aPrincipal) {
return EditorBase::ToGenericNSResult(rv);
}
EditActionResult result = OutdentAsSubAction();
const RefPtr<Element> editingHost = ComputeEditingHost();
if (!editingHost) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
EditActionResult result = OutdentAsSubAction(*editingHost);
NS_WARNING_ASSERTION(result.Succeeded(),
"HTMLEditor::OutdentAsSubAction() failed");
return EditorBase::ToGenericNSResult(result.Rv());
@ -2712,7 +2748,12 @@ nsresult HTMLEditor::AlignAsAction(const nsAString& aAlignType,
return EditorBase::ToGenericNSResult(rv);
}
EditActionResult result = AlignAsSubAction(aAlignType);
const RefPtr<Element> editingHost = ComputeEditingHost();
if (!editingHost) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
EditActionResult result = AlignAsSubAction(aAlignType, *editingHost);
NS_WARNING_ASSERTION(result.Succeeded(),
"HTMLEditor::AlignAsSubAction() failed");
return EditorBase::ToGenericNSResult(result.Rv());
@ -5860,9 +5901,8 @@ nsresult HTMLEditor::SetBackgroundColorAsAction(const nsAString& aColor,
}
Result<EditorDOMPoint, nsresult>
HTMLEditor::CopyLastEditableChildStylesWithTransaction(Element& aPreviousBlock,
Element& aNewBlock,
Element& aEditingHost) {
HTMLEditor::CopyLastEditableChildStylesWithTransaction(
Element& aPreviousBlock, Element& aNewBlock, const Element& aEditingHost) {
MOZ_ASSERT(IsEditActionDataAvailable());
// First, clear out aNewBlock. Contract is that we want only the styles

View File

@ -758,14 +758,14 @@ class HTMLEditor final : public EditorBase,
* are cloned to aNewBlock.
* @param aNewBlock New block container element. All children of
* this is deleted first.
* @param aEditingHost Current editing host.
* @param aEditingHost The editing host.
* @return If succeeded, returns a suggesting point to put
* caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
CopyLastEditableChildStylesWithTransaction(Element& aPreviousBlock,
Element& aNewBlock,
Element& aEditingHost);
const Element& aEditingHost);
/**
* RemoveBlockContainerWithTransaction() removes aElement from the DOM tree
@ -1087,14 +1087,14 @@ class HTMLEditor final : public EditorBase,
*
* @param aMailCiteElement The mail-cite element which should be split.
* @param aPointToSplit The point to split.
* @param aEditingHost Current editing host element.
* @param aEditingHost The editing host.
* @return Candidate caret position where is at inserted
* <br> element into the split point.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
HandleInsertParagraphInMailCiteElement(Element& aMailCiteElement,
const EditorDOMPoint& aPointToSplit,
Element& aEditingHost);
const Element& aEditingHost);
/**
* HandleInsertBRElement() inserts a <br> element into aInsertToBreak.
@ -1108,7 +1108,7 @@ class HTMLEditor final : public EditorBase,
* candidate caret point.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateElementResult HandleInsertBRElement(
const EditorDOMPoint& aInsertToBreak, Element& aEditingHost);
const EditorDOMPoint& aInsertToBreak, const Element& aEditingHost);
/**
* HandleInsertLinefeed() inserts a linefeed character into aInsertToBreak.
@ -1118,7 +1118,7 @@ class HTMLEditor final : public EditorBase,
* @param aEditingHost Current active editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleInsertLinefeed(
const EditorDOMPoint& aInsertToBreak, Element& aEditingHost);
const EditorDOMPoint& aInsertToBreak, const Element& aEditingHost);
/**
* SplitParentInlineElementsAtRangeEdges() splits parent inline nodes at both
@ -1243,19 +1243,6 @@ class HTMLEditor final : public EditorBase,
const EditorDOMPointType1& aStartPoint,
const EditorDOMPointType2& aEndPoint);
/**
* GetSelectionRangesExtendedToHardLineStartAndEnd() collects selection ranges
* with extending to start/end of hard line from each range start and end.
* XXX This means that same range may be included in the result.
*
* @param aOutArrayOfRanges [out] Always appended same number of ranges
* as Selection::RangeCount(). Must be empty
* when you call this.
*/
void GetSelectionRangesExtendedToHardLineStartAndEnd(
nsTArray<OwningNonNull<nsRange>>& aOutArrayOfRanges,
EditSubAction aEditSubAction);
/**
* GetChildNodesOf() returns all child nodes of aParent with an array.
*/
@ -1497,9 +1484,10 @@ class HTMLEditor final : public EditorBase,
* be called.
* Otherwise, CreateOrChangeBlockContainerElement() will be
* called.
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
FormatBlockContainerWithTransaction(nsAtom& aBlockType);
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult FormatBlockContainerWithTransaction(
nsAtom& aBlockType, const Element& aEditingHost);
/**
* InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() determines if
@ -1583,20 +1571,22 @@ class HTMLEditor final : public EditorBase,
*
* @param aListItemElement The list item which has the following point.
* @param aPointToSplit The point to split aListItemElement.
* @param aEditingHost The editing host for aListItemElement
* @param aEditingHost The editing host.
* @return A candidate position to put caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
HandleInsertParagraphInListItemElement(Element& aListItemElement,
const EditorDOMPoint& aPointToSplit,
Element& aEditingHost);
const Element& aEditingHost);
/**
* InsertParagraphSeparatorAsSubAction() handles insertPargraph commad
* (i.e., handling Enter key press) with the above helper methods.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
InsertParagraphSeparatorAsSubAction();
InsertParagraphSeparatorAsSubAction(const Element& aEditingHost);
/**
* InsertLineBreakAsSubAction() inserts a new <br> element or a linefeed
@ -1641,12 +1631,14 @@ class HTMLEditor final : public EditorBase,
* attributes will be removed.
* @param aSelectAllOfCurrentList Yes if this should treat all of
* ancestor list element at selection.
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
ChangeSelectedHardLinesToList(nsAtom& aListElementTagName,
nsAtom& aListItemElementTagName,
const nsAString& aBulletType,
SelectAllOfCurrentList aSelectAllOfCurrentList);
SelectAllOfCurrentList aSelectAllOfCurrentList,
const Element& aEditingHost);
/**
* MakeOrChangeListAndListItemAsSubAction() handles create list commands with
@ -2318,8 +2310,11 @@ class HTMLEditor final : public EditorBase,
* RemoveListAtSelectionAsSubAction() removes list elements and list item
* elements at Selection. And move contents in them where the removed list
* was.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveListAtSelectionAsSubAction();
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
RemoveListAtSelectionAsSubAction(const Element& aEditingHost);
/**
* ChangeMarginStart() changes margin of aElement to indent or outdent.
@ -2336,9 +2331,11 @@ class HTMLEditor final : public EditorBase,
* need to check if the editor is still available even if this returns
* NS_OK.
* NOTE: Use HandleCSSIndentAtSelection() instead.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandleCSSIndentAtSelectionInternal();
HandleCSSIndentAtSelectionInternal(const Element& aEditingHost);
/**
* HandleHTMLIndentAtSelectionInternal() indents around Selection with HTML.
@ -2346,30 +2343,41 @@ class HTMLEditor final : public EditorBase,
* need to check if the editor is still available even if this returns
* NS_OK.
* NOTE: Use HandleHTMLIndentAtSelection() instead.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandleHTMLIndentAtSelectionInternal();
HandleHTMLIndentAtSelectionInternal(const Element& aEditingHost);
/**
* HandleCSSIndentAtSelection() indents around Selection with CSS.
* NOTE: This is a helper method of `HandleIndentAtSelection()`. If you
* want to call this directly, you should check whether you need
* do do something which `HandleIndentAtSelection()` does.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleCSSIndentAtSelection();
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandleCSSIndentAtSelection(const Element& aEditingHost);
/**
* HandleHTMLIndentAtSelection() indents around Selection with HTML.
* NOTE: This is a helper method of `HandleIndentAtSelection()`. If you
* want to call this directly, you should check whether you need
* do do something which `HandleIndentAtSelection()` does.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleHTMLIndentAtSelection();
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
HandleHTMLIndentAtSelection(const Element& aEditingHost);
/**
* HandleIndentAtSelection() indents around Selection with HTML or CSS.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult HandleIndentAtSelection();
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
HandleIndentAtSelection(const Element& aEditingHost);
/**
* OutdentPartOfBlock() outdents the nodes between aStartOfOutdent and
@ -2407,6 +2415,7 @@ class HTMLEditor final : public EditorBase,
* NS_OK.
* NOTE: Call `HandleOutdentAtSelection()` instead.
*
* @param aEditingHost The editing host.
* @return The left content is left content of last
* outdented element.
* The right content is right content of last
@ -2415,12 +2424,15 @@ class HTMLEditor final : public EditorBase,
* outdented element.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffFromNodeResult
HandleOutdentAtSelectionInternal();
HandleOutdentAtSelectionInternal(const Element& aEditingHost);
/**
* HandleOutdentAtSelection() outdents contents around Selection.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult HandleOutdentAtSelection();
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
HandleOutdentAtSelection(const Element& aEditingHost);
/**
* AlignBlockContentsWithDivElement() sets align attribute of <div> element
@ -2555,18 +2567,20 @@ class HTMLEditor final : public EditorBase,
*
* @param aAlignType New align attribute value where the contents
* should be aligned to.
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
AlignContentsAtSelection(const nsAString& aAlignType);
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AlignContentsAtSelection(
const nsAString& aAlignType, const Element& aEditingHost);
/**
* AlignAsSubAction() handles "align" command with `Selection`.
*
* @param aAlignType New align attribute value where the contents
* should be aligned to.
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
AlignAsSubAction(const nsAString& aAlignType);
AlignAsSubAction(const nsAString& aAlignType, const Element& aEditingHost);
/**
* AdjustCaretPositionAndEnsurePaddingBRElement() may adjust caret
@ -2642,10 +2656,12 @@ class HTMLEditor final : public EditorBase,
*
* @param aTargetElement Returns target `<div>` element which should be
* changed to absolute positioned.
* @param aEditingHost The editing host.
*/
// TODO: Rewrite this with `Result<RefPtr<Element>, nsresult>`.
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
RefPtr<Element>* aTargetElement);
RefPtr<Element>* aTargetElement, const Element& aEditingHost);
/**
* SetSelectionToAbsoluteAsSubAction() move selected contents to first
@ -2653,9 +2669,11 @@ class HTMLEditor final : public EditorBase,
* the `<div>` element positioned absolutely.
* mNewBlockElement of TopLevelEditSubActionData will be set to the `<div>`
* element.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
SetSelectionToAbsoluteAsSubAction();
SetSelectionToAbsoluteAsSubAction(const Element& aEditingHost);
/**
* SetSelectionToStaticAsSubAction() sets the `position` property of a
@ -3275,13 +3293,19 @@ class HTMLEditor final : public EditorBase,
/**
* IndentAsSubAction() indents the content around Selection.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult IndentAsSubAction();
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
IndentAsSubAction(const Element& aEditingHost);
/**
* OutdentAsSubAction() outdents the content around Selection.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult OutdentAsSubAction();
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
OutdentAsSubAction(const Element& aEditingHost);
MOZ_CAN_RUN_SCRIPT nsresult LoadHTML(const nsAString& aInputString);
@ -4486,10 +4510,8 @@ class HTMLEditor final : public EditorBase,
ParagraphSeparator mDefaultParagraphSeparator;
friend class
AlignStateAtSelection; // CollectEditableTargetNodes,
// CollectNonEditableNodes,
// GetSelectionRangesExtendedToHardLineStartAndEnd
friend class AlignStateAtSelection; // CollectEditableTargetNodes,
// CollectNonEditableNodes
friend class AutoSelectionSetterAfterTableEdit; // SetSelectionAfterEdit
friend class
AutoSetTemporaryAncestorLimiter; // InitializeSelectionAncestorLimit
@ -4503,29 +4525,23 @@ class HTMLEditor final : public EditorBase,
// ReflectPaddingBRElementForEmptyEditor,
// RefreshEditingUI,
// RemoveEmptyInclusiveAncestorInlineElements,
// mComposerUpdater, mHasBeforeINputBeenCancelded
// mComposerUpdater, mHasBeforeInputBeenCanceled
friend class JoinNodesTransaction; // DidJoinNodesTransaction, DoJoinNodes,
// DoSplitNode, RangeUpdaterRef
friend class
ListElementSelectionState; // CollectEditTargetNodes,
// CollectNonEditableNodes,
// GetSelectionRangesExtendedToHardLineStartAndEnd
friend class
ListItemElementSelectionState; // CollectEditTargetNodes,
// CollectNonEditableNodes,
// GetSelectionRangesExtendedToHardLineStartAndEnd
friend class ListElementSelectionState; // CollectEditTargetNodes,
// CollectNonEditableNodes
friend class ListItemElementSelectionState; // CollectEditTargetNodes,
// CollectNonEditableNodes
friend class MoveNodeResult; // AllowsTransactionsToChangeSelection,
// CollapseSelectionTo
friend class MoveNodeTransaction; // AllowsTransactionsToChangeSelection,
// CollapseSelectionTo, MarkElementDirty,
// RangeUpdaterRef
friend class
ParagraphStateAtSelection; // CollectChildren,
// CollectEditTargetNodes,
// CollectListChildren,
// CollectNonEditableNodes,
// CollectTableChildren,
// GetSelectionRangesExtendedToHardLineStartAndEnd
friend class ParagraphStateAtSelection; // CollectChildren,
// CollectEditTargetNodes,
// CollectListChildren,
// CollectNonEditableNodes,
// CollectTableChildren
friend class SlurpBlobEventListener; // BlobReader
friend class SplitNodeResult; // CollapseSelectionTo
friend class SplitNodeTransaction; // DoJoinNodes, DoSplitNode
@ -4662,13 +4678,13 @@ class MOZ_STACK_CLASS ParagraphStateAtSelection final {
/**
* CollectEditableFormatNodesInSelection() collects only editable nodes
* around selection ranges (with
* `HTMLEditor::GetSelectionRangesExtendedToHardLineStartAndEnd()` and
* `AutoRangeArray::ExtendRangesToWrapLinesToHandleBlockLevelEditAction()` and
* `HTMLEditor::CollectEditTargetNodes()`, see its document for the detail).
* If it includes list, list item or table related elements, they will be
* replaced their children.
*/
static nsresult CollectEditableFormatNodesInSelection(
HTMLEditor& aHTMLEditor,
HTMLEditor& aHTMLEditor, const dom::Element& aEditingHost,
nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents);
RefPtr<nsAtom> mFirstParagraphState;

View File

@ -64,13 +64,23 @@ ListElementSelectionState::ListElementSelectionState(HTMLEditor& aHTMLEditor,
return;
}
Element* editingHostOrRoot = aHTMLEditor.ComputeEditingHost();
if (!editingHostOrRoot) {
// This is not a handler of editing command so that if there is no active
// editing host, let's use the <body> or document element instead.
editingHostOrRoot = aHTMLEditor.GetRoot();
if (!editingHostOrRoot) {
return;
}
}
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
{
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
aHTMLEditor.GetSelectionRangesExtendedToHardLineStartAndEnd(
extendedSelectionRanges, EditSubAction::eCreateOrChangeList);
AutoRangeArray extendedSelectionRanges(aHTMLEditor.SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eCreateOrChangeList, *editingHostOrRoot);
nsresult rv = aHTMLEditor.CollectEditTargetNodes(
extendedSelectionRanges, arrayOfContents,
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eCreateOrChangeList,
HTMLEditor::CollectNonEditableNodes::No);
if (NS_FAILED(rv)) {
@ -135,13 +145,23 @@ ListItemElementSelectionState::ListItemElementSelectionState(
return;
}
Element* editingHostOrRoot = aHTMLEditor.ComputeEditingHost();
if (!editingHostOrRoot) {
// This is not a handler of editing command so that if there is no active
// editing host, let's use the <body> or document element instead.
editingHostOrRoot = aHTMLEditor.GetRoot();
if (!editingHostOrRoot) {
return;
}
}
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
{
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
aHTMLEditor.GetSelectionRangesExtendedToHardLineStartAndEnd(
extendedSelectionRanges, EditSubAction::eCreateOrChangeList);
AutoRangeArray extendedSelectionRanges(aHTMLEditor.SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eCreateOrChangeList, *editingHostOrRoot);
nsresult rv = aHTMLEditor.CollectEditTargetNodes(
extendedSelectionRanges, arrayOfContents,
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eCreateOrChangeList,
HTMLEditor::CollectNonEditableNodes::No);
if (NS_FAILED(rv)) {
@ -274,13 +294,23 @@ AlignStateAtSelection::AlignStateAtSelection(HTMLEditor& aHTMLEditor,
// ranges. `HTMLEditor` should have
// `GetFirstSelectionRangeExtendedToHardLineStartAndEnd()`.
else {
AutoTArray<OwningNonNull<nsRange>, 4> arrayOfRanges;
aHTMLEditor.GetSelectionRangesExtendedToHardLineStartAndEnd(
arrayOfRanges, EditSubAction::eSetOrClearAlignment);
Element* editingHostOrRoot = aHTMLEditor.ComputeEditingHost();
if (!editingHostOrRoot) {
// This is not a handler of editing command so that if there is no active
// editing host, let's use the <body> or document element instead.
editingHostOrRoot = aHTMLEditor.GetRoot();
if (!editingHostOrRoot) {
return;
}
}
AutoRangeArray extendedSelectionRanges(aHTMLEditor.SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eSetOrClearAlignment, *editingHostOrRoot);
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
nsresult rv = aHTMLEditor.CollectEditTargetNodes(
arrayOfRanges, arrayOfContents, EditSubAction::eSetOrClearAlignment,
extendedSelectionRanges.Ranges(), arrayOfContents,
EditSubAction::eSetOrClearAlignment,
HTMLEditor::CollectNonEditableNodes::Yes);
if (NS_FAILED(rv)) {
NS_WARNING(
@ -440,9 +470,19 @@ ParagraphStateAtSelection::ParagraphStateAtSelection(HTMLEditor& aHTMLEditor,
return;
}
Element* editingHostOrRoot = aHTMLEditor.ComputeEditingHost();
if (!editingHostOrRoot) {
// This is not a handler of editing command so that if there is no active
// editing host, let's use the <body> or document element instead.
editingHostOrRoot = aHTMLEditor.GetRoot();
if (!editingHostOrRoot) {
return;
}
}
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
nsresult rv =
CollectEditableFormatNodesInSelection(aHTMLEditor, arrayOfContents);
nsresult rv = CollectEditableFormatNodesInSelection(
aHTMLEditor, *editingHostOrRoot, arrayOfContents);
if (NS_FAILED(rv)) {
NS_WARNING(
"ParagraphStateAtSelection::CollectEditableFormatNodesInSelection() "
@ -581,14 +621,14 @@ void ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
// static
nsresult ParagraphStateAtSelection::CollectEditableFormatNodesInSelection(
HTMLEditor& aHTMLEditor,
HTMLEditor& aHTMLEditor, const Element& aEditingHost,
nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents) {
{
AutoTArray<OwningNonNull<nsRange>, 4> extendedSelectionRanges;
aHTMLEditor.GetSelectionRangesExtendedToHardLineStartAndEnd(
extendedSelectionRanges, EditSubAction::eCreateOrRemoveBlock);
AutoRangeArray extendedSelectionRanges(aHTMLEditor.SelectionRef());
extendedSelectionRanges.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
EditSubAction::eCreateOrRemoveBlock, aEditingHost);
nsresult rv = aHTMLEditor.CollectEditTargetNodes(
extendedSelectionRanges, aArrayOfContents,
extendedSelectionRanges.Ranges(), aArrayOfContents,
EditSubAction::eCreateOrRemoveBlock,
HTMLEditor::CollectNonEditableNodes::Yes);
if (NS_FAILED(rv)) {

View File

@ -717,7 +717,7 @@ EditActionResult WhiteSpaceVisibilityKeeper::
// static
CreateElementResult WhiteSpaceVisibilityKeeper::InsertBRElement(
HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToInsert,
Element& aEditingHost) {
const Element& aEditingHost) {
if (MOZ_UNLIKELY(NS_WARN_IF(!aPointToInsert.IsSet()))) {
return CreateElementResult(NS_ERROR_INVALID_ARG);
}

View File

@ -1471,7 +1471,7 @@ class WhiteSpaceVisibilityKeeper final {
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT static CreateElementResult InsertBRElement(
HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToInsert,
Element& aEditingHost);
const Element& aEditingHost);
/**
* InsertText() inserts aStringToInsert to aPointToInsert and makes any needed

View File

@ -1510,6 +1510,18 @@ class Matrix4x4Typed {
_44 = UnspecifiedNaN<T>();
}
// Verifies that the matrix contains no Infs or NaNs
bool IsFinite() const {
return mozilla::IsFinite(_11) && mozilla::IsFinite(_12) &&
mozilla::IsFinite(_13) && mozilla::IsFinite(_14) &&
mozilla::IsFinite(_21) && mozilla::IsFinite(_22) &&
mozilla::IsFinite(_23) && mozilla::IsFinite(_24) &&
mozilla::IsFinite(_31) && mozilla::IsFinite(_32) &&
mozilla::IsFinite(_33) && mozilla::IsFinite(_34) &&
mozilla::IsFinite(_41) && mozilla::IsFinite(_42) &&
mozilla::IsFinite(_43) && mozilla::IsFinite(_44);
}
void SkewXY(double aXSkew, double aYSkew) {
// XXX Is double precision really necessary here
T tanX = SafeTangent(aXSkew);

View File

@ -1322,8 +1322,10 @@ HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer(
// In such cases, go with the one that does not include the perspective
// component; the perspective transform is remembered and applied to the
// children instead.
if (!aAncestorTransform.CombinedTransform().FuzzyEqualsMultiplicative(
apzc->GetAncestorTransform())) {
auto ancestorTransform = aAncestorTransform.CombinedTransform();
auto existingAncestorTransform = apzc->GetAncestorTransform();
if (!ancestorTransform.FuzzyEqualsMultiplicative(
existingAncestorTransform)) {
typedef TreeBuildingState::DeferredTransformMap::value_type PairType;
if (!aAncestorTransform.ContainsPerspectiveTransform() &&
!apzc->AncestorTransformContainsPerspective()) {
@ -1333,9 +1335,16 @@ HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer(
// cases, it's expected that different instances can have different
// transforms, since each page renders a different part of the item.
if (!aLayer.Metadata().IsPaginatedPresentation()) {
MOZ_ASSERT(false,
"Two layers that scroll together have different ancestor "
"transforms");
if (ancestorTransform.IsFinite() &&
existingAncestorTransform.IsFinite()) {
MOZ_ASSERT(
false,
"Two layers that scroll together have different ancestor "
"transforms");
} else {
MOZ_ASSERT(ancestorTransform.IsFinite() ==
existingAncestorTransform.IsFinite());
}
}
} else if (!aAncestorTransform.ContainsPerspectiveTransform()) {
aState.mPerspectiveTransformsDeferredToChildren.insert(

View File

@ -95,8 +95,9 @@ class DownscalingFilter final : public SurfaceFilter {
mInputSize = aConfig.mInputSize;
gfx::IntSize outputSize = mNext.InputSize();
mScale = gfxSize(double(mInputSize.width) / outputSize.width,
double(mInputSize.height) / outputSize.height);
mScale =
gfx::MatrixScalesDouble(double(mInputSize.width) / outputSize.width,
double(mInputSize.height) / outputSize.height);
mHasAlpha = aConfig.mFormat == gfx::SurfaceFormat::OS_RGBA;
ReleaseWindow();
@ -154,7 +155,7 @@ class DownscalingFilter final : public SurfaceFilter {
if (invalidRect) {
// Compute the input space invalid rect by scaling.
invalidRect->mInputSpaceRect.ScaleRoundOut(mScale.width, mScale.height);
invalidRect->mInputSpaceRect.ScaleRoundOut(mScale.xScale, mScale.yScale);
}
return invalidRect;
@ -285,10 +286,10 @@ class DownscalingFilter final : public SurfaceFilter {
Next mNext; /// The next SurfaceFilter in the chain.
gfx::IntSize mInputSize; /// The size of the input image.
gfxSize mScale; /// The scale factors in each dimension.
/// Computed from @mInputSize and
/// the next filter's input size.
gfx::IntSize mInputSize; /// The size of the input image.
gfx::MatrixScalesDouble mScale; /// The scale factors in each dimension.
/// Computed from @mInputSize and
/// the next filter's input size.
UniquePtr<uint8_t[]> mRowBuffer; /// The buffer into which input is written.
UniquePtr<uint8_t*[]> mWindow; /// The last few rows which were written.

View File

@ -1210,39 +1210,6 @@ set_define(
depends_if("--enable-wasm-type-reflections")(lambda x: True),
)
# Support for WebAssembly exceptions
# ==================================
@depends("--enable-cranelift")
def default_wasm_exceptions(cranelift_enabled):
if cranelift_enabled:
return
return True
option(
"--enable-wasm-exceptions",
default=default_wasm_exceptions,
help="{Enable|Disable} WebAssembly exceptions",
)
@depends("--enable-wasm-exceptions", "--enable-cranelift")
def wasm_exceptions(value, cranelift_enabled):
if not value:
return
if cranelift_enabled:
die("--enable-wasm-exceptions is not supported for --enable-cranelift")
return True
set_config("ENABLE_WASM_EXCEPTIONS", wasm_exceptions)
set_define("ENABLE_WASM_EXCEPTIONS", wasm_exceptions)
# Wasi configuration
# ===================================================

View File

@ -265,24 +265,6 @@ enum JSWhyMagic {
/** for local use */
JS_GENERIC_MAGIC,
/**
* Write records queued up in WritableStreamDefaultController.[[queue]] in the
* spec are either "close" (a String) or Record { [[chunk]]: chunk }, where
* chunk is an arbitrary user-provided (and therefore non-magic) value.
* Represent "close" the String as this magic value; represent Record records
* as the |chunk| value within each of them.
*/
JS_WRITABLESTREAM_CLOSE_RECORD,
/**
* The ReadableStream pipe-to operation concludes with a "finalize" operation
* that accepts an optional |error| argument. In certain cases that optional
* |error| must be stored in a handler function, for use after a promise has
* settled. We represent the argument not being provided, in those cases,
* using this magic value.
*/
JS_READABLESTREAM_PIPETO_FINALIZE_WITHOUT_ERROR,
/**
* When an error object is created without the error cause argument, we set
* the error's cause slot to this magic value.

View File

@ -70,11 +70,6 @@
#else
# define WASM_EXTENDED_CONST_ENABLED 0
#endif
#ifdef ENABLE_WASM_EXCEPTIONS
# define WASM_EXCEPTIONS_ENABLED 1
#else
# define WASM_EXCEPTIONS_ENABLED 0
#endif
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
# define WASM_FUNCTION_REFERENCES_ENABLED 1
#else
@ -116,7 +111,7 @@
TENTATIVE( \
/* capitalized name */ Exceptions, \
/* lower case name */ exceptions, \
/* compile predicate */ WASM_EXCEPTIONS_ENABLED, \
/* compile predicate */ true, \
/* compiler predicate */ BaselineAvailable(cx) || IonAvailable(cx), \
/* flag predicate */ !IsFuzzingCranelift(cx), \
/* shell flag */ "exceptions", \

View File

@ -169,10 +169,7 @@ MSG_DEF(JSMSG_PRECISION_RANGE, 1, JSEXN_RANGEERR, "precision {0} out of
// Function
MSG_DEF(JSMSG_BAD_APPLY_ARGS, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array")
MSG_DEF(JSMSG_BAD_FORMAL, 0, JSEXN_SYNTAXERR, "malformed formal parameter")
MSG_DEF(JSMSG_CALLER_IS_STRICT, 0, JSEXN_TYPEERR, "access to strict mode caller function is censored")
MSG_DEF(JSMSG_DEPRECATED_USAGE, 1, JSEXN_REFERENCEERR, "deprecated {0} usage")
MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 1, JSEXN_TYPEERR, "{0} is not a scripted function")
MSG_DEF(JSMSG_NO_REST_NAME, 0, JSEXN_SYNTAXERR, "no parameter name after ...")
MSG_DEF(JSMSG_PARAMETER_AFTER_REST, 0, JSEXN_SYNTAXERR, "parameter after rest parameter")
MSG_DEF(JSMSG_TOO_MANY_ARGUMENTS, 0, JSEXN_RANGEERR, "too many arguments provided for a function call")
@ -198,12 +195,10 @@ MSG_DEF(JSMSG_USER_DEFINED_ERROR, 0, JSEXN_ERR, "JS_ReportError was called"
MSG_DEF(JSMSG_ALLOC_OVERFLOW, 0, JSEXN_INTERNALERR, "allocation size overflow")
MSG_DEF(JSMSG_BAD_BYTECODE, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}")
MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 0, JSEXN_INTERNALERR, "buffer too small")
MSG_DEF(JSMSG_BUILD_ID_NOT_AVAILABLE, 0, JSEXN_INTERNALERR, "build ID is not available")
MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})")
MSG_DEF(JSMSG_NEED_DIET, 1, JSEXN_INTERNALERR, "{0} too large")
MSG_DEF(JSMSG_OUT_OF_MEMORY, 0, JSEXN_INTERNALERR, "out of memory")
MSG_DEF(JSMSG_OVER_RECURSED, 0, JSEXN_INTERNALERR, "too much recursion")
MSG_DEF(JSMSG_TOO_BIG_TO_ENCODE, 0, JSEXN_INTERNALERR, "data are to big to encode")
MSG_DEF(JSMSG_TOO_DEEP, 1, JSEXN_INTERNALERR, "{0} nested too deeply")
MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 1, JSEXN_INTERNALERR, "uncaught exception: {0}")
MSG_DEF(JSMSG_UNKNOWN_FORMAT, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}")
@ -220,7 +215,6 @@ MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid
MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC_OR_MODULE, 0, JSEXN_SYNTAXERR, "await is only valid in async functions, async generators and modules")
MSG_DEF(JSMSG_TOP_LEVEL_AWAIT_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "top level await is not supported in this context")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
MSG_DEF(JSMSG_BAD_COALESCE_MIXING, 0, JSEXN_SYNTAXERR, "cannot use `??` unparenthesized within `||` and `&&` expressions")
MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration")
MSG_DEF(JSMSG_BAD_CONTINUE, 0, JSEXN_SYNTAXERR, "continue must be inside loop")
@ -229,7 +223,6 @@ MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET, 0, JSEXN_SYNTAXERR, "invalid destructurin
MSG_DEF(JSMSG_BAD_DESTRUCT_PARENS, 0, JSEXN_SYNTAXERR, "destructuring patterns in assignments can't be parenthesized")
MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration")
MSG_DEF(JSMSG_BAD_DUP_ARGS, 0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context")
MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loop")
MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side")
MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding")
MSG_DEF(JSMSG_BAD_STARTING_FOROF_LHS, 1, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with '{0}'")
@ -252,7 +245,6 @@ MSG_DEF(JSMSG_BAD_ARGUMENTS, 0, JSEXN_SYNTAXERR, "arguments is not val
MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list")
MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
MSG_DEF(JSMSG_BRACKET_OPENED, 2, JSEXN_NOTE, "[ opened at line {0}, column {1}")
MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 0, JSEXN_SYNTAXERR, "catch after unconditional catch")
MSG_DEF(JSMSG_CATCH_IDENTIFIER, 0, JSEXN_SYNTAXERR, "missing identifier in catch")
MSG_DEF(JSMSG_CATCH_OR_FINALLY, 0, JSEXN_SYNTAXERR, "missing catch or finally after try")
MSG_DEF(JSMSG_CATCH_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "catch without try")
@ -285,7 +277,6 @@ MSG_DEF(JSMSG_DUPLICATE_FORMAL, 1, JSEXN_SYNTAXERR, "duplicate formal arg
MSG_DEF(JSMSG_DUPLICATE_LABEL, 0, JSEXN_SYNTAXERR, "duplicate label")
MSG_DEF(JSMSG_DUPLICATE_PROPERTY, 1, JSEXN_SYNTAXERR, "property name {0} appears more than once in object literal")
MSG_DEF(JSMSG_DUPLICATE_PROTO_PROPERTY, 0, JSEXN_SYNTAXERR, "property name __proto__ appears more than once in object literal")
MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?")
MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?")
MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0, JSEXN_SYNTAXERR, "export declarations may only appear at top level of a module")
MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "finally without try")
@ -312,7 +303,6 @@ MSG_DEF(JSMSG_GENERATOR_LABEL, 0, JSEXN_SYNTAXERR, "generator functions
MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled")
MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
MSG_DEF(JSMSG_LINE_BREAK_BEFORE_ARROW, 0, JSEXN_SYNTAXERR, "no line break is allowed before '=>'")
MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
MSG_DEF(JSMSG_MISSING_EXPONENT, 0, JSEXN_SYNTAXERR, "missing exponent")
@ -329,14 +319,12 @@ MSG_DEF(JSMSG_NO_BINDING_NAME, 0, JSEXN_SYNTAXERR, "missing binding name"
MSG_DEF(JSMSG_NO_EXPORT_NAME, 0, JSEXN_SYNTAXERR, "missing export name")
MSG_DEF(JSMSG_NO_IMPORT_NAME, 0, JSEXN_SYNTAXERR, "missing import name")
MSG_DEF(JSMSG_NO_VARIABLE_NAME, 0, JSEXN_SYNTAXERR, "missing variable name")
MSG_DEF(JSMSG_OF_AFTER_FOR_NAME, 0, JSEXN_SYNTAXERR, "missing 'of' after for")
MSG_DEF(JSMSG_PAREN_AFTER_ARGS, 0, JSEXN_SYNTAXERR, "missing ) after argument list")
MSG_DEF(JSMSG_PAREN_AFTER_CATCH, 0, JSEXN_SYNTAXERR, "missing ) after catch")
MSG_DEF(JSMSG_PAREN_AFTER_COND, 0, JSEXN_SYNTAXERR, "missing ) after condition")
MSG_DEF(JSMSG_PAREN_AFTER_FOR, 0, JSEXN_SYNTAXERR, "missing ( after for")
MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters")
MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL, 0, JSEXN_SYNTAXERR, "missing ) after for-loop control")
MSG_DEF(JSMSG_PAREN_AFTER_FOR_OF_ITERABLE, 0, JSEXN_SYNTAXERR, "missing ) after for-of iterable")
MSG_DEF(JSMSG_PAREN_AFTER_SWITCH, 0, JSEXN_SYNTAXERR, "missing ) after switch expression")
MSG_DEF(JSMSG_PAREN_AFTER_WITH, 0, JSEXN_SYNTAXERR, "missing ) after with-statement object")
MSG_DEF(JSMSG_PAREN_BEFORE_CATCH, 0, JSEXN_SYNTAXERR, "missing ( before catch")
@ -347,13 +335,10 @@ MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 0, JSEXN_SYNTAXERR, "missing ( before wit
MSG_DEF(JSMSG_PAREN_IN_PAREN, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical")
MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list")
MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after module specifier list")
MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_SYNTAXERR, "redeclaration of identifier '{0}' in catch")
MSG_DEF(JSMSG_RESERVED_ID, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier")
MSG_DEF(JSMSG_REST_WITH_COMMA, 0, JSEXN_SYNTAXERR, "rest element may not have a trailing comma")
MSG_DEF(JSMSG_REST_WITH_DEFAULT, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, 1, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level {0} declarations")
MSG_DEF(JSMSG_SELFHOSTED_METHOD_CALL, 0, JSEXN_SYNTAXERR, "self-hosted code may not contain direct method calls. Use callFunction() or callContentFunction()")
MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition")
MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")
MSG_DEF(JSMSG_SOURCE_TOO_LONG, 0, JSEXN_RANGEERR, "source is too long")
@ -362,7 +347,6 @@ MSG_DEF(JSMSG_STRICT_CODE_WITH, 0, JSEXN_SYNTAXERR, "strict mode code may
MSG_DEF(JSMSG_STRICT_NON_SIMPLE_PARAMS, 1, JSEXN_SYNTAXERR, "\"use strict\" not allowed in function with {0} parameter")
MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing } in template string")
MSG_DEF(JSMSG_TOO_MANY_CASES, 0, JSEXN_INTERNALERR, "too many switch cases")
MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 0, JSEXN_SYNTAXERR, "too many catch variables")
MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 0, JSEXN_SYNTAXERR, "too many constructor arguments")
MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 0, JSEXN_SYNTAXERR, "more than one switch default")
MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 0, JSEXN_SYNTAXERR, "too many function arguments")
@ -388,14 +372,9 @@ MSG_DEF(JSMSG_YIELD_IN_PARAMETER, 0, JSEXN_SYNTAXERR, "yield expression can
MSG_DEF(JSMSG_YIELD_OUTSIDE_GENERATOR, 0, JSEXN_SYNTAXERR, "yield expression is only valid in generators")
MSG_DEF(JSMSG_BAD_COLUMN_NUMBER, 0, JSEXN_RANGEERR, "column number out of range")
MSG_DEF(JSMSG_BAD_LINE_NUMBER, 0, JSEXN_RANGEERR, "line number out of range")
MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property names aren't supported in this destructuring declaration")
MSG_DEF(JSMSG_DEFAULT_IN_PATTERN, 0, JSEXN_SYNTAXERR, "destructuring defaults aren't supported in this destructuring declaration")
MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allowed within functions")
MSG_DEF(JSMSG_BAD_NEW_OPTIONAL, 0, JSEXN_SYNTAXERR, "new keyword cannot be used with an optional chain")
MSG_DEF(JSMSG_BAD_OPTIONAL_TEMPLATE, 0, JSEXN_SYNTAXERR, "tagged template cannot be used with optional chain")
MSG_DEF(JSMSG_ESCAPED_KEYWORD, 0, JSEXN_SYNTAXERR, "keywords must be written literally, without embedded escapes")
MSG_DEF(JSMSG_PRIVATE_FIELDS_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "private fields are not currently supported")
MSG_DEF(JSMSG_CLASS_STATIC_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "class static blocks are not currently supported")
MSG_DEF(JSMSG_IMPORT_ASSERTIONS_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "import assertions are not currently supported")
MSG_DEF(JSMSG_ILLEGAL_PRIVATE_FIELD, 0, JSEXN_SYNTAXERR, "private fields aren't valid in this context")
MSG_DEF(JSMSG_ILLEGAL_PRIVATE_NAME, 0, JSEXN_SYNTAXERR, "private names aren't valid in this context")
@ -431,7 +410,6 @@ MSG_DEF(JSMSG_WASM_VERBOSE, 1, JSEXN_WARN, "WebAssembly verbose: {
MSG_DEF(JSMSG_WASM_COMPILE_WARNING, 1, JSEXN_WARN, "WebAssembly module validated with warning: {0}")
MSG_DEF(JSMSG_WASM_HUGE_MEMORY_FAILED, 0, JSEXN_WARN, "WebAssembly.Memory failed to reserve a large virtual memory region. This may be due to low configured virtual memory limits on this system.")
MSG_DEF(JSMSG_WASM_COMPILE_ERROR, 1, JSEXN_WASMCOMPILEERROR, "{0}")
MSG_DEF(JSMSG_WASM_NO_SHMEM_COMPILE, 0, JSEXN_WASMCOMPILEERROR, "shared memory is disabled")
MSG_DEF(JSMSG_WASM_BAD_IMPORT_TYPE, 2, JSEXN_WASMLINKERROR, "import object field '{0}' is not a {1}")
MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG, 2, JSEXN_WASMLINKERROR, "imported function '{0}.{1}' signature mismatch")
MSG_DEF(JSMSG_WASM_BAD_TAG_SIG, 2, JSEXN_WASMLINKERROR, "imported tag '{0}.{1}' signature mismatch")
@ -459,7 +437,7 @@ MSG_DEF(JSMSG_WASM_BAD_CAST, 0, JSEXN_WASMRUNTIMEERROR, "bad cast")
MSG_DEF(JSMSG_WASM_MEM_IMP_LIMIT, 0, JSEXN_WASMRUNTIMEERROR, "too many memory pages")
MSG_DEF(JSMSG_WASM_TABLE_IMP_LIMIT, 0, JSEXN_WASMRUNTIMEERROR, "too many table elements")
MSG_DEF(JSMSG_WASM_ARRAY_IMP_LIMIT, 0, JSEXN_WASMRUNTIMEERROR, "too many array elements")
MSG_DEF(JSMSG_WASM_BAD_RANGE , 2, JSEXN_RANGEERR, "bad {0} {1}")
MSG_DEF(JSMSG_WASM_BAD_RANGE, 2, JSEXN_RANGEERR, "bad {0} {1}")
MSG_DEF(JSMSG_WASM_BAD_GROW, 1, JSEXN_RANGEERR, "failed to grow {0}")
MSG_DEF(JSMSG_WASM_TABLE_OUT_OF_BOUNDS, 0, JSEXN_WASMRUNTIMEERROR, "table index out of bounds")
MSG_DEF(JSMSG_WASM_BAD_UINT32, 2, JSEXN_TYPEERR, "bad {0} {1}")
@ -481,23 +459,19 @@ MSG_DEF(JSMSG_WASM_BAD_EXN_PAYLOAD_LEN, 2, JSEXN_TYPEERR, "expected {0} value
MSG_DEF(JSMSG_WASM_BAD_EXN_TAG, 0, JSEXN_TYPEERR, "exception's tag did not match the provided exception tag")
MSG_DEF(JSMSG_WASM_BAD_EXN_OPTIONS, 0, JSEXN_TYPEERR, "argument cannot be converted to an ExceptionOptions")
MSG_DEF(JSMSG_WASM_BAD_FUNCTION_VALUE, 0, JSEXN_TYPEERR, "second argument must be a function")
MSG_DEF(JSMSG_WASM_BAD_ARG_TYPE, 0, JSEXN_TYPEERR, "parameters and results must be an iterator of value types")
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE, 0, JSEXN_TYPEERR, "can't set value of immutable global")
MSG_DEF(JSMSG_WASM_WRONG_NUMBER_OF_VALUES, 2, JSEXN_TYPEERR, "wrong number of values returned by JavaScript to WebAssembly (expected {0}, got {1})")
MSG_DEF(JSMSG_WASM_NONSHARED_WAIT , 0, JSEXN_WASMRUNTIMEERROR, "atomic wait on non-shared memory")
MSG_DEF(JSMSG_WASM_NULL_REQUIRED, 0, JSEXN_TYPEERR, "nullref requires a null value")
MSG_DEF(JSMSG_WASM_NONSHARED_WAIT, 0, JSEXN_WASMRUNTIMEERROR, "atomic wait on non-shared memory")
MSG_DEF(JSMSG_WASM_SUPPLY_ONLY_ONE, 2, JSEXN_TYPEERR, "exactly one of {0} and {1} must be supplied")
MSG_DEF(JSMSG_WASM_MISSING_REQUIRED, 1, JSEXN_TYPEERR, "Missing required argument {0}")
// Proxy
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
MSG_DEF(JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler returned a non-object, non-null value")
MSG_DEF(JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler didn't return the target object's prototype")
MSG_DEF(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy setPrototypeOf handler returned false")
MSG_DEF(JSMSG_PROXY_ISEXTENSIBLE_RETURNED_FALSE,0,JSEXN_TYPEERR,"proxy isExtensible handler must return the same extensibility as target")
MSG_DEF(JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy setPrototypeOf handler returned true, even though the target's prototype is immutable because the target is non-extensible")
MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 0, JSEXN_TYPEERR, "can't change object's extensibility")
MSG_DEF(JSMSG_CANT_DEFINE_INVALID, 2, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor ('{0}', {1})")
@ -527,7 +501,6 @@ MSG_DEF(JSMSG_PROXY_CONSTRUCT_OBJECT, 0, JSEXN_TYPEERR, "proxy [[Construct]] mu
MSG_DEF(JSMSG_PROXY_EXTENSIBILITY, 0, JSEXN_TYPEERR, "proxy must report same extensiblitity as target")
MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF, 1, JSEXN_TYPEERR, "proxy [[GetOwnProperty]] must return an object or undefined for property '{0}'")
MSG_DEF(JSMSG_PROXY_REVOKED, 0, JSEXN_TYPEERR, "illegal operation attempted on a revoked proxy")
MSG_DEF(JSMSG_PROXY_ARG_REVOKED, 1, JSEXN_TYPEERR, "argument {0} cannot be a revoked proxy")
MSG_DEF(JSMSG_BAD_TRAP, 1, JSEXN_TYPEERR, "proxy handler's {0} trap wasn't undefined, null, or callable")
// Structured cloning
@ -550,7 +523,6 @@ MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 0, JSEXN_TYPEERR, "invalid script offset"
MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
MSG_DEF(JSMSG_DEBUG_BAD_RESUMPTION, 0, JSEXN_TYPEERR, "debugger resumption value must be undefined, {throw: val}, {return: val}, or null")
MSG_DEF(JSMSG_DEBUG_RESUMPTION_CONFLICT, 0, JSEXN_TYPEERR, "debugger hook returned a resumption, but an earlier hook already did")
MSG_DEF(JSMSG_DEBUG_BAD_YIELD, 0, JSEXN_TYPEERR, "generator yielded invalid value")
MSG_DEF(JSMSG_DEBUG_CANT_DEBUG_GLOBAL, 0, JSEXN_TYPEERR, "passing non-debuggable global to addDebuggee")
MSG_DEF(JSMSG_DEBUG_SAME_COMPARTMENT, 0, JSEXN_TYPEERR, "debugger and debuggee must be in different compartments")
MSG_DEF(JSMSG_DEBUG_CCW_REQUIRED, 1, JSEXN_TYPEERR, "{0}: argument must be an object from a different compartment")
@ -583,7 +555,6 @@ MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_RESOLVED, 0, JSEXN_TYPEERR, "Promise hasn't been
MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_FULFILLED, 0, JSEXN_TYPEERR, "Promise hasn't been fulfilled")
MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_REJECTED, 0, JSEXN_TYPEERR, "Promise hasn't been rejected")
MSG_DEF(JSMSG_DEBUG_NO_BINARY_SOURCE, 0, JSEXN_ERR, "WebAssembly binary source is not available")
MSG_DEF(JSMSG_DEBUG_NO_PRIVATE_METHOD, 0, JSEXN_ERR, "private method calls aren't available in this context")
// Testing functions
MSG_DEF(JSMSG_TESTING_SCRIPTS_ONLY, 0, JSEXN_TYPEERR, "only works on scripts")
@ -599,7 +570,6 @@ MSG_DEF(JSMSG_INTERNAL_INTL_ERROR, 0, JSEXN_ERR, "internal error while compu
MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
MSG_DEF(JSMSG_INVALID_UNIT_IDENTIFIER, 1, JSEXN_RANGEERR, "invalid unit identifier in NumberFormat(): {0}")
MSG_DEF(JSMSG_INVALID_DIGITS_VALUE, 1, JSEXN_RANGEERR, "invalid digits value: {0}")
MSG_DEF(JSMSG_INVALID_KEYS_TYPE, 0, JSEXN_TYPEERR, "calendar info keys must be an object or undefined")
MSG_DEF(JSMSG_INVALID_KEY, 1, JSEXN_RANGEERR, "invalid key: {0}")
MSG_DEF(JSMSG_INVALID_LANGUAGE_TAG, 1, JSEXN_RANGEERR, "invalid language tag: {0}")
MSG_DEF(JSMSG_INVALID_LOCALES_ELEMENT, 0, JSEXN_TYPEERR, "invalid element in locales argument")
@ -665,7 +635,6 @@ MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer
MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")
MSG_DEF(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected ArrayBuffer with at least {0} bytes, but species constructor returns ArrayBuffer with {1} bytes")
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 0, JSEXN_TYPEERR, "invalid arguments")
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED, 0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_BOUNDS, 2, JSEXN_RANGEERR, "start offset of {0}Array should be a multiple of {1}")
MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_MISALIGNED, 2, JSEXN_RANGEERR, "buffer length for {0}Array should be a multiple of {1}")
@ -696,7 +665,6 @@ MSG_DEF(JSMSG_SYMBOL_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert symbol t
// Atomics and futexes
MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY, 0, JSEXN_TYPEERR, "invalid array type for the operation")
MSG_DEF(JSMSG_ATOMICS_TOO_LONG, 0, JSEXN_RANGEERR, "timeout value too large")
MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED, 0, JSEXN_TYPEERR, "waiting is not allowed on this thread")
// XPConnect wrappers and DOM bindings
@ -716,12 +684,10 @@ MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involvin
MSG_DEF(JSMSG_REINIT_THIS, 0, JSEXN_REFERENCEERR, "super() called twice in derived class constructor")
// Modules
MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT, 0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "indirect export not found")
MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "ambiguous indirect export")
MSG_DEF(JSMSG_MISSING_IMPORT, 0, JSEXN_SYNTAXERR, "import not found")
MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 0, JSEXN_SYNTAXERR, "ambiguous import")
MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found for namespace")
MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
MSG_DEF(JSMSG_DYNAMIC_IMPORT_FAILED, 0, JSEXN_TYPEERR, "error loading dynamically imported module")
@ -760,39 +726,17 @@ MSG_DEF(JSMSG_READABLESTREAM_BYTES_TYPE_NOT_IMPLEMENTED, 0, JSEXN_RANGEERR,"supp
MSG_DEF(JSMSG_READABLESTREAM_BYOB_READER_FOR_NON_BYTE_STREAM,0,JSEXN_TYPEERR,"can't get a BYOB reader for a non-byte stream")
MSG_DEF(JSMSG_READABLESTREAM_INVALID_READER_MODE, 0, JSEXN_TYPEERR,"'mode' must be \"byob\" or undefined.")
MSG_DEF(JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, 1, JSEXN_RANGEERR, "'{0}' must be a finite, non-negative number.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_BYTESWRITTEN, 0, JSEXN_RANGEERR, "'bytesWritten' exceeds remaining length.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_SIZE, 0, JSEXN_RANGEERR, "view size does not match requested data.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_OFFSET, 0, JSEXN_RANGEERR, "view offset does not match requested position.")
MSG_DEF(JSMSG_READABLESTREAM_LOCKED_METHOD, 1, JSEXN_TYPEERR, "'{0}' can't be called on a locked stream.")
MSG_DEF(JSMSG_READABLESTREAM_LOCKED, 0, JSEXN_TYPEERR, "A Reader may only be created for an unlocked ReadableStream.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableByteStreamController.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableStreamDefaultController.")
MSG_DEF(JSMSG_READABLESTREAM_CONTROLLER_SET, 0, JSEXN_TYPEERR, "The ReadableStream already has a controller defined.")
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_OWNED, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may only be called on a reader owned by a stream.")
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_EMPTY, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may not be called on a reader with read requests.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREADER_READ_EMPTY_VIEW, 0, JSEXN_TYPEERR, "ReadableStreamBYOBReader.read() was passed an empty TypedArrayBuffer view.")
MSG_DEF(JSMSG_READABLESTREAMREADER_RELEASED, 0, JSEXN_TYPEERR, "The ReadableStream reader was released.")
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_CLOSED, 1, JSEXN_TYPEERR, "'{0}' called on a stream already closing.")
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, 1, JSEXN_TYPEERR, "'{0}' may only be called on a stream in the 'readable' state.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE,0, JSEXN_RANGEERR, "ReadableByteStreamController requires a positive integer or undefined for 'autoAllocateChunkSize'.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, 1, JSEXN_TYPEERR, "{0} passed a bad chunk.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL, 0, JSEXN_TYPEERR, "The ReadableByteStreamController cannot be closed while the buffer is being filled.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_NO_CONTROLLER, 1, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method '{0}' called on a request with no controller.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_RESPOND_CLOSED, 0, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method 'respond' called with non-zero number of bytes with a closed controller.")
MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "ReadableStream method {0} not yet implemented")
MSG_DEF(JSMSG_READABLESTREAM_PIPETO_BAD_SIGNAL, 0, JSEXN_TYPEERR, "signal must be either undefined or an AbortSignal")
// WritableStream
MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSINK_TYPE_WRONG, 0, JSEXN_RANGEERR,"'underlyingSink.type' must be undefined.")
MSG_DEF(JSMSG_WRITABLESTREAMWRITER_NOT_OWNED, 1, JSEXN_TYPEERR, "the WritableStream writer method '{0}' may only be called on a writer owned by a stream")
MSG_DEF(JSMSG_WRITABLESTREAM_CLOSED_OR_ERRORED, 0, JSEXN_TYPEERR, "writable stream is already closed or errored")
MSG_DEF(JSMSG_WRITABLESTREAM_RELEASED_DURING_WRITE, 0, JSEXN_TYPEERR, "writer's lock on the stream was released before writing completed")
MSG_DEF(JSMSG_WRITABLESTREAM_WRITE_CLOSING_OR_CLOSED, 0, JSEXN_TYPEERR, "can't write to a stream that's currently closing or already closed")
MSG_DEF(JSMSG_CANT_USE_LOCKED_WRITABLESTREAM, 1, JSEXN_TYPEERR, "can't {0} a WritableStream that's locked to a writer")
MSG_DEF(JSMSG_WRITABLESTREAM_CLOSE_CLOSING_OR_CLOSED, 0, JSEXN_TYPEERR, "can't close a stream that's currently closing or already closed")
MSG_DEF(JSMSG_WRITABLESTREAM_CANT_RELEASE_ALREADY_CLOSED,0, JSEXN_TYPEERR, "writer has already been released and can't be closed")
MSG_DEF(JSMSG_WRITABLESTREAM_ALREADY_LOCKED, 0, JSEXN_TYPEERR, "writable stream is already locked by another writer")
MSG_DEF(JSMSG_READABLESTREAM_NYI, 0, JSEXN_ERR, "full WritableStream support is not yet implemented")
// Other Stream-related
MSG_DEF(JSMSG_STREAM_MISSING_HIGHWATERMARK, 0, JSEXN_TYPEERR, "'highWaterMark' must not be undefined.")
@ -815,15 +759,11 @@ MSG_DEF(JSMSG_BIGINT_DIVISION_BY_ZERO, 0, JSEXN_RANGEERR, "BigInt division by ze
MSG_DEF(JSMSG_BIGINT_NEGATIVE_EXPONENT, 0, JSEXN_RANGEERR, "BigInt negative exponent")
MSG_DEF(JSMSG_BIGINT_INVALID_SYNTAX, 0, JSEXN_SYNTAXERR, "invalid BigInt syntax")
MSG_DEF(JSMSG_BIGINT_NOT_SERIALIZABLE, 0, JSEXN_TYPEERR, "BigInt value can't be serialized in JSON")
MSG_DEF(JSMSG_SC_BIGINT_DISABLED, 0, JSEXN_ERR, "BigInt not cloned - feature disabled in receiver")
// FinalizationRegistry
MSG_DEF(JSMSG_NOT_A_FINALIZATION_REGISTRY, 1, JSEXN_TYPEERR, "{0} is not a FinalizationRegistry")
MSG_DEF(JSMSG_NOT_A_FINALIZATION_ITERATOR, 1, JSEXN_TYPEERR, "{0} is not a FinalizationRegistryCleanupIterator")
MSG_DEF(JSMSG_BAD_HELD_VALUE, 0, JSEXN_TYPEERR, "The heldValue parameter passed to FinalizationRegistry.register must not be the same as the target parameter")
MSG_DEF(JSMSG_BAD_UNREGISTER_TOKEN, 1, JSEXN_TYPEERR, "Invalid unregister token passed to {0}")
MSG_DEF(JSMSG_STALE_FINALIZATION_REGISTRY_ITERATOR, 0, JSEXN_TYPEERR, "Can't use stale finalization registry iterator")
MSG_DEF(JSMSG_BAD_CLEANUP_STATE, 0, JSEXN_TYPEERR, "Can't call FinalizeRegistry.cleanupSome while cleanup is in progress")
MSG_DEF(JSMSG_BAD_FINALIZATION_REGISTRY_OBJECT, 0, JSEXN_TYPEERR, "cannot register the given object with a FinalizationRegistry")
// WeakRef
@ -837,12 +777,8 @@ MSG_DEF(JSMSG_NEGATIVE_LIMIT, 0, JSEXN_RANGEERR, "Ite
MSG_DEF(JSMSG_RECORD_TUPLE_NO_OBJECT, 0, JSEXN_TYPEERR, "Record and Tuple can only contain primitive values")
MSG_DEF(JSMSG_RECORD_NO_PROTO, 0, JSEXN_SYNTAXERR, "__proto__ is not a valid literal key in records")
MSG_DEF(JSMSG_RECORD_NO_SYMBOL_KEY, 0, JSEXN_TYPEERR, "Symbols cannot be used as record keys")
MSG_DEF(JSMSG_RECORD_INVALID_DESCRIPTOR, 0, JSEXN_TYPEERR, "Invalid record property descriptor")
MSG_DEF(JSMSG_BAD_TUPLE_LENGTH, 0, JSEXN_TYPEERR, "attempted to create excessively long tuple")
MSG_DEF(JSMSG_BAD_TUPLE_INDEX, 0, JSEXN_RANGEERR, "index out of range for tuple")
MSG_DEF(JSMSG_BAD_TUPLE_OBJECT, 0, JSEXN_TYPEERR, "value of TupleObject must be a Tuple")
MSG_DEF(JSMSG_EMPTY_TUPLE_REDUCE, 0, JSEXN_TYPEERR, "reduce of empty tuple with no initial values")
MSG_DEF(JSMSG_RECORD_TUPLE_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert Record or Tuple to number")
MSG_DEF(JSMSG_BAD_RECORD_TUPLE_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "a Record or Tuple WeakMap key must contain a Box")
//clang-format on

View File

@ -1354,8 +1354,6 @@ class NativeObject : public JSObject {
inline void ensureDenseInitializedLength(uint32_t index, uint32_t extra);
void setDenseElement(uint32_t index, const Value& val) {
// Note: Streams code can call this for the internal ListObject type with
// MagicValue(JS_WRITABLESTREAM_CLOSE_RECORD).
MOZ_ASSERT_IF(val.isMagic(), val.whyMagic() != JS_ELEMENTS_HOLE);
setDenseElementUnchecked(index, val);
}

View File

@ -814,9 +814,7 @@ CoderResult CodeMetadataTier(Coder<mode>& coder,
MOZ_TRY((CodeVector<mode, FuncExport, CodeFuncExport<mode>>(
coder, &item->funcExports)));
MOZ_TRY(CodeStackMaps(coder, &item->stackMaps, codeStart));
#ifdef ENABLE_WASM_EXCEPTIONS
MOZ_TRY(CodePodVector(coder, &item->tryNotes));
#endif
return Ok();
}
@ -838,9 +836,7 @@ CoderResult CodeMetadata(Coder<mode>& coder,
MOZ_TRY((CodeVector<mode, GlobalDesc, &CodeGlobalDesc<mode>>(
coder, &item->globals)));
MOZ_TRY(CodePodVector(coder, &item->tables));
#ifdef ENABLE_WASM_EXCEPTIONS
MOZ_TRY((CodeVector<mode, TagDesc, &CodeTagDesc<mode>>(coder, &item->tags)));
#endif
MOZ_TRY(CodePod(coder, &item->moduleName));
MOZ_TRY(CodePodVector(coder, &item->funcNames));
MOZ_TRY(CodeCacheableChars(coder, &item->filename));

View File

@ -31,6 +31,8 @@
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/glean/bindings/Glean.h"
#include "mozilla/glean/bindings/GleanPings.h"
#include "mozilla/ScriptPreloader.h"
#include "nsDOMMutationObserver.h"

View File

@ -5462,7 +5462,7 @@ PresShell::CanvasBackground PresShell::ComputeCanvasBackground() const {
return {GetDefaultBackgroundColorToDraw(), false};
}
ComputedStyle* bgStyle =
const ComputedStyle* bgStyle =
nsCSSRendering::FindRootFrameBackground(rootStyleFrame);
// XXX We should really be passing the canvasframe, not the root element
// style frame but we don't have access to the canvasframe here. It isn't

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<style>
* {
transform: matrix3d(130, -7052, 1000, 35803, 122, 7197, 197, 126, 201, 64, 38, -69, 5.727476671737168, 124, 22882, 168.04863081346616);
overflow: auto clip ! important;
padding-right: 39%;
min-inline-size: min-content;
}
</style>
</head>
<body>
<table>
<th>
<fieldset>
<textarea cols='4096' autofocus></textarea>
</fieldset>
</th>
</table>
</body>
</html>

View File

@ -568,3 +568,4 @@ pref(layout.accessiblecaret.enabled,true) load 1746989.html
load 1752649.html
load 1753779.html
pref(layout.css.backdrop-filter.enabled,true) load 1755790.html
load 1771503.html

View File

@ -6868,15 +6868,16 @@ nsTransparencyMode nsLayoutUtils::GetFrameTransparency(
return eTransparencyOpaque;
}
ComputedStyle* bgSC;
if (!nsCSSRendering::FindBackground(aBackgroundFrame, &bgSC)) {
ComputedStyle* bgSC = nsCSSRendering::FindBackground(aBackgroundFrame);
if (!bgSC) {
return eTransparencyTransparent;
}
const nsStyleBackground* bg = bgSC->StyleBackground();
if (NS_GET_A(bg->BackgroundColor(bgSC)) < 255 ||
// bottom layer's clip is used for the color
bg->BottomLayer().mClip != StyleGeometryBox::BorderBox)
bg->BottomLayer().mClip != StyleGeometryBox::BorderBox) {
return eTransparencyTransparent;
}
return eTransparencyOpaque;
}
@ -8923,14 +8924,11 @@ ScrollMetadata nsLayoutUtils::ComputeScrollMetadata(
if (isRootScrollFrame) {
metadata.SetBackgroundColor(
sRGBColor::FromABGR(presShell->GetCanvasBackground()));
} else {
ComputedStyle* backgroundStyle;
if (nsCSSRendering::FindBackground(aScrollFrame, &backgroundStyle)) {
nscolor backgroundColor =
backgroundStyle->StyleBackground()->BackgroundColor(
backgroundStyle);
metadata.SetBackgroundColor(sRGBColor::FromABGR(backgroundColor));
}
} else if (const auto* backgroundStyle =
nsCSSRendering::FindBackground(aScrollFrame)) {
nscolor backgroundColor =
backgroundStyle->StyleBackground()->BackgroundColor(backgroundStyle);
metadata.SetBackgroundColor(sRGBColor::FromABGR(backgroundColor));
}
}

View File

@ -237,7 +237,7 @@ static nscolor GetBackplateColor(nsIFrame* aFrame) {
// colors with the non-native theme, and native system colors should also
// match the native theme), then we're alright and we should compute an
// appropriate backplate color.
auto* style = frame->Style();
const auto* style = frame->Style();
if (style->StyleBackground()->IsTransparent(style)) {
continue;
}

View File

@ -461,11 +461,13 @@ void nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
ComputedStyle* bg = nullptr;
nsIFrame* dependentFrame = nullptr;
bool isThemed = IsThemed();
if (!isThemed &&
nsCSSRendering::FindBackgroundFrame(this, &dependentFrame)) {
bg = dependentFrame->Style();
if (dependentFrame == this) {
dependentFrame = nullptr;
if (!isThemed) {
dependentFrame = nsCSSRendering::FindBackgroundFrame(this);
if (dependentFrame) {
bg = dependentFrame->Style();
if (dependentFrame == this) {
dependentFrame = nullptr;
}
}
}

View File

@ -5773,7 +5773,11 @@ void nsIFrame::DisassociateImage(const StyleImage& aImage) {
StyleImageRendering nsIFrame::UsedImageRendering() const {
ComputedStyle* style;
if (nsCSSRendering::IsCanvasFrame(this)) {
nsCSSRendering::FindBackground(this, &style);
// XXXdholbert Maybe we should use FindCanvasBackground here (instead of
// FindBackground), since we're inside an IsCanvasFrame check? Though then
// we'd also have to copypaste or abstract-away the multi-part root-frame
// lookup that the canvas-flavored API requires.
style = nsCSSRendering::FindBackground(this);
} else {
style = Style();
}

View File

@ -6,6 +6,8 @@
/* utility functions for drawing borders and backgrounds */
#include "nsCSSRendering.h"
#include <ctime>
#include "gfx2DGlue.h"
@ -41,7 +43,6 @@
#include "nsIScrollableFrame.h"
#include "imgIContainer.h"
#include "ImageOps.h"
#include "nsCSSRendering.h"
#include "nsCSSColorUtils.h"
#include "nsITheme.h"
#include "nsLayoutUtils.h"
@ -1137,8 +1138,8 @@ auto nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame* aFrame,
}
if (IsCanvasFrame(frame)) {
nsIFrame* bgFrame = nullptr;
if (FindBackgroundFrame(frame, &bgFrame) &&
nsIFrame* bgFrame = FindBackgroundFrame(frame);
if (bgFrame &&
NS_GET_A(bgFrame->StyleBackground()->BackgroundColor(bgFrame))) {
return {bgFrame, false, true};
}
@ -1223,16 +1224,23 @@ nsIFrame* nsCSSRendering::FindBackgroundStyleFrame(nsIFrame* aForFrame) {
* + we don't paint the background on the BODY element in *some* cases,
* and for SGML-based HTML documents only.
*
* |FindBackground| returns true if a background should be painted, and
* the resulting ComputedStyle to use for the background information
* will be filled in to |aBackground|.
* |FindBackground| checks whether a background should be painted. If yes, it
* returns the resulting ComputedStyle to use for the background information;
* Otherwise, it returns nullptr.
*/
ComputedStyle* nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame) {
return FindBackgroundStyleFrame(aForFrame)->Style();
}
inline bool FindElementBackground(const nsIFrame* aForFrame,
nsIFrame* aRootElementFrame) {
// Helper for FindBackgroundFrame. Returns true if aForFrame has a meaningful
// background that it should draw (i.e. that it hasn't propagated to another
// frame). See documentation for FindBackground.
inline bool FrameHasMeaningfulBackground(const nsIFrame* aForFrame,
nsIFrame* aRootElementFrame) {
MOZ_ASSERT(!nsCSSRendering::IsCanvasFrame(aForFrame),
"FindBackgroundFrame handles canvas frames before calling us, "
"so we don't need to consider them here");
if (aForFrame == aRootElementFrame) {
// We must have propagated our background to the viewport or canvas. Abort.
return false;
@ -1272,27 +1280,25 @@ inline bool FindElementBackground(const nsIFrame* aForFrame,
return !htmlBG->IsTransparent(aRootElementFrame);
}
bool nsCSSRendering::FindBackgroundFrame(const nsIFrame* aForFrame,
nsIFrame** aBackgroundFrame) {
nsIFrame* nsCSSRendering::FindBackgroundFrame(const nsIFrame* aForFrame) {
nsIFrame* rootElementFrame =
aForFrame->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
if (IsCanvasFrame(aForFrame)) {
*aBackgroundFrame = FindCanvasBackgroundFrame(aForFrame, rootElementFrame);
return true;
return FindCanvasBackgroundFrame(aForFrame, rootElementFrame);
}
*aBackgroundFrame = const_cast<nsIFrame*>(aForFrame);
return FindElementBackground(aForFrame, rootElementFrame);
if (FrameHasMeaningfulBackground(aForFrame, rootElementFrame)) {
return const_cast<nsIFrame*>(aForFrame);
}
return nullptr;
}
bool nsCSSRendering::FindBackground(const nsIFrame* aForFrame,
ComputedStyle** aBackgroundSC) {
nsIFrame* backgroundFrame = nullptr;
if (FindBackgroundFrame(aForFrame, &backgroundFrame)) {
*aBackgroundSC = backgroundFrame->Style();
return true;
ComputedStyle* nsCSSRendering::FindBackground(const nsIFrame* aForFrame) {
if (auto* backgroundFrame = FindBackgroundFrame(aForFrame)) {
return backgroundFrame->Style();
}
return false;
return nullptr;
}
void nsCSSRendering::BeginFrameTreesLocked() { ++gFrameTreeLockCount; }
@ -1790,8 +1796,8 @@ ImgDrawResult nsCSSRendering::PaintStyleImageLayer(const PaintBGParams& aParams,
MOZ_ASSERT(aParams.frame,
"Frame is expected to be provided to PaintStyleImageLayer");
ComputedStyle* sc;
if (!FindBackground(aParams.frame, &sc)) {
const ComputedStyle* sc = FindBackground(aParams.frame);
if (!sc) {
// We don't want to bail out if moz-appearance is set on a root
// node. If it has a parent content node, bail because it's not
// a root, otherwise keep going in order to let the theme stuff
@ -1878,8 +1884,8 @@ ImgDrawResult nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
"Frame is expected to be provided to "
"BuildWebRenderDisplayItemsForStyleImageLayer");
ComputedStyle* sc;
if (!FindBackground(aParams.frame, &sc)) {
ComputedStyle* sc = FindBackground(aParams.frame);
if (!sc) {
// We don't want to bail out if moz-appearance is set on a root
// node. If it has a parent content node, bail because it's not
// a root, otherwise keep going in order to let the theme stuff
@ -2287,7 +2293,8 @@ static Maybe<nscolor> CalcScrollbarColor(nsIFrame* aFrame,
return Some(color.CalcColor(*scrollbarStyle));
}
static nscolor GetBackgroundColor(nsIFrame* aFrame, ComputedStyle* aStyle) {
static nscolor GetBackgroundColor(nsIFrame* aFrame,
const ComputedStyle* aStyle) {
switch (aStyle->StyleDisplay()->EffectiveAppearance()) {
case StyleAppearance::ScrollbarthumbVertical:
case StyleAppearance::ScrollbarthumbHorizontal: {
@ -2313,7 +2320,7 @@ static nscolor GetBackgroundColor(nsIFrame* aFrame, ComputedStyle* aStyle) {
}
nscolor nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext,
ComputedStyle* aStyle,
const ComputedStyle* aStyle,
nsIFrame* aFrame,
bool& aDrawBackgroundImage,
bool& aDrawBackgroundColor) {
@ -2380,7 +2387,7 @@ static CompositionOp DetermineCompositionOp(
ImgDrawResult nsCSSRendering::PaintStyleImageLayerWithSC(
const PaintBGParams& aParams, gfxContext& aRenderingCtx,
ComputedStyle* aBackgroundSC, const nsStyleBorder& aBorder) {
const ComputedStyle* aBackgroundSC, const nsStyleBorder& aBorder) {
MOZ_ASSERT(aParams.frame,
"Frame is expected to be provided to PaintStyleImageLayerWithSC");

View File

@ -283,15 +283,16 @@ struct nsCSSRendering {
static bool IsCanvasFrame(const nsIFrame* aFrame);
/**
* Fill in an aBackgroundSC to be used to paint the background
* for an element. This applies the rules for propagating
* backgrounds between BODY, the root element, and the canvas.
* @return true if there is some meaningful background.
* Returns the ComputedStyle to be used to paint the background for the given
* frame, if its element has a meaningful background. This applies the rules
* for propagating backgrounds between BODY, the root element, and the
* canvas.
*
* @return the ComputedStyle (if any) to be used for painting aForFrame's
* background.
*/
static bool FindBackground(const nsIFrame* aForFrame,
mozilla::ComputedStyle** aBackgroundSC);
static bool FindBackgroundFrame(const nsIFrame* aForFrame,
nsIFrame** aBackgroundFrame);
static mozilla::ComputedStyle* FindBackground(const nsIFrame* aForFrame);
static nsIFrame* FindBackgroundFrame(const nsIFrame* aForFrame);
/**
* As FindBackground, but the passed-in frame is known to be a root frame
@ -348,7 +349,7 @@ struct nsCSSRendering {
* Determine the background color to draw taking into account print settings.
*/
static nscolor DetermineBackgroundColor(nsPresContext* aPresContext,
mozilla::ComputedStyle* aStyle,
const mozilla::ComputedStyle* aStyle,
nsIFrame* aFrame,
bool& aDrawBackgroundImage,
bool& aDrawBackgroundColor);
@ -502,7 +503,8 @@ struct nsCSSRendering {
*/
static ImgDrawResult PaintStyleImageLayerWithSC(
const PaintBGParams& aParams, gfxContext& aRenderingCtx,
mozilla::ComputedStyle* mBackgroundSC, const nsStyleBorder& aBorder);
const mozilla::ComputedStyle* aBackgroundSC,
const nsStyleBorder& aBorder);
static bool CanBuildWebRenderDisplayItemsForStyleImageLayer(
WebRenderLayerManager* aManager, nsPresContext& aPresCtx,

View File

@ -2885,7 +2885,7 @@ static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame(
nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, uint16_t aLayer,
const nsRect& aBackgroundRect,
ComputedStyle* aBackgroundStyle) {
const ComputedStyle* aBackgroundStyle) {
nsPresContext* presContext = aFrame->PresContext();
uint32_t flags = aBuilder->GetBackgroundPaintFlags();
const nsStyleImageLayers::Layer& layer =
@ -2957,8 +2957,8 @@ nsDisplayBackgroundImage::~nsDisplayBackgroundImage() {
}
static nsIFrame* GetBackgroundComputedStyleFrame(nsIFrame* aFrame) {
nsIFrame* f;
if (!nsCSSRendering::FindBackgroundFrame(aFrame, &f)) {
nsIFrame* f = nsCSSRendering::FindBackgroundFrame(aFrame);
if (!f) {
// We don't want to bail out if moz-appearance is set on a root
// node. If it has a parent content node, bail because it's not
// a root, other wise keep going in order to let the theme stuff
@ -3085,7 +3085,7 @@ static nsDisplayThemedBackground* CreateThemedBackground(
static nsDisplayBackgroundColor* CreateBackgroundColor(
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame,
nsRect& aBgRect, ComputedStyle* aBgSC, nscolor aColor) {
nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) {
if (aSecondaryFrame) {
const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame));
return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>(
@ -3104,7 +3104,7 @@ AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
const nsRect& aBackgroundOriginRect, nsIFrame* aSecondaryReferenceFrame,
Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
aAutoBuildingDisplayList) {
ComputedStyle* bgSC = aComputedStyle;
const ComputedStyle* bgSC = aComputedStyle;
const nsStyleBackground* bg = nullptr;
nsRect bgRect = aBackgroundRect;
nsRect bgOriginRect = bgRect;

View File

@ -4117,7 +4117,7 @@ class nsDisplayBackgroundImage : public nsPaintedDisplayItem {
public:
struct InitData {
nsDisplayListBuilder* builder;
ComputedStyle* backgroundStyle;
const ComputedStyle* backgroundStyle;
nsCOMPtr<imgIContainer> image;
nsRect backgroundRect;
nsRect fillArea;
@ -4136,7 +4136,7 @@ class nsDisplayBackgroundImage : public nsPaintedDisplayItem {
*/
static InitData GetInitData(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
uint16_t aLayer, const nsRect& aBackgroundRect,
ComputedStyle* aBackgroundStyle);
const ComputedStyle* aBackgroundStyle);
explicit nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, const InitData& aInitData,
@ -4251,7 +4251,7 @@ class nsDisplayBackgroundImage : public nsPaintedDisplayItem {
// Cache the result of nsCSSRendering::FindBackground. Always null if
// mIsThemed is true or if FindBackground returned false.
RefPtr<ComputedStyle> mBackgroundStyle;
RefPtr<const ComputedStyle> mBackgroundStyle;
nsCOMPtr<imgIContainer> mImage;
nsIFrame* mDependentFrame;
nsRect mBackgroundRect; // relative to the reference frame

View File

@ -2127,7 +2127,7 @@ bool nsStyleBackground::IsTransparent(const nsIFrame* aFrame) const {
return IsTransparent(aFrame->Style());
}
bool nsStyleBackground::IsTransparent(mozilla::ComputedStyle* aStyle) const {
bool nsStyleBackground::IsTransparent(const ComputedStyle* aStyle) const {
return BottomLayer().mImage.IsNone() && mImage.mImageCount == 1 &&
NS_GET_A(BackgroundColor(aStyle)) == 0;
}

View File

@ -369,7 +369,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBackground {
// True if this background is completely transparent.
bool IsTransparent(const nsIFrame* aFrame) const;
bool IsTransparent(mozilla::ComputedStyle* aStyle) const;
bool IsTransparent(const mozilla::ComputedStyle* aStyle) const;
// We have to take slower codepaths for fixed background attachment,
// but we don't want to do that when there's no image.

View File

@ -491,7 +491,7 @@ using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams;
*/
static bool PaintMaskSurface(const PaintFramesParams& aParams,
DrawTarget* aMaskDT, float aOpacity,
ComputedStyle* aSC,
const ComputedStyle* aSC,
const nsTArray<SVGMaskFrame*>& aMaskFrames,
const nsPoint& aOffsetToUserSpace) {
MOZ_ASSERT(aMaskFrames.Length() > 0);
@ -568,7 +568,7 @@ struct MaskPaintResult {
};
static MaskPaintResult CreateAndPaintMaskSurface(
const PaintFramesParams& aParams, float aOpacity, ComputedStyle* aSC,
const PaintFramesParams& aParams, float aOpacity, const ComputedStyle* aSC,
const nsTArray<SVGMaskFrame*>& aMaskFrames,
const nsPoint& aOffsetToUserSpace) {
const nsStyleSVGReset* svgReset = aSC->StyleSVGReset();

View File

@ -7499,9 +7499,13 @@ nsRect nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder,
}
void nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame) {
ComputedStyle* bgSC;
if (!nsCSSRendering::FindBackground(aFrame, &bgSC)) return;
if (!bgSC->StyleBackground()->HasFixedBackground(aFrame)) return;
ComputedStyle* bgSC = nsCSSRendering::FindBackground(aFrame);
if (!bgSC) {
return;
}
if (!bgSC->StyleBackground()->HasFixedBackground(aFrame)) {
return;
}
mPartHasFixedBackground = true;
}

View File

@ -52,13 +52,24 @@ public class ExampleCrashHandler extends Service {
String id = createNotificationChannel();
int intentFlag = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
intentFlag = PendingIntent.FLAG_IMMUTABLE;
}
PendingIntent reportIntent =
PendingIntent.getService(
this, 0, new Intent(ACTION_REPORT_CRASH, null, this, ExampleCrashHandler.class), 0);
this,
0,
new Intent(ACTION_REPORT_CRASH, null, this, ExampleCrashHandler.class),
intentFlag);
PendingIntent dismissIntent =
PendingIntent.getService(
this, 0, new Intent(ACTION_DISMISS, null, this, ExampleCrashHandler.class), 0);
this,
0,
new Intent(ACTION_DISMISS, null, this, ExampleCrashHandler.class),
intentFlag);
Notification notification =
new NotificationCompat.Builder(this, id)
@ -66,8 +77,8 @@ public class ExampleCrashHandler extends Service {
.setContentTitle(getResources().getString(R.string.crashed_title))
.setContentText(getResources().getString(R.string.crashed_text))
.setDefaults(Notification.DEFAULT_ALL)
.setContentIntent(reportIntent)
.addAction(0, getResources().getString(R.string.crashed_ignore), dismissIntent)
.addAction(0, getResources().getString(R.string.crashed_report), reportIntent)
.setAutoCancel(true)
.setOngoing(false)
.build();

View File

@ -812,7 +812,8 @@ public class GeckoViewActivity extends AppCompatActivity
Intent clickIntent = new Intent(GeckoViewActivity.this, GeckoViewActivity.class);
clickIntent.putExtra("onClick", notification);
PendingIntent dismissIntent =
PendingIntent.getActivity(GeckoViewActivity.this, mLastID, clickIntent, 0);
PendingIntent.getActivity(
GeckoViewActivity.this, mLastID, clickIntent, PendingIntent.FLAG_IMMUTABLE);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(GeckoViewActivity.this, CHANNEL_ID)

View File

@ -33,6 +33,7 @@
<string name="crashed_title">GeckoView Example Crashed</string>
<string name="crashed_text">Tap to report to Mozilla.</string>
<string name="crashed_ignore">Ignore</string>
<string name="crashed_report">Report</string>
<string name="device_sharing_microphone">Microphone is on</string>
<string name="device_sharing_camera">Camera is on</string>
<string name="device_sharing_camera_and_mic">Camera and microphone are on</string>

View File

@ -6861,12 +6861,10 @@
mirror: always
#endif // defined(ENABLE_WASM_EXTENDED_CONST)
#if defined(ENABLE_WASM_EXCEPTIONS)
- name: javascript.options.wasm_exceptions
type: bool
value: true
mirror: always
#endif // defined(ENABLE_WASM_EXCEPTIONS)
#if defined(ENABLE_WASM_FUNCTION_REFERENCES)
- name: javascript.options.wasm_function_references

View File

@ -5003,7 +5003,7 @@ HttpBaseChannel::SetAllRedirectsPassTimingAllowCheck(bool aPassesCheck) {
return NS_OK;
}
// http://www.w3.org/TR/resource-timing/#timing-allow-check
// https://fetch.spec.whatwg.org/#tao-check
NS_IMETHODIMP
HttpBaseChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* _retval) {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
@ -5017,7 +5017,13 @@ HttpBaseChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* _retval) {
bool sameOrigin = false;
rv = resourcePrincipal->Equals(aOrigin, &sameOrigin);
if (NS_SUCCEEDED(rv) && sameOrigin) {
nsAutoCString serializedOrigin;
nsContentSecurityManager::GetSerializedOrigin(aOrigin, resourcePrincipal,
serializedOrigin, mLoadInfo);
// All redirects are same origin
if (sameOrigin && !serializedOrigin.IsEmpty()) {
*_retval = true;
return NS_OK;
}
@ -5029,9 +5035,6 @@ HttpBaseChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* _retval) {
return NS_OK;
}
nsAutoCString origin;
aOrigin->GetAsciiOrigin(origin);
Tokenizer p(headerValue);
Tokenizer::Token t;
@ -5044,7 +5047,7 @@ HttpBaseChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* _retval) {
nsHttp::TrimHTTPWhitespace(headerItem, headerItem);
// If the list item contains a case-sensitive match for the value of the
// origin, or a wildcard, return pass
if (headerItem == origin || headerItem == "*") {
if (headerItem == serializedOrigin || headerItem == "*") {
*_retval = true;
return NS_OK;
}

View File

@ -731,31 +731,6 @@ NullHttpChannel::SetAllRedirectsPassTimingAllowCheck(
NS_IMETHODIMP
NullHttpChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* _retval) {
if (!mResourcePrincipal || !aOrigin) {
*_retval = false;
return NS_OK;
}
bool sameOrigin = false;
nsresult rv = mResourcePrincipal->Equals(aOrigin, &sameOrigin);
if (NS_SUCCEEDED(rv) && sameOrigin) {
*_retval = true;
return NS_OK;
}
if (mTimingAllowOriginHeader == "*") {
*_retval = true;
return NS_OK;
}
nsAutoCString origin;
aOrigin->GetAsciiOrigin(origin);
if (mTimingAllowOriginHeader == origin) {
*_retval = true;
return NS_OK;
}
*_retval = false;
return NS_OK;
}

View File

@ -600,7 +600,7 @@
temp_rows = rows;
input.expect_delim('/')?;
flow = parse_auto_flow(input, false)?;
auto_cols = grid_auto_columns::parse(context, input).unwrap_or_default();
auto_cols = input.try_parse(|i| grid_auto_columns::parse(context, i)).unwrap_or_default();
} else {
flow = parse_auto_flow(input, true)?;
auto_rows = input.try_parse(|i| grid_auto_rows::parse(context, i)).unwrap_or_default();

View File

@ -1,7 +0,0 @@
[SO-XO-SO-redirect-chain-tao.https.html]
[Verify that cross origin resources' timings are not exposed when same-origin=>cross-origin=>same-origin redirects have `Timing-Allow-Origin:` headers only on some of the responses.]
expected: FAIL
[Verify that cross origin resources' timings are not exposed when same-origin=>cross-origin=>same-origin redirects have `Timing-Allow-Origin:` headers with a specific origin.]
expected: FAIL

View File

@ -39,11 +39,11 @@
expected:
if release_or_beta: FAIL
[anyfunc, mutable]
[funcref, mutable]
expected:
if release_or_beta: FAIL
[anyfunc, immutable]
[funcref, immutable]
expected:
if release_or_beta: FAIL
@ -93,11 +93,11 @@
expected:
if release_or_beta: FAIL
[anyfunc, mutable]
[funcref, mutable]
expected:
if release_or_beta: FAIL
[anyfunc, immutable]
[funcref, immutable]
expected:
if release_or_beta: FAIL
@ -147,11 +147,11 @@
expected:
if release_or_beta: FAIL
[anyfunc, mutable]
[funcref, mutable]
expected:
if release_or_beta: FAIL
[anyfunc, immutable]
[funcref, immutable]
expected:
if release_or_beta: FAIL

View File

@ -46,6 +46,7 @@ test_invalid_value("grid", 'auto-flow 100px');
test_invalid_value("grid", 'auto-flow / auto-flow');
test_invalid_value("grid", 'auto-flow 1fr / auto-flow 1fr');
test_invalid_value("grid", 'dense auto-flow / dense auto-flow');
test_invalid_value("grid", 'auto / auto-flow foo()');
// FIXME: add more values to test full syntax
</script>

View File

@ -16,15 +16,17 @@ var EXPORTED_SYMBOLS = [
"TestingCrashManager",
];
const { CrashManager } = ChromeUtils.import(
"resource://gre/modules/CrashManager.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
CrashManager: "resource://gre/modules/CrashManager.jsm",
Log: "resource://gre/modules/Log.jsm",
OS: "resource://gre/modules/osfile.jsm",
setTimeout: "resource://gre/modules/Timer.jsm",
@ -54,11 +56,11 @@ var sleep = function(wait) {
};
var TestingCrashManager = function(options) {
lazy.CrashManager.call(this, options);
CrashManager.call(this, options);
};
TestingCrashManager.prototype = {
__proto__: lazy.CrashManager.prototype,
__proto__: CrashManager.prototype,
createDummyDump(submitted = false, date = new Date(), hr = false) {
let uuid = Services.uuid.generateUUID().toString();
@ -156,7 +158,7 @@ TestingCrashManager.prototype = {
return this.EVENT_FILE_ERROR_UNKNOWN_EVENT;
}
return lazy.CrashManager.prototype._handleEventFilePayload.call(
return CrashManager.prototype._handleEventFilePayload.call(
this,
store,
entry,

Some files were not shown because too many files have changed in this diff Show More