зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
58559a3e5b
|
@ -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";
|
||||
|
|
29
browser/extensions/formautofill/test/fixtures/without_autocomplete_creditcard_basic.html
поставляемый
Normal file
29
browser/extensions/formautofill/test/fixtures/without_autocomplete_creditcard_basic.html
поставляемый
Normal file
|
@ -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");
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче