mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Merge m-c to autoland. a=merge
This commit is contained in:
commit
4202c4feab
@ -910,17 +910,17 @@
|
||||
let sizedIconUrl = browser.mIconURL || "";
|
||||
if (sizedIconUrl != aTab.getAttribute("image")) {
|
||||
if (sizedIconUrl) {
|
||||
aTab.setAttribute("image", sizedIconUrl);
|
||||
if (!browser.mIconLoadingPrincipal ||
|
||||
!browser.mIconLoadingPrincipal.equals(loadingPrincipal)) {
|
||||
aTab.setAttribute("iconLoadingPrincipal",
|
||||
this.serializationHelper.serializeToString(loadingPrincipal));
|
||||
browser.mIconLoadingPrincipal = loadingPrincipal;
|
||||
}
|
||||
aTab.setAttribute("image", sizedIconUrl);
|
||||
} else {
|
||||
aTab.removeAttribute("image");
|
||||
aTab.removeAttribute("iconLoadingPrincipal");
|
||||
delete browser.mIconLoadingPrincipal;
|
||||
aTab.removeAttribute("image");
|
||||
}
|
||||
this._tabAttrModified(aTab, ["image"]);
|
||||
}
|
||||
|
@ -132,6 +132,8 @@ AboutRedirector::NewChannel(nsIURI* aURI,
|
||||
nsIChannel** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
NS_ENSURE_ARG_POINTER(aLoadInfo);
|
||||
|
||||
NS_ASSERTION(result, "must not be null");
|
||||
|
||||
nsAutoCString path = GetAboutModuleName(aURI);
|
||||
@ -172,26 +174,22 @@ AboutRedirector::NewChannel(nsIURI* aURI,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If tempURI links to an external URI (i.e. something other than
|
||||
// chrome:// or resource://) then set the LOAD_REPLACE flag on the
|
||||
// channel which forces the channel owner to reflect the displayed
|
||||
// chrome:// or resource://) then set the result principal URI on the
|
||||
// load info which forces the channel prncipal to reflect the displayed
|
||||
// URL rather then being the systemPrincipal.
|
||||
bool isUIResource = false;
|
||||
rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
||||
&isUIResource);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsLoadFlags loadFlags = isUIResource
|
||||
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
|
||||
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
|
||||
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
|
||||
tempURI,
|
||||
aLoadInfo,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
loadFlags);
|
||||
aLoadInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!isUIResource) {
|
||||
aLoadInfo->SetResultPrincipalURI(tempURI);
|
||||
}
|
||||
tempChannel->SetOriginalURI(aURI);
|
||||
|
||||
NS_ADDREF(*result = tempChannel);
|
||||
|
@ -199,44 +199,48 @@ const CustomizableWidgets = [
|
||||
for (let i = 0, l = staticButtons.length; i < l; ++i)
|
||||
CustomizableUI.addShortcut(staticButtons[i]);
|
||||
|
||||
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||
.asyncExecuteLegacyQueries([query], 1, options, {
|
||||
handleResult(aResultSet) {
|
||||
let onItemCommand = function(aItemCommandEvent) {
|
||||
// Only handle the click event for middle clicks, we're using the command
|
||||
// event otherwise.
|
||||
if (aItemCommandEvent.type == "click" &&
|
||||
aItemCommandEvent.button != 1) {
|
||||
return;
|
||||
}
|
||||
let item = aItemCommandEvent.target;
|
||||
win.openUILink(item.getAttribute("targetURI"), aItemCommandEvent);
|
||||
CustomizableUI.hidePanelForNode(item);
|
||||
};
|
||||
let fragment = doc.createDocumentFragment();
|
||||
let row;
|
||||
while ((row = aResultSet.getNextRow())) {
|
||||
let uri = row.getResultByIndex(1);
|
||||
let title = row.getResultByIndex(2);
|
||||
aEvent.detail.addBlocker(new Promise((resolve, reject) => {
|
||||
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||
.asyncExecuteLegacyQueries([query], 1, options, {
|
||||
handleResult(aResultSet) {
|
||||
let onItemCommand = function(aItemCommandEvent) {
|
||||
// Only handle the click event for middle clicks, we're using the command
|
||||
// event otherwise.
|
||||
if (aItemCommandEvent.type == "click" &&
|
||||
aItemCommandEvent.button != 1) {
|
||||
return;
|
||||
}
|
||||
let item = aItemCommandEvent.target;
|
||||
win.openUILink(item.getAttribute("targetURI"), aItemCommandEvent);
|
||||
CustomizableUI.hidePanelForNode(item);
|
||||
};
|
||||
let fragment = doc.createDocumentFragment();
|
||||
let row;
|
||||
while ((row = aResultSet.getNextRow())) {
|
||||
let uri = row.getResultByIndex(1);
|
||||
let title = row.getResultByIndex(2);
|
||||
|
||||
let item = doc.createElementNS(kNSXUL, "toolbarbutton");
|
||||
item.setAttribute("label", title || uri);
|
||||
item.setAttribute("targetURI", uri);
|
||||
item.setAttribute("class", "subviewbutton");
|
||||
item.addEventListener("command", onItemCommand);
|
||||
item.addEventListener("click", onItemCommand);
|
||||
item.setAttribute("image", "page-icon:" + uri);
|
||||
fragment.appendChild(item);
|
||||
}
|
||||
items.appendChild(fragment);
|
||||
},
|
||||
handleError(aError) {
|
||||
log.debug("History view tried to show but had an error: " + aError);
|
||||
},
|
||||
handleCompletion(aReason) {
|
||||
log.debug("History view is being shown!");
|
||||
},
|
||||
});
|
||||
let item = doc.createElementNS(kNSXUL, "toolbarbutton");
|
||||
item.setAttribute("label", title || uri);
|
||||
item.setAttribute("targetURI", uri);
|
||||
item.setAttribute("class", "subviewbutton");
|
||||
item.addEventListener("command", onItemCommand);
|
||||
item.addEventListener("click", onItemCommand);
|
||||
item.setAttribute("image", "page-icon:" + uri);
|
||||
fragment.appendChild(item);
|
||||
}
|
||||
items.appendChild(fragment);
|
||||
},
|
||||
handleError(aError) {
|
||||
log.debug("History view tried to show but had an error: " + aError);
|
||||
reject();
|
||||
},
|
||||
handleCompletion(aReason) {
|
||||
log.debug("History view is being shown!");
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
let recentlyClosedTabs = doc.getElementById("PanelUI-recentlyClosedTabs");
|
||||
while (recentlyClosedTabs.firstChild) {
|
||||
|
@ -37,7 +37,7 @@ let getExtension = () => {
|
||||
};
|
||||
|
||||
add_task(async function testProfilerControl() {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[
|
||||
"extensions.geckoProfiler.symbols.url",
|
||||
|
@ -1,20 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const EDGE_AVAILABLE_MIGRATIONS =
|
||||
MigrationUtils.resourceTypes.COOKIES |
|
||||
MigrationUtils.resourceTypes.BOOKMARKS |
|
||||
MigrationUtils.resourceTypes.HISTORY |
|
||||
MigrationUtils.resourceTypes.PASSWORDS;
|
||||
|
||||
add_task(function* () {
|
||||
let migrator = MigrationUtils.getMigrator("edge");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Assert.equal(!!(migrator && migrator.sourceExists), AppConstants.isPlatformAndVersionAtLeast("win", "10"),
|
||||
"Edge should be available for migration if and only if we're on Win 10+");
|
||||
if (migrator) {
|
||||
let migratableData = migrator.getMigrateData(null, false);
|
||||
Assert.equal(migratableData, EDGE_AVAILABLE_MIGRATIONS,
|
||||
"All the data types we expect should be available");
|
||||
}
|
||||
});
|
||||
|
@ -12,7 +12,6 @@ support-files =
|
||||
skip-if = os != "mac" # Relies on ULibDir
|
||||
[test_Chrome_passwords.js]
|
||||
skip-if = os != "win"
|
||||
[test_Edge_availability.js]
|
||||
[test_Edge_db_migration.js]
|
||||
skip-if = os != "win" || os_version == "5.1" || os_version == "5.2" # Relies on post-XP bits of ESEDB
|
||||
[test_fx_telemetry.js]
|
||||
|
@ -18,6 +18,19 @@ const TEST_STATE = {
|
||||
}]
|
||||
};
|
||||
|
||||
const TEST_STATE_2 = {
|
||||
windows: [{
|
||||
tabs: [
|
||||
{ entries: [{ url: "about:robots" }]
|
||||
},
|
||||
{ entries: [],
|
||||
userTypedValue: "http://example.com",
|
||||
userTypedClear: 1
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
|
||||
function countNonLazyTabs(win) {
|
||||
win = win || window;
|
||||
let count = 0;
|
||||
@ -82,6 +95,17 @@ add_task(async function test() {
|
||||
newWindow.close();
|
||||
});
|
||||
|
||||
// Bug 1365933.
|
||||
info("Check that session with tab having empty entries array gets restored properly");
|
||||
await promiseBrowserState(TEST_STATE_2);
|
||||
|
||||
is(gBrowser.tabs.length, 2, "Window has 2 tabs");
|
||||
is(gBrowser.selectedBrowser.currentURI.spec, "about:robots", "Tab has the expected URL");
|
||||
|
||||
gBrowser.selectedTab = gBrowser.tabs[1];
|
||||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
is(gBrowser.selectedBrowser.currentURI.spec, "http://example.com/", "Tab has the expected URL");
|
||||
|
||||
// Cleanup.
|
||||
await promiseBrowserState(backupState);
|
||||
});
|
||||
|
@ -265,14 +265,6 @@ menuitem.bookmark-item {
|
||||
}
|
||||
|
||||
/* Location bar */
|
||||
#main-window {
|
||||
--urlbar-border-color: ThreeDShadow;
|
||||
--urlbar-border-color-hover: var(--urlbar-border-color);
|
||||
}
|
||||
|
||||
#navigator-toolbox:-moz-lwtheme {
|
||||
--urlbar-border-color: rgba(0,0,0,.3);
|
||||
}
|
||||
|
||||
#urlbar {
|
||||
/* override textbox[enablehistory="true"] styling: */
|
||||
@ -283,6 +275,11 @@ menuitem.bookmark-item {
|
||||
|
||||
%ifdef MOZ_PHOTON_THEME
|
||||
|
||||
#urlbar:not(:-moz-lwtheme):not([focused="true"]),
|
||||
.searchbar-textbox:not(:-moz-lwtheme):not([focused="true"]) {
|
||||
border-color: ThreeDShadow;
|
||||
}
|
||||
|
||||
#urlbar[focused="true"],
|
||||
.searchbar-textbox[focused="true"] {
|
||||
border-color: Highlight;
|
||||
@ -290,6 +287,15 @@ menuitem.bookmark-item {
|
||||
|
||||
%else
|
||||
|
||||
#main-window {
|
||||
--urlbar-border-color: ThreeDShadow;
|
||||
--urlbar-border-color-hover: var(--urlbar-border-color);
|
||||
}
|
||||
|
||||
#navigator-toolbox:-moz-lwtheme {
|
||||
--urlbar-border-color: rgba(0,0,0,.3);
|
||||
}
|
||||
|
||||
#urlbar,
|
||||
.searchbar-textbox {
|
||||
-moz-appearance: none;
|
||||
|
@ -526,9 +526,9 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
|
||||
|
||||
%ifdef MOZ_PHOTON_THEME
|
||||
|
||||
#main-window {
|
||||
--urlbar-border-color: hsla(240, 5%, 5%, .25);
|
||||
--urlbar-border-color-hover: hsla(240, 5%, 5%, .35);
|
||||
#urlbar,
|
||||
.searchbar-textbox {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
#urlbar[focused="true"],
|
||||
@ -537,11 +537,6 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
|
||||
box-shadow: var(--focus-ring-box-shadow);
|
||||
}
|
||||
|
||||
#urlbar,
|
||||
.searchbar-textbox {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
%else
|
||||
|
||||
#urlbar,
|
||||
|
@ -8,14 +8,20 @@
|
||||
.searchbar-textbox {
|
||||
-moz-appearance: none;
|
||||
background-clip: content-box;
|
||||
border: 1px solid var(--urlbar-border-color);
|
||||
border: 1px solid hsla(240,5%,5%,.25);
|
||||
border-radius: var(--toolbarbutton-border-radius);
|
||||
box-shadow: 0 1px 4px hsla(0, 0%, 0%, .05);
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.05);
|
||||
padding: 0;
|
||||
margin: 0 5px;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
#urlbar:hover,
|
||||
.searchbar-textbox:hover {
|
||||
border-color: hsla(240,5%,5%,.35);
|
||||
box-shadow: 0 1px 6px rgba(0,0,0,.1), 0 0 1px rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
#urlbar:-moz-lwtheme,
|
||||
.searchbar-textbox:-moz-lwtheme {
|
||||
background-color: hsla(0,0%,100%,.8);
|
||||
@ -29,12 +35,6 @@
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#urlbar:hover,
|
||||
.searchbar-textbox:hover {
|
||||
border: 1px solid var(--urlbar-border-color-hover);
|
||||
box-shadow: 0 1px 6px hsla(0, 0%, 0%, .1), 0 0 1px 0 rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
%endif
|
||||
|
||||
#urlbar-container {
|
||||
|
@ -652,31 +652,22 @@ toolbar[brighttext] #close-button {
|
||||
|
||||
/* ::::: Location Bar ::::: */
|
||||
|
||||
#main-window {
|
||||
--urlbar-border-color: ThreeDShadow;
|
||||
--urlbar-border-color-hover: var(--urlbar-border-color);
|
||||
}
|
||||
|
||||
#navigator-toolbox:-moz-lwtheme {
|
||||
--urlbar-border-color: var(--toolbarbutton-hover-bordercolor);
|
||||
}
|
||||
|
||||
%include ../shared/urlbar-searchbar.inc.css
|
||||
|
||||
%ifdef MOZ_PHOTON_THEME
|
||||
|
||||
@media (-moz-windows-default-theme) {
|
||||
#main-window:not(:-moz-lwtheme) {
|
||||
--urlbar-border-color: hsla(240, 5%, 5%, .25);
|
||||
--urlbar-border-color-hover: hsla(240, 5%, 5%, .35);
|
||||
}
|
||||
}
|
||||
|
||||
#urlbar,
|
||||
.searchbar-textbox {
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
@media (-moz-windows-default-theme: 0) {
|
||||
#urlbar:not(:-moz-lwtheme):not([focused="true"]),
|
||||
.searchbar-textbox:not(:-moz-lwtheme):not([focused="true"]) {
|
||||
border-color: ThreeDShadow;
|
||||
}
|
||||
}
|
||||
|
||||
#urlbar[focused="true"],
|
||||
.searchbar-textbox[focused="true"] {
|
||||
border-color: Highlight;
|
||||
@ -684,6 +675,15 @@ toolbar[brighttext] #close-button {
|
||||
|
||||
%else
|
||||
|
||||
#main-window {
|
||||
--urlbar-border-color: ThreeDShadow;
|
||||
--urlbar-border-color-hover: var(--urlbar-border-color);
|
||||
}
|
||||
|
||||
#navigator-toolbox:-moz-lwtheme {
|
||||
--urlbar-border-color: var(--toolbarbutton-hover-bordercolor);
|
||||
}
|
||||
|
||||
@media (-moz-windows-default-theme) {
|
||||
@media (-moz-os-version: windows-win7),
|
||||
(-moz-os-version: windows-win8) {
|
||||
|
@ -105,6 +105,8 @@ nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
|
||||
nsresult rv;
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
NS_ENSURE_ARG_POINTER(aLoadInfo);
|
||||
|
||||
NS_PRECONDITION(aResult, "Null out param");
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -145,6 +147,12 @@ nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We don't want to allow the inner protocol handler modify the result principal URI
|
||||
// since we want either |aURI| or anything pre-set by upper layers to prevail.
|
||||
nsCOMPtr<nsIURI> savedResultPrincipalURI;
|
||||
rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(result),
|
||||
resolvedURI,
|
||||
aLoadInfo);
|
||||
@ -168,9 +176,8 @@ nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
|
||||
|
||||
// Make sure that the channel remembers where it was
|
||||
// originally loaded from.
|
||||
nsLoadFlags loadFlags = 0;
|
||||
result->GetLoadFlags(&loadFlags);
|
||||
result->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
|
||||
rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = result->SetOriginalURI(aURI);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
|
@ -26,6 +26,7 @@ AboutURL.prototype = {
|
||||
newChannel: function (aURI, aLoadInfo) {
|
||||
let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, aLoadInfo);
|
||||
chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
chan.originalURI = aURI;
|
||||
return chan;
|
||||
},
|
||||
|
||||
|
@ -71,7 +71,10 @@
|
||||
div.addEventListener("mousedown", function (evt) {
|
||||
if (previousEvent === "touchend" && touchendTime !== 0) {
|
||||
let now = performance.now();
|
||||
div.dataset.isDelay = ((now - touchendTime) >= 300);
|
||||
// Do to time spent processing events our measurement might
|
||||
// be fractionally short of the actual delay. Round up any
|
||||
// microsecond changes in case we get something like 299.9.
|
||||
div.dataset.isDelay = ((now - touchendTime) >= 299.5);
|
||||
} else {
|
||||
div.dataset.isDelay = false;
|
||||
}
|
||||
|
@ -70,7 +70,10 @@
|
||||
div.addEventListener("mousedown", function(evt){
|
||||
if (previousEvent === "touchend" && touchendTime !== 0) {
|
||||
let now = performance.now();
|
||||
div.dataset.isDelay = ((now - touchendTime) >= 300) ? true : false;
|
||||
// Do to time spent processing events our measurement might
|
||||
// be fractionally short of the actual delay. Round up any
|
||||
// microsecond changes in case we get something like 299.9.
|
||||
div.dataset.isDelay = ((now - touchendTime) >= 299.5) ? true : false;
|
||||
} else {
|
||||
div.dataset.isDelay = false;
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ function* testCompletion([key, completion, index, total], editor) {
|
||||
|
||||
yield onSuggest;
|
||||
yield onVisibilityChange;
|
||||
yield waitForTick();
|
||||
yield waitForTime(5);
|
||||
|
||||
info("Checking the state");
|
||||
if (completion !== null) {
|
||||
|
@ -158,6 +158,7 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
|
||||
nsIChannel** aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
NS_ENSURE_ARG_POINTER(aLoadInfo);
|
||||
NS_ASSERTION(aResult, "must not be null");
|
||||
|
||||
nsAutoCString path;
|
||||
@ -174,9 +175,14 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
|
||||
rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
|
||||
tempURI,
|
||||
aLoadInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If tempURI links to an external URI (i.e. something other than
|
||||
// chrome:// or resource://) then set the LOAD_REPLACE flag on the
|
||||
// channel which forces the channel owner to reflect the displayed
|
||||
// chrome:// or resource://) then set result principal URI on the
|
||||
// load info which forces the channel principal to reflect the displayed
|
||||
// URL rather then being the systemPrincipal.
|
||||
bool isUIResource = false;
|
||||
rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
||||
@ -185,17 +191,9 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
|
||||
|
||||
bool isAboutBlank = NS_IsAboutBlank(tempURI);
|
||||
|
||||
nsLoadFlags loadFlags = isUIResource || isAboutBlank
|
||||
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
|
||||
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
|
||||
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
|
||||
tempURI,
|
||||
aLoadInfo,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!isUIResource && !isAboutBlank) {
|
||||
aLoadInfo->SetResultPrincipalURI(tempURI);
|
||||
}
|
||||
|
||||
tempChannel->SetOriginalURI(aURI);
|
||||
|
||||
|
@ -11182,6 +11182,12 @@ nsDocShell::DoURILoad(nsIURI* aURI,
|
||||
|
||||
if (aOriginalURI) {
|
||||
channel->SetOriginalURI(aOriginalURI);
|
||||
// The LOAD_REPLACE flag and its handling here will be removed as part
|
||||
// of bug 1319110. For now preserve its restoration here to not break
|
||||
// any code expecting it being set specially on redirected channels.
|
||||
// If the flag has originally been set to change result of
|
||||
// NS_GetFinalChannelURI it won't have any effect and also won't cause
|
||||
// any harm.
|
||||
if (aLoadReplace) {
|
||||
uint32_t loadFlags;
|
||||
channel->GetLoadFlags(&loadFlags);
|
||||
|
@ -260,8 +260,8 @@ private:
|
||||
bool mCalledPropertyDtor;
|
||||
#endif
|
||||
|
||||
uint32_t mMayHaveOpacityAnim;
|
||||
uint32_t mMayHaveTransformAnim;
|
||||
bool mMayHaveOpacityAnim;
|
||||
bool mMayHaveTransformAnim;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSIEVENTTARGET_FULL
|
||||
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
|
||||
|
||||
explicit EventSourceImpl(EventSource* aEventSource);
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsICancelableRunnable.h"
|
||||
#include "nsIIncrementalRunnable.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsString.h"
|
||||
|
||||
|
@ -1579,7 +1579,7 @@ void
|
||||
TimeoutManager::MaybeStartThrottleTrackingTimout()
|
||||
{
|
||||
if (gTrackingTimeoutThrottlingDelay <= 0 ||
|
||||
mWindow.AsInner()->InnerObjectsFreed()) {
|
||||
mWindow.AsInner()->InnerObjectsFreed() || mWindow.IsSuspended()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -87,8 +87,7 @@ public:
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIREQUEST
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
using nsIEventTarget::Dispatch;
|
||||
NS_DECL_NSIEVENTTARGET_FULL
|
||||
|
||||
explicit WebSocketImpl(WebSocket* aWebSocket)
|
||||
: mWebSocket(aWebSocket)
|
||||
|
@ -5087,7 +5087,7 @@ nsDocument::BeginUpdate(nsUpdateType aUpdateType)
|
||||
// in the wrong DocGroup. We're unlikely to run JS or do anything else
|
||||
// observable at this point. We reach this point when cycle collecting a
|
||||
// <link> element and the unlink code removes a style sheet.
|
||||
if (mDocGroup && !mIsGoingAway && !mIgnoreDocGroupMismatches) {
|
||||
if (mDocGroup && !mIsGoingAway && !mInUnlinkOrDeletion && !mIgnoreDocGroupMismatches) {
|
||||
mDocGroup->ValidateAccess();
|
||||
}
|
||||
|
||||
|
@ -3732,6 +3732,37 @@ nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsFocusManager::CanSkipFocus(nsIContent* aContent)
|
||||
{
|
||||
if (!aContent ||
|
||||
nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mFocusedContent == aContent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIDocShell* ds = aContent->OwnerDoc()->GetDocShell();
|
||||
if (!ds) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> root;
|
||||
ds->GetRootTreeItem(getter_AddRefs(root));
|
||||
nsCOMPtr<nsPIDOMWindowOuter> newRootWindow =
|
||||
root ? root->GetWindow() : nullptr;
|
||||
if (mActiveWindow != newRootWindow) {
|
||||
nsPIDOMWindowOuter* outerWindow = aContent->OwnerDoc()->GetWindow();
|
||||
if (outerWindow && outerWindow->GetFocusedNode() == aContent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NS_NewFocusManager(nsIFocusManager** aResult)
|
||||
{
|
||||
|
@ -101,10 +101,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool CanSkipFocus(nsIContent* aContent)
|
||||
{
|
||||
return mFocusedContent == aContent;
|
||||
}
|
||||
bool CanSkipFocus(nsIContent* aContent);
|
||||
|
||||
void FlushBeforeEventHandlingIfNeeded(nsIContent* aContent)
|
||||
{
|
||||
|
@ -563,7 +563,7 @@ NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
|
||||
class IdleRequestExecutor final : public nsIRunnable
|
||||
, public nsICancelableRunnable
|
||||
, public nsINamed
|
||||
, public nsIIncrementalRunnable
|
||||
, public nsIIdleRunnable
|
||||
{
|
||||
public:
|
||||
explicit IdleRequestExecutor(nsGlobalWindow* aWindow)
|
||||
@ -650,7 +650,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINamed)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsImageFrame.h"
|
||||
#include "nsSVGImageFrame.h"
|
||||
|
||||
#include "nsIPresShell.h"
|
||||
|
||||
@ -1180,8 +1181,9 @@ nsImageLoadingContent::CancelPendingEvent()
|
||||
RefPtr<imgRequestProxy>&
|
||||
nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
|
||||
{
|
||||
nsImageFrame* frame = do_QueryFrame(GetOurPrimaryFrame());
|
||||
if (frame) {
|
||||
nsImageFrame* imageFrame = do_QueryFrame(GetOurPrimaryFrame());
|
||||
nsSVGImageFrame* svgImageFrame = do_QueryFrame(GetOurPrimaryFrame());
|
||||
if (imageFrame || svgImageFrame) {
|
||||
// Detect JavaScript-based animations created by changing the |src|
|
||||
// attribute on a timer.
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
@ -1191,7 +1193,12 @@ nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
|
||||
|
||||
// If the length of time between request changes is less than the threshold,
|
||||
// then force sync decoding to eliminate flicker from the animation.
|
||||
frame->SetForceSyncDecoding(now - mMostRecentRequestChange < threshold);
|
||||
bool forceSync = (now - mMostRecentRequestChange < threshold);
|
||||
if (imageFrame) {
|
||||
imageFrame->SetForceSyncDecoding(forceSync);
|
||||
} else {
|
||||
svgImageFrame->SetForceSyncDecoding(forceSync);
|
||||
}
|
||||
|
||||
mMostRecentRequestChange = now;
|
||||
}
|
||||
|
@ -1689,7 +1689,7 @@ public:
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) override
|
||||
Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags = NS_DISPATCH_NORMAL) override
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
@ -2106,7 +2106,7 @@ public:
|
||||
}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSIEVENTTARGET_FULL
|
||||
|
||||
private:
|
||||
~EventTarget()
|
||||
|
30
editor/libeditor/crashtests/1366176.html
Normal file
30
editor/libeditor/crashtests/1366176.html
Normal file
@ -0,0 +1,30 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
|
||||
function go()
|
||||
{
|
||||
try { o114 = document.createElement('canvas'); } catch(e) {;}
|
||||
try { o103 = (new DOMParser).parseFromString('/', 'text/html'); } catch(e) {;}
|
||||
try { o217 = o103.all[1]; } catch(e) {;}
|
||||
try { o253 = window.getSelection(); } catch(e) {;}
|
||||
|
||||
try { o270 = document.body.appendChild(document.createElement('a')); } catch(e) {;}
|
||||
|
||||
try { o301 = window.getSelection(); } catch(e) {;}
|
||||
try { o314 = window.getSelection(); } catch(e) {;}
|
||||
try { document.body.appendChild(o114); } catch(e) {;}
|
||||
try { document.documentElement.appendChild(o217); } catch(e) {;}
|
||||
try { document.body.appendChild(o270); } catch(e) {;}
|
||||
try { o217.contentEditable = true } catch(e) {;}
|
||||
|
||||
try { o253.modify('move','forward','lineboundary'); } catch(e) {;}
|
||||
try { o314.modify('extend','left','line'); } catch(e) {;}
|
||||
try { o301.modify('extend','right','line'); } catch(e) {;}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go();">
|
||||
<plaintext></plaintext>
|
||||
</bdoy>
|
||||
</html>
|
@ -74,3 +74,4 @@ load 1317718.html
|
||||
load 1324505.html
|
||||
load 1348851.html
|
||||
load 1350772.html
|
||||
load 1366176.html
|
||||
|
@ -416,6 +416,15 @@ public:
|
||||
return mLayer->GetScrollThumbData();
|
||||
}
|
||||
|
||||
uint64_t GetScrollbarAnimationId() const
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
// This function is only really needed for template-compatibility with
|
||||
// WebRenderScrollDataWrapper. Although it will be called, the return
|
||||
// value is not used.
|
||||
return 0;
|
||||
}
|
||||
|
||||
FrameMetrics::ViewID GetScrollbarTargetContainerId() const
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
|
@ -205,14 +205,20 @@ Layer::~Layer()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Layer::EnsureAnimationsId()
|
||||
{
|
||||
if (!mCompositorAnimationsId) {
|
||||
mCompositorAnimationsId = AnimationHelper::GetNextCompositorAnimationsId();
|
||||
}
|
||||
}
|
||||
|
||||
Animation*
|
||||
Layer::AddAnimation()
|
||||
{
|
||||
// Here generates a new id when the first animation is added and
|
||||
// this id is used to represent the animations in this layer.
|
||||
if (!mCompositorAnimationsId) {
|
||||
mCompositorAnimationsId = AnimationHelper::GetNextCompositorAnimationsId();
|
||||
}
|
||||
EnsureAnimationsId();
|
||||
|
||||
MOZ_LAYERS_LOG_IF_SHADOWABLE(
|
||||
this, ("Layer::Mutated(%p) AddAnimation with id=%" PRIu64, this, mCompositorAnimationsId));
|
||||
|
@ -1223,6 +1223,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that this layer has a valid (non-zero) animations id. This value is
|
||||
// unique across layers.
|
||||
void EnsureAnimationsId();
|
||||
// Call AddAnimation to add a new animation to this layer from layout code.
|
||||
// Caller must fill in all the properties of the returned animation.
|
||||
// A later animation overrides an earlier one.
|
||||
|
@ -367,13 +367,21 @@ APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
|
||||
|
||||
bool
|
||||
APZCTreeManager::PushStateToWR(wr::WebRenderAPI* aWrApi,
|
||||
const TimeStamp& aSampleTime)
|
||||
const TimeStamp& aSampleTime,
|
||||
nsTArray<WrTransformProperty>& aTransformArray)
|
||||
{
|
||||
APZThreadUtils::AssertOnCompositorThread();
|
||||
MOZ_ASSERT(aWrApi);
|
||||
|
||||
MutexAutoLock lock(mTreeLock);
|
||||
|
||||
// During the first pass through the tree, we build a cache of guid->HTTN so
|
||||
// that we can find the relevant APZC instances quickly in subsequent passes,
|
||||
// such as the one below to generate scrollbar transforms. Without this, perf
|
||||
// could end up being O(n^2) instead of O(n log n) because we'd have to search
|
||||
// the tree to find the corresponding APZC every time we hit a thumb node.
|
||||
std::map<ScrollableLayerGuid, HitTestingTreeNode*> httnMap;
|
||||
|
||||
bool activeAnimations = false;
|
||||
uint64_t lastLayersId = -1;
|
||||
WrPipelineId lastPipelineId;
|
||||
@ -396,12 +404,22 @@ APZCTreeManager::PushStateToWR(wr::WebRenderAPI* aWrApi,
|
||||
if (aNode->GetLayersId() != lastLayersId) {
|
||||
// If we walked into or out of a subtree, we need to get the new
|
||||
// pipeline id.
|
||||
lastLayersId = aNode->GetLayersId();
|
||||
const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(lastLayersId);
|
||||
MOZ_ASSERT(state && state->mWrBridge);
|
||||
const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aNode->GetLayersId());
|
||||
if (!(state && state->mWrBridge)) {
|
||||
// During shutdown we might have layer tree information for stuff
|
||||
// that has already been torn down. In that case just skip over
|
||||
// those layers.
|
||||
return;
|
||||
}
|
||||
lastPipelineId = state->mWrBridge->PipelineId();
|
||||
lastLayersId = aNode->GetLayersId();
|
||||
}
|
||||
|
||||
// Use a 0 presShellId because when we do a lookup in this map for the
|
||||
// scrollbar below we don't have (or care about) the presShellId.
|
||||
ScrollableLayerGuid guid(lastLayersId, 0, apzc->GetGuid().mScrollId);
|
||||
httnMap.emplace(guid, aNode);
|
||||
|
||||
ParentLayerPoint layerTranslation = apzc->GetCurrentAsyncTransform(
|
||||
AsyncPanZoomController::RESPECT_FORCE_DISABLE).mTranslation;
|
||||
// The positive translation means the painted content is supposed to
|
||||
@ -416,6 +434,45 @@ APZCTreeManager::PushStateToWR(wr::WebRenderAPI* aWrApi,
|
||||
activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
|
||||
});
|
||||
|
||||
// Now we iterate over the nodes again, and generate the transforms needed
|
||||
// for scrollbar thumbs. Although we *could* do this as part of the previous
|
||||
// iteration, it's cleaner and more efficient to do it as a separate pass
|
||||
// because now we have a populated httnMap which allows O(log n) lookup here,
|
||||
// resulting in O(n log n) runtime.
|
||||
ForEachNode<ReverseIterator>(mRootNode.get(),
|
||||
[&](HitTestingTreeNode* aNode)
|
||||
{
|
||||
if (!aNode->IsScrollThumbNode()) {
|
||||
return;
|
||||
}
|
||||
ScrollableLayerGuid guid(aNode->GetLayersId(), 0, aNode->GetScrollTargetId());
|
||||
auto it = httnMap.find(guid);
|
||||
if (it == httnMap.end()) {
|
||||
// A scrollbar for content which didn't have an APZC. Possibly the
|
||||
// content isn't layerized. Regardless, we can't async-scroll it so
|
||||
// we can skip the async transform on the scrollbar.
|
||||
return;
|
||||
}
|
||||
|
||||
HitTestingTreeNode* scrollTargetNode = it->second;
|
||||
AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc();
|
||||
MOZ_ASSERT(scrollTargetApzc);
|
||||
LayerToParentLayerMatrix4x4 transform = scrollTargetApzc->CallWithLastContentPaintMetrics(
|
||||
[&](const FrameMetrics& aMetrics) {
|
||||
return AsyncCompositionManager::ComputeTransformForScrollThumb(
|
||||
aNode->GetTransform() * AsyncTransformMatrix(),
|
||||
scrollTargetNode->GetTransform().ToUnknownMatrix(),
|
||||
scrollTargetApzc,
|
||||
aMetrics,
|
||||
aNode->GetScrollThumbData(),
|
||||
scrollTargetNode->IsAncestorOf(aNode),
|
||||
nullptr);
|
||||
});
|
||||
aTransformArray.AppendElement(wr::ToWrTransformProperty(
|
||||
aNode->GetScrollbarAnimationId(),
|
||||
transform));
|
||||
});
|
||||
|
||||
return activeAnimations;
|
||||
}
|
||||
|
||||
@ -573,6 +630,7 @@ APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
|
||||
aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(),
|
||||
GetEventRegionsOverride(aParent, aLayer));
|
||||
node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
|
||||
aLayer.GetScrollbarAnimationId(),
|
||||
aLayer.GetScrollThumbData(),
|
||||
aLayer.IsScrollbarContainer());
|
||||
node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
|
||||
@ -762,6 +820,7 @@ APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
|
||||
// LayerTransactionParent.cpp must ensure that APZ will be notified
|
||||
// when those properties change.
|
||||
node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
|
||||
aLayer.GetScrollbarAnimationId(),
|
||||
aLayer.GetScrollThumbData(),
|
||||
aLayer.IsScrollbarContainer());
|
||||
node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
|
||||
#endif // defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
struct WrTransformProperty;
|
||||
|
||||
namespace mozilla {
|
||||
class MultiTouchInput;
|
||||
@ -153,12 +154,15 @@ public:
|
||||
* walks through the tree of APZC instances and tells webrender about the
|
||||
* async scroll position. It also advances APZ animations to the specified
|
||||
* sample time. In effect it is the webrender equivalent of (part of) the
|
||||
* code in AsyncCompositionManager.
|
||||
* code in AsyncCompositionManager. If scrollbar transforms need updating
|
||||
* to reflect the async scroll position, the updated transforms are appended
|
||||
* to the provided aTransformArray.
|
||||
* Returns true if any APZ animations are in progress and we need to keep
|
||||
* compositing.
|
||||
*/
|
||||
bool PushStateToWR(wr::WebRenderAPI* aWrApi,
|
||||
const TimeStamp& aSampleTime);
|
||||
const TimeStamp& aSampleTime,
|
||||
nsTArray<WrTransformProperty>& aTransformArray);
|
||||
|
||||
/**
|
||||
* Walk the tree of APZCs and flushes the repaint requests for all the APZCS
|
||||
|
@ -27,6 +27,7 @@ HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
|
||||
, mIsPrimaryApzcHolder(aIsPrimaryHolder)
|
||||
, mLayersId(aLayersId)
|
||||
, mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
|
||||
, mScrollbarAnimationId(0)
|
||||
, mIsScrollbarContainer(false)
|
||||
, mFixedPosTarget(FrameMetrics::NULL_SCROLL_ID)
|
||||
, mOverride(EventRegionsOverride::NoOverride)
|
||||
@ -94,10 +95,12 @@ HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild)
|
||||
|
||||
void
|
||||
HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
|
||||
const uint64_t& aScrollbarAnimationId,
|
||||
const ScrollThumbData& aThumbData,
|
||||
bool aIsScrollContainer)
|
||||
{
|
||||
mScrollViewId = aScrollViewId;
|
||||
mScrollbarAnimationId = aScrollbarAnimationId;
|
||||
mScrollThumbData = aThumbData;
|
||||
mIsScrollbarContainer = aIsScrollContainer;
|
||||
}
|
||||
@ -128,6 +131,12 @@ HitTestingTreeNode::GetScrollTargetId() const
|
||||
return mScrollViewId;
|
||||
}
|
||||
|
||||
const uint64_t&
|
||||
HitTestingTreeNode::GetScrollbarAnimationId() const
|
||||
{
|
||||
return mScrollbarAnimationId;
|
||||
}
|
||||
|
||||
const ScrollThumbData&
|
||||
HitTestingTreeNode::GetScrollThumbData() const
|
||||
{
|
||||
|
@ -94,6 +94,7 @@ public:
|
||||
/* Scrollbar info */
|
||||
|
||||
void SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
|
||||
const uint64_t& aScrollbarAnimationId,
|
||||
const ScrollThumbData& aThumbData,
|
||||
bool aIsScrollContainer);
|
||||
bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
|
||||
@ -101,6 +102,7 @@ public:
|
||||
bool IsScrollThumbNode() const; // Scroll thumb container layer.
|
||||
FrameMetrics::ViewID GetScrollTargetId() const;
|
||||
const ScrollThumbData& GetScrollThumbData() const;
|
||||
const uint64_t& GetScrollbarAnimationId() const;
|
||||
|
||||
/* Fixed pos info */
|
||||
|
||||
@ -138,6 +140,11 @@ private:
|
||||
// represents the scroll id of the scroll frame scrolled by the scrollbar.
|
||||
FrameMetrics::ViewID mScrollViewId;
|
||||
|
||||
// This is only set to non-zero if WebRender is enabled, and only for HTTNs
|
||||
// where IsScrollThumbNode() returns true. It holds the animation id that we
|
||||
// use to move the thumb node to reflect async scrolling.
|
||||
uint64_t mScrollbarAnimationId;
|
||||
|
||||
// This is set for scroll thumb Container layers only.
|
||||
ScrollThumbData mScrollThumbData;
|
||||
|
||||
|
@ -373,7 +373,7 @@ WebRenderBridgeParent::UpdateAPZ()
|
||||
}
|
||||
|
||||
bool
|
||||
WebRenderBridgeParent::PushAPZStateToWR()
|
||||
WebRenderBridgeParent::PushAPZStateToWR(nsTArray<WrTransformProperty>& aTransformArray)
|
||||
{
|
||||
CompositorBridgeParent* cbp = GetRootCompositorBridgeParent();
|
||||
if (!cbp) {
|
||||
@ -387,7 +387,7 @@ WebRenderBridgeParent::PushAPZStateToWR()
|
||||
if (frameInterval != TimeDuration::Forever()) {
|
||||
animationTime += frameInterval;
|
||||
}
|
||||
return apzc->PushStateToWR(mApi, animationTime);
|
||||
return apzc->PushStateToWR(mApi, animationTime, aTransformArray);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -776,28 +776,36 @@ WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::In
|
||||
return;
|
||||
}
|
||||
|
||||
if (PushAPZStateToWR()) {
|
||||
ScheduleComposition();
|
||||
}
|
||||
bool scheduleComposite = false;
|
||||
nsTArray<WrOpacityProperty> opacityArray;
|
||||
nsTArray<WrTransformProperty> transformArray;
|
||||
|
||||
if (gfxPrefs::WebRenderOMTAEnabled()) {
|
||||
nsTArray<WrOpacityProperty> opacityArray;
|
||||
nsTArray<WrTransformProperty> transformArray;
|
||||
SampleAnimations(opacityArray, transformArray);
|
||||
|
||||
if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
|
||||
mApi->GenerateFrame(opacityArray, transformArray);
|
||||
ScheduleComposition();
|
||||
return;
|
||||
scheduleComposite = true;
|
||||
}
|
||||
}
|
||||
|
||||
mApi->GenerateFrame();
|
||||
if (PushAPZStateToWR(transformArray)) {
|
||||
scheduleComposite = true;
|
||||
}
|
||||
|
||||
if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
|
||||
mApi->GenerateFrame(opacityArray, transformArray);
|
||||
} else {
|
||||
mApi->GenerateFrame();
|
||||
}
|
||||
|
||||
// XXX Enable it when async video is supported.
|
||||
// if (!mCompositableHolder->GetCompositeUntilTime().IsNull()) {
|
||||
// ScheduleComposition();
|
||||
// scheduleComposite = true;
|
||||
// }
|
||||
|
||||
if (scheduleComposite) {
|
||||
ScheduleComposition();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -199,7 +199,9 @@ private:
|
||||
|
||||
// Have APZ push the async scroll state to WR. Returns true if an APZ
|
||||
// animation is in effect and we need to schedule another composition.
|
||||
bool PushAPZStateToWR();
|
||||
// If scrollbars need their transforms updated, the provided aTransformArray
|
||||
// is populated with the property update details.
|
||||
bool PushAPZStateToWR(nsTArray<WrTransformProperty>& aTransformArray);
|
||||
|
||||
private:
|
||||
struct PendingTransactionId {
|
||||
|
@ -90,6 +90,23 @@ WebRenderContainerLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
|
||||
WrBridge()->AddWebRenderParentCommand(anim);
|
||||
}
|
||||
|
||||
// If APZ is enabled and this layer is a scroll thumb, then it might need
|
||||
// to move in the compositor to represent the async scroll position. So we
|
||||
// ensure that there is an animations id set on it, we will use this to give
|
||||
// WebRender updated transforms for composition.
|
||||
if (WrManager()->AsyncPanZoomEnabled() &&
|
||||
GetScrollThumbData().mDirection != ScrollDirection::NONE) {
|
||||
// A scroll thumb better not have a transform animation already or we're
|
||||
// going to end up clobbering it with APZ animating it too.
|
||||
MOZ_ASSERT(transformForSC);
|
||||
|
||||
EnsureAnimationsId();
|
||||
animationsId = GetCompositorAnimationsId();
|
||||
// We need to set the transform in the stacking context to null for it to
|
||||
// pick up and install the animation id.
|
||||
transformForSC = nullptr;
|
||||
}
|
||||
|
||||
ScrollingLayersHelper scroller(this, aBuilder, aSc);
|
||||
StackingContextHelper sc(aSc, aBuilder, this, animationsId, opacityForSC, transformForSC);
|
||||
|
||||
|
@ -44,6 +44,7 @@ WebRenderLayerScrollData::Initialize(WebRenderScrollData& aOwner,
|
||||
? aLayer->AsContainerLayer()->GetEventRegionsOverride()
|
||||
: EventRegionsOverride::NoOverride;
|
||||
mScrollThumbData = aLayer->GetScrollThumbData();
|
||||
mScrollbarAnimationId = aLayer->GetCompositorAnimationsId();
|
||||
mScrollbarTargetContainerId = aLayer->GetScrollbarTargetContainerId();
|
||||
mIsScrollbarContainer = aLayer->IsScrollbarContainer();
|
||||
mFixedPosScrollContainerId = aLayer->GetFixedPositionScrollContainerId();
|
||||
|
@ -54,6 +54,7 @@ public:
|
||||
Maybe<uint64_t> GetReferentId() const { return mReferentId; }
|
||||
EventRegionsOverride GetEventRegionsOverride() const { return mEventRegionsOverride; }
|
||||
const ScrollThumbData& GetScrollThumbData() const { return mScrollThumbData; }
|
||||
const uint64_t& GetScrollbarAnimationId() const { return mScrollbarAnimationId; }
|
||||
FrameMetrics::ViewID GetScrollbarTargetContainerId() const { return mScrollbarTargetContainerId; }
|
||||
bool IsScrollbarContainer() const { return mIsScrollbarContainer; }
|
||||
FrameMetrics::ViewID GetFixedPositionScrollContainerId() const { return mFixedPosScrollContainerId; }
|
||||
@ -83,6 +84,7 @@ private:
|
||||
Maybe<uint64_t> mReferentId;
|
||||
EventRegionsOverride mEventRegionsOverride;
|
||||
ScrollThumbData mScrollThumbData;
|
||||
uint64_t mScrollbarAnimationId;
|
||||
FrameMetrics::ViewID mScrollbarTargetContainerId;
|
||||
bool mIsScrollbarContainer;
|
||||
FrameMetrics::ViewID mFixedPosScrollContainerId;
|
||||
@ -176,6 +178,7 @@ struct ParamTraits<mozilla::layers::WebRenderLayerScrollData>
|
||||
WriteParam(aMsg, aParam.mReferentId);
|
||||
WriteParam(aMsg, aParam.mEventRegionsOverride);
|
||||
WriteParam(aMsg, aParam.mScrollThumbData);
|
||||
WriteParam(aMsg, aParam.mScrollbarAnimationId);
|
||||
WriteParam(aMsg, aParam.mScrollbarTargetContainerId);
|
||||
WriteParam(aMsg, aParam.mIsScrollbarContainer);
|
||||
WriteParam(aMsg, aParam.mFixedPosScrollContainerId);
|
||||
@ -193,6 +196,7 @@ struct ParamTraits<mozilla::layers::WebRenderLayerScrollData>
|
||||
&& ReadParam(aMsg, aIter, &aResult->mReferentId)
|
||||
&& ReadParam(aMsg, aIter, &aResult->mEventRegionsOverride)
|
||||
&& ReadParam(aMsg, aIter, &aResult->mScrollThumbData)
|
||||
&& ReadParam(aMsg, aIter, &aResult->mScrollbarAnimationId)
|
||||
&& ReadParam(aMsg, aIter, &aResult->mScrollbarTargetContainerId)
|
||||
&& ReadParam(aMsg, aIter, &aResult->mIsScrollbarContainer)
|
||||
&& ReadParam(aMsg, aIter, &aResult->mFixedPosScrollContainerId);
|
||||
|
@ -285,6 +285,12 @@ public:
|
||||
return mLayer->GetScrollThumbData();
|
||||
}
|
||||
|
||||
uint64_t GetScrollbarAnimationId() const
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
return mLayer->GetScrollbarAnimationId();
|
||||
}
|
||||
|
||||
FrameMetrics::ViewID GetScrollbarTargetContainerId() const
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
|
@ -363,7 +363,9 @@ static inline WrRepeatMode ToWrRepeatMode(uint8_t repeatMode)
|
||||
return WrRepeatMode::Stretch;
|
||||
}
|
||||
|
||||
static inline WrTransformProperty ToWrTransformProperty(uint64_t id, gfx::Matrix4x4& transform)
|
||||
template<class S, class T>
|
||||
static inline WrTransformProperty ToWrTransformProperty(uint64_t id,
|
||||
const gfx::Matrix4x4Typed<S, T>& transform)
|
||||
{
|
||||
WrTransformProperty prop;
|
||||
prop.id = id;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "URIUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
@ -304,6 +305,13 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
|
||||
sandboxedLoadingPrincipalInfo = sandboxedLoadingPrincipalInfoTemp;
|
||||
}
|
||||
|
||||
OptionalURIParams optionalResultPrincipalURI = mozilla::void_t();
|
||||
nsCOMPtr<nsIURI> resultPrincipalURI;
|
||||
Unused << aLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
|
||||
if (resultPrincipalURI) {
|
||||
SerializeURI(resultPrincipalURI, optionalResultPrincipalURI);
|
||||
}
|
||||
|
||||
nsTArray<PrincipalInfo> redirectChainIncludingInternalRedirects;
|
||||
for (const nsCOMPtr<nsIPrincipal>& principal : aLoadInfo->RedirectChainIncludingInternalRedirects()) {
|
||||
rv = PrincipalToPrincipalInfo(principal, redirectChainIncludingInternalRedirects.AppendElement());
|
||||
@ -322,6 +330,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
|
||||
triggeringPrincipalInfo,
|
||||
principalToInheritInfo,
|
||||
sandboxedLoadingPrincipalInfo,
|
||||
optionalResultPrincipalURI,
|
||||
aLoadInfo->GetSecurityFlags(),
|
||||
aLoadInfo->InternalContentPolicyType(),
|
||||
static_cast<uint32_t>(aLoadInfo->GetTainting()),
|
||||
@ -385,6 +394,12 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> resultPrincipalURI;
|
||||
if (loadInfoArgs.resultPrincipalURI().type() != OptionalURIParams::Tvoid_t) {
|
||||
resultPrincipalURI = DeserializeURI(loadInfoArgs.resultPrincipalURI());
|
||||
NS_ENSURE_TRUE(resultPrincipalURI, NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
nsTArray<nsCOMPtr<nsIPrincipal>> redirectChainIncludingInternalRedirects;
|
||||
for (const PrincipalInfo& principalInfo : loadInfoArgs.redirectChainIncludingInternalRedirects()) {
|
||||
nsCOMPtr<nsIPrincipal> redirectedPrincipal =
|
||||
@ -406,6 +421,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
|
||||
triggeringPrincipal,
|
||||
principalToInherit,
|
||||
sandboxedLoadingPrincipal,
|
||||
resultPrincipalURI,
|
||||
loadInfoArgs.securityFlags(),
|
||||
loadInfoArgs.contentPolicyType(),
|
||||
static_cast<LoadTainting>(loadInfoArgs.tainting()),
|
||||
|
@ -126,7 +126,7 @@ struct ParamTraits<mozilla::mscom::COMPtrHolder<Interface, _IID>>
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::mscom::ProxyStream proxyStream(buf.get(), length);
|
||||
mozilla::mscom::ProxyStream proxyStream(_IID, buf.get(), length);
|
||||
if (!proxyStream.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/NotNull.h"
|
||||
#include "nsExceptionHandler.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
#include <oleauto.h>
|
||||
|
||||
@ -354,7 +355,14 @@ AnnotateInterfaceRegistration(REFIID aIid)
|
||||
|
||||
json.End();
|
||||
|
||||
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("InterfaceRegistrationInfo"),
|
||||
nsAutoCString annotationKey;
|
||||
annotationKey.AppendLiteral("InterfaceRegistrationInfo");
|
||||
if (XRE_IsParentProcess()) {
|
||||
annotationKey.AppendLiteral("Parent");
|
||||
} else {
|
||||
annotationKey.AppendLiteral("Child");
|
||||
}
|
||||
CrashReporter::AnnotateCrashReport(annotationKey,
|
||||
static_cast<CStringWriter*>(json.WriteFunc())->Get());
|
||||
}
|
||||
|
||||
|
@ -32,12 +32,12 @@ ProxyStream::ProxyStream()
|
||||
|
||||
// GetBuffer() fails with this variant, but that's okay because we're just
|
||||
// reconstructing the stream from a buffer anyway.
|
||||
ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
|
||||
ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
|
||||
const int aInitBufSize)
|
||||
: mStream(InitStream(aInitBuf, static_cast<const UINT>(aInitBufSize)))
|
||||
, mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
, mBufSize(aInitBufSize)
|
||||
, mUnmarshalResult(E_UNEXPECTED)
|
||||
{
|
||||
if (!aInitBufSize) {
|
||||
// We marshaled a nullptr. Nothing else to do here.
|
||||
@ -51,6 +51,8 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
|
||||
return;
|
||||
}
|
||||
|
||||
HRESULT unmarshalResult = S_OK;
|
||||
|
||||
// We need to convert to an interface here otherwise we mess up const
|
||||
// correctness with IPDL. We'll request an IUnknown and then QI the
|
||||
// actual interface later.
|
||||
@ -59,10 +61,10 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
|
||||
{
|
||||
// OK to forget mStream when calling into this function because the stream
|
||||
// gets released even if the unmarshaling part fails.
|
||||
mUnmarshalResult =
|
||||
unmarshalResult =
|
||||
::CoGetInterfaceAndReleaseStream(mStream.forget().take(), IID_IUnknown,
|
||||
getter_AddRefs(mUnmarshaledProxy));
|
||||
MOZ_ASSERT(SUCCEEDED(mUnmarshalResult));
|
||||
MOZ_ASSERT(SUCCEEDED(unmarshalResult));
|
||||
};
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
@ -75,10 +77,11 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
|
||||
}
|
||||
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
if (FAILED(mUnmarshalResult)) {
|
||||
nsPrintfCString hrAsStr("0x%08X", mUnmarshalResult);
|
||||
if (FAILED(unmarshalResult)) {
|
||||
nsPrintfCString hrAsStr("0x%08X", unmarshalResult);
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("CoGetInterfaceAndReleaseStreamFailure"), hrAsStr);
|
||||
AnnotateInterfaceRegistration(aIID);
|
||||
}
|
||||
#endif // defined(MOZ_CRASHREPORTER)
|
||||
}
|
||||
@ -197,12 +200,6 @@ ProxyStream::GetInterface(REFIID aIID, void** aOutInterface) const
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
if (FAILED(mUnmarshalResult)) {
|
||||
AnnotateInterfaceRegistration(aIID);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mUnmarshaledProxy) {
|
||||
*aOutInterface = nullptr;
|
||||
return true;
|
||||
|
@ -21,7 +21,7 @@ class ProxyStream final
|
||||
public:
|
||||
ProxyStream();
|
||||
ProxyStream(REFIID aIID, IUnknown* aObject);
|
||||
ProxyStream(const BYTE* aInitBuf, const int aInitBufSize);
|
||||
ProxyStream(REFIID aIID, const BYTE* aInitBuf, const int aInitBufSize);
|
||||
|
||||
~ProxyStream();
|
||||
|
||||
|
@ -65,6 +65,20 @@ const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
|
||||
static const uint32_t BLACK = 0;
|
||||
static const uint32_t GRAY = 1;
|
||||
|
||||
/*
|
||||
* Two bits determine the mark color as follows:
|
||||
* BLACK_BIT GRAY_OR_BLACK_BIT color
|
||||
* 0 0 white
|
||||
* 0 1 gray
|
||||
* 1 0 black
|
||||
* 1 1 black
|
||||
*/
|
||||
enum class ColorBit : uint32_t
|
||||
{
|
||||
BlackBit = 0,
|
||||
GrayOrBlackBit = 1
|
||||
};
|
||||
|
||||
/*
|
||||
* The "location" field in the Chunk trailer is a enum indicating various roles
|
||||
* of the chunk.
|
||||
@ -301,11 +315,12 @@ GetGCThingMarkBitmap(const uintptr_t addr)
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
GetGCThingMarkWordAndMask(const uintptr_t addr, uint32_t color,
|
||||
GetGCThingMarkWordAndMask(const uintptr_t addr, ColorBit colorBit,
|
||||
uintptr_t** wordp, uintptr_t* maskp)
|
||||
{
|
||||
MOZ_ASSERT(addr);
|
||||
const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit + color;
|
||||
const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit +
|
||||
static_cast<uint32_t>(colorBit);
|
||||
MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits);
|
||||
uintptr_t* bitmap = GetGCThingMarkBitmap(addr);
|
||||
const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT;
|
||||
@ -325,12 +340,20 @@ GetGCThingZone(const uintptr_t addr)
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
TenuredCellIsMarkedGray(const Cell* cell)
|
||||
{
|
||||
// Return true if GrayOrBlackBit is set and BlackBit is not set.
|
||||
MOZ_ASSERT(cell);
|
||||
MOZ_ASSERT(!js::gc::IsInsideNursery(cell));
|
||||
|
||||
uintptr_t* word, mask;
|
||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::GRAY, &word, &mask);
|
||||
return *word & mask;
|
||||
uintptr_t* grayWord, grayMask;
|
||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::ColorBit::GrayOrBlackBit,
|
||||
&grayWord, &grayMask);
|
||||
if (!(*grayWord & grayMask))
|
||||
return false;
|
||||
|
||||
uintptr_t* blackWord, blackMask;
|
||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::ColorBit::BlackBit,
|
||||
&blackWord, &blackMask);
|
||||
return !(*blackWord & blackMask);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
|
@ -907,7 +907,6 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable,
|
||||
#if ENABLE_INTL_API
|
||||
RootedAtom a(cx);
|
||||
uint32_t count = countAvailable();
|
||||
RootedValue t(cx, BooleanValue(true));
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const char* locale = getAvailable(i);
|
||||
auto lang = DuplicateString(cx, locale);
|
||||
@ -919,7 +918,7 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable,
|
||||
a = Atomize(cx, lang.get(), strlen(lang.get()));
|
||||
if (!a)
|
||||
return false;
|
||||
if (!DefineProperty(cx, locales, a->asPropertyName(), t, nullptr, nullptr,
|
||||
if (!DefineProperty(cx, locales, a->asPropertyName(), TrueHandleValue, nullptr, nullptr,
|
||||
JSPROP_ENUMERATE))
|
||||
{
|
||||
return false;
|
||||
@ -1215,7 +1214,6 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
|
||||
if (!DefineElement(cx, collations, index++, NullHandleValue))
|
||||
return false;
|
||||
|
||||
RootedString jscollation(cx);
|
||||
RootedValue element(cx);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const char* collation = uenum_next(values, nullptr, &status);
|
||||
@ -1232,7 +1230,7 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
|
||||
continue;
|
||||
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents.
|
||||
jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
|
||||
JSString* jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
|
||||
if (!jscollation)
|
||||
return false;
|
||||
element = StringValue(jscollation);
|
||||
@ -2674,6 +2672,30 @@ js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DefaultCalendar(JSContext* cx, const JSAutoByteString& locale, MutableHandleValue rval)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
|
||||
|
||||
// This correctly handles nullptr |cal| when opening failed.
|
||||
ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
|
||||
|
||||
const char* calendar = ucal_getType(cal, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents
|
||||
JSString* str = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
rval.setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct CalendarAlias
|
||||
{
|
||||
const char* const calendar;
|
||||
@ -2702,31 +2724,15 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
|
||||
uint32_t index = 0;
|
||||
|
||||
// We need the default calendar for the locale as the first result.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
RootedString jscalendar(cx);
|
||||
{
|
||||
UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
|
||||
RootedValue element(cx);
|
||||
if (!DefaultCalendar(cx, locale, &element))
|
||||
return false;
|
||||
|
||||
// This correctly handles nullptr |cal| when opening failed.
|
||||
ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
|
||||
|
||||
const char* calendar = ucal_getType(cal, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents
|
||||
jscalendar = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
|
||||
if (!jscalendar)
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue element(cx, StringValue(jscalendar));
|
||||
if (!DefineElement(cx, calendars, index++, element))
|
||||
return false;
|
||||
|
||||
// Now get the calendars that "would make a difference", i.e., not the default.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
|
||||
@ -2750,7 +2756,7 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
|
||||
// ICU returns old-style keyword values; map them to BCP 47 equivalents
|
||||
calendar = uloc_toUnicodeLocaleType("ca", calendar);
|
||||
|
||||
jscalendar = JS_NewStringCopyZ(cx, calendar);
|
||||
JSString* jscalendar = JS_NewStringCopyZ(cx, calendar);
|
||||
if (!jscalendar)
|
||||
return false;
|
||||
element = StringValue(jscalendar);
|
||||
@ -2760,7 +2766,7 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
|
||||
// ICU doesn't return calendar aliases, append them here.
|
||||
for (const auto& calendarAlias : calendarAliases) {
|
||||
if (equal(calendar, calendarAlias.calendar)) {
|
||||
jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
|
||||
JSString* jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
|
||||
if (!jscalendar)
|
||||
return false;
|
||||
element = StringValue(jscalendar);
|
||||
@ -2774,6 +2780,20 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isString());
|
||||
|
||||
JSAutoByteString locale(cx, args[0].toString());
|
||||
if (!locale)
|
||||
return false;
|
||||
|
||||
return DefaultCalendar(cx, locale, args.rval());
|
||||
}
|
||||
|
||||
template<typename Char>
|
||||
static constexpr Char
|
||||
ToUpperASCII(Char c)
|
||||
|
@ -435,6 +435,16 @@ intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* Returns the calendar type identifier per Unicode Technical Standard 35,
|
||||
* Unicode Locale Data Markup Language, for the default calendar for the given
|
||||
* locale.
|
||||
*
|
||||
* Usage: calendar = intl_defaultCalendar(locale)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/**
|
||||
* 6.4.1 IsValidTimeZoneName ( timeZone )
|
||||
*
|
||||
|
@ -898,7 +898,6 @@ function BestAvailableLocaleIgnoringDefault(availableLocales, locale) {
|
||||
return BestAvailableLocaleHelper(availableLocales, locale, false);
|
||||
}
|
||||
|
||||
var noRelevantExtensionKeys = [];
|
||||
|
||||
/**
|
||||
* Compares a BCP 47 language priority list against the set of locales in
|
||||
@ -1058,26 +1057,17 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte
|
||||
// Step 8.
|
||||
var supportedExtension = "-u";
|
||||
|
||||
// In this implementation, localeData is a function, not an object.
|
||||
var localeDataProvider = localeData();
|
||||
|
||||
// Steps 9-12.
|
||||
var i = 0;
|
||||
var len = relevantExtensionKeys.length;
|
||||
var foundLocaleData;
|
||||
if (len > 0) {
|
||||
// In this implementation, localeData is a function, not an object.
|
||||
// Step 12.b.
|
||||
foundLocaleData = localeData(foundLocale);
|
||||
}
|
||||
while (i < len) {
|
||||
for (var i = 0; i < relevantExtensionKeys.length; i++) {
|
||||
// Step 12.a.
|
||||
var key = relevantExtensionKeys[i];
|
||||
|
||||
// Step 12.c.
|
||||
var keyLocaleData = foundLocaleData[key];
|
||||
|
||||
// Locale data provides default value.
|
||||
// Step 12.d.
|
||||
var value = keyLocaleData[0];
|
||||
assert(typeof value === "string" || value === null, "unexpected locale data value");
|
||||
// Steps 12.b-d (The locale data is only computed when needed).
|
||||
var keyLocaleData = undefined;
|
||||
var value = undefined;
|
||||
|
||||
// Locale tag may override.
|
||||
|
||||
@ -1096,6 +1086,9 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte
|
||||
|
||||
// Step 12.f.ii.
|
||||
if (requestedValue !== undefined) {
|
||||
// Steps 12.b-c.
|
||||
keyLocaleData = callFunction(localeDataProvider[key], null, foundLocale);
|
||||
|
||||
// Step 12.f.ii.1.
|
||||
if (requestedValue !== "") {
|
||||
// Step 12.f.ii.1.a.
|
||||
@ -1120,18 +1113,29 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte
|
||||
var optionsValue = options[key];
|
||||
|
||||
// Step 12.g, 12.g.ii.
|
||||
if (optionsValue !== undefined &&
|
||||
optionsValue !== value &&
|
||||
callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1)
|
||||
{
|
||||
value = optionsValue;
|
||||
supportedExtensionAddition = "";
|
||||
if (optionsValue !== undefined && optionsValue !== value) {
|
||||
// Steps 12.b-c.
|
||||
if (keyLocaleData === undefined)
|
||||
keyLocaleData = callFunction(localeDataProvider[key], null, foundLocale);
|
||||
|
||||
if (callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1) {
|
||||
value = optionsValue;
|
||||
supportedExtensionAddition = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Locale data provides default value.
|
||||
if (value === undefined) {
|
||||
// Steps 12.b-d.
|
||||
value = keyLocaleData === undefined
|
||||
? callFunction(localeDataProvider.default[key], null, foundLocale)
|
||||
: keyLocaleData[0];
|
||||
}
|
||||
|
||||
// Steps 12.h-j.
|
||||
assert(typeof value === "string" || value === null, "unexpected locale data value");
|
||||
result[key] = value;
|
||||
supportedExtension += supportedExtensionAddition;
|
||||
i++;
|
||||
}
|
||||
|
||||
// Step 13.
|
||||
@ -1554,15 +1558,11 @@ function resolveCollatorInternals(lazyCollatorData) {
|
||||
// Steps 21-22.
|
||||
var s = lazyCollatorData.rawSensitivity;
|
||||
if (s === undefined) {
|
||||
if (collatorIsSorting) {
|
||||
// Step 21.a.
|
||||
s = "variant";
|
||||
} else {
|
||||
// Step 21.b.
|
||||
var dataLocale = r.dataLocale;
|
||||
var dataLocaleData = localeData(dataLocale);
|
||||
s = dataLocaleData.sensitivity;
|
||||
}
|
||||
// In theory the default sensitivity for the "search" collator is
|
||||
// locale dependent; in reality the CLDR/ICU default strength is
|
||||
// always tertiary. Therefore use "variant" as the default value for
|
||||
// both collation modes.
|
||||
s = "variant";
|
||||
}
|
||||
internalProps.sensitivity = s;
|
||||
|
||||
@ -1735,49 +1735,97 @@ var collatorInternalProperties = {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default caseFirst values for the given locale and usage. The
|
||||
* first element in the returned array denotes the default value per ES2017
|
||||
* Intl, 9.1 Internal slots of Service Constructors.
|
||||
* Returns the actual locale used when a collator for |locale| is constructed.
|
||||
*/
|
||||
function collatorCaseFirst(locale, usage) {
|
||||
function collatorActualLocale(locale) {
|
||||
assert(typeof locale === "string", "locale should be string");
|
||||
assert(usage === "sort" || usage === "search", "invalid usage option");
|
||||
|
||||
if (usage === "sort") {
|
||||
// If |locale| is the default locale (e.g. da-DK), but only supported
|
||||
// through a fallback (da), we need to get the actual locale before we
|
||||
// can call intl_isUpperCaseFirst. Also see BestAvailableLocaleHelper.
|
||||
var availableLocales = callFunction(collatorInternalProperties.availableLocales,
|
||||
collatorInternalProperties);
|
||||
var actualLocale = BestAvailableLocaleIgnoringDefault(availableLocales, locale);
|
||||
// If |locale| is the default locale (e.g. da-DK), but only supported
|
||||
// through a fallback (da), we need to get the actual locale before we
|
||||
// can call intl_isUpperCaseFirst. Also see BestAvailableLocaleHelper.
|
||||
var availableLocales = callFunction(collatorInternalProperties.availableLocales,
|
||||
collatorInternalProperties);
|
||||
return BestAvailableLocaleIgnoringDefault(availableLocales, locale);
|
||||
}
|
||||
|
||||
if (intl_isUpperCaseFirst(actualLocale))
|
||||
return ["upper", "false", "lower"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default caseFirst values for the given locale. The first
|
||||
* element in the returned array denotes the default value per ES2017 Intl,
|
||||
* 9.1 Internal slots of Service Constructors.
|
||||
*/
|
||||
function collatorSortCaseFirst(locale) {
|
||||
var actualLocale = collatorActualLocale(locale);
|
||||
if (intl_isUpperCaseFirst(actualLocale))
|
||||
return ["upper", "false", "lower"];
|
||||
|
||||
// Default caseFirst values for all other languages.
|
||||
return ["false", "lower", "upper"];
|
||||
}
|
||||
|
||||
|
||||
function collatorSortLocaleData(locale) {
|
||||
return {
|
||||
co: intl_availableCollations(locale),
|
||||
kn: ["false", "true"],
|
||||
kf: collatorCaseFirst(locale, "sort"),
|
||||
};
|
||||
/**
|
||||
* Returns the default caseFirst value for the given locale.
|
||||
*/
|
||||
function collatorSortCaseFirstDefault(locale) {
|
||||
var actualLocale = collatorActualLocale(locale);
|
||||
if (intl_isUpperCaseFirst(actualLocale))
|
||||
return "upper";
|
||||
|
||||
// Default caseFirst value for all other languages.
|
||||
return "false";
|
||||
}
|
||||
|
||||
|
||||
function collatorSearchLocaleData(locale) {
|
||||
function collatorSortLocaleData() {
|
||||
/* eslint-disable object-shorthand */
|
||||
return {
|
||||
co: [null],
|
||||
kn: ["false", "true"],
|
||||
kf: collatorCaseFirst(locale, "search"),
|
||||
// In theory the default sensitivity is locale dependent;
|
||||
// in reality the CLDR/ICU default strength is always tertiary.
|
||||
sensitivity: "variant"
|
||||
co: intl_availableCollations,
|
||||
kn: function() {
|
||||
return ["false", "true"];
|
||||
},
|
||||
kf: collatorSortCaseFirst,
|
||||
default: {
|
||||
co: function() {
|
||||
// The first element of the collations array must be |null|
|
||||
// per ES2017 Intl, 10.2.3 Internal Slots.
|
||||
return null;
|
||||
},
|
||||
kn: function() {
|
||||
return "false";
|
||||
},
|
||||
kf: collatorSortCaseFirstDefault,
|
||||
}
|
||||
};
|
||||
/* eslint-enable object-shorthand */
|
||||
}
|
||||
|
||||
|
||||
function collatorSearchLocaleData() {
|
||||
/* eslint-disable object-shorthand */
|
||||
return {
|
||||
co: function() {
|
||||
return [null];
|
||||
},
|
||||
kn: function() {
|
||||
return ["false", "true"];
|
||||
},
|
||||
kf: function() {
|
||||
return ["false", "lower", "upper"];
|
||||
},
|
||||
default: {
|
||||
co: function() {
|
||||
return null;
|
||||
},
|
||||
kn: function() {
|
||||
return "false";
|
||||
},
|
||||
kf: function() {
|
||||
return "false";
|
||||
},
|
||||
}
|
||||
};
|
||||
/* eslint-enable object-shorthand */
|
||||
}
|
||||
|
||||
|
||||
@ -2232,9 +2280,12 @@ function getNumberingSystems(locale) {
|
||||
}
|
||||
|
||||
|
||||
function numberFormatLocaleData(locale) {
|
||||
function numberFormatLocaleData() {
|
||||
return {
|
||||
nu: getNumberingSystems(locale)
|
||||
nu: getNumberingSystems,
|
||||
default: {
|
||||
nu: intl_numberingSystem,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -2926,10 +2977,14 @@ var dateTimeFormatInternalProperties = {
|
||||
};
|
||||
|
||||
|
||||
function dateTimeFormatLocaleData(locale) {
|
||||
function dateTimeFormatLocaleData() {
|
||||
return {
|
||||
ca: intl_availableCalendars(locale),
|
||||
nu: getNumberingSystems(locale)
|
||||
ca: intl_availableCalendars,
|
||||
nu: getNumberingSystems,
|
||||
default: {
|
||||
ca: intl_defaultCalendar,
|
||||
nu: intl_numberingSystem,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -3143,6 +3198,7 @@ function resolveICUPattern(pattern, result) {
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.3.3.
|
||||
*/
|
||||
var pluralRulesInternalProperties = {
|
||||
localeData: pluralRulesLocaleData,
|
||||
_availableLocales: null,
|
||||
availableLocales: function() // eslint-disable-line object-shorthand
|
||||
{
|
||||
@ -3153,9 +3209,17 @@ var pluralRulesInternalProperties = {
|
||||
locales = intl_PluralRules_availableLocales();
|
||||
addSpecialMissingLanguageTags(locales);
|
||||
return (this._availableLocales = locales);
|
||||
}
|
||||
},
|
||||
relevantExtensionKeys: [],
|
||||
};
|
||||
|
||||
|
||||
function pluralRulesLocaleData() {
|
||||
// PluralRules don't support any extension keys.
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute an internal properties object from |lazyPluralRulesData|.
|
||||
*/
|
||||
@ -3170,7 +3234,7 @@ function resolvePluralRulesInternals(lazyPluralRulesData) {
|
||||
const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules),
|
||||
lazyPluralRulesData.requestedLocales,
|
||||
lazyPluralRulesData.opt,
|
||||
noRelevantExtensionKeys, undefined);
|
||||
PluralRules.relevantExtensionKeys, PluralRules.localeData);
|
||||
|
||||
// Step 14.
|
||||
internalProps.locale = r.locale;
|
||||
|
@ -293,7 +293,7 @@ class TenuredCell : public Cell
|
||||
MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const;
|
||||
// The return value indicates if the cell went from unmarked to marked.
|
||||
MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const;
|
||||
MOZ_ALWAYS_INLINE void unmark(uint32_t color) const;
|
||||
MOZ_ALWAYS_INLINE void markBlack() const;
|
||||
MOZ_ALWAYS_INLINE void copyMarkBitsFrom(const TenuredCell* src);
|
||||
|
||||
// Access to the arena.
|
||||
@ -898,31 +898,42 @@ struct ChunkBitmap
|
||||
public:
|
||||
ChunkBitmap() { }
|
||||
|
||||
MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell* cell, uint32_t color,
|
||||
MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell* cell, ColorBit colorBit,
|
||||
uintptr_t** wordp, uintptr_t* maskp)
|
||||
{
|
||||
detail::GetGCThingMarkWordAndMask(uintptr_t(cell), color, wordp, maskp);
|
||||
detail::GetGCThingMarkWordAndMask(uintptr_t(cell), colorBit, wordp, maskp);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool markBit(const Cell* cell, ColorBit colorBit) {
|
||||
uintptr_t* word, mask;
|
||||
getMarkWordAndMask(cell, colorBit, &word, &mask);
|
||||
return *word & mask;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarked(const Cell* cell, uint32_t color) {
|
||||
uintptr_t* word, mask;
|
||||
getMarkWordAndMask(cell, color, &word, &mask);
|
||||
return *word & mask;
|
||||
if (color == BLACK) {
|
||||
return markBit(cell, ColorBit::BlackBit) ||
|
||||
markBit(cell, ColorBit::GrayOrBlackBit);
|
||||
} else {
|
||||
return !markBit(cell, ColorBit::BlackBit) &&
|
||||
markBit(cell, ColorBit::GrayOrBlackBit);
|
||||
}
|
||||
}
|
||||
|
||||
// The return value indicates if the cell went from unmarked to marked.
|
||||
MOZ_ALWAYS_INLINE bool markIfUnmarked(const Cell* cell, uint32_t color) {
|
||||
uintptr_t* word, mask;
|
||||
getMarkWordAndMask(cell, BLACK, &word, &mask);
|
||||
getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
|
||||
if (*word & mask)
|
||||
return false;
|
||||
*word |= mask;
|
||||
if (color != BLACK) {
|
||||
if (color == BLACK) {
|
||||
*word |= mask;
|
||||
} else {
|
||||
/*
|
||||
* We use getMarkWordAndMask to recalculate both mask and word as
|
||||
* doing just mask << color may overflow the mask.
|
||||
*/
|
||||
getMarkWordAndMask(cell, color, &word, &mask);
|
||||
getMarkWordAndMask(cell, ColorBit::GrayOrBlackBit, &word, &mask);
|
||||
if (*word & mask)
|
||||
return false;
|
||||
*word |= mask;
|
||||
@ -930,16 +941,22 @@ struct ChunkBitmap
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void unmark(const Cell* cell, uint32_t color) {
|
||||
MOZ_ALWAYS_INLINE void markBlack(const Cell* cell) {
|
||||
uintptr_t* word, mask;
|
||||
getMarkWordAndMask(cell, color, &word, &mask);
|
||||
*word &= ~mask;
|
||||
getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
|
||||
*word |= mask;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void copyMarkBit(Cell* dst, const TenuredCell* src, uint32_t color) {
|
||||
uintptr_t* word, mask;
|
||||
getMarkWordAndMask(dst, color, &word, &mask);
|
||||
*word = (*word & ~mask) | (src->isMarked(color) ? mask : 0);
|
||||
MOZ_ALWAYS_INLINE void copyMarkBit(Cell* dst, const TenuredCell* src, ColorBit colorBit) {
|
||||
uintptr_t* srcWord, srcMask;
|
||||
getMarkWordAndMask(src, colorBit, &srcWord, &srcMask);
|
||||
|
||||
uintptr_t* dstWord, dstMask;
|
||||
getMarkWordAndMask(dst, colorBit, &dstWord, &dstMask);
|
||||
|
||||
*dstWord &= ~dstMask;
|
||||
if (*srcWord & srcMask)
|
||||
*dstWord |= dstMask;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
@ -953,7 +970,7 @@ struct ChunkBitmap
|
||||
"that covers bits from two arenas.");
|
||||
|
||||
uintptr_t* word, unused;
|
||||
getMarkWordAndMask(reinterpret_cast<Cell*>(arena->address()), BLACK, &word, &unused);
|
||||
getMarkWordAndMask(reinterpret_cast<Cell*>(arena->address()), ColorBit::BlackBit, &word, &unused);
|
||||
return word;
|
||||
}
|
||||
};
|
||||
@ -1239,19 +1256,17 @@ TenuredCell::markIfUnmarked(uint32_t color /* = BLACK */) const
|
||||
}
|
||||
|
||||
void
|
||||
TenuredCell::unmark(uint32_t color) const
|
||||
TenuredCell::markBlack() const
|
||||
{
|
||||
MOZ_ASSERT(color != BLACK);
|
||||
AssertValidColor(this, color);
|
||||
chunk()->bitmap.unmark(this, color);
|
||||
chunk()->bitmap.markBlack(this);
|
||||
}
|
||||
|
||||
void
|
||||
TenuredCell::copyMarkBitsFrom(const TenuredCell* src)
|
||||
{
|
||||
ChunkBitmap& bitmap = chunk()->bitmap;
|
||||
bitmap.copyMarkBit(this, src, BLACK);
|
||||
bitmap.copyMarkBit(this, src, GRAY);
|
||||
bitmap.copyMarkBit(this, src, ColorBit::BlackBit);
|
||||
bitmap.copyMarkBit(this, src, ColorBit::GrayOrBlackBit);
|
||||
}
|
||||
|
||||
inline Arena*
|
||||
|
@ -3369,7 +3369,7 @@ UnmarkGrayTracer::onChild(const JS::GCCellPtr& thing)
|
||||
if (!tenured.isMarked(GRAY))
|
||||
return;
|
||||
|
||||
tenured.unmark(GRAY);
|
||||
tenured.markBlack();
|
||||
unmarkedAny = true;
|
||||
|
||||
if (!stack.append(thing))
|
||||
@ -3459,19 +3459,22 @@ GetMarkWordAddress(Cell* cell)
|
||||
|
||||
uintptr_t* wordp;
|
||||
uintptr_t mask;
|
||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::BLACK, &wordp, &mask);
|
||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), ColorBit::BlackBit, &wordp, &mask);
|
||||
return wordp;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
GetMarkMask(Cell* cell, uint32_t color)
|
||||
{
|
||||
MOZ_ASSERT(color == 0 || color == 1);
|
||||
|
||||
if (!cell->isTenured())
|
||||
return 0;
|
||||
|
||||
ColorBit bit = color == 0 ? ColorBit::BlackBit : ColorBit::GrayOrBlackBit;
|
||||
uintptr_t* wordp;
|
||||
uintptr_t mask;
|
||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), color, &wordp, &mask);
|
||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), bit, &wordp, &mask);
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
@ -4224,6 +4224,21 @@ JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::DecodeMultiOffThreadScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
TranscodeSources& sources,
|
||||
OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
size_t length = 0;
|
||||
for (auto& source : sources) {
|
||||
length += source.range.length();
|
||||
}
|
||||
MOZ_ASSERT(CanCompileOffThread(cx, options, length));
|
||||
#endif
|
||||
return StartOffThreadDecodeMultiScripts(cx, options, sources, callback, callbackData);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSScript*)
|
||||
JS::FinishOffThreadScriptDecoder(JSContext* cx, void* token)
|
||||
{
|
||||
@ -4240,6 +4255,22 @@ JS::CancelOffThreadScriptDecoder(JSContext* cx, void* token)
|
||||
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::ScriptDecode, token);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::FinishMultiOffThreadScriptsDecoder(JSContext* cx, void* token, MutableHandle<ScriptVector> scripts)
|
||||
{
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
return HelperThreadState().finishMultiScriptsDecodeTask(cx, token, scripts);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::CancelMultiOffThreadScriptsDecoder(JSContext* cx, void* token)
|
||||
{
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::MultiScriptsDecode, token);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
|
||||
const JS::CompileOptions& options, MutableHandleScript script)
|
||||
|
@ -841,6 +841,8 @@ class MOZ_STACK_CLASS SourceBufferHolder final
|
||||
bool ownsChars_;
|
||||
};
|
||||
|
||||
struct TranscodeSource;
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
/************************************************************************/
|
||||
@ -4301,6 +4303,17 @@ FinishOffThreadScriptDecoder(JSContext* cx, void* token);
|
||||
extern JS_PUBLIC_API(void)
|
||||
CancelOffThreadScriptDecoder(JSContext* cx, void* token);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
DecodeMultiOffThreadScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
mozilla::Vector<TranscodeSource>& sources,
|
||||
OffThreadCompileCallback callback, void* callbackData);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
FinishMultiOffThreadScriptsDecoder(JSContext* cx, void* token, JS::MutableHandle<JS::ScriptVector> scripts);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
CancelMultiOffThreadScriptsDecoder(JSContext* cx, void* token);
|
||||
|
||||
/**
|
||||
* Compile a function with envChain plus the global as its scope chain.
|
||||
* envChain must contain objects in the current compartment of cx. The actual
|
||||
@ -6187,6 +6200,19 @@ class MOZ_RAII AutoHideScriptedCaller
|
||||
typedef mozilla::Vector<uint8_t> TranscodeBuffer;
|
||||
typedef mozilla::Range<uint8_t> TranscodeRange;
|
||||
|
||||
struct TranscodeSource
|
||||
{
|
||||
TranscodeSource(const TranscodeRange& range_, const char* file, uint32_t line)
|
||||
: range(range_), filename(file), lineno(line)
|
||||
{}
|
||||
|
||||
const TranscodeRange range;
|
||||
const char* filename;
|
||||
const uint32_t lineno;
|
||||
};
|
||||
|
||||
typedef mozilla::Vector<JS::TranscodeSource> TranscodeSources;
|
||||
|
||||
enum TranscodeResult
|
||||
{
|
||||
// Successful encoding / decoding.
|
||||
|
@ -300,27 +300,43 @@ static const JSClass parseTaskGlobalClass = {
|
||||
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: kind(kind), options(cx),
|
||||
: kind(kind), options(cx), data(AsVariant(TwoByteChars(chars, length))),
|
||||
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
parseGlobal(parseGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
script(nullptr), sourceObject(nullptr),
|
||||
scripts(cx), sourceObjects(cx),
|
||||
overRecursed(false), outOfMemory(false)
|
||||
{
|
||||
data.construct<TwoByteChars>(chars, length);
|
||||
MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
|
||||
MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
|
||||
}
|
||||
|
||||
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
const JS::TranscodeRange& range,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: kind(kind), options(cx),
|
||||
: kind(kind), options(cx), data(AsVariant(range)),
|
||||
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
parseGlobal(parseGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
script(nullptr), sourceObject(nullptr),
|
||||
scripts(cx), sourceObjects(cx),
|
||||
overRecursed(false), outOfMemory(false)
|
||||
{
|
||||
data.construct<const JS::TranscodeRange>(range);
|
||||
MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
|
||||
MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
|
||||
}
|
||||
|
||||
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
JS::TranscodeSources& sources,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: kind(kind), options(cx), data(AsVariant(&sources)),
|
||||
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
parseGlobal(parseGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
scripts(cx), sourceObjects(cx),
|
||||
overRecursed(false), outOfMemory(false)
|
||||
{
|
||||
MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
|
||||
MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -341,7 +357,7 @@ ParseTask::activate(JSRuntime* rt)
|
||||
bool
|
||||
ParseTask::finish(JSContext* cx)
|
||||
{
|
||||
if (sourceObject) {
|
||||
for (auto& sourceObject : sourceObjects) {
|
||||
RootedScriptSource sso(cx, sourceObject);
|
||||
if (!ScriptSourceObject::initFromOptions(cx, sso, options))
|
||||
return false;
|
||||
@ -370,10 +386,8 @@ ParseTask::trace(JSTracer* trc)
|
||||
}
|
||||
|
||||
TraceManuallyBarrieredEdge(trc, &parseGlobal, "ParseTask::parseGlobal");
|
||||
if (script)
|
||||
TraceManuallyBarrieredEdge(trc, &script, "ParseTask::script");
|
||||
if (sourceObject)
|
||||
TraceManuallyBarrieredEdge(trc, &sourceObject, "ParseTask::sourceObject");
|
||||
scripts.trace(trc);
|
||||
sourceObjects.trace(trc);
|
||||
}
|
||||
|
||||
ScriptParseTask::ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
|
||||
@ -387,11 +401,17 @@ ScriptParseTask::ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
|
||||
void
|
||||
ScriptParseTask::parse(JSContext* cx)
|
||||
{
|
||||
auto& range = data.ref<TwoByteChars>();
|
||||
auto& range = data.as<TwoByteChars>();
|
||||
SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
|
||||
script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
|
||||
options, srcBuf,
|
||||
/* sourceObjectOut = */ &sourceObject);
|
||||
Rooted<ScriptSourceObject*> sourceObject(cx);
|
||||
|
||||
JSScript* script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
|
||||
options, srcBuf,
|
||||
/* sourceObjectOut = */ &sourceObject.get());
|
||||
if (script)
|
||||
scripts.infallibleAppend(script);
|
||||
if (sourceObject)
|
||||
sourceObjects.infallibleAppend(sourceObject);
|
||||
}
|
||||
|
||||
ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
|
||||
@ -405,11 +425,16 @@ ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
|
||||
void
|
||||
ModuleParseTask::parse(JSContext* cx)
|
||||
{
|
||||
auto& range = data.ref<TwoByteChars>();
|
||||
auto& range = data.as<TwoByteChars>();
|
||||
SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
|
||||
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
|
||||
if (module)
|
||||
script = module->script();
|
||||
Rooted<ScriptSourceObject*> sourceObject(cx);
|
||||
|
||||
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject.get());
|
||||
if (module) {
|
||||
scripts.infallibleAppend(module->script());
|
||||
if (sourceObject)
|
||||
sourceObjects.infallibleAppend(sourceObject);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
|
||||
@ -424,14 +449,54 @@ void
|
||||
ScriptDecodeTask::parse(JSContext* cx)
|
||||
{
|
||||
RootedScript resultScript(cx);
|
||||
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
|
||||
data.ref<const JS::TranscodeRange>());
|
||||
Rooted<ScriptSourceObject*> sourceObject(cx);
|
||||
|
||||
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject.get(),
|
||||
data.as<const JS::TranscodeRange>());
|
||||
decoder.codeScript(&resultScript);
|
||||
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
|
||||
if (decoder.resultCode() == JS::TranscodeResult_Ok) {
|
||||
script = resultScript.get();
|
||||
} else {
|
||||
sourceObject = nullptr;
|
||||
scripts.infallibleAppend(resultScript);
|
||||
if (sourceObject)
|
||||
sourceObjects.infallibleAppend(sourceObject);
|
||||
}
|
||||
}
|
||||
|
||||
MultiScriptsDecodeTask::MultiScriptsDecodeTask(JSContext* cx, JSObject* parseGlobal,
|
||||
JS::TranscodeSources& sources,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: ParseTask(ParseTaskKind::MultiScriptsDecode, cx, parseGlobal,
|
||||
sources, callback, callbackData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MultiScriptsDecodeTask::parse(JSContext* cx)
|
||||
{
|
||||
auto sources = data.as<JS::TranscodeSources*>();
|
||||
|
||||
if (!scripts.reserve(sources->length()) ||
|
||||
!sourceObjects.reserve(sources->length()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& source : *sources) {
|
||||
CompileOptions opts(cx, options);
|
||||
opts.setFileAndLine(source.filename, source.lineno);
|
||||
|
||||
RootedScript resultScript(cx);
|
||||
Rooted<ScriptSourceObject*> sourceObject(cx);
|
||||
|
||||
XDROffThreadDecoder decoder(cx, alloc, &opts, &sourceObject.get(), source.range);
|
||||
decoder.codeScript(&resultScript);
|
||||
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
|
||||
|
||||
if (decoder.resultCode() != JS::TranscodeResult_Ok)
|
||||
break;
|
||||
MOZ_ASSERT(resultScript);
|
||||
scripts.infallibleAppend(resultScript);
|
||||
sourceObjects.infallibleAppend(sourceObject);
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,6 +731,17 @@ js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& opti
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadDecodeMultiScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
JS::TranscodeSources& sources,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
auto functor = [&](JSObject* global) -> MultiScriptsDecodeTask* {
|
||||
return cx->new_<MultiScriptsDecodeTask>(cx, global, sources, callback, callbackData);
|
||||
};
|
||||
return StartOffThreadParseTask(cx, options, ParseTaskKind::MultiScriptsDecode, functor);
|
||||
}
|
||||
|
||||
void
|
||||
js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
|
||||
{
|
||||
@ -1300,8 +1376,9 @@ GlobalHelperThreadState::removeFinishedParseTask(ParseTaskKind kind, void* token
|
||||
MOZ_CRASH("Invalid ParseTask token");
|
||||
}
|
||||
|
||||
JSScript*
|
||||
GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token)
|
||||
template <typename F, typename>
|
||||
bool
|
||||
GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, F&& finishCallback)
|
||||
{
|
||||
MOZ_ASSERT(cx->compartment());
|
||||
|
||||
@ -1312,21 +1389,23 @@ GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void
|
||||
Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
|
||||
if (!EnsureParserCreatedClasses(cx, kind)) {
|
||||
LeaveParseTaskZone(cx->runtime(), parseTask);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
mergeParseTaskCompartment(cx, parseTask, global, cx->compartment());
|
||||
|
||||
RootedScript script(cx, parseTask->script);
|
||||
releaseAssertSameCompartment(cx, script);
|
||||
bool ok = finishCallback(parseTask);
|
||||
|
||||
if (!parseTask->finish(cx))
|
||||
return nullptr;
|
||||
for (auto& script : parseTask->scripts)
|
||||
releaseAssertSameCompartment(cx, script);
|
||||
|
||||
if (!parseTask->finish(cx) || !ok)
|
||||
return false;
|
||||
|
||||
// Report out of memory errors eagerly, or errors could be malformed.
|
||||
if (parseTask->outOfMemory) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Report any error or warnings generated during the parse, and inform the
|
||||
@ -1336,6 +1415,26 @@ GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void
|
||||
if (parseTask->overRecursed)
|
||||
ReportOverRecursed(cx);
|
||||
if (cx->isExceptionPending())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSScript*
|
||||
GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token)
|
||||
{
|
||||
JS::RootedScript script(cx);
|
||||
|
||||
bool ok = finishParseTask(cx, kind, token, [&script] (ParseTask* parseTask) {
|
||||
MOZ_RELEASE_ASSERT(parseTask->scripts.length() <= 1);
|
||||
|
||||
if (parseTask->scripts.length() > 0)
|
||||
script = parseTask->scripts[0];
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!ok)
|
||||
return nullptr;
|
||||
|
||||
if (!script) {
|
||||
@ -1351,6 +1450,45 @@ GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void
|
||||
return script;
|
||||
}
|
||||
|
||||
bool
|
||||
GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token,
|
||||
MutableHandle<ScriptVector> scripts)
|
||||
{
|
||||
size_t expectedLength = 0;
|
||||
|
||||
bool ok = finishParseTask(cx, kind, token, [&scripts, &expectedLength] (ParseTask* parseTask) {
|
||||
expectedLength = parseTask->data.as<JS::TranscodeSources*>()->length();
|
||||
|
||||
if (!scripts.reserve(parseTask->scripts.length()))
|
||||
return false;
|
||||
|
||||
for (auto& script : parseTask->scripts)
|
||||
scripts.infallibleAppend(script);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
if (scripts.length() != expectedLength) {
|
||||
// No error was reported, but fewer scripts produced than expected.
|
||||
// Assume we hit out of memory.
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The Debugger only needs to be told about the topmost script that was compiled.
|
||||
JS::RootedScript rooted(cx);
|
||||
for (auto& script : scripts) {
|
||||
MOZ_ASSERT(script->isGlobalCode());
|
||||
|
||||
rooted = script;
|
||||
Debugger::onNewScript(cx, rooted);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSScript*
|
||||
GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
|
||||
{
|
||||
@ -1367,6 +1505,12 @@ GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, void* token)
|
||||
return script;
|
||||
}
|
||||
|
||||
bool
|
||||
GlobalHelperThreadState::finishMultiScriptsDecodeTask(JSContext* cx, void* token, MutableHandle<ScriptVector> scripts)
|
||||
{
|
||||
return finishParseTask(cx, ParseTaskKind::MultiScriptsDecode, token, scripts);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
|
||||
{
|
||||
|
@ -15,9 +15,9 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/GuardObjects.h"
|
||||
#include "mozilla/MaybeOneOf.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "mozilla/Variant.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
@ -53,7 +53,8 @@ enum class ParseTaskKind
|
||||
{
|
||||
Script,
|
||||
Module,
|
||||
ScriptDecode
|
||||
ScriptDecode,
|
||||
MultiScriptsDecode
|
||||
};
|
||||
|
||||
// Per-process state for off thread work items.
|
||||
@ -262,7 +263,19 @@ class GlobalHelperThreadState
|
||||
return bool(numWasmFailedJobs);
|
||||
}
|
||||
|
||||
template <
|
||||
typename F,
|
||||
typename = typename mozilla::EnableIf<
|
||||
// Matches when the type is a function or lambda with the signature `bool(ParseTask*)`
|
||||
mozilla::IsSame<bool, decltype((*(F*)nullptr)((ParseTask*)nullptr))>::value
|
||||
>::Type
|
||||
>
|
||||
bool finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, F&& finishCallback);
|
||||
|
||||
JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, void* token);
|
||||
|
||||
bool finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, MutableHandle<ScriptVector> scripts);
|
||||
|
||||
void cancelParseTask(JSRuntime* rt, ParseTaskKind kind, void* token);
|
||||
|
||||
void mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
|
||||
@ -286,6 +299,7 @@ class GlobalHelperThreadState
|
||||
public:
|
||||
JSScript* finishScriptParseTask(JSContext* cx, void* token);
|
||||
JSScript* finishScriptDecodeTask(JSContext* cx, void* token);
|
||||
bool finishMultiScriptsDecodeTask(JSContext* cx, void* token, MutableHandle<ScriptVector> scripts);
|
||||
JSObject* finishModuleParseTask(JSContext* cx, void* token);
|
||||
|
||||
bool hasActiveThreads(const AutoLockHelperThreadState&);
|
||||
@ -538,6 +552,11 @@ StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const JS::TranscodeRange& range,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
|
||||
bool
|
||||
StartOffThreadDecodeMultiScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
JS::TranscodeSources& sources,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
|
||||
/*
|
||||
* Called at the end of GC to enqueue any Parse tasks that were waiting on an
|
||||
* atoms-zone GC to finish.
|
||||
@ -595,7 +614,9 @@ struct ParseTask
|
||||
ParseTaskKind kind;
|
||||
OwningCompileOptions options;
|
||||
|
||||
mozilla::MaybeOneOf<const JS::TranscodeRange, JS::TwoByteChars> data;
|
||||
mozilla::Variant<const JS::TranscodeRange,
|
||||
JS::TwoByteChars,
|
||||
JS::TranscodeSources*> data;
|
||||
|
||||
LifoAlloc alloc;
|
||||
|
||||
@ -606,13 +627,13 @@ struct ParseTask
|
||||
JS::OffThreadCompileCallback callback;
|
||||
void* callbackData;
|
||||
|
||||
// Holds the final script between the invocation of the callback and the
|
||||
// Holds the final scripts between the invocation of the callback and the
|
||||
// point where FinishOffThreadScript is called, which will destroy the
|
||||
// ParseTask.
|
||||
JSScript* script;
|
||||
GCVector<JSScript*, 1> scripts;
|
||||
|
||||
// Holds the ScriptSourceObject generated for the script compilation.
|
||||
ScriptSourceObject* sourceObject;
|
||||
// Holds the ScriptSourceObjects generated for the script compilation.
|
||||
GCVector<ScriptSourceObject*, 1> sourceObjects;
|
||||
|
||||
// Any errors or warnings produced during compilation. These are reported
|
||||
// when finishing the script.
|
||||
@ -626,6 +647,9 @@ struct ParseTask
|
||||
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
const JS::TranscodeRange& range,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
|
||||
JS::TranscodeSources& sources,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
|
||||
|
||||
void activate(JSRuntime* rt);
|
||||
@ -665,6 +689,14 @@ struct ScriptDecodeTask : public ParseTask
|
||||
void parse(JSContext* cx) override;
|
||||
};
|
||||
|
||||
struct MultiScriptsDecodeTask : public ParseTask
|
||||
{
|
||||
MultiScriptsDecodeTask(JSContext* cx, JSObject* parseGlobal,
|
||||
JS::TranscodeSources& sources,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
void parse(JSContext* cx) override;
|
||||
};
|
||||
|
||||
// Return whether, if a new parse task was started, it would need to wait for
|
||||
// an in-progress GC to complete before starting.
|
||||
extern bool
|
||||
|
@ -2553,6 +2553,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0),
|
||||
JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2,0),
|
||||
JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0),
|
||||
JS_FN("intl_defaultCalendar", intl_defaultCalendar, 1,0),
|
||||
JS_FN("intl_defaultTimeZone", intl_defaultTimeZone, 0,0),
|
||||
JS_FN("intl_defaultTimeZoneOffset", intl_defaultTimeZoneOffset, 0,0),
|
||||
JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),
|
||||
|
@ -269,19 +269,16 @@ ScriptPreloader::Cleanup()
|
||||
void
|
||||
ScriptPreloader::InvalidateCache()
|
||||
{
|
||||
mMonitor.AssertNotCurrentThreadOwns();
|
||||
MonitorAutoLock mal(mMonitor);
|
||||
|
||||
mCacheInvalidated = true;
|
||||
|
||||
for (auto& script : IterHash(mScripts)) {
|
||||
// We can only purge finished scripts here. Async scripts that are
|
||||
// still being parsed off-thread have a non-refcounted reference to
|
||||
// this script, which needs to stay alive until they finish parsing.
|
||||
if (script->mReadyToExecute) {
|
||||
script->Cancel();
|
||||
script.Remove();
|
||||
}
|
||||
}
|
||||
mParsingScripts.clearAndFree();
|
||||
while (auto script = mPendingScripts.getFirst())
|
||||
script->remove();
|
||||
for (auto& script : IterHash(mScripts))
|
||||
script.Remove();
|
||||
|
||||
// If we've already finished saving the cache at this point, start a new
|
||||
// delayed save operation. This will write out an empty cache file in place
|
||||
@ -437,13 +434,13 @@ ScriptPreloader::InitCacheInternal()
|
||||
return Err(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
AutoTArray<CachedScript*, 256> scripts;
|
||||
|
||||
{
|
||||
auto cleanup = MakeScopeExit([&] () {
|
||||
mScripts.Clear();
|
||||
});
|
||||
|
||||
LinkedList<CachedScript> scripts;
|
||||
|
||||
Range<uint8_t> header(data, data + headerSize);
|
||||
data += headerSize;
|
||||
|
||||
@ -468,7 +465,14 @@ ScriptPreloader::InitCacheInternal()
|
||||
|
||||
script->mXDRRange.emplace(scriptData, scriptData + script->mSize);
|
||||
|
||||
scripts.AppendElement(script.get());
|
||||
// Don't pre-decode the script unless it was used in this process type during the
|
||||
// previous session.
|
||||
if (script->mOriginalProcessTypes.contains(CurrentProcessType())) {
|
||||
scripts.insertBack(script.get());
|
||||
} else {
|
||||
script->mReadyToExecute = true;
|
||||
}
|
||||
|
||||
mScripts.Put(script->mCachePath, script.get());
|
||||
Unused << script.release();
|
||||
}
|
||||
@ -477,32 +481,11 @@ ScriptPreloader::InitCacheInternal()
|
||||
return Err(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
mPendingScripts = Move(scripts);
|
||||
cleanup.release();
|
||||
}
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
auto start = TimeStamp::Now();
|
||||
LOG(Info, "Off-thread decoding scripts...\n");
|
||||
|
||||
JS::CompileOptions options(cx, JSVERSION_LATEST);
|
||||
|
||||
for (auto& script : scripts) {
|
||||
// Only async decode scripts which have been used in this process type.
|
||||
if (script->mProcessTypes.contains(CurrentProcessType()) &&
|
||||
script->AsyncDecodable() &&
|
||||
JS::CanCompileOffThread(cx, options, script->mSize)) {
|
||||
DecodeScriptOffThread(cx, script);
|
||||
} else {
|
||||
script->mReadyToExecute = true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(Info, "Initialized decoding in %fms\n",
|
||||
(TimeStamp::Now() - start).ToMilliseconds());
|
||||
|
||||
DecodeNextBatch(OFF_THREAD_FIRST_CHUNK_SIZE);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
@ -530,39 +513,37 @@ ScriptPreloader::PrepareCacheWrite()
|
||||
return;
|
||||
}
|
||||
|
||||
bool found = Find(IterHash(mScripts), [] (CachedScript* script) {
|
||||
return (script->mStatus == ScriptStatus::Restored ||
|
||||
!script->HasRange() || script->HasArray());
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
mSaveComplete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
AutoSafeJSAPI jsapi;
|
||||
|
||||
bool found = false;
|
||||
for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
|
||||
// Don't write any scripts that are also in the child cache. They'll be
|
||||
// loaded from the child cache in that case, so there's no need to write
|
||||
// them twice.
|
||||
CachedScript* childScript = mChildCache ? mChildCache->mScripts.Get(script->mCachePath) : nullptr;
|
||||
if (childScript) {
|
||||
if (childScript->mStatus == ScriptStatus::Saved) {
|
||||
childScript->UpdateLoadTime(script->mLoadTime);
|
||||
childScript->mProcessTypes += script->mProcessTypes;
|
||||
} else {
|
||||
childScript = nullptr;
|
||||
}
|
||||
if (childScript && !childScript->mProcessTypes.isEmpty()) {
|
||||
childScript->UpdateLoadTime(script->mLoadTime);
|
||||
childScript->mProcessTypes += script->mProcessTypes;
|
||||
script.Remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (childScript || (!script->mSize && !script->XDREncode(jsapi.cx()))) {
|
||||
if (!(script->mProcessTypes == script->mOriginalProcessTypes)) {
|
||||
// Note: EnumSet doesn't support operator!=, hence the weird form above.
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!script->mSize && !script->XDREncode(jsapi.cx())) {
|
||||
script.Remove();
|
||||
} else {
|
||||
script->mSize = script->Range().length();
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
mSaveComplete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mDataPrepared = true;
|
||||
}
|
||||
|
||||
@ -693,9 +674,7 @@ ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
|
||||
auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, jsscript);
|
||||
|
||||
if (script->mStatus == ScriptStatus::Restored) {
|
||||
script->mStatus = ScriptStatus::Saved;
|
||||
|
||||
if (!script->mScript) {
|
||||
MOZ_ASSERT(jsscript);
|
||||
script->mScript = jsscript;
|
||||
script->mReadyToExecute = true;
|
||||
@ -712,20 +691,14 @@ ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
{
|
||||
auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, nullptr);
|
||||
|
||||
if (script->mStatus == ScriptStatus::Restored) {
|
||||
script->mStatus = ScriptStatus::Saved;
|
||||
if (!script->HasRange()) {
|
||||
MOZ_ASSERT(!script->HasArray());
|
||||
|
||||
script->mReadyToExecute = true;
|
||||
} else {
|
||||
if (!script->HasRange()) {
|
||||
MOZ_ASSERT(!script->HasArray());
|
||||
script->mSize = xdrData.Length();
|
||||
script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
|
||||
|
||||
script->mSize = xdrData.Length();
|
||||
script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
|
||||
|
||||
auto& data = script->Array();
|
||||
script->mXDRRange.emplace(data.Elements(), data.Length());
|
||||
}
|
||||
auto& data = script->Array();
|
||||
script->mXDRRange.emplace(data.Elements(), data.Length());
|
||||
}
|
||||
|
||||
if (!script->mSize && !script->mScript) {
|
||||
@ -770,12 +743,25 @@ ScriptPreloader::GetCachedScript(JSContext* cx, const nsCString& path)
|
||||
JSScript*
|
||||
ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
|
||||
{
|
||||
// Check for finished operations before locking so that we can move onto
|
||||
// decoding the next batch as soon as possible after the pending batch is
|
||||
// ready. If we wait until we hit an unfinished script, we wind up having at
|
||||
// most one batch of buffered scripts, and occasionally under-running that
|
||||
// buffer.
|
||||
FinishOffThreadDecode();
|
||||
|
||||
if (!script->mReadyToExecute) {
|
||||
LOG(Info, "Must wait for async script load: %s\n", script->mURL.get());
|
||||
auto start = TimeStamp::Now();
|
||||
|
||||
mMonitor.AssertNotCurrentThreadOwns();
|
||||
MonitorAutoLock mal(mMonitor);
|
||||
|
||||
// Check for finished operations again *after* locking, or we may race
|
||||
// against mToken being set between our last check and the time we
|
||||
// entered the mutex.
|
||||
FinishOffThreadDecode();
|
||||
|
||||
if (!script->mReadyToExecute && script->mSize < MAX_MAINTHREAD_DECODE_SIZE) {
|
||||
LOG(Info, "Script is small enough to recompile on main thread\n");
|
||||
|
||||
@ -783,68 +769,178 @@ ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
|
||||
} else {
|
||||
while (!script->mReadyToExecute) {
|
||||
mal.Wait();
|
||||
|
||||
MonitorAutoUnlock mau(mMonitor);
|
||||
FinishOffThreadDecode();
|
||||
}
|
||||
}
|
||||
|
||||
LOG(Info, "Waited %fms\n", (TimeStamp::Now() - start).ToMilliseconds());
|
||||
LOG(Debug, "Waited %fms\n", (TimeStamp::Now() - start).ToMilliseconds());
|
||||
}
|
||||
|
||||
return script->GetJSScript(cx);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScriptPreloader::DecodeScriptOffThread(JSContext* cx, CachedScript* script)
|
||||
|
||||
/* static */ void
|
||||
ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
|
||||
{
|
||||
JS::CompileOptions options(cx, JSVERSION_LATEST);
|
||||
auto cache = static_cast<ScriptPreloader*>(context);
|
||||
|
||||
options.setNoScriptRval(true)
|
||||
.setFileAndLine(script->mURL.get(), 1);
|
||||
cache->mMonitor.AssertNotCurrentThreadOwns();
|
||||
MonitorAutoLock mal(cache->mMonitor);
|
||||
|
||||
if (!JS::DecodeOffThreadScript(cx, options, script->Range(),
|
||||
OffThreadDecodeCallback,
|
||||
static_cast<void*>(script))) {
|
||||
// First notify any tasks that are already waiting on scripts, since they'll
|
||||
// be blocking the main thread, and prevent any runnables from executing.
|
||||
cache->mToken = token;
|
||||
mal.NotifyAll();
|
||||
|
||||
// If nothing processed the token, and we don't already have a pending
|
||||
// runnable, then dispatch a new one to finish the processing on the main
|
||||
// thread as soon as possible.
|
||||
if (cache->mToken && !cache->mFinishDecodeRunnablePending) {
|
||||
cache->mFinishDecodeRunnablePending = true;
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod(cache, &ScriptPreloader::DoFinishOffThreadDecode));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::DoFinishOffThreadDecode()
|
||||
{
|
||||
mFinishDecodeRunnablePending = false;
|
||||
FinishOffThreadDecode();
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::FinishOffThreadDecode()
|
||||
{
|
||||
if (!mToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cleanup = MakeScopeExit([&] () {
|
||||
mToken = nullptr;
|
||||
mParsingSources.clear();
|
||||
mParsingScripts.clear();
|
||||
|
||||
DecodeNextBatch(OFF_THREAD_CHUNK_SIZE);
|
||||
});
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JS::ScriptVector> jsScripts(cx, JS::ScriptVector(cx));
|
||||
|
||||
// If this fails, we still need to mark the scripts as finished. Any that
|
||||
// weren't successfully compiled in this operation (which should never
|
||||
// happen under ordinary circumstances) will be re-decoded on the main
|
||||
// thread, and raise the appropriate errors when they're executed.
|
||||
//
|
||||
// The exception from the off-thread decode operation will be reported when
|
||||
// we pop the AutoJSAPI off the stack.
|
||||
Unused << JS::FinishMultiOffThreadScriptsDecoder(cx, mToken, &jsScripts);
|
||||
|
||||
unsigned i = 0;
|
||||
for (auto script : mParsingScripts) {
|
||||
LOG(Debug, "Finished off-thread decode of %s\n", script->mURL.get());
|
||||
if (i < jsScripts.length())
|
||||
script->mScript = jsScripts[i++];
|
||||
script->mReadyToExecute = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::CancelOffThreadParse(void* token)
|
||||
ScriptPreloader::DecodeNextBatch(size_t chunkSize)
|
||||
{
|
||||
AutoSafeJSAPI jsapi;
|
||||
JS::CancelOffThreadScriptDecoder(jsapi.cx(), token);
|
||||
}
|
||||
MOZ_ASSERT(mParsingSources.length() == 0);
|
||||
MOZ_ASSERT(mParsingScripts.length() == 0);
|
||||
|
||||
/* static */ void
|
||||
ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
|
||||
{
|
||||
auto script = static_cast<CachedScript*>(context);
|
||||
auto cleanup = MakeScopeExit([&] () {
|
||||
mParsingScripts.clearAndFree();
|
||||
mParsingSources.clearAndFree();
|
||||
});
|
||||
|
||||
MonitorAutoLock mal(script->mCache.mMonitor);
|
||||
auto start = TimeStamp::Now();
|
||||
LOG(Debug, "Off-thread decoding scripts...\n");
|
||||
|
||||
if (script->mReadyToExecute) {
|
||||
// We've already executed this script on the main thread, and opted to
|
||||
// main thread decode it rather waiting for off-thread decoding to
|
||||
// finish. So just cancel the off-thread parse rather than completing
|
||||
// it.
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod<void*>(&script->mCache,
|
||||
&ScriptPreloader::CancelOffThreadParse,
|
||||
token));
|
||||
size_t size = 0;
|
||||
for (CachedScript* next = mPendingScripts.getFirst(); next;) {
|
||||
auto script = next;
|
||||
next = script->getNext();
|
||||
|
||||
// Skip any scripts that we decoded on the main thread rather than
|
||||
// waiting for an off-thread operation to complete.
|
||||
if (script->mReadyToExecute) {
|
||||
script->remove();
|
||||
continue;
|
||||
}
|
||||
// If we have enough data for one chunk and this script would put us
|
||||
// over our chunk size limit, we're done.
|
||||
if (size > SMALL_SCRIPT_CHUNK_THRESHOLD &&
|
||||
size + script->mSize > chunkSize) {
|
||||
break;
|
||||
}
|
||||
if (!mParsingScripts.append(script) ||
|
||||
!mParsingSources.emplaceBack(script->Range(), script->mURL.get(), 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(Debug, "Beginning off-thread decode of script %s (%u bytes)\n",
|
||||
script->mURL.get(), script->mSize);
|
||||
|
||||
script->remove();
|
||||
size += script->mSize;
|
||||
}
|
||||
|
||||
if (size == 0 && mPendingScripts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
script->mToken = token;
|
||||
script->mReadyToExecute = true;
|
||||
AutoJSAPI jsapi;
|
||||
MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
mal.NotifyAll();
|
||||
JS::CompileOptions options(cx, JSVERSION_LATEST);
|
||||
options.setNoScriptRval(true);
|
||||
|
||||
if (!JS::CanCompileOffThread(cx, options, size) ||
|
||||
!JS::DecodeMultiOffThreadScripts(cx, options, mParsingSources,
|
||||
OffThreadDecodeCallback,
|
||||
static_cast<void*>(this))) {
|
||||
// If we fail here, we don't move on to process the next batch, so make
|
||||
// sure we don't have any other scripts left to process.
|
||||
MOZ_ASSERT(mPendingScripts.isEmpty());
|
||||
for (auto script : mPendingScripts) {
|
||||
script->mReadyToExecute = true;
|
||||
}
|
||||
|
||||
LOG(Info, "Can't decode %lu bytes of scripts off-thread", (unsigned long)size);
|
||||
for (auto script : mParsingScripts) {
|
||||
script->mReadyToExecute = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup.release();
|
||||
|
||||
LOG(Debug, "Initialized decoding of %u scripts (%u bytes) in %fms\n",
|
||||
(unsigned)mParsingSources.length(), (unsigned)size, (TimeStamp::Now() - start).ToMilliseconds());
|
||||
}
|
||||
|
||||
|
||||
ScriptPreloader::CachedScript::CachedScript(ScriptPreloader& cache, InputBuffer& buf)
|
||||
: mCache(cache)
|
||||
, mStatus(ScriptStatus::Restored)
|
||||
{
|
||||
Code(buf);
|
||||
|
||||
// Swap the mProcessTypes and mOriginalProcessTypes values, since we want to
|
||||
// start with an empty set of processes loaded into for this session, and
|
||||
// compare against last session's values later.
|
||||
mOriginalProcessTypes = mProcessTypes;
|
||||
mProcessTypes = {};
|
||||
}
|
||||
|
||||
bool
|
||||
@ -864,20 +960,6 @@ ScriptPreloader::CachedScript::XDREncode(JSContext* cx)
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::CachedScript::Cancel()
|
||||
{
|
||||
if (mToken) {
|
||||
mCache.mMonitor.AssertCurrentThreadOwns();
|
||||
|
||||
AutoSafeJSAPI jsapi;
|
||||
JS::CancelOffThreadScriptDecoder(jsapi.cx(), mToken);
|
||||
|
||||
mReadyToExecute = true;
|
||||
mToken = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JSScript*
|
||||
ScriptPreloader::CachedScript::GetJSScript(JSContext* cx)
|
||||
{
|
||||
@ -886,35 +968,27 @@ ScriptPreloader::CachedScript::GetJSScript(JSContext* cx)
|
||||
return mScript;
|
||||
}
|
||||
|
||||
// If we have no token at this point, the script was too small to decode
|
||||
// If we have no script at this point, the script was too small to decode
|
||||
// off-thread, or it was needed before the off-thread compilation was
|
||||
// finished, and is small enough to decode on the main thread rather than
|
||||
// wait for the off-thread decoding to finish. In either case, we decode
|
||||
// it synchronously the first time it's needed.
|
||||
if (!mToken) {
|
||||
MOZ_ASSERT(HasRange());
|
||||
MOZ_ASSERT(HasRange());
|
||||
|
||||
JS::RootedScript script(cx);
|
||||
if (JS::DecodeScript(cx, Range(), &script)) {
|
||||
mScript = script;
|
||||
auto start = TimeStamp::Now();
|
||||
LOG(Info, "Decoding script %s on main thread...\n", mURL.get());
|
||||
|
||||
if (mCache.mSaveComplete) {
|
||||
FreeData();
|
||||
}
|
||||
JS::RootedScript script(cx);
|
||||
if (JS::DecodeScript(cx, Range(), &script)) {
|
||||
mScript = script;
|
||||
|
||||
if (mCache.mSaveComplete) {
|
||||
FreeData();
|
||||
}
|
||||
|
||||
return mScript;
|
||||
}
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (JS::CompartmentCreationOptionsRef(cx).addonIdOrNull()) {
|
||||
// Make sure we never try to finish the parse in a compartment with an
|
||||
// add-on ID, it wasn't started in one.
|
||||
ac.emplace(cx, xpc::CompilationScope());
|
||||
}
|
||||
LOG(Debug, "Finished decoding in %fms", (TimeStamp::Now() - start).ToMilliseconds());
|
||||
|
||||
mScript = JS::FinishOffThreadScriptDecoder(cx, mToken);
|
||||
mToken = nullptr;
|
||||
return mScript;
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ private:
|
||||
// the next session's cache file. If it was compiled in this session, its
|
||||
// mXDRRange will initially be empty, and its mXDRData buffer will be
|
||||
// populated just before it is written to the cache file.
|
||||
class CachedScript
|
||||
class CachedScript : public LinkedListElement<CachedScript>
|
||||
{
|
||||
public:
|
||||
CachedScript(CachedScript&&) = default;
|
||||
@ -146,7 +146,6 @@ private:
|
||||
: mCache(cache)
|
||||
, mURL(url)
|
||||
, mCachePath(cachePath)
|
||||
, mStatus(ScriptStatus::Saved)
|
||||
, mScript(script)
|
||||
, mReadyToExecute(true)
|
||||
{}
|
||||
@ -155,29 +154,25 @@ private:
|
||||
|
||||
~CachedScript() = default;
|
||||
|
||||
ScriptStatus Status() const
|
||||
{
|
||||
return mProcessTypes.isEmpty() ? ScriptStatus::Restored : ScriptStatus::Saved;
|
||||
}
|
||||
|
||||
// For use with nsTArray::Sort.
|
||||
//
|
||||
// Orders scripts by:
|
||||
//
|
||||
// 1) Async-decoded scripts before sync-decoded scripts, since the
|
||||
// former are needed immediately at startup, and should be stored
|
||||
// contiguously.
|
||||
// 2) Script load time, so that scripts which are needed earlier are
|
||||
// stored earlier, and scripts needed at approximately the same
|
||||
// time are stored approximately contiguously.
|
||||
// Orders scripts by script load time, so that scripts which are needed
|
||||
// earlier are stored earlier, and scripts needed at approximately the
|
||||
// same time are stored approximately contiguously.
|
||||
struct Comparator
|
||||
{
|
||||
bool Equals(const CachedScript* a, const CachedScript* b) const
|
||||
{
|
||||
return (a->AsyncDecodable() == b->AsyncDecodable() &&
|
||||
a->mLoadTime == b->mLoadTime);
|
||||
return a->mLoadTime == b->mLoadTime;
|
||||
}
|
||||
|
||||
bool LessThan(const CachedScript* a, const CachedScript* b) const
|
||||
{
|
||||
if (a->AsyncDecodable() != b->AsyncDecodable()) {
|
||||
return a->AsyncDecodable();
|
||||
}
|
||||
return a->mLoadTime < b->mLoadTime;
|
||||
}
|
||||
};
|
||||
@ -188,14 +183,12 @@ private:
|
||||
|
||||
virtual bool Matches(CachedScript* script)
|
||||
{
|
||||
return script->mStatus == mStatus;
|
||||
return script->Status() == mStatus;
|
||||
}
|
||||
|
||||
const ScriptStatus mStatus;
|
||||
};
|
||||
|
||||
void Cancel();
|
||||
|
||||
void FreeData()
|
||||
{
|
||||
// If the script data isn't mmapped, we need to release both it
|
||||
@ -213,8 +206,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncDecodable() const { return mSize > MIN_OFFTHREAD_SIZE; }
|
||||
|
||||
// Encodes this script into XDR data, and stores the result in mXDRData.
|
||||
// Returns true on success, false on failure.
|
||||
bool XDREncode(JSContext* cx);
|
||||
@ -292,8 +283,6 @@ private:
|
||||
// The size of this script's encoded XDR data.
|
||||
uint32_t mSize = 0;
|
||||
|
||||
ScriptStatus mStatus;
|
||||
|
||||
TimeStamp mLoadTime{};
|
||||
|
||||
JS::Heap<JSScript*> mScript;
|
||||
@ -304,13 +293,13 @@ private:
|
||||
// whenever it is first executed.
|
||||
bool mReadyToExecute = false;
|
||||
|
||||
// The off-thread decode token for a completed off-thread decode, which
|
||||
// has not yet been finalized on the main thread.
|
||||
void* mToken = nullptr;
|
||||
|
||||
// The set of processes in which this script has been used.
|
||||
EnumSet<ProcessType> mProcessTypes{};
|
||||
|
||||
// The set of processes which the script was loaded into during the
|
||||
// last session, as read from the cache file.
|
||||
EnumSet<ProcessType> mOriginalProcessTypes{};
|
||||
|
||||
// The read-only XDR data for this script, which was either read from an
|
||||
// existing cache file, or generated by encoding a script which was
|
||||
// compiled during this session.
|
||||
@ -328,15 +317,31 @@ private:
|
||||
return &matcher;
|
||||
}
|
||||
|
||||
// There's a trade-off between the time it takes to setup an off-thread
|
||||
// decode and the time we save by doing the decode off-thread. At this
|
||||
// point, the setup is quite expensive, and 20K is about where we start to
|
||||
// see an improvement rather than a regression.
|
||||
// There's a significant setup cost for each off-thread decode operation,
|
||||
// so scripts are decoded in chunks to minimize the overhead. There's a
|
||||
// careful balancing act in choosing the size of chunks, to minimize the
|
||||
// number of decode operations, while also minimizing the number of buffer
|
||||
// underruns that require the main thread to wait for a script to finish
|
||||
// decoding.
|
||||
//
|
||||
// This also means that we get much better performance loading one big
|
||||
// script than several small scripts, since the setup is per-script, and the
|
||||
// OMT compile is almost always complete by the time we need a given script.
|
||||
static constexpr int MIN_OFFTHREAD_SIZE = 20 * 1024;
|
||||
// For the first chunk, we don't have much time between the start of the
|
||||
// decode operation and the time the first script is needed, so that chunk
|
||||
// needs to be fairly small. After the first chunk is finished, we have
|
||||
// some buffered scripts to fall back on, and a lot more breathing room,
|
||||
// so the chunks can be a bit bigger, but still not too big.
|
||||
static constexpr int OFF_THREAD_FIRST_CHUNK_SIZE = 128 * 1024;
|
||||
static constexpr int OFF_THREAD_CHUNK_SIZE = 512 * 1024;
|
||||
|
||||
// Ideally, we want every chunk to be smaller than the chunk sizes
|
||||
// specified above. However, if we have some number of small scripts
|
||||
// followed by a huge script that would put us over the normal chunk size,
|
||||
// we're better off processing them as a single chunk.
|
||||
//
|
||||
// In order to guarantee that the JS engine will process a chunk
|
||||
// off-thread, it needs to be at least 100K (which is an implementation
|
||||
// detail that can change at any time), so make sure that we always hit at
|
||||
// least that size, with a bit of breathing room to be safe.
|
||||
static constexpr int SMALL_SCRIPT_CHUNK_THRESHOLD = 128 * 1024;
|
||||
|
||||
// The maximum size of scripts to re-decode on the main thread if off-thread
|
||||
// decoding hasn't finished yet. In practice, we don't hit this very often,
|
||||
@ -371,11 +376,11 @@ private:
|
||||
// decodes it synchronously on the main thread, as appropriate.
|
||||
JSScript* WaitForCachedScript(JSContext* cx, CachedScript* script);
|
||||
|
||||
// Begins decoding the given script in a background thread.
|
||||
void DecodeScriptOffThread(JSContext* cx, CachedScript* script);
|
||||
void DecodeNextBatch(size_t chunkSize);
|
||||
|
||||
static void OffThreadDecodeCallback(void* token, void* context);
|
||||
void CancelOffThreadParse(void* token);
|
||||
void FinishOffThreadDecode();
|
||||
void DoFinishOffThreadDecode();
|
||||
|
||||
size_t ShallowHeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
@ -406,6 +411,22 @@ private:
|
||||
bool mDataPrepared = false;
|
||||
bool mCacheInvalidated = false;
|
||||
|
||||
// The list of scripts that we read from the initial startup cache file,
|
||||
// but have yet to initiate a decode task for.
|
||||
LinkedList<CachedScript> mPendingScripts;
|
||||
|
||||
// The lists of scripts and their sources that make up the chunk currently
|
||||
// being decoded in a background thread.
|
||||
JS::TranscodeSources mParsingSources;
|
||||
Vector<CachedScript*> mParsingScripts;
|
||||
|
||||
// The token for the completed off-thread decode task.
|
||||
void* mToken = nullptr;
|
||||
|
||||
// True if a runnable has been dispatched to the main thread to finish an
|
||||
// off-thread decode operation.
|
||||
bool mFinishDecodeRunnablePending = false;
|
||||
|
||||
// The process type of the current process.
|
||||
static ProcessType sProcessType;
|
||||
|
||||
|
@ -4674,7 +4674,7 @@ PresShell::StyleRuleRemoved(StyleSheet* aStyleSheet)
|
||||
RecordStyleSheetChange(aStyleSheet);
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsPlaceholderFrame*
|
||||
PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const
|
||||
{
|
||||
return mFrameConstructor->GetPlaceholderFrameFor(aFrame);
|
||||
|
@ -122,7 +122,7 @@ public:
|
||||
virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override;
|
||||
virtual nsCanvasFrame* GetCanvasFrame() const override;
|
||||
|
||||
virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override;
|
||||
virtual nsPlaceholderFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override;
|
||||
virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
||||
nsFrameState aBitToAdd,
|
||||
ReflowRootHandling aRootHandling =
|
||||
|
@ -1316,6 +1316,14 @@ nsBidiPresUtils::ChildListMayRequireBidi(nsIFrame* aFirstChild,
|
||||
|
||||
if (IsBidiLeaf(frame)) {
|
||||
if (frame->IsTextFrame()) {
|
||||
// If the frame already has a BidiDataProperty, we know we need to
|
||||
// perform bidi resolution (even if no bidi content is NOW present --
|
||||
// we might need to remove the property set by a previous reflow, if
|
||||
// content has changed; see bug 1366623).
|
||||
if (frame->Properties().Has(nsIFrame::BidiDataProperty())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check whether the text frame has any RTL characters; if so, bidi
|
||||
// resolution will be needed.
|
||||
nsIContent* content = frame->GetContent();
|
||||
|
@ -77,6 +77,7 @@ template<class E> class nsCOMArray;
|
||||
class AutoWeakFrame;
|
||||
class WeakFrame;
|
||||
class nsIScrollableFrame;
|
||||
class nsPlaceholderFrame;
|
||||
class gfxContext;
|
||||
class nsIDOMEvent;
|
||||
class nsDisplayList;
|
||||
@ -478,7 +479,7 @@ public:
|
||||
* Gets the placeholder frame associated with the specified frame. This is
|
||||
* a helper frame that forwards the request to the frame manager.
|
||||
*/
|
||||
virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const = 0;
|
||||
virtual nsPlaceholderFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const = 0;
|
||||
|
||||
/**
|
||||
* Tell the pres shell that a frame needs to be marked dirty and needs
|
||||
|
@ -1224,20 +1224,29 @@ ReflowInput::CalculateBorderPaddingMargin(
|
||||
* Returns true iff a pre-order traversal of the normal child
|
||||
* frames rooted at aFrame finds no non-empty frame before aDescendant.
|
||||
*/
|
||||
static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
|
||||
nsIFrame* aDescendant, bool* aFound) {
|
||||
static bool
|
||||
AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
|
||||
nsIFrame* aDescendant,
|
||||
bool* aFound)
|
||||
{
|
||||
if (aFrame == aDescendant) {
|
||||
*aFound = true;
|
||||
return true;
|
||||
}
|
||||
if (!aFrame->IsSelfEmpty()) {
|
||||
*aFound = false;
|
||||
return false;
|
||||
}
|
||||
for (nsIFrame* f : aFrame->PrincipalChildList()) {
|
||||
bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
|
||||
if (*aFound || !allEmpty) {
|
||||
return allEmpty;
|
||||
if (aFrame->IsPlaceholderFrame()) {
|
||||
auto ph = static_cast<nsPlaceholderFrame*>(aFrame);
|
||||
MOZ_ASSERT(ph->IsSelfEmpty() && ph->PrincipalChildList().IsEmpty());
|
||||
ph->SetLineIsEmptySoFar(true);
|
||||
} else {
|
||||
if (!aFrame->IsSelfEmpty()) {
|
||||
*aFound = false;
|
||||
return false;
|
||||
}
|
||||
for (nsIFrame* f : aFrame->PrincipalChildList()) {
|
||||
bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
|
||||
if (*aFound || !allEmpty) {
|
||||
return allEmpty;
|
||||
}
|
||||
}
|
||||
}
|
||||
*aFound = false;
|
||||
@ -1256,7 +1265,7 @@ static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
|
||||
void
|
||||
ReflowInput::CalculateHypotheticalPosition(
|
||||
nsPresContext* aPresContext,
|
||||
nsIFrame* aPlaceholderFrame,
|
||||
nsPlaceholderFrame* aPlaceholderFrame,
|
||||
const ReflowInput* cbrs,
|
||||
nsHypotheticalPosition& aHypotheticalPos,
|
||||
LayoutFrameType aFrameType) const
|
||||
@ -1389,15 +1398,31 @@ ReflowInput::CalculateHypotheticalPosition(
|
||||
// XXXbz the line box is not fully reflowed yet if our
|
||||
// containing block is relatively positioned...
|
||||
if (lineBox != iter.End()) {
|
||||
nsIFrame * firstFrame = lineBox->mFirstChild;
|
||||
bool found = false;
|
||||
bool allEmpty = true;
|
||||
while (firstFrame) { // See bug 223064
|
||||
allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
|
||||
aPlaceholderFrame, &found);
|
||||
if (found || !allEmpty)
|
||||
break;
|
||||
firstFrame = firstFrame->GetNextSibling();
|
||||
nsIFrame* firstFrame = lineBox->mFirstChild;
|
||||
bool allEmpty = false;
|
||||
if (firstFrame == aPlaceholderFrame) {
|
||||
aPlaceholderFrame->SetLineIsEmptySoFar(true);
|
||||
allEmpty = true;
|
||||
} else {
|
||||
auto prev = aPlaceholderFrame->GetPrevSibling();
|
||||
if (prev && prev->IsPlaceholderFrame()) {
|
||||
auto ph = static_cast<nsPlaceholderFrame*>(prev);
|
||||
if (ph->GetLineIsEmptySoFar(&allEmpty)) {
|
||||
aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!allEmpty) {
|
||||
bool found = false;
|
||||
while (firstFrame) { // See bug 223064
|
||||
allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
|
||||
aPlaceholderFrame, &found);
|
||||
if (found || !allEmpty) {
|
||||
break;
|
||||
}
|
||||
firstFrame = firstFrame->GetNextSibling();
|
||||
}
|
||||
aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty);
|
||||
}
|
||||
NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
|
||||
|
||||
@ -1581,9 +1606,9 @@ ReflowInput::InitAbsoluteConstraints(nsPresContext* aPresContext,
|
||||
// have been if it had been in the flow
|
||||
nsHypotheticalPosition hypotheticalPos;
|
||||
if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) {
|
||||
nsIFrame* placeholderFrame =
|
||||
nsPlaceholderFrame* placeholderFrame =
|
||||
aPresContext->PresShell()->GetPlaceholderFrameFor(mFrame);
|
||||
NS_ASSERTION(placeholderFrame, "no placeholder frame");
|
||||
MOZ_ASSERT(placeholderFrame, "no placeholder frame");
|
||||
|
||||
if (placeholderFrame->HasAnyStateBits(
|
||||
PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {
|
||||
|
@ -14,12 +14,13 @@
|
||||
#include "mozilla/Assertions.h"
|
||||
#include <algorithm>
|
||||
|
||||
class nsFloatManager;
|
||||
struct nsHypotheticalPosition;
|
||||
class nsIPercentBSizeObserver;
|
||||
class nsLineLayout;
|
||||
class nsPlaceholderFrame;
|
||||
class nsPresContext;
|
||||
class nsRenderingContext;
|
||||
class nsFloatManager;
|
||||
class nsLineLayout;
|
||||
class nsIPercentBSizeObserver;
|
||||
struct nsHypotheticalPosition;
|
||||
|
||||
/**
|
||||
* @return aValue clamped to [aMinValue, aMaxValue].
|
||||
@ -992,7 +993,7 @@ protected:
|
||||
// mode with the same block direction as the absolute containing block
|
||||
// (cbrs->frame), though it may differ in inline direction.
|
||||
void CalculateHypotheticalPosition(nsPresContext* aPresContext,
|
||||
nsIFrame* aPlaceholderFrame,
|
||||
nsPlaceholderFrame* aPlaceholderFrame,
|
||||
const ReflowInput* cbrs,
|
||||
nsHypotheticalPosition& aHypotheticalPos,
|
||||
mozilla::LayoutFrameType aFrameType) const;
|
||||
|
@ -4209,6 +4209,11 @@ nsBlockFrame::ReflowInlineFrame(BlockReflowInput& aState,
|
||||
aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
|
||||
#endif
|
||||
|
||||
if (aFrame->IsPlaceholderFrame()) {
|
||||
auto ph = static_cast<nsPlaceholderFrame*>(aFrame);
|
||||
ph->ForgetLineIsEmptySoFar();
|
||||
}
|
||||
|
||||
// Reflow the inline frame
|
||||
nsReflowStatus frameReflowStatus;
|
||||
bool pushedFrame;
|
||||
@ -5659,6 +5664,14 @@ nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
|
||||
return;
|
||||
|
||||
LineIterator line_end = aFrame->LinesEnd();
|
||||
mLine = aFrame->LinesBegin();
|
||||
if (mLine != line_end && mLine.next() == line_end &&
|
||||
!aFrame->HasOverflowLines()) {
|
||||
// The block has a single line - that must be it!
|
||||
*aFoundValidLine = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to use the cursor if it exists, otherwise fall back to the first line
|
||||
if (nsLineBox* const cursor = aFrame->GetLineCursor()) {
|
||||
mLine = line_end;
|
||||
|
@ -624,6 +624,10 @@ FRAME_STATE_BIT(Placeholder, 24, PLACEHOLDER_FOR_TOPLAYER)
|
||||
// resolve the actual static position using the alignment properties.
|
||||
FRAME_STATE_BIT(Placeholder, 25, PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)
|
||||
|
||||
// Are all earlier frames on the same block line empty?
|
||||
FRAME_STATE_BIT(Placeholder, 26, PLACEHOLDER_LINE_IS_EMPTY_SO_FAR)
|
||||
// Does the above bit have a valid value?
|
||||
FRAME_STATE_BIT(Placeholder, 27, PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR)
|
||||
|
||||
// == Frame state bits that apply to table cell frames ========================
|
||||
|
||||
|
@ -1952,6 +1952,9 @@ public:
|
||||
*/
|
||||
void AddStateBits(nsFrameState aBits) { mState |= aBits; }
|
||||
void RemoveStateBits(nsFrameState aBits) { mState &= ~aBits; }
|
||||
void AddOrRemoveStateBits(nsFrameState aBits, bool aVal) {
|
||||
aVal ? AddStateBits(aBits) : RemoveStateBits(aBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current frame-state includes all of the listed bits
|
||||
|
@ -69,6 +69,7 @@ public:
|
||||
nsFrameState aTypeBit);
|
||||
nsPlaceholderFrame(nsStyleContext* aContext, nsFrameState aTypeBit)
|
||||
: nsFrame(aContext, mozilla::LayoutFrameType::Placeholder)
|
||||
, mOutOfFlowFrame(nullptr)
|
||||
{
|
||||
NS_PRECONDITION(aTypeBit == PLACEHOLDER_FOR_FLOAT ||
|
||||
aTypeBit == PLACEHOLDER_FOR_ABSPOS ||
|
||||
@ -121,6 +122,21 @@ public:
|
||||
|
||||
virtual bool CanContinueTextRun() const override;
|
||||
|
||||
void SetLineIsEmptySoFar(bool aValue) {
|
||||
AddOrRemoveStateBits(PLACEHOLDER_LINE_IS_EMPTY_SO_FAR, aValue);
|
||||
AddStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
|
||||
}
|
||||
bool GetLineIsEmptySoFar(bool* aResult) const {
|
||||
bool haveValue = HasAnyStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
|
||||
if (haveValue) {
|
||||
*aResult = HasAnyStateBits(PLACEHOLDER_LINE_IS_EMPTY_SO_FAR);
|
||||
}
|
||||
return haveValue;
|
||||
}
|
||||
void ForgetLineIsEmptySoFar() {
|
||||
RemoveStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
virtual mozilla::a11y::AccType AccessibleType() override
|
||||
{
|
||||
@ -166,7 +182,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
nsIFrame* mOutOfFlowFrame { nullptr };
|
||||
nsIFrame* mOutOfFlowFrame;
|
||||
};
|
||||
|
||||
#endif /* nsPlaceholderFrame_h___ */
|
||||
|
@ -3864,7 +3864,7 @@ Selection::AddItem(nsRange* aItem, int32_t* aOutIndex, bool aNoStartSelect)
|
||||
|
||||
if (mUserInitiated) {
|
||||
AutoTArray<RefPtr<nsRange>, 4> rangesToAdd;
|
||||
*aOutIndex = -1;
|
||||
*aOutIndex = int32_t(mRanges.Length()) - 1;
|
||||
|
||||
nsIDocument* doc = GetParentObject();
|
||||
bool selectEventsEnabled =
|
||||
|
16
layout/reftests/bidi/1366623-1-ref.html
Normal file
16
layout/reftests/bidi/1366623-1-ref.html
Normal file
@ -0,0 +1,16 @@
|
||||
<html class="reftest-wait">
|
||||
<meta charset=utf-8>
|
||||
<body>
|
||||
<div id="test" contenteditable>Example</div>
|
||||
<script>
|
||||
var elem = document.getElementById("test");
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
var range = document.createRange();
|
||||
range.setStart(elem.firstChild, 1);
|
||||
range.setEnd(elem.firstChild, 1);
|
||||
sel.addRange(range);
|
||||
sel.removeAllRanges();
|
||||
elem.offsetHeight;
|
||||
document.documentElement.removeAttribute("class");
|
||||
</script>
|
17
layout/reftests/bidi/1366623-1.html
Normal file
17
layout/reftests/bidi/1366623-1.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html class="reftest-wait">
|
||||
<meta charset=utf-8>
|
||||
<body>
|
||||
<div id="test" contenteditable>E‮xample</div>
|
||||
<script>
|
||||
var elem = document.getElementById("test");
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
var range = document.createRange();
|
||||
range.setStart(elem.firstChild, 1);
|
||||
range.setEnd(elem.firstChild, 2);
|
||||
sel.addRange(range);
|
||||
sel.deleteFromDocument();
|
||||
sel.removeAllRanges();
|
||||
elem.offsetHeight;
|
||||
document.documentElement.removeAttribute("class");
|
||||
</script>
|
@ -172,3 +172,4 @@ fuzzy-if(Android,254,231) == brackets-2c-rtl.html brackets-2c-rtl-ref.html
|
||||
fails-if(stylo) == 1217833-1.html 1217833-1-ref.html
|
||||
fails-if(stylo) == 1217833-2.html 1217833-2-ref.html
|
||||
== 1231175-1.html 1231175-1-ref.html
|
||||
== 1366623-1.html 1366623-1-ref.html
|
||||
|
@ -20,6 +20,7 @@ EXPORTS += [
|
||||
'nsSVGEffects.h',
|
||||
'nsSVGFilterInstance.h',
|
||||
'nsSVGForeignObjectFrame.h',
|
||||
'nsSVGImageFrame.h',
|
||||
'nsSVGIntegrationUtils.h',
|
||||
'nsSVGUtils.h',
|
||||
'SVGImageContext.h',
|
||||
|
@ -3,6 +3,8 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsSVGImageFrame.h"
|
||||
|
||||
// Keep in (case-insensitive) order:
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPlatform.h"
|
||||
@ -29,97 +31,11 @@ using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::image;
|
||||
|
||||
class nsSVGImageFrame;
|
||||
|
||||
class nsSVGImageListener final : public imgINotificationObserver
|
||||
{
|
||||
public:
|
||||
explicit nsSVGImageListener(nsSVGImageFrame *aFrame);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_IMGINOTIFICATIONOBSERVER
|
||||
|
||||
void SetFrame(nsSVGImageFrame *frame) { mFrame = frame; }
|
||||
|
||||
private:
|
||||
~nsSVGImageListener() {}
|
||||
|
||||
nsSVGImageFrame *mFrame;
|
||||
};
|
||||
|
||||
class nsSVGImageFrame final
|
||||
: public SVGGeometryFrame
|
||||
, public nsIReflowCallback
|
||||
{
|
||||
friend nsIFrame*
|
||||
NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
|
||||
protected:
|
||||
explicit nsSVGImageFrame(nsStyleContext* aContext)
|
||||
: SVGGeometryFrame(aContext, LayoutFrameType::SVGImage)
|
||||
, mReflowCallbackPosted(false)
|
||||
{
|
||||
EnableVisibilityTracking();
|
||||
}
|
||||
|
||||
virtual ~nsSVGImageFrame();
|
||||
|
||||
public:
|
||||
NS_DECL_FRAMEARENA_HELPERS
|
||||
|
||||
// nsSVGDisplayableFrame interface:
|
||||
virtual void PaintSVG(gfxContext& aContext,
|
||||
const gfxMatrix& aTransform,
|
||||
imgDrawingParams& aImgParams,
|
||||
const nsIntRect* aDirtyRect = nullptr) override;
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
|
||||
virtual void ReflowSVG() override;
|
||||
|
||||
// SVGGeometryFrame methods:
|
||||
virtual uint16_t GetHitTestFlags() override;
|
||||
|
||||
// nsIFrame interface:
|
||||
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
void OnVisibilityChange(Visibility aNewVisibility,
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) override;
|
||||
|
||||
virtual void Init(nsIContent* aContent,
|
||||
nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) override;
|
||||
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
virtual nsresult GetFrameName(nsAString& aResult) const override
|
||||
{
|
||||
return MakeFrameName(NS_LITERAL_STRING("SVGImage"), aResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
// nsIReflowCallback
|
||||
virtual bool ReflowFinished() override;
|
||||
virtual void ReflowCallbackCanceled() override;
|
||||
|
||||
private:
|
||||
gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth,
|
||||
int32_t aNativeHeight);
|
||||
gfx::Matrix GetVectorImageTransform();
|
||||
bool TransformContextForPainting(gfxContext* aGfxContext,
|
||||
const gfxMatrix& aTransform);
|
||||
|
||||
nsCOMPtr<imgINotificationObserver> mListener;
|
||||
|
||||
nsCOMPtr<imgIContainer> mImageContainer;
|
||||
|
||||
bool mReflowCallbackPosted;
|
||||
|
||||
friend class nsSVGImageListener;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Implementation
|
||||
// ---------------------------------------------------------------------
|
||||
// nsQueryFrame methods
|
||||
NS_QUERYFRAME_HEAD(nsSVGImageFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsSVGImageFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(SVGGeometryFrame)
|
||||
|
||||
nsIFrame*
|
||||
NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
@ -397,6 +313,11 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext,
|
||||
dirtyRect.MoveBy(-rootRect.TopLeft());
|
||||
}
|
||||
|
||||
uint32_t flags = aImgParams.imageFlags;
|
||||
if (mForceSyncDecoding) {
|
||||
flags |= imgIContainer::FLAG_SYNC_DECODE;
|
||||
}
|
||||
|
||||
if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
|
||||
// Package up the attributes of this image element which can override the
|
||||
// attributes of mImageContainer's internal SVG document. The 'width' &
|
||||
@ -426,7 +347,7 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext,
|
||||
destRect,
|
||||
aDirtyRect ? dirtyRect : destRect,
|
||||
context,
|
||||
aImgParams.imageFlags);
|
||||
flags);
|
||||
} else { // mImageContainer->GetType() == TYPE_RASTER
|
||||
aImgParams.result &= nsLayoutUtils::DrawSingleUnscaledImage(
|
||||
aContext,
|
||||
@ -435,7 +356,7 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext,
|
||||
nsLayoutUtils::GetSamplingFilterForFrame(this),
|
||||
nsPoint(0, 0),
|
||||
aDirtyRect ? &dirtyRect : nullptr,
|
||||
aImgParams.imageFlags);
|
||||
flags);
|
||||
}
|
||||
|
||||
if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
|
||||
|
131
layout/svg/nsSVGImageFrame.h
Normal file
131
layout/svg/nsSVGImageFrame.h
Normal file
@ -0,0 +1,131 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __NS_SVGIMAGEFRAME_H__
|
||||
#define __NS_SVGIMAGEFRAME_H__
|
||||
|
||||
// Keep in (case-insensitive) order:
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsIDOMMutationEvent.h"
|
||||
#include "nsIImageLoadingContent.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "imgINotificationObserver.h"
|
||||
#include "nsSVGEffects.h"
|
||||
#include "mozilla/dom/SVGSVGElement.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "SVGContentUtils.h"
|
||||
#include "SVGGeometryFrame.h"
|
||||
#include "SVGImageContext.h"
|
||||
#include "mozilla/dom/SVGImageElement.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIReflowCallback.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::image;
|
||||
|
||||
class nsSVGImageFrame;
|
||||
|
||||
class nsSVGImageListener final : public imgINotificationObserver
|
||||
{
|
||||
public:
|
||||
explicit nsSVGImageListener(nsSVGImageFrame *aFrame);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_IMGINOTIFICATIONOBSERVER
|
||||
|
||||
void SetFrame(nsSVGImageFrame *frame) { mFrame = frame; }
|
||||
|
||||
private:
|
||||
~nsSVGImageListener() {}
|
||||
|
||||
nsSVGImageFrame *mFrame;
|
||||
};
|
||||
|
||||
class nsSVGImageFrame final
|
||||
: public SVGGeometryFrame
|
||||
, public nsIReflowCallback
|
||||
{
|
||||
friend nsIFrame*
|
||||
NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
|
||||
protected:
|
||||
explicit nsSVGImageFrame(nsStyleContext* aContext)
|
||||
: SVGGeometryFrame(aContext, LayoutFrameType::SVGImage)
|
||||
, mReflowCallbackPosted(false)
|
||||
, mForceSyncDecoding(false)
|
||||
{
|
||||
EnableVisibilityTracking();
|
||||
}
|
||||
|
||||
virtual ~nsSVGImageFrame();
|
||||
|
||||
public:
|
||||
NS_DECL_QUERYFRAME_TARGET(nsSVGImageFrame)
|
||||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_FRAMEARENA_HELPERS
|
||||
|
||||
// nsSVGDisplayableFrame interface:
|
||||
virtual void PaintSVG(gfxContext& aContext,
|
||||
const gfxMatrix& aTransform,
|
||||
imgDrawingParams& aImgParams,
|
||||
const nsIntRect* aDirtyRect = nullptr) override;
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
|
||||
virtual void ReflowSVG() override;
|
||||
|
||||
// SVGGeometryFrame methods:
|
||||
virtual uint16_t GetHitTestFlags() override;
|
||||
|
||||
// nsIFrame interface:
|
||||
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
void OnVisibilityChange(Visibility aNewVisibility,
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) override;
|
||||
|
||||
virtual void Init(nsIContent* aContent,
|
||||
nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) override;
|
||||
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
virtual nsresult GetFrameName(nsAString& aResult) const override
|
||||
{
|
||||
return MakeFrameName(NS_LITERAL_STRING("SVGImage"), aResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
// nsIReflowCallback
|
||||
virtual bool ReflowFinished() override;
|
||||
virtual void ReflowCallbackCanceled() override;
|
||||
|
||||
/// Always sync decode our image when painting if @aForce is true.
|
||||
void SetForceSyncDecoding(bool aForce) { mForceSyncDecoding = aForce; }
|
||||
|
||||
private:
|
||||
gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth,
|
||||
int32_t aNativeHeight);
|
||||
gfx::Matrix GetVectorImageTransform();
|
||||
bool TransformContextForPainting(gfxContext* aGfxContext,
|
||||
const gfxMatrix& aTransform);
|
||||
|
||||
nsCOMPtr<imgINotificationObserver> mListener;
|
||||
|
||||
nsCOMPtr<imgIContainer> mImageContainer;
|
||||
|
||||
bool mReflowCallbackPosted;
|
||||
bool mForceSyncDecoding;
|
||||
|
||||
friend class nsSVGImageListener;
|
||||
};
|
||||
|
||||
#endif // __NS_SVGIMAGEFRAME_H__
|
@ -920,8 +920,8 @@ nsJARChannel::OnDownloadComplete(MemoryDownloader* aDownloader,
|
||||
uint32_t loadFlags;
|
||||
channel->GetLoadFlags(&loadFlags);
|
||||
if (loadFlags & LOAD_REPLACE) {
|
||||
mLoadFlags |= LOAD_REPLACE;
|
||||
|
||||
// Update our URI to reflect any redirects that happen during
|
||||
// the HTTP request.
|
||||
if (!mOriginalURI) {
|
||||
SetOriginalURI(mJarURI);
|
||||
}
|
||||
@ -943,6 +943,9 @@ nsJARChannel::OnDownloadComplete(MemoryDownloader* aDownloader,
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(status) && channel) {
|
||||
// In case the load info object has changed during a redirect,
|
||||
// grab it from the target channel.
|
||||
channel->GetLoadInfo(getter_AddRefs(mLoadInfo));
|
||||
// Grab the security info from our base channel
|
||||
channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
|
||||
|
||||
|
@ -269,6 +269,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
|
||||
, mTriggeringPrincipal(rhs.mTriggeringPrincipal)
|
||||
, mPrincipalToInherit(rhs.mPrincipalToInherit)
|
||||
, mSandboxedLoadingPrincipal(rhs.mSandboxedLoadingPrincipal)
|
||||
, mResultPrincipalURI(rhs.mResultPrincipalURI)
|
||||
, mLoadingContext(rhs.mLoadingContext)
|
||||
, mSecurityFlags(rhs.mSecurityFlags)
|
||||
, mInternalContentPolicyType(rhs.mInternalContentPolicyType)
|
||||
@ -300,6 +301,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
|
||||
nsIPrincipal* aTriggeringPrincipal,
|
||||
nsIPrincipal* aPrincipalToInherit,
|
||||
nsIPrincipal* aSandboxedLoadingPrincipal,
|
||||
nsIURI* aResultPrincipalURI,
|
||||
nsSecurityFlags aSecurityFlags,
|
||||
nsContentPolicyType aContentPolicyType,
|
||||
LoadTainting aTainting,
|
||||
@ -325,6 +327,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
|
||||
: mLoadingPrincipal(aLoadingPrincipal)
|
||||
, mTriggeringPrincipal(aTriggeringPrincipal)
|
||||
, mPrincipalToInherit(aPrincipalToInherit)
|
||||
, mResultPrincipalURI(aResultPrincipalURI)
|
||||
, mSecurityFlags(aSecurityFlags)
|
||||
, mInternalContentPolicyType(aContentPolicyType)
|
||||
, mTainting(aTainting)
|
||||
@ -936,5 +939,19 @@ LoadInfo::GetIsTopLevelLoad(bool *aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadInfo::GetResultPrincipalURI(nsIURI **aURI)
|
||||
{
|
||||
NS_IF_ADDREF(*aURI = mResultPrincipalURI);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadInfo::SetResultPrincipalURI(nsIURI *aURI)
|
||||
{
|
||||
mResultPrincipalURI = aURI;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
@ -84,6 +84,7 @@ private:
|
||||
nsIPrincipal* aTriggeringPrincipal,
|
||||
nsIPrincipal* aPrincipalToInherit,
|
||||
nsIPrincipal* aSandboxedLoadingPrincipal,
|
||||
nsIURI* aResultPrincipalURI,
|
||||
nsSecurityFlags aSecurityFlags,
|
||||
nsContentPolicyType aContentPolicyType,
|
||||
LoadTainting aTainting,
|
||||
@ -128,6 +129,7 @@ private:
|
||||
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
|
||||
nsCOMPtr<nsIPrincipal> mSandboxedLoadingPrincipal;
|
||||
nsCOMPtr<nsIURI> mResultPrincipalURI;
|
||||
nsWeakPtr mLoadingContext;
|
||||
nsSecurityFlags mSecurityFlags;
|
||||
nsContentPolicyType mInternalContentPolicyType;
|
||||
|
@ -438,6 +438,11 @@ nsChannelClassifier::StartInternal()
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Don't bother checking certain types of URIs.
|
||||
bool isAbout = false;
|
||||
rv = uri->SchemeIs("about", &isAbout);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (isAbout) return NS_ERROR_UNEXPECTED;
|
||||
|
||||
bool hasFlags;
|
||||
rv = NS_URIChainHasFlags(uri,
|
||||
nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
|
||||
|
@ -10,6 +10,7 @@
|
||||
interface nsIDOMDocument;
|
||||
interface nsINode;
|
||||
interface nsIPrincipal;
|
||||
interface nsIURI;
|
||||
|
||||
%{C++
|
||||
#include "nsTArray.h"
|
||||
@ -740,6 +741,14 @@ interface nsILoadInfo : nsISupports
|
||||
*/
|
||||
[infallible] readonly attribute boolean isTopLevelLoad;
|
||||
|
||||
/**
|
||||
* If this is non-null, this property represents two things: (1) the
|
||||
* URI to be used for the principal if the channel with this loadinfo
|
||||
* gets a principal based on URI and (2) the URI to use for a document
|
||||
* created from the channel with this loadinfo.
|
||||
*/
|
||||
attribute nsIURI resultPrincipalURI;
|
||||
|
||||
/**
|
||||
* Returns the null principal of the resulting resource if the SEC_SANDBOXED
|
||||
* flag is set. Otherwise returns null. This is used by
|
||||
|
@ -188,11 +188,17 @@ NS_NewChannelInternal(nsIChannel **outChannel,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsLoadFlags channelLoadFlags = 0;
|
||||
channel->GetLoadFlags(&channelLoadFlags);
|
||||
// Will be removed when we remove LOAD_REPLACE altogether
|
||||
// This check is trying to catch protocol handlers that still
|
||||
// try to set the LOAD_REPLACE flag.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!(channelLoadFlags & nsIChannel::LOAD_REPLACE));
|
||||
#endif
|
||||
|
||||
if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
|
||||
// Retain the LOAD_REPLACE load flag if set.
|
||||
nsLoadFlags normalLoadFlags = 0;
|
||||
channel->GetLoadFlags(&normalLoadFlags);
|
||||
rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
|
||||
rv = channel->SetLoadFlags(aLoadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
@ -267,11 +273,17 @@ NS_NewChannelInternal(nsIChannel **outChannel,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsLoadFlags channelLoadFlags = 0;
|
||||
channel->GetLoadFlags(&channelLoadFlags);
|
||||
// Will be removed when we remove LOAD_REPLACE altogether
|
||||
// This check is trying to catch protocol handlers that still
|
||||
// try to set the LOAD_REPLACE flag.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!(channelLoadFlags & nsIChannel::LOAD_REPLACE));
|
||||
#endif
|
||||
|
||||
if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
|
||||
// Retain the LOAD_REPLACE load flag if set.
|
||||
nsLoadFlags normalLoadFlags = 0;
|
||||
channel->GetLoadFlags(&normalLoadFlags);
|
||||
rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
|
||||
rv = channel->SetLoadFlags(aLoadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
@ -1885,12 +1897,15 @@ nsresult
|
||||
NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri)
|
||||
{
|
||||
*uri = nullptr;
|
||||
nsLoadFlags loadFlags = 0;
|
||||
nsresult rv = channel->GetLoadFlags(&loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (loadFlags & nsIChannel::LOAD_REPLACE) {
|
||||
return channel->GetURI(uri);
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
|
||||
if (loadInfo) {
|
||||
nsCOMPtr<nsIURI> resultPrincipalURI;
|
||||
loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
|
||||
if (resultPrincipalURI) {
|
||||
resultPrincipalURI.forget(uri);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return channel->GetOriginalURI(uri);
|
||||
|
@ -781,11 +781,10 @@ nsresult NS_URIChainHasFlags(nsIURI *uri,
|
||||
already_AddRefed<nsIURI> NS_GetInnermostURI(nsIURI *aURI);
|
||||
|
||||
/**
|
||||
* Get the "final" URI for a channel. This is either the same as GetURI or
|
||||
* GetOriginalURI, depending on whether this channel has
|
||||
* nsIChanel::LOAD_REPLACE set. For channels without that flag set, the final
|
||||
* URI is the original URI, while for ones with the flag the final URI is the
|
||||
* channel URI.
|
||||
* Get the "final" URI for a channel. This is either channel's load info
|
||||
* resultPrincipalURI, if set, or GetOriginalURI. In most cases (but not all) load
|
||||
* info resultPrincipalURI, if set, corresponds to URI of the channel if it's required
|
||||
* to represent the actual principal for the channel.
|
||||
*/
|
||||
nsresult NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri);
|
||||
|
||||
|
@ -93,11 +93,10 @@ public:
|
||||
NS_DECL_NSPISOCKETTRANSPORTSERVICE
|
||||
NS_DECL_NSISOCKETTRANSPORTSERVICE
|
||||
NS_DECL_NSIROUTEDSOCKETTRANSPORTSERVICE
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSIEVENTTARGET_FULL
|
||||
NS_DECL_NSITHREADOBSERVER
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_NSIOBSERVER
|
||||
using nsIEventTarget::Dispatch;
|
||||
|
||||
nsSocketTransportService();
|
||||
|
||||
|
@ -25,9 +25,8 @@ class nsStreamTransportService final : public nsIStreamTransportService
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSISTREAMTRANSPORTSERVICE
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSIEVENTTARGET_FULL
|
||||
NS_DECL_NSIOBSERVER
|
||||
using nsIEventTarget::Dispatch;
|
||||
|
||||
nsresult Init();
|
||||
|
||||
|
@ -34,6 +34,7 @@ struct LoadInfoArgs
|
||||
PrincipalInfo triggeringPrincipalInfo;
|
||||
OptionalPrincipalInfo principalToInheritInfo;
|
||||
OptionalPrincipalInfo sandboxedLoadingPrincipalInfo;
|
||||
OptionalURIParams resultPrincipalURI;
|
||||
uint32_t securityFlags;
|
||||
uint32_t contentPolicyType;
|
||||
uint32_t tainting;
|
||||
|
@ -251,7 +251,20 @@ nsFileUploadContentStream::OnCopyComplete()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsFileChannel::nsFileChannel(nsIURI *uri)
|
||||
: mFileURI(uri)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFileChannel::Init()
|
||||
{
|
||||
NS_ENSURE_STATE(mLoadInfo);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
rv = nsBaseChannel::Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If we have a link file, we should resolve its target right away.
|
||||
// This is to protect against a same origin attack where the same link file
|
||||
// can point to different resources right after the first resource is loaded.
|
||||
@ -264,24 +277,24 @@ nsFileChannel::nsFileChannel(nsIURI *uri)
|
||||
#endif
|
||||
nsCOMPtr<nsIFile> resolvedFile;
|
||||
bool symLink;
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
|
||||
if (fileURL &&
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mFileURI);
|
||||
if (fileURL &&
|
||||
NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
|
||||
NS_SUCCEEDED(file->IsSymlink(&symLink)) &&
|
||||
NS_SUCCEEDED(file->IsSymlink(&symLink)) &&
|
||||
symLink &&
|
||||
#ifdef XP_WIN
|
||||
NS_SUCCEEDED(file->GetTarget(fileTarget)) &&
|
||||
NS_SUCCEEDED(NS_NewLocalFile(fileTarget, PR_TRUE,
|
||||
NS_SUCCEEDED(NS_NewLocalFile(fileTarget, true,
|
||||
getter_AddRefs(resolvedFile))) &&
|
||||
#else
|
||||
NS_SUCCEEDED(file->GetNativeTarget(fileTarget)) &&
|
||||
NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, PR_TRUE,
|
||||
NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, true,
|
||||
getter_AddRefs(resolvedFile))) &&
|
||||
#endif
|
||||
NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI),
|
||||
resolvedFile, nullptr))) {
|
||||
NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI),
|
||||
resolvedFile, nullptr))) {
|
||||
// Make an effort to match up the query strings.
|
||||
nsCOMPtr<nsIURL> origURL = do_QueryInterface(uri);
|
||||
nsCOMPtr<nsIURL> origURL = do_QueryInterface(mFileURI);
|
||||
nsCOMPtr<nsIURL> targetURL = do_QueryInterface(targetURI);
|
||||
nsAutoCString queryString;
|
||||
if (origURL && targetURL && NS_SUCCEEDED(origURL->GetQuery(queryString))) {
|
||||
@ -289,13 +302,13 @@ nsFileChannel::nsFileChannel(nsIURI *uri)
|
||||
}
|
||||
|
||||
SetURI(targetURI);
|
||||
SetOriginalURI(uri);
|
||||
nsLoadFlags loadFlags = 0;
|
||||
GetLoadFlags(&loadFlags);
|
||||
SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
|
||||
SetOriginalURI(mFileURI);
|
||||
mLoadInfo->SetResultPrincipalURI(targetURI);
|
||||
} else {
|
||||
SetURI(uri);
|
||||
SetURI(mFileURI);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsFileChannel::~nsFileChannel()
|
||||
|
@ -22,6 +22,8 @@ public:
|
||||
|
||||
explicit nsFileChannel(nsIURI *uri);
|
||||
|
||||
nsresult Init();
|
||||
|
||||
protected:
|
||||
~nsFileChannel();
|
||||
|
||||
@ -40,6 +42,7 @@ protected:
|
||||
private:
|
||||
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||
int64_t mUploadLength;
|
||||
nsCOMPtr<nsIURI> mFileURI;
|
||||
};
|
||||
|
||||
#endif // !nsFileChannel_h__
|
||||
|
@ -190,6 +190,8 @@ nsFileProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsFileChannel *chan;
|
||||
if (IsNeckoChild()) {
|
||||
chan = new mozilla::net::FileChannelChild(uri);
|
||||
@ -200,14 +202,16 @@ nsFileProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(chan);
|
||||
|
||||
nsresult rv = chan->Init();
|
||||
// set the loadInfo on the new channel ; must do this
|
||||
// before calling Init() on it, since it needs the load
|
||||
// info be already set.
|
||||
rv = chan->SetLoadInfo(aLoadInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(chan);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// set the loadInfo on the new channel
|
||||
rv = chan->SetLoadInfo(aLoadInfo);
|
||||
rv = chan->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(chan);
|
||||
return rv;
|
||||
|
@ -2973,6 +2973,72 @@ void HttpBaseChannel::AssertPrivateBrowsingId()
|
||||
}
|
||||
#endif
|
||||
|
||||
already_AddRefed<nsILoadInfo>
|
||||
HttpBaseChannel::CloneLoadInfoForRedirect(nsIURI * newURI, uint32_t redirectFlags)
|
||||
{
|
||||
// make a copy of the loadinfo, append to the redirectchain
|
||||
// this will be set on the newly created channel for the redirect target.
|
||||
if (!mLoadInfo) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> newLoadInfo =
|
||||
static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
|
||||
|
||||
nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
|
||||
if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
|
||||
contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
|
||||
nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
|
||||
newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
|
||||
}
|
||||
|
||||
// re-compute the origin attributes of the loadInfo if it's top-level load.
|
||||
bool isTopLevelDoc =
|
||||
newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
|
||||
|
||||
if (isTopLevelDoc) {
|
||||
nsCOMPtr<nsILoadContext> loadContext;
|
||||
NS_QueryNotificationCallbacks(this, loadContext);
|
||||
OriginAttributes docShellAttrs;
|
||||
if (loadContext) {
|
||||
loadContext->GetOriginAttributes(docShellAttrs);
|
||||
}
|
||||
|
||||
OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
|
||||
|
||||
MOZ_ASSERT(docShellAttrs.mUserContextId == attrs.mUserContextId,
|
||||
"docshell and necko should have the same userContextId attribute.");
|
||||
MOZ_ASSERT(docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
|
||||
"docshell and necko should have the same inIsolatedMozBrowser attribute.");
|
||||
MOZ_ASSERT(docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
|
||||
"docshell and necko should have the same privateBrowsingId attribute.");
|
||||
|
||||
attrs = docShellAttrs;
|
||||
attrs.SetFirstPartyDomain(true, newURI);
|
||||
newLoadInfo->SetOriginAttributes(attrs);
|
||||
}
|
||||
|
||||
// This makes the original URI (the redirect URL) of the target channel become
|
||||
// the resulting principal URI.
|
||||
// After the veto and security checking of this redirect is done, the original
|
||||
// URI of the target channel will be rewriten with the first URI in the redirect
|
||||
// chain (the source URI of this load). Hance the original URI itself can't be
|
||||
// used for determining the result principal URI.
|
||||
// If the target schema protocol handler wants to express a different URI as
|
||||
// the result principal URI, it's free to rewrite this property.
|
||||
// By not modifying the result principal URI the target protocol handler
|
||||
// expresses that it's OK to use the channel's pre-redirect-veto original URI
|
||||
// (the redirect URL) as the result principal URI.
|
||||
newLoadInfo->SetResultPrincipalURI(newURI);
|
||||
|
||||
bool isInternalRedirect =
|
||||
(redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
|
||||
nsIChannelEventSink::REDIRECT_STS_UPGRADE));
|
||||
newLoadInfo->AppendRedirectedPrincipal(GetURIPrincipal(), isInternalRedirect);
|
||||
|
||||
return newLoadInfo.forget();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsITraceableChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -3161,57 +3227,6 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
|
||||
}
|
||||
}
|
||||
|
||||
// make a copy of the loadinfo, append to the redirectchain
|
||||
// and set it on the new channel
|
||||
if (mLoadInfo) {
|
||||
nsCOMPtr<nsILoadInfo> newLoadInfo =
|
||||
static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
|
||||
|
||||
nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
|
||||
if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
|
||||
contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
|
||||
nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
|
||||
newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
|
||||
}
|
||||
|
||||
// re-compute the origin attributes of the loadInfo if it's top-level load.
|
||||
bool isTopLevelDoc =
|
||||
newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
|
||||
|
||||
if (isTopLevelDoc) {
|
||||
nsCOMPtr<nsILoadContext> loadContext;
|
||||
NS_QueryNotificationCallbacks(this, loadContext);
|
||||
OriginAttributes docShellAttrs;
|
||||
if (loadContext) {
|
||||
loadContext->GetOriginAttributes(docShellAttrs);
|
||||
}
|
||||
|
||||
OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
|
||||
|
||||
MOZ_ASSERT(docShellAttrs.mUserContextId == attrs.mUserContextId,
|
||||
"docshell and necko should have the same userContextId attribute.");
|
||||
MOZ_ASSERT(docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
|
||||
"docshell and necko should have the same inIsolatedMozBrowser attribute.");
|
||||
MOZ_ASSERT(docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
|
||||
"docshell and necko should have the same privateBrowsingId attribute.");
|
||||
|
||||
attrs = docShellAttrs;
|
||||
attrs.SetFirstPartyDomain(true, newURI);
|
||||
newLoadInfo->SetOriginAttributes(attrs);
|
||||
}
|
||||
|
||||
bool isInternalRedirect =
|
||||
(redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
|
||||
nsIChannelEventSink::REDIRECT_STS_UPGRADE));
|
||||
newLoadInfo->AppendRedirectedPrincipal(GetURIPrincipal(), isInternalRedirect);
|
||||
newChannel->SetLoadInfo(newLoadInfo);
|
||||
}
|
||||
else {
|
||||
// the newChannel was created with a dummy loadInfo, we should clear
|
||||
// it in case the original channel does not have a loadInfo
|
||||
newChannel->SetLoadInfo(nullptr);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
|
||||
if (!httpChannel)
|
||||
return NS_OK; // no other options to set
|
||||
|
@ -424,6 +424,8 @@ protected:
|
||||
void AssertPrivateBrowsingId();
|
||||
#endif
|
||||
|
||||
already_AddRefed<nsILoadInfo> CloneLoadInfoForRedirect(nsIURI *newURI, uint32_t redirectFlags);
|
||||
|
||||
friend class PrivateBrowsingChannel<HttpBaseChannel>;
|
||||
friend class InterceptFailedOnStop;
|
||||
|
||||
|
@ -1493,9 +1493,10 @@ HttpChannelChild::SetupRedirect(nsIURI* uri,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIChannel> newChannel;
|
||||
nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(uri, redirectFlags);
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
|
||||
uri,
|
||||
mLoadInfo,
|
||||
redirectLoadInfo,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
nsIRequest::LOAD_NORMAL,
|
||||
|
@ -2756,6 +2756,8 @@ nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags)
|
||||
LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
|
||||
|
||||
nsCOMPtr<nsIChannel> newChannel;
|
||||
nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(upgradedURI,
|
||||
flags);
|
||||
|
||||
nsCOMPtr<nsIIOService> ioService;
|
||||
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
|
||||
@ -2763,7 +2765,7 @@ nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags)
|
||||
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
|
||||
upgradedURI,
|
||||
mLoadInfo,
|
||||
redirectLoadInfo,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
nsIRequest::LOAD_NORMAL,
|
||||
@ -5621,22 +5623,23 @@ nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv)
|
||||
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIChannel> newChannel;
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
|
||||
mRedirectURI,
|
||||
mLoadInfo,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
nsIRequest::LOAD_NORMAL,
|
||||
ioService);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32_t redirectFlags;
|
||||
if (nsHttp::IsPermanentRedirect(mRedirectType))
|
||||
redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
|
||||
else
|
||||
redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
|
||||
|
||||
nsCOMPtr<nsIChannel> newChannel;
|
||||
nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(mRedirectURI, redirectFlags);
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
|
||||
mRedirectURI,
|
||||
redirectLoadInfo,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
nsIRequest::LOAD_NORMAL,
|
||||
ioService);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = SetupReplacementChannel(mRedirectURI, newChannel,
|
||||
!rewriteToGET, redirectFlags);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
@ -33,6 +33,10 @@ protected:
|
||||
const nsACString& aPathname,
|
||||
nsACString& aResult) override;
|
||||
|
||||
// |result| is an inout param. On entry to this function, *result
|
||||
// is expected to be non-null and already addrefed. This function
|
||||
// may release the object stored in *result on entry and write
|
||||
// a new pointer to an already addrefed channel to *result.
|
||||
virtual MOZ_MUST_USE nsresult SubstituteChannel(nsIURI* uri,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result) override;
|
||||
|
@ -246,6 +246,8 @@ SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
nsIChannel** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(uri);
|
||||
NS_ENSURE_ARG_POINTER(aLoadInfo);
|
||||
|
||||
nsAutoCString spec;
|
||||
nsresult rv = ResolveURI(uri, spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -254,12 +256,18 @@ SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
rv = NS_NewURI(getter_AddRefs(newURI), spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We don't want to allow the inner protocol handler to modify the result
|
||||
// principal URI since we want either |uri| or anything pre-set by upper
|
||||
// layers to prevail.
|
||||
nsCOMPtr<nsIURI> savedResultPrincipalURI;
|
||||
rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewChannelInternal(result, newURI, aLoadInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsLoadFlags loadFlags = 0;
|
||||
(*result)->GetLoadFlags(&loadFlags);
|
||||
(*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
|
||||
rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = (*result)->SetOriginalURI(uri);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -877,6 +877,10 @@ nsMultiMixedConv::SendStart()
|
||||
|
||||
mPartChannel->SetContentDisposition(mContentDisposition);
|
||||
|
||||
// Each part of a multipart/replace response can be used
|
||||
// for the top level document. We must inform upper layers
|
||||
// about this by setting the LOAD_REPLACE flag so that certain
|
||||
// state assertions are evaluated as positive.
|
||||
nsLoadFlags loadFlags = 0;
|
||||
mPartChannel->GetLoadFlags(&loadFlags);
|
||||
loadFlags |= nsIChannel::LOAD_REPLACE;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user