Merge autoland to mozilla-central. a=merge

This commit is contained in:
Sandor Molnar 2022-08-12 12:25:59 +03:00
Родитель f96ee03708 4782331e6c
Коммит 58559a3e5b
229 изменённых файлов: 7769 добавлений и 3120 удалений

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

@ -29,8 +29,6 @@ jobs:
- mozilla-central
when:
by-project:
# `l10n-bumper` job should have enough time to finish before this
# job runs
mozilla-central: [{hour: 10, minute: 0}, {hour: 22, minute: 0}]
# No default
@ -277,12 +275,14 @@ jobs:
treeherder-symbol: l10n-bump
target-tasks-method: l10n_bump
run-on-projects:
- autoland
- mozilla-central
- mozilla-beta
when:
by-project:
# 3h15m before launch of `nightly-desktop`
mozilla-central: [{hour: 6, minute: 45}, {hour: 18, minute: 45}]
autoland: [{hour: 6, minute: 45}, {hour: 18, minute: 45}]
mozilla-central: []
# 3h15m before launch of `daily-releases`
mozilla-beta: [{hour: 18, minute: 45}]
# No default

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

@ -2061,8 +2061,8 @@ pref("browser.tabs.crashReporting.includeURL", false);
// nightly and developer edition.
pref("extensions.experiments.enabled", false);
#if defined(XP_WIN)
// Allows us to deprioritize the processes of background tabs at an OS level
#if defined(XP_LINUX) || defined(XP_WIN)
// Allows us to adjust the priority of child processes at the OS level
pref("dom.ipc.processPriorityManager.enabled", true);
#endif

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

@ -42,7 +42,7 @@
command="Browser:Stop"/>
<menuitem id="context-bookmarkpage"
class="menuitem-iconic"
data-l10n-id="main-context-menu-bookmark-add"
data-l10n-id="main-context-menu-bookmark-page"
oncommand="gContextMenu.bookmarkThisPage();"/>
</menugroup>
#endif
@ -94,7 +94,7 @@
oncommand="gContextMenu.openLinkInPrivateWindow();"/>
<menuseparator id="context-sep-open"/>
<menuitem id="context-bookmarklink"
data-l10n-id="main-context-menu-bookmark-link"
data-l10n-id="main-context-menu-bookmark-link-2"
oncommand="gContextMenu.bookmarkLink();"/>
<menuitem id="context-savelink"
data-l10n-id="main-context-menu-save-link"
@ -252,7 +252,7 @@
<menuseparator id="context-sep-ctp"/>
#ifdef XP_MACOSX
<menuitem id="context-bookmarkpage"
data-l10n-id="main-context-menu-bookmark-add-mac"
data-l10n-id="main-context-menu-bookmark-page-mac"
oncommand="gContextMenu.bookmarkThisPage();"/>
#endif
<menuitem id="context-savepage"
@ -310,7 +310,7 @@
data-l10n-id="main-context-menu-reveal-password"
oncommand="gContextMenu.toggleRevealPassword();"/>
<menuitem id="context-print-selection"
data-l10n-id="main-context-menu-print-selection"
data-l10n-id="main-context-menu-print-selection-2"
oncommand="gContextMenu.printSelection();"/>
<menuseparator id="context-sep-selectall"/>
@ -368,7 +368,7 @@
oncommand="gContextMenu.reloadFrame(event);"/>
<menuseparator/>
<menuitem id="context-bookmarkframe"
data-l10n-id="main-context-menu-frame-bookmark"
data-l10n-id="main-context-menu-frame-add-bookmark"
oncommand="gContextMenu.addBookmarkForFrame();"/>
<menuitem id="context-saveframe"
data-l10n-id="main-context-menu-frame-save-as"

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

@ -310,7 +310,7 @@
<menuitem id="menu_bookmarkThisPage"
command="Browser:AddBookmarkAs"
key="addBookmarkAsKb"
data-l10n-id="menu-bookmark-current-tab"/>
data-l10n-id="menu-bookmark-tab"/>
<menuitem id="menu_bookmarkAllTabs"
class="show-only-for-keyboard"
command="Browser:BookmarkAllTabs"

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

@ -1829,7 +1829,7 @@ var BookmarkingUI = {
if (!this.starBox) {
// The BOOKMARK_BUTTON_SHORTCUT exists only in browser.xhtml.
// Return early if we're not in this context, but still reset the
// Bookmark This Page items.
// Bookmark Page items.
this.updateBookmarkPageMenuItem(true);
return;
}
@ -1845,7 +1845,7 @@ var BookmarkingUI = {
l10nArgs
);
// Update the Bookmark This Page menuitem when bookmarked state changes.
// Update the Bookmark Page menuitem when bookmarked state changes.
this.updateBookmarkPageMenuItem();
Services.obs.notifyObservers(
@ -1856,19 +1856,16 @@ var BookmarkingUI = {
},
/**
* Update the "bookmark this page" menuitems on the menubar, panels, context
* Update the "Bookmark Page…" menuitems on the menubar, panels, context
* menu and page actions.
* @param {boolean} [forceReset] passed when we're destroyed and the label
* should go back to the default (Bookmark This Page), for MacOS.
* should go back to the default (Bookmark Page), for MacOS.
*/
updateBookmarkPageMenuItem(forceReset = false) {
let isStarred = !forceReset && this._itemGuids.size > 0;
// Define the l10n id which will be used to localize elements
// that only require a label using the menubar.ftl messages.
let menuItemL10nId = isStarred
? "menu-bookmark-edit"
: "menu-bookmark-current-tab";
let menuItemL10nId = isStarred ? "menu-edit-bookmark" : "menu-bookmark-tab";
let menuItem = document.getElementById("menu_bookmarkThisPage");
if (menuItem) {
// Localize the menubar item.
@ -1876,8 +1873,8 @@ var BookmarkingUI = {
}
let panelMenuItemL10nId = isStarred
? "bookmarks-bookmark-edit-panel"
: "bookmarks-current-tab";
? "bookmarks-subview-edit-bookmark"
: "bookmarks-subview-bookmark-tab";
let panelMenuToolbarButton = PanelMultiView.getViewNode(
document,
"panelMenuBookmarkThisPage"
@ -1892,8 +1889,8 @@ var BookmarkingUI = {
if (contextItem) {
if (AppConstants.platform == "macosx") {
let contextItemL10nId = isStarred
? "main-context-menu-bookmark-edit-mac"
: "main-context-menu-bookmark-add-mac";
? "main-context-menu-edit-bookmark-mac"
: "main-context-menu-bookmark-page-mac";
document.l10n.setAttributes(contextItem, contextItemL10nId);
} else {
let shortcutElem = document.getElementById(
@ -1902,14 +1899,14 @@ var BookmarkingUI = {
if (shortcutElem) {
let shortcut = ShortcutUtils.prettifyShortcut(shortcutElem);
let contextItemL10nId = isStarred
? "main-context-menu-bookmark-change-with-shortcut"
: "main-context-menu-bookmark-add-with-shortcut";
? "main-context-menu-edit-bookmark-with-shortcut"
: "main-context-menu-bookmark-page-with-shortcut";
let l10nArgs = { shortcut };
document.l10n.setAttributes(contextItem, contextItemL10nId, l10nArgs);
} else {
let contextItemL10nId = isStarred
? "main-context-menu-bookmark-change"
: "main-context-menu-bookmark-add";
? "main-context-menu-edit-bookmark"
: "main-context-menu-bookmark-page";
document.l10n.setAttributes(contextItem, contextItemL10nId);
}
}

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

@ -153,7 +153,7 @@ panelview[mainview] > .panel-header {
transition: height var(--panelui-subview-transition-duration);
}
@supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
@media (-moz-box-flexbox-emulation) {
#tabbrowser-tabs {
/* Without this, the tabs container width extends beyond the window width */
width: 0;

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

@ -2172,6 +2172,17 @@ var gBrowserInit = {
return;
}
// For custom new window URLs that are not empty, we need to wait for the
// page to load before we select the URL bar. So we set the
// _selectUrlbarOnLoad flag here and will make use of it in
// onLocationChange.
if (!isBlankPageURL(uriToLoad) && HomePage.get(window) == uriToLoad) {
gBrowserInit._selectUrlbarOnLoad = true;
gBrowserInit._initiallyFocusedElement = initiallyFocusedElement;
shouldRemoveFocusedAttribute = false;
return;
}
if (gBrowser.selectedBrowser.isRemoteBrowser) {
// If the initial browser is remote, in order to optimize for first paint,
// we'll defer switching focus to that browser until it has painted.
@ -5403,6 +5414,17 @@ var XULBrowserWindow = {
// via TabsProgressListener.onLocationChange and we do not want it called twice
gURLBar.setURI(aLocationURI, aIsSimulated, isSessionRestore);
if (gBrowserInit._selectUrlbarOnLoad) {
if (
document.commandDispatcher.focusedElement ==
gBrowserInit._initiallyFocusedElement
) {
gURLBar.select();
}
gBrowserInit._selectUrlbarOnLoad = false;
gBrowserInit._initiallyFocusedElement = null;
}
BookmarkingUI.onLocationChange();
// If we've actually changed document, update the toolbar visibility.
if (!isSameDocument) {

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

@ -38,7 +38,7 @@
data-lazy-l10n-id="bookmark-selected-tabs"
oncommand="PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.uniqueSelectedPages);"/>
<menuitem id="context_bookmarkTab"
data-lazy-l10n-id="bookmark-tab"
data-lazy-l10n-id="tab-context-bookmark-tab"
oncommand="PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.getUniquePages([TabContextMenu.contextTab]));"/>
<menu id="context_moveTabOptions"
data-lazy-l10n-id="tab-context-move-tabs"
@ -555,7 +555,7 @@
<menuitem data-lazy-l10n-id="synced-tabs-context-open-in-private-window"
id="syncedTabsOpenSelectedInPrivateWindow" where="window" private="true"/>
<menuseparator/>
<menuitem data-lazy-l10n-id="synced-tabs-context-bookmark-tab"
<menuitem data-lazy-l10n-id="synced-tabs-context-bookmark"
id="syncedTabsBookmarkSelected"/>
<menuitem data-lazy-l10n-id="synced-tabs-context-copy"
id="syncedTabsCopySelected"/>

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

@ -752,7 +752,7 @@ class nsContextMenu {
}
initMiscItems() {
// Use "Bookmark This Link" if on a link.
// Use "Bookmark Link" if on a link.
let bookmarkPage = document.getElementById("context-bookmarkpage");
this.showItem(
bookmarkPage,

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

@ -6,7 +6,7 @@
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src resource: chrome:; img-src https://www.mozilla.org https://firefox-settings-attachments.cdn.mozilla.net blob:; object-src 'none'">
<meta http-equiv="Content-Security-Policy" content="default-src resource: chrome:; img-src https://www.mozilla.org https://firefox-settings-attachments.cdn.mozilla.net blob: chrome:; object-src 'none'">
<meta name="referrer" content="no-referrer">
<link rel="stylesheet" type="text/css" href="chrome://global/skin/in-content/common.css">
<link rel="localization" href="branding/brand.ftl">

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

@ -7167,7 +7167,7 @@ var TabContextMenu = {
closeTabsToTheEndItem.disabled &&
closeOtherTabsItem.disabled;
// Hide "Bookmark Tab" for multiselection.
// Hide "Bookmark Tab" for multiselection.
// Update its state if visible.
let bookmarkTab = document.getElementById("context_bookmarkTab");
bookmarkTab.hidden = multiselectionContext;

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

@ -11,3 +11,4 @@ skip-if =
[browser_selectpopup_colors.js]
skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
[browser_selectpopup_searchfocus.js]
[browser_selectpopup_width.js]

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

@ -0,0 +1,28 @@
const PAGE = `
<!doctype html>
<select style="width: 600px">
<option>ABC</option>
<option>DEFG</option>
</select>
`;
add_task(async function() {
const url = "data:text/html," + encodeURI(PAGE);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url,
},
async function(browser) {
let popupShownPromise = BrowserTestUtils.waitForSelectPopupShown(window);
await BrowserTestUtils.synthesizeMouseAtCenter("select", {}, browser);
let popup = await popupShownPromise;
let arrowSB = popup.shadowRoot.querySelector(".menupopup-arrowscrollbox");
is(
arrowSB.getBoundingClientRect().width,
600,
"Should be the right size"
);
}
);
});

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

@ -9,9 +9,9 @@ function finishTest() {
let l10n = document.l10n.getAttributes(elem);
ok(
[
"main-context-menu-bookmark-add",
"main-context-menu-bookmark-add-with-shortcut",
"main-context-menu-bookmark-add-mac",
"main-context-menu-bookmark-page",
"main-context-menu-bookmark-page-with-shortcut",
"main-context-menu-bookmark-page-mac",
].includes(l10n.id)
);

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

@ -103,9 +103,9 @@ add_task(async function bookmark() {
// Open the panel.
await promisePageActionPanelOpen(win);
// The bookmark button should read "Bookmark Current Tab" and not be starred.
// The bookmark button should read "Bookmark Current Tab" and not be starred.
let bookmarkButton = win.document.getElementById("pageAction-panel-bookmark");
Assert.equal(bookmarkButton.label, "Bookmark Current Tab");
Assert.equal(bookmarkButton.label, "Bookmark Current Tab");
Assert.ok(!bookmarkButton.hasAttribute("starred"));
// Click the button.
@ -122,8 +122,8 @@ add_task(async function bookmark() {
// Open the panel again.
await promisePageActionPanelOpen(win);
// The bookmark button should now read "Edit This Bookmark" and be starred.
Assert.equal(bookmarkButton.label, "Edit This Bookmark");
// The bookmark button should now read "Edit This Bookmark" and be starred.
Assert.equal(bookmarkButton.label, "Edit This Bookmark");
Assert.ok(bookmarkButton.hasAttribute("starred"));
Assert.equal(bookmarkButton.getAttribute("starred"), "true");
@ -149,8 +149,8 @@ add_task(async function bookmark() {
// Open the panel again.
await promisePageActionPanelOpen(win);
// The bookmark button should read "Bookmark Current Tab" and not be starred.
Assert.equal(bookmarkButton.label, "Bookmark Current Tab");
// The bookmark button should read "Bookmark Current Tab" and not be starred.
Assert.equal(bookmarkButton.label, "Bookmark Current Tab");
Assert.ok(!bookmarkButton.hasAttribute("starred"));
// Done.

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

@ -195,10 +195,13 @@ figure > img {
border-radius: 4px;
border: 1px solid var(--in-content-box-border-color);
color: var(--in-content-text-color);
display: flex;
flex: 1;
overflow: clip;
padding: .5em;
width: 100%;
display: flex;
align-items: center;
}
.colorway-intensity-radio {

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

@ -162,7 +162,7 @@ const MESSAGES = [
},
{
id: "FEATURE_CALLOUT_3",
parent_selector: "#colorways",
parent_selector: "#colorways.content-container",
content: {
position: "callout",
arrow_position: "end",
@ -380,15 +380,29 @@ function _observeRender(container) {
}
async function _loadConfig(messageId) {
let content = MESSAGES.find(m => m.id === messageId);
const screenId = lazy.featureTourProgress.screen;
if (content?.screens && screenId) {
// Remove screens the user has already seen
const screenIndex = content.screens.findIndex(s => s.id === screenId);
content.screens = content.screens.filter((s, i) => {
return i >= screenIndex;
// If the parent element a screen describes doesn't exist, remove screen
// and ensure last screen displays the final primary CTA
// (for example, when there are no active colorways in about:firefoxview)
// If a user has seen a screen, remove it
function _getRelevantScreens(screens, index) {
const finalCTA = screens[screens.length - 1].content.primary_button;
screens = screens.filter((s, i) => {
return i >= index && document.querySelector(s.parent_selector);
});
screens[screens.length - 1].content.primary_button = finalCTA;
return screens;
}
let content = MESSAGES.find(m => m.id === messageId);
if (!content?.screens) {
return;
}
const screenIndex = content.screens.findIndex(
s => s.id === lazy.featureTourProgress.screen
);
content.screens = _getRelevantScreens(content.screens, screenIndex);
CURRENT_SCREEN = content?.screens[0];
CONFIG = content;
}

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

@ -56,6 +56,11 @@ body {
--fxview-text-secondary-color: color-mix(in srgb, currentColor 80%, transparent);
--newtab-background-color-secondary: #FFF;
--colorways-no-collection-notice-bg-color: transparent;
/* ensure utility button hover states match those of the rest of the page */
--in-content-button-background-hover: var(--fxview-element-hover-color);
--in-content-button-background-active: var(--fxview-element-active-color);
display: flex;
justify-content: center;
padding-block: var(--header-spacing) var(--footer-spacing);
@ -299,6 +304,18 @@ body > main > aside {
display: none !important;
}
button.ghost-button,
button.ghost-button:not(.semi-transparent):enabled:is(:hover, :active) {
color: inherit;
}
@media (prefers-contrast) {
button.ghost-button:not(.semi-transparent):enabled:is(:hover, :active) {
background-color: ButtonText;
color: ButtonFace;
}
}
button.primary {
white-space: nowrap;
min-width: fit-content;
@ -311,7 +328,7 @@ button.close {
}
.card,
.synced-tab-li,
.synced-tab-a,
.synced-tab-li-placeholder,
.empty-container {
background-color: var(--newtab-background-color-secondary);
@ -369,6 +386,23 @@ button.close {
grid-area: desc;
}
.card-body {
display: flex;
flex-grow: 1;
align-content: space-between;
align-items: center;
gap: 8px;
}
@media only screen and (max-width: 45rem) {
.card-body {
flex-wrap: wrap;
}
}
.card-body > button.primary {
margin-inline-start: 0;
}
.setup-step {
padding: var(--card-padding);
margin: 0 0 8px;
@ -396,15 +430,11 @@ button.close {
margin-block: 0 8px;
}
.setup-step > .step-body {
display: flex;
align-content: space-between;
align-items: center;
.setup-step > .card-body {
margin-block: 8px;
padding-block: 8px;
}
.setup-step > .step-body > .step-content {
.setup-step > .card-body > .step-content {
flex: 1 1 auto;
}
@ -441,7 +471,6 @@ button.close {
.message-box {
display: flex;
align-content: space-between;
align-items: center;
margin-block: 8px;
gap: 8px;
@ -472,10 +501,22 @@ button.close {
font-size: inherit;
display: inline;
}
/* ensure we get the local color values as container doesnt change color with theme */
.confirmation-message-box > .icon-button {
/* ensure we get the local color value as container doesnt change color with theme */
color: inherit;
}
.confirmation-message-box > button.icon-button:enabled:is(:hover, :active) {
background-color: color-mix(in srgb, var(--success-background-color) 90%, currentColor);
}
@media (prefers-contrast) {
.confirmation-message-box > button.icon-button {
border-color: ButtonText;
}
.confirmation-message-box > button.icon-button:enabled:is(:hover, :active) {
background-color: ButtonText;
color: ButtonFace;
}
}
/* 117px is the total height of the collapsible-tabs-container; setting that size
for the second row stabilizes the layout so it doesn't shift when collapsibled */
@ -514,14 +555,12 @@ button.close {
cursor: pointer;
}
.closed-tab-li:hover,
.ghost-button:hover {
background-color: var(--fxview-element-hover-color) !important;
.closed-tab-li:hover {
background-color: var(--fxview-element-hover-color);
}
.ghost-button:active,
.closed-tab-li:active {
background-color: var(--fxview-element-active-color) !important;
background-color: var(--fxview-element-active-color);
}
.closed-tab-li-title {
@ -539,6 +578,16 @@ button.close {
text-align: end;
}
.synced-tab-a,
.synced-tab-a:hover,
.synced-tab-a:active,
.synced-tab-a:hover:active,
.synced-tab-a:visited {
color: inherit;
text-decoration: none;
height: 100%;
}
.closed-tab-li-url,
.closed-tab-li-time,
.synced-tab-li-device,
@ -550,13 +599,13 @@ button.close {
.closed-tab-li-title,
.closed-tab-li-url,
.synced-tab-li:not(:first-child) > .synced-tab-li-title,
.synced-tab-li:not(:first-child) > .synced-tab-a > .synced-tab-li-title,
.synced-tab-li-device {
overflow: hidden;
}
.closed-tab-li-title,
.synced-tab-li:not(:first-child) > .synced-tab-li-title,
.synced-tab-li:not(:first-child) > .synced-tab-a > .synced-tab-li-title,
.synced-tab-li-device {
text-overflow: ellipsis;
white-space: nowrap;
@ -580,7 +629,7 @@ button.close {
"first third";
}
.synced-tab-li,
.synced-tab-a,
.synced-tab-li-placeholder {
box-sizing: border-box;
border-radius: 4px;
@ -594,12 +643,11 @@ button.close {
"favicon device device time"
}
.synced-tab-li:hover {
.synced-tab-a:hover {
box-shadow: 0px 2px 14px var(--fxview-contrast-border);
cursor: pointer;
}
.synced-tab-li:not(:first-child) {
.synced-tab-a:not(:first-child) {
align-content: center;
}
@ -610,7 +658,7 @@ button.close {
"favicon domain domain domain"
"favicon device device device"
}
.synced-tab-li:not(:first-child) > .synced-tab-li-time {
.synced-tab-li:not(:first-child) > .synced-tab-a > .synced-tab-li-time {
display: none;
}
}
@ -648,8 +696,11 @@ button.close {
}
.synced-tab-li:first-child {
padding-top: 20px;
grid-area: first;
}
.synced-tab-li:first-child > .synced-tab-a {
padding-top: 20px;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto;
grid-template-areas:
@ -670,7 +721,7 @@ button.close {
.synced-tab-li-url,
.synced-tab-li-device,
.synced-tab-li-time,
.synced-tab-li:not(:first-child) > .synced-tab-li-title {
.synced-tab-li:not(:first-child) > .synced-tab-a > .synced-tab-li-title {
font-size: .85em;
}
@ -678,7 +729,7 @@ button.close {
text-decoration-line: underline;
}
.synced-tab-li-url {
.synced-tab-li:first-child > .synced-tab-a > .synced-tab-li-url {
align-self: end;
grid-area: domain;
}
@ -692,7 +743,7 @@ button.close {
font-weight: 500;
}
.synced-tab-li:first-child > .synced-tab-li-title {
.synced-tab-li:first-child > .synced-tab-a > .synced-tab-li-title {
color: inherit;
padding-top: 5px;
overflow: hidden;
@ -713,7 +764,7 @@ button.close {
align-self: end;
}
.synced-tab-li:first-child > .synced-tab-li-time {
.synced-tab-li:first-child > .synced-tab-a > .synced-tab-li-time {
align-self: center;
}

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

@ -45,7 +45,7 @@
</div>
<div name="sync-setup-view1" data-prefix="id:-view1" class="card card-no-hover zap-card setup-step" data-prefix="aria-labelledby:-view1-header">
<h2 data-prefix="id:-view1-header" data-l10n-id="firefoxview-tabpickup-step-signin-header" class="card-header"></h2>
<section class="step-body">
<section class="card-body">
<p class="step-content" data-l10n-id="firefoxview-tabpickup-step-signin-description"></p>
<button class="primary" data-action="view1-primary-action" data-l10n-id="firefoxview-tabpickup-step-signin-primarybutton"></button>
</section>
@ -59,7 +59,7 @@
</div>
<div name="sync-setup-view2" data-prefix="id:-view2" class="card card-no-hover zap-card setup-step" data-prefix="aria-labelledby:-view2-header">
<h2 data-prefix="id:-view2-header" data-l10n-id="firefoxview-tabpickup-adddevice-header" class="card-header"></h2>
<section class="step-body">
<section class="card-body">
<p class="step-content">
<span data-l10n-id="firefoxview-tabpickup-adddevice-description"></span>
<br/>
@ -77,7 +77,7 @@
</div>
<div name="sync-setup-view3" data-prefix="id:-view3" class="card card-no-hover zap-card setup-step" data-prefix="aria-labelledby:-view3-header">
<h2 data-prefix="id:-view3-header" data-l10n-id="firefoxview-tabpickup-synctabs-header" class="card-header"></h2>
<section class="step-body">
<section class="card-body">
<p class="step-content">
<span data-l10n-id="firefoxview-tabpickup-synctabs-description"></span>
<br/>
@ -128,11 +128,13 @@
<div id="sync-setup-placeholder" hidden></div>
<div id="synced-tabs-placeholder" hidden></div>
<div class="promo-box message-box zap-card card-no-hover card" hidden>
<div class="message-content">
<h2 data-l10n-id="firefoxview-mobile-promo-header" class="message-header"></h2>
<p class="message-description" data-l10n-id="firefoxview-mobile-promo-description"></p>
<div class="card-body">
<div class="message-content">
<h2 data-l10n-id="firefoxview-mobile-promo-header" class="message-header"></h2>
<p class="message-description" data-l10n-id="firefoxview-mobile-promo-description"></p>
</div>
<button class="primary" data-action="mobile-promo-primary-action" data-l10n-id="firefoxview-mobile-promo-primarybutton"></button>
</div>
<button class="primary" data-action="mobile-promo-primary-action" data-l10n-id="firefoxview-mobile-promo-primarybutton"></button>
<button data-action="mobile-promo-dismiss" class="close icon-button ghost-button" data-l10n-id="firefoxview-close-button"></button>
</div>

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

@ -74,7 +74,17 @@ class TabPickupList extends HTMLElement {
event.type == "click" ||
(event.type == "keydown" && event.keyCode == KeyEvent.DOM_VK_RETURN)
) {
this.openTab(event);
const item = event.target.closest(".synced-tab-li");
let index = [...this.tabsList.children].indexOf(item);
Services.telemetry.recordEvent(
"firefoxview",
"tab_pickup",
"tabs",
null,
{
position: (++index).toString(),
}
);
}
}
@ -92,17 +102,6 @@ class TabPickupList extends HTMLElement {
);
}
}
openTab(event) {
event.preventDefault();
const item = event.target.closest(".synced-tab-li");
window.open(item.dataset.targetURI, "_blank");
let index = [...this.tabsList.children].indexOf(item);
Services.telemetry.recordEvent("firefoxview", "tab_pickup", "tabs", null, {
position: (++index).toString(),
});
}
togglePlaceholderVisibility(visible) {
this.placeholderContainer.toggleAttribute("hidden", !visible);
@ -174,20 +173,21 @@ class TabPickupList extends HTMLElement {
generateListItem(tab, index) {
const li = document.createElement("li");
li.classList.add("synced-tab-li");
li.setAttribute("tabindex", 0);
li.setAttribute("role", "button");
const targetURI = tab.url;
const a = document.createElement("a");
a.classList.add("synced-tab-a");
a.href = targetURI;
a.target = "_blank";
document.l10n.setAttributes(a, "firefoxview-tabs-list-tab-button", {
targetURI,
});
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 lastUsedMs = tab.lastUsed * 1000;
const time = document.createElement("span");
@ -214,11 +214,12 @@ class TabPickupList extends HTMLElement {
// the first list item is different from the second and third
if (index == 0) {
const badge = this.createBadge();
li.append(favicon, badge, title, url, device, time);
a.append(favicon, badge, title, url, device, time);
} else {
li.append(favicon, title, url, device, time);
a.append(favicon, title, url, device, time);
}
li.append(a);
return li;
}

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

@ -4,8 +4,9 @@ const { calloutMessages } = ChromeUtils.importESModule(
"chrome://browser/content/featureCallout.mjs"
);
const calloutSelector = "#root.featureCallout";
const featureTourPref = "browser.firefox-view.feature-tour";
const calloutSelector = "#root.featureCallout";
const primaryButtonSelector = "#root .primary";
const waitForCalloutRender = async doc => {
// Wait for callout to be rendered
@ -45,7 +46,7 @@ const waitForCalloutRemoved = async doc => {
};
const clickPrimaryButton = doc => {
doc.querySelector(".action-buttons .primary").click();
doc.querySelector(primaryButtonSelector).click();
};
add_task(async function feature_callout_renders_in_firefox_view() {
@ -180,7 +181,7 @@ add_task(
await waitForCalloutRender(document);
await waitForCalloutPositioned(document);
// Advance to second screen
clickPrimaryButton(document);
await clickPrimaryButton(document);
await waitForCalloutScreen(document, ".FEATURE_CALLOUT_2");
let parentTop = document
@ -215,9 +216,9 @@ add_task(
await waitForCalloutRender(document);
await waitForCalloutPositioned(document);
// Advance to third screen
clickPrimaryButton(document);
await clickPrimaryButton(document);
await waitForCalloutScreen(document, ".FEATURE_CALLOUT_2");
clickPrimaryButton(document);
await clickPrimaryButton(document);
await waitForCalloutScreen(document, ".FEATURE_CALLOUT_3");
let parentLeft = document
@ -256,9 +257,9 @@ add_task(
await waitForCalloutRender(document);
await waitForCalloutPositioned(document);
// Advance to third screen
clickPrimaryButton(document);
await clickPrimaryButton(document);
await waitForCalloutScreen(document, ".FEATURE_CALLOUT_2");
clickPrimaryButton(document);
await clickPrimaryButton(document);
await waitForCalloutScreen(document, ".FEATURE_CALLOUT_3");
let parentLeft = document
@ -313,7 +314,7 @@ add_task(async function feature_callout_syncs_across_visits_and_tabs() {
"Second tab's Feature Callout shows the tour screen saved in the user pref"
);
tab2Doc.querySelector(".action-buttons .primary").click();
await clickPrimaryButton(tab2Doc);
await waitForCalloutScreen(tab1Doc, ".FEATURE_CALLOUT_3");
@ -322,7 +323,7 @@ add_task(async function feature_callout_syncs_across_visits_and_tabs() {
"First tab's Feature Callout advances to the next screen when the tour is advanced in second tab"
);
tab1Doc.querySelector(".action-buttons .primary").click();
await clickPrimaryButton(tab1Doc);
await waitForCalloutRemoved(tab1Doc);
await waitForCalloutRemoved(tab2Doc);
@ -373,3 +374,44 @@ add_task(async function feature_callout_closes_on_dismiss() {
}
);
});
add_task(async function feature_callout_only_highlights_existing_elements() {
// Third comma-separated value of the pref is set to a string value once a user completes the tour
await SpecialPowers.pushPrefEnv({
set: [
[
"browser.firefox-view.feature-tour",
'{"message":"FIREFOX_VIEW_FEATURE_TOUR","screen":"FEATURE_CALLOUT_1","complete":false}',
],
],
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
await waitForCalloutRender(document);
await waitForCalloutPositioned(document);
// Remove parent element for third screen in tour
document.querySelector("#colorways").remove();
// Advance to second screen
await clickPrimaryButton(document);
await waitForCalloutScreen(document, ".FEATURE_CALLOUT_2");
// This test should be updated when landing localized strings
ok(
document.querySelector(primaryButtonSelector).innerText === "Finish",
"When parent element for third screen isn't present, second screen has CTA to finish tour"
);
// Click to finish tour
await clickPrimaryButton(document);
ok(
!document.querySelector(calloutSelector),
"Feature Callout screen does not render if its parent element does not exist"
);
}
);
});

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

@ -350,7 +350,7 @@ add_task(async function test_time_updates_correctly() {
"synced-tab-li-time text has updated"
);
document.querySelector("ol.synced-tabs-list > li").click();
document.querySelector(".synced-tab-a").click();
await TestUtils.waitForCondition(
() => {

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

@ -658,22 +658,33 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
this.mainContentHeader.focus();
}
getLogoStyle({
imageURL,
height
}) {
let style = {
height
};
style.backgroundImage = imageURL ? `url(${imageURL})` : null;
return style;
}
getScreenClassName(isFirstCenteredScreen, isLastCenteredScreen, includeNoodles) {
const screenClass = `screen-${this.props.order % 2 !== 0 ? 1 : 2}`;
return `${isFirstCenteredScreen ? `dialog-initial` : ``} ${isLastCenteredScreen ? `dialog-last` : ``} ${includeNoodles ? `with-noodles` : ``} ${screenClass}`;
}
renderLogo({
imageURL = "chrome://branding/content/about-logo.svg",
alt = "",
darkModeImageURL,
height
}) {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("picture", {
className: "logo-container"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", {
srcSet: darkModeImageURL,
media: "(prefers-color-scheme: dark)"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
className: "brand-logo",
style: {
height
},
src: imageURL,
alt: alt,
role: alt ? null : "presentation"
}));
}
renderContentTiles() {
const {
content
@ -788,10 +799,7 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
style: content.background && isCenterPosition ? {
background: content.background
} : {}
}, content.dismiss_button ? this.renderDismissButton() : null, content.logo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: `brand-logo`,
style: this.getLogoStyle(content.logo)
}) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, content.dismiss_button ? this.renderDismissButton() : null, content.logo ? this.renderLogo(content.logo) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: `${isRtamo ? "rtamo-icon" : "hide-rtamo-icon"}`
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
className: `${isTheme ? "rtamo-theme-icon" : ""}`,

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

@ -346,13 +346,15 @@ body[lwt-newtab-brighttext] {
text-decoration: none;
background-color: var(--in-content-button-background-hover) !important;
}
.onboardingContainer .screen[pos=split] .section-main .main-content .logo-container {
text-align: start;
}
.onboardingContainer .screen[pos=split] .section-main .main-content .logo-container:dir(rtl) {
text-align: end;
}
.onboardingContainer .screen[pos=split] .section-main .main-content .brand-logo {
height: 25px;
margin-block: 0;
background-position: left;
}
.onboardingContainer .screen[pos=split] .section-main .main-content .brand-logo:dir(rtl) {
background-position: right;
}
.onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text {
margin-inline: 0 10px;
@ -526,10 +528,6 @@ body[lwt-newtab-brighttext] {
.onboardingContainer .brand-logo {
margin-block: 60px 10px;
transition: var(--transition);
background-repeat: no-repeat;
background-position: center;
background-size: contain;
background-image: url("chrome://branding/content/about-logo.svg");
height: 80px;
}
.onboardingContainer .brand-logo.cta-top {

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

@ -7,7 +7,7 @@
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; media-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src resource: chrome:;">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; media-src resource: chrome:; connect-src https:; img-src https: data: blob: chrome:; style-src resource: chrome:;">
<title data-l10n-id="onboarding-welcome-header"></title>
<link rel="icon" type="image/png" href="chrome://branding/content/icon32.png">
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">

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

@ -313,14 +313,17 @@ body {
}
}
.logo-container {
text-align: start;
&:dir(rtl) {
text-align: end;
}
}
.brand-logo {
height: 25px;
margin-block: 0;
background-position: left;
&:dir(rtl) {
background-position: right;
}
}
.welcome-text {
@ -539,10 +542,6 @@ body {
.brand-logo {
margin-block: 60px 10px;
transition: var(--transition);
background-repeat: no-repeat;
background-position: center;
background-size: contain;
background-image: url('chrome://branding/content/about-logo.svg');
height: 80px;
&.cta-top {

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

@ -57,12 +57,6 @@ export class ProtonScreen extends React.PureComponent {
this.mainContentHeader.focus();
}
getLogoStyle({ imageURL, height }) {
let style = { height };
style.backgroundImage = imageURL ? `url(${imageURL})` : null;
return style;
}
getScreenClassName(
isFirstCenteredScreen,
isLastCenteredScreen,
@ -74,6 +68,29 @@ export class ProtonScreen extends React.PureComponent {
} ${includeNoodles ? `with-noodles` : ``} ${screenClass}`;
}
renderLogo({
imageURL = "chrome://branding/content/about-logo.svg",
alt = "",
darkModeImageURL,
height,
}) {
return (
<picture className="logo-container">
<source
srcSet={darkModeImageURL}
media="(prefers-color-scheme: dark)"
/>
<img
className="brand-logo"
style={{ height }}
src={imageURL}
alt={alt}
role={alt ? null : "presentation"}
/>
</picture>
);
}
renderContentTiles() {
const { content } = this.props;
return (
@ -228,12 +245,9 @@ export class ProtonScreen extends React.PureComponent {
}
>
{content.dismiss_button ? this.renderDismissButton() : null}
{content.logo ? (
<div
className={`brand-logo`}
style={this.getLogoStyle(content.logo)}
/>
) : null}
{content.logo ? this.renderLogo(content.logo) : null}
<div className={`${isRtamo ? "rtamo-icon" : "hide-rtamo-icon"}`}>
<img
className={`${isTheme ? "rtamo-theme-icon" : ""}`}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

До

Ширина:  |  Высота:  |  Размер: 21 KiB

После

Ширина:  |  Высота:  |  Размер: 21 KiB

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

До

Ширина:  |  Высота:  |  Размер: 21 KiB

После

Ширина:  |  Высота:  |  Размер: 21 KiB

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

@ -416,7 +416,11 @@ const MESSAGES = () => [
title: {
string_id: "mr1-onboarding-pin-header",
},
logo: {},
logo: {
darkModeImageURL:
"https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/a3c640c8-7594-4bb2-bc18-8b4744f3aaf2.gif",
alt: "sample alt text",
},
hero_text: {
string_id: "mr1-welcome-screen-hero-text",
},

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

@ -69,87 +69,66 @@ add_task(async function test_aboutwelcome_with_noodles() {
*/
add_task(async function test_aboutwelcome_with_customized_logo() {
const TEST_LOGO_URL = "chrome://branding/content/icon64.png";
const TEST_LOGO_HEIGHT = "50px";
const TEST_LOGO_CONTENT = makeTestContent("TEST_LOGO_STEP", {
logo: {
height: "50px",
height: TEST_LOGO_HEIGHT,
imageURL: TEST_LOGO_URL,
},
});
const TEST_LOGO_JSON = JSON.stringify([TEST_LOGO_CONTENT]);
const LOGO_HEIGHT = TEST_LOGO_CONTENT.content.logo.height;
let browser = await openAboutWelcome(TEST_LOGO_JSON);
await test_screen_content(
browser,
"renders screen with customized logo",
// Expected selectors:
["main.TEST_LOGO_STEP[pos='center']", `div.brand-logo`]
["main.TEST_LOGO_STEP[pos='center']", `.brand-logo[src="${TEST_LOGO_URL}"]`]
);
// Ensure logo has custom height
await test_element_styles(
browser,
"div.brand-logo",
".brand-logo",
// Expected styles:
{
height: LOGO_HEIGHT,
"background-image": `url("${TEST_LOGO_URL}")`,
// Override default text-link styles
height: TEST_LOGO_HEIGHT,
}
);
});
/**
* Test rendering a screen with a URL value and default color for backdrop
* Test rendering a screen with empty logo used for padding
*/
add_task(async function test_aboutwelcome_with_url_backdrop() {
const TEST_BACKDROP_URL = `url("chrome://activity-stream/content/data/content/assets/proton-bkg.avif")`;
const TEST_BACKDROP_VALUE = `#212121 ${TEST_BACKDROP_URL} center/cover no-repeat fixed`;
const TEST_URL_BACKDROP_CONTENT = makeTestContent("TEST_URL_BACKDROP_STEP");
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "aboutwelcome",
value: {
enabled: true,
backdrop: TEST_BACKDROP_VALUE,
screens: [TEST_URL_BACKDROP_CONTENT],
add_task(async function test_aboutwelcome_with_empty_logo_spacing() {
const TEST_LOGO_HEIGHT = "50px";
const TEST_LOGO_CONTENT = makeTestContent("TEST_LOGO_STEP", {
logo: {
height: TEST_LOGO_HEIGHT,
imageURL: "none",
},
});
let browser = await openAboutWelcome();
const TEST_LOGO_JSON = JSON.stringify([TEST_LOGO_CONTENT]);
let browser = await openAboutWelcome(TEST_LOGO_JSON);
await test_screen_content(
browser,
"renders screen with background image",
"renders screen with empty logo element",
// Expected selectors:
[`div.outer-wrapper.onboardingContainer[style*='${TEST_BACKDROP_URL}']`]
);
await doExperimentCleanup();
});
/**
* Test rendering a screen with a color name for backdrop
*/
add_task(async function test_aboutwelcome_with_color_backdrop() {
const TEST_BACKDROP_COLOR = "transparent";
const TEST_BACKDROP_COLOR_CONTENT = makeTestContent(
"TEST_COLOR_NAME_BACKDROP_STEP"
["main.TEST_LOGO_STEP[pos='center']", ".brand-logo[src='none']"]
);
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "aboutwelcome",
value: {
enabled: true,
backdrop: TEST_BACKDROP_COLOR,
screens: [TEST_BACKDROP_COLOR_CONTENT],
},
});
let browser = await openAboutWelcome();
await test_screen_content(
// Ensure logo has custom height
await test_element_styles(
browser,
"renders screen with background color",
// Expected selectors:
[`div.outer-wrapper.onboardingContainer[style*='${TEST_BACKDROP_COLOR}']`]
".brand-logo",
// Expected styles:
{
// Override default text-link styles
height: TEST_LOGO_HEIGHT,
}
);
await doExperimentCleanup();
});
/**
@ -210,6 +189,134 @@ add_task(async function test_aboutwelcome_with_background() {
);
});
/**
* Test rendering a screen with a dismiss button
*/
add_task(async function test_aboutwelcome_dismiss_button() {
const TEST_DISMISS_CONTENT = makeTestContent("TEST_DISMISS_STEP", {
dismiss_button: {
action: {
navigate: true,
},
},
});
const TEST_DISMISS_JSON = JSON.stringify([TEST_DISMISS_CONTENT]);
let browser = await openAboutWelcome(TEST_DISMISS_JSON);
let aboutWelcomeActor = await getAboutWelcomeParent(browser);
let sandbox = sinon.createSandbox();
// Spy AboutWelcomeParent Content Message Handler
sandbox.spy(aboutWelcomeActor, "onContentMessage");
registerCleanupFunction(() => {
sandbox.restore();
});
// Click dismiss button
await onButtonClick(browser, "button.dismiss-button");
const { callCount } = aboutWelcomeActor.onContentMessage;
ok(callCount >= 1, `${callCount} Stub was called`);
});
/**
* Test rendering a screen with the "split" position
*/
add_task(async function test_aboutwelcome_split_position() {
const TEST_SPLIT_STEP = makeTestContent("TEST_SPLIT_STEP", {
position: "split",
hero_text: "hero test",
});
const TEST_SPLIT_JSON = JSON.stringify([TEST_SPLIT_STEP]);
let browser = await openAboutWelcome(TEST_SPLIT_JSON);
await test_screen_content(
browser,
"renders screen secondary section containing hero text",
// Expected selectors:
[`main.screen[pos="split"]`, `.section-secondary`, `.message-text h1`]
);
// Ensure secondary section has split template styling
await test_element_styles(
browser,
"main.screen .section-secondary",
// Expected styles:
{
display: "flex",
margin: "auto 0px auto auto",
}
);
// Ensure secondary action has button styling
await test_element_styles(
browser,
".action-buttons .secondary-cta .secondary",
// Expected styles:
{
// Override default text-link styles
"background-color": "rgba(207, 207, 216, 0.33)",
color: "rgb(21, 20, 26)",
}
);
});
/**
* Test rendering a screen with a URL value and default color for backdrop
*/
add_task(async function test_aboutwelcome_with_url_backdrop() {
const TEST_BACKDROP_URL = `url("chrome://activity-stream/content/data/content/assets/proton-bkg.avif")`;
const TEST_BACKDROP_VALUE = `#212121 ${TEST_BACKDROP_URL} center/cover no-repeat fixed`;
const TEST_URL_BACKDROP_CONTENT = makeTestContent("TEST_URL_BACKDROP_STEP");
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "aboutwelcome",
value: {
enabled: true,
backdrop: TEST_BACKDROP_VALUE,
screens: [TEST_URL_BACKDROP_CONTENT],
},
});
let browser = await openAboutWelcome();
await test_screen_content(
browser,
"renders screen with background image",
// Expected selectors:
[`div.outer-wrapper.onboardingContainer[style*='${TEST_BACKDROP_URL}']`]
);
await doExperimentCleanup();
});
/**
* Test rendering a screen with a color name for backdrop
*/
add_task(async function test_aboutwelcome_with_color_backdrop() {
const TEST_BACKDROP_COLOR = "transparent";
const TEST_BACKDROP_COLOR_CONTENT = makeTestContent(
"TEST_COLOR_NAME_BACKDROP_STEP"
);
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "aboutwelcome",
value: {
enabled: true,
backdrop: TEST_BACKDROP_COLOR,
screens: [TEST_BACKDROP_COLOR_CONTENT],
},
});
let browser = await openAboutWelcome();
await test_screen_content(
browser,
"renders screen with background color",
// Expected selectors:
[`div.outer-wrapper.onboardingContainer[style*='${TEST_BACKDROP_COLOR}']`]
);
await doExperimentCleanup();
});
/**
* Test rendering a screen with a text color override
*/
@ -348,79 +455,6 @@ add_task(async function test_aboutwelcome_with_progress_bar() {
await doExperimentCleanup();
});
/**
* Test rendering a screen with a dismiss button
*/
add_task(async function test_aboutwelcome_dismiss_button() {
const TEST_DISMISS_CONTENT = makeTestContent("TEST_DISMISS_STEP", {
dismiss_button: {
action: {
navigate: true,
},
},
});
const TEST_DISMISS_JSON = JSON.stringify([TEST_DISMISS_CONTENT]);
let browser = await openAboutWelcome(TEST_DISMISS_JSON);
let aboutWelcomeActor = await getAboutWelcomeParent(browser);
let sandbox = sinon.createSandbox();
// Spy AboutWelcomeParent Content Message Handler
sandbox.spy(aboutWelcomeActor, "onContentMessage");
registerCleanupFunction(() => {
sandbox.restore();
});
// Click dismiss button
await onButtonClick(browser, "button.dismiss-button");
const { callCount } = aboutWelcomeActor.onContentMessage;
ok(callCount >= 1, `${callCount} Stub was called`);
});
/**
* Test rendering a screen with the "split" position
*/
add_task(async function test_aboutwelcome_split_position() {
const TEST_SPLIT_STEP = makeTestContent("TEST_SPLIT_STEP", {
position: "split",
hero_text: "hero test",
});
const TEST_SPLIT_JSON = JSON.stringify([TEST_SPLIT_STEP]);
let browser = await openAboutWelcome(TEST_SPLIT_JSON);
await test_screen_content(
browser,
"renders screen secondary section containing hero text",
// Expected selectors:
[`main.screen[pos="split"]`, `.section-secondary`, `.message-text h1`]
);
// Ensure secondary section has split template styling
await test_element_styles(
browser,
"main.screen .section-secondary",
// Expected styles:
{
display: "flex",
margin: "auto 0px auto auto",
}
);
// Ensure secondary action has button styling
await test_element_styles(
browser,
".action-buttons .secondary-cta .secondary",
// Expected styles:
{
// Override default text-link styles
"background-color": "rgb(43, 42, 51)",
color: "rgb(251, 251, 254)",
}
);
});
/**
* Test rendering a message with session history updates disabled
*/
@ -460,3 +494,48 @@ add_task(async function test_aboutwelcome_history_updates_disabled() {
await doExperimentCleanup();
});
/**
* Test rendering a screen with a dark mode logo
*/
add_task(async function test_aboutwelcome_with_dark_mode_logo() {
await SpecialPowers.pushPrefEnv({
set: [
// Override the system color scheme to dark
["ui.systemUsesDarkTheme", 1],
],
});
let screens = [];
// we need at least two screens to test the step indicator
for (let i = 0; i < 2; i++) {
screens.push(
makeTestContent("TEST_TEXT_COLOR_OVERRIDE_STEP", {
logo: {
darkModeImageURL:
"chrome://activity-stream/content/data/content/assets/heart.webp",
},
})
);
}
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "aboutwelcome",
value: {
enabled: true,
screens,
},
});
let browser = await openAboutWelcome(JSON.stringify(screens));
await test_screen_content(
browser,
"renders screen with dark mode logo",
// Expected selectors:
[
'.logo-container source[srcset="chrome://activity-stream/content/data/content/assets/heart.webp"]',
]
);
await doExperimentCleanup();
});

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

@ -326,7 +326,7 @@ add_task(async function test_multistage_aboutwelcome_experimentAPI() {
[
"div.onboardingContainer",
"main.AW_STEP3",
"div.brand-logo",
"img.brand-logo",
"div.welcome-text",
],
// Unexpected selectors:

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

@ -671,10 +671,10 @@ PlacesController.prototype = {
"placesContext_createBookmark"
);
createBookmarkItem.label = PlacesUIUtils.getString(
`cmd.bookmark${stringId}.label`
`cmd.bookmark${stringId}2.label`
);
createBookmarkItem.accessKey = PlacesUIUtils.getString(
`cmd.bookmark${stringId}.accesskey`
`cmd.bookmark${stringId}2.accesskey`
);
return usableItemCount > 0;

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

@ -77,6 +77,7 @@ support-files =
https_first_disabled = true
support-files =
authenticate.sjs
[browser_custom_new_window_url.js]
[browser_customizeMode.js]
[browser_cutting.js]
[browser_decode.js]

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

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This test ensures that when a custom new window URL is configured and a new
* window is opened, the URL bar is focused and the URL string is selected.
*
* See bug 1770818 for additional details.
*/
"use strict";
const CUSTOM_URL = "http://example.com/";
const DEFAULT_URL = HomePage.getOriginalDefault();
add_setup(async function() {
registerCleanupFunction(async () => {
await PlacesUtils.history.clear();
});
});
add_task(async function checkFocusAndSelectionWithCustomURL() {
function urlTextSelectedPredicateFunc() {
return (
newWin.gURLBar.selectionStart == 0 &&
newWin.gURLBar.selectionEnd == newWin.gURLBar.value.length &&
newWin.gURLBar.selectionStart != newWin.gURLBar.selectionEnd
);
}
await SpecialPowers.pushPrefEnv({
set: [
["browser.startup.homepage", CUSTOM_URL],
["browser.startup.page", 1],
],
});
let newWinPromise = BrowserTestUtils.waitForNewWindow({
url: CUSTOM_URL,
});
let newWin = await BrowserTestUtils.openNewBrowserWindow();
await newWinPromise;
let isURLTextSelected = await TestUtils.waitForCondition(
urlTextSelectedPredicateFunc,
"The text inside the URL bar should be selected"
);
Assert.ok(
newWin.gURLBar.focused,
"URL bar is focused upon opening a new window"
);
Assert.ok(
isURLTextSelected,
"URL bar text is selected upon opening a new window"
);
await BrowserTestUtils.closeWindow(newWin);
await SpecialPowers.popPrefEnv();
});
add_task(async function checkFocusWithDefaultURL() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.startup.homepage", DEFAULT_URL],
["browser.startup.page", 1],
],
});
let newWinPromise = BrowserTestUtils.waitForNewWindow({
url: DEFAULT_URL,
});
let newWin = await BrowserTestUtils.openNewBrowserWindow();
await newWinPromise;
Assert.ok(
newWin.gURLBar.focused,
"URL bar is focused upon opening a new window"
);
Assert.equal(newWin.gURLBar.value, "", "URL bar should be empty");
await BrowserTestUtils.closeWindow(newWin);
await SpecialPowers.popPrefEnv();
});

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

@ -10,6 +10,7 @@ support-files =
../../fixtures/autocomplete_creditcard_basic.html
../../fixtures/autocomplete_creditcard_iframe.html
../../fixtures/autocomplete_creditcard_cc_exp_field.html
../../fixtures/without_autocomplete_creditcard_basic.html
head_cc.js
[browser_creditCard_doorhanger.js]

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

@ -193,6 +193,59 @@ add_task(async function test_popup_opened() {
await SpecialPowers.popPrefEnv();
});
add_task(async function test_popup_opened_form_without_autocomplete() {
await SpecialPowers.pushPrefEnv({
set: [
[ENABLED_AUTOFILL_CREDITCARDS_PREF, true],
[AUTOFILL_CREDITCARDS_AVAILABLE_PREF, "on"],
["extensions.formautofill.creditCards.heuristics.testConfidence", "1"],
],
});
Services.telemetry.clearEvents();
Services.telemetry.clearScalars();
Services.telemetry.setEventRecordingEnabled("creditcard", true);
await setStorage(TEST_CREDIT_CARD_1);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: CREDITCARD_FORM_WITHOUT_AUTOCOMPLETE_URL },
async function(browser) {
const focusInput = "#cc-number";
await openPopupOn(browser, focusInput);
// Clean up
await closePopup(browser);
}
);
await removeAllRecords();
await assertTelemetry([
ccFormArgsv1("detected"),
ccFormArgsv2(
"detected",
buildccFormv2Extra({ cc_number: "1", cc_exp: "false" }, "true")
),
ccFormArgsv1("popup_shown"),
ccFormArgsv2("popup_shown", { field_name: "cc-number" }),
]);
TelemetryTestUtils.assertScalar(
TelemetryTestUtils.getProcessScalars("content"),
"formautofill.creditCards.detected_sections_count",
1,
"There should be 1 section detected."
);
TelemetryTestUtils.assertScalarUnset(
TelemetryTestUtils.getProcessScalars("content"),
"formautofill.creditCards.submitted_sections_count"
);
await SpecialPowers.popPrefEnv();
});
add_task(async function test_submit_creditCard_new() {
async function test_per_command(
command,

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

@ -36,6 +36,10 @@ const CREDITCARD_FORM_COMBINED_EXPIRY_URL =
"https://example.org" +
HTTP_TEST_PATH +
"creditCard/autocomplete_creditcard_cc_exp_field.html";
const CREDITCARD_FORM_WITHOUT_AUTOCOMPLETE_URL =
"https://example.org" +
HTTP_TEST_PATH +
"creditCard/without_autocomplete_creditcard_basic.html";
const FTU_PREF = "extensions.formautofill.firstTimeUse";
const CREDITCARDS_USED_STATUS_PREF = "extensions.formautofill.creditCards.used";

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

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Form Autofill Credit Card Demo Page</title>
</head>
<body>
<h1>Form Autofill Credit Card Demo Page (without Autocomplete attribute)</h1>
<form id="form">
<p><label>Name: <input id="cc-name" placeholder="Credit Card Name"></label></p>
<p><label>Card Number: <input id="cc-number" placeholder="Credit Card Number"></label></p>
<p><label>Expiration month: <input id="cc-exp-month" placeholder="Credit Card Expiration Month"></label></p>
<p><label>Expiration year: <input id="cc-exp-year" placeholder="Credit Card Expiration Year"></label></p>
<p><label>CSC: <input id="cc-csc" placeholder="Credit Card Security Code"></label></p>
<p><label>Card Type: <select id="cc-type" placeholder="Credit Card Type">
<option></option>
<option value="discover">Discover</option>
<option value="jcb">JCB</option>
<option value="visa">Visa</option>
<option value="mastercard">MasterCard</option>
<option value="gringotts">Unknown card network</option>
</select></label></p>
<p>
<input type="submit" value="Submit">
<button type="reset">Reset</button>
</p>
</form>
</body>
</html>

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

@ -194,6 +194,7 @@ function verifySectionFieldDetails(sections, expectedResults) {
let expectedField = expectedSectionInfo[fieldIndex];
delete field._reason;
delete field.elementWeakRef;
delete field.confidence;
Assert.deepEqual(field, expectedField);
});
});

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

@ -30,6 +30,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -45,6 +46,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -59,6 +61,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -70,6 +73,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -81,6 +85,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -96,6 +101,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -108,6 +114,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -120,6 +127,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -133,6 +141,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -146,6 +155,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -159,6 +169,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -170,6 +181,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -187,6 +199,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -198,6 +211,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -219,6 +233,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -232,6 +247,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -246,6 +262,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: 1,
},
},
{
@ -260,6 +277,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -273,6 +291,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -286,6 +305,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -299,6 +319,7 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
{
@ -312,10 +333,24 @@ const TESTCASES = [
section: "",
addressType: "",
contactType: "",
confidence: null,
},
},
];
add_setup(async function() {
Services.prefs.setStringPref(
"extensions.formautofill.creditCards.heuristics.testConfidence",
"1"
);
registerCleanupFunction(() => {
Services.prefs.clearUserPref(
"extensions.formautofill.creditCards.heuristics.testConfidence"
);
});
});
TESTCASES.forEach(testcase => {
add_task(async function() {
info("Starting testcase: " + testcase.description);
@ -371,6 +406,7 @@ add_task(async function test_regexp_list() {
section: "",
addressType: "",
contactType: "",
confidence: null,
}
: null,
};
@ -423,6 +459,7 @@ add_task(async function test_autofill_creditCards_autocomplete_off_pref() {
section: "",
addressType: "",
contactType: "",
confidence: 1,
};
info(
`Set pref so that credit card autofill does not respect autocomplete="off"`
@ -476,6 +513,7 @@ add_task(async function test_autofill_addresses_autocomplete_off_pref() {
section: "",
addressType: "",
contactType: "",
confidence: null,
};
info(`Set pref so that address autofill does not respect autocomplete="off"`);
Services.prefs.setBoolPref(

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

@ -612,6 +612,30 @@ const AVAILABLE_INJECTIONS = [
allFrames: true,
},
},
{
id: "bug1784309",
platform: "all",
domain: "bet365.com",
bug: "1784309",
contentScripts: {
matches: [
"*://*.bet365.com/*",
"*://*.bet365.gr/*",
"*://*.bet365.com.au/*",
"*://*.bet365.de/*",
"*://*.bet365.es/*",
"*://*.bet365.ca/*",
"*://*.bet365.dk/*",
"*://*.bet365.mx/*",
"*://*.bet365.bet.ar/*",
],
js: [
{
file: "injections/js/bug1784309-bet365.com-math-pow.js",
},
],
},
},
];
module.exports = AVAILABLE_INJECTIONS;

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

@ -0,0 +1,26 @@
"use strict";
/**
* Bug 1784309 - Shim Math.pow if Math.PI and exponent "-100" are passed on bet365.com
*
* The site is expecting Math.pow to return a certain value if these parameters are passed,
* otherwise some content parts are missing. Shimming the return value fixes the issue.
*/
/* globals exportFunction */
console.info(
"Math.pow with arguments of `Math.PI` and exponent `-100` has been overridden for compatibility reasons. See https://bugzilla.mozilla.org/show_bug.cgi?id=1784111 for details."
);
const originalMath = Math;
const originalPow = Math.pow;
Object.defineProperty(window.Math.wrappedJSObject, "pow", {
value: exportFunction(function(base, exponent) {
if (exponent === -100 && base === originalMath.PI) {
return Number("1.9275814160560185e-50");
}
return originalPow.call(originalMath, base, exponent);
}, window),
});

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

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

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

@ -84,6 +84,7 @@ FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["injections"]["js"] += [
"injections/js/bug1772949-youtube-webshare-shim.js",
"injections/js/bug1774005-installtrigger-shim.js",
"injections/js/bug1778239-m.pji.co.kr-banner-hide.js",
"injections/js/bug1784309-bet365.com-math-pow.js",
]
FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["shims"] += [

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

@ -701,8 +701,8 @@ bookmarks-search =
.label = Search bookmarks
bookmarks-tools =
.label = Bookmarking Tools
bookmarks-bookmark-edit-panel =
.label = Edit this bookmark
bookmarks-subview-edit-bookmark =
.label = Edit this bookmark
# The aria-label is a spoken label that should not include the word "toolbar" or
# such, because screen readers already know that this container is a toolbar.
@ -719,8 +719,8 @@ bookmarks-toolbar-placeholder-button =
.label = Bookmarks toolbar items
# "Bookmark" is a verb, as in "Add current tab to bookmarks".
bookmarks-current-tab =
.label = Bookmark current tab
bookmarks-subview-bookmark-tab =
.label = Bookmark current tab
## Library Panel items

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

@ -95,43 +95,43 @@ main-context-menu-page-save =
## Simple menu items
main-context-menu-bookmark-add =
.aria-label = Bookmark This Page
main-context-menu-bookmark-page =
.aria-label = Bookmark Page
.accesskey = m
.tooltiptext = Bookmark this page
.tooltiptext = Bookmark page
# This menuitem is only visible on macOS
# Cannot be shown at the same time as main-context-menu-bookmark-edit-mac,
# Cannot be shown at the same time as main-context-menu-edit-bookmark-mac,
# so should probably have the same access key if possible.
main-context-menu-bookmark-add-mac =
.label = Bookmark Page
main-context-menu-bookmark-page-mac =
.label = Bookmark Page
.accesskey = m
# This menuitem is only visible on macOS
# Cannot be shown at the same time as main-context-menu-bookmark-add-mac,
# Cannot be shown at the same time as main-context-menu-bookmark-page-mac,
# so should probably have the same access key if possible.
main-context-menu-bookmark-edit-mac =
.label = Edit Bookmark
main-context-menu-edit-bookmark-mac =
.label = Edit Bookmark
.accesskey = m
# Variables
# $shortcut (String) - A keyboard shortcut for the add bookmark command.
main-context-menu-bookmark-add-with-shortcut =
.aria-label = Bookmark This Page
main-context-menu-bookmark-page-with-shortcut =
.aria-label = Bookmark Page
.accesskey = m
.tooltiptext = Bookmark this page ({ $shortcut })
.tooltiptext = Bookmark page ({ $shortcut })
main-context-menu-bookmark-change =
.aria-label = Edit This Bookmark
main-context-menu-edit-bookmark =
.aria-label = Edit Bookmark
.accesskey = m
.tooltiptext = Edit this bookmark
.tooltiptext = Edit bookmark
# Variables
# $shortcut (String) - A keyboard shortcut for the edit bookmark command.
main-context-menu-bookmark-change-with-shortcut =
.aria-label = Edit This Bookmark
main-context-menu-edit-bookmark-with-shortcut =
.aria-label = Edit Bookmark
.accesskey = m
.tooltiptext = Edit this bookmark ({ $shortcut })
.tooltiptext = Edit bookmark ({ $shortcut })
main-context-menu-open-link =
.label = Open Link
@ -153,8 +153,8 @@ main-context-menu-open-link-new-private-window =
.label = Open Link in New Private Window
.accesskey = P
main-context-menu-bookmark-link =
.label = Bookmark Link
main-context-menu-bookmark-link-2 =
.label = Bookmark Link
.accesskey = B
main-context-menu-save-link =
@ -393,8 +393,8 @@ main-context-menu-frame-reload =
.label = Reload Frame
.accesskey = R
main-context-menu-frame-bookmark =
.label = Bookmark This Frame
main-context-menu-frame-add-bookmark =
.label = Bookmark Frame
.accesskey = m
main-context-menu-frame-save-as =
@ -413,8 +413,8 @@ main-context-menu-frame-view-info =
.label = View Frame Info
.accesskey = I
main-context-menu-print-selection =
.label = Print Selection
main-context-menu-print-selection-2 =
.label = Print Selection
.accesskey = r
main-context-menu-view-selection-source =

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

@ -218,10 +218,10 @@ menu-bookmarks-menu =
.accesskey = B
menu-bookmarks-manage =
.label = Manage Bookmarks
menu-bookmark-current-tab =
.label = Bookmark Current Tab
menu-bookmark-edit =
.label = Edit This Bookmark
menu-bookmark-tab =
.label = Bookmark Current Tab
menu-edit-bookmark =
.label = Edit This Bookmark
menu-bookmarks-all-tabs =
.label = Bookmark All Tabs…
menu-bookmarks-toolbar =

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

@ -41,9 +41,9 @@ synced-tabs-context-open-in-private-window =
# and/or devices in the list. This string is for a menuitem equivalent to one in
# the tab context menu (activated by right-clicking a tab in the tabstrip). That
# string is located in tabContextMenu.ftl. So, this string should be translated
# consistently with the "Bookmark Tab" string there.
synced-tabs-context-bookmark-tab =
.label = Bookmark Tab
# consistently with the "Bookmark Tab" string there.
synced-tabs-context-bookmark =
.label = Bookmark Tab
.accesskey = B
synced-tabs-context-copy =
.label = Copy

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

@ -54,8 +54,8 @@ unpin-selected-tabs =
bookmark-selected-tabs =
.label = Bookmark Tabs…
.accesskey = B
bookmark-tab =
.label = Bookmark Tab
tab-context-bookmark-tab =
.label = Bookmark Tab
.accesskey = B
tab-context-open-in-new-container-tab =
.label = Open in New Container Tab

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

@ -65,7 +65,7 @@ cmd.deleteMultiplePages.accesskey=D
# LOCALIZATION NOTE (cmd.bookmarkSinglePage.accesskey,
# cmd.bookmarkMultiplePages.accesskey): these accesskeys can use the same
# character, since they're never displayed at the same time
cmd.bookmarkSinglePage.label=Bookmark Page
cmd.bookmarkSinglePage.accesskey=B
cmd.bookmarkMultiplePages.label=Bookmark Pages
cmd.bookmarkMultiplePages.accesskey=B
cmd.bookmarkSinglePage2.label=Bookmark Page
cmd.bookmarkSinglePage2.accesskey=B
cmd.bookmarkMultiplePages2.label=Bookmark Pages
cmd.bookmarkMultiplePages2.accesskey=B

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

@ -58,7 +58,7 @@ function getHomepagePref(useDefault) {
}
/**
* HomePage provides tools to keep try of the current homepage, and the
* HomePage provides tools to keep track of the current homepage, and the
* applications's default homepage. It includes tools to insure that certain
* urls are ignored. As a result, all set/get requests for the homepage
* preferences should be routed through here.

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

@ -667,7 +667,7 @@ description#identity-popup-content-verifier,
/* PERMISSIONS */
@supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
@media (-moz-box-flexbox-emulation) {
/* The extra padding-bottom is there to work around XUL flex (Bug 1368281).
This rule and the 1.5em above can both be removed once we are only using CSS flex. */
#permission-popup-permissions-content {

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

@ -75,7 +75,7 @@
padding-inline: var(--tab-shadow-max-size);
}
@supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
@media (-moz-box-flexbox-emulation) {
.tab-stack {
/* Needed to allow tabs to shrink to be skinnier than their page-title: */
min-width: 0;

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

@ -12,10 +12,10 @@ For firefox's purposes, these patches still work fine, and are necessary
to make -Zbuild-std work in a vendored environment.
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 6181a611ec3..8d99c148dcf 100644
index 6291b204e48..d4c90d29824 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -824,6 +824,30 @@ fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
@@ -837,6 +837,30 @@ fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
builder.copy(&builder.src.join(file), &dst_src.join(file));
}
@ -47,10 +47,10 @@ index 6181a611ec3..8d99c148dcf 100644
}
}
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index a4e35bf6d47..cd5e478bed0 100644
index f84de73297a..d584444dfd2 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -1418,6 +1418,27 @@ fn tempdir(&self) -> PathBuf {
@@ -1416,6 +1416,27 @@ fn tempdir(&self) -> PathBuf {
tmp
}
@ -77,4 +77,4 @@ index a4e35bf6d47..cd5e478bed0 100644
+
/// Copies a file from `src` to `dst`
pub fn copy(&self, src: &Path, dst: &Path) {
if self.config.dry_run {
self.copy_internal(src, dst, false);

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

@ -2,33 +2,16 @@
* 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/>. */
import { validateContext } from "../utils/context";
import { getContext } from "../selectors";
import { createThread } from "../client/firefox/create";
export function addTarget(targetFront) {
return async function(args) {
const { client, getState, dispatch } = args;
const cx = getContext(getState());
const thread = await client.addThread(targetFront);
validateContext(getState(), cx);
dispatch({ type: "INSERT_THREAD", cx, newThread: thread });
};
return { type: "INSERT_THREAD", newThread: createThread(targetFront) };
}
export function removeTarget(targetFront) {
return async function(args) {
const { getState, client, dispatch } = args;
const cx = getContext(getState());
const threadActorID = targetFront.targetForm.threadActor;
client.removeThread(threadActorID);
dispatch({
type: "REMOVE_THREAD",
cx,
threadActorID,
});
return {
type: "REMOVE_THREAD",
threadActorID: targetFront.targetForm.threadActor,
};
}

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

@ -2,12 +2,11 @@
* 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/>. */
import { createThread, createFrame } from "./create";
import { createFrame } from "./create";
import { makePendingLocationId } from "../../utils/breakpoint";
import Reps from "devtools/client/shared/components/reps/index";
let targets;
let commands;
let breakpoints;
@ -16,7 +15,6 @@ const CALL_STACK_PAGE_SIZE = 1000;
function setupCommands(innerCommands) {
commands = innerCommands;
targets = {};
breakpoints = {};
}
@ -61,22 +59,15 @@ function releaseActor(actor) {
}
}
// Get a copy of the current targets.
function getTargetsMap() {
return Object.assign({}, targets);
}
function lookupTarget(thread) {
if (thread == currentThreadFront().actor) {
return currentTarget();
}
const targetsMap = getTargetsMap();
if (!targetsMap[thread]) {
throw new Error(`Unknown thread front: ${thread}`);
}
return targetsMap[thread];
const targets = commands.targetCommand.getAllTargets(
commands.targetCommand.ALL_TYPES
);
return targets.find(target => target.targetForm.threadActor == thread);
}
function lookupThreadFront(thread) {
@ -85,8 +76,10 @@ function lookupThreadFront(thread) {
}
function listThreadFronts() {
const list = Object.values(getTargetsMap());
return list.map(target => target.threadFront).filter(t => !!t);
const targets = commands.targetCommand.getAllTargets(
commands.targetCommand.ALL_TYPES
);
return targets.map(target => target.threadFront).filter(front => !!front);
}
function forEachThread(iteratee) {
@ -96,7 +89,7 @@ function forEachThread(iteratee) {
// resolve in FIFO order, and this could result in client and server state
// going out of sync.
const promises = [currentThreadFront(), ...listThreadFronts()].map(
const promises = listThreadFronts().map(
// If a thread shuts down while sending the message then it will
// throw. Ignore these exceptions.
t => iteratee(t).catch(e => console.log(e))
@ -385,18 +378,6 @@ async function toggleEventLogging(logEventBreakpoints) {
});
}
async function addThread(targetFront) {
const threadActorID = targetFront.targetForm.threadActor;
if (!targets[threadActorID]) {
targets[threadActorID] = targetFront;
}
return createThread(threadActorID, targetFront);
}
function removeThread(threadActorID) {
delete targets[threadActorID];
}
function getMainThread() {
return currentThreadFront().actor;
}
@ -460,13 +441,10 @@ const clientCommands = {
getFrames,
pauseOnExceptions,
toggleEventLogging,
addThread,
removeThread,
getMainThread,
setSkipPausing,
setEventListenerBreakpoints,
getEventListenerBreakpointTypes,
lookupTarget,
getFrontByID,
fetchAncestorFramePositions,
toggleJavaScriptEnabled,

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

@ -317,15 +317,12 @@ export function createPrettyPrintOriginalSource(id, url, thread) {
export function createSourceActor(sourceResource, sourceObject) {
const actorId = sourceResource.actor;
// As sourceResource is only SourceActor's form and not the SourceFront,
// we have to go through the target to retrieve the related ThreadActor's ID.
const threadActorID = sourceResource.targetFront.getCachedFront("thread")
.actorID;
return {
id: actorId,
actor: actorId,
thread: threadActorID,
// As sourceResource is only SourceActor's form and not the SourceFront,
// we have to go through the target to retrieve the related ThreadActor's ID.
thread: sourceResource.targetFront.getCachedFront("thread").actorID,
// `source` is the reducer source ID
source: makeSourceId(sourceResource),
sourceObject,
@ -345,17 +342,19 @@ export async function createPause(thread, packet) {
};
}
export function createThread(actor, target) {
const name = target.isTopLevel ? L10N.getStr("mainThread") : target.name;
export function createThread(targetFront) {
const name = targetFront.isTopLevel
? L10N.getStr("mainThread")
: targetFront.name;
return {
actor,
url: target.url,
isTopLevel: target.isTopLevel,
targetType: target.targetType,
actor: targetFront.targetForm.threadActor,
url: targetFront.url,
isTopLevel: targetFront.isTopLevel,
targetType: targetFront.targetType,
name,
serviceWorkerStatus: target.debuggerServiceWorkerStatus,
isWebExtension: target.isWebExtension,
serviceWorkerStatus: targetFront.debuggerServiceWorkerStatus,
isWebExtension: targetFront.isWebExtension,
};
}

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

@ -19,7 +19,7 @@ add_task(async function testBreakableLinesOverReloads() {
);
info("Assert breakable lines of the first html page load");
await assertBreakableLines(dbg, "index.html", 68, [
await assertBreakableLines(dbg, "index.html", 73, [
[16, 17],
[21],
[23],

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

@ -30,7 +30,7 @@ add_task(async function testBreakableLinesOverReloads() {
);
info("Assert breakable lines of the first html page load");
await assertBreakablePositions(dbg, "index.html", 68, [
await assertBreakablePositions(dbg, "index.html", 73, [
{ line: 16, columns: [6, 14] },
{ line: 17, columns: [] },
{ line: 21, columns: [6, 14] },

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

@ -33,6 +33,11 @@
`);
</script>
<!-- Test JS file with encoded characters as well as custom protocol -->
<script>
//# sourceURL=foo://bar/%e6%96%87%e5%ad%97%e3%82%b3%e3%83%bc%e3%83%89.js
</script>
<!-- A simple script -->
<script src="script.js"></script>
<!-- A script whose URL is shared by many other sources -->

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

@ -33,6 +33,11 @@
`);
</script>
<!-- Test JS file with encoded characters as well as custom protocol -->
<script>
//# sourceURL=foo://bar/%e6%96%87%e5%ad%97%e3%82%b3%e3%83%bc%e3%83%89.js
</script>
<!-- A simple script -->
<script src="script.js"></script>
<!-- A script whose URL is shared by many other sources -->

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

@ -119,6 +119,8 @@ const INTEGRATION_TEST_PAGE_SOURCES = [
"replaced-bundle.js",
"removed-original.js",
"named-eval.js",
// This is the JS file with encoded characters and custom protocol
"文字コード.js",
// Webpack generated some extra sources:
"bootstrap 3b1a221408fdde86aa49",
"bootstrap a1ecee2f86e1d0ea3fb5",

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

@ -127,7 +127,6 @@ function setPrefDefaults() {
"devtools.command-button-noautohide.enabled",
true
);
Services.prefs.setBoolPref("layout.css.emulate-moz-box-with-flex", false);
// We force enabling the performance panel in the browser toolbox.
Services.prefs.setBoolPref("devtools.performance.enabled", true);

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

@ -489,24 +489,6 @@ HarBuilder.prototype = {
return cache;
},
getBlockingEndTime(file) {
if (file.resolveStarted && file.connectStarted) {
return file.resolvingTime;
}
if (file.connectStarted) {
return file.connectingTime;
}
if (file.sendStarted) {
return file.sendingTime;
}
return file.sendingTime > file.startTime
? file.sendingTime
: file.waitingForTime;
},
// RDP Helpers
fetchData(string) {

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

@ -4,6 +4,10 @@
# 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/.
DIRS += [
"session-data",
]
DevToolsModules(
"content-process.js",
"index.js",

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

@ -0,0 +1,19 @@
/* 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";
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
for (const { url, range } of entries) {
targetActor.sourcesManager.blackBox(url, range);
}
},
removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
for (const { url, range } of entries) {
targetActor.sourcesManager.unblackBox(url, range);
}
},
};

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

@ -0,0 +1,35 @@
/* 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 { STATES: THREAD_STATES } = require("devtools/server/actors/thread");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
const isTargetCreation =
targetActor.threadActor.state == THREAD_STATES.DETACHED;
if (isTargetCreation && !targetActor.targetType.endsWith("worker")) {
// If addSessionDataEntry is called during target creation, attach the
// thread actor automatically and pass the initial breakpoints.
// However, do not attach the thread actor for Workers. They use a codepath
// which releases the worker on `attach`. For them, the client will call `attach`. (bug 1691986)
await targetActor.threadActor.attach({ breakpoints: entries });
} else {
// If addSessionDataEntry is called for an existing target, set the new
// breakpoints on the already running thread actor.
await Promise.all(
entries.map(({ location, options }) =>
targetActor.threadActor.setBreakpoint(location, options)
)
);
}
},
removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
for (const { location } of entries) {
targetActor.threadActor.removeBreakpoint(location);
}
},
};

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

@ -0,0 +1,24 @@
/* 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 { STATES: THREAD_STATES } = require("devtools/server/actors/thread");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
// Same as comments for XHR breakpoints. See lines 117-118
if (
targetActor.threadActor.state == THREAD_STATES.DETACHED &&
!targetActor.targetType.endsWith("worker")
) {
targetActor.threadActor.attach();
}
targetActor.threadActor.addEventBreakpoints(entries);
},
removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
targetActor.threadActor.removeEventBreakpoints(entries);
},
};

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

@ -0,0 +1,50 @@
/* 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 {
SessionDataHelpers,
} = require("devtools/server/actors/watcher/SessionDataHelpers.jsm");
const { SUPPORTED_DATA } = SessionDataHelpers;
const SessionData = {};
loader.lazyRequireGetter(
SessionData,
SUPPORTED_DATA.BLACKBOXING,
"devtools/server/actors/targets/session-data/blackboxing"
);
loader.lazyRequireGetter(
SessionData,
SUPPORTED_DATA.BREAKPOINTS,
"devtools/server/actors/targets/session-data/breakpoints"
);
loader.lazyRequireGetter(
SessionData,
SUPPORTED_DATA.EVENT_BREAKPOINTS,
"devtools/server/actors/targets/session-data/event-breakpoints"
);
loader.lazyRequireGetter(
SessionData,
SUPPORTED_DATA.RESOURCES,
"devtools/server/actors/targets/session-data/resources"
);
loader.lazyRequireGetter(
SessionData,
SUPPORTED_DATA.TARGET_CONFIGURATION,
"devtools/server/actors/targets/session-data/target-configuration"
);
loader.lazyRequireGetter(
SessionData,
SUPPORTED_DATA.THREAD_CONFIGURATION,
"devtools/server/actors/targets/session-data/thread-configuration"
);
loader.lazyRequireGetter(
SessionData,
SUPPORTED_DATA.XHR_BREAKPOINTS,
"devtools/server/actors/targets/session-data/xhr-breakpoints"
);
exports.SessionData = SessionData;

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

@ -0,0 +1,16 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
"blackboxing.js",
"breakpoints.js",
"event-breakpoints.js",
"index.js",
"resources.js",
"target-configuration.js",
"thread-configuration.js",
"xhr-breakpoints.js",
)

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

@ -0,0 +1,15 @@
/* 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";
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
await targetActor._watchTargetResources(entries);
},
removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
targetActor._unwatchTargetResources(entries);
},
};

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

@ -0,0 +1,23 @@
/* 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";
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
// Only WindowGlobalTargetActor implements updateTargetConfiguration,
// skip targetActor data entry update for other targets.
if (typeof targetActor.updateTargetConfiguration == "function") {
const options = {};
for (const { key, value } of entries) {
options[key] = value;
}
targetActor.updateTargetConfiguration(options, isDocumentCreation);
}
},
removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
// configuration data entries are always added/updated, never removed.
},
};

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

@ -0,0 +1,30 @@
/* 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 { STATES: THREAD_STATES } = require("devtools/server/actors/thread");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
const threadOptions = {};
for (const { key, value } of entries) {
threadOptions[key] = value;
}
if (
!targetActor.targetType.endsWith("worker") &&
targetActor.threadActor.state == THREAD_STATES.DETACHED
) {
await targetActor.threadActor.attach(threadOptions);
} else {
await targetActor.threadActor.reconfigure(threadOptions);
}
},
removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
// configuration data entries are always added/updated, never removed.
},
};

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

@ -0,0 +1,32 @@
/* 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 { STATES: THREAD_STATES } = require("devtools/server/actors/thread");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
// The thread actor has to be initialized in order to correctly
// retrieve the stack trace when hitting an XHR
if (
targetActor.threadActor.state == THREAD_STATES.DETACHED &&
!targetActor.targetType.endsWith("worker")
) {
await targetActor.threadActor.attach();
}
await Promise.all(
entries.map(({ path, method }) =>
targetActor.threadActor.setXHRBreakpoint(path, method)
)
);
},
removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
for (const { path, method } of entries) {
targetActor.threadActor.removeXHRBreakpoint(path, method);
}
},
};

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

@ -7,20 +7,13 @@
const { ActorClassWithSpec } = require("devtools/shared/protocol");
const Resources = require("devtools/server/actors/resources/index");
const {
SessionDataHelpers,
} = require("devtools/server/actors/watcher/SessionDataHelpers.jsm");
const { STATES: THREAD_STATES } = require("devtools/server/actors/thread");
const {
RESOURCES,
BLACKBOXING,
BREAKPOINTS,
TARGET_CONFIGURATION,
THREAD_CONFIGURATION,
XHR_BREAKPOINTS,
EVENT_BREAKPOINTS,
} = SessionDataHelpers.SUPPORTED_DATA;
loader.lazyRequireGetter(
this,
"SessionData",
"devtools/server/actors/targets/session-data/index",
true
);
loader.lazyRequireGetter(
this,
"StyleSheetsManager",
@ -47,106 +40,23 @@ module.exports = function(targetType, targetActorSpec, implementation) {
* Set to true if this function is called just after a new document (and its
* associated target) is created.
*/
// eslint-disable-next-line complexity
async addSessionDataEntry(type, entries, isDocumentCreation = false) {
if (type == RESOURCES) {
await this._watchTargetResources(entries);
} else if (type == BLACKBOXING) {
for (const { url, range } of entries) {
this.sourcesManager.blackBox(url, range);
}
} else if (type == BREAKPOINTS) {
const isTargetCreation =
this.threadActor.state == THREAD_STATES.DETACHED;
if (isTargetCreation && !this.targetType.endsWith("worker")) {
// If addSessionDataEntry is called during target creation, attach the
// thread actor automatically and pass the initial breakpoints.
// However, do not attach the thread actor for Workers. They use a codepath
// which releases the worker on `attach`. For them, the client will call `attach`. (bug 1691986)
await this.threadActor.attach({ breakpoints: entries });
} else {
// If addSessionDataEntry is called for an existing target, set the new
// breakpoints on the already running thread actor.
await Promise.all(
entries.map(({ location, options }) =>
this.threadActor.setBreakpoint(location, options)
)
);
}
} else if (type == TARGET_CONFIGURATION) {
// Only WindowGlobalTargetActor implements updateTargetConfiguration,
// skip this data entry update for other targets.
if (typeof this.updateTargetConfiguration == "function") {
const options = {};
for (const { key, value } of entries) {
options[key] = value;
}
this.updateTargetConfiguration(options, isDocumentCreation);
}
} else if (type == THREAD_CONFIGURATION) {
const threadOptions = {};
for (const { key, value } of entries) {
threadOptions[key] = value;
}
if (
!this.targetType.endsWith("worker") &&
this.threadActor.state == THREAD_STATES.DETACHED
) {
await this.threadActor.attach(threadOptions);
} else {
await this.threadActor.reconfigure(threadOptions);
}
} else if (type == XHR_BREAKPOINTS) {
// The thread actor has to be initialized in order to correctly
// retrieve the stack trace when hitting an XHR
if (
this.threadActor.state == THREAD_STATES.DETACHED &&
!this.targetType.endsWith("worker")
) {
await this.threadActor.attach();
}
await Promise.all(
entries.map(({ path, method }) =>
this.threadActor.setXHRBreakpoint(path, method)
)
);
} else if (type == EVENT_BREAKPOINTS) {
// Same as comments for XHR breakpoints. See lines 117-118
if (
this.threadActor.state == THREAD_STATES.DETACHED &&
!this.targetType.endsWith("worker")
) {
this.threadActor.attach();
}
this.threadActor.addEventBreakpoints(entries);
const processor = SessionData[type];
if (processor) {
await processor.addSessionDataEntry(this, entries, isDocumentCreation);
}
},
/**
* Remove data entries that have been previously added via addSessionDataEntry
*
* See addSessionDataEntry for argument description.
*/
removeSessionDataEntry(type, entries) {
if (type == RESOURCES) {
return this._unwatchTargetResources(entries);
} else if (type == BLACKBOXING) {
for (const { url, range } of entries) {
this.sourcesManager.unblackBox(url, range);
}
} else if (type == BREAKPOINTS) {
for (const { location } of entries) {
this.threadActor.removeBreakpoint(location);
}
} else if (type == TARGET_CONFIGURATION || type == THREAD_CONFIGURATION) {
// configuration data entries are always added/updated, never removed.
} else if (type == XHR_BREAKPOINTS) {
for (const { path, method } of entries) {
this.threadActor.removeXHRBreakpoint(path, method);
}
} else if (type == EVENT_BREAKPOINTS) {
this.threadActor.removeEventBreakpoints(entries);
const processor = SessionData[type];
if (processor) {
processor.removeSessionDataEntry(this, entries);
}
return Promise.resolve();
},
/**

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

@ -67,11 +67,12 @@ Here are the Rust versions for each Firefox version.
| Firefox 101 | Rust 1.60.0 | 1.59.0 | 2022 April 7 | 2022 April 28 | 2022 May 31
| Firefox 102 | Rust 1.60.0 | 1.59.0 | 2022 April 7 | 2022 May 26 | 2022 June 28
| Firefox 103 | Rust 1.61.0 | 1.59.0 | 2022 May 19 | 2022 June 23 | 2022 July 27
| Firefox 104 | Rust 1.62.0 | 1.59.0 | 2022 June 30 | 2022 July 21 | 2022 August 23
| **Estimated** |
| Firefox 104 | Rust 1.62.0 | ? | 2022 June 30 | 2022 July 21 | 2022 August 23
| Firefox 105 | Rust 1.63.0 | ? | 2022 August 11 | 2022 August 18 | 2022 September 20
| Firefox 106 | Rust 1.63.0 | ? | 2022 August 11 | 2022 September 15 | 2022 October 18
| Firefox 107 | Rust 1.64.0 | ? | 2022 September 22 | 2022 October 13 | 2022 November 15
| Firefox 108 | Rust 1.65.0 | ? | 2022 November 3 | 2022 November 10 | 2022 December 13
New feature adoption schedule:

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

@ -135,8 +135,9 @@ class CanonicalBrowsingContext final : public BrowsingContext {
LoadingSessionHistoryInfo* aInfo, nsIChannel* aNewChannel);
using PrintPromise = MozPromise</* unused */ bool, nsresult, false>;
RefPtr<PrintPromise> Print(nsIPrintSettings*);
already_AddRefed<Promise> PrintJS(nsIPrintSettings*, ErrorResult&);
MOZ_CAN_RUN_SCRIPT RefPtr<PrintPromise> Print(nsIPrintSettings*);
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PrintJS(nsIPrintSettings*,
ErrorResult&);
// Call the given callback on all top-level descendant BrowsingContexts.
// Return Callstate::Stop from the callback to stop calling

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

@ -6,6 +6,9 @@
// Test whether fallback mechanism is working if don't trust nsIExternalProtocolService.
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const { MockRegistrar } = ChromeUtils.import(
"resource://testing-common/MockRegistrar.jsm"
);
@ -60,9 +63,12 @@ add_task(function basic() {
const testData = [
{
input: "mailto:test@example.com",
expected: isSupportedInHandlerService("mailto")
? "mailto:test@example.com"
: "http://mailto:test@example.com/",
expected:
isSupportedInHandlerService("mailto") ||
// Thunderbird IS a mailto handler, it doesn't have handlers.
AppConstants.MOZ_APP_NAME == "thunderbird"
? "mailto:test@example.com"
: "http://mailto:test@example.com/",
},
{
input: "keyword:search",

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

@ -8,7 +8,9 @@
#define mozilla_dom_AutoPrintEventDispatcher_h
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "nsContentUtils.h"
#include "nsIPrintSettings.h"
namespace mozilla::dom {
@ -16,35 +18,91 @@ class AutoPrintEventDispatcher {
// NOTE(emilio): For fission iframes, we dispatch this event in
// RecvCloneDocumentTreeIntoSelf.
static void CollectInProcessSubdocuments(
Document& aDoc, nsTArray<nsCOMPtr<Document>>& aDocs) {
aDocs.AppendElement(&aDoc);
Document& aDoc, nsTArray<std::pair<nsCOMPtr<Document>, bool>>& aDocs) {
auto recurse = [&aDocs](Document& aSubDoc) {
aDocs.AppendElement(std::make_pair(&aSubDoc, false));
CollectInProcessSubdocuments(aSubDoc, aDocs);
return CallState::Continue;
};
aDoc.EnumerateSubDocuments(recurse);
}
void DispatchEvent(bool aBefore) {
for (nsCOMPtr<Document>& doc : mDocuments) {
MOZ_CAN_RUN_SCRIPT void DispatchEvent(bool aBefore) {
for (auto& [doc, isTop] : mDocuments) {
nsContentUtils::DispatchTrustedEvent(
doc, doc->GetWindow(), aBefore ? u"beforeprint"_ns : u"afterprint"_ns,
CanBubble::eNo, Cancelable::eNo, nullptr);
if (RefPtr<nsPresContext> presContext = doc->GetPresContext()) {
presContext->EmulateMedium(aBefore ? nsGkAtoms::print : nullptr);
bool needResizeEvent = false;
if (isTop) {
// NOTE(emilio): Having this code here means that we only fire the
// resize on the initial document clone, not on any further print
// settings changes. This is an intentional trade-off.
//
// On a continuously changing document, we don't want to keep
// re-cloning every time the user changes the print settings, as it
// changes what you're printing.
if (aBefore) {
mVisibleAreaToRestore = presContext->GetVisibleArea();
presContext->SetVisibleArea(
nsRect(mVisibleAreaToRestore.TopLeft(), mPageSize));
needResizeEvent = mVisibleAreaToRestore.Size() != mPageSize;
} else {
if (presContext->GetVisibleArea().Size() == mPageSize) {
presContext->SetVisibleArea(mVisibleAreaToRestore);
needResizeEvent = mVisibleAreaToRestore.Size() != mPageSize;
} else {
// This shouldn't happen, but can if used by privileged script,
// like in test_printpreview.xhtml. It's ok to do nothing here
// tho, we just need to make sure that the mPageSize viewport size
// doesn't persist.
NS_WARNING(
"Something changed our viewport size between firing "
"before/afterprint?");
}
}
}
// Ensure media query listeners fire.
doc->FlushPendingNotifications(FlushType::Style);
if (needResizeEvent) {
if (RefPtr ps = presContext->GetPresShell()) {
ps->FireResizeEventSync();
}
}
}
}
}
static nsSize ComputePrintPageSize(nsIPrintSettings* aPrintSettings) {
double pageWidth = 0;
double pageHeight = 0;
aPrintSettings->GetEffectivePageSize(&pageWidth, &pageHeight);
return nsSize(nsPresContext::CSSTwipsToAppUnits(NSToIntFloor(pageWidth)),
nsPresContext::CSSTwipsToAppUnits(NSToIntFloor(pageHeight)));
}
public:
explicit AutoPrintEventDispatcher(Document& aDoc) {
MOZ_CAN_RUN_SCRIPT AutoPrintEventDispatcher(Document& aDoc,
nsIPrintSettings* aPrintSettings,
bool aIsTop)
: mPageSize(ComputePrintPageSize(aPrintSettings)) {
if (!aDoc.IsStaticDocument()) {
mDocuments.AppendElement(std::make_pair(&aDoc, aIsTop));
CollectInProcessSubdocuments(aDoc, mDocuments);
}
DispatchEvent(true);
}
~AutoPrintEventDispatcher() { DispatchEvent(false); }
MOZ_CAN_RUN_SCRIPT ~AutoPrintEventDispatcher() { DispatchEvent(false); }
AutoTArray<nsCOMPtr<Document>, 8> mDocuments;
// The bool represents whether this is the top-level paginated document.
AutoTArray<std::pair<nsCOMPtr<Document>, bool>, 8> mDocuments;
const nsSize mPageSize;
nsRect mVisibleAreaToRestore;
};
} // namespace mozilla::dom

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

@ -68,7 +68,8 @@ static nsSize GetContentRectSize(const nsIFrame& aFrame) {
/**
* Returns |aTarget|'s size in the form of gfx::Size (in pixels).
* If the target is SVG, width and height are determined from bounding box.
* If the target is an SVG that does not participate in CSS layout,
* its width and height are determined from bounding box.
*
* https://www.w3.org/TR/resize-observer-1/#calculate-box-size
*/
@ -81,10 +82,10 @@ static gfx::Size CalculateBoxSize(Element* aTarget,
return size;
}
if (aTarget->IsSVGElement()) {
// Per the spec, SVG size is always its bounding box size no matter what
// box option you choose, because SVG elements do not use standard CSS box
// model.
if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
// Per the spec, this target's SVG size is always its bounding box size no
// matter what box option you choose, because SVG elements do not use
// standard CSS box model.
const gfxRect bbox = SVGUtils::GetBBox(frame);
size.width = static_cast<float>(bbox.width);
size.height = static_cast<float>(bbox.height);

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

@ -0,0 +1,25 @@
<style>
:not(cursor) {
column-count: 1;
user-select: none;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", () => {
window.getSelection().modify("move", "backward", "line")
setInterval(() => {
const m = new MutationObserver(() => {
let x = document.getSelection().getRangeAt(0)
a.appendChild(document.createElement("optgroup"))
x.comparePoint(document.activeElement, 1)
})
m.observe(b, {attributes: true})
b.setAttribute("x", "false")
}, 100)
})
</script>
<hr contenteditable="true">
<shadow id="a">
<select autofocus="autofocus">a</select>
<div id="b">a</div>

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

@ -268,3 +268,4 @@ skip-if(Android) HTTP load 1728670-1.html
load 1757923.html
load 1766472.html
pref(dom.enable_web_task_scheduling,true) load 1780790.html
load 1700237.html

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

@ -222,9 +222,9 @@ class nsFrameLoader final : public nsStubMutationObserver,
void RequestSHistoryUpdate();
already_AddRefed<Promise> PrintPreview(nsIPrintSettings* aPrintSettings,
BrowsingContext* aSourceBC,
mozilla::ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PrintPreview(
nsIPrintSettings* aPrintSettings, BrowsingContext* aSourceBC,
mozilla::ErrorResult& aRv);
void ExitPrintPreview();

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

@ -342,7 +342,7 @@ using mozilla::dom::cache::CacheStorage;
#define FORWARD_TO_OUTER(method, args, err_rval) \
PR_BEGIN_MACRO \
nsGlobalWindowOuter* outer = GetOuterWindowInternal(); \
RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
if (!HasActiveDocument()) { \
NS_WARNING(outer ? "Inner window does not have active document." \
: "No outer window available!"); \
@ -366,18 +366,18 @@ static nsGlobalWindowOuter* GetOuterWindowForForwarding(
return nullptr;
}
#define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \
PR_BEGIN_MACRO \
nsGlobalWindowOuter* outer = GetOuterWindowForForwarding(this, errorresult); \
if (MOZ_LIKELY(outer)) { \
return outer->method args; \
} \
return err_rval; \
#define FORWARD_TO_OUTER_OR_THROW(method, args, rv, err_rval) \
PR_BEGIN_MACRO \
RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowForForwarding(this, rv); \
if (MOZ_LIKELY(outer)) { \
return outer->method args; \
} \
return err_rval; \
PR_END_MACRO
#define FORWARD_TO_OUTER_VOID(method, args) \
PR_BEGIN_MACRO \
nsGlobalWindowOuter* outer = GetOuterWindowInternal(); \
RefPtr<nsGlobalWindowOuter> outer = GetOuterWindowInternal(); \
if (!HasActiveDocument()) { \
NS_WARNING(outer ? "Inner window does not have active document." \
: "No outer window available!"); \

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

@ -689,10 +689,10 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
const mozilla::dom::RequestOrUSVString& aInput,
const mozilla::dom::RequestInit& aInit,
mozilla::dom::CallerType aCallerType, mozilla::ErrorResult& aRv);
void Print(mozilla::ErrorResult& aError);
mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> PrintPreview(
nsIPrintSettings*, nsIWebProgressListener*, nsIDocShell*,
mozilla::ErrorResult&);
MOZ_CAN_RUN_SCRIPT void Print(mozilla::ErrorResult& aError);
MOZ_CAN_RUN_SCRIPT mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder>
PrintPreview(nsIPrintSettings*, nsIWebProgressListener*, nsIDocShell*,
mozilla::ErrorResult&);
void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const nsAString& aTargetOrigin,
const mozilla::dom::Sequence<JSObject*>& aTransfer,

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

@ -5251,8 +5251,7 @@ Nullable<WindowProxyHolder> nsGlobalWindowOuter::Print(
}
}
// TODO(emilio): Should dispatch this to OOP iframes too.
AutoPrintEventDispatcher dispatcher(*docToPrint);
AutoPrintEventDispatcher dispatcher(*docToPrint, ps, /* aIsTop = */ true);
nsAutoScriptBlocker blockScripts;
RefPtr<Document> clone = docToPrint->CreateStaticClone(

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

@ -595,14 +595,15 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
nsAString& aReturn, nsIPrincipal& aSubjectPrincipal,
mozilla::ErrorResult& aError);
void PrintOuter(mozilla::ErrorResult& aError);
MOZ_CAN_RUN_SCRIPT void PrintOuter(mozilla::ErrorResult& aError);
enum class IsPreview : bool { No, Yes };
enum class IsForWindowDotPrint : bool { No, Yes };
mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> Print(
nsIPrintSettings*, mozilla::layout::RemotePrintJobChild* aRemotePrintJob,
nsIWebProgressListener*, nsIDocShell*, IsPreview, IsForWindowDotPrint,
PrintPreviewResolver&&, mozilla::ErrorResult&);
MOZ_CAN_RUN_SCRIPT mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder>
Print(nsIPrintSettings*,
mozilla::layout::RemotePrintJobChild* aRemotePrintJob,
nsIWebProgressListener*, nsIDocShell*, IsPreview, IsForWindowDotPrint,
PrintPreviewResolver&&, mozilla::ErrorResult&);
mozilla::dom::Selection* GetSelectionOuter();
already_AddRefed<mozilla::dom::Selection> GetSelection() override;
nsScreen* GetScreen();

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

@ -742,6 +742,15 @@ bool nsRange::IsPointComparableToRange(const nsINode& aContainer,
return false;
}
auto chromeOnlyAccess = mStart.Container()->ChromeOnlyAccess();
NS_ASSERTION(chromeOnlyAccess == mEnd.Container()->ChromeOnlyAccess(),
"Start and end of a range must be either both native anonymous "
"content or not.");
if (aContainer.ChromeOnlyAccess() != chromeOnlyAccess) {
aErrorResult.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
return false;
}
if (aContainer.NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
aErrorResult.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
return false;
@ -777,21 +786,14 @@ int16_t nsRange::ComparePoint(const nsINode& aContainer, uint32_t aOffset,
MOZ_ASSERT(point.IsSetAndValid());
Maybe<int32_t> order = nsContentUtils::ComparePoints(point, mStart);
// `order` must contain a value, because `IsPointComparableToRange()` was
// checked above.
if (*order <= 0) {
return *order;
if (Maybe<int32_t> order = nsContentUtils::ComparePoints(point, mStart);
order && *order <= 0) {
return int16_t(*order);
}
order = nsContentUtils::ComparePoints(mEnd, point);
// `order` must contain a value, because `IsPointComparableToRange()` was
// checked above.
if (*order == -1) {
if (Maybe<int32_t> order = nsContentUtils::ComparePoints(mEnd, point);
order && *order == -1) {
return 1;
}
return 0;
}

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

@ -1035,7 +1035,8 @@ nsresult BrowserChild::CloneDocumentTreeIntoSelf(
RefPtr<Document> clone;
{
AutoPrintEventDispatcher dispatcher(*sourceDocument);
AutoPrintEventDispatcher dispatcher(*sourceDocument, printSettings,
/* aIsTop = */ false);
nsAutoScriptBlocker scriptBlocker;
bool hasInProcessCallbacks = false;
clone = sourceDocument->CreateStaticClone(ourDocShell, cv, printSettings,
@ -2534,13 +2535,14 @@ mozilla::ipc::IPCResult BrowserChild::RecvPrint(
printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
{
IgnoredErrorResult rv;
outerWindow->Print(
printSettings,
static_cast<RemotePrintJobChild*>(aPrintData.remotePrintJobChild()),
/* aListener = */ nullptr,
/* aWindowToCloneInto = */ nullptr, nsGlobalWindowOuter::IsPreview::No,
nsGlobalWindowOuter::IsForWindowDotPrint::No,
/* aPrintPreviewCallback = */ nullptr, rv);
RefPtr printJob =
static_cast<RemotePrintJobChild*>(aPrintData.remotePrintJobChild());
outerWindow->Print(printSettings, printJob,
/* aListener = */ nullptr,
/* aWindowToCloneInto = */ nullptr,
nsGlobalWindowOuter::IsPreview::No,
nsGlobalWindowOuter::IsForWindowDotPrint::No,
/* aPrintPreviewCallback = */ nullptr, rv);
if (NS_WARN_IF(rv.Failed())) {
return IPC_OK();
}

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

@ -545,15 +545,15 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
mozilla::ipc::IPCResult RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent,
nsTArray<uint32_t>&& aCharCodes);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
mozilla::ipc::IPCResult RecvPrintPreview(const PrintData& aPrintData,
const MaybeDiscardedBrowsingContext&,
PrintPreviewResolver&& aCallback);
mozilla::ipc::IPCResult RecvExitPrintPreview();
mozilla::ipc::IPCResult RecvPrint(const MaybeDiscardedBrowsingContext&,
const PrintData&);
MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvPrint(
const MaybeDiscardedBrowsingContext&, const PrintData&);
mozilla::ipc::IPCResult RecvUpdateNativeWindowHandle(
const uintptr_t& aNewHandle);

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

@ -6609,10 +6609,6 @@ mozilla::ipc::IPCResult ContentParent::RecvCompleteAllowAccessFor(
return IPC_OK();
}
if (!aTrackingPrincipal) {
return IPC_FAIL(this, "No principal");
}
StorageAccessAPIHelper::CompleteAllowAccessFor(
aParentContext.get_canonical(), aTopLevelWindowId, aTrackingPrincipal,
aTrackingOrigin, aCookieBehavior, aReason, nullptr)

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

@ -9,7 +9,7 @@ support-files =
[browser_CrashService_crash.js]
skip-if = !crashreporter
[browser_ProcessPriorityManager.js]
skip-if = os != "win" # The Process Priority Manager is only enabled for Windows so far. Bug 1522879.
skip-if = (os != "win") || (os != "linux") # The Process Priority Manager is only enabled for Windows and Linux so far. Bug 1522879.
# However, you can still run browser_ProcessPriorityManager.js locally on other
# OSes. This will test the priority manager infrastructure but not actually
# change the priority.

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

@ -132,25 +132,12 @@ static Element::MappedAttributeEntry sGlobalAttributes[] = {
{nsGkAtoms::mathvariant_}, {nsGkAtoms::scriptlevel_},
{nsGkAtoms::displaystyle_}, {nullptr}};
static Element::MappedAttributeEntry sDeprecatedStyleAttributes[] = {
{nsGkAtoms::background},
{nsGkAtoms::color},
{nsGkAtoms::fontfamily_},
{nsGkAtoms::fontsize_},
{nsGkAtoms::fontstyle_},
{nsGkAtoms::fontweight_},
{nullptr}};
bool MathMLElement::IsAttributeMapped(const nsAtom* aAttribute) const {
MOZ_ASSERT(IsMathMLElement());
static const MappedAttributeEntry* const globalMap[] = {sGlobalAttributes};
static const MappedAttributeEntry* const styleMap[] = {
sDeprecatedStyleAttributes};
return FindAttributeDependence(aAttribute, globalMap) ||
(!StaticPrefs::mathml_deprecated_style_attributes_disabled() &&
FindAttributeDependence(aAttribute, styleMap)) ||
(!StaticPrefs::mathml_scriptminsize_attribute_disabled() &&
aAttribute == nsGkAtoms::scriptminsize_) ||
(!StaticPrefs::mathml_scriptsizemultiplier_attribute_disabled() &&
@ -463,61 +450,15 @@ void MathMLElement::MapMathMLAttributesInto(
}
// mathsize
//
// "Specifies the size to display the token content. The values 'small' and
// 'big' choose a size smaller or larger than the current font size, but
// leave the exact proportions unspecified; 'normal' is allowed for
// completeness, but since it is equivalent to '100%' or '1em', it has no
// effect."
//
// values: "small" | "normal" | "big" | length
// default: inherited
//
// fontsize
//
// "Specified the size for the token. Deprecated in favor of mathsize."
//
// values: length
// default: inherited
//
// In both cases, we don't allow negative values.
// Unitless values give a multiple of the default value.
//
bool parseSizeKeywords = !StaticPrefs::mathml_mathsize_names_disabled();
// https://w3c.github.io/mathml-core/#dfn-mathsize
value = aAttributes->GetAttr(nsGkAtoms::mathsize_);
if (!value) {
parseSizeKeywords = false;
value = aAttributes->GetAttr(nsGkAtoms::fontsize_);
if (value) {
aDecls.Document()->WarnOnceAbout(
dom::DeprecatedOperations::eMathML_DeprecatedStyleAttribute);
}
}
if (value && value->Type() == nsAttrValue::eString &&
!aDecls.PropertyIsSet(eCSSProperty_font_size)) {
auto str = value->GetStringValue();
nsCSSValue fontSize;
uint32_t flags = PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT;
if (parseSizeKeywords) {
// Do not warn for invalid value if mathsize keywords are accepted.
flags |= PARSE_SUPPRESS_WARNINGS;
}
if (!ParseNumericValue(str, fontSize, flags, nullptr) &&
parseSizeKeywords) {
static const char sizes[3][7] = {"small", "normal", "big"};
static const StyleFontSizeKeyword values[MOZ_ARRAY_LENGTH(sizes)] = {
StyleFontSizeKeyword::Small, StyleFontSizeKeyword::Medium,
StyleFontSizeKeyword::Large};
str.CompressWhitespace();
for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
if (str.EqualsASCII(sizes[i])) {
aDecls.Document()->WarnOnceAbout(
dom::DeprecatedOperations::eMathML_DeprecatedMathSizeValue);
aDecls.SetKeywordValue(eCSSProperty_font_size, values[i]);
break;
}
}
} else if (fontSize.GetUnit() == eCSSUnit_Percent) {
ParseNumericValue(str, fontSize, flags, nullptr);
if (fontSize.GetUnit() == eCSSUnit_Percent) {
aDecls.SetPercentValue(eCSSProperty_font_size,
fontSize.GetPercentValue());
} else if (fontSize.GetUnit() != eCSSUnit_Null) {
@ -525,80 +466,6 @@ void MathMLElement::MapMathMLAttributesInto(
}
}
// fontfamily
//
// "Should be the name of a font that may be available to a MathML renderer,
// or a CSS font specification; See Section 6.5 Using CSS with MathML and
// CSS for more information. Deprecated in favor of mathvariant."
//
// values: string
//
value = aAttributes->GetAttr(nsGkAtoms::fontfamily_);
if (value) {
aDecls.Document()->WarnOnceAbout(
dom::DeprecatedOperations::eMathML_DeprecatedStyleAttribute);
}
if (value && value->Type() == nsAttrValue::eString &&
!aDecls.PropertyIsSet(eCSSProperty_font_family)) {
aDecls.SetFontFamily(NS_ConvertUTF16toUTF8(value->GetStringValue()));
}
// fontstyle
//
// "Specified the font style to use for the token. Deprecated in favor of
// mathvariant."
//
// values: "normal" | "italic"
// default: normal (except on <mi>)
//
// Note that the font-style property is reset in layout/style/ when
// -moz-math-variant is specified.
value = aAttributes->GetAttr(nsGkAtoms::fontstyle_);
if (value) {
aDecls.Document()->WarnOnceAbout(
dom::DeprecatedOperations::eMathML_DeprecatedStyleAttribute);
if (value->Type() == nsAttrValue::eString &&
!aDecls.PropertyIsSet(eCSSProperty_font_style)) {
auto str = value->GetStringValue();
str.CompressWhitespace();
// FIXME(emilio): This should use FontSlantStyle or what not. Or even
// better, it looks deprecated since forever, we should just kill it.
if (str.EqualsASCII("normal")) {
aDecls.SetKeywordValue(eCSSProperty_font_style, NS_FONT_STYLE_NORMAL);
} else if (str.EqualsASCII("italic")) {
aDecls.SetKeywordValue(eCSSProperty_font_style, NS_FONT_STYLE_ITALIC);
}
}
}
// fontweight
//
// "Specified the font weight for the token. Deprecated in favor of
// mathvariant."
//
// values: "normal" | "bold"
// default: normal
//
// Note that the font-weight property is reset in layout/style/ when
// -moz-math-variant is specified.
value = aAttributes->GetAttr(nsGkAtoms::fontweight_);
if (value) {
aDecls.Document()->WarnOnceAbout(
dom::DeprecatedOperations::eMathML_DeprecatedStyleAttribute);
if (value->Type() == nsAttrValue::eString &&
!aDecls.PropertyIsSet(eCSSProperty_font_weight)) {
auto str = value->GetStringValue();
str.CompressWhitespace();
if (str.EqualsASCII("normal")) {
aDecls.SetKeywordValue(eCSSProperty_font_weight,
FontWeight::NORMAL.ToFloat());
} else if (str.EqualsASCII("bold")) {
aDecls.SetKeywordValue(eCSSProperty_font_weight,
FontWeight::BOLD.ToFloat());
}
}
}
// mathvariant
//
// "Specifies the logical class of the token. Note that this class is more
@ -661,31 +528,8 @@ void MathMLElement::MapMathMLAttributesInto(
}
// mathbackground
//
// "Specifies the background color to be used to fill in the bounding box of
// the element and its children. The default, 'transparent', lets the
// background color, if any, used in the current rendering context to show
// through."
//
// values: color | "transparent"
// default: "transparent"
//
// background
//
// "Specified the background color to be used to fill in the bounding box of
// the element and its children. Deprecated in favor of mathbackground."
//
// values: color | "transparent"
// default: "transparent"
//
// https://w3c.github.io/mathml-core/#dfn-mathbackground
value = aAttributes->GetAttr(nsGkAtoms::mathbackground_);
if (!value) {
value = aAttributes->GetAttr(nsGkAtoms::background);
if (value) {
aDecls.Document()->WarnOnceAbout(
dom::DeprecatedOperations::eMathML_DeprecatedStyleAttribute);
}
}
if (value) {
nscolor color;
if (value->GetColorValue(color)) {
@ -694,30 +538,8 @@ void MathMLElement::MapMathMLAttributesInto(
}
// mathcolor
//
// "Specifies the foreground color to use when drawing the components of this
// element, such as the content for token elements or any lines, surds, or
// other decorations. It also establishes the default mathcolor used for
// child elements when used on a layout element."
//
// values: color
// default: inherited
//
// color
//
// "Specified the color for the token. Deprecated in favor of mathcolor."
//
// values: color
// default: inherited
//
// https://w3c.github.io/mathml-core/#dfn-mathcolor
value = aAttributes->GetAttr(nsGkAtoms::mathcolor_);
if (!value) {
value = aAttributes->GetAttr(nsGkAtoms::color);
if (value) {
aDecls.Document()->WarnOnceAbout(
dom::DeprecatedOperations::eMathML_DeprecatedStyleAttribute);
}
}
nscolor color;
if (value && value->GetColorValue(color)) {
aDecls.SetColorValueIfUnset(eCSSProperty_color, color);
@ -750,25 +572,7 @@ void MathMLElement::MapMathMLAttributesInto(
}
// dir
//
// Overall Directionality of Mathematics Formulas:
// "The overall directionality for a formula, basically the direction of the
// Layout Schemata, is specified by the dir attribute on the containing math
// element (see Section 2.2 The Top-Level math Element). The default is ltr.
// [...] The overall directionality is usually set on the math, but may also
// be switched for individual subformula by using the dir attribute on mrow
// or mstyle elements."
//
// Bidirectional Layout in Token Elements:
// "Specifies the initial directionality for text within the token:
// ltr (Left To Right) or rtl (Right To Left). This attribute should only be
// needed in rare cases involving weak or neutral characters;
// see Section 3.1.5.1 Overall Directionality of Mathematics Formulas for
// further discussion. It has no effect on mspace."
//
// values: "ltr" | "rtl"
// default: inherited
//
// https://w3c.github.io/mathml-core/#dfn-dir
value = aAttributes->GetAttr(nsGkAtoms::dir);
if (value && value->Type() == nsAttrValue::eString &&
!aDecls.PropertyIsSet(eCSSProperty_direction)) {

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

@ -110,15 +110,16 @@ MIDIPermissionRequest::Run() {
}
// If the add-on is not installed, auto-deny (except for localhost).
if (!nsContentUtils::HasSitePerm(mPrincipal, kPermName) &&
if (StaticPrefs::dom_webmidi_gated() &&
!nsContentUtils::HasSitePerm(mPrincipal, kPermName) &&
!BasePrincipal::Cast(mPrincipal)->IsLoopbackHost()) {
Cancel();
return NS_OK;
}
// We can only get here for localhost, or if the add-on is installed, but the
// user has subsequently changed the permission from ALLOW to ASK. In that
// unusual case, throw up a prompt.
// We can only get here for localhost, if add-on gating is disabled or if the
// add-on is installed but the user has subsequently changed the permission
// from ALLOW to ASK. In that unusual case, throw up a prompt.
if (NS_FAILED(nsContentPermissionUtils::AskPermission(this, mWindow))) {
Cancel();
return NS_ERROR_FAILURE;

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

@ -1557,6 +1557,8 @@ let interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "ondragenter", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "ondragexit", insecureContext: true, nightly: false },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "ondragleave", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "ondragover", insecureContext: true },
@ -1779,7 +1781,7 @@ let interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "resizeTo", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "scheduler", insecureContext: true },
{ name: "scheduler", insecureContext: true, nightly: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "screen", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -1913,11 +1915,11 @@ function runTest(parentName, parent, ...interfaceGroups) {
ok(
name in parent,
`${name} is exposed as an own property on '" + parentName + "' but tests false for "in" in the global scope`
`${name} is exposed as an own property on '${parentName}' but tests false for "in" in the global scope`
);
ok(
Object.getOwnPropertyDescriptor(parent, name),
`${name} is exposed as an own property on '" + parentName + "' but has no property descriptor in the global scope`
`${name} is exposed as an own property on '${parentName}' but has no property descriptor in the global scope`
);
delete interfaceMap[name];

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

@ -282,6 +282,13 @@ fn prepare_interned_prim_for_render(
.ceil().to_i32();
}
// It's plausible, due to float accuracy issues that the line decoration may be considered
// visible even if the scale factors are ~0. However, the render task allocation below requires
// that the size of the task is > 0. To work around this, ensure that the task size is at least
// 1x1 pixels
task_size.width = task_size.width.max(1);
task_size.height = task_size.height.max(1);
// Request a pre-rendered image task.
// TODO(gw): This match is a bit untidy, but it should disappear completely
// once the prepare_prims and batching are unified. When that

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

@ -258,8 +258,8 @@ static nsresult GetIconHandleFromPathInfo(const IconPathInfo& aPathInfo,
}
// Match stock icons with names
static SHSTOCKICONID GetStockIconIDForName(const nsACString& aStockName) {
return aStockName.EqualsLiteral("uac-shield") ? SIID_SHIELD : SIID_INVALID;
static mozilla::Maybe<SHSTOCKICONID> GetStockIconIDForName(const nsACString& aStockName) {
return aStockName.EqualsLiteral("uac-shield") ? Some(SIID_SHIELD) : Nothing();
}
// Specific to Vista and above
@ -269,8 +269,8 @@ static nsresult GetStockHIcon(nsIMozIconURI* aIconURI, HICON* aIcon) {
nsAutoCString stockIcon;
aIconURI->GetStockIcon(stockIcon);
SHSTOCKICONID stockIconID = GetStockIconIDForName(stockIcon);
if (stockIconID == SIID_INVALID) {
Maybe<SHSTOCKICONID> stockIconID = GetStockIconIDForName(stockIcon);
if (stockIconID.isNothing()) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -279,7 +279,7 @@ static nsresult GetStockHIcon(nsIMozIconURI* aIconURI, HICON* aIcon) {
SHSTOCKICONINFO sii = {0};
sii.cbSize = sizeof(sii);
HRESULT hr = SHGetStockIconInfo(stockIconID, infoFlags, &sii);
HRESULT hr = SHGetStockIconInfo(*stockIconID, infoFlags, &sii);
if (FAILED(hr)) {
return NS_ERROR_FAILURE;
}

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

@ -105,39 +105,38 @@ nsConverterInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
void* aClosure, uint32_t aCount,
uint32_t* aReadCount) {
NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
uint32_t bytesToWrite = mUnicharDataLength - mUnicharDataOffset;
nsresult rv;
if (0 == bytesToWrite) {
uint32_t codeUnitsToWrite = mUnicharDataLength - mUnicharDataOffset;
if (0 == codeUnitsToWrite) {
// Fill the unichar buffer
bytesToWrite = Fill(&rv);
if (bytesToWrite <= 0) {
codeUnitsToWrite = Fill(&mLastErrorCode);
if (codeUnitsToWrite == 0) {
*aReadCount = 0;
return rv;
}
if (NS_FAILED(rv)) {
return rv;
return mLastErrorCode;
}
}
if (bytesToWrite > aCount) bytesToWrite = aCount;
if (codeUnitsToWrite > aCount) {
codeUnitsToWrite = aCount;
}
uint32_t bytesWritten;
uint32_t totalBytesWritten = 0;
uint32_t codeUnitsWritten;
uint32_t totalCodeUnitsWritten = 0;
while (bytesToWrite) {
rv = aWriter(this, aClosure, mUnicharData.Elements() + mUnicharDataOffset,
totalBytesWritten, bytesToWrite, &bytesWritten);
while (codeUnitsToWrite) {
nsresult rv =
aWriter(this, aClosure, mUnicharData.Elements() + mUnicharDataOffset,
totalCodeUnitsWritten, codeUnitsToWrite, &codeUnitsWritten);
if (NS_FAILED(rv)) {
// don't propagate errors to the caller
break;
}
bytesToWrite -= bytesWritten;
totalBytesWritten += bytesWritten;
mUnicharDataOffset += bytesWritten;
codeUnitsToWrite -= codeUnitsWritten;
totalCodeUnitsWritten += codeUnitsWritten;
mUnicharDataOffset += codeUnitsWritten;
}
*aReadCount = totalBytesWritten;
*aReadCount = totalCodeUnitsWritten;
return NS_OK;
}
@ -166,7 +165,7 @@ nsConverterInputStream::ReadString(uint32_t aCount, nsAString& aString,
}
uint32_t nsConverterInputStream::Fill(nsresult* aErrorCode) {
if (nullptr == mInput) {
if (!mInput) {
// We already closed the stream!
*aErrorCode = NS_BASE_STREAM_CLOSED;
return 0;
@ -179,54 +178,72 @@ uint32_t nsConverterInputStream::Fill(nsresult* aErrorCode) {
return 0;
}
// We assume a many to one conversion and are using equal sizes for
// the two buffers. However if an error happens at the very start
// of a byte buffer we may end up in a situation where n bytes lead
// to n+1 unicode chars. Thus we need to keep track of the leftover
// bytes as we convert.
uint32_t nb;
*aErrorCode = NS_FillArray(mByteData, mInput, mLeftOverBytes, &nb);
if (nb == 0 && mLeftOverBytes == 0) {
// No more data
*aErrorCode = NS_OK;
return 0;
}
NS_ASSERTION(uint32_t(nb) + mLeftOverBytes == mByteData.Length(),
"mByteData is lying to us somewhere");
// Now convert as much of the byte buffer to unicode as possible
auto src = AsBytes(Span(mByteData));
auto dst = Span(mUnicharData);
// mUnicharData.Length() is the buffer length, not the fill status.
// mUnicharDataLength reflects the current fill status.
mUnicharDataLength = 0;
// Whenever we convert, mUnicharData is logically empty.
mUnicharDataOffset = 0;
// Truncation from size_t to uint32_t below is OK, because the sizes
// are bounded by the lengths of mByteData and mUnicharData.
uint32_t result;
size_t read;
size_t written;
// The design of this class is fundamentally bogus in that trailing
// errors are ignored. Always passing false as the last argument to
// Decode* calls below.
if (mErrorsAreFatal) {
std::tie(result, read, written) =
mConverter->DecodeToUTF16WithoutReplacement(src, dst, false);
} else {
std::tie(result, read, written, std::ignore) =
mConverter->DecodeToUTF16(src, dst, false);
}
mLeftOverBytes = mByteData.Length() - read;
mUnicharDataLength = written;
if (result == kInputEmpty || result == kOutputFull) {
*aErrorCode = NS_OK;
} else {
MOZ_ASSERT(mErrorsAreFatal, "How come DecodeToUTF16() reported error?");
*aErrorCode = NS_ERROR_UDEC_ILLEGALINPUT;
// Continue trying to read from the source stream until we successfully decode
// a character or encounter an error, as returning `0` here implies that the
// stream is complete.
//
// If the converter has been cleared, we've fully consumed the stream, and
// want to report EOF.
while (mUnicharDataLength == 0 && mConverter) {
// We assume a many to one conversion and are using equal sizes for
// the two buffers. However if an error happens at the very start
// of a byte buffer we may end up in a situation where n bytes lead
// to n+1 unicode chars. Thus we need to keep track of the leftover
// bytes as we convert.
uint32_t nb;
*aErrorCode = NS_FillArray(mByteData, mInput, mLeftOverBytes, &nb);
if (NS_FAILED(*aErrorCode)) {
return 0;
}
NS_ASSERTION(uint32_t(nb) + mLeftOverBytes == mByteData.Length(),
"mByteData is lying to us somewhere");
// If `NS_FillArray` failed to read any new bytes, this is the last read,
// and we're at the end of the stream.
bool last = (nb == 0);
// Now convert as much of the byte buffer to unicode as possible
auto src = AsBytes(Span(mByteData));
auto dst = Span(mUnicharData);
// Truncation from size_t to uint32_t below is OK, because the sizes
// are bounded by the lengths of mByteData and mUnicharData.
uint32_t result;
size_t read;
size_t written;
if (mErrorsAreFatal) {
std::tie(result, read, written) =
mConverter->DecodeToUTF16WithoutReplacement(src, dst, last);
} else {
std::tie(result, read, written, std::ignore) =
mConverter->DecodeToUTF16(src, dst, last);
}
mLeftOverBytes = mByteData.Length() - read;
mUnicharDataLength = written;
// Clear `mConverter` if we reached the end of the stream, as we can't
// call methods on it anymore. This will also signal EOF to the caller
// through the loop condition.
if (last) {
MOZ_ASSERT(mLeftOverBytes == 0,
"Failed to read all bytes on the last pass?");
mConverter = nullptr;
}
// If we got a decode error, we're done.
if (result != kInputEmpty && result != kOutputFull) {
MOZ_ASSERT(mErrorsAreFatal, "How come DecodeToUTF16() reported error?");
*aErrorCode = NS_ERROR_UDEC_ILLEGALINPUT;
return 0;
}
}
*aErrorCode = NS_OK;
return mUnicharDataLength;
}

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

@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "gtest/gtest.h"
#include "mozilla/ErrorNames.h"
#include "nsCOMPtr.h"
#include "nsConverterInputStream.h"
#include "nsIInputStream.h"
#include "nsISupports.h"
#include "nsStringStream.h"
namespace {
class ShortReadWrapper final : public nsIInputStream {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
template <size_t N>
ShortReadWrapper(const uint32_t (&aShortReads)[N],
nsIInputStream* aBaseStream)
: mShortReadIter(std::begin(aShortReads)),
mShortReadEnd(std::end(aShortReads)),
mBaseStream(aBaseStream) {}
ShortReadWrapper(const ShortReadWrapper&) = delete;
ShortReadWrapper& operator=(const ShortReadWrapper&) = delete;
private:
~ShortReadWrapper() = default;
const uint32_t* mShortReadIter;
const uint32_t* mShortReadEnd;
nsCOMPtr<nsIInputStream> mBaseStream;
};
NS_IMPL_ISUPPORTS(ShortReadWrapper, nsIInputStream)
NS_IMETHODIMP
ShortReadWrapper::Close() { return mBaseStream->Close(); }
NS_IMETHODIMP
ShortReadWrapper::Available(uint64_t* aAvailable) {
nsresult rv = mBaseStream->Available(aAvailable);
NS_ENSURE_SUCCESS(rv, rv);
if (mShortReadIter != mShortReadEnd) {
*aAvailable = std::min(uint64_t(*mShortReadIter), *aAvailable);
}
return NS_OK;
}
NS_IMETHODIMP
ShortReadWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
if (mShortReadIter != mShortReadEnd) {
aCount = std::min(*mShortReadIter, aCount);
}
nsresult rv = mBaseStream->Read(aBuf, aCount, _retval);
if (NS_SUCCEEDED(rv) && mShortReadIter != mShortReadEnd) {
++mShortReadIter;
}
return rv;
}
NS_IMETHODIMP
ShortReadWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* _retval) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ShortReadWrapper::IsNonBlocking(bool* _retval) {
return mBaseStream->IsNonBlocking(_retval);
}
} // namespace
TEST(ConverterStreamShortRead, ShortRead)
{
uint8_t bytes[] = {0xd8, 0x35, 0xdc, 0x20};
nsCOMPtr<nsIInputStream> baseStream;
ASSERT_TRUE(NS_SUCCEEDED(NS_NewByteInputStream(getter_AddRefs(baseStream),
AsChars(mozilla::Span(bytes)),
NS_ASSIGNMENT_COPY)));
static const uint32_t kShortReads[] = {1, 2, 1};
nsCOMPtr<nsIInputStream> shortStream =
new ShortReadWrapper(kShortReads, baseStream);
RefPtr<nsConverterInputStream> unicharStream = new nsConverterInputStream();
ASSERT_TRUE(NS_SUCCEEDED(
unicharStream->Init(shortStream, "UTF-16BE", 4096,
nsIConverterInputStream::ERRORS_ARE_FATAL)));
uint32_t read;
nsAutoString result;
ASSERT_TRUE(
NS_SUCCEEDED(unicharStream->ReadString(UINT32_MAX, result, &read)));
ASSERT_EQ(read, 2u);
ASSERT_TRUE(result == u"\U0001d420");
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше