зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
3f782c2587
|
@ -973,9 +973,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.2"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
|
||||
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils 0.8.6",
|
||||
|
|
|
@ -201,6 +201,13 @@ class AboutReaderParent extends JSWindowActorParent {
|
|||
break;
|
||||
}
|
||||
|
||||
case "RedirectTo": {
|
||||
gCachedArticles.set(message.data.newURL, message.data.article);
|
||||
// This is setup as a query so we can navigate the page after we've
|
||||
// cached the relevant info in the parent.
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
this.callListeners(message);
|
||||
break;
|
||||
|
|
|
@ -377,6 +377,7 @@ class ContextMenuChild extends JSWindowActorChild {
|
|||
this.context.linkProtocol &&
|
||||
!(
|
||||
this.context.linkProtocol == "mailto" ||
|
||||
this.context.linkProtocol == "tel" ||
|
||||
this.context.linkProtocol == "javascript" ||
|
||||
this.context.linkProtocol == "news" ||
|
||||
this.context.linkProtocol == "snews"
|
||||
|
@ -872,6 +873,7 @@ class ContextMenuChild extends JSWindowActorChild {
|
|||
context.onLink = false;
|
||||
context.onLoadedImage = false;
|
||||
context.onMailtoLink = false;
|
||||
context.onTelLink = false;
|
||||
context.onMozExtLink = false;
|
||||
context.onNumeric = false;
|
||||
context.onPassword = false;
|
||||
|
@ -1157,6 +1159,7 @@ class ContextMenuChild extends JSWindowActorChild {
|
|||
context.linkTextStr = this._getLinkText();
|
||||
context.linkProtocol = this._getLinkProtocol();
|
||||
context.onMailtoLink = context.linkProtocol == "mailto";
|
||||
context.onTelLink = context.linkProtocol == "tel";
|
||||
context.onMozExtLink = context.linkProtocol == "moz-extension";
|
||||
context.onSaveableLink = this._isLinkSaveable(context.link);
|
||||
|
||||
|
|
|
@ -106,6 +106,9 @@
|
|||
<menuitem id="context-copyemail"
|
||||
data-l10n-id="main-context-menu-copy-email"
|
||||
oncommand="gContextMenu.copyEmail();"/>
|
||||
<menuitem id="context-copyphone"
|
||||
data-l10n-id="main-context-menu-copy-phone"
|
||||
oncommand="gContextMenu.copyPhone();"/>
|
||||
<menuitem id="context-copylink"
|
||||
data-l10n-id="main-context-menu-copy-link-simple"
|
||||
oncommand="gContextMenu.copyLink();"/>
|
||||
|
|
|
@ -43,25 +43,11 @@ var gDataNotificationInfoBar = {
|
|||
return;
|
||||
}
|
||||
|
||||
let brandBundle = document.getElementById("bundle_brand");
|
||||
let appName = brandBundle.getString("brandShortName");
|
||||
let vendorName = brandBundle.getString("vendorShortName");
|
||||
|
||||
let message = gNavigatorBundle.getFormattedString(
|
||||
"dataReportingNotification.message",
|
||||
[appName, vendorName]
|
||||
);
|
||||
|
||||
this._actionTaken = false;
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
label: gNavigatorBundle.getString(
|
||||
"dataReportingNotification.button.label"
|
||||
),
|
||||
accessKey: gNavigatorBundle.getString(
|
||||
"dataReportingNotification.button.accessKey"
|
||||
),
|
||||
"l10n-id": "data-reporting-notification-button",
|
||||
popup: null,
|
||||
callback: () => {
|
||||
this._actionTaken = true;
|
||||
|
@ -74,7 +60,9 @@ var gDataNotificationInfoBar = {
|
|||
gNotificationBox.appendNotification(
|
||||
this._DATA_REPORTING_NOTIFICATION,
|
||||
{
|
||||
label: message,
|
||||
label: {
|
||||
"l10n-id": "data-reporting-notification-message",
|
||||
},
|
||||
priority: gNotificationBox.PRIORITY_INFO_HIGH,
|
||||
eventCallback: event => {
|
||||
if (event == "removed") {
|
||||
|
|
|
@ -224,6 +224,7 @@ class nsContextMenu {
|
|||
this.onLink = context.onLink;
|
||||
this.onLoadedImage = context.onLoadedImage;
|
||||
this.onMailtoLink = context.onMailtoLink;
|
||||
this.onTelLink = context.onTelLink;
|
||||
this.onMozExtLink = context.onMozExtLink;
|
||||
this.onNumeric = context.onNumeric;
|
||||
this.onPassword = context.onPassword;
|
||||
|
@ -712,7 +713,10 @@ class nsContextMenu {
|
|||
|
||||
this.showItem(
|
||||
"context-bookmarklink",
|
||||
(this.onLink && !this.onMailtoLink && !this.onMozExtLink) ||
|
||||
(this.onLink &&
|
||||
!this.onMailtoLink &&
|
||||
!this.onTelLink &&
|
||||
!this.onMozExtLink) ||
|
||||
this.onPlainTextLink
|
||||
);
|
||||
this.showItem("context-keywordfield", this.shouldShowAddKeyword());
|
||||
|
@ -836,14 +840,25 @@ class nsContextMenu {
|
|||
// Copy email link depends on whether we're on an email link.
|
||||
this.showItem("context-copyemail", this.onMailtoLink);
|
||||
|
||||
// Copy phone link depends on whether we're on a phone link.
|
||||
this.showItem("context-copyphone", this.onTelLink);
|
||||
|
||||
// Copy link location depends on whether we're on a non-mailto link.
|
||||
this.showItem("context-copylink", this.onLink && !this.onMailtoLink);
|
||||
this.showItem(
|
||||
"context-copylink",
|
||||
this.onLink && !this.onMailtoLink && !this.onTelLink
|
||||
);
|
||||
|
||||
let copyLinkSeparator = document.getElementById("context-sep-copylink");
|
||||
// Show "Copy Link" and "Copy" with no divider, and "copy link" and "Send link to Device" with no divider between.
|
||||
// Other cases will show a divider.
|
||||
copyLinkSeparator.toggleAttribute(
|
||||
"ensureHidden",
|
||||
this.onLink && !this.onMailtoLink && !this.onImage && this.syncItemsShown
|
||||
this.onLink &&
|
||||
!this.onMailtoLink &&
|
||||
!this.onTelLink &&
|
||||
!this.onImage &&
|
||||
this.syncItemsShown
|
||||
);
|
||||
|
||||
this.showItem("context-copyvideourl", this.onVideo);
|
||||
|
@ -1923,6 +1938,26 @@ class nsContextMenu {
|
|||
clipboard.copyString(addresses);
|
||||
}
|
||||
|
||||
// Extract phone and put it on clipboard
|
||||
copyPhone() {
|
||||
// Copies the phone number only. We won't be doing any complex parsing
|
||||
var url = this.linkURL;
|
||||
var phone = url.substr(4);
|
||||
|
||||
// Let's try to unescape it using a character set
|
||||
// in case the phone number is not ASCII.
|
||||
try {
|
||||
phone = Services.textToSubURI.unEscapeURIForUI(phone);
|
||||
} catch (ex) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
|
||||
Ci.nsIClipboardHelper
|
||||
);
|
||||
clipboard.copyString(phone);
|
||||
}
|
||||
|
||||
copyLink() {
|
||||
// If we're in a view source tab, remove the view-source: prefix
|
||||
let linkURL = this.linkURL.replace(/^view-source:/, "");
|
||||
|
|
|
@ -234,6 +234,19 @@ add_task(async function test_mailto() {
|
|||
]);
|
||||
});
|
||||
|
||||
add_task(async function test_tel() {
|
||||
await test_contextmenu("#test-tel", [
|
||||
"context-copyphone",
|
||||
true,
|
||||
"---",
|
||||
null,
|
||||
"context-searchselect",
|
||||
true,
|
||||
"context-searchselect-private",
|
||||
true,
|
||||
]);
|
||||
});
|
||||
|
||||
add_task(async function test_image() {
|
||||
for (let selector of ["#test-image", "#test-svg-image"]) {
|
||||
await test_contextmenu(
|
||||
|
|
|
@ -19,6 +19,7 @@ document.getElementById("shadow-host-in-link").attachShadow({ mode: "closed" }).
|
|||
"<span>Click the monkey!</span>";
|
||||
</script>
|
||||
<a id="test-mailto" href="mailto:codemonkey@mozilla.com">Mail the monkey!</a><br>
|
||||
<a id="test-tel" href="tel:555-123-4567">Call random number!</a><br>
|
||||
<input id="test-input"><br>
|
||||
<img id="test-image" src="ctxmenu-image.png">
|
||||
<svg>
|
||||
|
|
|
@ -5,4 +5,3 @@
|
|||
brandShorterName=Firefox
|
||||
brandShortName=Firefox Developer Edition
|
||||
brandFullName=Firefox Developer Edition
|
||||
vendorShortName=Mozilla
|
||||
|
|
|
@ -5,4 +5,3 @@
|
|||
brandShorterName=Nightly
|
||||
brandShortName=Nightly
|
||||
brandFullName=Firefox Nightly
|
||||
vendorShortName=Mozilla
|
||||
|
|
|
@ -5,4 +5,3 @@
|
|||
brandShorterName=Firefox
|
||||
brandShortName=Firefox
|
||||
brandFullName=Mozilla Firefox
|
||||
vendorShortName=Mozilla
|
||||
|
|
|
@ -5,4 +5,3 @@
|
|||
brandShorterName=Nightly
|
||||
brandShortName=Nightly
|
||||
brandFullName=Nightly
|
||||
vendorShortName=Mozilla
|
||||
|
|
|
@ -778,97 +778,6 @@ let JSWINDOWACTORS = {
|
|||
},
|
||||
};
|
||||
|
||||
(function earlyBlankFirstPaint() {
|
||||
let startTime = Cu.now();
|
||||
if (
|
||||
AppConstants.platform == "macosx" ||
|
||||
Services.startup.wasSilentlyStarted ||
|
||||
!Services.prefs.getBoolPref("browser.startup.blankWindow", false)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Until bug 1450626 and bug 1488384 are fixed, skip the blank window when
|
||||
// using a non-default theme.
|
||||
if (
|
||||
!Services.startup.showedPreXULSkeletonUI &&
|
||||
Services.prefs.getCharPref(
|
||||
"extensions.activeThemeID",
|
||||
"default-theme@mozilla.org"
|
||||
) != "default-theme@mozilla.org"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let store = Services.xulStore;
|
||||
let getValue = attr =>
|
||||
store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr);
|
||||
let width = getValue("width");
|
||||
let height = getValue("height");
|
||||
|
||||
// The clean profile case isn't handled yet. Return early for now.
|
||||
if (!width || !height) {
|
||||
return;
|
||||
}
|
||||
|
||||
let browserWindowFeatures =
|
||||
"chrome,all,dialog=no,extrachrome,menubar,resizable,scrollbars,status," +
|
||||
"location,toolbar,personalbar";
|
||||
let win = Services.ww.openWindow(
|
||||
null,
|
||||
"about:blank",
|
||||
null,
|
||||
browserWindowFeatures,
|
||||
null
|
||||
);
|
||||
|
||||
// Hide the titlebar if the actual browser window will draw in it.
|
||||
let hiddenTitlebar = Services.appinfo.drawInTitlebar;
|
||||
if (hiddenTitlebar) {
|
||||
win.windowUtils.setChromeMargin(0, 2, 2, 2);
|
||||
}
|
||||
|
||||
let docElt = win.document.documentElement;
|
||||
docElt.setAttribute("screenX", getValue("screenX"));
|
||||
docElt.setAttribute("screenY", getValue("screenY"));
|
||||
|
||||
// The sizemode="maximized" attribute needs to be set before first paint.
|
||||
let sizemode = getValue("sizemode");
|
||||
if (sizemode == "maximized") {
|
||||
docElt.setAttribute("sizemode", sizemode);
|
||||
|
||||
// Set the size to use when the user leaves the maximized mode.
|
||||
// The persisted size is the outer size, but the height/width
|
||||
// attributes set the inner size.
|
||||
let appWin = win.docShell.treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIAppWindow);
|
||||
height -= appWin.outerToInnerHeightDifferenceInCSSPixels;
|
||||
width -= appWin.outerToInnerWidthDifferenceInCSSPixels;
|
||||
docElt.setAttribute("height", height);
|
||||
docElt.setAttribute("width", width);
|
||||
} else {
|
||||
// Setting the size of the window in the features string instead of here
|
||||
// causes the window to grow by the size of the titlebar.
|
||||
win.resizeTo(width, height);
|
||||
}
|
||||
|
||||
// Set this before showing the window so that graphics code can use it to
|
||||
// decide to skip some expensive code paths (eg. starting the GPU process).
|
||||
docElt.setAttribute("windowtype", "navigator:blank");
|
||||
|
||||
// The window becomes visible after OnStopRequest, so make this happen now.
|
||||
win.stop();
|
||||
|
||||
ChromeUtils.addProfilerMarker("earlyBlankFirstPaint", startTime);
|
||||
win.openTime = Cu.now();
|
||||
|
||||
let { TelemetryTimestamps } = ChromeUtils.import(
|
||||
"resource://gre/modules/TelemetryTimestamps.jsm"
|
||||
);
|
||||
TelemetryTimestamps.add("blankWindowShown");
|
||||
})();
|
||||
|
||||
XPCOMUtils.defineLazyGetter(
|
||||
this,
|
||||
"WeaveService",
|
||||
|
@ -1187,6 +1096,9 @@ BrowserGlue.prototype = {
|
|||
// Allow certain viewable internally types to be opened from downloads.
|
||||
DownloadsViewableInternally.register();
|
||||
|
||||
break;
|
||||
case "app-startup":
|
||||
this._earlyBlankFirstPaint(subject);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -1524,6 +1436,104 @@ BrowserGlue.prototype = {
|
|||
);
|
||||
},
|
||||
|
||||
_earlyBlankFirstPaint(cmdLine) {
|
||||
let startTime = Cu.now();
|
||||
if (
|
||||
AppConstants.platform == "macosx" ||
|
||||
Services.startup.wasSilentlyStarted ||
|
||||
!Services.prefs.getBoolPref("browser.startup.blankWindow", false)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Until bug 1450626 and bug 1488384 are fixed, skip the blank window when
|
||||
// using a non-default theme.
|
||||
if (
|
||||
!Services.startup.showedPreXULSkeletonUI &&
|
||||
Services.prefs.getCharPref(
|
||||
"extensions.activeThemeID",
|
||||
"default-theme@mozilla.org"
|
||||
) != "default-theme@mozilla.org"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let store = Services.xulStore;
|
||||
let getValue = attr =>
|
||||
store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr);
|
||||
let width = getValue("width");
|
||||
let height = getValue("height");
|
||||
|
||||
// The clean profile case isn't handled yet. Return early for now.
|
||||
if (!width || !height) {
|
||||
return;
|
||||
}
|
||||
|
||||
let browserWindowFeatures =
|
||||
"chrome,all,dialog=no,extrachrome,menubar,resizable,scrollbars,status," +
|
||||
"location,toolbar,personalbar";
|
||||
// This needs to be set when opening the window to ensure that the AppUserModelID
|
||||
// is set correctly on Windows. Without it, initial launches with `-private-window`
|
||||
// will show up under the regular Firefox taskbar icon first, and then switch
|
||||
// to the Private Browsing icon shortly thereafter.
|
||||
if (cmdLine.findFlag("private-window", false) != -1) {
|
||||
browserWindowFeatures += ",private";
|
||||
}
|
||||
let win = Services.ww.openWindow(
|
||||
null,
|
||||
"about:blank",
|
||||
null,
|
||||
browserWindowFeatures,
|
||||
null
|
||||
);
|
||||
|
||||
// Hide the titlebar if the actual browser window will draw in it.
|
||||
let hiddenTitlebar = Services.appinfo.drawInTitlebar;
|
||||
if (hiddenTitlebar) {
|
||||
win.windowUtils.setChromeMargin(0, 2, 2, 2);
|
||||
}
|
||||
|
||||
let docElt = win.document.documentElement;
|
||||
docElt.setAttribute("screenX", getValue("screenX"));
|
||||
docElt.setAttribute("screenY", getValue("screenY"));
|
||||
|
||||
// The sizemode="maximized" attribute needs to be set before first paint.
|
||||
let sizemode = getValue("sizemode");
|
||||
if (sizemode == "maximized") {
|
||||
docElt.setAttribute("sizemode", sizemode);
|
||||
|
||||
// Set the size to use when the user leaves the maximized mode.
|
||||
// The persisted size is the outer size, but the height/width
|
||||
// attributes set the inner size.
|
||||
let appWin = win.docShell.treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIAppWindow);
|
||||
height -= appWin.outerToInnerHeightDifferenceInCSSPixels;
|
||||
width -= appWin.outerToInnerWidthDifferenceInCSSPixels;
|
||||
docElt.setAttribute("height", height);
|
||||
docElt.setAttribute("width", width);
|
||||
} else {
|
||||
// Setting the size of the window in the features string instead of here
|
||||
// causes the window to grow by the size of the titlebar.
|
||||
win.resizeTo(width, height);
|
||||
}
|
||||
|
||||
// Set this before showing the window so that graphics code can use it to
|
||||
// decide to skip some expensive code paths (eg. starting the GPU process).
|
||||
docElt.setAttribute("windowtype", "navigator:blank");
|
||||
|
||||
// The window becomes visible after OnStopRequest, so make this happen now.
|
||||
win.stop();
|
||||
|
||||
ChromeUtils.addProfilerMarker("earlyBlankFirstPaint", startTime);
|
||||
win.openTime = Cu.now();
|
||||
|
||||
let { TelemetryTimestamps } = ChromeUtils.import(
|
||||
"resource://gre/modules/TelemetryTimestamps.jsm"
|
||||
);
|
||||
TelemetryTimestamps.add("blankWindowShown");
|
||||
},
|
||||
|
||||
_firstWindowTelemetry(aWindow) {
|
||||
let scaling = aWindow.devicePixelRatio * 100;
|
||||
try {
|
||||
|
|
|
@ -33,6 +33,22 @@ hr {
|
|||
line-height: 1.23em;
|
||||
}
|
||||
|
||||
.header_small {
|
||||
margin: 12px 0 8px;
|
||||
font-size: 0.84em;
|
||||
line-height: 1.07em;
|
||||
}
|
||||
|
||||
.header_flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header_center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 8px 0;
|
||||
font-size: 0.84em;
|
||||
|
|
|
@ -417,6 +417,22 @@ hr {
|
|||
line-height: 1.23em;
|
||||
}
|
||||
|
||||
.header_small {
|
||||
margin: 12px 0 8px;
|
||||
font-size: 0.84em;
|
||||
line-height: 1.07em;
|
||||
}
|
||||
|
||||
.header_flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header_center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 8px 0;
|
||||
font-size: 0.84em;
|
||||
|
@ -2070,6 +2086,7 @@ body.theme_dark .stp_popular_topics .stp_popular_topic .stp_popular_topic_link {
|
|||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.stp_article_list .stp_article_list_saved_article,
|
||||
.stp_article_list .stp_article_list_link {
|
||||
display: flex;
|
||||
border-radius: 4px;
|
||||
|
@ -2091,6 +2108,10 @@ body.theme_dark .stp_article_list .stp_article_list_link:hover {
|
|||
margin-inline-end: 8px;
|
||||
background-color: #ECECEE;
|
||||
}
|
||||
.stp_article_list .stp_article_list_thumb:-moz-broken,
|
||||
.stp_article_list .stp_article_list_thumb_placeholder:-moz-broken {
|
||||
display: none;
|
||||
}
|
||||
.stp_article_list .stp_article_list_header {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
|
@ -2098,6 +2119,11 @@ body.theme_dark .stp_article_list .stp_article_list_link:hover {
|
|||
line-height: 1.27em;
|
||||
color: #15141A;
|
||||
margin: 0 0 4px;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
body.theme_dark .stp_article_list .stp_article_list_header {
|
||||
color: #FBFBFE;
|
||||
|
@ -2219,4 +2245,16 @@ body.stp_signup_body {
|
|||
margin-inline-start: 16px;
|
||||
}
|
||||
|
||||
.stp_panel_error {
|
||||
margin: 23px 0 32px;
|
||||
}
|
||||
.stp_panel_error .stp_panel_error_icon {
|
||||
float: inline-start;
|
||||
margin-block: 6px 16px;
|
||||
margin-inline: 7px 17px;
|
||||
background-image: url(../img/pocketerror@1x.png);
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=main.compiled.css.map */
|
||||
|
|
|
@ -14,3 +14,4 @@
|
|||
@import "../js/components/Header/Header";
|
||||
@import "../js/components/Button/Button";
|
||||
@import "../js/components/Signup/Signup";
|
||||
@import "../js/components/Saved/Saved";
|
||||
|
|
|
@ -4,29 +4,66 @@
|
|||
|
||||
import React from "react";
|
||||
|
||||
function ArticleUrl(props) {
|
||||
// We turn off the link if we're either a saved article, or if the url doesn't exist.
|
||||
if (props.savedArticle || !props.url) {
|
||||
return (
|
||||
<div className="stp_article_list_saved_article">{props.children}</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<a className="stp_article_list_link" href={props.url}>
|
||||
{props.children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
function Article(props) {
|
||||
function encodeThumbnail(rawSource) {
|
||||
return rawSource
|
||||
? `https://img-getpocket.cdn.mozilla.net/80x80/filters:format(jpeg):quality(60):no_upscale():strip_exif()/${encodeURIComponent(
|
||||
rawSource
|
||||
)}`
|
||||
: null;
|
||||
}
|
||||
|
||||
const { article } = props;
|
||||
const url = article.url || article.resolved_url;
|
||||
// Using array notation because there is a key titled `1` (`images` is an object)
|
||||
const thumbnail =
|
||||
article.thumbnail ||
|
||||
encodeThumbnail(article?.top_image_url || article?.images?.["1"]?.src);
|
||||
const alt = article.alt || "thumbnail image";
|
||||
const title = article.title || article.resolved_title;
|
||||
// Sometimes domain_metadata is not there, depending on the source.
|
||||
const publisher =
|
||||
article.publisher ||
|
||||
article.domain_metadata?.name ||
|
||||
article.resolved_domain;
|
||||
return (
|
||||
<li className="stp_article_list_item">
|
||||
<ArticleUrl url={url} savedArticle={props.savedArticle}>
|
||||
<>
|
||||
{thumbnail ? (
|
||||
<img className="stp_article_list_thumb" src={thumbnail} alt={alt} />
|
||||
) : (
|
||||
<div className="stp_article_list_thumb_placeholder" />
|
||||
)}
|
||||
<div className="stp_article_list_meta">
|
||||
<header className="stp_article_list_header">{title}</header>
|
||||
<p className="stp_article_list_publisher">{publisher}</p>
|
||||
</div>
|
||||
</>
|
||||
</ArticleUrl>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
function ArticleList(props) {
|
||||
return (
|
||||
<ul className="stp_article_list">
|
||||
{props.articles?.map(article => (
|
||||
<li className="stp_article_list_item">
|
||||
<a className="stp_article_list_link" href={article.url}>
|
||||
{article.thumbnail ? (
|
||||
<img
|
||||
className="stp_article_list_thumb"
|
||||
src={article.thumbnail}
|
||||
alt={article.alt}
|
||||
/>
|
||||
) : (
|
||||
<div className="stp_article_list_thumb_placeholder" />
|
||||
)}
|
||||
<div className="stp_article_list_meta">
|
||||
<header className="stp_article_list_header">
|
||||
{article.title}
|
||||
</header>
|
||||
<p className="stp_article_list_publisher">{article.publisher}</p>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<Article article={article} savedArticle={props.savedArticle} />
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
.stp_article_list_saved_article,
|
||||
.stp_article_list_link {
|
||||
display: flex;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.stp_article_list_link {
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background-color: #ECECEE;
|
||||
|
@ -24,6 +27,9 @@
|
|||
border-radius: 4px;
|
||||
margin-inline-end: 8px;
|
||||
background-color: #ECECEE;
|
||||
&:-moz-broken {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.stp_article_list_header {
|
||||
|
@ -34,6 +40,12 @@
|
|||
color: #15141A;
|
||||
margin: 0 0 4px;
|
||||
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
|
||||
@include theme_dark {
|
||||
color: #FBFBFE;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ function Button(props) {
|
|||
return (
|
||||
<a
|
||||
href={props.url}
|
||||
onClick={props.onClick}
|
||||
className={`stp_button${props?.style && ` stp_button_${props.style}`}`}
|
||||
>
|
||||
{props.children}
|
||||
|
|
|
@ -9,14 +9,6 @@ import PopularTopics from "../PopularTopics/PopularTopics";
|
|||
import Button from "../Button/Button";
|
||||
import panelMessaging from "../../messages";
|
||||
|
||||
function encodeThumbnail(rawSource) {
|
||||
return rawSource
|
||||
? `https://img-getpocket.cdn.mozilla.net/80x80/filters:format(jpeg):quality(60):no_upscale():strip_exif()/${encodeURIComponent(
|
||||
rawSource
|
||||
)}`
|
||||
: null;
|
||||
}
|
||||
|
||||
function Home(props) {
|
||||
const { locale, topics, pockethost, hideRecentSaves } = props;
|
||||
const [{ articles, status }, setArticlesState] = useState({
|
||||
|
@ -52,16 +44,7 @@ function Home(props) {
|
|||
}
|
||||
|
||||
setArticlesState({
|
||||
articles: data.map(item => ({
|
||||
url: item.resolved_url,
|
||||
// Using array notation because there is a key titled `1` (`images` is an object)
|
||||
thumbnail: encodeThumbnail(
|
||||
item?.top_image_url || item?.images?.["1"]?.src
|
||||
),
|
||||
alt: "thumbnail image",
|
||||
title: item.resolved_title,
|
||||
publisher: item.domain_metadata?.name,
|
||||
})),
|
||||
articles: data,
|
||||
status: "success",
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,31 +2,165 @@
|
|||
* 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 React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Header from "../Header/Header";
|
||||
import Button from "../Button/Button";
|
||||
import ArticleList from "../ArticleList/ArticleList";
|
||||
import panelMessaging from "../../messages";
|
||||
|
||||
function Saved(props) {
|
||||
const { similarRecs, savedStory } = props;
|
||||
const { locale } = props;
|
||||
// savedStatus can be success, loading, or error.
|
||||
const [
|
||||
{ savedStatus, savedErrorId, itemId },
|
||||
setSavedStatusState,
|
||||
] = useState({ savedStatus: "loading" });
|
||||
// removedStatus can be removed, removing, or error.
|
||||
const [
|
||||
{ removedStatus, removedErrorMessage },
|
||||
setRemovedStatusState,
|
||||
] = useState({});
|
||||
const [savedStory, setSavedStoryState] = useState();
|
||||
const [similarRecs, setSimilarRecsState] = useState();
|
||||
|
||||
function removeItem(event) {
|
||||
event.preventDefault();
|
||||
setRemovedStatusState({ removedStatus: "removing" });
|
||||
panelMessaging.sendMessage(
|
||||
"PKT_deleteItem",
|
||||
{
|
||||
itemId,
|
||||
},
|
||||
function(resp) {
|
||||
const { data } = resp;
|
||||
if (data.status == "success") {
|
||||
setRemovedStatusState({ removedStatus: "removed" });
|
||||
} else if (data.status == "error") {
|
||||
let errorMessage = "";
|
||||
// The server returns English error messages, so in the case of
|
||||
// non English, we do our best with a generic translated error.
|
||||
if (data.error.message && locale?.startsWith("en")) {
|
||||
errorMessage = data.error.message;
|
||||
}
|
||||
setRemovedStatusState({
|
||||
removedStatus: "error",
|
||||
removedErrorMessage: errorMessage,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Wait confirmation of save before flipping to final saved state
|
||||
panelMessaging.addMessageListener("PKT_saveLink", function(resp) {
|
||||
const { data } = resp;
|
||||
if (data.status == "error") {
|
||||
// Use localizedKey or fallback to a generic catch all error.
|
||||
setSavedStatusState({
|
||||
savedStatus: "error",
|
||||
savedErrorId:
|
||||
data?.error?.localizedKey || "pocket-panel-saved-error-generic",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Success, so no localized error id needed.
|
||||
setSavedStatusState({
|
||||
savedStatus: "success",
|
||||
itemId: data.item.item_id,
|
||||
savedErrorId: "",
|
||||
});
|
||||
});
|
||||
|
||||
panelMessaging.addMessageListener("PKT_renderSavedStory", function(resp) {
|
||||
setSavedStoryState(resp?.data?.item_preview);
|
||||
});
|
||||
|
||||
panelMessaging.addMessageListener("PKT_renderItemRecs", function(resp) {
|
||||
const { data } = resp;
|
||||
setSimilarRecsState(data?.recommendations?.map(rec => rec.item));
|
||||
});
|
||||
|
||||
// tell back end we're ready
|
||||
panelMessaging.sendMessage("PKT_show_saved");
|
||||
}, []);
|
||||
|
||||
if (savedStatus === "error") {
|
||||
return (
|
||||
<div className="stp_panel_container">
|
||||
<div className="stp_panel stp_panel_error">
|
||||
<div className="stp_panel_error_icon" />
|
||||
<h3
|
||||
className="header_large"
|
||||
data-l10n-id="pocket-panel-saved-error-not-saved"
|
||||
/>
|
||||
<p data-l10n-id={savedErrorId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="stp_panel_container">
|
||||
<div className="stp_panel stp_panel_home">
|
||||
<div className="stp_panel stp_panel_saved">
|
||||
<Header>
|
||||
<a>
|
||||
<Button style="primary">
|
||||
<span data-l10n-id="pocket-panel-header-my-list"></span>
|
||||
</a>
|
||||
</Button>
|
||||
</Header>
|
||||
<hr />
|
||||
{savedStory && (
|
||||
{!removedStatus && savedStatus === "success" && (
|
||||
<>
|
||||
<p data-l10n-id="pocket-panel-saved-page-saved"></p>
|
||||
<ArticleList articles={[savedStory]} />
|
||||
<span data-l10n-id="pocket-panel-button-add-tags"></span>
|
||||
<span data-l10n-id="pocket-panel-saved-remove-page"></span>
|
||||
<h3 className="header_large header_flex">
|
||||
<span data-l10n-id="pocket-panel-saved-page-saved-b" />
|
||||
<Button style="text" url="google.com" onClick={removeItem}>
|
||||
<span data-l10n-id="pocket-panel-button-remove"></span>
|
||||
</Button>
|
||||
</h3>
|
||||
{savedStory && (
|
||||
<ArticleList articles={[savedStory]} savedArticle={true} />
|
||||
)}
|
||||
<h3
|
||||
className="header_small"
|
||||
data-l10n-id="pocket-panel-cta-add-tags"
|
||||
/>
|
||||
{similarRecs?.length && locale?.startsWith("en") && (
|
||||
<>
|
||||
<hr />
|
||||
<h3 className="header_medium">Similar Stories</h3>
|
||||
<ArticleList articles={similarRecs} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{savedStatus === "loading" && (
|
||||
<h3
|
||||
className="header_large"
|
||||
data-l10n-id="pocket-panel-saved-saving-tags"
|
||||
/>
|
||||
)}
|
||||
{removedStatus === "removing" && (
|
||||
<h3
|
||||
className="header_large header_center"
|
||||
data-l10n-id="pocket-panel-saved-processing-remove"
|
||||
/>
|
||||
)}
|
||||
{removedStatus === "removed" && (
|
||||
<h3
|
||||
className="header_large header_center"
|
||||
data-l10n-id="pocket-panel-saved-removed"
|
||||
/>
|
||||
)}
|
||||
{removedStatus === "error" && (
|
||||
<>
|
||||
<h3
|
||||
className="header_large"
|
||||
data-l10n-id="pocket-panel-saved-error-remove"
|
||||
/>
|
||||
{removedErrorMessage && <p>{removedErrorMessage}</p>}
|
||||
</>
|
||||
)}
|
||||
<hr />
|
||||
{similarRecs?.length && <ArticleList articles={similarRecs} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
.stp_panel_error {
|
||||
margin: 23px 0 32px;
|
||||
.stp_panel_error_icon {
|
||||
float: inline-start;
|
||||
margin-block: 6px 16px;
|
||||
margin-inline: 7px 17px;
|
||||
background-image: url(../img/pocketerror@1x.png);
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
}
|
||||
}
|
|
@ -55,27 +55,62 @@ function Header(props) {
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
function ArticleList(props) {
|
||||
return /*#__PURE__*/react.createElement("ul", {
|
||||
className: "stp_article_list"
|
||||
}, props.articles?.map(article => /*#__PURE__*/react.createElement("li", {
|
||||
className: "stp_article_list_item"
|
||||
}, /*#__PURE__*/react.createElement("a", {
|
||||
function ArticleUrl(props) {
|
||||
// We turn off the link if we're either a saved article, or if the url doesn't exist.
|
||||
if (props.savedArticle || !props.url) {
|
||||
return /*#__PURE__*/react.createElement("div", {
|
||||
className: "stp_article_list_saved_article"
|
||||
}, props.children);
|
||||
}
|
||||
|
||||
return /*#__PURE__*/react.createElement("a", {
|
||||
className: "stp_article_list_link",
|
||||
href: article.url
|
||||
}, article.thumbnail ? /*#__PURE__*/react.createElement("img", {
|
||||
href: props.url
|
||||
}, props.children);
|
||||
}
|
||||
|
||||
function Article(props) {
|
||||
function encodeThumbnail(rawSource) {
|
||||
return rawSource ? `https://img-getpocket.cdn.mozilla.net/80x80/filters:format(jpeg):quality(60):no_upscale():strip_exif()/${encodeURIComponent(rawSource)}` : null;
|
||||
}
|
||||
|
||||
const {
|
||||
article
|
||||
} = props;
|
||||
const url = article.url || article.resolved_url; // Using array notation because there is a key titled `1` (`images` is an object)
|
||||
|
||||
const thumbnail = article.thumbnail || encodeThumbnail(article?.top_image_url || article?.images?.["1"]?.src);
|
||||
const alt = article.alt || "thumbnail image";
|
||||
const title = article.title || article.resolved_title; // Sometimes domain_metadata is not there, depending on the source.
|
||||
|
||||
const publisher = article.publisher || article.domain_metadata?.name || article.resolved_domain;
|
||||
return /*#__PURE__*/react.createElement("li", {
|
||||
className: "stp_article_list_item"
|
||||
}, /*#__PURE__*/react.createElement(ArticleUrl, {
|
||||
url: url,
|
||||
savedArticle: props.savedArticle
|
||||
}, /*#__PURE__*/react.createElement(react.Fragment, null, thumbnail ? /*#__PURE__*/react.createElement("img", {
|
||||
className: "stp_article_list_thumb",
|
||||
src: article.thumbnail,
|
||||
alt: article.alt
|
||||
src: thumbnail,
|
||||
alt: alt
|
||||
}) : /*#__PURE__*/react.createElement("div", {
|
||||
className: "stp_article_list_thumb_placeholder"
|
||||
}), /*#__PURE__*/react.createElement("div", {
|
||||
className: "stp_article_list_meta"
|
||||
}, /*#__PURE__*/react.createElement("header", {
|
||||
className: "stp_article_list_header"
|
||||
}, article.title), /*#__PURE__*/react.createElement("p", {
|
||||
}, title), /*#__PURE__*/react.createElement("p", {
|
||||
className: "stp_article_list_publisher"
|
||||
}, article.publisher))))));
|
||||
}, publisher)))));
|
||||
}
|
||||
|
||||
function ArticleList(props) {
|
||||
return /*#__PURE__*/react.createElement("ul", {
|
||||
className: "stp_article_list"
|
||||
}, props.articles?.map(article => /*#__PURE__*/react.createElement(Article, {
|
||||
article: article,
|
||||
savedArticle: props.savedArticle
|
||||
})));
|
||||
}
|
||||
|
||||
/* harmony default export */ const ArticleList_ArticleList = (ArticleList);
|
||||
|
@ -107,6 +142,7 @@ function PopularTopics(props) {
|
|||
function Button(props) {
|
||||
return /*#__PURE__*/react.createElement("a", {
|
||||
href: props.url,
|
||||
onClick: props.onClick,
|
||||
className: `stp_button${props?.style && ` stp_button_${props.style}`}`
|
||||
}, props.children);
|
||||
}
|
||||
|
@ -177,10 +213,6 @@ var pktPanelMessaging = {
|
|||
|
||||
|
||||
|
||||
function encodeThumbnail(rawSource) {
|
||||
return rawSource ? `https://img-getpocket.cdn.mozilla.net/80x80/filters:format(jpeg):quality(60):no_upscale():strip_exif()/${encodeURIComponent(rawSource)}` : null;
|
||||
}
|
||||
|
||||
function Home(props) {
|
||||
const {
|
||||
locale,
|
||||
|
@ -220,14 +252,7 @@ function Home(props) {
|
|||
}
|
||||
|
||||
setArticlesState({
|
||||
articles: data.map(item => ({
|
||||
url: item.resolved_url,
|
||||
// Using array notation because there is a key titled `1` (`images` is an object)
|
||||
thumbnail: encodeThumbnail(item?.top_image_url || item?.images?.["1"]?.src),
|
||||
alt: "thumbnail image",
|
||||
title: item.resolved_title,
|
||||
publisher: item.domain_metadata?.name
|
||||
})),
|
||||
articles: data,
|
||||
status: "success"
|
||||
});
|
||||
});
|
||||
|
@ -580,28 +605,152 @@ var SignupOverlay = function (options) {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
function Saved(props) {
|
||||
const {
|
||||
similarRecs,
|
||||
savedStory
|
||||
} = props;
|
||||
locale
|
||||
} = props; // savedStatus can be success, loading, or error.
|
||||
|
||||
const [{
|
||||
savedStatus,
|
||||
savedErrorId,
|
||||
itemId
|
||||
}, setSavedStatusState] = (0,react.useState)({
|
||||
savedStatus: "loading"
|
||||
}); // removedStatus can be removed, removing, or error.
|
||||
|
||||
const [{
|
||||
removedStatus,
|
||||
removedErrorMessage
|
||||
}, setRemovedStatusState] = (0,react.useState)({});
|
||||
const [savedStory, setSavedStoryState] = (0,react.useState)();
|
||||
const [similarRecs, setSimilarRecsState] = (0,react.useState)();
|
||||
|
||||
function removeItem(event) {
|
||||
event.preventDefault();
|
||||
setRemovedStatusState({
|
||||
removedStatus: "removing"
|
||||
});
|
||||
messages.sendMessage("PKT_deleteItem", {
|
||||
itemId
|
||||
}, function (resp) {
|
||||
const {
|
||||
data
|
||||
} = resp;
|
||||
|
||||
if (data.status == "success") {
|
||||
setRemovedStatusState({
|
||||
removedStatus: "removed"
|
||||
});
|
||||
} else if (data.status == "error") {
|
||||
let errorMessage = ""; // The server returns English error messages, so in the case of
|
||||
// non English, we do our best with a generic translated error.
|
||||
|
||||
if (data.error.message && locale?.startsWith("en")) {
|
||||
errorMessage = data.error.message;
|
||||
}
|
||||
|
||||
setRemovedStatusState({
|
||||
removedStatus: "error",
|
||||
removedErrorMessage: errorMessage
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
(0,react.useEffect)(() => {
|
||||
// Wait confirmation of save before flipping to final saved state
|
||||
messages.addMessageListener("PKT_saveLink", function (resp) {
|
||||
const {
|
||||
data
|
||||
} = resp;
|
||||
|
||||
if (data.status == "error") {
|
||||
// Use localizedKey or fallback to a generic catch all error.
|
||||
setSavedStatusState({
|
||||
savedStatus: "error",
|
||||
savedErrorId: data?.error?.localizedKey || "pocket-panel-saved-error-generic"
|
||||
});
|
||||
return;
|
||||
} // Success, so no localized error id needed.
|
||||
|
||||
|
||||
setSavedStatusState({
|
||||
savedStatus: "success",
|
||||
itemId: data.item.item_id,
|
||||
savedErrorId: ""
|
||||
});
|
||||
});
|
||||
messages.addMessageListener("PKT_renderSavedStory", function (resp) {
|
||||
setSavedStoryState(resp?.data?.item_preview);
|
||||
});
|
||||
messages.addMessageListener("PKT_renderItemRecs", function (resp) {
|
||||
const {
|
||||
data
|
||||
} = resp;
|
||||
setSimilarRecsState(data?.recommendations?.map(rec => rec.item));
|
||||
}); // tell back end we're ready
|
||||
|
||||
messages.sendMessage("PKT_show_saved");
|
||||
}, []);
|
||||
|
||||
if (savedStatus === "error") {
|
||||
return /*#__PURE__*/react.createElement("div", {
|
||||
className: "stp_panel_container"
|
||||
}, /*#__PURE__*/react.createElement("div", {
|
||||
className: "stp_panel stp_panel_error"
|
||||
}, /*#__PURE__*/react.createElement("div", {
|
||||
className: "stp_panel_error_icon"
|
||||
}), /*#__PURE__*/react.createElement("h3", {
|
||||
className: "header_large",
|
||||
"data-l10n-id": "pocket-panel-saved-error-not-saved"
|
||||
}), /*#__PURE__*/react.createElement("p", {
|
||||
"data-l10n-id": savedErrorId
|
||||
})));
|
||||
}
|
||||
|
||||
return /*#__PURE__*/react.createElement("div", {
|
||||
className: "stp_panel_container"
|
||||
}, /*#__PURE__*/react.createElement("div", {
|
||||
className: "stp_panel stp_panel_home"
|
||||
}, /*#__PURE__*/react.createElement(Header_Header, null, /*#__PURE__*/react.createElement("a", null, /*#__PURE__*/react.createElement("span", {
|
||||
className: "stp_panel stp_panel_saved"
|
||||
}, /*#__PURE__*/react.createElement(Header_Header, null, /*#__PURE__*/react.createElement(Button_Button, {
|
||||
style: "primary"
|
||||
}, /*#__PURE__*/react.createElement("span", {
|
||||
"data-l10n-id": "pocket-panel-header-my-list"
|
||||
}))), /*#__PURE__*/react.createElement("hr", null), savedStory && /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("p", {
|
||||
"data-l10n-id": "pocket-panel-saved-page-saved"
|
||||
}), /*#__PURE__*/react.createElement(ArticleList_ArticleList, {
|
||||
articles: [savedStory]
|
||||
}), /*#__PURE__*/react.createElement("span", {
|
||||
"data-l10n-id": "pocket-panel-button-add-tags"
|
||||
}), /*#__PURE__*/react.createElement("span", {
|
||||
"data-l10n-id": "pocket-panel-saved-remove-page"
|
||||
})), /*#__PURE__*/react.createElement("hr", null), similarRecs?.length && /*#__PURE__*/react.createElement(ArticleList_ArticleList, {
|
||||
}))), /*#__PURE__*/react.createElement("hr", null), !removedStatus && savedStatus === "success" && /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("h3", {
|
||||
className: "header_large header_flex"
|
||||
}, /*#__PURE__*/react.createElement("span", {
|
||||
"data-l10n-id": "pocket-panel-saved-page-saved-b"
|
||||
}), /*#__PURE__*/react.createElement(Button_Button, {
|
||||
style: "text",
|
||||
url: "google.com",
|
||||
onClick: removeItem
|
||||
}, /*#__PURE__*/react.createElement("span", {
|
||||
"data-l10n-id": "pocket-panel-button-remove"
|
||||
}))), savedStory && /*#__PURE__*/react.createElement(ArticleList_ArticleList, {
|
||||
articles: [savedStory],
|
||||
savedArticle: true
|
||||
}), /*#__PURE__*/react.createElement("h3", {
|
||||
className: "header_small",
|
||||
"data-l10n-id": "pocket-panel-cta-add-tags"
|
||||
}), similarRecs?.length && locale?.startsWith("en") && /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("hr", null), /*#__PURE__*/react.createElement("h3", {
|
||||
className: "header_medium"
|
||||
}, "Similar Stories"), /*#__PURE__*/react.createElement(ArticleList_ArticleList, {
|
||||
articles: similarRecs
|
||||
})));
|
||||
}))), savedStatus === "loading" && /*#__PURE__*/react.createElement("h3", {
|
||||
className: "header_large",
|
||||
"data-l10n-id": "pocket-panel-saved-saving-tags"
|
||||
}), removedStatus === "removing" && /*#__PURE__*/react.createElement("h3", {
|
||||
className: "header_large header_center",
|
||||
"data-l10n-id": "pocket-panel-saved-processing-remove"
|
||||
}), removedStatus === "removed" && /*#__PURE__*/react.createElement("h3", {
|
||||
className: "header_large header_center",
|
||||
"data-l10n-id": "pocket-panel-saved-removed"
|
||||
}), removedStatus === "error" && /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("h3", {
|
||||
className: "header_large",
|
||||
"data-l10n-id": "pocket-panel-saved-error-remove"
|
||||
}), removedErrorMessage && /*#__PURE__*/react.createElement("p", null, removedErrorMessage))));
|
||||
}
|
||||
|
||||
/* harmony default export */ const Saved_Saved = (Saved);
|
||||
|
@ -1129,10 +1278,14 @@ SavedOverlay.prototype = {
|
|||
const layoutRefresh = searchParams.get(`layoutRefresh`) === `true`;
|
||||
|
||||
if (layoutRefresh) {
|
||||
// Create actual content
|
||||
// For now, we need to do a little work on the body element
|
||||
// to support both old and new versions.
|
||||
document.querySelector(`.pkt_ext_containersaved`)?.classList.add(`stp_saved_body`);
|
||||
document.querySelector(`.pkt_ext_containersaved`)?.classList.remove(`pkt_ext_containersaved`); // Create actual content
|
||||
|
||||
react_dom.render( /*#__PURE__*/react.createElement(Saved_Saved, {
|
||||
pockethost: pockethost,
|
||||
savedStory: {}
|
||||
locale: locale
|
||||
}), document.querySelector(`body`));
|
||||
} else {
|
||||
// set host
|
||||
|
@ -1183,11 +1336,10 @@ SavedOverlay.prototype = {
|
|||
data
|
||||
} = resp;
|
||||
myself.renderItemRecs(data);
|
||||
});
|
||||
} // tell back end we're ready
|
||||
}); // tell back end we're ready
|
||||
|
||||
|
||||
messages.sendMessage("PKT_show_saved");
|
||||
messages.sendMessage("PKT_show_saved");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -667,9 +667,17 @@ SavedOverlay.prototype = {
|
|||
const layoutRefresh = searchParams.get(`layoutRefresh`) === `true`;
|
||||
|
||||
if (layoutRefresh) {
|
||||
// For now, we need to do a little work on the body element
|
||||
// to support both old and new versions.
|
||||
document
|
||||
.querySelector(`.pkt_ext_containersaved`)
|
||||
?.classList.add(`stp_saved_body`);
|
||||
document
|
||||
.querySelector(`.pkt_ext_containersaved`)
|
||||
?.classList.remove(`pkt_ext_containersaved`);
|
||||
// Create actual content
|
||||
ReactDOM.render(
|
||||
<Saved pockethost={pockethost} savedStory={{}} />,
|
||||
<Saved pockethost={pockethost} locale={locale} />,
|
||||
document.querySelector(`body`)
|
||||
);
|
||||
} else {
|
||||
|
@ -748,10 +756,10 @@ SavedOverlay.prototype = {
|
|||
const { data } = resp;
|
||||
myself.renderItemRecs(data);
|
||||
});
|
||||
}
|
||||
|
||||
// tell back end we're ready
|
||||
pktPanelMessaging.sendMessage("PKT_show_saved");
|
||||
// tell back end we're ready
|
||||
pktPanelMessaging.sendMessage("PKT_show_saved");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -367,6 +367,14 @@ var pktUI = (function() {
|
|||
pktUIMessaging.sendMessageToPanel(saveLinkMessageId, successResponse);
|
||||
SaveToPocket.itemSaved();
|
||||
|
||||
if (item?.resolved_id && item?.resolved_id !== "0") {
|
||||
pktApi.getArticleInfo(item.resolved_url, {
|
||||
success(data) {
|
||||
pktUIMessaging.sendMessageToPanel("PKT_renderSavedStory", data);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getAndShowRecsForItem(item, {
|
||||
success(data) {
|
||||
pktUIMessaging.sendMessageToPanel("PKT_renderItemRecs", data);
|
||||
|
|
|
@ -47,6 +47,10 @@ class PictureInPictureVideoWrapper {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
shouldHideToggle(video) {
|
||||
return !!video.classList.contains("tst-video-overlay-player-html5");
|
||||
}
|
||||
}
|
||||
|
||||
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;
|
||||
|
|
|
@ -15,9 +15,12 @@ pocket-panel-saved-error-tag-length = Tags are limited to 25 characters
|
|||
pocket-panel-saved-error-only-links = Only links can be saved
|
||||
pocket-panel-saved-error-not-saved = Page Not Saved
|
||||
pocket-panel-saved-error-no-internet = You must be connected to the Internet in order to save to { -pocket-brand-name }. Please connect to the Internet and try again.
|
||||
pocket-panel-saved-error-remove = There was an error while trying to remove this page.
|
||||
pocket-panel-saved-page-removed = Page Removed
|
||||
pocket-panel-saved-page-saved = Saved to { -pocket-brand-name }
|
||||
pocket-panel-saved-page-saved-b = Saved to { -pocket-brand-name }!
|
||||
pocket-panel-saved-processing-remove = Removing Page…
|
||||
pocket-panel-saved-removed = Page Removed from My List
|
||||
pocket-panel-saved-processing-tags = Adding tags…
|
||||
pocket-panel-saved-remove-page = Remove Page
|
||||
pocket-panel-saved-save-tags = Save
|
||||
|
@ -26,6 +29,9 @@ pocket-panel-saved-suggested-tags = Suggested Tags
|
|||
pocket-panel-saved-tags-saved = Tags Added
|
||||
pocket-panel-signup-view-list = View List
|
||||
|
||||
# This is displayed above a field where the user can add tags
|
||||
pocket-panel-signup-add-tags = Add Tags:
|
||||
|
||||
## about:pocket-signup panel
|
||||
|
||||
pocket-panel-signup-already-have = Already a { -pocket-brand-name } user?
|
||||
|
@ -65,5 +71,5 @@ pocket-panel-header-sign-in = Sign In
|
|||
## Pocket panel buttons
|
||||
|
||||
pocket-panel-button-show-all = Show All
|
||||
pocket-panel-button-add-tags = Add Tags
|
||||
pocket-panel-button-activate = Activate { -pocket-brand-name } in { -brand-product-name }
|
||||
pocket-panel-button-remove = Remove
|
||||
|
|
|
@ -829,3 +829,10 @@ tabs-toolbar-list-all-tabs =
|
|||
# <img data-l10n-name="icon"/> will be replaced by the application menu icon
|
||||
restore-session-startup-suggestion-message = <strong>Open previous tabs?</strong> You can restore your previous session from the { -brand-short-name } application menu <img data-l10n-name="icon"/>, under History.
|
||||
restore-session-startup-suggestion-button = Show me how
|
||||
|
||||
## Mozilla data reporting notification (Telemetry, Firefox Health Report, etc)
|
||||
|
||||
data-reporting-notification-message = { -brand-short-name } automatically sends some data to { -vendor-short-name } so that we can improve your experience.
|
||||
data-reporting-notification-button =
|
||||
.label = Choose What I Share
|
||||
.accesskey = C
|
||||
|
|
|
@ -173,6 +173,10 @@ main-context-menu-copy-email =
|
|||
.label = Copy Email Address
|
||||
.accesskey = l
|
||||
|
||||
main-context-menu-copy-phone =
|
||||
.label = Copy Phone Number
|
||||
.accesskey = o
|
||||
|
||||
main-context-menu-copy-link-simple =
|
||||
.label = Copy Link
|
||||
.accesskey = L
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
# Dialog close button
|
||||
spotlight-dialog-close-button =
|
||||
.title = Close
|
||||
.aria-label = Close
|
||||
|
||||
## Mobile download button strings
|
||||
|
@ -16,6 +17,6 @@ spotlight-ios-marketplace-button =
|
|||
## Firefox Focus promo message strings
|
||||
|
||||
spotlight-focus-promo-title = Get { -focus-brand-name }
|
||||
spotlight-focus-promo-subtitle = Scan the QR code to download
|
||||
spotlight-focus-promo-subtitle = Scan the QR code to download.
|
||||
spotlight-focus-promo-qr-code =
|
||||
.alt = Scan the QR code to get { -focus-brand-name }
|
||||
|
|
|
@ -673,11 +673,6 @@ troubleshootModeRestartButton=Restart
|
|||
# menu, set this to "true". Otherwise, you can leave it as "false".
|
||||
browser.menu.showCharacterEncoding=false
|
||||
|
||||
# Mozilla data reporting notification (Telemetry, Firefox Health Report, etc)
|
||||
dataReportingNotification.message = %1$S automatically sends some data to %2$S so that we can improve your experience.
|
||||
dataReportingNotification.button.label = Choose What I Share
|
||||
dataReportingNotification.button.accessKey = C
|
||||
|
||||
# Process hang reporter
|
||||
# LOCALIZATION NOTE (processHang.selected_tab.label): %1$S is the name of the product (e.g., Firefox)
|
||||
processHang.selected_tab.label = This page is slowing down %1$S. To speed up your browser, stop this page.
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.style-puzzle-piece {
|
||||
fill: url('#gradient-linear-puzzle-piece');
|
||||
}
|
||||
.style-badge-shadow {
|
||||
fill: #0d131a;
|
||||
fill-opacity: .15;
|
||||
}
|
||||
.style-badge-background {
|
||||
fill: #fff;
|
||||
}
|
||||
.style-badge-inside {
|
||||
fill: #e62117;
|
||||
}
|
||||
.style-badge-icon {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
<linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#999999" stop-opacity="1"/>
|
||||
<stop offset="100%" stop-color="#8c8c8c" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
|
||||
<svg width="32" height="32" x="32" y="0">
|
||||
<ellipse class="style-badge-shadow" rx="14" ry="15" cx="16" cy="17" />
|
||||
<circle class="style-badge-background" r="15" cy="15" cx="16" />
|
||||
<circle class="style-badge-inside" r="12" cy="15" cx="16" />
|
||||
<path class="style-badge-icon" d="M14.9,16.2c0,0,0.1,0.8,1.1,0.8c1,0,1.1-0.8,1.1-0.8 s0.7-3.5,0.8-5.2C18,9.3,18.4,7,16,7s-2,2.4-1.9,4C14.2,12.7,14.9,16.2,14.9,16.2z M16,19c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2 c1.1,0,2-0.9,2-2C18,19.9,17.1,19,16,19z" />
|
||||
</svg>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 2.1 KiB |
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.style-puzzle-piece {
|
||||
fill: url('#gradient-linear-puzzle-piece');
|
||||
}
|
||||
.style-badge-shadow {
|
||||
fill: #0d131a;
|
||||
fill-opacity: .15;
|
||||
}
|
||||
.style-badge-background {
|
||||
fill: #fff;
|
||||
}
|
||||
.style-badge-inside {
|
||||
fill: #ffcd02;
|
||||
}
|
||||
.style-badge-icon {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
<linearGradient id="gradient-linear-puzzle-piece" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#999999" stop-opacity="1"/>
|
||||
<stop offset="100%" stop-color="#8c8c8c" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path class="style-puzzle-piece" d="M42,62c2.2,0,4-1.8,4-4l0-14.2c0,0,0.4-3.7,2.8-3.7c2.4,0,2.2,3.9,6.7,3.9c2.3,0,6.2-1.2,6.2-8.2 c0-7-3.9-7.9-6.2-7.9c-4.5,0-4.3,3.7-6.7,3.7c-2.4,0-2.8-3.8-2.8-3.8V22c0-2.2-1.8-4-4-4H31.5c0,0-3.4-0.6-3.4-3 c0-2.4,3.8-2.6,3.8-7.1c0-2.3-1.3-5.9-8.3-5.9s-8,3.6-8,5.9c0,4.5,3.4,4.7,3.4,7.1c0,2.4-3.4,3-3.4,3H6c-2.2,0-4,1.8-4,4l0,7.8 c0,0-0.4,6,4.4,6c3.1,0,3.2-4.1,7.3-4.1c2,0,4,1.9,4,6c0,4.2-2,6.3-4,6.3c-4,0-4.2-4.1-7.3-4.1c-4.8,0-4.4,5.8-4.4,5.8L2,58 c0,2.2,1.8,4,4,4H19c0,0,6.3,0.4,6.3-4.4c0-3.1-4-3.6-4-7.7c0-2,2.2-4.5,6.4-4.5c4.2,0,6.6,2.5,6.6,4.5c0,4-3.9,4.6-3.9,7.7 c0,4.9,6.3,4.4,6.3,4.4H42z"/>
|
||||
<svg width="32" height="32" x="32" y="0">
|
||||
<path class="style-badge-shadow" d="M29.5,25.8L18.7,4c-0.6-1.2-1.6-2-2.7-2c-1.1,0-2.1,0.7-2.7,2L2.5,25.8 c-0.6,1.2-0.6,2.5-0.1,3.6C2.9,30.4,4,31,5.2,31h21.6c1.2,0,2.3-0.6,2.8-1.6C30.2,28.4,30.1,27.1,29.5,25.8z" />
|
||||
<path class="style-badge-background" d="M16,0c-1.7,0-3.2,1-4.1,2.7L1.7,21.9c-0.9,1.7-0.9,3.4,0,4.8C2.5,28.2,4.1,29,5.9,29H26 c1.9,0,3.4-0.8,4.3-2.2c0.9-1.4,0.8-3.2,0-4.8L20.1,2.7C19.2,1,17.7,0,16,0L16,0z" />
|
||||
<path class="style-badge-inside" d="M5.9,26c-1.7,0-2.4-1.2-1.6-2.7L14.6,4.1c0.8-1.5,2.1-1.5,2.8,0l10.3,19.3 c0.8,1.5,0.1,2.7-1.6,2.7H5.9z" />
|
||||
<path class="style-badge-icon" d="M14.9,17.6c0,0,0.1,0.7,1.1,0.7c1,0,1.1-0.7,1.1-0.7 s0.7-2.9,0.8-4.2c0.1-1.3,0.5-3.2-1.9-3.2c-2.4,0-2,1.9-1.9,3.2C14.2,14.8,14.9,17.6,14.9,17.6z M16,20c-1.1,0-2,0.9-2,2 c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C18,20.9,17.1,20,16,20z" />
|
||||
</svg>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 2.4 KiB |
|
@ -36,9 +36,7 @@
|
|||
skin/classic/browser/webRTC-indicator.css (../shared/webRTC-indicator.css)
|
||||
skin/classic/browser/addons/addon-install-blocked.svg (../shared/addons/addon-install-blocked.svg)
|
||||
skin/classic/browser/addons/addon-install-downloading.svg (../shared/addons/addon-install-downloading.svg)
|
||||
skin/classic/browser/addons/addon-install-error.svg (../shared/addons/addon-install-error.svg)
|
||||
skin/classic/browser/addons/addon-install-installed.svg (../shared/addons/addon-install-installed.svg)
|
||||
skin/classic/browser/addons/addon-install-warning.svg (../shared/addons/addon-install-warning.svg)
|
||||
skin/classic/browser/addons/extension-controlled.css (../shared/addons/extension-controlled.css)
|
||||
skin/classic/browser/controlcenter/3rdpartycookies.svg (../shared/controlcenter/3rdpartycookies.svg)
|
||||
skin/classic/browser/controlcenter/cryptominers.svg (../shared/controlcenter/cryptominers.svg)
|
||||
|
|
|
@ -52,12 +52,10 @@
|
|||
list-style-image: url(chrome://global/skin/icons/info.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="storage-access"],
|
||||
.storage-access-icon {
|
||||
list-style-image: url(chrome://browser/skin/controlcenter/3rdpartycookies.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="persistent-storage"],
|
||||
.persistent-storage-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage.svg);
|
||||
}
|
||||
|
@ -66,7 +64,6 @@
|
|||
list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage-blocked.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="web-notifications"],
|
||||
.desktop-notification-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification.svg);
|
||||
}
|
||||
|
@ -83,10 +80,6 @@
|
|||
list-style-image: url(chrome://browser/skin/notification-icons/geo-blocked.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="geolocation"] {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/geo.svg);
|
||||
}
|
||||
|
||||
.open-protocol-handler-icon {
|
||||
list-style-image: url(chrome://global/skin/icons/open-in-new.svg);
|
||||
}
|
||||
|
@ -103,18 +96,10 @@
|
|||
list-style-image: url(chrome://browser/skin/notification-icons/xr-blocked.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="xr"] {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/xr.svg);
|
||||
}
|
||||
|
||||
.autoplay-media-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="autoplay-media"] {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media.svg);
|
||||
}
|
||||
|
||||
.autoplay-media-icon.blocked-permission-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media-blocked.svg);
|
||||
}
|
||||
|
@ -137,12 +122,10 @@
|
|||
padding: 20px;
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
|
||||
.indexedDB-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/persistent-storage.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="password"],
|
||||
#password-notification-icon {
|
||||
list-style-image: url(chrome://browser/skin/login.svg);
|
||||
}
|
||||
|
@ -197,14 +180,11 @@
|
|||
}
|
||||
|
||||
.midi-icon,
|
||||
.midi-sysex-icon,
|
||||
.popup-notification-icon[popupid="midi"],
|
||||
.popup-notification-icon[popupid="midi-sysex"] {
|
||||
.midi-sysex-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons/midi.svg);
|
||||
}
|
||||
|
||||
#canvas-notification-icon,
|
||||
.popup-notification-icon[popupid="canvas-permissions-prompt"],
|
||||
.canvas-icon {
|
||||
list-style-image: url(chrome://browser/skin/canvas.svg);
|
||||
}
|
||||
|
@ -294,7 +274,6 @@
|
|||
|
||||
/* EME */
|
||||
|
||||
.popup-notification-icon[popupid="drmContentPlaying"],
|
||||
.drm-icon {
|
||||
list-style-image: url("chrome://browser/skin/drm-icon.svg");
|
||||
}
|
||||
|
@ -317,39 +296,10 @@
|
|||
list-style-image: url(chrome://mozapps/skin/extensions/extension.svg);
|
||||
}
|
||||
|
||||
.install-icon.blocked-permission-icon,
|
||||
.popup-notification-icon[popupid="xpinstall-disabled"],
|
||||
.popup-notification-icon[popupid="addon-install-blocked"],
|
||||
.popup-notification-icon[popupid="addon-install-origin-blocked"] {
|
||||
.install-icon.blocked-permission-icon {
|
||||
list-style-image: url(chrome://browser/skin/addons/addon-install-blocked.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="addon-progress"] {
|
||||
list-style-image: url(chrome://browser/skin/addons/addon-install-downloading.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="addon-install-failed"] {
|
||||
list-style-image: url(chrome://browser/skin/addons/addon-install-error.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="addon-install-confirmation"] {
|
||||
list-style-image: url(chrome://mozapps/skin/extensions/extension.svg);
|
||||
}
|
||||
|
||||
#addon-install-confirmation-notification[warning] .popup-notification-icon[popupid="addon-install-confirmation"] {
|
||||
list-style-image: url(chrome://browser/skin/addons/addon-install-warning.svg);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="addon-install-complete"] {
|
||||
list-style-image: url(chrome://browser/skin/addons/addon-install-installed.svg);
|
||||
}
|
||||
|
||||
/* OFFLINE APPS */
|
||||
|
||||
.popup-notification-icon[popupid="offline-app-usage"] {
|
||||
list-style-image: url(chrome://global/skin/icons/help.svg);
|
||||
}
|
||||
|
||||
/* PLUGINS */
|
||||
|
||||
.plugin-icon {
|
||||
|
@ -436,20 +386,3 @@
|
|||
-moz-image-region: rect(0px, 64px, 32px, 32px);
|
||||
}
|
||||
}
|
||||
|
||||
/* UPDATE */
|
||||
.popup-notification-icon[popupid="update-available"],
|
||||
.popup-notification-icon[popupid="update-downloading"],
|
||||
.popup-notification-icon[popupid="update-manual"],
|
||||
.popup-notification-icon[popupid="update-other-instance"],
|
||||
.popup-notification-icon[popupid="update-restart"] {
|
||||
background: url(chrome://browser/skin/update-badge.svg) no-repeat center;
|
||||
-moz-context-properties: fill;
|
||||
fill: var(--panel-banner-item-update-supported-bgcolor);
|
||||
}
|
||||
|
||||
.popup-notification-icon[popupid="update-unsupported"] {
|
||||
background: url(chrome://global/skin/icons/warning.svg) no-repeat center;
|
||||
-moz-context-properties: fill;
|
||||
fill: var(--warning-icon-bgcolor);
|
||||
}
|
||||
|
|
|
@ -305,6 +305,10 @@ checkbox {
|
|||
font-size: 90%;
|
||||
}
|
||||
|
||||
#colors {
|
||||
margin-inline-start: 5px;
|
||||
}
|
||||
|
||||
#FontsDialog menulist:empty {
|
||||
/* When there's no menupopup, while the font builder is running,
|
||||
* the height of the menulist would otherwise shrink. This throws
|
||||
|
|
|
@ -7,9 +7,6 @@ path:build/moz.configure/init.configure
|
|||
path:build/moz.configure/util.configure
|
||||
# Used for bootstrapping the mach driver.
|
||||
path:build/mach_initialize.py
|
||||
path:build/build_virtualenv_packages.txt
|
||||
path:build/common_virtualenv_packages.txt
|
||||
path:build/mach_virtualenv_packages.txt
|
||||
path:build/psutil_requirements.txt
|
||||
path:build/zstandard_requirements.txt
|
||||
path:mach
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[include]
|
||||
# List of dependencies for the command
|
||||
path:build/docs_virtualenv_packages.txt
|
||||
path:python/sites/docs.txt
|
||||
|
||||
# Code for generating docs.
|
||||
glob:docs/**
|
||||
|
|
|
@ -66,6 +66,19 @@ const FIREFOX_DEFAULT_HEADERS = [
|
|||
"Upgrade",
|
||||
"Via",
|
||||
];
|
||||
|
||||
const HTTP_METHODS = [
|
||||
"GET",
|
||||
"HEAD",
|
||||
"POST",
|
||||
"DELETE",
|
||||
"PUT",
|
||||
"CONNECT",
|
||||
"OPTIONS",
|
||||
"TRACE",
|
||||
"PATH",
|
||||
];
|
||||
|
||||
/*
|
||||
* HTTP Custom request panel component
|
||||
* A network request panel which enables creating and sending new requests
|
||||
|
@ -95,7 +108,7 @@ class HTTPCustomRequestPanel extends Component {
|
|||
}
|
||||
|
||||
if (request.requestPostData?.postData?.text) {
|
||||
request.requestPostData = request.requestPostData.postData.text;
|
||||
request.postBody = request.requestPostData.postData.text;
|
||||
}
|
||||
|
||||
this.URLTextareaRef = createRef();
|
||||
|
@ -120,11 +133,11 @@ class HTTPCustomRequestPanel extends Component {
|
|||
});
|
||||
|
||||
this.state = {
|
||||
method: request.method || "",
|
||||
method: request.method || HTTP_METHODS[0],
|
||||
url: request.url || "",
|
||||
urlQueryParams: this.createQueryParamsListFromURL(request.url),
|
||||
headers: requestHeaders || [],
|
||||
requestPostData: request.requestPostData || "",
|
||||
postBody: request.postBody || "",
|
||||
};
|
||||
|
||||
Services.prefs.setCharPref(
|
||||
|
@ -147,6 +160,19 @@ class HTTPCustomRequestPanel extends Component {
|
|||
this.getStateFromPref = this.getStateFromPref.bind(this);
|
||||
}
|
||||
|
||||
async componentWillMount() {
|
||||
const { connector, request } = this.props;
|
||||
if (request?.requestPostDataAvailable && !this.state.postBody) {
|
||||
const requestData = await connector.requestData(
|
||||
request.id,
|
||||
"requestPostData"
|
||||
);
|
||||
this.setState({
|
||||
postBody: requestData.postData.text,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
// This is when the query params change in the url params input map
|
||||
if (
|
||||
|
@ -273,35 +299,18 @@ class HTTPCustomRequestPanel extends Component {
|
|||
|
||||
handleClear() {
|
||||
this.updateStateAndPref({
|
||||
method: "",
|
||||
method: HTTP_METHODS[0],
|
||||
url: "",
|
||||
urlQueryParams: [],
|
||||
headers: [],
|
||||
requestPostData: "",
|
||||
postBody: "",
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { sendCustomRequest } = this.props;
|
||||
const {
|
||||
method,
|
||||
urlQueryParams,
|
||||
requestPostData,
|
||||
url,
|
||||
headers,
|
||||
} = this.state;
|
||||
const { method, urlQueryParams, postBody, url, headers } = this.state;
|
||||
|
||||
const methods = [
|
||||
"GET",
|
||||
"HEAD",
|
||||
"POST",
|
||||
"DELETE",
|
||||
"PUT",
|
||||
"CONNECT",
|
||||
"OPTIONS",
|
||||
"TRACE",
|
||||
"PATH",
|
||||
];
|
||||
return div(
|
||||
{ className: "http-custom-request-panel" },
|
||||
div(
|
||||
|
@ -321,7 +330,7 @@ class HTTPCustomRequestPanel extends Component {
|
|||
value: method,
|
||||
},
|
||||
|
||||
methods.map(item =>
|
||||
HTTP_METHODS.map(item =>
|
||||
option(
|
||||
{
|
||||
value: item,
|
||||
|
@ -438,11 +447,11 @@ class HTTPCustomRequestPanel extends Component {
|
|||
textarea({
|
||||
className: "tabpanel-summary-input",
|
||||
id: "http-custom-postdata-value",
|
||||
name: "requestPostData",
|
||||
name: "postBody",
|
||||
placeholder: CUSTOM_POSTDATA_PLACEHOLDER,
|
||||
onChange: this.handleInputChange,
|
||||
rows: 6,
|
||||
value: requestPostData,
|
||||
value: postBody,
|
||||
wrap: "off",
|
||||
})
|
||||
),
|
||||
|
@ -462,14 +471,27 @@ class HTTPCustomRequestPanel extends Component {
|
|||
{
|
||||
className: "devtools-button",
|
||||
id: "http-custom-request-send-button",
|
||||
disabled: !this.state.url,
|
||||
onClick: () =>
|
||||
sendCustomRequest({
|
||||
disabled: !this.state.url || !this.state.method,
|
||||
onClick: () => {
|
||||
const customRequestDetails = {
|
||||
...this.state,
|
||||
headers: this.state.headers.filter(
|
||||
({ checked }) => checked
|
||||
urlQueryParams: urlQueryParams.map(
|
||||
({ checked, ...params }) => params
|
||||
),
|
||||
}),
|
||||
headers: headers
|
||||
.filter(({ checked }) => checked)
|
||||
.map(({ checked, ...headersValues }) => headersValues),
|
||||
};
|
||||
if (postBody) {
|
||||
customRequestDetails.requestPostData = {
|
||||
postData: {
|
||||
text: postBody,
|
||||
},
|
||||
};
|
||||
}
|
||||
delete customRequestDetails.postBody;
|
||||
sendCustomRequest(customRequestDetails);
|
||||
},
|
||||
},
|
||||
CUSTOM_SEND
|
||||
)
|
||||
|
|
|
@ -190,7 +190,7 @@ class RequestPanel extends Component {
|
|||
const { request, targetSearchResult } = this.props;
|
||||
const { filterText, rawRequestPayloadDisplayed } = this.state;
|
||||
const { formDataSections, mimeType, requestPostData } = request;
|
||||
const postData = requestPostData ? requestPostData.postData.text : null;
|
||||
const postData = requestPostData ? requestPostData.postData?.text : null;
|
||||
|
||||
if ((!formDataSections || formDataSections.length === 0) && !postData) {
|
||||
return div({ className: "empty-notice" }, REQUEST_EMPTY_TEXT);
|
||||
|
|
|
@ -201,6 +201,9 @@ skip-if = verify # Bug 1607678
|
|||
[browser_net_large-response.js]
|
||||
[browser_net_leak_on_tab_close.js]
|
||||
[browser_net_new_request_panel.js]
|
||||
[browser_net_new_request_panel_clear_button.js]
|
||||
[browser_net_new_request_panel_context_menu.js]
|
||||
[browser_net_new_request_panel_send_request.js]
|
||||
[browser_net_new_request_panel_persisted_content.js]
|
||||
[browser_net_new_request_panel_sync_url_params.js]
|
||||
[browser_net_open_in_debugger.js]
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Test if the New Request panel shows in the left when the pref is true
|
||||
* Test if the New Request panel shows up as a expected when opened from the toolbar
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
// Turn true the pref
|
||||
// Turn on the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", true);
|
||||
// Resetting the pref
|
||||
await pushPref("devtools.netmonitor.customRequest", "");
|
||||
|
@ -24,18 +24,15 @@ add_task(async function() {
|
|||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
info("open the left panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
|
||||
info("switching to new HTTP Custom Request panel");
|
||||
info("Open the HTTP Custom Panel through the toolbar button");
|
||||
let HTTPCustomRequestButton = document.querySelector(
|
||||
"#netmonitor-toolbar-container .devtools-http-custom-request-icon"
|
||||
);
|
||||
ok(HTTPCustomRequestButton, "The Toolbar button should be visible.");
|
||||
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
HTTPCustomRequestButton.click();
|
||||
await waitForPanels;
|
||||
|
||||
|
@ -44,184 +41,15 @@ add_task(async function() {
|
|||
true,
|
||||
"The 'New Request' header should be visible when the pref is true."
|
||||
);
|
||||
|
||||
// Turn false the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", false);
|
||||
|
||||
// Close the panel to updated after changing the pref
|
||||
const closePanel = document.querySelector(
|
||||
".network-action-bar .tabs-navigation .sidebar-toggle"
|
||||
);
|
||||
closePanel.click();
|
||||
|
||||
// Check if the toolbar is hidden when tre pref is false
|
||||
HTTPCustomRequestButton = document.querySelector(
|
||||
"#netmonitor-toolbar-container .devtools-http-custom-request-icon"
|
||||
);
|
||||
|
||||
is(
|
||||
!!HTTPCustomRequestButton,
|
||||
false,
|
||||
"The toolbar button should be hidden when the pref is false."
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test if the context menu open the new HTTP Custom Request Panel
|
||||
* when the pref is true
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
// Turn true the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", true);
|
||||
// Resetting the pref
|
||||
await pushPref("devtools.netmonitor.customRequest", "");
|
||||
|
||||
const { tab, monitor } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
|
||||
// Action should be processed synchronously in tests.
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
await performRequests(monitor, tab, 1);
|
||||
|
||||
info("selecting first request");
|
||||
const firstRequestItem = document.querySelectorAll(".request-list-item")[0];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, firstRequestItem);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequestItem);
|
||||
|
||||
// Cheking if the item "Resend" is hidden
|
||||
is(
|
||||
!!getContextMenuItem(monitor, "request-list-context-resend-only"),
|
||||
false,
|
||||
"The'Resend' item should be hidden when the pref is true."
|
||||
);
|
||||
|
||||
info("opening the new request panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
getContextMenuItem(monitor, "request-list-context-resend").click();
|
||||
await waitForPanels;
|
||||
|
||||
is(
|
||||
!!document.querySelector("#network-action-bar-HTTP-custom-request-panel"),
|
||||
!!document.querySelector(
|
||||
".devtools-button.devtools-http-custom-request-icon.checked"
|
||||
),
|
||||
true,
|
||||
"The 'New Request' header should be visible when the pref is true."
|
||||
"The toolbar button should be highlighted"
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test if the content it is not connected to the current selection.
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
// Turn true the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", true);
|
||||
// Resetting the pref
|
||||
await pushPref("devtools.netmonitor.customRequest", "");
|
||||
|
||||
const { tab, monitor } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
|
||||
// Action should be processed synchronously in tests.
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
await performRequests(monitor, tab, 2);
|
||||
|
||||
info("selecting first request");
|
||||
const firstRequestItem = document.querySelectorAll(".request-list-item")[0];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, firstRequestItem);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequestItem);
|
||||
|
||||
info("opening the new request panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
getContextMenuItem(monitor, "request-list-context-resend").click();
|
||||
await waitForPanels;
|
||||
|
||||
const urlValue = document.querySelector("http-custom-url-value");
|
||||
const request = document.querySelectorAll(".request-list-item")[1];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, request);
|
||||
|
||||
const urlValueChanged = document.querySelector("http-custom-url-value");
|
||||
|
||||
is(
|
||||
urlValue,
|
||||
urlValueChanged,
|
||||
"The url should not change when click on a new request"
|
||||
);
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test cleaning a custom request.
|
||||
*/
|
||||
add_task(async function() {
|
||||
// Turn on the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", true);
|
||||
// Resetting the pref
|
||||
await pushPref("devtools.netmonitor.customRequest", "");
|
||||
|
||||
const { monitor, tab } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
|
||||
// Action should be processed synchronously in tests.
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
const { getSelectedRequest } = windowRequire(
|
||||
"devtools/client/netmonitor/src/selectors/index"
|
||||
);
|
||||
|
||||
await performRequests(monitor, tab, 1);
|
||||
|
||||
info("selecting first request");
|
||||
const firstRequestItem = document.querySelectorAll(".request-list-item")[0];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, firstRequestItem);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequestItem);
|
||||
|
||||
info("Opening the new request panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
getContextMenuItem(monitor, "request-list-context-resend").click();
|
||||
await waitForPanels;
|
||||
|
||||
const request = getSelectedRequest(store.getState());
|
||||
|
||||
// Check if the panel is updated with the content by the request clicked
|
||||
const urlValue = document.querySelector(".http-custom-url-value");
|
||||
is(
|
||||
urlValue.textContent,
|
||||
request.url,
|
||||
"The URL in the form should match the request we clicked"
|
||||
);
|
||||
|
||||
info("Clicking on the clear button");
|
||||
document.querySelector("#http-custom-request-clear-button").click();
|
||||
info("if the default state is empty");
|
||||
is(
|
||||
document.querySelector(".http-custom-method-value").value,
|
||||
"GET",
|
||||
|
@ -246,5 +74,23 @@ add_task(async function() {
|
|||
"The Post body input should be empty"
|
||||
);
|
||||
|
||||
// Turn off the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", false);
|
||||
info("Close the panel to update the interface after changing the pref");
|
||||
const closePanel = document.querySelector(
|
||||
".network-action-bar .tabs-navigation .sidebar-toggle"
|
||||
);
|
||||
closePanel.click();
|
||||
|
||||
info("Check if the toolbar button is hidden when the pref is false");
|
||||
HTTPCustomRequestButton = document.querySelector(
|
||||
"#netmonitor-toolbar-container .devtools-http-custom-request-icon"
|
||||
);
|
||||
is(
|
||||
!!HTTPCustomRequestButton,
|
||||
false,
|
||||
"The toolbar button should be hidden when the pref is false."
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test cleaning a custom request.
|
||||
*/
|
||||
add_task(async function() {
|
||||
// Turn on the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", true);
|
||||
// Resetting the pref
|
||||
await pushPref("devtools.netmonitor.customRequest", "");
|
||||
|
||||
const { monitor, tab } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
|
||||
// Action should be processed synchronously in tests.
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
const { getSelectedRequest } = windowRequire(
|
||||
"devtools/client/netmonitor/src/selectors/index"
|
||||
);
|
||||
|
||||
await performRequests(monitor, tab, 1);
|
||||
|
||||
info("selecting first request");
|
||||
const firstRequestItem = document.querySelectorAll(".request-list-item")[0];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, firstRequestItem);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequestItem);
|
||||
|
||||
info("Opening the new request panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
getContextMenuItem(monitor, "request-list-context-resend").click();
|
||||
await waitForPanels;
|
||||
|
||||
const request = getSelectedRequest(store.getState());
|
||||
|
||||
// Check if the panel is updated with the content by the request clicked
|
||||
const urlValue = document.querySelector(".http-custom-url-value");
|
||||
is(
|
||||
urlValue.textContent,
|
||||
request.url,
|
||||
"The URL in the form should match the request we clicked"
|
||||
);
|
||||
|
||||
info("Clicking on the clear button");
|
||||
document.querySelector("#http-custom-request-clear-button").click();
|
||||
is(
|
||||
document.querySelector(".http-custom-method-value").value,
|
||||
"GET",
|
||||
"The method input should be 'GET' by default"
|
||||
);
|
||||
is(
|
||||
document.querySelector(".http-custom-url-value").textContent,
|
||||
"",
|
||||
"The URL input should be empty"
|
||||
);
|
||||
const urlParametersValue = document.querySelectorAll(
|
||||
"#http-custom-query .tabpanel-summary-container.http-custom-input"
|
||||
);
|
||||
is(urlParametersValue.length, 0, "The URL Parameters input should be empty");
|
||||
const headersValue = document.querySelectorAll(
|
||||
"#http-custom-headers .tabpanel-summary-container.http-custom-input"
|
||||
);
|
||||
is(headersValue.length, 0, "The Headers input should be empty");
|
||||
is(
|
||||
document.querySelector("#http-custom-postdata-value").textContent,
|
||||
"",
|
||||
"The Post body input should be empty"
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
|
@ -0,0 +1,213 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test if the New Request Panel shows up as a expected
|
||||
* when opened from an existing request
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
// Turn true the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", true);
|
||||
// Resetting the pref
|
||||
await pushPref("devtools.netmonitor.customRequest", "");
|
||||
|
||||
const { tab, monitor } = await initNetMonitor(POST_DATA_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
|
||||
// Action should be processed synchronously in tests.
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
await performRequests(monitor, tab, 1);
|
||||
|
||||
const { getSelectedRequest } = windowRequire(
|
||||
"devtools/client/netmonitor/src/selectors/index"
|
||||
);
|
||||
|
||||
const expectedURLQueryParams = [
|
||||
{
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
},
|
||||
{ name: "baz", value: "42" },
|
||||
{ name: "type", value: "urlencoded" },
|
||||
];
|
||||
|
||||
info("selecting first request");
|
||||
const firstRequestItem = document.querySelectorAll(".request-list-item")[0];
|
||||
const waitForHeaders = waitUntil(() =>
|
||||
document.querySelector(".headers-overview")
|
||||
);
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, firstRequestItem);
|
||||
await waitForHeaders;
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequestItem);
|
||||
|
||||
info("if the item 'Resend' is hidden");
|
||||
is(
|
||||
!!getContextMenuItem(monitor, "request-list-context-resend-only"),
|
||||
false,
|
||||
"The 'Resend' item should be hidden when the pref is true."
|
||||
);
|
||||
|
||||
info("Opening the new request panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
const menuItem = getContextMenuItem(monitor, "request-list-context-resend");
|
||||
const menuPopup = menuItem.parentNode;
|
||||
|
||||
const onHidden = new Promise(resolve => {
|
||||
menuPopup.addEventListener("popuphidden", resolve, { once: true });
|
||||
});
|
||||
|
||||
menuItem.click();
|
||||
menuPopup.hidePopup();
|
||||
|
||||
await onHidden;
|
||||
await waitForPanels;
|
||||
|
||||
is(
|
||||
!!document.querySelector(
|
||||
".devtools-button.devtools-http-custom-request-icon.checked"
|
||||
),
|
||||
true,
|
||||
"The toolbar button should be highlighted"
|
||||
);
|
||||
|
||||
const request = getSelectedRequest(store.getState());
|
||||
|
||||
// Verify if the default state contains the data from the request
|
||||
const methodValue = document.querySelector(".http-custom-method-value");
|
||||
is(
|
||||
methodValue.value,
|
||||
request.method,
|
||||
"The method in the form should match the request we clicked"
|
||||
);
|
||||
|
||||
const urlValue = document.querySelector(".http-custom-url-value");
|
||||
is(
|
||||
urlValue.textContent,
|
||||
request.url,
|
||||
"The URL in the form should match the request we clicked"
|
||||
);
|
||||
|
||||
const urlParametersValues = document.querySelectorAll(
|
||||
"#http-custom-query .tabpanel-summary-container.http-custom-input"
|
||||
);
|
||||
is(
|
||||
urlParametersValues.length,
|
||||
3,
|
||||
"The URL Parameters length in the form should match the request we clicked"
|
||||
);
|
||||
|
||||
for (let i = 0; i < urlParametersValues.length; i++) {
|
||||
const { name, value } = expectedURLQueryParams[i];
|
||||
const [formName, formValue] = urlParametersValues[i].querySelectorAll(
|
||||
"textarea"
|
||||
);
|
||||
is(
|
||||
name,
|
||||
formName.value,
|
||||
"The query param name in the form should match the request we clicked"
|
||||
);
|
||||
is(
|
||||
value,
|
||||
formValue.value,
|
||||
"The query param value in the form should match the request we clicked"
|
||||
);
|
||||
}
|
||||
|
||||
const headersValues = document.querySelectorAll(
|
||||
"#http-custom-headers .tabpanel-summary-container.http-custom-input"
|
||||
);
|
||||
ok(
|
||||
headersValues.length >= 6,
|
||||
"The headers length in the form should match the request we clicked"
|
||||
);
|
||||
|
||||
for (const { name, value } of request.requestHeaders.headers) {
|
||||
const found = Array.from(headersValues).find(item => {
|
||||
const [formName, formValue] = item.querySelectorAll("textarea");
|
||||
if (formName.value === name && formValue.value === value) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
ok(found, "The header was found in the form");
|
||||
}
|
||||
|
||||
// Wait to the post body because it is only updated in the componentWillMount
|
||||
const postValue = document.querySelector("#http-custom-postdata-value");
|
||||
await waitUntil(() => postValue.textContent !== "");
|
||||
is(
|
||||
postValue.value,
|
||||
request.requestPostData.postData.text,
|
||||
"The Post body input value in the form should match the request we clicked"
|
||||
);
|
||||
|
||||
info(
|
||||
"Uncheck the header an make sure the header is removed from the new request"
|
||||
);
|
||||
const headers = document.querySelectorAll(
|
||||
"#http-custom-headers .tabpanel-summary-container.http-custom-input"
|
||||
);
|
||||
|
||||
const lastHeader = Array.from(headers).pop();
|
||||
const checkbox = lastHeader.querySelector("input");
|
||||
checkbox.click();
|
||||
|
||||
info("Click on the button to send a new request");
|
||||
const waitUntilEventsDisplayed = waitForNetworkEvents(monitor, 1);
|
||||
const buttonSend = document.querySelector("#http-custom-request-send-button");
|
||||
buttonSend.click();
|
||||
await waitUntilEventsDisplayed;
|
||||
|
||||
const newRequestSelected = getSelectedRequest(store.getState());
|
||||
let found = newRequestSelected.requestHeaders.headers.some(
|
||||
item => item.name == "My-header-2" && item.value == "my-value-2"
|
||||
);
|
||||
|
||||
is(
|
||||
found,
|
||||
false,
|
||||
"The header unchecked should not be found on the headers list"
|
||||
);
|
||||
|
||||
info(
|
||||
"Delete the header and make sure the header is removed in the custom request panel"
|
||||
);
|
||||
const buttonDelete = lastHeader.querySelector("button");
|
||||
buttonDelete.click();
|
||||
|
||||
const headersValue = document.querySelectorAll(
|
||||
"#http-custom-headers .tabpanel-summary-container.http-custom-input textarea"
|
||||
);
|
||||
found = Array.from(headersValue).some(
|
||||
item => item.name == "My-header-2" && item.value == "my-value-2"
|
||||
);
|
||||
is(found, false, "The header delete should not be found on the headers form");
|
||||
|
||||
info(
|
||||
"Change the request selected to make sure the request in the custom request panel does not change"
|
||||
);
|
||||
const previousRequest = document.querySelectorAll(".request-list-item")[0];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, previousRequest);
|
||||
|
||||
const urlValueChanged = document.querySelector(".http-custom-url-value");
|
||||
is(
|
||||
urlValue.textContent,
|
||||
urlValueChanged.textContent,
|
||||
"The url should not change when click on a new request"
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
|
@ -0,0 +1,159 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test sending a custom request.
|
||||
*/
|
||||
add_task(async function() {
|
||||
// Turn on the pref
|
||||
await pushPref("devtools.netmonitor.features.newEditAndResend", true);
|
||||
await pushPref("devtools.netmonitor.customRequest", "");
|
||||
const { monitor } = await initNetMonitor(CORS_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire, connector } = monitor.panelWin;
|
||||
const { sendHTTPRequest } = connector;
|
||||
|
||||
// Action should be processed synchronously in tests.
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
const { getSelectedRequest } = windowRequire(
|
||||
"devtools/client/netmonitor/src/selectors/index"
|
||||
);
|
||||
|
||||
// Build request with known information so that we can check later
|
||||
const request = {
|
||||
url: "https://test1.example.com" + CORS_SJS_PATH,
|
||||
method: "POST",
|
||||
headers: [
|
||||
{ name: "Host", value: "fakehost.example.com" },
|
||||
{ name: "User-Agent", value: "Testzilla" },
|
||||
{ name: "Referer", value: "http://example.com/referrer" },
|
||||
{ name: "Accept", value: "application/jarda" },
|
||||
{ name: "Accept-Encoding", value: "compress, identity, funcoding" },
|
||||
{ name: "Accept-Language", value: "cs-CZ" },
|
||||
],
|
||||
body: "Hello",
|
||||
cause: {
|
||||
loadingDocumentUri: "http://example.com",
|
||||
stacktraceAvailable: true,
|
||||
type: "xhr",
|
||||
},
|
||||
};
|
||||
const waitUntilRequestDisplayed = waitForNetworkEvents(monitor, 1);
|
||||
sendHTTPRequest(request);
|
||||
await waitUntilRequestDisplayed;
|
||||
|
||||
info("selecting first request");
|
||||
const firstRequestItem = document.querySelectorAll(".request-list-item")[0];
|
||||
const waitForHeaders = waitUntil(() =>
|
||||
document.querySelector(".headers-overview")
|
||||
);
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, firstRequestItem);
|
||||
await waitForHeaders;
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequestItem);
|
||||
|
||||
info("Opening the new request panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
const menuItem = getContextMenuItem(monitor, "request-list-context-resend");
|
||||
const menuPopup = menuItem.parentNode;
|
||||
|
||||
const onHidden = new Promise(resolve => {
|
||||
menuPopup.addEventListener("popuphidden", resolve, { once: true });
|
||||
});
|
||||
|
||||
menuItem.click();
|
||||
menuPopup.hidePopup();
|
||||
|
||||
await onHidden;
|
||||
await waitForPanels;
|
||||
|
||||
info(
|
||||
"Change the request method to send a new custom request with a different method"
|
||||
);
|
||||
const methodValue = document.querySelector("#http-custom-method-value");
|
||||
methodValue.value = "PUT";
|
||||
methodValue.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
|
||||
info("Change the URL to send a custom request with a different URL");
|
||||
const urlValue = document.querySelector(".http-custom-url-value");
|
||||
urlValue.focus();
|
||||
urlValue.value = "";
|
||||
EventUtils.sendString(`${request.url}?hello=world`);
|
||||
|
||||
info("Check if the parameter section was updated");
|
||||
is(
|
||||
document.querySelectorAll(
|
||||
"#http-custom-query .tabpanel-summary-container.http-custom-input"
|
||||
).length,
|
||||
1,
|
||||
"The parameter section should be updated"
|
||||
);
|
||||
|
||||
info("Adding new headers");
|
||||
const newHeaderName = document.querySelector(
|
||||
"#http-custom-headers .map-add-new-inputs .http-custom-input-name"
|
||||
);
|
||||
newHeaderName.focus();
|
||||
EventUtils.sendString("My-header");
|
||||
|
||||
const newHeaderValue = Array.from(
|
||||
document.querySelectorAll(
|
||||
"#http-custom-headers #http-custom-name-and-value .http-custom-input-value"
|
||||
)
|
||||
).pop();
|
||||
newHeaderValue.focus();
|
||||
EventUtils.sendString("my-value");
|
||||
|
||||
const postValue = document.querySelector("#http-custom-postdata-value");
|
||||
postValue.focus();
|
||||
postValue.value = "";
|
||||
EventUtils.sendString("{'name': 'value'}");
|
||||
|
||||
// Close the details panel to see if after sending a new request
|
||||
// this request will be selected by default and
|
||||
// if the deitails panel will be open automatically.
|
||||
const waitForDetailsPanelToClose = waitUntil(
|
||||
() => !document.querySelector(".monitor-panel .network-details-bar")
|
||||
);
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
await waitForDetailsPanelToClose;
|
||||
|
||||
info("Click on the button to send a new request");
|
||||
const waitUntilEventsDisplayed = waitForNetworkEvents(monitor, 1);
|
||||
const buttonSend = document.querySelector("#http-custom-request-send-button");
|
||||
buttonSend.click();
|
||||
await waitUntilEventsDisplayed;
|
||||
|
||||
const newRequestSelectedId = getSelectedRequest(store.getState()).id;
|
||||
await connector.requestData(newRequestSelectedId, "requestPostData");
|
||||
const updatedSelectedRequest = getSelectedRequest(store.getState());
|
||||
is(updatedSelectedRequest.method, "PUT", "The request has the right method");
|
||||
is(
|
||||
updatedSelectedRequest.url,
|
||||
urlValue.value,
|
||||
"The request has the right URL"
|
||||
);
|
||||
|
||||
const found = updatedSelectedRequest.requestHeaders.headers.some(
|
||||
item => item.name === "My-header" && item.value === "my-value"
|
||||
);
|
||||
|
||||
is(found, true, "The header was found in the form");
|
||||
|
||||
is(
|
||||
updatedSelectedRequest.requestPostData.postData.text,
|
||||
"{'name': 'value'}",
|
||||
"The request has the right post body"
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
|
@ -63,9 +63,7 @@ int32_t nsScreen::GetPixelDepth(ErrorResult& aRv) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
uint32_t depth;
|
||||
context->GetDepth(depth);
|
||||
return depth;
|
||||
return context->GetDepth();
|
||||
}
|
||||
|
||||
nsPIDOMWindowOuter* nsScreen::GetOuter() const {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ServoUtils.h"
|
||||
#include "mozilla/RustCell.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
@ -89,15 +91,7 @@ class JS_HAZ_ROOTED nsWrapperCache {
|
|||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID)
|
||||
|
||||
nsWrapperCache()
|
||||
: mWrapper(nullptr),
|
||||
mFlags(0)
|
||||
#ifdef BOOL_FLAGS_ON_WRAPPER_CACHE
|
||||
,
|
||||
mBoolFlags(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
nsWrapperCache() = default;
|
||||
~nsWrapperCache() {
|
||||
// Preserved wrappers should never end up getting cleared, but this can
|
||||
// happen during shutdown when a leaked wrapper object is finalized, causing
|
||||
|
@ -256,33 +250,43 @@ class JS_HAZ_ROOTED nsWrapperCache {
|
|||
|
||||
using FlagsType = uint32_t;
|
||||
|
||||
FlagsType GetFlags() const { return mFlags & ~kWrapperFlagsMask; }
|
||||
FlagsType GetFlags() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mozilla::IsInServoTraversal());
|
||||
return mFlags.Get() & ~kWrapperFlagsMask;
|
||||
}
|
||||
|
||||
// This can be called from stylo threads too, so it needs to be atomic, as
|
||||
// this value may be mutated from multiple threads during servo traversal from
|
||||
// rust.
|
||||
bool HasFlag(FlagsType aFlag) const {
|
||||
MOZ_ASSERT((aFlag & kWrapperFlagsMask) == 0, "Bad flag mask");
|
||||
return !!(mFlags & aFlag);
|
||||
return __atomic_load_n(mFlags.AsPtr(), __ATOMIC_RELAXED) & aFlag;
|
||||
}
|
||||
|
||||
// Identical to HasFlag, but more explicit about its handling of multiple
|
||||
// flags.
|
||||
bool HasAnyOfFlags(FlagsType aFlags) const {
|
||||
MOZ_ASSERT((aFlags & kWrapperFlagsMask) == 0, "Bad flag mask");
|
||||
return !!(mFlags & aFlags);
|
||||
}
|
||||
// flags. This can be called from stylo threads too.
|
||||
bool HasAnyOfFlags(FlagsType aFlags) const { return HasFlag(aFlags); }
|
||||
|
||||
// This can also be called from stylo, in the sequential part of the
|
||||
// traversal, though it's probably not worth differentiating them for the
|
||||
// purposes of assertions.
|
||||
bool HasAllFlags(FlagsType aFlags) const {
|
||||
MOZ_ASSERT((aFlags & kWrapperFlagsMask) == 0, "Bad flag mask");
|
||||
return (mFlags & aFlags) == aFlags;
|
||||
return (__atomic_load_n(mFlags.AsPtr(), __ATOMIC_RELAXED) & aFlags) ==
|
||||
aFlags;
|
||||
}
|
||||
|
||||
void SetFlags(FlagsType aFlagsToSet) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT((aFlagsToSet & kWrapperFlagsMask) == 0, "Bad flag mask");
|
||||
mFlags |= aFlagsToSet;
|
||||
mFlags.Set(mFlags.Get() | aFlagsToSet);
|
||||
}
|
||||
|
||||
void UnsetFlags(FlagsType aFlagsToUnset) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT((aFlagsToUnset & kWrapperFlagsMask) == 0, "Bad flag mask");
|
||||
mFlags &= ~aFlagsToUnset;
|
||||
mFlags.Set(mFlags.Get() & ~aFlagsToUnset);
|
||||
}
|
||||
|
||||
void PreserveWrapper(nsISupports* aScriptObjectHolder) {
|
||||
|
@ -335,23 +339,30 @@ class JS_HAZ_ROOTED nsWrapperCache {
|
|||
private:
|
||||
void SetWrapperJSObject(JSObject* aWrapper);
|
||||
|
||||
FlagsType GetWrapperFlags() const { return mFlags & kWrapperFlagsMask; }
|
||||
// We'd like to assert that these aren't used from servo threads, but we don't
|
||||
// have a great way to do that because:
|
||||
// * We can't just assert that they get used on the main thread, because
|
||||
// these are used from workers.
|
||||
// * We can't just assert that they aren't used when IsInServoTraversal(),
|
||||
// because the traversal has a sequential, main-thread-only phase, where we
|
||||
// run animations that can fiddle with JS promises.
|
||||
FlagsType GetWrapperFlags() const { return mFlags.Get() & kWrapperFlagsMask; }
|
||||
|
||||
bool HasWrapperFlag(FlagsType aFlag) const {
|
||||
MOZ_ASSERT((aFlag & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
|
||||
return !!(mFlags & aFlag);
|
||||
return !!(mFlags.Get() & aFlag);
|
||||
}
|
||||
|
||||
void SetWrapperFlags(FlagsType aFlagsToSet) {
|
||||
MOZ_ASSERT((aFlagsToSet & ~kWrapperFlagsMask) == 0,
|
||||
"Bad wrapper flag bits");
|
||||
mFlags |= aFlagsToSet;
|
||||
mFlags.Set(mFlags.Get() | aFlagsToSet);
|
||||
}
|
||||
|
||||
void UnsetWrapperFlags(FlagsType aFlagsToUnset) {
|
||||
MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0,
|
||||
"Bad wrapper flag bits");
|
||||
mFlags &= ~aFlagsToUnset;
|
||||
mFlags.Set(mFlags.Get() & ~aFlagsToUnset);
|
||||
}
|
||||
|
||||
void HoldJSObjects(void* aScriptObjectHolder, nsScriptObjectTracer* aTracer,
|
||||
|
@ -379,12 +390,22 @@ class JS_HAZ_ROOTED nsWrapperCache {
|
|||
|
||||
enum { kWrapperFlagsMask = WRAPPER_BIT_PRESERVED };
|
||||
|
||||
JSObject* mWrapper;
|
||||
FlagsType mFlags;
|
||||
JSObject* mWrapper = nullptr;
|
||||
|
||||
// Rust code needs to read and write some flags atomically, but we don't want
|
||||
// to make the wrapper flags atomic whole-sale because main-thread code would
|
||||
// become more expensive (loads wouldn't change, but flag setting /
|
||||
// unsetting could become slower enough to be noticeable). Making this an
|
||||
// Atomic whole-sale needs more measuring.
|
||||
//
|
||||
// In order to not mess with aliasing rules the type should not be frozen, so
|
||||
// we use a RustCell, which contains an UnsafeCell internally. See also the
|
||||
// handling of ServoData (though that's a bit different).
|
||||
mozilla::RustCell<FlagsType> mFlags{0};
|
||||
|
||||
protected:
|
||||
#ifdef BOOL_FLAGS_ON_WRAPPER_CACHE
|
||||
uint32_t mBoolFlags;
|
||||
uint32_t mBoolFlags = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -3962,10 +3962,9 @@ nsresult ArrayBufferBuilder::MapToFileInPackage(const nsCString& aFile,
|
|||
nsresult rv;
|
||||
|
||||
// Open Jar file to get related attributes of target file.
|
||||
RefPtr<nsZipArchive> zip = new nsZipArchive();
|
||||
rv = zip->OpenArchive(aJarFile);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
RefPtr<nsZipArchive> zip = nsZipArchive::OpenArchive(aJarFile);
|
||||
if (!zip) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsZipItem* zipItem = zip->GetItem(aFile.get());
|
||||
if (!zipItem) {
|
||||
|
|
|
@ -211,7 +211,7 @@ already_AddRefed<gfxContext> nsDeviceContext::CreateRenderingContextCommon(
|
|||
return pContext.forget();
|
||||
}
|
||||
|
||||
nsresult nsDeviceContext::GetDepth(uint32_t& aDepth) {
|
||||
uint32_t nsDeviceContext::GetDepth() {
|
||||
nsCOMPtr<nsIScreen> screen;
|
||||
FindScreen(getter_AddRefs(screen));
|
||||
if (!screen) {
|
||||
|
@ -219,9 +219,9 @@ nsresult nsDeviceContext::GetDepth(uint32_t& aDepth) {
|
|||
screenManager.GetPrimaryScreen(getter_AddRefs(screen));
|
||||
MOZ_ASSERT(screen);
|
||||
}
|
||||
screen->GetColorDepth(reinterpret_cast<int32_t*>(&aDepth));
|
||||
|
||||
return NS_OK;
|
||||
int32_t depth = 0;
|
||||
screen->GetColorDepth(&depth);
|
||||
return uint32_t(depth);
|
||||
}
|
||||
|
||||
nsresult nsDeviceContext::GetDeviceSurfaceDimensions(nscoord& aWidth,
|
||||
|
|
|
@ -110,7 +110,7 @@ class nsDeviceContext final {
|
|||
/**
|
||||
* Return the bit depth of the device.
|
||||
*/
|
||||
nsresult GetDepth(uint32_t& aDepth);
|
||||
uint32_t GetDepth();
|
||||
|
||||
/**
|
||||
* Get the size of the displayable area of the output device
|
||||
|
|
|
@ -522,6 +522,8 @@ bool gfxPlatformFontList::InitFontList() {
|
|||
// unless InitFontListForPlatform() fails and we reset it below.
|
||||
mFontlistInitCount++;
|
||||
|
||||
InitializeCodepointsWithNoFonts();
|
||||
|
||||
// Try to initialize the cross-process shared font list if enabled by prefs,
|
||||
// but not if we're running in Safe Mode.
|
||||
if (StaticPrefs::gfx_e10s_font_list_shared_AtStartup() &&
|
||||
|
|
|
@ -1484,7 +1484,6 @@ static bool BytecodeIsEffectful(JSOp op) {
|
|||
case JSOp::InitHiddenElemGetter:
|
||||
case JSOp::InitElemSetter:
|
||||
case JSOp::InitHiddenElemSetter:
|
||||
case JSOp::FunCall:
|
||||
case JSOp::SpreadCall:
|
||||
case JSOp::Call:
|
||||
case JSOp::CallIgnoresRv:
|
||||
|
|
|
@ -146,10 +146,6 @@ function treatAsSafeArgument(entry, varName, csuName)
|
|||
[/^Gecko_/, null, "nsStyleImageLayers"],
|
||||
[/^Gecko_/, null, /FontFamilyList/],
|
||||
|
||||
// RawGeckoBorrowedNode thread-mutable parameters.
|
||||
["Gecko_SetNodeFlags", "aNode", null],
|
||||
["Gecko_UnsetNodeFlags", "aNode", null],
|
||||
|
||||
// Various Servo binding out parameters. This is a mess and there needs
|
||||
// to be a way to indicate which params are out parameters, either using
|
||||
// an attribute or a naming convention.
|
||||
|
|
|
@ -8070,7 +8070,7 @@ ParseNode* BytecodeEmitter::getCoordNode(ParseNode* callNode,
|
|||
ParseNode* calleeNode, JSOp op,
|
||||
ListNode* argsList) {
|
||||
ParseNode* coordNode = callNode;
|
||||
if (op == JSOp::Call || op == JSOp::SpreadCall || op == JSOp::FunCall) {
|
||||
if (op == JSOp::Call || op == JSOp::SpreadCall) {
|
||||
// Default to using the location of the `(` itself.
|
||||
// obj[expr]() // expression
|
||||
// ^ // column coord
|
||||
|
|
|
@ -280,7 +280,7 @@ class MOZ_STACK_CLASS CallOrNewEmitter {
|
|||
private:
|
||||
[[nodiscard]] bool isCall() const {
|
||||
return op_ == JSOp::Call || op_ == JSOp::CallIgnoresRv ||
|
||||
op_ == JSOp::SpreadCall || isEval() || isFunCall();
|
||||
op_ == JSOp::SpreadCall || isEval();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNew() const {
|
||||
|
@ -296,8 +296,6 @@ class MOZ_STACK_CLASS CallOrNewEmitter {
|
|||
op_ == JSOp::SpreadEval || op_ == JSOp::StrictSpreadEval;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isFunCall() const { return op_ == JSOp::FunCall; }
|
||||
|
||||
[[nodiscard]] bool isSpread() const { return IsSpreadOp(op_); }
|
||||
|
||||
[[nodiscard]] bool isSingleSpread() const {
|
||||
|
|
|
@ -10730,13 +10730,7 @@ typename ParseHandler::Node GeneralParser<ParseHandler, Unit>::memberCall(
|
|||
|
||||
JSOp op = JSOp::Call;
|
||||
bool maybeAsyncArrow = false;
|
||||
if (auto prop = handler_.maybeDottedProperty(lhs)) {
|
||||
// Use the JSOp::FunCall optimizations given the right syntax.
|
||||
if (prop == TaggedParserAtomIndex::WellKnown::call()) {
|
||||
op = JSOp::FunCall;
|
||||
}
|
||||
} else if (tt == TokenKind::LeftParen &&
|
||||
optionalKind == OptionalKind::NonOptional) {
|
||||
if (tt == TokenKind::LeftParen && optionalKind == OptionalKind::NonOptional) {
|
||||
if (handler_.isAsyncKeyword(lhs)) {
|
||||
// |async (| can be the start of an async arrow
|
||||
// function, so we need to defer reporting possible
|
||||
|
|
|
@ -34,11 +34,7 @@ gczeal(0);
|
|||
assertEqPreciseStacks(
|
||||
endProfiling(),
|
||||
[
|
||||
// Expected output for (simulator+via-Ion).
|
||||
['', '!>', '0,!>', '<,0,!>', 'filtering GC postbarrier,0,!>',
|
||||
'<,0,!>', '0,!>', '!>', ''],
|
||||
|
||||
// Expected output for (simulator+baseline).
|
||||
// Expected output for (simulator+via-Ion/baseline).
|
||||
['', '!>', '0,!>', '<,0,!>', 'GC postbarrier,0,!>',
|
||||
'<,0,!>', '0,!>', '!>', ''],
|
||||
|
||||
|
@ -55,8 +51,8 @@ gczeal(0);
|
|||
assertEqPreciseStacks(
|
||||
endProfiling(),
|
||||
[
|
||||
// Expected output for (simulator+via-Ion).
|
||||
['', '!>', '0,!>', '', '0,!>', '<,0,!>', 'filtering GC postbarrier,0,!>',
|
||||
// Expected output for (simulator+via-Ion/baseline).
|
||||
['', '!>', '0,!>', '', '0,!>', '<,0,!>', 'GC postbarrier,0,!>',
|
||||
'<,0,!>', '0,!>', '!>', ''],
|
||||
|
||||
// Expected output for other configurations.
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
// Add test for issue with a post-write barrier that doesn't remove
|
||||
// store buffer entries when used on a table that may grow.
|
||||
|
||||
let {set, table} = wasmEvalText(`(module
|
||||
(table (export "table") 1 externref)
|
||||
(func (export "set") (param externref)
|
||||
i32.const 0
|
||||
local.get 0
|
||||
table.set
|
||||
)
|
||||
)`).exports;
|
||||
|
||||
let tenured = {};
|
||||
gc(tenured);
|
||||
assertEq(isNurseryAllocated(tenured), false);
|
||||
let nursery = {};
|
||||
assertEq(isNurseryAllocated(nursery), true);
|
||||
set(nursery);
|
||||
set(null);
|
||||
assertEq(table.grow(1000), 1, 'table grows');
|
||||
gc();
|
|
@ -225,6 +225,12 @@ class ExceptionBailoutInfo {
|
|||
|
||||
[[nodiscard]] bool FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfoArg);
|
||||
|
||||
#ifdef DEBUG
|
||||
[[nodiscard]] bool AssertBailoutStackDepth(JSContext* cx, JSScript* script,
|
||||
jsbytecode* pc, ResumeMode mode,
|
||||
uint32_t exprStackSlots);
|
||||
#endif
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "builtin/ModuleObject.h"
|
||||
#include "debugger/DebugAPI.h"
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
#include "jit/Bailouts.h"
|
||||
#include "jit/BaselineFrame.h"
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
|
@ -114,6 +115,7 @@ class MOZ_STACK_CLASS BaselineStackBuilder {
|
|||
|
||||
jsbytecode* pc_ = nullptr;
|
||||
JSOp op_ = JSOp::Nop;
|
||||
mozilla::Maybe<ResumeMode> resumeMode_;
|
||||
uint32_t exprStackSlots_ = 0;
|
||||
void* prevFramePtr_ = nullptr;
|
||||
Maybe<BufferPointer<BaselineFrame>> blFrame_;
|
||||
|
@ -224,8 +226,10 @@ class MOZ_STACK_CLASS BaselineStackBuilder {
|
|||
return !catchingException() && iter_.resumeAfter();
|
||||
}
|
||||
|
||||
ResumeMode resumeMode() const { return *resumeMode_; }
|
||||
|
||||
bool needToSaveCallerArgs() const {
|
||||
return IsIonInlinableGetterOrSetterOp(op_);
|
||||
return resumeMode() == ResumeMode::InlinedAccessor;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool enlarge() {
|
||||
|
@ -546,6 +550,7 @@ bool BaselineStackBuilder::initFrame() {
|
|||
pc_ = catchingException() ? excInfo_->resumePC()
|
||||
: script_->offsetToPC(iter_.pcOffset());
|
||||
op_ = JSOp(*pc_);
|
||||
resumeMode_ = mozilla::Some(iter_.resumeMode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -744,7 +749,7 @@ bool BaselineStackBuilder::buildFixedSlots() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// The caller side of inlined JSOp::FunCall and accessors must look
|
||||
// The caller side of inlined js::fun_call and accessors must look
|
||||
// like the function wasn't inlined.
|
||||
bool BaselineStackBuilder::fixUpCallerArgs(
|
||||
MutableHandleValueVector savedCallerArgs, bool* fixedUp) {
|
||||
|
@ -753,18 +758,20 @@ bool BaselineStackBuilder::fixUpCallerArgs(
|
|||
// Inlining of SpreadCall-like frames not currently supported.
|
||||
MOZ_ASSERT(!IsSpreadOp(op_));
|
||||
|
||||
if (op_ != JSOp::FunCall && !needToSaveCallerArgs()) {
|
||||
if (resumeMode() != ResumeMode::InlinedFunCall && !needToSaveCallerArgs()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate how many arguments are consumed by the inlined call.
|
||||
// All calls pass |callee| and |this|.
|
||||
uint32_t inlinedArgs = 2;
|
||||
if (op_ == JSOp::FunCall) {
|
||||
if (resumeMode() == ResumeMode::InlinedFunCall) {
|
||||
// The first argument to an inlined FunCall becomes |this|,
|
||||
// if it exists. The rest are passed normally.
|
||||
MOZ_ASSERT(IsInvokeOp(op_));
|
||||
inlinedArgs += GET_ARGC(pc_) > 0 ? GET_ARGC(pc_) - 1 : 0;
|
||||
} else {
|
||||
MOZ_ASSERT(resumeMode() == ResumeMode::InlinedAccessor);
|
||||
MOZ_ASSERT(IsIonInlinableGetterOrSetterOp(op_));
|
||||
// Setters are passed one argument. Getters are passed none.
|
||||
if (IsSetPropOp(op_)) {
|
||||
|
@ -787,11 +794,11 @@ bool BaselineStackBuilder::fixUpCallerArgs(
|
|||
}
|
||||
}
|
||||
|
||||
// When we inline JSOp::FunCall, we bypass the native and inline the
|
||||
// When we inline js::fun_call, we bypass the native and inline the
|
||||
// target directly. When rebuilding the stack, we need to fill in
|
||||
// the right number of slots to make it look like the js_native was
|
||||
// actually called.
|
||||
if (op_ == JSOp::FunCall) {
|
||||
if (resumeMode() == ResumeMode::InlinedFunCall) {
|
||||
// We must transform the stack from |target, this, args| to
|
||||
// |js_fun_call, target, this, args|. The value of |js_fun_call|
|
||||
// will never be observed, so we push |undefined| for it, followed
|
||||
|
@ -994,7 +1001,7 @@ bool BaselineStackBuilder::buildStubFrame(uint32_t frameSize,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
} else if (op_ == JSOp::FunCall && GET_ARGC(pc_) == 0) {
|
||||
} else if (resumeMode() == ResumeMode::InlinedFunCall && GET_ARGC(pc_) == 0) {
|
||||
// When calling FunCall with 0 arguments, we push |undefined|
|
||||
// for this. See BaselineCacheIRCompiler::pushFunCallArguments.
|
||||
MOZ_ASSERT(!pushedNewTarget);
|
||||
|
@ -1012,8 +1019,10 @@ bool BaselineStackBuilder::buildStubFrame(uint32_t frameSize,
|
|||
callee = *blFrame()->valueSlot(calleeSlot);
|
||||
|
||||
} else {
|
||||
MOZ_ASSERT(resumeMode() == ResumeMode::InlinedStandardCall ||
|
||||
resumeMode() == ResumeMode::InlinedFunCall);
|
||||
actualArgc = GET_ARGC(pc_);
|
||||
if (op_ == JSOp::FunCall) {
|
||||
if (resumeMode() == ResumeMode::InlinedFunCall) {
|
||||
// See BaselineCacheIRCompiler::pushFunCallArguments.
|
||||
MOZ_ASSERT(actualArgc > 0);
|
||||
actualArgc--;
|
||||
|
@ -1278,6 +1287,61 @@ bool BaselineStackBuilder::envChainSlotCanBeOptimized() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool jit::AssertBailoutStackDepth(JSContext* cx, JSScript* script,
|
||||
jsbytecode* pc, ResumeMode mode,
|
||||
uint32_t exprStackSlots) {
|
||||
if (mode == ResumeMode::ResumeAfter) {
|
||||
pc = GetNextPc(pc);
|
||||
}
|
||||
|
||||
uint32_t expectedDepth;
|
||||
bool reachablePC;
|
||||
if (!ReconstructStackDepth(cx, script, pc, &expectedDepth, &reachablePC)) {
|
||||
return false;
|
||||
}
|
||||
if (!reachablePC) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSOp op = JSOp(*pc);
|
||||
|
||||
if (mode == ResumeMode::InlinedFunCall) {
|
||||
// For inlined fun.call(this, ...); the reconstructed stack depth will
|
||||
// include the |this|, but the exprStackSlots won't.
|
||||
// Exception: if there are no arguments, the depths do match.
|
||||
MOZ_ASSERT(IsInvokeOp(op));
|
||||
if (GET_ARGC(pc) > 0) {
|
||||
MOZ_ASSERT(expectedDepth == exprStackSlots + 1);
|
||||
} else {
|
||||
MOZ_ASSERT(expectedDepth == exprStackSlots);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mode == ResumeMode::InlinedAccessor) {
|
||||
// Accessors coming out of ion are inlined via a complete lie perpetrated by
|
||||
// the compiler internally. Ion just rearranges the stack, and pretends that
|
||||
// it looked like a call all along.
|
||||
// This means that the depth is actually one *more* than expected by the
|
||||
// interpreter, as there is now a JSFunction, |this| and [arg], rather than
|
||||
// the expected |this| and [arg].
|
||||
// If the inlined accessor is a GetElem operation, the numbers do match, but
|
||||
// that's just because GetElem expects one more item on the stack. Note that
|
||||
// none of that was pushed, but it's still reflected in exprStackSlots.
|
||||
MOZ_ASSERT(IsIonInlinableGetterOrSetterOp(op));
|
||||
if (IsGetElemOp(op)) {
|
||||
MOZ_ASSERT(exprStackSlots == expectedDepth);
|
||||
} else {
|
||||
MOZ_ASSERT(exprStackSlots == expectedDepth + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// In all other cases, the depth must match.
|
||||
MOZ_ASSERT(exprStackSlots == expectedDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaselineStackBuilder::validateFrame() {
|
||||
const uint32_t frameSize = framePushed();
|
||||
blFrame()->setDebugFrameSize(frameSize);
|
||||
|
@ -1287,38 +1351,8 @@ bool BaselineStackBuilder::validateFrame() {
|
|||
MOZ_ASSERT(blFrame()->debugNumValueSlots() >= script_->nfixed());
|
||||
MOZ_ASSERT(blFrame()->debugNumValueSlots() <= script_->nslots());
|
||||
|
||||
uint32_t expectedDepth;
|
||||
bool reachablePC;
|
||||
jsbytecode* pcForStackDepth = resumeAfter() ? GetNextPc(pc_) : pc_;
|
||||
if (!ReconstructStackDepth(cx_, script_, pcForStackDepth, &expectedDepth,
|
||||
&reachablePC)) {
|
||||
return false;
|
||||
}
|
||||
if (!reachablePC) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (op_ == JSOp::FunCall) {
|
||||
// For fun.call(this, ...); the reconstructed stack depth will
|
||||
// include the this. When inlining that is not included.
|
||||
// So the exprStackSlots will be one less.
|
||||
MOZ_ASSERT(expectedDepth - exprStackSlots() <= 1);
|
||||
} else if (iter_.moreFrames() && IsIonInlinableGetterOrSetterOp(op_)) {
|
||||
// Accessors coming out of ion are inlined via a complete
|
||||
// lie perpetrated by the compiler internally. Ion just rearranges
|
||||
// the stack, and pretends that it looked like a call all along.
|
||||
// This means that the depth is actually one *more* than expected
|
||||
// by the interpreter, as there is now a JSFunction, |this| and [arg],
|
||||
// rather than the expected |this| and [arg].
|
||||
// If the inlined accessor is a getelem operation, the numbers do match,
|
||||
// but that's just because getelem expects one more item on the stack.
|
||||
// Note that none of that was pushed, but it's still reflected
|
||||
// in exprStackSlots.
|
||||
MOZ_ASSERT(exprStackSlots() - expectedDepth == (IsGetElemOp(op_) ? 0 : 1));
|
||||
} else {
|
||||
MOZ_ASSERT(exprStackSlots() == expectedDepth);
|
||||
}
|
||||
return true;
|
||||
return AssertBailoutStackDepth(cx_, script_, pc_, resumeMode(),
|
||||
exprStackSlots());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -4494,11 +4494,6 @@ bool BaselineCodeGen<Handler>::emit_SuperCall() {
|
|||
return emitCall(JSOp::SuperCall);
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
bool BaselineCodeGen<Handler>::emit_FunCall() {
|
||||
return emitCall(JSOp::FunCall);
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
bool BaselineCodeGen<Handler>::emit_Eval() {
|
||||
return emitCall(JSOp::Eval);
|
||||
|
|
|
@ -295,7 +295,6 @@ class MOZ_STATIC_CLASS OpToFallbackKindTable {
|
|||
setKind(JSOp::Call, BaselineICFallbackKind::Call);
|
||||
setKind(JSOp::CallIgnoresRv, BaselineICFallbackKind::Call);
|
||||
setKind(JSOp::CallIter, BaselineICFallbackKind::Call);
|
||||
setKind(JSOp::FunCall, BaselineICFallbackKind::Call);
|
||||
setKind(JSOp::Eval, BaselineICFallbackKind::Call);
|
||||
setKind(JSOp::StrictEval, BaselineICFallbackKind::Call);
|
||||
|
||||
|
@ -1582,8 +1581,8 @@ bool DoCallFallback(JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub,
|
|||
}
|
||||
} else {
|
||||
MOZ_ASSERT(op == JSOp::Call || op == JSOp::CallIgnoresRv ||
|
||||
op == JSOp::CallIter || op == JSOp::FunCall ||
|
||||
op == JSOp::Eval || op == JSOp::StrictEval);
|
||||
op == JSOp::CallIter || op == JSOp::Eval ||
|
||||
op == JSOp::StrictEval);
|
||||
if (op == JSOp::CallIter && callee.isPrimitive()) {
|
||||
MOZ_ASSERT(argc == 0, "thisv must be on top of the stack");
|
||||
ReportValueError(cx, JSMSG_NOT_ITERABLE, -1, callArgs.thisv(), nullptr);
|
||||
|
|
|
@ -9843,7 +9843,6 @@ AttachDecision CallIRGenerator::tryAttachStub() {
|
|||
case JSOp::SpreadNew:
|
||||
case JSOp::SuperCall:
|
||||
case JSOp::SpreadSuperCall:
|
||||
case JSOp::FunCall:
|
||||
break;
|
||||
default:
|
||||
return AttachDecision::NoAction;
|
||||
|
@ -9874,7 +9873,7 @@ AttachDecision CallIRGenerator::tryAttachStub() {
|
|||
// Try inlining Function.prototype.{call,apply}. We don't use the
|
||||
// InlinableNative mechanism for this because we want to optimize these more
|
||||
// aggressively than other natives.
|
||||
if (op_ == JSOp::FunCall || op_ == JSOp::Call || op_ == JSOp::CallIgnoresRv) {
|
||||
if (op_ == JSOp::Call || op_ == JSOp::CallIgnoresRv) {
|
||||
TRY_ATTACH(tryAttachFunCall(calleeFunc));
|
||||
TRY_ATTACH(tryAttachFunApply(calleeFunc));
|
||||
}
|
||||
|
|
|
@ -8178,11 +8178,26 @@ void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) {
|
|||
}
|
||||
}
|
||||
|
||||
void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) {
|
||||
Register elements = ToRegister(ins->elements());
|
||||
Register index = ToRegister(ins->index());
|
||||
Register output = ToRegister(ins->output());
|
||||
masm.loadPtr(BaseIndex(elements, index, ScalePointer), output);
|
||||
}
|
||||
|
||||
void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) {
|
||||
masm.movePtr(ToRegister(ins->base()), ToRegister(ins->output()));
|
||||
masm.addPtr(Imm32(int32_t(ins->offset())), ToRegister(ins->output()));
|
||||
}
|
||||
|
||||
void CodeGenerator::visitWasmDerivedIndexPointer(
|
||||
LWasmDerivedIndexPointer* ins) {
|
||||
Register base = ToRegister(ins->base());
|
||||
Register index = ToRegister(ins->index());
|
||||
Register output = ToRegister(ins->output());
|
||||
masm.computeEffectiveAddress(BaseIndex(base, index, ins->scale()), output);
|
||||
}
|
||||
|
||||
void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) {
|
||||
Register tls = ToRegister(ins->tls());
|
||||
Register valueAddr = ToRegister(ins->valueAddr());
|
||||
|
|
|
@ -907,7 +907,7 @@ static void EliminateTriviallyDeadResumePointOperands(MIRGraph& graph,
|
|||
MResumePoint* rp) {
|
||||
// If we will pop the top of the stack immediately after resuming,
|
||||
// then don't preserve the top value in the resume point.
|
||||
if (rp->mode() != MResumePoint::ResumeAt || JSOp(*rp->pc()) != JSOp::Pop) {
|
||||
if (rp->mode() != ResumeMode::ResumeAt || JSOp(*rp->pc()) != JSOp::Pop) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -991,6 +991,42 @@ constexpr T SplatByteToUInt(uint8_t val, uint8_t x) {
|
|||
return splatted;
|
||||
}
|
||||
|
||||
// Resume information for a frame, stored in a resume point.
|
||||
enum class ResumeMode : uint8_t {
|
||||
// Innermost frame. Resume at the next bytecode op when bailing out.
|
||||
ResumeAfter,
|
||||
|
||||
// Innermost frame. Resume at the current bytecode op when bailing out.
|
||||
ResumeAt,
|
||||
|
||||
// Outer frame for an inlined "standard" call at an IsInvokeOp bytecode op.
|
||||
InlinedStandardCall,
|
||||
|
||||
// Outer frame for an inlined js::fun_call at an IsInvokeOp bytecode op.
|
||||
InlinedFunCall,
|
||||
|
||||
// Outer frame for an inlined getter/setter at a Get*/Set* bytecode op.
|
||||
InlinedAccessor,
|
||||
|
||||
Last = InlinedAccessor
|
||||
};
|
||||
|
||||
inline const char* ResumeModeToString(ResumeMode mode) {
|
||||
switch (mode) {
|
||||
case ResumeMode::ResumeAfter:
|
||||
return "ResumeAfter";
|
||||
case ResumeMode::ResumeAt:
|
||||
return "ResumeAt";
|
||||
case ResumeMode::InlinedStandardCall:
|
||||
return "InlinedStandardCall";
|
||||
case ResumeMode::InlinedFunCall:
|
||||
return "InlinedFunCall";
|
||||
case ResumeMode::InlinedAccessor:
|
||||
return "InlinedAccessor";
|
||||
}
|
||||
MOZ_CRASH("Invalid mode");
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -476,14 +476,12 @@ class SnapshotIterator {
|
|||
public:
|
||||
// Exhibits frame properties contained in the snapshot.
|
||||
uint32_t pcOffset() const;
|
||||
[[nodiscard]] inline bool resumeAfter() const {
|
||||
// Inline frames are inlined on calls, which are considered as being
|
||||
// resumed on the Call as baseline will push the pc once we return from
|
||||
// the call.
|
||||
if (moreFrames()) {
|
||||
return false;
|
||||
}
|
||||
return recover_.resumeAfter();
|
||||
ResumeMode resumeMode() const;
|
||||
|
||||
bool resumeAfter() const {
|
||||
// Calls in outer frames are never considered resume-after.
|
||||
MOZ_ASSERT_IF(moreFrames(), resumeMode() != ResumeMode::ResumeAfter);
|
||||
return resumeMode() == ResumeMode::ResumeAfter;
|
||||
}
|
||||
inline BailoutKind bailoutKind() const { return snapshot_.bailoutKind(); }
|
||||
|
||||
|
|
|
@ -45,17 +45,7 @@ void JSONSpewer::spewMResumePoint(MResumePoint* rp) {
|
|||
property("caller", rp->caller()->block()->id());
|
||||
}
|
||||
|
||||
switch (rp->mode()) {
|
||||
case MResumePoint::ResumeAt:
|
||||
property("mode", "At");
|
||||
break;
|
||||
case MResumePoint::ResumeAfter:
|
||||
property("mode", "After");
|
||||
break;
|
||||
case MResumePoint::Outer:
|
||||
property("mode", "Outer");
|
||||
break;
|
||||
}
|
||||
property("mode", ResumeModeToString(rp->mode()));
|
||||
|
||||
beginListProperty("operands");
|
||||
for (MResumePoint* iter = rp; iter; iter = iter->caller()) {
|
||||
|
|
|
@ -1817,6 +1817,10 @@ uint32_t SnapshotIterator::pcOffset() const {
|
|||
return resumePoint()->pcOffset();
|
||||
}
|
||||
|
||||
ResumeMode SnapshotIterator::resumeMode() const {
|
||||
return resumePoint()->mode();
|
||||
}
|
||||
|
||||
void SnapshotIterator::skipInstruction() {
|
||||
MOZ_ASSERT(snapshot_.numAllocationsRead() == 0);
|
||||
size_t numOperands = instruction()->numOperands();
|
||||
|
@ -2043,18 +2047,23 @@ void InlineFrameIterator::findNextFrame() {
|
|||
|
||||
size_t i = 1;
|
||||
for (; i <= remaining && si_.moreFrames(); i++) {
|
||||
ResumeMode mode = si_.resumeMode();
|
||||
MOZ_ASSERT(IsIonInlinableOp(JSOp(*pc_)));
|
||||
|
||||
// Recover the number of actual arguments from the script.
|
||||
if (IsInvokeOp(JSOp(*pc_))) {
|
||||
MOZ_ASSERT(mode == ResumeMode::InlinedStandardCall ||
|
||||
mode == ResumeMode::InlinedFunCall);
|
||||
numActualArgs_ = GET_ARGC(pc_);
|
||||
if (JSOp(*pc_) == JSOp::FunCall && numActualArgs_ > 0) {
|
||||
if (mode == ResumeMode::InlinedFunCall && numActualArgs_ > 0) {
|
||||
numActualArgs_--;
|
||||
}
|
||||
} else if (IsGetPropPC(pc_) || IsGetElemPC(pc_)) {
|
||||
MOZ_ASSERT(mode == ResumeMode::InlinedAccessor);
|
||||
numActualArgs_ = 0;
|
||||
} else {
|
||||
MOZ_RELEASE_ASSERT(IsSetPropPC(pc_));
|
||||
MOZ_ASSERT(mode == ResumeMode::InlinedAccessor);
|
||||
numActualArgs_ = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -2723,9 +2723,18 @@
|
|||
arguments:
|
||||
offset: size_t
|
||||
|
||||
- name: WasmLoadTableElement
|
||||
result_type: WordSized
|
||||
operands:
|
||||
elements: WordSized
|
||||
index: WordSized
|
||||
|
||||
- name: WasmDerivedPointer
|
||||
gen_boilerplate: false
|
||||
|
||||
- name: WasmDerivedIndexPointer
|
||||
gen_boilerplate: false
|
||||
|
||||
- name: WasmStoreRef
|
||||
operands:
|
||||
tls: WordSized
|
||||
|
|
|
@ -5116,6 +5116,12 @@ void LIRGenerator::visitWasmLoadGlobalCell(MWasmLoadGlobalCell* ins) {
|
|||
}
|
||||
}
|
||||
|
||||
void LIRGenerator::visitWasmLoadTableElement(MWasmLoadTableElement* ins) {
|
||||
LAllocation elements = useRegisterAtStart(ins->elements());
|
||||
LAllocation index = useRegisterAtStart(ins->index());
|
||||
define(new (alloc()) LWasmLoadTableElement(elements, index), ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitWasmStoreGlobalVar(MWasmStoreGlobalVar* ins) {
|
||||
MDefinition* value = ins->value();
|
||||
size_t offs = wasm::Instance::offsetOfGlobalArea() + ins->globalDataOffset();
|
||||
|
@ -5178,6 +5184,12 @@ void LIRGenerator::visitWasmDerivedPointer(MWasmDerivedPointer* ins) {
|
|||
define(new (alloc()) LWasmDerivedPointer(base), ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitWasmDerivedIndexPointer(MWasmDerivedIndexPointer* ins) {
|
||||
LAllocation base = useRegisterAtStart(ins->base());
|
||||
LAllocation index = useRegisterAtStart(ins->index());
|
||||
define(new (alloc()) LWasmDerivedIndexPointer(base, index), ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitWasmStoreRef(MWasmStoreRef* ins) {
|
||||
LAllocation tls = useRegister(ins->tls());
|
||||
LAllocation valueAddr = useFixed(ins->valueAddr(), PreBarrierReg);
|
||||
|
|
|
@ -3340,7 +3340,7 @@ MUrsh* MUrsh::NewWasm(TempAllocator& alloc, MDefinition* left,
|
|||
}
|
||||
|
||||
MResumePoint* MResumePoint::New(TempAllocator& alloc, MBasicBlock* block,
|
||||
jsbytecode* pc, Mode mode) {
|
||||
jsbytecode* pc, ResumeMode mode) {
|
||||
MResumePoint* resume = new (alloc) MResumePoint(block, pc, mode);
|
||||
if (!resume->init(alloc)) {
|
||||
block->discardPreAllocatedResumePoint(resume);
|
||||
|
@ -3350,7 +3350,7 @@ MResumePoint* MResumePoint::New(TempAllocator& alloc, MBasicBlock* block,
|
|||
return resume;
|
||||
}
|
||||
|
||||
MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, Mode mode)
|
||||
MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode)
|
||||
: MNode(block, Kind::ResumePoint),
|
||||
pc_(pc),
|
||||
instruction_(nullptr),
|
||||
|
@ -3400,18 +3400,15 @@ void MResumePoint::dump(GenericPrinter& out) const {
|
|||
out.printf("resumepoint mode=");
|
||||
|
||||
switch (mode()) {
|
||||
case MResumePoint::ResumeAt:
|
||||
case ResumeMode::ResumeAt:
|
||||
if (instruction_) {
|
||||
out.printf("At(%u)", instruction_->id());
|
||||
out.printf("ResumeAt(%u)", instruction_->id());
|
||||
} else {
|
||||
out.printf("At");
|
||||
out.printf("ResumeAt");
|
||||
}
|
||||
break;
|
||||
case MResumePoint::ResumeAfter:
|
||||
out.printf("After");
|
||||
break;
|
||||
case MResumePoint::Outer:
|
||||
out.printf("Outer");
|
||||
default:
|
||||
out.put(ResumeModeToString(mode()));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
108
js/src/jit/MIR.h
108
js/src/jit/MIR.h
|
@ -333,32 +333,34 @@ class AliasSet {
|
|||
1 << 9, // An array buffer view's length or byteOffset
|
||||
WasmGlobalCell = 1 << 10, // A wasm global cell
|
||||
WasmTableElement = 1 << 11, // An element of a wasm table
|
||||
WasmStackResult = 1 << 12, // A stack result from the current function
|
||||
WasmTableMeta = 1 << 12, // A wasm table elements pointer and
|
||||
// length field, in Tls.
|
||||
WasmStackResult = 1 << 13, // A stack result from the current function
|
||||
|
||||
// JSContext's exception state. This is used on instructions like MThrow
|
||||
// or MNewArrayDynamicLength that throw exceptions (other than OOM) but have
|
||||
// no other side effect, to ensure that they get their own up-to-date resume
|
||||
// point. (This resume point will be used when constructing the Baseline
|
||||
// frame during exception bailouts.)
|
||||
ExceptionState = 1 << 13,
|
||||
ExceptionState = 1 << 14,
|
||||
|
||||
// Used for instructions that load the privateSlot of DOM proxies and
|
||||
// the ExpandoAndGeneration.
|
||||
DOMProxyExpando = 1 << 14,
|
||||
DOMProxyExpando = 1 << 15,
|
||||
|
||||
// Hash table of a Map or Set object.
|
||||
MapOrSetHashTable = 1 << 15,
|
||||
MapOrSetHashTable = 1 << 16,
|
||||
|
||||
// Internal state of the random number generator
|
||||
RNG = 1 << 16,
|
||||
RNG = 1 << 17,
|
||||
|
||||
// The pendingException slot on the wasm tls object.
|
||||
WasmPendingException = 1 << 17,
|
||||
WasmPendingException = 1 << 18,
|
||||
|
||||
Last = WasmPendingException,
|
||||
|
||||
Any = Last | (Last - 1),
|
||||
NumCategories = 18,
|
||||
NumCategories = 19,
|
||||
|
||||
// Indicates load or store.
|
||||
Store_ = 1 << 31
|
||||
|
@ -8444,13 +8446,6 @@ class MResumePoint final : public MNode
|
|||
public InlineForwardListNode<MResumePoint>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
enum Mode {
|
||||
ResumeAt, // Resume until before the current instruction
|
||||
ResumeAfter, // Resume after the current instruction
|
||||
Outer // State before inlining.
|
||||
};
|
||||
|
||||
private:
|
||||
friend class MBasicBlock;
|
||||
friend void AssertBasicGraphCoherency(MIRGraph& graph, bool force);
|
||||
|
@ -8464,9 +8459,9 @@ class MResumePoint final : public MNode
|
|||
|
||||
jsbytecode* pc_;
|
||||
MInstruction* instruction_;
|
||||
Mode mode_;
|
||||
ResumeMode mode_;
|
||||
|
||||
MResumePoint(MBasicBlock* block, jsbytecode* pc, Mode mode);
|
||||
MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode);
|
||||
void inherit(MBasicBlock* state);
|
||||
|
||||
// Calling isDefinition or isResumePoint on MResumePoint is unnecessary.
|
||||
|
@ -8490,7 +8485,7 @@ class MResumePoint final : public MNode
|
|||
|
||||
public:
|
||||
static MResumePoint* New(TempAllocator& alloc, MBasicBlock* block,
|
||||
jsbytecode* pc, Mode mode);
|
||||
jsbytecode* pc, ResumeMode mode);
|
||||
|
||||
MBasicBlock* block() const { return resumePointBlock(); }
|
||||
|
||||
|
@ -8535,7 +8530,7 @@ class MResumePoint final : public MNode
|
|||
MOZ_ASSERT(instruction_);
|
||||
instruction_ = nullptr;
|
||||
}
|
||||
Mode mode() const { return mode_; }
|
||||
ResumeMode mode() const { return mode_; }
|
||||
|
||||
void releaseUses() {
|
||||
for (size_t i = 0, e = numOperands(); i < e; i++) {
|
||||
|
@ -8988,11 +8983,12 @@ class MWasmLoadTls : public MUnaryInstruction, public NoTypePolicy::Data {
|
|||
aliases_(aliases) {
|
||||
// Different Tls data have different alias classes and only those classes
|
||||
// are allowed.
|
||||
MOZ_ASSERT(aliases_.flags() ==
|
||||
AliasSet::Load(AliasSet::WasmHeapMeta).flags() ||
|
||||
aliases_.flags() ==
|
||||
AliasSet::Load(AliasSet::WasmPendingException).flags() ||
|
||||
aliases_.flags() == AliasSet::None().flags());
|
||||
MOZ_ASSERT(
|
||||
aliases_.flags() == AliasSet::Load(AliasSet::WasmHeapMeta).flags() ||
|
||||
aliases_.flags() == AliasSet::Load(AliasSet::WasmTableMeta).flags() ||
|
||||
aliases_.flags() ==
|
||||
AliasSet::Load(AliasSet::WasmPendingException).flags() ||
|
||||
aliases_.flags() == AliasSet::None().flags());
|
||||
|
||||
// The only types supported at the moment.
|
||||
MOZ_ASSERT(type == MIRType::Pointer || type == MIRType::Int32 ||
|
||||
|
@ -9081,12 +9077,21 @@ class MWasmHeapBase : public MUnaryInstruction, public NoTypePolicy::Data {
|
|||
// For memory64, bounds check nodes are always of type Int64.
|
||||
|
||||
class MWasmBoundsCheck : public MBinaryInstruction, public NoTypePolicy::Data {
|
||||
public:
|
||||
enum Target {
|
||||
Memory,
|
||||
Table,
|
||||
};
|
||||
|
||||
private:
|
||||
wasm::BytecodeOffset bytecodeOffset_;
|
||||
Target target_;
|
||||
|
||||
explicit MWasmBoundsCheck(MDefinition* index, MDefinition* boundsCheckLimit,
|
||||
wasm::BytecodeOffset bytecodeOffset)
|
||||
wasm::BytecodeOffset bytecodeOffset, Target target)
|
||||
: MBinaryInstruction(classOpcode, index, boundsCheckLimit),
|
||||
bytecodeOffset_(bytecodeOffset) {
|
||||
bytecodeOffset_(bytecodeOffset),
|
||||
target_(target) {
|
||||
MOZ_ASSERT(index->type() == boundsCheckLimit->type());
|
||||
|
||||
// Bounds check is effectful: it throws for OOB.
|
||||
|
@ -9104,6 +9109,8 @@ class MWasmBoundsCheck : public MBinaryInstruction, public NoTypePolicy::Data {
|
|||
|
||||
AliasSet getAliasSet() const override { return AliasSet::None(); }
|
||||
|
||||
bool isMemory() const { return target_ == MWasmBoundsCheck::Memory; }
|
||||
|
||||
bool isRedundant() const { return !isGuard(); }
|
||||
|
||||
void setRedundant() { setNotGuard(); }
|
||||
|
@ -9578,6 +9585,25 @@ class MWasmLoadGlobalCell : public MUnaryInstruction,
|
|||
AliasType mightAlias(const MDefinition* def) const override;
|
||||
};
|
||||
|
||||
class MWasmLoadTableElement : public MBinaryInstruction,
|
||||
public NoTypePolicy::Data {
|
||||
MWasmLoadTableElement(MDefinition* elements, MDefinition* index)
|
||||
: MBinaryInstruction(classOpcode, elements, index) {
|
||||
setResultType(MIRType::RefOrNull);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(WasmLoadTableElement)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, elements))
|
||||
NAMED_OPERANDS((1, index))
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::WasmTableElement);
|
||||
}
|
||||
};
|
||||
|
||||
class MWasmStoreGlobalVar : public MBinaryInstruction,
|
||||
public NoTypePolicy::Data {
|
||||
MWasmStoreGlobalVar(unsigned globalDataOffset, MDefinition* value,
|
||||
|
@ -9668,6 +9694,38 @@ class MWasmDerivedPointer : public MUnaryInstruction,
|
|||
ALLOW_CLONE(MWasmDerivedPointer)
|
||||
};
|
||||
|
||||
class MWasmDerivedIndexPointer : public MBinaryInstruction,
|
||||
public NoTypePolicy::Data {
|
||||
MWasmDerivedIndexPointer(MDefinition* base, MDefinition* index, Scale scale)
|
||||
: MBinaryInstruction(classOpcode, base, index), scale_(scale) {
|
||||
setResultType(MIRType::Pointer);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
Scale scale_;
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(WasmDerivedIndexPointer)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, base))
|
||||
NAMED_OPERANDS((1, index))
|
||||
|
||||
Scale scale() const { return scale_; }
|
||||
|
||||
AliasSet getAliasSet() const override { return AliasSet::None(); }
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins) &&
|
||||
ins->toWasmDerivedIndexPointer()->scale() == scale();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MWasmDerivedIndexPointer)
|
||||
};
|
||||
|
||||
// Stores a reference to an address. This performs a pre-barrier on the address,
|
||||
// but not a post-barrier. A post-barrier must be performed separately, if it's
|
||||
// required.
|
||||
|
||||
class MWasmStoreRef : public MAryInstruction<3>, public NoTypePolicy::Data {
|
||||
AliasSet::Flag aliasSet_;
|
||||
|
||||
|
|
|
@ -219,7 +219,7 @@ MBasicBlock* MBasicBlock::NewSplitEdge(MIRGraph& graph, MBasicBlock* pred,
|
|||
|
||||
// Create a resume point using our initial stack position.
|
||||
MResumePoint* splitEntry = new (graph.alloc())
|
||||
MResumePoint(split, succEntry->pc(), MResumePoint::ResumeAt);
|
||||
MResumePoint(split, succEntry->pc(), ResumeMode::ResumeAt);
|
||||
if (!splitEntry->init(graph.alloc())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -462,7 +462,7 @@ bool MBasicBlock::inherit(TempAllocator& alloc, size_t stackDepth,
|
|||
|
||||
// Create a resume point using our initial stack state.
|
||||
entryResumePoint_ =
|
||||
new (alloc) MResumePoint(this, pc(), MResumePoint::ResumeAt);
|
||||
new (alloc) MResumePoint(this, pc(), ResumeMode::ResumeAt);
|
||||
if (!entryResumePoint_->init(alloc)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -512,7 +512,7 @@ bool MBasicBlock::initEntrySlots(TempAllocator& alloc) {
|
|||
|
||||
// Create a resume point using our initial stack state.
|
||||
entryResumePoint_ =
|
||||
MResumePoint::New(alloc, this, pc(), MResumePoint::ResumeAt);
|
||||
MResumePoint::New(alloc, this, pc(), ResumeMode::ResumeAt);
|
||||
if (!entryResumePoint_) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -2607,6 +2607,9 @@
|
|||
- name: WasmLoadGlobalCell
|
||||
gen_boilerplate: false
|
||||
|
||||
- name: WasmLoadTableElement
|
||||
gen_boilerplate: false
|
||||
|
||||
- name: WasmStoreGlobalVar
|
||||
gen_boilerplate: false
|
||||
|
||||
|
@ -2619,6 +2622,9 @@
|
|||
- name: WasmDerivedPointer
|
||||
gen_boilerplate: false
|
||||
|
||||
- name: WasmDerivedIndexPointer
|
||||
gen_boilerplate: false
|
||||
|
||||
- name: WasmStoreRef
|
||||
gen_boilerplate: false
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "builtin/RegExp.h"
|
||||
#include "builtin/String.h"
|
||||
#include "jit/Bailouts.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/JitSpewer.h"
|
||||
|
@ -68,37 +69,10 @@ bool MResumePoint::writeRecoverData(CompactBufferWriter& writer) const {
|
|||
#ifdef DEBUG
|
||||
// Ensure that all snapshot which are encoded can safely be used for
|
||||
// bailouts.
|
||||
if (GetJitContext()->cx) {
|
||||
uint32_t stackDepth;
|
||||
bool reachablePC;
|
||||
jsbytecode* bailPC = pc();
|
||||
|
||||
if (mode() == MResumePoint::ResumeAfter) {
|
||||
bailPC = GetNextPc(pc());
|
||||
}
|
||||
|
||||
if (!ReconstructStackDepth(GetJitContext()->cx, script, bailPC, &stackDepth,
|
||||
&reachablePC)) {
|
||||
if (JSContext* cx = GetJitContext()->cx) {
|
||||
if (!AssertBailoutStackDepth(cx, script, pc(), mode(), exprStack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reachablePC) {
|
||||
JSOp bailOp = JSOp(*bailPC);
|
||||
if (bailOp == JSOp::FunCall) {
|
||||
// For fun.call(this, ...); the reconstructStackDepth will
|
||||
// include the this. When inlining that is not included. So the
|
||||
// exprStackSlots will be one less.
|
||||
MOZ_ASSERT(stackDepth - exprStack <= 1);
|
||||
} else {
|
||||
// With accessors, we have different stack depths depending on
|
||||
// whether or not we inlined the accessor, as the inlined stack
|
||||
// contains a callee function that should never have been there
|
||||
// and we might just be capturing an uneventful property site,
|
||||
// in which case there won't have been any violence.
|
||||
MOZ_ASSERT_IF(!IsIonInlinableGetterOrSetterOp(bailOp),
|
||||
exprStack == stackDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -119,19 +93,26 @@ bool MResumePoint::writeRecoverData(CompactBufferWriter& writer) const {
|
|||
"Starting frame; implicit %u, formals %u, fixed %zu, exprs %u",
|
||||
implicit, formalArgs - implicit, script->nfixed(), exprStack);
|
||||
|
||||
uint32_t pcoff = script->pcToOffset(pc());
|
||||
JitSpew(JitSpew_IonSnapshots, "Writing pc offset %u, nslots %u", pcoff,
|
||||
nallocs);
|
||||
writer.writeUnsigned(pcoff);
|
||||
uint32_t pcOff = script->pcToOffset(pc());
|
||||
JitSpew(JitSpew_IonSnapshots, "Writing pc offset %u, mode %s, nslots %u",
|
||||
pcOff, ResumeModeToString(mode()), nallocs);
|
||||
|
||||
uint32_t pcOffAndMode =
|
||||
(pcOff << RResumePoint::PCOffsetShift) | uint32_t(mode());
|
||||
MOZ_RELEASE_ASSERT((pcOffAndMode >> RResumePoint::PCOffsetShift) == pcOff,
|
||||
"pcOff doesn't fit in pcOffAndMode");
|
||||
writer.writeUnsigned(pcOffAndMode);
|
||||
|
||||
writer.writeUnsigned(nallocs);
|
||||
return true;
|
||||
}
|
||||
|
||||
RResumePoint::RResumePoint(CompactBufferReader& reader) {
|
||||
pcOffset_ = reader.readUnsigned();
|
||||
pcOffsetAndMode_ = reader.readUnsigned();
|
||||
numOperands_ = reader.readUnsigned();
|
||||
JitSpew(JitSpew_IonSnapshots, "Read RResumePoint (pc offset %u, nslots %u)",
|
||||
pcOffset_, numOperands_);
|
||||
JitSpew(JitSpew_IonSnapshots,
|
||||
"Read RResumePoint (pc offset %u, mode %s, nslots %u)", pcOffset(),
|
||||
ResumeModeToString(mode()), numOperands_);
|
||||
}
|
||||
|
||||
bool RResumePoint::recover(JSContext* cx, SnapshotIterator& iter) const {
|
||||
|
|
|
@ -207,13 +207,22 @@ class MOZ_NON_PARAM RInstruction {
|
|||
|
||||
class RResumePoint final : public RInstruction {
|
||||
private:
|
||||
uint32_t pcOffset_; // Offset from script->code.
|
||||
uint32_t numOperands_; // Number of slots.
|
||||
uint32_t pcOffsetAndMode_; // Offset from script->code and ResumeMode.
|
||||
uint32_t numOperands_; // Number of slots.
|
||||
|
||||
public:
|
||||
RINSTRUCTION_HEADER_(ResumePoint)
|
||||
|
||||
uint32_t pcOffset() const { return pcOffset_; }
|
||||
// Used to encode/decode pcOffsetAndMode_.
|
||||
static constexpr uint32_t PCOffsetShift = 4;
|
||||
static constexpr uint32_t ResumeModeMask = 0b1111;
|
||||
static_assert(uint32_t(ResumeMode::Last) <= ResumeModeMask);
|
||||
|
||||
uint32_t pcOffset() const { return pcOffsetAndMode_ >> PCOffsetShift; }
|
||||
ResumeMode mode() const {
|
||||
return ResumeMode(pcOffsetAndMode_ & ResumeModeMask);
|
||||
}
|
||||
|
||||
uint32_t numOperands() const override { return numOperands_; }
|
||||
[[nodiscard]] bool recover(JSContext* cx,
|
||||
SnapshotIterator& iter) const override;
|
||||
|
|
|
@ -404,17 +404,6 @@ static const uint32_t SNAPSHOT_ROFFSET_SHIFT =
|
|||
static const uint32_t SNAPSHOT_ROFFSET_BITS = 32 - SNAPSHOT_ROFFSET_SHIFT;
|
||||
static const uint32_t SNAPSHOT_ROFFSET_MASK = COMPUTE_MASK_(SNAPSHOT_ROFFSET);
|
||||
|
||||
// Details of recover header packing.
|
||||
static const uint32_t RECOVER_RESUMEAFTER_SHIFT = 0;
|
||||
static const uint32_t RECOVER_RESUMEAFTER_BITS = 1;
|
||||
static const uint32_t RECOVER_RESUMEAFTER_MASK =
|
||||
COMPUTE_MASK_(RECOVER_RESUMEAFTER);
|
||||
|
||||
static const uint32_t RECOVER_RINSCOUNT_SHIFT =
|
||||
COMPUTE_SHIFT_AFTER_(RECOVER_RESUMEAFTER);
|
||||
static const uint32_t RECOVER_RINSCOUNT_BITS = 32 - RECOVER_RINSCOUNT_SHIFT;
|
||||
static const uint32_t RECOVER_RINSCOUNT_MASK = COMPUTE_MASK_(RECOVER_RINSCOUNT);
|
||||
|
||||
#undef COMPUTE_MASK_
|
||||
#undef COMPUTE_SHIFT_AFTER_
|
||||
|
||||
|
@ -478,10 +467,7 @@ SnapshotWriter::SnapshotWriter()
|
|||
|
||||
RecoverReader::RecoverReader(SnapshotReader& snapshot, const uint8_t* recovers,
|
||||
uint32_t size)
|
||||
: reader_(nullptr, nullptr),
|
||||
numInstructions_(0),
|
||||
numInstructionsRead_(0),
|
||||
resumeAfter_(false) {
|
||||
: reader_(nullptr, nullptr), numInstructions_(0), numInstructionsRead_(0) {
|
||||
if (!recovers) {
|
||||
return;
|
||||
}
|
||||
|
@ -494,8 +480,7 @@ RecoverReader::RecoverReader(SnapshotReader& snapshot, const uint8_t* recovers,
|
|||
RecoverReader::RecoverReader(const RecoverReader& rr)
|
||||
: reader_(rr.reader_),
|
||||
numInstructions_(rr.numInstructions_),
|
||||
numInstructionsRead_(rr.numInstructionsRead_),
|
||||
resumeAfter_(rr.resumeAfter_) {
|
||||
numInstructionsRead_(rr.numInstructionsRead_) {
|
||||
if (reader_.currentPosition()) {
|
||||
rr.instruction()->cloneInto(&rawData_);
|
||||
}
|
||||
|
@ -505,7 +490,6 @@ RecoverReader& RecoverReader::operator=(const RecoverReader& rr) {
|
|||
reader_ = rr.reader_;
|
||||
numInstructions_ = rr.numInstructions_;
|
||||
numInstructionsRead_ = rr.numInstructionsRead_;
|
||||
resumeAfter_ = rr.resumeAfter_;
|
||||
if (reader_.currentPosition()) {
|
||||
rr.instruction()->cloneInto(&rawData_);
|
||||
}
|
||||
|
@ -513,15 +497,11 @@ RecoverReader& RecoverReader::operator=(const RecoverReader& rr) {
|
|||
}
|
||||
|
||||
void RecoverReader::readRecoverHeader() {
|
||||
uint32_t bits = reader_.readUnsigned();
|
||||
|
||||
numInstructions_ = (bits & RECOVER_RINSCOUNT_MASK) >> RECOVER_RINSCOUNT_SHIFT;
|
||||
resumeAfter_ = (bits & RECOVER_RESUMEAFTER_MASK) >> RECOVER_RESUMEAFTER_SHIFT;
|
||||
numInstructions_ = reader_.readUnsigned();
|
||||
MOZ_ASSERT(numInstructions_);
|
||||
|
||||
JitSpew(JitSpew_IonSnapshots,
|
||||
"Read recover header with instructionCount %u (ra: %d)",
|
||||
numInstructions_, resumeAfter_);
|
||||
JitSpew(JitSpew_IonSnapshots, "Read recover header with instructionCount %u",
|
||||
numInstructions_);
|
||||
}
|
||||
|
||||
void RecoverReader::readInstruction() {
|
||||
|
@ -600,8 +580,7 @@ void SnapshotWriter::endSnapshot() {
|
|||
uint32_t(writer_.length() - lastStart_), lastStart_);
|
||||
}
|
||||
|
||||
RecoverOffset RecoverWriter::startRecover(uint32_t instructionCount,
|
||||
bool resumeAfter) {
|
||||
RecoverOffset RecoverWriter::startRecover(uint32_t instructionCount) {
|
||||
MOZ_ASSERT(instructionCount);
|
||||
instructionCount_ = instructionCount;
|
||||
instructionsWritten_ = 0;
|
||||
|
@ -609,13 +588,8 @@ RecoverOffset RecoverWriter::startRecover(uint32_t instructionCount,
|
|||
JitSpew(JitSpew_IonSnapshots, "starting recover with %u instruction(s)",
|
||||
instructionCount);
|
||||
|
||||
MOZ_ASSERT(!(uint32_t(resumeAfter) & ~RECOVER_RESUMEAFTER_MASK));
|
||||
MOZ_ASSERT(instructionCount < uint32_t(1 << RECOVER_RINSCOUNT_BITS));
|
||||
uint32_t bits = (uint32_t(resumeAfter) << RECOVER_RESUMEAFTER_SHIFT) |
|
||||
(instructionCount << RECOVER_RINSCOUNT_SHIFT);
|
||||
|
||||
RecoverOffset recoverOffset = writer_.length();
|
||||
writer_.writeUnsigned(bits);
|
||||
writer_.writeUnsigned(instructionCount);
|
||||
return recoverOffset;
|
||||
}
|
||||
|
||||
|
|
|
@ -402,7 +402,7 @@ class RecoverWriter {
|
|||
uint32_t instructionsWritten_;
|
||||
|
||||
public:
|
||||
SnapshotOffset startRecover(uint32_t instructionCount, bool resumeAfter);
|
||||
SnapshotOffset startRecover(uint32_t instructionCount);
|
||||
|
||||
void writeInstruction(const MNode* rp);
|
||||
|
||||
|
@ -496,10 +496,6 @@ class RecoverReader {
|
|||
// Number of instruction read.
|
||||
uint32_t numInstructionsRead_;
|
||||
|
||||
// True if we need to resume after the Resume Point instruction of the
|
||||
// innermost frame.
|
||||
bool resumeAfter_;
|
||||
|
||||
// Space is reserved as part of the RecoverReader to avoid allocations of
|
||||
// data which is needed to decode the current instruction.
|
||||
RInstructionStorage rawData_;
|
||||
|
@ -525,8 +521,6 @@ class RecoverReader {
|
|||
const RInstruction* instruction() const {
|
||||
return reinterpret_cast<const RInstruction*>(rawData_.addr());
|
||||
}
|
||||
|
||||
bool resumeAfter() const { return resumeAfter_; }
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -162,7 +162,7 @@ ICCacheIRStub* TrialInliner::maybeSingleStub(const ICEntry& entry) {
|
|||
Maybe<InlinableOpData> FindInlinableOpData(ICCacheIRStub* stub,
|
||||
BytecodeLocation loc) {
|
||||
if (loc.isInvokeOp()) {
|
||||
Maybe<InlinableCallData> call = FindInlinableCallData(stub, loc);
|
||||
Maybe<InlinableCallData> call = FindInlinableCallData(stub);
|
||||
if (call.isSome()) {
|
||||
return call;
|
||||
}
|
||||
|
@ -182,8 +182,7 @@ Maybe<InlinableOpData> FindInlinableOpData(ICCacheIRStub* stub,
|
|||
return mozilla::Nothing();
|
||||
}
|
||||
|
||||
Maybe<InlinableCallData> FindInlinableCallData(ICCacheIRStub* stub,
|
||||
BytecodeLocation loc) {
|
||||
Maybe<InlinableCallData> FindInlinableCallData(ICCacheIRStub* stub) {
|
||||
Maybe<InlinableCallData> data;
|
||||
|
||||
const CacheIRStubInfo* stubInfo = stub->stubInfo();
|
||||
|
@ -273,15 +272,6 @@ Maybe<InlinableCallData> FindInlinableCallData(ICCacheIRStub* stub,
|
|||
flags.getArgFormat() != CallFlags::FunCall) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
// Warp assumes any inlining at JSOp::FunCall ops is for js::fun_call and
|
||||
// that any fun-call-specific inlining is at JSOp::FunCall ops. Bailouts and
|
||||
// numActualArgs recovery (in InlineFrameIterator::findNextFrame) depend on
|
||||
// this.
|
||||
bool isFunCallOp = loc.getOp() == JSOp::FunCall;
|
||||
bool isFunCall = flags.getArgFormat() == CallFlags::FunCall;
|
||||
if (isFunCallOp != isFunCall) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
data->calleeOperand = calleeGuardOperand;
|
||||
data->callFlags = flags;
|
||||
data->target = target;
|
||||
|
@ -422,9 +412,10 @@ Maybe<InlinableSetterData> FindInlinableSetterData(ICCacheIRStub* stub) {
|
|||
return data;
|
||||
}
|
||||
|
||||
// Return the number of actual arguments that will be passed to the
|
||||
// target function.
|
||||
static uint32_t GetCalleeNumActuals(BytecodeLocation loc) {
|
||||
// Return the maximum number of actual arguments that will be passed to the
|
||||
// target function. This may be an overapproximation, for example when inlining
|
||||
// js::fun_call we may omit an argument.
|
||||
static uint32_t GetMaxCalleeNumActuals(BytecodeLocation loc) {
|
||||
switch (loc.getOp()) {
|
||||
case JSOp::GetProp:
|
||||
case JSOp::GetElem:
|
||||
|
@ -436,12 +427,6 @@ static uint32_t GetCalleeNumActuals(BytecodeLocation loc) {
|
|||
// Setters pass 1 argument.
|
||||
return 1;
|
||||
|
||||
case JSOp::FunCall: {
|
||||
// If FunCall is passed arguments, one of them will become |this|.
|
||||
uint32_t callArgc = loc.getCallArgc();
|
||||
return callArgc > 0 ? callArgc - 1 : 0;
|
||||
}
|
||||
|
||||
case JSOp::Call:
|
||||
case JSOp::CallIgnoresRv:
|
||||
case JSOp::CallIter:
|
||||
|
@ -484,12 +469,12 @@ bool TrialInliner::canInline(JSFunction* target, HandleScript caller,
|
|||
return false;
|
||||
}
|
||||
|
||||
uint32_t calleeNumActuals = GetCalleeNumActuals(loc);
|
||||
uint32_t maxCalleeNumActuals = GetMaxCalleeNumActuals(loc);
|
||||
if (script->needsArgsObj() &&
|
||||
calleeNumActuals > ArgumentsObject::MaxInlinedArgs) {
|
||||
maxCalleeNumActuals > ArgumentsObject::MaxInlinedArgs) {
|
||||
JitSpew(JitSpew_WarpTrialInlining,
|
||||
"SKIP: needs arguments object with %u actual args (maximum %u)",
|
||||
calleeNumActuals, ArgumentsObject::MaxInlinedArgs);
|
||||
maxCalleeNumActuals, ArgumentsObject::MaxInlinedArgs);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -499,7 +484,7 @@ bool TrialInliner::canInline(JSFunction* target, HandleScript caller,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (TooManyFormalArguments(calleeNumActuals)) {
|
||||
if (TooManyFormalArguments(maxCalleeNumActuals)) {
|
||||
JitSpew(JitSpew_WarpTrialInlining, "SKIP: argc too large: %u",
|
||||
unsigned(loc.getCallArgc()));
|
||||
return false;
|
||||
|
@ -637,7 +622,7 @@ bool TrialInliner::maybeInlineCall(ICEntry& entry, ICFallbackStub* fallback,
|
|||
MOZ_ASSERT(!icScript_->hasInlinedChild(fallback->pcOffset()));
|
||||
|
||||
// Look for a CallScriptedFunction with a known target.
|
||||
Maybe<InlinableCallData> data = FindInlinableCallData(stub, loc);
|
||||
Maybe<InlinableCallData> data = FindInlinableCallData(stub);
|
||||
if (data.isNothing()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -649,10 +634,6 @@ bool TrialInliner::maybeInlineCall(ICEntry& entry, ICFallbackStub* fallback,
|
|||
return true;
|
||||
}
|
||||
|
||||
// We only inline FunCall if we are calling the js::fun_call builtin.
|
||||
MOZ_ASSERT_IF(loc.getOp() == JSOp::FunCall,
|
||||
data->callFlags.getArgFormat() == CallFlags::FunCall);
|
||||
|
||||
ICScript* newICScript = createInlinedICScript(data->target, loc);
|
||||
if (!newICScript) {
|
||||
return false;
|
||||
|
@ -762,7 +743,6 @@ bool TrialInliner::tryInlining() {
|
|||
case JSOp::Call:
|
||||
case JSOp::CallIgnoresRv:
|
||||
case JSOp::CallIter:
|
||||
case JSOp::FunCall:
|
||||
case JSOp::New:
|
||||
case JSOp::SuperCall:
|
||||
if (!maybeInlineCall(entry, fallback, loc)) {
|
||||
|
|
|
@ -135,8 +135,7 @@ class InlinableSetterData : public InlinableOpData {
|
|||
mozilla::Maybe<InlinableOpData> FindInlinableOpData(ICCacheIRStub* stub,
|
||||
BytecodeLocation loc);
|
||||
|
||||
mozilla::Maybe<InlinableCallData> FindInlinableCallData(ICCacheIRStub* stub,
|
||||
BytecodeLocation loc);
|
||||
mozilla::Maybe<InlinableCallData> FindInlinableCallData(ICCacheIRStub* stub);
|
||||
mozilla::Maybe<InlinableGetterData> FindInlinableGetterData(
|
||||
ICCacheIRStub* stub);
|
||||
mozilla::Maybe<InlinableSetterData> FindInlinableSetterData(
|
||||
|
|
|
@ -1871,10 +1871,6 @@ bool WarpBuilder::build_CallIter(BytecodeLocation loc) {
|
|||
return buildCallOp(loc);
|
||||
}
|
||||
|
||||
bool WarpBuilder::build_FunCall(BytecodeLocation loc) {
|
||||
return buildCallOp(loc);
|
||||
}
|
||||
|
||||
bool WarpBuilder::build_New(BytecodeLocation loc) { return buildCallOp(loc); }
|
||||
|
||||
bool WarpBuilder::build_SuperCall(BytecodeLocation loc) {
|
||||
|
@ -3391,7 +3387,7 @@ bool WarpBuilder::buildInlinedCall(BytecodeLocation loc,
|
|||
return false;
|
||||
}
|
||||
MResumePoint* outerResumePoint =
|
||||
MResumePoint::New(alloc(), current, pc, MResumePoint::Outer);
|
||||
MResumePoint::New(alloc(), current, pc, callInfo.inliningResumeMode());
|
||||
if (!outerResumePoint) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ bool WarpBuilderShared::resumeAfter(MInstruction* ins, BytecodeLocation loc) {
|
|||
MOZ_ASSERT(!ins->isMovable());
|
||||
|
||||
MResumePoint* resumePoint = MResumePoint::New(
|
||||
alloc(), ins->block(), loc.toRawBytecode(), MResumePoint::ResumeAfter);
|
||||
alloc(), ins->block(), loc.toRawBytecode(), ResumeMode::ResumeAfter);
|
||||
if (!resumePoint) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define jit_WarpBuilderShared_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "js/Value.h"
|
||||
|
@ -54,6 +55,7 @@ class MOZ_STACK_CLASS CallInfo {
|
|||
|
||||
private:
|
||||
ArgFormat argFormat_ = ArgFormat::Standard;
|
||||
mozilla::Maybe<ResumeMode> inliningMode_;
|
||||
|
||||
public:
|
||||
CallInfo(TempAllocator& alloc, bool constructing, bool ignoresReturnValue,
|
||||
|
@ -234,6 +236,17 @@ class MOZ_STACK_CLASS CallInfo {
|
|||
bool isInlined() const { return inlined_; }
|
||||
void markAsInlined() { inlined_ = true; }
|
||||
|
||||
ResumeMode inliningResumeMode() const {
|
||||
MOZ_ASSERT(isInlined());
|
||||
return *inliningMode_;
|
||||
}
|
||||
|
||||
void setInliningResumeMode(ResumeMode mode) {
|
||||
MOZ_ASSERT(isInlined());
|
||||
MOZ_ASSERT(inliningMode_.isNothing());
|
||||
inliningMode_.emplace(mode);
|
||||
}
|
||||
|
||||
MDefinition* callee() const {
|
||||
MOZ_ASSERT(callee_);
|
||||
return callee_;
|
||||
|
|
|
@ -4798,6 +4798,13 @@ bool WarpCacheIRTranspiler::emitCallInlinedFunction(ObjOperandId calleeId,
|
|||
thisArg->type() == MIRType::MagicUninitializedLexical);
|
||||
}
|
||||
|
||||
if (flags.getArgFormat() == CallFlags::FunCall) {
|
||||
callInfo_->setInliningResumeMode(ResumeMode::InlinedFunCall);
|
||||
} else {
|
||||
MOZ_ASSERT(flags.getArgFormat() == CallFlags::Standard);
|
||||
callInfo_->setInliningResumeMode(ResumeMode::InlinedStandardCall);
|
||||
}
|
||||
|
||||
switch (callInfo_->argFormat()) {
|
||||
case CallInfo::ArgFormat::Standard:
|
||||
break;
|
||||
|
@ -5001,6 +5008,7 @@ bool WarpCacheIRTranspiler::emitCallInlinedGetterResult(
|
|||
MDefinition* receiver = getOperand(receiverId);
|
||||
MDefinition* getter = objectStubField(getterOffset);
|
||||
callInfo_->initForGetterCall(getter, receiver);
|
||||
callInfo_->setInliningResumeMode(ResumeMode::InlinedAccessor);
|
||||
|
||||
// Make sure there's enough room to push the arguments on the stack.
|
||||
if (!current->ensureHasSlots(2)) {
|
||||
|
@ -5072,6 +5080,7 @@ bool WarpCacheIRTranspiler::emitCallInlinedSetter(
|
|||
MDefinition* setter = objectStubField(setterOffset);
|
||||
MDefinition* rhs = getOperand(rhsId);
|
||||
callInfo_->initForSetterCall(setter, receiver, rhs);
|
||||
callInfo_->setInliningResumeMode(ResumeMode::InlinedAccessor);
|
||||
|
||||
// Make sure there's enough room to push the arguments on the stack.
|
||||
if (!current->ensureHasSlots(3)) {
|
||||
|
@ -5158,7 +5167,7 @@ bool WarpCacheIRTranspiler::emitAssertRecoveredOnBailoutResult(
|
|||
add(nop);
|
||||
|
||||
auto* resumePoint = MResumePoint::New(
|
||||
alloc(), nop->block(), loc_.toRawBytecode(), MResumePoint::ResumeAfter);
|
||||
alloc(), nop->block(), loc_.toRawBytecode(), ResumeMode::ResumeAfter);
|
||||
if (!resumePoint) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -470,7 +470,6 @@ AbortReasonOr<WarpScriptSnapshot*> WarpScriptOracle::createScriptSnapshot() {
|
|||
case JSOp::Call:
|
||||
case JSOp::CallIgnoresRv:
|
||||
case JSOp::CallIter:
|
||||
case JSOp::FunCall:
|
||||
case JSOp::New:
|
||||
case JSOp::SuperCall:
|
||||
case JSOp::SpreadCall:
|
||||
|
|
|
@ -45,7 +45,13 @@ bool jit::EliminateBoundsChecks(MIRGenerator* mir, MIRGraph& graph) {
|
|||
MWasmBoundsCheck* bc = def->toWasmBoundsCheck();
|
||||
MDefinition* addr = bc->index();
|
||||
|
||||
// Eliminate constant-address bounds checks to addresses below
|
||||
// We only support bounds check elimination on wasm memory, not
|
||||
// tables. See bug 1625891.
|
||||
if (!bc->isMemory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Eliminate constant-address memory bounds checks to addresses below
|
||||
// the heap minimum.
|
||||
//
|
||||
// The payload of the MConstant will be Double if the constant
|
||||
|
|
|
@ -522,11 +522,7 @@ void CodeGeneratorShared::encode(LRecoverInfo* recover) {
|
|||
"Encoding LRecoverInfo %p (frameCount %u, instructions %u)",
|
||||
(void*)recover, recover->mir()->frameCount(), numInstructions);
|
||||
|
||||
MResumePoint::Mode mode = recover->mir()->mode();
|
||||
MOZ_ASSERT(mode != MResumePoint::Outer);
|
||||
bool resumeAfter = (mode == MResumePoint::ResumeAfter);
|
||||
|
||||
RecoverOffset offset = recovers_.startRecover(numInstructions, resumeAfter);
|
||||
RecoverOffset offset = recovers_.startRecover(numInstructions);
|
||||
|
||||
for (MNode* insn : *recover) {
|
||||
recovers_.writeInstruction(insn);
|
||||
|
|
|
@ -3157,6 +3157,20 @@ class LWasmDerivedPointer : public LInstructionHelper<1, 1, 0> {
|
|||
size_t offset() { return mirRaw()->toWasmDerivedPointer()->offset(); }
|
||||
};
|
||||
|
||||
class LWasmDerivedIndexPointer : public LInstructionHelper<1, 2, 0> {
|
||||
public:
|
||||
LIR_HEADER(WasmDerivedIndexPointer);
|
||||
explicit LWasmDerivedIndexPointer(const LAllocation& base,
|
||||
const LAllocation& index)
|
||||
: LInstructionHelper(classOpcode) {
|
||||
setOperand(0, base);
|
||||
setOperand(1, index);
|
||||
}
|
||||
const LAllocation* base() { return getOperand(0); }
|
||||
const LAllocation* index() { return getOperand(1); }
|
||||
Scale scale() { return mirRaw()->toWasmDerivedIndexPointer()->scale(); }
|
||||
};
|
||||
|
||||
class LWasmParameterI64 : public LInstructionHelper<INT64_PIECES, 0, 0> {
|
||||
public:
|
||||
LIR_HEADER(WasmParameterI64);
|
||||
|
|
|
@ -1965,8 +1965,7 @@ bool ExpressionDecompiler::decompilePC(jsbytecode* pc, uint8_t defIndex) {
|
|||
return write("new.target");
|
||||
case JSOp::Call:
|
||||
case JSOp::CallIgnoresRv:
|
||||
case JSOp::CallIter:
|
||||
case JSOp::FunCall: {
|
||||
case JSOp::CallIter: {
|
||||
uint16_t argc = GET_ARGC(pc);
|
||||
return decompilePCForStackOperand(pc, -int32_t(argc + 2)) &&
|
||||
write(argc ? "(...)" : "()");
|
||||
|
|
|
@ -348,7 +348,7 @@ MOZ_ALWAYS_INLINE unsigned StackUses(jsbytecode* pc) {
|
|||
/* stack: fun, this, [argc arguments] */
|
||||
MOZ_ASSERT(op == JSOp::Call || op == JSOp::CallIgnoresRv ||
|
||||
op == JSOp::Eval || op == JSOp::CallIter ||
|
||||
op == JSOp::StrictEval || op == JSOp::FunCall);
|
||||
op == JSOp::StrictEval);
|
||||
return 2 + GET_ARGC(pc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3255,8 +3255,7 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
CASE(Call)
|
||||
CASE(CallIgnoresRv)
|
||||
CASE(CallIter)
|
||||
CASE(SuperCall)
|
||||
CASE(FunCall) {
|
||||
CASE(SuperCall) {
|
||||
static_assert(JSOpLength_Call == JSOpLength_New,
|
||||
"call and new must be the same size");
|
||||
static_assert(JSOpLength_Call == JSOpLength_CallIgnoresRv,
|
||||
|
@ -3265,8 +3264,6 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
"call and calliter must be the same size");
|
||||
static_assert(JSOpLength_Call == JSOpLength_SuperCall,
|
||||
"call and supercall must be the same size");
|
||||
static_assert(JSOpLength_Call == JSOpLength_FunCall,
|
||||
"call and funcall must be the same size");
|
||||
|
||||
if (REGS.fp()->hasPushedGeckoProfilerFrame()) {
|
||||
cx->geckoProfiler().updatePC(cx, script, REGS.pc);
|
||||
|
|
|
@ -1739,9 +1739,6 @@
|
|||
* iterable") rather than `JSMSG_NOT_FUNCTION` ("x[Symbol.iterator] is not
|
||||
* a function"). The `argc` operand must be 0 for this variation.
|
||||
*
|
||||
* `JSOp::FunCall` hints to the VM that the callee is likely
|
||||
* `Function.prototype.call`.
|
||||
*
|
||||
* `JSOp::CallIgnoresRv` hints to the VM that the return value is ignored.
|
||||
* This allows alternate faster implementations to be used that avoid
|
||||
* unnecesary allocations.
|
||||
|
@ -1757,7 +1754,6 @@
|
|||
*/ \
|
||||
MACRO(Call, call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
|
||||
MACRO(CallIter, call_iter, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
|
||||
MACRO(FunCall, fun_call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
|
||||
MACRO(CallIgnoresRv, call_ignores_rv, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_IC) \
|
||||
/*
|
||||
* Like `JSOp::Call`, but the arguments are provided in an array rather than
|
||||
|
@ -3541,13 +3537,14 @@
|
|||
* a power of two. Use this macro to do so.
|
||||
*/
|
||||
#define FOR_EACH_TRAILING_UNUSED_OPCODE(MACRO) \
|
||||
IF_RECORD_TUPLE(/* empty */, MACRO(225)) \
|
||||
IF_RECORD_TUPLE(/* empty */, MACRO(226)) \
|
||||
IF_RECORD_TUPLE(/* empty */, MACRO(227)) \
|
||||
IF_RECORD_TUPLE(/* empty */, MACRO(228)) \
|
||||
IF_RECORD_TUPLE(/* empty */, MACRO(229)) \
|
||||
IF_RECORD_TUPLE(/* empty */, MACRO(230)) \
|
||||
IF_RECORD_TUPLE(/* empty */, MACRO(231)) \
|
||||
IF_RECORD_TUPLE(/* empty */, MACRO(232)) \
|
||||
MACRO(232) \
|
||||
MACRO(233) \
|
||||
MACRO(234) \
|
||||
MACRO(235) \
|
||||
|
|
|
@ -2658,6 +2658,7 @@ class MOZ_STACK_CLASS FunctionValidator : public FunctionValidatorShared {
|
|||
}
|
||||
|
||||
[[nodiscard]] bool writeCall(ParseNode* pn, Op op) {
|
||||
MOZ_ASSERT(op == Op::Call);
|
||||
if (!encoder().writeOp(op)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2665,6 +2666,7 @@ class MOZ_STACK_CLASS FunctionValidator : public FunctionValidatorShared {
|
|||
return appendCallSiteLineNumber(pn);
|
||||
}
|
||||
[[nodiscard]] bool writeCall(ParseNode* pn, MozOp op) {
|
||||
MOZ_ASSERT(op == MozOp::OldCallDirect || op == MozOp::OldCallIndirect);
|
||||
if (!encoder().writeOp(op)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -150,6 +150,16 @@ struct FunctionCall {
|
|||
size_t stackArgAreaSize;
|
||||
};
|
||||
|
||||
enum class PostBarrierKind {
|
||||
// Remove an existing store buffer entry if the new value does not require
|
||||
// one. This is required to preserve invariants with HeapPtr when used for
|
||||
// movable storage.
|
||||
Precise,
|
||||
// Add a store buffer entry if the new value requires it, but do not attempt
|
||||
// to remove a pre-existing entry.
|
||||
Imprecise,
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Wasm baseline compiler proper.
|
||||
|
@ -1078,6 +1088,15 @@ struct BaseCompiler final {
|
|||
|
||||
Address addressOfGlobalVar(const GlobalDesc& global, RegPtr tmp);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Table access.
|
||||
|
||||
Address addressOfTableField(const TableDesc& table, uint32_t fieldOffset,
|
||||
RegPtr tls);
|
||||
void loadTableLength(const TableDesc& table, RegPtr tls, RegI32 length);
|
||||
void loadTableElements(const TableDesc& table, RegPtr tls, RegPtr elements);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Heap access.
|
||||
|
@ -1239,7 +1258,7 @@ struct BaseCompiler final {
|
|||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Object support.
|
||||
// Barriers support.
|
||||
|
||||
// This emits a GC pre-write barrier. The pre-barrier is needed when we
|
||||
// replace a member field with a new value, and the previous field value
|
||||
|
@ -1254,15 +1273,39 @@ struct BaseCompiler final {
|
|||
// update. This function preserves that register.
|
||||
void emitPreBarrier(RegPtr valueAddr);
|
||||
|
||||
// This frees the register `valueAddr`.
|
||||
[[nodiscard]] bool emitPostBarrierCall(RegPtr valueAddr);
|
||||
// This emits a GC post-write barrier. The post-barrier is needed when we
|
||||
// replace a member field with a new value, the new value is in the nursery,
|
||||
// and the containing object is a tenured object. The field must then be
|
||||
// added to the store buffer so that the nursery can be correctly collected.
|
||||
// The field might belong to an object or be a stack slot or a register or a
|
||||
// heap allocated value.
|
||||
//
|
||||
// For the difference between 'precise' and 'imprecise', look at the
|
||||
// documentation on PostBarrierKind.
|
||||
//
|
||||
// `object` is a pointer to the object that contains the field. It is used, if
|
||||
// present, to skip adding a store buffer entry when the containing object is
|
||||
// in the nursery. This register is preserved by this function.
|
||||
// `valueAddr` is the address of the location that we are writing to. This
|
||||
// register is consumed by this function.
|
||||
// `prevValue` is the value that existed in the field before `value` was
|
||||
// stored. This register is consumed by this function.
|
||||
// `value` is the value that was stored in the field. This register is
|
||||
// preserved by this function.
|
||||
[[nodiscard]] bool emitPostBarrierImprecise(const Maybe<RegRef>& object,
|
||||
RegPtr valueAddr, RegRef value);
|
||||
[[nodiscard]] bool emitPostBarrierPrecise(const Maybe<RegRef>& object,
|
||||
RegPtr valueAddr, RegRef prevValue, RegRef value);
|
||||
|
||||
// Emits a store to a JS object pointer at the address valueAddr, which is
|
||||
// inside the GC cell `object`. Preserves `object` and `value`.
|
||||
// Emits a store to a JS object pointer at the address `valueAddr`, which is
|
||||
// inside the GC cell `object`.
|
||||
//
|
||||
// Preserves `object` and `value`. Consumes `valueAddr`.
|
||||
[[nodiscard]] bool emitBarrieredStore(const Maybe<RegRef>& object,
|
||||
RegPtr valueAddr, RegRef value);
|
||||
RegPtr valueAddr, RegRef value,
|
||||
PostBarrierKind kind);
|
||||
|
||||
// Emits a store of nullptr to a JS object pointer at the address valueAddr,
|
||||
// Emits a store of nullptr to a JS object pointer at the address valueAddr.
|
||||
// Preserves `valueAddr`.
|
||||
void emitBarrieredClear(RegPtr valueAddr);
|
||||
|
||||
|
@ -1542,6 +1585,10 @@ struct BaseCompiler final {
|
|||
[[nodiscard]] bool emitTableSet();
|
||||
[[nodiscard]] bool emitTableSize();
|
||||
|
||||
void emitTableBoundsCheck(const TableDesc& table, RegI32 index, RegPtr tls);
|
||||
[[nodiscard]] bool emitTableGetAnyRef(uint32_t tableIndex);
|
||||
[[nodiscard]] bool emitTableSetAnyRef(uint32_t tableIndex);
|
||||
|
||||
#ifdef ENABLE_WASM_GC
|
||||
[[nodiscard]] bool emitStructNewWithRtt();
|
||||
[[nodiscard]] bool emitStructNewDefaultWithRtt();
|
||||
|
|
|
@ -2103,6 +2103,29 @@ Address BaseCompiler::addressOfGlobalVar(const GlobalDesc& global, RegPtr tmp) {
|
|||
return Address(tmp, globalToTlsOffset);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Table access.
|
||||
|
||||
Address BaseCompiler::addressOfTableField(const TableDesc& table,
|
||||
uint32_t fieldOffset, RegPtr tls) {
|
||||
uint32_t tableToTlsOffset =
|
||||
wasm::Instance::offsetOfGlobalArea() + table.globalDataOffset + fieldOffset;
|
||||
return Address(tls, tableToTlsOffset);
|
||||
}
|
||||
|
||||
void BaseCompiler::loadTableLength(const TableDesc& table, RegPtr tls,
|
||||
RegI32 length) {
|
||||
masm.load32(addressOfTableField(table, offsetof(TableInstanceData, length), tls),
|
||||
length);
|
||||
}
|
||||
|
||||
void BaseCompiler::loadTableElements(const TableDesc& table, RegPtr tls,
|
||||
RegPtr elements) {
|
||||
masm.loadPtr(addressOfTableField(table, offsetof(TableInstanceData, elements), tls),
|
||||
elements);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Basic emitters for simple operators.
|
||||
|
@ -4267,7 +4290,8 @@ bool BaseCompiler::emitThrow() {
|
|||
RegRef rv = popRef();
|
||||
pushPtr(data);
|
||||
// emitBarrieredStore preserves exn, rv
|
||||
if (!emitBarrieredStore(Some(exn), valueAddr, rv)) {
|
||||
if (!emitBarrieredStore(Some(exn), valueAddr, rv,
|
||||
PostBarrierKind::Imprecise)) {
|
||||
return false;
|
||||
}
|
||||
popPtr(data);
|
||||
|
@ -5134,7 +5158,8 @@ bool BaseCompiler::emitSetGlobal() {
|
|||
}
|
||||
RegRef rv = popRef();
|
||||
// emitBarrieredStore preserves rv
|
||||
if (!emitBarrieredStore(Nothing(), valueAddr, rv)) {
|
||||
if (!emitBarrieredStore(Nothing(), valueAddr, rv,
|
||||
PostBarrierKind::Imprecise)) {
|
||||
return false;
|
||||
}
|
||||
freeRef(rv);
|
||||
|
@ -5811,12 +5836,21 @@ bool BaseCompiler::emitTableFill() {
|
|||
}
|
||||
|
||||
bool BaseCompiler::emitTableGet() {
|
||||
uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
|
||||
uint32_t tableIndex;
|
||||
Nothing nothing;
|
||||
if (!iter_.readTableGet(&tableIndex, ¬hing)) {
|
||||
return false;
|
||||
}
|
||||
if (deadCode_) {
|
||||
return true;
|
||||
}
|
||||
if (moduleEnv_.tables[tableIndex].elemType.tableRepr() == TableRepr::Ref) {
|
||||
return emitTableGetAnyRef(tableIndex);
|
||||
}
|
||||
pushI32(tableIndex);
|
||||
// get(index:u32, table:u32) -> AnyRef
|
||||
return emitInstanceCallOp<uint32_t>(
|
||||
SASigTableGet, [this](uint32_t* tableIndex) -> bool {
|
||||
Nothing nothing;
|
||||
return iter_.readTableGet(tableIndex, ¬hing);
|
||||
});
|
||||
return emitInstanceCall(lineOrBytecode, SASigTableGetFunc);
|
||||
}
|
||||
|
||||
bool BaseCompiler::emitTableGrow() {
|
||||
|
@ -5829,20 +5863,112 @@ bool BaseCompiler::emitTableGrow() {
|
|||
}
|
||||
|
||||
bool BaseCompiler::emitTableSet() {
|
||||
uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
|
||||
uint32_t tableIndex;
|
||||
Nothing nothing;
|
||||
if (!iter_.readTableSet(&tableIndex, ¬hing, ¬hing)) {
|
||||
return false;
|
||||
}
|
||||
if (deadCode_) {
|
||||
return true;
|
||||
}
|
||||
if (moduleEnv_.tables[tableIndex].elemType.tableRepr() == TableRepr::Ref) {
|
||||
return emitTableSetAnyRef(tableIndex);
|
||||
}
|
||||
pushI32(tableIndex);
|
||||
// set(index:u32, value:ref, table:u32) -> void
|
||||
return emitInstanceCallOp<uint32_t>(
|
||||
SASigTableSet, [this](uint32_t* tableIndex) -> bool {
|
||||
Nothing nothing;
|
||||
return iter_.readTableSet(tableIndex, ¬hing, ¬hing);
|
||||
});
|
||||
return emitInstanceCall(lineOrBytecode, SASigTableSetFunc);
|
||||
}
|
||||
|
||||
bool BaseCompiler::emitTableSize() {
|
||||
// size(table:u32) -> u32
|
||||
return emitInstanceCallOp<uint32_t>(SASigTableSize,
|
||||
[this](uint32_t* tableIndex) -> bool {
|
||||
return iter_.readTableSize(tableIndex);
|
||||
});
|
||||
uint32_t tableIndex;
|
||||
if (!iter_.readTableSize(&tableIndex)) {
|
||||
return false;
|
||||
}
|
||||
if (deadCode_) {
|
||||
return true;
|
||||
}
|
||||
const TableDesc& table = moduleEnv_.tables[tableIndex];
|
||||
|
||||
RegPtr tls = needPtr();
|
||||
RegI32 length = needI32();
|
||||
|
||||
fr.loadTlsPtr(tls);
|
||||
loadTableLength(table, tls, length);
|
||||
|
||||
pushI32(length);
|
||||
freePtr(tls);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseCompiler::emitTableBoundsCheck(const TableDesc& table, RegI32 index,
|
||||
RegPtr tls) {
|
||||
Label ok;
|
||||
masm.wasmBoundsCheck32(
|
||||
Assembler::Condition::Below, index,
|
||||
addressOfTableField(table, offsetof(TableInstanceData, length), tls), &ok);
|
||||
masm.wasmTrap(wasm::Trap::OutOfBounds, bytecodeOffset());
|
||||
masm.bind(&ok);
|
||||
}
|
||||
|
||||
bool BaseCompiler::emitTableGetAnyRef(uint32_t tableIndex) {
|
||||
const TableDesc& table = moduleEnv_.tables[tableIndex];
|
||||
|
||||
RegPtr tls = needPtr();
|
||||
RegPtr elements = needPtr();
|
||||
RegI32 index = popI32();
|
||||
|
||||
fr.loadTlsPtr(tls);
|
||||
emitTableBoundsCheck(table, index, tls);
|
||||
loadTableElements(table, tls, elements);
|
||||
masm.loadPtr(BaseIndex(elements, index, ScalePointer), elements);
|
||||
|
||||
pushRef(RegRef(elements));
|
||||
freeI32(index);
|
||||
freePtr(tls);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseCompiler::emitTableSetAnyRef(uint32_t tableIndex) {
|
||||
const TableDesc& table = moduleEnv_.tables[tableIndex];
|
||||
|
||||
// Create temporaries for valueAddr that is not in the prebarrier register
|
||||
// and can be consumed by the barrier operation
|
||||
RegPtr valueAddr = RegPtr(PreBarrierReg);
|
||||
needPtr(valueAddr);
|
||||
|
||||
RegPtr tls = needPtr();
|
||||
RegPtr elements = needPtr();
|
||||
RegRef value = popRef();
|
||||
RegI32 index = popI32();
|
||||
|
||||
// x86 is one register too short for this operation, shuffle `value` back
|
||||
// onto the stack until it is needed.
|
||||
#ifdef JS_CODEGEN_X86
|
||||
pushRef(value);
|
||||
#endif
|
||||
|
||||
fr.loadTlsPtr(tls);
|
||||
emitTableBoundsCheck(table, index, tls);
|
||||
loadTableElements(table, tls, elements);
|
||||
masm.computeEffectiveAddress(BaseIndex(elements, index, ScalePointer),
|
||||
valueAddr);
|
||||
|
||||
freeI32(index);
|
||||
freePtr(elements);
|
||||
freePtr(tls);
|
||||
|
||||
#ifdef JS_CODEGEN_X86
|
||||
value = popRef();
|
||||
#endif
|
||||
|
||||
if (!emitBarrieredStore(Nothing(), valueAddr, value,
|
||||
PostBarrierKind::Precise)) {
|
||||
return false;
|
||||
}
|
||||
freeRef(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -5889,48 +6015,35 @@ void BaseCompiler::emitPreBarrier(RegPtr valueAddr) {
|
|||
masm.bind(&skipBarrier);
|
||||
}
|
||||
|
||||
// This frees the register `valueAddr`.
|
||||
|
||||
bool BaseCompiler::emitPostBarrierCall(RegPtr valueAddr) {
|
||||
bool BaseCompiler::emitPostBarrierImprecise(const Maybe<RegRef>& object,
|
||||
RegPtr valueAddr, RegRef value) {
|
||||
uint32_t bytecodeOffset = iter_.lastOpcodeOffset();
|
||||
|
||||
// The `valueAddr` is a raw pointer to the cell within some GC object or
|
||||
// TLS area, and we guarantee that the GC will not run while the
|
||||
// postbarrier call is active, so push a uintptr_t value.
|
||||
pushPtr(valueAddr);
|
||||
return emitInstanceCall(bytecodeOffset, SASigPostBarrier);
|
||||
}
|
||||
|
||||
// Emits a store to a JS object pointer at the address valueAddr, which is
|
||||
// inside the GC cell `object`. Preserves `object` and `value`.
|
||||
bool BaseCompiler::emitBarrieredStore(const Maybe<RegRef>& object,
|
||||
RegPtr valueAddr, RegRef value) {
|
||||
// TODO/AnyRef-boxing: With boxed immediates and strings, the write
|
||||
// barrier is going to have to be more complicated.
|
||||
ASSERT_ANYREF_IS_JSOBJECT;
|
||||
|
||||
emitPreBarrier(valueAddr); // Preserves valueAddr
|
||||
masm.storePtr(value, Address(valueAddr, 0));
|
||||
|
||||
Label skipBarrier;
|
||||
// We must force a sync before the guard so that locals are in a consistent
|
||||
// location for whether or not the post-barrier call is taken.
|
||||
sync();
|
||||
|
||||
RegRef otherScratch = needRef();
|
||||
// Emit a guard to skip the post-barrier call if it is not needed.
|
||||
Label skipBarrier;
|
||||
RegPtr otherScratch = needPtr();
|
||||
EmitWasmPostBarrierGuard(masm, object, otherScratch, value, &skipBarrier);
|
||||
freeRef(otherScratch);
|
||||
freePtr(otherScratch);
|
||||
|
||||
// Push `object` and `value` to preserve them across the call.
|
||||
if (object) {
|
||||
pushRef(*object);
|
||||
}
|
||||
pushRef(value);
|
||||
|
||||
// Consumes valueAddr
|
||||
if (!emitPostBarrierCall(valueAddr)) {
|
||||
// The `valueAddr` is a raw pointer to the cell within some GC object or
|
||||
// TLS area, and we are careful so that the GC will not run while the
|
||||
// post-barrier call is active, so push a uintptr_t value.
|
||||
pushPtr(valueAddr);
|
||||
if (!emitInstanceCall(bytecodeOffset, SASigPostBarrier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Consume all other operands as they may have been clobbered by the post
|
||||
// barrier call
|
||||
// Restore `object` and `value`.
|
||||
popRef(value);
|
||||
if (object) {
|
||||
popRef(*object);
|
||||
|
@ -5940,15 +6053,72 @@ bool BaseCompiler::emitBarrieredStore(const Maybe<RegRef>& object,
|
|||
return true;
|
||||
}
|
||||
|
||||
// Emits a store of nullptr to a JS object pointer at the address valueAddr.
|
||||
// Preserves `valueAddr`.
|
||||
bool BaseCompiler::emitPostBarrierPrecise(const Maybe<RegRef>& object,
|
||||
RegPtr valueAddr, RegRef prevValue, RegRef value) {
|
||||
uint32_t bytecodeOffset = iter_.lastOpcodeOffset();
|
||||
|
||||
// Push `object` and `value` to preserve them across the call.
|
||||
if (object) {
|
||||
pushRef(*object);
|
||||
}
|
||||
pushRef(value);
|
||||
|
||||
// Push the arguments and call the precise post-barrier
|
||||
pushPtr(valueAddr);
|
||||
pushRef(prevValue);
|
||||
if (!emitInstanceCall(bytecodeOffset, SASigPostBarrierPrecise)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore `object` and `value`.
|
||||
popRef(value);
|
||||
if (object) {
|
||||
popRef(*object);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseCompiler::emitBarrieredStore(const Maybe<RegRef>& object,
|
||||
RegPtr valueAddr, RegRef value,
|
||||
PostBarrierKind kind) {
|
||||
// TODO/AnyRef-boxing: With boxed immediates and strings, the write
|
||||
// barrier is going to have to be more complicated.
|
||||
ASSERT_ANYREF_IS_JSOBJECT;
|
||||
|
||||
// The pre-barrier preserves all allocated registers.
|
||||
emitPreBarrier(valueAddr);
|
||||
|
||||
// The precise post-barrier requires the previous value stored in the field,
|
||||
// in order to know if the previous store buffer entry needs to be removed.
|
||||
RegRef prevValue;
|
||||
if (kind == PostBarrierKind::Precise) {
|
||||
prevValue = needRef();
|
||||
masm.loadPtr(Address(valueAddr, 0), prevValue);
|
||||
}
|
||||
|
||||
// Store the value
|
||||
masm.storePtr(value, Address(valueAddr, 0));
|
||||
|
||||
// The post-barrier preserves object and value.
|
||||
if (kind == PostBarrierKind::Precise) {
|
||||
return emitPostBarrierPrecise(object, valueAddr, prevValue, value);
|
||||
}
|
||||
return emitPostBarrierImprecise(object, valueAddr, value);
|
||||
}
|
||||
|
||||
void BaseCompiler::emitBarrieredClear(RegPtr valueAddr) {
|
||||
// TODO/AnyRef-boxing: With boxed immediates and strings, the write
|
||||
// barrier is going to have to be more complicated.
|
||||
ASSERT_ANYREF_IS_JSOBJECT;
|
||||
|
||||
emitPreBarrier(valueAddr); // Preserves valueAddr
|
||||
// The pre-barrier preserves all allocated registers.
|
||||
emitPreBarrier(valueAddr);
|
||||
|
||||
// Store null
|
||||
masm.storePtr(ImmWord(0), Address(valueAddr, 0));
|
||||
|
||||
// No post-barrier is needed, as null does not require a store buffer entry
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -6136,7 +6306,8 @@ bool BaseCompiler::emitGcStructSet(RegRef object, RegPtr data,
|
|||
pushPtr(data);
|
||||
|
||||
// emitBarrieredStore preserves object and value
|
||||
if (!emitBarrieredStore(Some(object), valueAddr, value.ref())) {
|
||||
if (!emitBarrieredStore(Some(object), valueAddr, value.ref(),
|
||||
PostBarrierKind::Imprecise)) {
|
||||
return false;
|
||||
}
|
||||
freeRef(value.ref());
|
||||
|
@ -6187,7 +6358,8 @@ bool BaseCompiler::emitGcArraySet(RegRef object, RegPtr data, RegI32 index,
|
|||
pushI32(index);
|
||||
|
||||
// emitBarrieredStore preserves object and value
|
||||
if (!emitBarrieredStore(Some(object), valueAddr, value.ref())) {
|
||||
if (!emitBarrieredStore(Some(object), valueAddr, value.ref(),
|
||||
PostBarrierKind::Imprecise)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -228,11 +228,12 @@ const SymbolicAddressSignature SASigTableFill = {
|
|||
_FailOnNegI32,
|
||||
5,
|
||||
{_PTR, _I32, _RoN, _I32, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableGet = {SymbolicAddress::TableGet,
|
||||
_RoN,
|
||||
_FailOnInvalidRef,
|
||||
3,
|
||||
{_PTR, _I32, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableGetFunc = {
|
||||
SymbolicAddress::TableGetFunc,
|
||||
_RoN,
|
||||
_FailOnInvalidRef,
|
||||
3,
|
||||
{_PTR, _I32, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableGrow = {
|
||||
SymbolicAddress::TableGrow,
|
||||
_I32,
|
||||
|
@ -245,13 +246,12 @@ const SymbolicAddressSignature SASigTableInit = {
|
|||
_FailOnNegI32,
|
||||
6,
|
||||
{_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableSet = {SymbolicAddress::TableSet,
|
||||
_VOID,
|
||||
_FailOnNegI32,
|
||||
4,
|
||||
{_PTR, _I32, _RoN, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableSize = {
|
||||
SymbolicAddress::TableSize, _I32, _Infallible, 2, {_PTR, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableSetFunc = {
|
||||
SymbolicAddress::TableSetFunc,
|
||||
_VOID,
|
||||
_FailOnNegI32,
|
||||
4,
|
||||
{_PTR, _I32, _RoN, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigRefFunc = {
|
||||
SymbolicAddress::RefFunc, _RoN, _FailOnInvalidRef, 2, {_PTR, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigPreBarrierFiltering = {
|
||||
|
@ -262,6 +262,12 @@ const SymbolicAddressSignature SASigPreBarrierFiltering = {
|
|||
{_PTR, _PTR, _END}};
|
||||
const SymbolicAddressSignature SASigPostBarrier = {
|
||||
SymbolicAddress::PostBarrier, _VOID, _Infallible, 2, {_PTR, _PTR, _END}};
|
||||
const SymbolicAddressSignature SASigPostBarrierPrecise = {
|
||||
SymbolicAddress::PostBarrierPrecise,
|
||||
_VOID,
|
||||
_Infallible,
|
||||
3,
|
||||
{_PTR, _PTR, _RoN, _END}};
|
||||
const SymbolicAddressSignature SASigPostBarrierFiltering = {
|
||||
SymbolicAddress::PostBarrierFiltering,
|
||||
_VOID,
|
||||
|
@ -1221,22 +1227,18 @@ void* wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) {
|
|||
*abiType = Args_Int32_GeneralInt32Int32Int32Int32Int32;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigTableInit));
|
||||
return FuncCast(Instance::tableInit, *abiType);
|
||||
case SymbolicAddress::TableGet:
|
||||
case SymbolicAddress::TableGetFunc:
|
||||
*abiType = Args_General_GeneralInt32Int32;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigTableGet));
|
||||
return FuncCast(Instance::tableGet, *abiType);
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigTableGetFunc));
|
||||
return FuncCast(Instance::tableGetFunc, *abiType);
|
||||
case SymbolicAddress::TableGrow:
|
||||
*abiType = Args_Int32_GeneralGeneralInt32Int32;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigTableGrow));
|
||||
return FuncCast(Instance::tableGrow, *abiType);
|
||||
case SymbolicAddress::TableSet:
|
||||
case SymbolicAddress::TableSetFunc:
|
||||
*abiType = Args_Int32_GeneralInt32GeneralInt32;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigTableSet));
|
||||
return FuncCast(Instance::tableSet, *abiType);
|
||||
case SymbolicAddress::TableSize:
|
||||
*abiType = Args_Int32_GeneralInt32;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigTableSize));
|
||||
return FuncCast(Instance::tableSize, *abiType);
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigTableSetFunc));
|
||||
return FuncCast(Instance::tableSetFunc, *abiType);
|
||||
case SymbolicAddress::RefFunc:
|
||||
*abiType = Args_General_GeneralInt32;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigRefFunc));
|
||||
|
@ -1245,6 +1247,10 @@ void* wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) {
|
|||
*abiType = Args_Int32_GeneralGeneral;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigPostBarrier));
|
||||
return FuncCast(Instance::postBarrier, *abiType);
|
||||
case SymbolicAddress::PostBarrierPrecise:
|
||||
*abiType = Args_Int32_GeneralGeneralGeneral;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigPostBarrierPrecise));
|
||||
return FuncCast(Instance::postBarrierPrecise, *abiType);
|
||||
case SymbolicAddress::PreBarrierFiltering:
|
||||
*abiType = Args_Int32_GeneralGeneral;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigPreBarrierFiltering));
|
||||
|
@ -1438,14 +1444,14 @@ bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
|
|||
case SymbolicAddress::TableCopy:
|
||||
case SymbolicAddress::ElemDrop:
|
||||
case SymbolicAddress::TableFill:
|
||||
case SymbolicAddress::TableGet:
|
||||
case SymbolicAddress::TableGetFunc:
|
||||
case SymbolicAddress::TableGrow:
|
||||
case SymbolicAddress::TableInit:
|
||||
case SymbolicAddress::TableSet:
|
||||
case SymbolicAddress::TableSize:
|
||||
case SymbolicAddress::TableSetFunc:
|
||||
case SymbolicAddress::RefFunc:
|
||||
case SymbolicAddress::PreBarrierFiltering:
|
||||
case SymbolicAddress::PostBarrier:
|
||||
case SymbolicAddress::PostBarrierPrecise:
|
||||
case SymbolicAddress::PostBarrierFiltering:
|
||||
case SymbolicAddress::StructNew:
|
||||
#ifdef ENABLE_WASM_EXCEPTIONS
|
||||
|
|
|
@ -112,16 +112,16 @@ enum class SymbolicAddress {
|
|||
TableCopy,
|
||||
ElemDrop,
|
||||
TableFill,
|
||||
TableGet,
|
||||
TableGetFunc,
|
||||
TableGrow,
|
||||
TableInit,
|
||||
TableSet,
|
||||
TableSize,
|
||||
TableSetFunc,
|
||||
RefFunc,
|
||||
RefTest,
|
||||
RttSub,
|
||||
PreBarrierFiltering,
|
||||
PostBarrier,
|
||||
PostBarrierPrecise,
|
||||
PostBarrierFiltering,
|
||||
StructNew,
|
||||
#if defined(ENABLE_WASM_EXCEPTIONS)
|
||||
|
@ -236,14 +236,14 @@ extern const SymbolicAddressSignature SASigMemInitM64;
|
|||
extern const SymbolicAddressSignature SASigTableCopy;
|
||||
extern const SymbolicAddressSignature SASigElemDrop;
|
||||
extern const SymbolicAddressSignature SASigTableFill;
|
||||
extern const SymbolicAddressSignature SASigTableGet;
|
||||
extern const SymbolicAddressSignature SASigTableGetFunc;
|
||||
extern const SymbolicAddressSignature SASigTableGrow;
|
||||
extern const SymbolicAddressSignature SASigTableInit;
|
||||
extern const SymbolicAddressSignature SASigTableSet;
|
||||
extern const SymbolicAddressSignature SASigTableSize;
|
||||
extern const SymbolicAddressSignature SASigTableSetFunc;
|
||||
extern const SymbolicAddressSignature SASigRefFunc;
|
||||
extern const SymbolicAddressSignature SASigPreBarrierFiltering;
|
||||
extern const SymbolicAddressSignature SASigPostBarrier;
|
||||
extern const SymbolicAddressSignature SASigPostBarrierPrecise;
|
||||
extern const SymbolicAddressSignature SASigPostBarrierFiltering;
|
||||
extern const SymbolicAddressSignature SASigStructNew;
|
||||
#ifdef ENABLE_WASM_EXCEPTIONS
|
||||
|
|
|
@ -604,7 +604,7 @@ class CalleeDesc {
|
|||
uint32_t tableFunctionBaseGlobalDataOffset() const {
|
||||
MOZ_ASSERT(isTable());
|
||||
return u.table.globalDataOffset_ +
|
||||
offsetof(TableInstanceData, functionBase);
|
||||
offsetof(TableInstanceData, elements);
|
||||
}
|
||||
TypeIdDesc wasmTableSigId() const {
|
||||
MOZ_ASSERT(which_ == WasmTable);
|
||||
|
|
|
@ -1578,24 +1578,22 @@ static const char* ThunkedNativeToDescription(SymbolicAddress func) {
|
|||
return "call to native table.fill function";
|
||||
case SymbolicAddress::ElemDrop:
|
||||
return "call to native elem.drop function";
|
||||
case SymbolicAddress::TableGet:
|
||||
case SymbolicAddress::TableGetFunc:
|
||||
return "call to native table.get function";
|
||||
case SymbolicAddress::TableGrow:
|
||||
return "call to native table.grow function";
|
||||
case SymbolicAddress::TableInit:
|
||||
return "call to native table.init function";
|
||||
case SymbolicAddress::TableSet:
|
||||
case SymbolicAddress::TableSetFunc:
|
||||
return "call to native table.set function";
|
||||
case SymbolicAddress::TableSize:
|
||||
return "call to native table.size function";
|
||||
case SymbolicAddress::RefFunc:
|
||||
return "call to native ref.func function";
|
||||
case SymbolicAddress::PreBarrierFiltering:
|
||||
return "call to native filtering GC prebarrier (in wasm)";
|
||||
case SymbolicAddress::PostBarrier:
|
||||
return "call to native GC postbarrier (in wasm)";
|
||||
case SymbolicAddress::PostBarrierPrecise:
|
||||
case SymbolicAddress::PostBarrierFiltering:
|
||||
return "call to native filtering GC postbarrier (in wasm)";
|
||||
return "call to native GC postbarrier (in wasm)";
|
||||
case SymbolicAddress::StructNew:
|
||||
return "call to native struct.new (in wasm)";
|
||||
#if defined(ENABLE_WASM_EXCEPTIONS)
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче