Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
Gurzau Raul 2019-06-12 00:36:41 +03:00
Родитель 4c97ee3830 227f5329f7
Коммит 577021c8c2
35 изменённых файлов: 1056 добавлений и 695 удалений

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

@ -6,64 +6,66 @@
var EXPORTED_SYMBOLS = ["BrowserTabChild"];
const {ActorChild} = ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
ChromeUtils.defineModuleGetter(this, "E10SUtils",
"resource://gre/modules/E10SUtils.jsm");
class BrowserTabChild extends ActorChild {
class BrowserTabChild extends JSWindowActorChild {
constructor() {
super();
this.handledWindowCreated = false;
this.handledFirstPaint = false;
}
handleEvent(event) {
switch (event.type) {
case "DOMWindowCreated":
let loadContext = this.mm.docShell.QueryInterface(Ci.nsILoadContext);
case "DOMWindowCreated": {
if (this.handledWindowCreated) {
return;
}
this.handledWindowCreated = true;
let context = this.manager.browsingContext;
let loadContext = context.docShell.QueryInterface(Ci.nsILoadContext);
let userContextId = loadContext.originAttributes.userContextId;
this.mm.sendAsyncMessage("Browser:WindowCreated", { userContextId });
this.sendAsyncMessage("Browser:WindowCreated", { userContextId });
break;
}
case "MozAfterPaint":
this.mm.sendAsyncMessage("Browser:FirstPaint");
if (this.handledFirstPaint) {
return;
}
this.handledFirstPaint = true;
this.sendAsyncMessage("Browser:FirstPaint", {});
break;
case "MozDOMPointerLock:Entered":
this.mm.sendAsyncMessage("PointerLock:Entered", {
this.sendAsyncMessage("PointerLock:Entered", {
originNoSuffix: event.target.nodePrincipal.originNoSuffix,
});
break;
case "MozDOMPointerLock:Exited":
this.mm.sendAsyncMessage("PointerLock:Exited");
this.sendAsyncMessage("PointerLock:Exited");
break;
}
}
switchDocumentDirection(window = this.content) {
// document.dir can also be "auto", in which case it won't change
if (window.document.dir == "ltr" || window.document.dir == "") {
window.document.dir = "rtl";
} else if (window.document.dir == "rtl") {
window.document.dir = "ltr";
}
for (let i = 0; i < window.frames.length; i++) {
this.switchDocumentDirection(window.frames[i]);
}
}
receiveMessage(message) {
switch (message.name) {
case "AllowScriptsToClose":
this.content.windowUtils.allowScriptsToClose();
break;
let context = this.manager.browsingContext;
let docShell = context.docShell;
switch (message.name) {
case "Browser:AppTab":
if (this.docShell) {
this.docShell.isAppTab = message.data.isAppTab;
if (docShell) {
docShell.isAppTab = message.data.isAppTab;
}
break;
case "Browser:HasSiblings":
try {
let browserChild = this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
let browserChild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIBrowserChild);
let hasSiblings = message.data;
browserChild.hasSiblings = hasSiblings;
@ -78,8 +80,7 @@ class BrowserTabChild extends ActorChild {
* window (such as view-source) that has no session history, fall
* back on using the web navigation's reload method.
*/
let webNav = this.docShell.QueryInterface(Ci.nsIWebNavigation);
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
try {
if (webNav.sessionHistory) {
webNav = webNav.sessionHistory;
@ -89,23 +90,20 @@ class BrowserTabChild extends ActorChild {
let reloadFlags = message.data.flags;
try {
E10SUtils.wrapHandlingUserInput(this.content, message.data.handlingUserInput,
E10SUtils.wrapHandlingUserInput(this.document.defaultView,
message.data.handlingUserInput,
() => webNav.reload(reloadFlags));
} catch (e) {
}
break;
case "MixedContent:ReenableProtection":
this.docShell.mixedContentChannel = null;
break;
case "SwitchDocumentDirection":
this.switchDocumentDirection();
docShell.mixedContentChannel = null;
break;
case "UpdateCharacterSet":
this.docShell.charset = message.data.value;
this.docShell.gatherCharsetMenuTelemetry();
docShell.charset = message.data.value;
docShell.gatherCharsetMenuTelemetry();
break;
}
}

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

@ -0,0 +1,46 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["BrowserTabParent"];
class BrowserTabParent extends JSWindowActorParent {
receiveMessage(message) {
let browser = this.manager.browsingContext.embedderElement;
if (!browser) {
return; // Can happen sometimes if browser is being destroyed
}
let gBrowser = browser.ownerGlobal.gBrowser;
if (!gBrowser) {
// Note: gBrowser might be null because this message might be received
// from the extension process. There's still an embedderElement involved,
// but it's the one coming from dummy.xul.
// This should probably be fixed by adding support to specifying "group: 'browsers"
// in the registerWindowActor options/. See bug 1557118.
return;
}
switch (message.name) {
case "Browser:WindowCreated": {
gBrowser.announceWindowCreated(browser, message.data.userContextId);
break;
}
case "Browser:FirstPaint": {
browser.ownerGlobal.gBrowserInit._firstBrowserPaintDeferred.resolve();
break;
}
case "MozDOMPointerLock:Entered": {
browser.ownerGlobal.PointerLock.entered(message.data.originNoSuffix);
break;
}
case "MozDOMPointerLock:Exited":
browser.ownerGlobal.PointerLock.exited();
break;
}
}
}

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

@ -0,0 +1,30 @@
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["SwitchDocumentDirectionChild"];
class SwitchDocumentDirectionChild extends JSWindowActorChild {
receiveMessage(message) {
if (message.name == "SwitchDocumentDirection") {
let docShell = this.manager.browsingContext.docShell;
let document = docShell.QueryInterface(Ci.nsIWebNavigation).document;
this.switchDocumentDirection(document);
}
}
switchDocumentDirection(document) {
// document.dir can also be "auto", in which case it won't change
if (document.dir == "ltr" || document.dir == "") {
document.dir = "rtl";
} else if (document.dir == "rtl") {
document.dir = "ltr";
}
for (let frame of document.defaultView.frames) {
this.switchDocumentDirection(frame.document);
}
}
}

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

@ -26,6 +26,7 @@ FINAL_TARGET_FILES.actors += [
'AboutReaderChild.jsm',
'BlockedSiteChild.jsm',
'BrowserTabChild.jsm',
'BrowserTabParent.jsm',
'ClickHandlerChild.jsm',
'ContentSearchChild.jsm',
'ContextMenuChild.jsm',
@ -44,6 +45,7 @@ FINAL_TARGET_FILES.actors += [
'SearchTelemetryChild.jsm',
'SubframeCrashChild.jsm',
'SubframeCrashParent.jsm',
'SwitchDocumentDirectionChild.jsm',
'URIFixupChild.jsm',
'WebRTCChild.jsm',
]

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

@ -224,24 +224,13 @@ var PointerlockFsWarning = {
};
var PointerLock = {
entered(originNoSuffix) {
PointerlockFsWarning.showPointerLock(originNoSuffix);
},
init() {
window.messageManager.addMessageListener("PointerLock:Entered", this);
window.messageManager.addMessageListener("PointerLock:Exited", this);
},
receiveMessage(aMessage) {
switch (aMessage.name) {
case "PointerLock:Entered": {
PointerlockFsWarning.showPointerLock(aMessage.data.originNoSuffix);
break;
}
case "PointerLock:Exited": {
PointerlockFsWarning.close();
break;
}
}
},
exited() {
PointerlockFsWarning.close();
},
};
var FullScreen = {

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

@ -315,9 +315,7 @@
hidden="true"
label="&bidiSwitchPageDirectionItem.label;"
accesskey="&bidiSwitchPageDirectionItem.accesskey;"
oncommand="gBrowser.selectedBrowser
.messageManager
.sendAsyncMessage('SwitchDocumentDirection');"/>
oncommand="gBrowser.selectedBrowser.sendMessageToActor('SwitchDocumentDirection', {}, 'SwitchDocumentDirection', true);"/>
</menupopup>
</menu>

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

@ -325,8 +325,7 @@ var gIdentityHandler = {
},
enableMixedContentProtection() {
gBrowser.selectedBrowser.messageManager.sendAsyncMessage(
"MixedContent:ReenableProtection", {});
gBrowser.selectedBrowser.sendMessageToActor("MixedContent:ReenableProtection", {}, "BrowserTab");
BrowserReload();
PanelMultiView.hidePopup(this._identityPopup);
},

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

@ -1705,7 +1705,6 @@ var gBrowserInit = {
}
FullScreen.init();
PointerLock.init();
if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
MenuTouchModeObserver.init();
@ -1774,17 +1773,12 @@ var gBrowserInit = {
_setInitialFocus() {
let initiallyFocusedElement = document.commandDispatcher.focusedElement;
let firstBrowserPaintDeferred = {};
firstBrowserPaintDeferred.promise = new Promise(resolve => {
firstBrowserPaintDeferred.resolve = resolve;
this._firstBrowserPaintDeferred = {};
this._firstBrowserPaintDeferred.promise = new Promise(resolve => {
this._firstBrowserPaintDeferred.resolve = resolve;
});
let mm = window.messageManager;
mm.addMessageListener("Browser:FirstPaint", function onFirstPaint() {
mm.removeMessageListener("Browser:FirstPaint", onFirstPaint);
firstBrowserPaintDeferred.resolve();
});
let initialBrowser = gBrowser.selectedBrowser;
mm.addMessageListener("Browser:FirstNonBlankPaint",
function onFirstNonBlankPaint() {
@ -1807,7 +1801,7 @@ var gBrowserInit = {
if (gBrowser.selectedBrowser.isRemoteBrowser) {
// If the initial browser is remote, in order to optimize for first paint,
// we'll defer switching focus to that browser until it has painted.
firstBrowserPaintDeferred.promise.then(() => {
this._firstBrowserPaintDeferred.promise.then(() => {
// If focus didn't move while we were waiting for first paint, we're okay
// to move to the browser.
if (document.commandDispatcher.focusedElement == initiallyFocusedElement) {
@ -3484,10 +3478,8 @@ function BrowserReloadWithFlags(reloadFlags) {
}
function sendReloadMessage(tab) {
tab.linkedBrowser
.messageManager
.sendAsyncMessage("Browser:Reload",
{ flags: reloadFlags, handlingUserInput });
tab.linkedBrowser.sendMessageToActor("Browser:Reload",
{ flags: reloadFlags, handlingUserInput }, "BrowserTab");
}
}

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

@ -1458,7 +1458,7 @@ nsContextMenu.prototype = {
},
switchPageDirection: function CM_switchPageDirection() {
this.browser.messageManager.sendAsyncMessage("SwitchDocumentDirection");
gBrowser.selectedBrowser.sendMessageToActor("SwitchDocumentDirection", {}, "SwitchDocumentDirection", true);
},
mediaCommand: function CM_mediaCommand(command, data) {

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

@ -53,7 +53,6 @@ window._gBrowser = {
this.selectedBrowser);
}
messageManager.addMessageListener("RefreshBlocker:Blocked", this);
messageManager.addMessageListener("Browser:WindowCreated", this);
// To correctly handle keypresses for potential FindAsYouType, while
// the tab's find bar is not yet initialized.
@ -601,7 +600,7 @@ window._gBrowser = {
},
_notifyPinnedStatus(aTab) {
this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: aTab.pinned });
aTab.linkedBrowser.sendMessageToActor("Browser:AppTab", { isAppTab: aTab.pinned }, "BrowserTab");
let event = document.createEvent("Events");
event.initEvent(aTab.pinned ? "TabPinned" : "TabUnpinned", true, false);
@ -1780,7 +1779,7 @@ window._gBrowser = {
// crashed.
tab.removeAttribute("crashed");
} else {
aBrowser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned });
aBrowser.sendMessageToActor("Browser:AppTab", { isAppTab: tab.pinned }, "BrowserTab");
// Register the new outerWindowID.
this._outerWindowIDBrowserMap.set(aBrowser.outerWindowID, aBrowser);
@ -1794,9 +1793,7 @@ window._gBrowser = {
this.getCachedFindBar(tab).browser = aBrowser;
}
tab.linkedBrowser
.messageManager
.sendAsyncMessage("Browser:HasSiblings", this.tabs.length > 1);
tab.linkedBrowser.sendMessageToActor("Browser:HasSiblings", this.tabs.length > 1, "BrowserTab");
evt = document.createEvent("Events");
evt.initEvent("TabRemotenessChange", true, false);
@ -2142,12 +2139,10 @@ window._gBrowser = {
// If we transitioned from one browser to two browsers, we need to set
// hasSiblings=false on both the existing browser and the new browser.
if (this.tabs.length == 2) {
window.messageManager
.broadcastAsyncMessage("Browser:HasSiblings", true);
this.tabs[0].linkedBrowser.sendMessageToActor("Browser:HasSiblings", true, "BrowserTab");
this.tabs[1].linkedBrowser.sendMessageToActor("Browser:HasSiblings", true, "BrowserTab");
} else {
aTab.linkedBrowser
.messageManager
.sendAsyncMessage("Browser:HasSiblings", this.tabs.length > 1);
aTab.linkedBrowser.sendMessageToActor("Browser:HasSiblings", this.tabs.length > 1, "BrowserTab");
}
var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: { insertedOnTabCreation: aInsertedOnTabCreation } });
@ -3006,8 +3001,8 @@ window._gBrowser = {
if (this.tabs.length == 2) {
// We're closing one of our two open tabs, inform the other tab that its
// sibling is going away.
window.messageManager
.broadcastAsyncMessage("Browser:HasSiblings", false);
this.tabs[0].linkedBrowser.sendMessageToActor("Browser:HasSiblings", false, "BrowserTab");
this.tabs[1].linkedBrowser.sendMessageToActor("Browser:HasSiblings", false, "BrowserTab");
}
if (aTab.linkedPanel) {
@ -3475,6 +3470,20 @@ window._gBrowser = {
}
},
announceWindowCreated(browser, userContextId) {
let tab = this.getTabForBrowser(browser);
if (tab && userContextId) {
ContextualIdentityService.telemetry(userContextId);
tab.setUserContextId(userContextId);
}
// We don't want to update the container icon and identifier if
// this is not the selected browser.
if (browser == gBrowser.selectedBrowser) {
updateUserContextUIIndicator();
}
},
reloadMultiSelectedTabs() {
this.reloadTabs(this.selectedTabs);
},
@ -4348,23 +4357,7 @@ window._gBrowser = {
return undefined;
this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
browser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned });
break;
}
case "Browser:WindowCreated":
{
let tab = this.getTabForBrowser(browser);
if (tab && data.userContextId) {
ContextualIdentityService.telemetry(data.userContextId);
tab.setUserContextId(data.userContextId);
}
// We don't want to update the container icon and identifier if
// this is not the selected browser.
if (browser == gBrowser.selectedBrowser) {
updateUserContextUIIndicator();
}
browser.sendMessageToActor("Browser:AppTab", { isAppTab: tab.pinned }, "BrowserTab");
break;
}
case "Findbar:Keypress":

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

@ -16,6 +16,29 @@ ChromeUtils.defineModuleGetter(this, "ActorManagerParent",
const PREF_PDFJS_ENABLED_CACHE_STATE = "pdfjs.enabledCache.state";
let ACTORS = {
BrowserTab: {
parent: {
moduleURI: "resource:///actors/BrowserTabParent.jsm",
},
child: {
moduleURI: "resource:///actors/BrowserTabChild.jsm",
events: {
"DOMWindowCreated": {},
"MozAfterPaint": {},
"MozDOMPointerLock:Entered": {},
"MozDOMPointerLock:Exited": {},
},
messages: [
"Browser:Reload",
"Browser:AppTab",
"Browser:HasSiblings",
"MixedContent:ReenableProtection",
"UpdateCharacterSet",
],
},
},
ContextMenu: {
parent: {
moduleURI: "resource:///actors/ContextMenuParent.jsm",
@ -31,6 +54,18 @@ let ACTORS = {
allFrames: true,
},
SwitchDocumentDirection: {
child: {
moduleURI: "resource:///actors/SwitchDocumentDirectionChild.jsm",
messages: [
"SwitchDocumentDirection",
],
},
allFrames: true,
},
SubframeCrash: {
parent: {
moduleURI: "resource:///actors/SubframeCrashParent.jsm",
@ -99,30 +134,6 @@ let LEGACY_ACTORS = {
},
},
BrowserTab: {
child: {
module: "resource:///actors/BrowserTabChild.jsm",
group: "browsers",
events: {
"DOMWindowCreated": {once: true},
"MozAfterPaint": {once: true},
"MozDOMPointerLock:Entered": {},
"MozDOMPointerLock:Exited": {},
},
messages: [
"AllowScriptsToClose",
"Browser:AppTab",
"Browser:HasSiblings",
"Browser:Reload",
"MixedContent:ReenableProtection",
"SwitchDocumentDirection",
"UpdateCharacterSet",
],
},
},
ClickHandler: {
child: {
module: "resource:///actors/ClickHandlerChild.jsm",

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

@ -199,9 +199,9 @@ void GPUProcessManager::DisableGPUProcess(const char* aMessage) {
// need to rebind to the UI process.
HandleProcessLost();
// On Windows, always fallback to software.
// On Windows and Linux, always fallback to software.
// The assumption is that something in the graphics driver is crashing.
#if XP_WIN
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
FallbackToSoftware("GPU Process is disabled, fallback to software solution.");
#endif
}
@ -494,9 +494,11 @@ void GPUProcessManager::OnRemoteProcessDeviceReset(GPUProcessHost* aHost) {
void GPUProcessManager::FallbackToSoftware(const char* aMessage) {
gfxConfig::SetFailed(Feature::HW_COMPOSITING, FeatureStatus::Blocked,
aMessage);
#ifdef XP_WIN
gfxConfig::SetFailed(Feature::D3D11_COMPOSITING, FeatureStatus::Blocked,
aMessage);
gfxConfig::SetFailed(Feature::DIRECT2D, FeatureStatus::Blocked, aMessage);
#endif
}
void GPUProcessManager::NotifyListenersOnCompositeDeviceReset() {

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

@ -3272,12 +3272,24 @@ void gfxPlatform::GetCMSSupportInfo(mozilla::widget::InfoObject& aObj) {
return;
}
char* encodedProfile = nullptr;
nsresult rv =
Base64Encode(reinterpret_cast<char*>(profile), size, &encodedProfile);
if (NS_SUCCEEDED(rv)) {
aObj.DefineProperty("CMSOutputProfile", encodedProfile);
free(encodedProfile);
// Some profiles can be quite large. We don't want to include giant profiles
// by default in about:support. For now, we only accept less than 8kiB.
const size_t kMaxProfileSize = 8192;
if (size < kMaxProfileSize) {
char* encodedProfile = nullptr;
nsresult rv =
Base64Encode(reinterpret_cast<char*>(profile), size, &encodedProfile);
if (NS_SUCCEEDED(rv)) {
aObj.DefineProperty("CMSOutputProfile", encodedProfile);
free(encodedProfile);
} else {
nsPrintfCString msg("base64 encode failed 0x%08x",
static_cast<uint32_t>(rv));
aObj.DefineProperty("CMSOutputProfile", msg.get());
}
} else {
nsPrintfCString msg("%zu bytes, too large", size);
aObj.DefineProperty("CMSOutputProfile", msg.get());
}
free(profile);

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

@ -116,7 +116,6 @@ nsPNGDecoder::nsPNGDecoder(RasterImage* aImage)
mFrameIsHidden(false),
mDisablePremultipliedAlpha(false),
mGotInfoCallback(false),
mUsePipeTransform(false),
mNumFrames(0) {}
nsPNGDecoder::~nsPNGDecoder() {
@ -213,10 +212,9 @@ nsresult nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo) {
pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
}
qcms_transform* pipeTransform = mUsePipeTransform ? mTransform : nullptr;
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), aFrameInfo.mFrameRect, mFormat, animParams,
pipeTransform, pipeFlags);
/*aTransform*/ nullptr, pipeFlags);
if (!pipe) {
mPipe = SurfacePipe();
@ -415,7 +413,8 @@ static void PNGDoGammaCorrection(png_structp png_ptr, png_infop info_ptr) {
// Adapted from http://www.littlecms.com/pngchrm.c example code
static qcms_profile* PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr,
int color_type, uint32_t* intent) {
int color_type, qcms_data_type* inType,
uint32_t* intent) {
qcms_profile* profile = nullptr;
*intent = QCMS_INTENT_PERCEPTUAL; // Our default
@ -493,6 +492,24 @@ static qcms_profile* PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr,
}
}
if (profile) {
uint32_t profileSpace = qcms_profile_get_color_space(profile);
if (profileSpace == icSigGrayData) {
if (color_type & PNG_COLOR_MASK_ALPHA) {
*inType = QCMS_DATA_GRAYA_8;
} else {
*inType = QCMS_DATA_GRAY_8;
}
} else {
if (color_type & PNG_COLOR_MASK_ALPHA ||
png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
*inType = QCMS_DATA_RGBA_8;
} else {
*inType = QCMS_DATA_RGB_8;
}
}
}
return profile;
}
@ -570,41 +587,25 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
png_set_scale_16(png_ptr);
}
// Let libpng expand interlaced images.
const bool isInterlaced = interlace_type == PNG_INTERLACE_ADAM7;
if (isInterlaced) {
png_set_interlace_handling(png_ptr);
}
qcms_data_type inType = QCMS_DATA_RGBA_8;
uint32_t intent = -1;
uint32_t pIntent;
if (decoder->mCMSMode != eCMSMode_Off) {
intent = gfxPlatform::GetRenderingIntent();
decoder->mInProfile =
PNGGetColorProfile(png_ptr, info_ptr, color_type, &pIntent);
PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent);
// If we're not mandating an intent, use the one from the image.
if (intent == uint32_t(-1)) {
intent = pIntent;
}
}
if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
qcms_data_type inType;
qcms_data_type outType;
uint32_t profileSpace = qcms_profile_get_color_space(decoder->mInProfile);
decoder->mUsePipeTransform = profileSpace != icSigGrayData;
if (decoder->mUsePipeTransform) {
// If the transform happens with SurfacePipe, it will always be in BGRA.
inType = QCMS_DATA_BGRA_8;
outType = QCMS_DATA_BGRA_8;
if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) {
outType = QCMS_DATA_RGBA_8;
} else {
if (color_type & PNG_COLOR_MASK_ALPHA) {
inType = QCMS_DATA_GRAYA_8;
outType = QCMS_DATA_RGBA_8;
} else {
inType = QCMS_DATA_GRAY_8;
outType = QCMS_DATA_RGB_8;
}
outType = QCMS_DATA_RGB_8;
}
decoder->mTransform = qcms_transform_create(
@ -619,11 +620,20 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
}
if (decoder->mCMSMode == eCMSMode_All) {
decoder->mTransform = gfxPlatform::GetCMSBGRATransform();
decoder->mUsePipeTransform = true;
if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) {
decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
} else {
decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
}
}
}
// Let libpng expand interlaced images.
const bool isInterlaced = interlace_type == PNG_INTERLACE_ADAM7;
if (isInterlaced) {
png_set_interlace_handling(png_ptr);
}
// now all of those things we set above are used to update various struct
// members and whatnot, after which we can get channels, rowbytes, etc.
png_read_update_info(png_ptr, info_ptr);
@ -688,8 +698,8 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
}
#endif
if (decoder->mTransform && !decoder->mUsePipeTransform) {
uint32_t bpp[] = {0, 3, 4};
if (decoder->mTransform && (channels <= 2 || isInterlaced)) {
uint32_t bpp[] = {0, 3, 4, 3, 4};
decoder->mCMSLine =
static_cast<uint8_t*>(malloc(bpp[channels] * frameRect.Width()));
if (!decoder->mCMSLine) {
@ -830,11 +840,21 @@ void nsPNGDecoder::WriteRow(uint8_t* aRow) {
uint32_t width = uint32_t(mFrameRect.Width());
// Apply color management to the row, if necessary, before writing it out.
// This is only needed for grayscale images.
if (mTransform && !mUsePipeTransform) {
MOZ_ASSERT(mCMSLine);
qcms_transform_data(mTransform, rowToWrite, mCMSLine, width);
rowToWrite = mCMSLine;
if (mTransform) {
if (mCMSLine) {
qcms_transform_data(mTransform, rowToWrite, mCMSLine, width);
// Copy alpha over.
if (HasAlphaChannel()) {
for (uint32_t i = 0; i < width; ++i) {
mCMSLine[4 * i + 3] = rowToWrite[mChannels * i + mChannels - 1];
}
}
rowToWrite = mCMSLine;
} else {
qcms_transform_data(mTransform, rowToWrite, rowToWrite, width);
}
}
// Write this row to the SurfacePipe.

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

@ -101,7 +101,6 @@ class nsPNGDecoder : public Decoder {
bool mFrameIsHidden;
bool mDisablePremultipliedAlpha;
bool mGotInfoCallback;
bool mUsePipeTransform;
struct AnimFrameInfo {
AnimFrameInfo();

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

@ -228,8 +228,8 @@ nsresult nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect) {
}
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), aFrameRect, mFormat, animParams, mTransform,
pipeFlags);
this, Size(), OutputSize(), aFrameRect, mFormat, animParams,
/*aTransform*/ nullptr, pipeFlags);
if (!pipe) {
MOZ_LOG(sWebPLog, LogLevel::Error,
("[this=%p] nsWebPDecoder::CreateFrame -- no pipe\n", this));
@ -281,7 +281,7 @@ void nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength) {
("[this=%p] nsWebPDecoder::ApplyColorProfile -- not tagged, use "
"sRGB transform\n",
this));
mTransform = gfxPlatform::GetCMSBGRATransform();
mTransform = gfxPlatform::GetCMSRGBATransform();
return;
}
@ -311,9 +311,9 @@ void nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength) {
}
// Create the color management transform.
mTransform = qcms_transform_create(mInProfile, QCMS_DATA_BGRA_8,
mTransform = qcms_transform_create(mInProfile, QCMS_DATA_RGBA_8,
gfxPlatform::GetCMSOutputProfile(),
QCMS_DATA_BGRA_8, (qcms_intent)intent);
QCMS_DATA_RGBA_8, (qcms_intent)intent);
MOZ_LOG(sWebPLog, LogLevel::Debug,
("[this=%p] nsWebPDecoder::ApplyColorProfile -- use tagged "
"transform\n",
@ -463,6 +463,9 @@ LexerResult nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength,
for (int row = mLastRow; row < lastRow; row++) {
uint8_t* src = rowStart + row * stride;
if (mTransform) {
qcms_transform_data(mTransform, src, src, width);
}
WriteState result;
if (mFormat == SurfaceFormat::B8G8R8A8) {

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

@ -427,7 +427,7 @@ namespace JS {
D(FULL_WHOLE_CELL_BUFFER, 17) \
D(FULL_GENERIC_BUFFER, 18) \
D(FULL_VALUE_BUFFER, 19) \
D(FULL_CELL_PTR_BUFFER, 20) \
D(FULL_CELL_PTR_OBJ_BUFFER, 20) \
D(FULL_SLOT_BUFFER, 21) \
D(FULL_SHAPE_BUFFER, 22) \
D(TOO_MUCH_WASM_MEMORY, 23) \
@ -435,10 +435,10 @@ namespace JS {
D(FINISH_GC, 25) \
D(PREPARE_FOR_TRACING, 26) \
D(INCREMENTAL_ALLOC_TRIGGER, 27) \
D(FULL_CELL_PTR_STR_BUFFER, 28) \
D(INCREMENTAL_MALLOC_TRIGGER, 29) \
\
/* These are reserved for future use. */ \
D(RESERVED4, 28) \
D(RESERVED5, 29) \
D(RESERVED6, 30) \
D(RESERVED7, 31) \
D(RESERVED8, 32) \

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

@ -371,8 +371,9 @@ bool GCRuntime::gcIfNeededAtAllocation(JSContext* cx) {
// If we have grown past our GC heap threshold while in the middle of
// an incremental GC, we're growing faster than we're GCing, so stop
// the world and do a full, non-incremental GC right now, if possible.
Zone* zone = cx->zone();
if (isIncrementalGCInProgress() &&
cx->zone()->totalBytes() > cx->zone()->threshold.gcTriggerBytes()) {
zone->zoneSize.gcBytes() > zone->threshold.gcTriggerBytes()) {
PrepareZoneForGC(cx->zone());
gc(GC_NORMAL, JS::GCReason::INCREMENTAL_TOO_SLOW);
}

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

@ -1460,8 +1460,7 @@ bool GCRuntime::setParameter(JSGCParamKey key, uint32_t value,
return false;
}
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
zone->threshold.updateAfterGC(zone->totalBytes(), GC_NORMAL, tunables,
schedulingState, lock);
zone->updateAllGCThresholds(*this, lock);
}
}
@ -1720,8 +1719,7 @@ void GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) {
default:
tunables.resetParameter(key, lock);
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
zone->threshold.updateAfterGC(zone->totalBytes(), GC_NORMAL, tunables,
schedulingState, lock);
zone->updateAllGCThresholds(*this, lock);
}
}
}
@ -2071,7 +2069,7 @@ void GCRuntime::setMaxMallocBytes(size_t value, const AutoLockGC& lock) {
}
}
float ZoneHeapThreshold::eagerAllocTrigger(bool highFrequencyGC) const {
float ZoneThreshold::eagerAllocTrigger(bool highFrequencyGC) const {
float eagerTriggerFactor = highFrequencyGC
? HighFrequencyEagerAllocTriggerFactor
: LowFrequencyEagerAllocTriggerFactor;
@ -2167,6 +2165,22 @@ void ZoneHeapThreshold::updateForRemovedArena(
gcTriggerBytes_ -= amount;
}
/* static */
size_t ZoneMallocThreshold::computeZoneTriggerBytes(
float growthFactor, size_t lastBytes, const GCSchedulingTunables& tunables,
const AutoLockGC& lock) {
size_t base = Max(lastBytes, tunables.maxMallocBytes());
float trigger = float(base) * growthFactor;
return size_t(trigger);
}
void ZoneMallocThreshold::updateAfterGC(size_t lastBytes,
const GCSchedulingTunables& tunables,
const GCSchedulingState& state,
const AutoLockGC& lock) {
gcTriggerBytes_ = computeZoneTriggerBytes(2.0, lastBytes, tunables, lock);
}
MemoryCounter::MemoryCounter()
: bytes_(0), maxBytes_(0), triggered_(NoTrigger) {}
@ -3422,10 +3436,6 @@ bool GCRuntime::triggerGC(JS::GCReason reason) {
return true;
}
void js::gc::MaybeAllocTriggerZoneGC(JSRuntime* rt, ZoneAllocator* zoneAlloc) {
rt->gc.maybeAllocTriggerZoneGC(Zone::from(zoneAlloc));
}
void GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, size_t nbytes) {
if (!CurrentThreadCanAccessRuntime(rt)) {
// Zones in use by a helper thread can't be collected.
@ -3435,9 +3445,9 @@ void GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, size_t nbytes) {
MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
size_t usedBytes = zone->totalBytes(); // This already includes |nbytes|.
size_t usedBytes =
zone->zoneSize.gcBytes(); // This already includes |nbytes|.
size_t thresholdBytes = zone->threshold.gcTriggerBytes();
if (usedBytes >= thresholdBytes) {
// The threshold has been surpassed, immediately trigger a GC, which
// will be done non-incrementally.
@ -3478,6 +3488,39 @@ void GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, size_t nbytes) {
}
}
void js::gc::MaybeMallocTriggerZoneGC(JSRuntime* rt, ZoneAllocator* zoneAlloc) {
rt->gc.maybeMallocTriggerZoneGC(Zone::from(zoneAlloc));
}
void GCRuntime::maybeMallocTriggerZoneGC(Zone* zone) {
if (!CurrentThreadCanAccessRuntime(rt)) {
// Zones in use by a helper thread can't be collected.
MOZ_ASSERT(zone->usedByHelperThread() || zone->isAtomsZone());
return;
}
MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
size_t usedBytes = zone->gcMallocBytes.gcBytes();
size_t thresholdBytes = zone->gcMallocThreshold.gcTriggerBytes();
if (usedBytes >= thresholdBytes) {
// The threshold has been surpassed, immediately trigger a GC, which
// will be done non-incrementally.
triggerZoneGC(zone, JS::GCReason::TOO_MUCH_MALLOC, usedBytes,
thresholdBytes);
return;
}
float zoneGCThresholdFactor = tunables.allocThresholdFactor();
size_t igcThresholdBytes = thresholdBytes * zoneGCThresholdFactor;
if (usedBytes >= igcThresholdBytes) {
// Start or continue an in progress incremental GC.
triggerZoneGC(zone, JS::GCReason::INCREMENTAL_MALLOC_TRIGGER, usedBytes,
igcThresholdBytes);
return;
}
}
bool GCRuntime::triggerZoneGC(Zone* zone, JS::GCReason reason, size_t used,
size_t threshold) {
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
@ -3532,17 +3575,30 @@ void GCRuntime::maybeGC(Zone* zone) {
return;
}
float threshold = zone->threshold.eagerAllocTrigger(
schedulingState.inHighFrequencyGCMode());
float usedBytes = zone->totalBytes();
if (usedBytes > 1024 * 1024 && usedBytes >= threshold &&
!isIncrementalGCInProgress() && !isBackgroundSweeping()) {
stats().recordTrigger(usedBytes, threshold);
if (isIncrementalGCInProgress() || isBackgroundSweeping()) {
return;
}
if (checkEagerAllocTrigger(zone->zoneSize, zone->threshold) ||
checkEagerAllocTrigger(zone->gcMallocBytes, zone->gcMallocThreshold)) {
PrepareZoneForGC(zone);
startGC(GC_NORMAL, JS::GCReason::EAGER_ALLOC_TRIGGER);
}
}
bool GCRuntime::checkEagerAllocTrigger(const HeapSize& size,
const ZoneThreshold& threshold) {
float thresholdBytes =
threshold.eagerAllocTrigger(schedulingState.inHighFrequencyGCMode());
float usedBytes = size.gcBytes();
if (usedBytes <= 1024 * 1024 || usedBytes < thresholdBytes) {
return false;
}
stats().recordTrigger(usedBytes, thresholdBytes);
return true;
}
void GCRuntime::triggerFullGCForAtoms(JSContext* cx) {
MOZ_ASSERT(fullGCForAtomsRequested_);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
@ -5938,8 +5994,7 @@ IncrementalProgress GCRuntime::endSweepingSweepGroup(FreeOp* fop,
for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
AutoLockGC lock(rt);
zone->changeGCState(Zone::Sweep, Zone::Finished);
zone->threshold.updateAfterGC(zone->totalBytes(), invocationKind, tunables,
schedulingState, lock);
zone->updateAllGCThresholds(*this, lock);
zone->updateAllGCMallocCountersOnGCEnd(lock);
zone->arenas.unmarkPreMarkedFreeCells();
}
@ -7440,7 +7495,7 @@ GCRuntime::IncrementalResult GCRuntime::budgetIncrementalGC(
continue;
}
if (zone->totalBytes() >= zone->threshold.gcTriggerBytes()) {
if (zone->zoneSize.gcBytes() >= zone->threshold.gcTriggerBytes()) {
CheckZoneIsScheduled(zone, reason, "GC bytes");
budget.makeUnlimited();
stats().nonincremental(AbortReason::GCBytesTrigger);
@ -7449,6 +7504,16 @@ GCRuntime::IncrementalResult GCRuntime::budgetIncrementalGC(
}
}
if (zone->gcMallocBytes.gcBytes() >=
zone->gcMallocThreshold.gcTriggerBytes()) {
CheckZoneIsScheduled(zone, reason, "malloc bytes");
budget.makeUnlimited();
stats().nonincremental(AbortReason::MallocBytesTrigger);
if (zone->wasGCStarted() && zone->gcState() > Zone::Sweep) {
resetReason = AbortReason::MallocBytesTrigger;
}
}
if (zone->shouldTriggerGCForTooMuchMalloc() == NonIncrementalTrigger) {
CheckZoneIsScheduled(zone, reason, "malloc bytes");
budget.makeUnlimited();
@ -7493,10 +7558,14 @@ static void ScheduleZones(GCRuntime* gc) {
// This is a heuristic to reduce the total number of collections.
bool inHighFrequencyMode = gc->schedulingState.inHighFrequencyGCMode();
if (zone->totalBytes() >=
if (zone->zoneSize.gcBytes() >=
zone->threshold.eagerAllocTrigger(inHighFrequencyMode)) {
zone->scheduleGC();
}
if (zone->gcMallocBytes.gcBytes() >=
zone->gcMallocThreshold.eagerAllocTrigger(inHighFrequencyMode)) {
zone->scheduleGC();
}
// This ensures we collect zones that have reached the malloc limit.
if (zone->shouldTriggerGCForTooMuchMalloc()) {
@ -8025,6 +8094,7 @@ void GCRuntime::minorGC(JS::GCReason reason, gcstats::PhaseKind phase) {
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
maybeAllocTriggerZoneGC(zone);
maybeMallocTriggerZoneGC(zone);
}
}
@ -8209,6 +8279,7 @@ void gc::MergeRealms(Realm* source, Realm* target) {
JSRuntime* rt = source->runtimeFromMainThread();
rt->gc.mergeRealms(source, target);
rt->gc.maybeAllocTriggerZoneGC(target->zone());
rt->gc.maybeMallocTriggerZoneGC(target->zone());
}
void GCRuntime::mergeRealms(Realm* source, Realm* target) {

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

@ -258,13 +258,18 @@ class GCRuntime {
uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock);
MOZ_MUST_USE bool triggerGC(JS::GCReason reason);
// Check whether to trigger a zone GC. During an incremental GC, optionally
// count |nbytes| towards the threshold for performing the next slice.
// Check whether to trigger a zone GC after allocating GC cells. During an
// incremental GC, optionally count |nbytes| towards the threshold for
// performing the next slice.
void maybeAllocTriggerZoneGC(Zone* zone, size_t nbytes = 0);
// Check whether to trigger a zone GC after malloc memory.
void maybeMallocTriggerZoneGC(Zone* zone);
// The return value indicates if we were able to do the GC.
bool triggerZoneGC(Zone* zone, JS::GCReason reason, size_t usedBytes,
size_t thresholdBytes);
void maybeGC(Zone* zone);
bool checkEagerAllocTrigger(const HeapSize& size,
const ZoneThreshold& threshold);
// The return value indicates whether a major GC was performed.
bool gcIfRequested();
void gc(JSGCInvocationKind gckind, JS::GCReason reason);

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

@ -831,54 +831,6 @@ static_assert(
offsetof(Chunk, trailer) + offsetof(ChunkTrailer, storeBuffer),
"The hardcoded API storeBuffer offset must match the actual offset.");
/*
* Tracks the used sizes for owned heap data and automatically maintains the
* memory usage relationship between GCRuntime and Zones.
*/
class HeapSize {
/*
* A heap usage that contains our parent's heap usage, or null if this is
* the top-level usage container.
*/
HeapSize* const parent_;
/*
* The approximate number of bytes in use on the GC heap, to the nearest
* ArenaSize. This does not include any malloc data. It also does not
* include not-actively-used addresses that are still reserved at the OS
* level for GC usage. It is atomic because it is updated by both the active
* and GC helper threads.
*/
mozilla::Atomic<size_t, mozilla::ReleaseAcquire,
mozilla::recordreplay::Behavior::DontPreserve>
gcBytes_;
public:
explicit HeapSize(HeapSize* parent) : parent_(parent), gcBytes_(0) {}
size_t gcBytes() const { return gcBytes_; }
void addGCArena() {
gcBytes_ += ArenaSize;
if (parent_) {
parent_->addGCArena();
}
}
void removeGCArena() {
MOZ_ASSERT(gcBytes_ >= ArenaSize);
gcBytes_ -= ArenaSize;
if (parent_) {
parent_->removeGCArena();
}
}
/* Pair to adoptArenas. Adopts the attendant usage statistics. */
void adopt(HeapSize& other) {
gcBytes_ += other.gcBytes_;
other.gcBytes_ = 0;
}
};
inline void Arena::checkAddress() const {
mozilla::DebugOnly<uintptr_t> addr = uintptr_t(this);
MOZ_ASSERT(addr);

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

@ -778,11 +778,14 @@ bool js::Nursery::shouldCollect() const {
return belowBytesThreshold && belowFractionThreshold;
}
static inline bool IsFullStoreBufferReason(JS::GCReason reason) {
return reason == JS::GCReason::FULL_WHOLE_CELL_BUFFER ||
// typeReason is the gcReason for specified type, for example,
// FULL_CELL_PTR_OBJ_BUFFER is the gcReason for JSObject.
static inline bool IsFullStoreBufferReason(JS::GCReason reason,
JS::GCReason typeReason) {
return reason == typeReason ||
reason == JS::GCReason::FULL_WHOLE_CELL_BUFFER ||
reason == JS::GCReason::FULL_GENERIC_BUFFER ||
reason == JS::GCReason::FULL_VALUE_BUFFER ||
reason == JS::GCReason::FULL_CELL_PTR_BUFFER ||
reason == JS::GCReason::FULL_SLOT_BUFFER ||
reason == JS::GCReason::FULL_SHAPE_BUFFER;
}
@ -840,68 +843,7 @@ void js::Nursery::collect(JS::GCReason reason) {
// Resize the nursery.
maybeResizeNursery(reason);
// If we are promoting the nursery, or exhausted the store buffer with
// pointers to nursery things, which will force a collection well before
// the nursery is full, look for object groups that are getting promoted
// excessively and try to pretenure them.
startProfile(ProfileKey::Pretenure);
bool validPromotionRate;
const float promotionRate = calcPromotionRate(&validPromotionRate);
uint32_t pretenureCount = 0;
bool shouldPretenure =
tunables().attemptPretenuring() &&
((validPromotionRate && promotionRate > tunables().pretenureThreshold() &&
previousGC.nurseryUsedBytes >= 4 * 1024 * 1024) ||
IsFullStoreBufferReason(reason));
if (shouldPretenure) {
JSContext* cx = rt->mainContextFromOwnThread();
for (auto& entry : tenureCounts.entries) {
if (entry.count >= tunables().pretenureGroupThreshold()) {
ObjectGroup* group = entry.group;
AutoMaybeLeaveAtomsZone leaveAtomsZone(cx);
AutoRealm ar(cx, group);
AutoSweepObjectGroup sweep(group);
if (group->canPreTenure(sweep)) {
group->setShouldPreTenure(sweep, cx);
pretenureCount++;
}
}
}
}
stats().setStat(gcstats::STAT_OBJECT_GROUPS_PRETENURED, pretenureCount);
mozilla::Maybe<AutoGCSession> session;
uint32_t numStringsTenured = 0;
uint32_t numNurseryStringRealmsDisabled = 0;
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
if (shouldPretenure && zone->allocNurseryStrings &&
zone->tenuredStrings >= 30 * 1000) {
if (!session.isSome()) {
session.emplace(rt, JS::HeapState::MinorCollecting);
}
CancelOffThreadIonCompile(zone);
bool preserving = zone->isPreservingCode();
zone->setPreservingCode(false);
zone->discardJitCode(rt->defaultFreeOp());
zone->setPreservingCode(preserving);
for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
if (jit::JitRealm* jitRealm = r->jitRealm()) {
jitRealm->discardStubs();
jitRealm->setStringsCanBeInNursery(false);
numNurseryStringRealmsDisabled++;
}
}
zone->allocNurseryStrings = false;
}
numStringsTenured += zone->tenuredStrings;
zone->tenuredStrings = 0;
}
session.reset(); // End the minor GC session, if running one.
stats().setStat(gcstats::STAT_NURSERY_STRING_REALMS_DISABLED,
numNurseryStringRealmsDisabled);
stats().setStat(gcstats::STAT_STRINGS_TENURED, numStringsTenured);
endProfile(ProfileKey::Pretenure);
const float promotionRate = doPretenuring(rt, reason, tenureCounts);
// We ignore gcMaxBytes when allocating for minor collection. However, if we
// overflowed, we disable the nursery. The next time we allocate, we'll fail
@ -920,8 +862,6 @@ void js::Nursery::collect(JS::GCReason reason) {
rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON_LONG, uint32_t(reason));
}
rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_BYTES, committed());
rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount);
rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_PROMOTION_RATE, promotionRate * 100);
stats().endNurseryCollection(reason);
gcTracer.traceMinorGCEnd();
@ -1063,6 +1003,93 @@ void js::Nursery::doCollection(JS::GCReason reason,
previousGC.tenuredCells = mover.tenuredCells;
}
float js::Nursery::doPretenuring(JSRuntime* rt, JS::GCReason reason,
TenureCountCache& tenureCounts) {
// If we are promoting the nursery, or exhausted the store buffer with
// pointers to nursery things, which will force a collection well before
// the nursery is full, look for object groups that are getting promoted
// excessively and try to pretenure them.
startProfile(ProfileKey::Pretenure);
bool validPromotionRate;
const float promotionRate = calcPromotionRate(&validPromotionRate);
uint32_t pretenureCount = 0;
bool attempt = tunables().attemptPretenuring();
bool pretenureObj, pretenureStr;
if (attempt) {
// Should we do pretenuring regardless of gcreason?
bool shouldPretenure = validPromotionRate &&
promotionRate > tunables().pretenureThreshold() &&
previousGC.nurseryUsedBytes >= 4 * 1024 * 1024;
pretenureObj =
shouldPretenure ||
IsFullStoreBufferReason(reason, JS::GCReason::FULL_CELL_PTR_OBJ_BUFFER);
pretenureStr =
shouldPretenure ||
IsFullStoreBufferReason(reason, JS::GCReason::FULL_CELL_PTR_STR_BUFFER);
} else {
pretenureObj = false;
pretenureStr = false;
}
if (pretenureObj) {
JSContext* cx = rt->mainContextFromOwnThread();
uint32_t threshold = tunables().pretenureGroupThreshold();
for (auto& entry : tenureCounts.entries) {
if (entry.count < threshold) {
continue;
}
ObjectGroup* group = entry.group;
AutoMaybeLeaveAtomsZone leaveAtomsZone(cx);
AutoRealm ar(cx, group);
AutoSweepObjectGroup sweep(group);
if (group->canPreTenure(sweep)) {
group->setShouldPreTenure(sweep, cx);
pretenureCount++;
}
}
}
stats().setStat(gcstats::STAT_OBJECT_GROUPS_PRETENURED, pretenureCount);
mozilla::Maybe<AutoGCSession> session;
uint32_t numStringsTenured = 0;
uint32_t numNurseryStringRealmsDisabled = 0;
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
if (pretenureStr && zone->allocNurseryStrings &&
zone->tenuredStrings >= 30 * 1000) {
if (!session.isSome()) {
session.emplace(rt, JS::HeapState::MinorCollecting);
}
CancelOffThreadIonCompile(zone);
bool preserving = zone->isPreservingCode();
zone->setPreservingCode(false);
zone->discardJitCode(rt->defaultFreeOp());
zone->setPreservingCode(preserving);
for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
if (jit::JitRealm* jitRealm = r->jitRealm()) {
jitRealm->discardStubs();
jitRealm->setStringsCanBeInNursery(false);
numNurseryStringRealmsDisabled++;
}
}
zone->allocNurseryStrings = false;
}
numStringsTenured += zone->tenuredStrings;
zone->tenuredStrings = 0;
}
session.reset(); // End the minor GC session, if running one.
stats().setStat(gcstats::STAT_NURSERY_STRING_REALMS_DISABLED,
numNurseryStringRealmsDisabled);
stats().setStat(gcstats::STAT_STRINGS_TENURED, numStringsTenured);
endProfile(ProfileKey::Pretenure);
rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount);
rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_PROMOTION_RATE, promotionRate * 100);
return promotionRate;
}
bool js::Nursery::registerMallocedBuffer(void* buffer) {
MOZ_ASSERT(buffer);
return mallocedBuffers.putNew(buffer);

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

@ -608,6 +608,9 @@ class Nursery {
void doCollection(JS::GCReason reason, gc::TenureCountCache& tenureCounts);
float doPretenuring(JSRuntime* rt, JS::GCReason reason,
gc::TenureCountCache& tenureCounts);
/*
* Move the object at |src| in the Nursery to an already-allocated cell
* |dst| in Tenured.

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

@ -618,23 +618,82 @@ class MemoryCounter {
const AutoLockGC& lock);
};
// This class encapsulates the data that determines when we need to do a zone
// GC.
class ZoneHeapThreshold {
// The "growth factor" for computing our next thresholds after a GC.
GCLockData<float> gcHeapGrowthFactor_;
/*
* Tracks the used sizes for owned heap data and automatically maintains the
* memory usage relationship between GCRuntime and Zones.
*/
class HeapSize {
/*
* A heap usage that contains our parent's heap usage, or null if this is
* the top-level usage container.
*/
HeapSize* const parent_;
// GC trigger threshold for allocations on the GC heap.
/*
* The approximate number of bytes in use on the GC heap, to the nearest
* ArenaSize. This does not include any malloc data. It also does not
* include not-actively-used addresses that are still reserved at the OS
* level for GC usage. It is atomic because it is updated by both the active
* and GC helper threads.
*/
mozilla::Atomic<size_t, mozilla::ReleaseAcquire,
mozilla::recordreplay::Behavior::DontPreserve>
gcBytes_;
public:
explicit HeapSize(HeapSize* parent) : parent_(parent), gcBytes_(0) {}
size_t gcBytes() const { return gcBytes_; }
void addGCArena() { addBytes(ArenaSize); }
void removeGCArena() { removeBytes(ArenaSize); }
void addBytes(size_t nbytes) {
mozilla::DebugOnly<size_t> initialBytes(gcBytes_);
MOZ_ASSERT(initialBytes + nbytes > initialBytes);
gcBytes_ += nbytes;
if (parent_) {
parent_->addBytes(nbytes);
}
}
void removeBytes(size_t nbytes) {
MOZ_ASSERT(gcBytes_ >= nbytes);
gcBytes_ -= nbytes;
if (parent_) {
parent_->removeBytes(nbytes);
}
}
/* Pair to adoptArenas. Adopts the attendant usage statistics. */
void adopt(HeapSize& other) {
gcBytes_ += other.gcBytes_;
other.gcBytes_ = 0;
}
};
// Base class for GC heap and malloc thresholds.
class ZoneThreshold {
protected:
// GC trigger threshold.
mozilla::Atomic<size_t, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
gcTriggerBytes_;
public:
ZoneHeapThreshold() : gcHeapGrowthFactor_(3.0f), gcTriggerBytes_(0) {}
float gcHeapGrowthFactor() const { return gcHeapGrowthFactor_; }
size_t gcTriggerBytes() const { return gcTriggerBytes_; }
float eagerAllocTrigger(bool highFrequencyGC) const;
};
// This class encapsulates the data that determines when we need to do a zone GC
// base on GC heap size.
class ZoneHeapThreshold : public ZoneThreshold {
// The "growth factor" for computing our next thresholds after a GC.
GCLockData<float> gcHeapGrowthFactor_;
public:
ZoneHeapThreshold() : gcHeapGrowthFactor_(3.0f) {}
float gcHeapGrowthFactor() const { return gcHeapGrowthFactor_; }
void updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind,
const GCSchedulingTunables& tunables,
@ -651,6 +710,19 @@ class ZoneHeapThreshold {
const AutoLockGC& lock);
};
// This class encapsulates the data that determines when we need to do a zone
// GC based on malloc data.
class ZoneMallocThreshold : public ZoneThreshold {
public:
void updateAfterGC(size_t lastBytes, const GCSchedulingTunables& tunables,
const GCSchedulingState& state, const AutoLockGC& lock);
private:
static size_t computeZoneTriggerBytes(float growthFactor, size_t lastBytes,
const GCSchedulingTunables& tunables,
const AutoLockGC& lock);
};
#ifdef DEBUG
// Counts memory associated with GC things in a zone.
@ -661,8 +733,8 @@ class ZoneHeapThreshold {
class MemoryTracker {
public:
MemoryTracker();
~MemoryTracker();
void fixupAfterMovingGC();
void checkEmptyOnDestroy();
void adopt(MemoryTracker& other);

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

@ -55,10 +55,10 @@ void StoreBuffer::GenericBuffer::trace(JSTracer* trc) {
}
StoreBuffer::StoreBuffer(JSRuntime* rt, const Nursery& nursery)
: bufferVal(this),
bufStrCell(this),
bufObjCell(this),
bufferSlot(this),
: bufferVal(this, JS::GCReason::FULL_VALUE_BUFFER),
bufStrCell(this, JS::GCReason::FULL_CELL_PTR_STR_BUFFER),
bufObjCell(this, JS::GCReason::FULL_CELL_PTR_OBJ_BUFFER),
bufferSlot(this, JS::GCReason::FULL_SLOT_BUFFER),
bufferWholeCell(this),
bufferGeneric(this),
cancelIonCompilations_(false),

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

@ -80,10 +80,13 @@ class StoreBuffer {
StoreBuffer* owner_;
JS::GCReason gcReason_;
/* Maximum number of entries before we request a minor GC. */
const static size_t MaxEntries = 48 * 1024 / sizeof(T);
explicit MonoTypeBuffer(StoreBuffer* owner) : last_(T()), owner_(owner) {}
explicit MonoTypeBuffer(StoreBuffer* owner, JS::GCReason reason)
: last_(T()), owner_(owner), gcReason_(reason) {}
void clear() {
last_ = T();
@ -117,7 +120,7 @@ class StoreBuffer {
last_ = T();
if (MOZ_UNLIKELY(stores_.count() > MaxEntries)) {
owner_->setAboutToOverflow(T::FullBufferReason);
owner_->setAboutToOverflow(gcReason_);
}
}
@ -276,8 +279,6 @@ class StoreBuffer {
explicit operator bool() const { return edge != nullptr; }
typedef PointerEdgeHasher<CellPtrEdge> Hasher;
static const auto FullBufferReason = JS::GCReason::FULL_CELL_PTR_BUFFER;
};
struct ValueEdge {
@ -311,8 +312,6 @@ class StoreBuffer {
explicit operator bool() const { return edge != nullptr; }
typedef PointerEdgeHasher<ValueEdge> Hasher;
static const auto FullBufferReason = JS::GCReason::FULL_VALUE_BUFFER;
};
struct SlotsEdge {
@ -394,8 +393,6 @@ class StoreBuffer {
}
static bool match(const SlotsEdge& k, const Lookup& l) { return k == l; }
} Hasher;
static const auto FullBufferReason = JS::GCReason::FULL_SLOT_BUFFER;
};
template <typename Buffer, typename Edge>

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

@ -32,17 +32,26 @@ using namespace js::gc;
Zone* const Zone::NotOnList = reinterpret_cast<Zone*>(1);
ZoneAllocator::ZoneAllocator(JSRuntime* rt)
: JS::shadow::Zone(rt, &rt->gc.marker), zoneSize(&rt->gc.heapSize) {
: JS::shadow::Zone(rt, &rt->gc.marker),
zoneSize(&rt->gc.heapSize),
gcMallocBytes(nullptr) {
AutoLockGC lock(rt);
threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables,
rt->gc.schedulingState, lock);
gcMallocThreshold.updateAfterGC(8192, rt->gc.tunables, rt->gc.schedulingState,
lock);
setGCMaxMallocBytes(rt->gc.tunables.maxMallocBytes(), lock);
jitCodeCounter.setMax(jit::MaxCodeBytesPerProcess * 0.8, lock);
}
ZoneAllocator::~ZoneAllocator() {
MOZ_ASSERT_IF(runtimeFromAnyThread()->gc.shutdownCollectedEverything(),
gcMallocBytes == 0);
#ifdef DEBUG
if (runtimeFromAnyThread()->gc.shutdownCollectedEverything()) {
gcMallocTracker.checkEmptyOnDestroy();
MOZ_ASSERT(zoneSize.gcBytes() == 0);
MOZ_ASSERT(gcMallocBytes.gcBytes() == 0);
}
#endif
}
void ZoneAllocator::fixupAfterMovingGC() {
@ -63,6 +72,14 @@ void js::ZoneAllocator::updateAllGCMallocCountersOnGCEnd(
jitCodeCounter.updateOnGCEnd(gc.tunables, lock);
}
void js::ZoneAllocator::updateAllGCThresholds(GCRuntime& gc,
const js::AutoLockGC& lock) {
threshold.updateAfterGC(zoneSize.gcBytes(), GC_NORMAL, gc.tunables,
gc.schedulingState, lock);
gcMallocThreshold.updateAfterGC(gcMallocBytes.gcBytes(), gc.tunables,
gc.schedulingState, lock);
}
js::gc::TriggerKind js::ZoneAllocator::shouldTriggerGCForTooMuchMalloc() {
auto& gc = runtimeFromAnyThread()->gc;
return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables),
@ -658,12 +675,7 @@ static const char* MemoryUseName(MemoryUse use) {
MemoryTracker::MemoryTracker() : mutex(mutexid::MemoryTracker) {}
MemoryTracker::~MemoryTracker() {
if (!TlsContext.get()->runtime()->gc.shutdownCollectedEverything()) {
// Memory leak, suppress crashes.
return;
}
void MemoryTracker::checkEmptyOnDestroy() {
bool ok = true;
if (!map.empty()) {

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

@ -21,7 +21,7 @@ class Zone;
namespace js {
namespace gc {
void MaybeAllocTriggerZoneGC(JSRuntime* rt, ZoneAllocator* zoneAlloc);
void MaybeMallocTriggerZoneGC(JSRuntime* rt, ZoneAllocator* zoneAlloc);
}
// Base class of JS::Zone that provides malloc memory allocation and accounting.
@ -51,8 +51,7 @@ class ZoneAllocator : public JS::shadow::Zone,
}
void adoptMallocBytes(ZoneAllocator* other) {
gcMallocCounter.adopt(other->gcMallocCounter);
gcMallocBytes += other->gcMallocBytes;
other->gcMallocBytes = 0;
gcMallocBytes.adopt(other->gcMallocBytes);
#ifdef DEBUG
gcMallocTracker.adopt(other->gcMallocTracker);
#endif
@ -66,6 +65,7 @@ class ZoneAllocator : public JS::shadow::Zone,
void updateAllGCMallocCountersOnGCStart();
void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock);
void updateAllGCThresholds(gc::GCRuntime& gc, const js::AutoLockGC& lock);
js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc();
// Memory accounting APIs for malloc memory owned by GC cells.
@ -73,10 +73,8 @@ class ZoneAllocator : public JS::shadow::Zone,
void addCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
MOZ_ASSERT(cell);
MOZ_ASSERT(nbytes);
mozilla::DebugOnly<size_t> initialBytes(gcMallocBytes);
MOZ_ASSERT(initialBytes + nbytes > initialBytes);
gcMallocBytes.addBytes(nbytes);
gcMallocBytes += nbytes;
// We don't currently check GC triggers here.
#ifdef DEBUG
@ -87,9 +85,7 @@ class ZoneAllocator : public JS::shadow::Zone,
void removeCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
MOZ_ASSERT(cell);
MOZ_ASSERT(nbytes);
MOZ_ASSERT(gcMallocBytes >= nbytes);
gcMallocBytes -= nbytes;
gcMallocBytes.removeBytes(nbytes);
#ifdef DEBUG
gcMallocTracker.untrackMemory(cell, nbytes, use);
@ -113,36 +109,29 @@ class ZoneAllocator : public JS::shadow::Zone,
void incPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
MOZ_ASSERT(nbytes);
mozilla::DebugOnly<size_t> initialBytes(gcMallocBytes);
MOZ_ASSERT(initialBytes + nbytes > initialBytes);
gcMallocBytes += nbytes;
gcMallocBytes.addBytes(nbytes);
#ifdef DEBUG
gcMallocTracker.incPolicyMemory(policy, nbytes);
#endif
maybeAllocTriggerZoneGC();
maybeMallocTriggerZoneGC();
}
void decPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
MOZ_ASSERT(nbytes);
MOZ_ASSERT(gcMallocBytes >= nbytes);
gcMallocBytes -= nbytes;
gcMallocBytes.removeBytes(nbytes);
#ifdef DEBUG
gcMallocTracker.decPolicyMemory(policy, nbytes);
#endif
}
size_t totalBytes() const { return zoneSize.gcBytes() + gcMallocBytes; }
// Check allocation threshold and trigger a zone GC if necessary.
void maybeAllocTriggerZoneGC() {
// Check malloc allocation threshold and trigger a zone GC if necessary.
void maybeMallocTriggerZoneGC() {
JSRuntime* rt = runtimeFromAnyThread();
if (totalBytes() >= threshold.gcTriggerBytes() &&
if (gcMallocBytes.gcBytes() >= threshold.gcTriggerBytes() &&
rt->heapState() == JS::HeapState::Idle) {
gc::MaybeAllocTriggerZoneGC(rt, this);
gc::MaybeMallocTriggerZoneGC(rt, this);
}
}
@ -164,10 +153,10 @@ class ZoneAllocator : public JS::shadow::Zone,
js::gc::TriggerKind trigger);
public:
// Track heap size under this Zone.
// Track GC heap size under this Zone.
js::gc::HeapSize zoneSize;
// Thresholds used to trigger GC.
// Thresholds used to trigger GC based on heap size.
js::gc::ZoneHeapThreshold threshold;
// Amount of data to allocate before triggering a new incremental slice for
@ -180,12 +169,15 @@ class ZoneAllocator : public JS::shadow::Zone,
// free. Currently this is used for all internal malloc allocations.
js::gc::MemoryCounter gcMallocCounter;
public:
// Malloc counter used for allocations where size information is
// available. Used for some internal and all tracked external allocations.
mozilla::Atomic<size_t, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
gcMallocBytes;
js::gc::HeapSize gcMallocBytes;
// Thresholds used to trigger GC based on malloc allocations.
js::gc::ZoneMallocThreshold gcMallocThreshold;
private:
#ifdef DEBUG
// In debug builds, malloc allocations can be tracked to make debugging easier
// (possible?) if allocation and free sizes don't balance.

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

@ -1172,7 +1172,7 @@ JS_PUBLIC_API void JS::AddAssociatedMemory(JSObject* obj, size_t nbytes,
Zone* zone = obj->zone();
zone->addCellMemory(obj, nbytes, js::MemoryUse(use));
zone->maybeAllocTriggerZoneGC();
zone->maybeMallocTriggerZoneGC();
}
JS_PUBLIC_API void JS::RemoveAssociatedMemory(JSObject* obj, size_t nbytes,

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

@ -228,7 +228,7 @@ inline bool IsSpace(char16_t ch) {
return CharInfo(ch).isSpace();
}
inline bool IsSpaceOrBOM2(char16_t ch) {
inline bool IsSpaceOrBOM2(char32_t ch) {
if (ch < 128) {
return js_isspace[ch];
}
@ -238,6 +238,10 @@ inline bool IsSpaceOrBOM2(char16_t ch) {
return true;
}
if (ch >= NonBMPMin) {
return false;
}
return CharInfo(ch).isSpace();
}

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

@ -1650,23 +1650,20 @@ class ScriptSource::LoadSourceMatcher {
explicit LoadSourceMatcher(JSContext* cx, ScriptSource* ss, bool* loaded)
: cx_(cx), ss_(ss), loaded_(loaded) {}
template <typename Unit>
bool operator()(const Compressed<Unit>&) const {
template <typename Unit, SourceRetrievable CanRetrieve>
bool operator()(const Compressed<Unit, CanRetrieve>&) const {
*loaded_ = true;
return true;
}
template <typename Unit>
bool operator()(const Uncompressed<Unit>&) const {
template <typename Unit, SourceRetrievable CanRetrieve>
bool operator()(const Uncompressed<Unit, CanRetrieve>&) const {
*loaded_ = true;
return true;
}
template <typename Unit>
bool operator()(const Retrievable<Unit>&) {
MOZ_ASSERT(ss_->sourceRetrievable(),
"should be retrievable if Retrievable");
if (!cx_->runtime()->sourceHook.ref()) {
*loaded_ = false;
return true;
@ -1685,15 +1682,11 @@ class ScriptSource::LoadSourceMatcher {
}
bool operator()(const Missing&) const {
MOZ_ASSERT(!ss_->sourceRetrievable(),
"should have Retrievable<Unit> source, not Missing source, if "
"retrievable");
*loaded_ = false;
return true;
}
bool operator()(const BinAST&) const {
MOZ_ASSERT(!ss_->sourceRetrievable(), "binast source is never retrievable");
*loaded_ = false;
return true;
}
@ -1777,7 +1770,7 @@ template <typename Unit>
const Unit* UncompressedSourceCache::lookup(const ScriptSourceChunk& ssc,
AutoHoldEntry& holder) {
MOZ_ASSERT(!holder_);
MOZ_ASSERT(ssc.ss->compressedSourceIs<Unit>());
MOZ_ASSERT(ssc.ss->isCompressed<Unit>());
if (!map_) {
return nullptr;
@ -1841,7 +1834,7 @@ template <typename Unit>
const Unit* ScriptSource::chunkUnits(
JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
size_t chunk) {
const Compressed<Unit>& c = data.as<Compressed<Unit>>();
const CompressedData<Unit>& c = *compressedData<Unit>();
ScriptSourceChunk ssc(this, chunk);
if (const Unit* decompressed =
@ -1879,18 +1872,33 @@ const Unit* ScriptSource::chunkUnits(
}
template <typename Unit>
void ScriptSource::movePendingCompressedSource() {
void ScriptSource::convertToCompressedSource(SharedImmutableString compressed,
size_t uncompressedLength) {
MOZ_ASSERT(isUncompressed<Unit>());
MOZ_ASSERT(uncompressedData<Unit>()->length() == uncompressedLength);
if (data.is<Uncompressed<Unit, SourceRetrievable::Yes>>()) {
data = SourceType(Compressed<Unit, SourceRetrievable::Yes>(
std::move(compressed), uncompressedLength));
} else {
data = SourceType(Compressed<Unit, SourceRetrievable::No>(
std::move(compressed), uncompressedLength));
}
}
template <typename Unit>
void ScriptSource::performDelayedConvertToCompressedSource() {
// There might not be a conversion to compressed source happening at all.
if (pendingCompressed_.empty()) {
return;
}
Compressed<Unit>& pending = pendingCompressed_.ref<Compressed<Unit>>();
CompressedData<Unit>& pending =
pendingCompressed_.ref<CompressedData<Unit>>();
MOZ_ASSERT(!hasCompressedSource());
MOZ_ASSERT_IF(hasUncompressedSource(),
pending.uncompressedLength == length());
convertToCompressedSource<Unit>(std::move(pending.raw),
pending.uncompressedLength);
data = SourceType(std::move(pending));
pendingCompressed_.destroy();
}
@ -1900,7 +1908,7 @@ ScriptSource::PinnedUnits<Unit>::~PinnedUnits() {
MOZ_ASSERT(*stack_ == this);
*stack_ = prev_;
if (!prev_) {
source_->movePendingCompressedSource<Unit>();
source_->performDelayedConvertToCompressedSource<Unit>();
}
}
}
@ -1912,8 +1920,8 @@ const Unit* ScriptSource::units(JSContext* cx,
MOZ_ASSERT(begin <= length());
MOZ_ASSERT(begin + len <= length());
if (data.is<Uncompressed<Unit>>()) {
const Unit* units = data.as<Uncompressed<Unit>>().units();
if (isUncompressed<Unit>()) {
const Unit* units = uncompressedData<Unit>()->units();
if (!units) {
return nullptr;
}
@ -1928,7 +1936,7 @@ const Unit* ScriptSource::units(JSContext* cx,
MOZ_CRASH("ScriptSource::units() on ScriptSource with retrievable source");
}
MOZ_ASSERT(data.is<Compressed<Unit>>());
MOZ_ASSERT(isCompressed<Unit>());
// Determine first/last chunks, the offset (in bytes) into the first chunk
// of the requested units, and the number of bytes in the last chunk.
@ -2130,7 +2138,8 @@ JSFlatString* ScriptSource::functionBodyString(JSContext* cx) {
template <typename Unit>
MOZ_MUST_USE bool ScriptSource::setUncompressedSourceHelper(
JSContext* cx, EntryUnits<Unit>&& source, size_t length) {
JSContext* cx, EntryUnits<Unit>&& source, size_t length,
SourceRetrievable retrievable) {
auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
auto uniqueChars = SourceTypeTraits<Unit>::toCacheable(std::move(source));
@ -2140,7 +2149,13 @@ MOZ_MUST_USE bool ScriptSource::setUncompressedSourceHelper(
return false;
}
data = SourceType(Uncompressed<Unit>(std::move(*deduped)));
if (retrievable == SourceRetrievable::Yes) {
data = SourceType(
Uncompressed<Unit, SourceRetrievable::Yes>(std::move(*deduped)));
} else {
data = SourceType(
Uncompressed<Unit, SourceRetrievable::No>(std::move(*deduped)));
}
return true;
}
@ -2148,11 +2163,11 @@ template <typename Unit>
MOZ_MUST_USE bool ScriptSource::setRetrievedSource(JSContext* cx,
EntryUnits<Unit>&& source,
size_t length) {
MOZ_ASSERT(sourceRetrievable_);
MOZ_ASSERT(data.is<Retrievable<Unit>>(),
"retrieved source can only overwrite the corresponding "
"retrievable source");
return setUncompressedSourceHelper(cx, std::move(source), length);
return setUncompressedSourceHelper(cx, std::move(source), length,
SourceRetrievable::Yes);
}
#if defined(JS_BUILD_BINAST)
@ -2226,25 +2241,31 @@ bool ScriptSource::tryCompressOffThread(JSContext* cx) {
}
template <typename Unit>
void ScriptSource::convertToCompressedSource(SharedImmutableString compressed,
size_t uncompressedLength) {
MOZ_ASSERT(data.is<Uncompressed<Unit>>(),
"should only be converting uncompressed source to compressed "
"source identically encoded");
MOZ_ASSERT(length() == uncompressedLength);
void ScriptSource::triggerConvertToCompressedSource(
SharedImmutableString compressed, size_t uncompressedLength) {
MOZ_ASSERT(isUncompressed<Unit>(),
"should only be triggering compressed source installation to "
"overwrite identically-encoded uncompressed source");
MOZ_ASSERT(uncompressedData<Unit>()->length() == uncompressedLength);
if (pinnedUnitsStack_) {
MOZ_ASSERT(pendingCompressed_.empty());
pendingCompressed_.construct<Compressed<Unit>>(std::move(compressed),
uncompressedLength);
} else {
data =
SourceType(Compressed<Unit>(std::move(compressed), uncompressedLength));
// If units aren't pinned -- and they probably won't be, we'd have to have a
// GC in the small window of time where a |PinnedUnits| was live -- then we
// can immediately convert.
if (MOZ_LIKELY(!pinnedUnitsStack_)) {
convertToCompressedSource<Unit>(std::move(compressed), uncompressedLength);
return;
}
// Otherwise, set aside the compressed-data info. The conversion is performed
// when the last |PinnedUnits| dies.
MOZ_ASSERT(pendingCompressed_.empty(),
"shouldn't be multiple conversions happening");
pendingCompressed_.construct<CompressedData<Unit>>(std::move(compressed),
uncompressedLength);
}
template <typename Unit>
MOZ_MUST_USE bool ScriptSource::initializeWithCompressedSource(
MOZ_MUST_USE bool ScriptSource::initializeWithUnretrievableCompressedSource(
JSContext* cx, UniqueChars&& compressed, size_t rawLength,
size_t sourceLength) {
MOZ_ASSERT(data.is<Missing>(), "shouldn't be double-initializing");
@ -2261,7 +2282,9 @@ MOZ_MUST_USE bool ScriptSource::initializeWithCompressedSource(
"shouldn't be initializing a ScriptSource while its characters "
"are pinned -- that only makes sense with a ScriptSource actively "
"being inspected");
data = SourceType(Compressed<Unit>(std::move(*deduped), sourceLength));
data = SourceType(Compressed<Unit, SourceRetrievable::No>(std::move(*deduped),
sourceLength));
return true;
}
@ -2278,7 +2301,6 @@ bool ScriptSource::assignSource(JSContext* cx,
}
if (options.sourceIsLazy) {
sourceRetrievable_ = true;
data = SourceType(Retrievable<Unit>());
return true;
}
@ -2296,7 +2318,8 @@ bool ScriptSource::assignSource(JSContext* cx,
return false;
}
data = SourceType(Uncompressed<Unit>(std::move(*deduped)));
data = SourceType(
Uncompressed<Unit, SourceRetrievable::No>(std::move(*deduped)));
return true;
}
@ -2334,7 +2357,7 @@ static MOZ_MUST_USE bool reallocUniquePtr(UniqueChars& unique, size_t size) {
template <typename Unit>
void SourceCompressionTask::workEncodingSpecific() {
ScriptSource* source = sourceHolder_.get();
MOZ_ASSERT(source->data.is<ScriptSource::Uncompressed<Unit>>());
MOZ_ASSERT(source->isUncompressed<Unit>());
// Try to keep the maximum memory usage down by only allocating half the
// size of the string, first.
@ -2345,8 +2368,7 @@ void SourceCompressionTask::workEncodingSpecific() {
return;
}
const Unit* chars =
source->data.as<ScriptSource::Uncompressed<Unit>>().units();
const Unit* chars = source->uncompressedData<Unit>()->units();
Compressor comp(reinterpret_cast<const unsigned char*>(chars), inputBytes);
if (!comp.init()) {
return;
@ -2410,8 +2432,8 @@ struct SourceCompressionTask::PerformTaskWork {
explicit PerformTaskWork(SourceCompressionTask* task) : task_(task) {}
template <typename Unit>
void operator()(const ScriptSource::Uncompressed<Unit>&) {
template <typename Unit, SourceRetrievable CanRetrieve>
void operator()(const ScriptSource::Uncompressed<Unit, CanRetrieve>&) {
task_->workEncodingSpecific<Unit>();
}
@ -2439,15 +2461,15 @@ void SourceCompressionTask::runTask() {
source->performTaskWork(this);
}
void ScriptSource::convertToCompressedSourceFromTask(
void ScriptSource::triggerConvertToCompressedSourceFromTask(
SharedImmutableString compressed) {
data.match(ConvertToCompressedSourceFromTask(this, compressed));
data.match(TriggerConvertToCompressedSourceFromTask(this, compressed));
}
void SourceCompressionTask::complete() {
if (!shouldCancel() && resultString_.isSome()) {
ScriptSource* source = sourceHolder_.get();
source->convertToCompressedSourceFromTask(std::move(*resultString_));
source->triggerConvertToCompressedSourceFromTask(std::move(*resultString_));
}
}
@ -2524,21 +2546,23 @@ bool ScriptSource::xdrFinalizeEncoder(JS::TranscodeBuffer& buffer) {
}
template <typename Unit>
MOZ_MUST_USE bool ScriptSource::initializeUncompressedSource(
MOZ_MUST_USE bool ScriptSource::initializeUnretrievableUncompressedSource(
JSContext* cx, EntryUnits<Unit>&& source, size_t length) {
MOZ_ASSERT(data.is<Missing>(), "must be initializing a fresh ScriptSource");
return setUncompressedSourceHelper(cx, std::move(source), length);
return setUncompressedSourceHelper(cx, std::move(source), length,
SourceRetrievable::No);
}
template <typename Unit>
struct SourceDecoder {
struct UnretrievableSourceDecoder {
XDRState<XDR_DECODE>* const xdr_;
ScriptSource* const scriptSource_;
const uint32_t uncompressedLength_;
public:
SourceDecoder(XDRState<XDR_DECODE>* xdr, ScriptSource* scriptSource,
uint32_t uncompressedLength)
UnretrievableSourceDecoder(XDRState<XDR_DECODE>* xdr,
ScriptSource* scriptSource,
uint32_t uncompressedLength)
: xdr_(xdr),
scriptSource_(scriptSource),
uncompressedLength_(uncompressedLength) {}
@ -2552,7 +2576,7 @@ struct SourceDecoder {
MOZ_TRY(xdr_->codeChars(sourceUnits.get(), uncompressedLength_));
if (!scriptSource_->initializeUncompressedSource(
if (!scriptSource_->initializeUnretrievableUncompressedSource(
xdr_->cx(), std::move(sourceUnits), uncompressedLength_)) {
return xdr_->fail(JS::TranscodeResult_Throw);
}
@ -2564,34 +2588,35 @@ struct SourceDecoder {
namespace js {
template <>
XDRResult ScriptSource::xdrUncompressedSource<XDR_DECODE>(
XDRResult ScriptSource::xdrUnretrievableUncompressedSource<XDR_DECODE>(
XDRState<XDR_DECODE>* xdr, uint8_t sourceCharSize,
uint32_t uncompressedLength) {
MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);
if (sourceCharSize == 1) {
SourceDecoder<Utf8Unit> decoder(xdr, this, uncompressedLength);
UnretrievableSourceDecoder<Utf8Unit> decoder(xdr, this, uncompressedLength);
return decoder.decode();
}
SourceDecoder<char16_t> decoder(xdr, this, uncompressedLength);
UnretrievableSourceDecoder<char16_t> decoder(xdr, this, uncompressedLength);
return decoder.decode();
}
} // namespace js
template <typename Unit>
struct SourceEncoder {
struct UnretrievableSourceEncoder {
XDRState<XDR_ENCODE>* const xdr_;
ScriptSource* const source_;
const uint32_t uncompressedLength_;
SourceEncoder(XDRState<XDR_ENCODE>* xdr, ScriptSource* source,
uint32_t uncompressedLength)
UnretrievableSourceEncoder(XDRState<XDR_ENCODE>* xdr, ScriptSource* source,
uint32_t uncompressedLength)
: xdr_(xdr), source_(source), uncompressedLength_(uncompressedLength) {}
XDRResult encode() {
Unit* sourceUnits = const_cast<Unit*>(source_->uncompressedData<Unit>());
Unit* sourceUnits =
const_cast<Unit*>(source_->uncompressedData<Unit>()->units());
return xdr_->codeChars(sourceUnits, uncompressedLength_);
}
@ -2600,17 +2625,17 @@ struct SourceEncoder {
namespace js {
template <>
XDRResult ScriptSource::xdrUncompressedSource<XDR_ENCODE>(
XDRResult ScriptSource::xdrUnretrievableUncompressedSource<XDR_ENCODE>(
XDRState<XDR_ENCODE>* xdr, uint8_t sourceCharSize,
uint32_t uncompressedLength) {
MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);
if (sourceCharSize == 1) {
SourceEncoder<Utf8Unit> encoder(xdr, this, uncompressedLength);
UnretrievableSourceEncoder<Utf8Unit> encoder(xdr, this, uncompressedLength);
return encoder.encode();
}
SourceEncoder<char16_t> encoder(xdr, this, uncompressedLength);
UnretrievableSourceEncoder<char16_t> encoder(xdr, this, uncompressedLength);
return encoder.encode();
}
@ -2619,73 +2644,52 @@ XDRResult ScriptSource::xdrUncompressedSource<XDR_ENCODE>(
template <typename Unit, XDRMode mode>
/* static */
XDRResult ScriptSource::codeUncompressedData(XDRState<mode>* const xdr,
ScriptSource* const ss,
bool retrievable) {
ScriptSource* const ss) {
static_assert(std::is_same<Unit, Utf8Unit>::value ||
std::is_same<Unit, char16_t>::value,
"should handle UTF-8 and UTF-16");
if (mode == XDR_ENCODE) {
MOZ_ASSERT(ss->data.is<Uncompressed<Unit>>());
MOZ_ASSERT(ss->isUncompressed<Unit>());
} else {
MOZ_ASSERT(ss->data.is<Missing>());
}
MOZ_ASSERT(retrievable == ss->sourceRetrievable());
if (retrievable) {
// It's unnecessary to code uncompressed data if it can just be retrieved
// using the source hook.
if (mode == XDR_DECODE) {
ss->data = SourceType(Retrievable<Unit>());
}
return Ok();
}
uint32_t uncompressedLength;
if (mode == XDR_ENCODE) {
uncompressedLength = ss->data.as<Uncompressed<Unit>>().length();
uncompressedLength = ss->uncompressedData<Unit>()->length();
}
MOZ_TRY(xdr->codeUint32(&uncompressedLength));
return ss->xdrUncompressedSource(xdr, sizeof(Unit), uncompressedLength);
return ss->xdrUnretrievableUncompressedSource(xdr, sizeof(Unit),
uncompressedLength);
}
template <typename Unit, XDRMode mode>
/* static */
XDRResult ScriptSource::codeCompressedData(XDRState<mode>* const xdr,
ScriptSource* const ss,
bool retrievable) {
ScriptSource* const ss) {
static_assert(std::is_same<Unit, Utf8Unit>::value ||
std::is_same<Unit, char16_t>::value,
"should handle UTF-8 and UTF-16");
if (mode == XDR_ENCODE) {
MOZ_ASSERT(ss->data.is<Compressed<Unit>>());
MOZ_ASSERT(ss->isCompressed<Unit>());
} else {
MOZ_ASSERT(ss->data.is<Missing>());
}
MOZ_ASSERT(retrievable == ss->sourceRetrievable());
if (retrievable) {
// It's unnecessary to code compressed data if it can just be retrieved
// using the source hook.
if (mode == XDR_DECODE) {
ss->data = SourceType(Retrievable<Unit>());
}
return Ok();
}
uint32_t uncompressedLength;
if (mode == XDR_ENCODE) {
uncompressedLength = ss->data.as<Compressed<Unit>>().uncompressedLength;
uncompressedLength = ss->data.as<Compressed<Unit, SourceRetrievable::No>>()
.uncompressedLength;
}
MOZ_TRY(xdr->codeUint32(&uncompressedLength));
uint32_t compressedLength;
if (mode == XDR_ENCODE) {
compressedLength = ss->data.as<Compressed<Unit>>().raw.length();
compressedLength =
ss->data.as<Compressed<Unit, SourceRetrievable::No>>().raw.length();
}
MOZ_TRY(xdr->codeUint32(&compressedLength));
@ -2697,20 +2701,36 @@ XDRResult ScriptSource::codeCompressedData(XDRState<mode>* const xdr,
}
MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
if (!ss->initializeWithCompressedSource<Unit>(xdr->cx(), std::move(bytes),
compressedLength,
uncompressedLength)) {
if (!ss->initializeWithUnretrievableCompressedSource<Unit>(
xdr->cx(), std::move(bytes), compressedLength,
uncompressedLength)) {
return xdr->fail(JS::TranscodeResult_Throw);
}
} else {
void* bytes =
const_cast<char*>(ss->data.as<Compressed<Unit>>().raw.chars());
void* bytes = const_cast<char*>(ss->compressedData<Unit>()->raw.chars());
MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
}
return Ok();
}
template <typename Unit,
template <typename U, SourceRetrievable CanRetrieve> class Data,
XDRMode mode>
/* static */
void ScriptSource::codeRetrievable(ScriptSource* const ss) {
static_assert(std::is_same<Unit, Utf8Unit>::value ||
std::is_same<Unit, char16_t>::value,
"should handle UTF-8 and UTF-16");
if (mode == XDR_ENCODE) {
MOZ_ASSERT((ss->data.is<Data<Unit, SourceRetrievable::Yes>>()));
} else {
MOZ_ASSERT(ss->data.is<Missing>());
ss->data = SourceType(Retrievable<Unit>());
}
}
template <XDRMode mode>
/* static */
XDRResult ScriptSource::codeBinASTData(XDRState<mode>* const xdr,
@ -2880,25 +2900,17 @@ template <XDRMode mode>
/* static */
XDRResult ScriptSource::xdrData(XDRState<mode>* const xdr,
ScriptSource* const ss) {
// Retrievability is kept outside |ScriptSource::data| (and not solely as
// distinct variant types within it) because retrievable compressed or
// uncompressed data need not be XDR'd.
uint8_t retrievable;
if (mode == XDR_ENCODE) {
retrievable = ss->sourceRetrievable_;
}
MOZ_TRY(xdr->codeUint8(&retrievable));
if (mode == XDR_DECODE) {
ss->sourceRetrievable_ = retrievable != 0;
}
// The order here corresponds to the type order in |ScriptSource::SourceType|
// for simplicity, but it isn't truly necessary that it do so.
// so number->internal Variant tag is a no-op.
enum class DataType {
CompressedUtf8,
UncompressedUtf8,
CompressedUtf16,
UncompressedUtf16,
CompressedUtf8Retrievable,
UncompressedUtf8Retrievable,
CompressedUtf8NotRetrievable,
UncompressedUtf8NotRetrievable,
CompressedUtf16Retrievable,
UncompressedUtf16Retrievable,
CompressedUtf16NotRetrievable,
UncompressedUtf16NotRetrievable,
RetrievableUtf8,
RetrievableUtf16,
Missing,
@ -2914,17 +2926,33 @@ XDRResult ScriptSource::xdrData(XDRState<mode>* const xdr,
// it, then switch on it (and ignore the |Variant::match| API).
class XDRDataTag {
public:
DataType operator()(const Compressed<Utf8Unit>&) {
return DataType::CompressedUtf8;
DataType operator()(const Compressed<Utf8Unit, SourceRetrievable::Yes>&) {
return DataType::CompressedUtf8Retrievable;
}
DataType operator()(const Uncompressed<Utf8Unit>&) {
return DataType::UncompressedUtf8;
DataType operator()(
const Uncompressed<Utf8Unit, SourceRetrievable::Yes>&) {
return DataType::UncompressedUtf8Retrievable;
}
DataType operator()(const Compressed<char16_t>&) {
return DataType::CompressedUtf16;
DataType operator()(const Compressed<Utf8Unit, SourceRetrievable::No>&) {
return DataType::CompressedUtf8NotRetrievable;
}
DataType operator()(const Uncompressed<char16_t>&) {
return DataType::UncompressedUtf16;
DataType operator()(
const Uncompressed<Utf8Unit, SourceRetrievable::No>&) {
return DataType::UncompressedUtf8NotRetrievable;
}
DataType operator()(const Compressed<char16_t, SourceRetrievable::Yes>&) {
return DataType::CompressedUtf16Retrievable;
}
DataType operator()(
const Uncompressed<char16_t, SourceRetrievable::Yes>&) {
return DataType::UncompressedUtf16Retrievable;
}
DataType operator()(const Compressed<char16_t, SourceRetrievable::No>&) {
return DataType::CompressedUtf16NotRetrievable;
}
DataType operator()(
const Uncompressed<char16_t, SourceRetrievable::No>&) {
return DataType::UncompressedUtf16NotRetrievable;
}
DataType operator()(const Retrievable<Utf8Unit>&) {
return DataType::RetrievableUtf8;
@ -2952,17 +2980,33 @@ XDRResult ScriptSource::xdrData(XDRState<mode>* const xdr,
}
switch (tag) {
case DataType::CompressedUtf8:
return ScriptSource::codeCompressedData<Utf8Unit>(xdr, ss, retrievable);
case DataType::CompressedUtf8Retrievable:
ScriptSource::codeRetrievable<Utf8Unit, Compressed, mode>(ss);
return Ok();
case DataType::UncompressedUtf8:
return ScriptSource::codeUncompressedData<Utf8Unit>(xdr, ss, retrievable);
case DataType::CompressedUtf8NotRetrievable:
return ScriptSource::codeCompressedData<Utf8Unit>(xdr, ss);
case DataType::CompressedUtf16:
return ScriptSource::codeCompressedData<char16_t>(xdr, ss, retrievable);
case DataType::UncompressedUtf8Retrievable:
ScriptSource::codeRetrievable<Utf8Unit, Uncompressed, mode>(ss);
return Ok();
case DataType::UncompressedUtf16:
return ScriptSource::codeUncompressedData<char16_t>(xdr, ss, retrievable);
case DataType::UncompressedUtf8NotRetrievable:
return ScriptSource::codeUncompressedData<Utf8Unit>(xdr, ss);
case DataType::CompressedUtf16Retrievable:
ScriptSource::codeRetrievable<char16_t, Compressed, mode>(ss);
return Ok();
case DataType::CompressedUtf16NotRetrievable:
return ScriptSource::codeCompressedData<char16_t>(xdr, ss);
case DataType::UncompressedUtf16Retrievable:
ScriptSource::codeRetrievable<char16_t, Uncompressed, mode>(ss);
return Ok();
case DataType::UncompressedUtf16NotRetrievable:
return ScriptSource::codeUncompressedData<char16_t>(xdr, ss);
case DataType::Missing: {
MOZ_ASSERT(ss->data.is<Missing>(),
@ -2974,11 +3018,11 @@ XDRResult ScriptSource::xdrData(XDRState<mode>* const xdr,
}
case DataType::RetrievableUtf8:
codeRetrievableData<Utf8Unit, mode>(ss);
ScriptSource::codeRetrievableData<Utf8Unit, mode>(ss);
return Ok();
case DataType::RetrievableUtf16:
codeRetrievableData<char16_t, mode>(ss);
ScriptSource::codeRetrievableData<char16_t, mode>(ss);
return Ok();
case DataType::BinAST:

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

@ -485,6 +485,11 @@ struct SourceTypeTraits<char16_t> {
class ScriptSourceHolder;
// Retrievable source can be retrieved using the source hook (and therefore
// need not be XDR'd, can be discarded if desired because it can always be
// reconstituted later, etc.).
enum class SourceRetrievable { Yes, No };
class ScriptSource {
friend class SourceCompressionTask;
@ -533,13 +538,12 @@ class ScriptSource {
// Indicate which field in the |data| union is active.
// Uncompressed source text.
template <typename Unit>
class Uncompressed {
class UncompressedData {
typename SourceTypeTraits<Unit>::SharedImmutableString string_;
public:
explicit Uncompressed(
explicit UncompressedData(
typename SourceTypeTraits<Unit>::SharedImmutableString str)
: string_(std::move(str)) {}
@ -548,18 +552,35 @@ class ScriptSource {
size_t length() const { return string_.length(); }
};
// Compressed source text.
// Uncompressed source text.
template <typename Unit, SourceRetrievable CanRetrieve>
class Uncompressed : public UncompressedData<Unit> {
using Base = UncompressedData<Unit>;
public:
using Base::Base;
};
template <typename Unit>
struct Compressed {
struct CompressedData {
// Single-byte compressed text, regardless whether the original text
// was single-byte or two-byte.
SharedImmutableString raw;
size_t uncompressedLength;
Compressed(SharedImmutableString raw, size_t uncompressedLength)
CompressedData(SharedImmutableString raw, size_t uncompressedLength)
: raw(std::move(raw)), uncompressedLength(uncompressedLength) {}
};
// Compressed source text.
template <typename Unit, SourceRetrievable CanRetrieve>
struct Compressed : public CompressedData<Unit> {
using Base = CompressedData<Unit>;
public:
using Base::Base;
};
// Source that can be retrieved using the registered source hook. |Unit|
// records the source type so that source-text coordinates in functions and
// scripts that depend on this |ScriptSource| are correct.
@ -588,17 +609,27 @@ class ScriptSource {
};
using SourceType =
mozilla::Variant<Compressed<mozilla::Utf8Unit>,
Uncompressed<mozilla::Utf8Unit>, Compressed<char16_t>,
Uncompressed<char16_t>, Retrievable<mozilla::Utf8Unit>,
Retrievable<char16_t>, Missing, BinAST>;
mozilla::Variant<Compressed<mozilla::Utf8Unit, SourceRetrievable::Yes>,
Uncompressed<mozilla::Utf8Unit, SourceRetrievable::Yes>,
Compressed<mozilla::Utf8Unit, SourceRetrievable::No>,
Uncompressed<mozilla::Utf8Unit, SourceRetrievable::No>,
Compressed<char16_t, SourceRetrievable::Yes>,
Uncompressed<char16_t, SourceRetrievable::Yes>,
Compressed<char16_t, SourceRetrievable::No>,
Uncompressed<char16_t, SourceRetrievable::No>,
Retrievable<mozilla::Utf8Unit>, Retrievable<char16_t>,
Missing, BinAST>;
SourceType data;
// If the GC attempts to call convertToCompressedSource with PinnedUnits
// present, the first PinnedUnits (that is, bottom of the stack) will set
// the compressed chars upon destruction.
// If the GC calls triggerConvertToCompressedSource with PinnedUnits present,
// the first PinnedUnits (that is, bottom of the stack) will install the
// compressed chars upon destruction.
//
// Retrievability isn't part of the type here because uncompressed->compressed
// transitions must preserve existing retrievability.
PinnedUnitsBase* pinnedUnitsStack_;
mozilla::MaybeOneOf<Compressed<mozilla::Utf8Unit>, Compressed<char16_t>>
mozilla::MaybeOneOf<CompressedData<mozilla::Utf8Unit>,
CompressedData<char16_t>>
pendingCompressed_;
// The filename of this script.
@ -671,17 +702,6 @@ class ScriptSource {
mozilla::recordreplay::Behavior::DontPreserve>
idCount_;
// If this field is true, we can call JSRuntime::sourceHook to load the source
// on demand. Thus if this contains compressed/uncompressed data, we don't
// have to preserve it while we're not using it, because we can use the source
// hook to load it when we need it.
//
// This field is always true for retrievable source. It *may* be true for
// compressed/uncompressed source (if retrievable source was rewritten to
// compressed/uncompressed source loaded using the source hook). It is always
// false for missing or BinAST source.
bool sourceRetrievable_ : 1;
bool hasIntroductionOffset_ : 1;
bool containsAsmJS_ : 1;
@ -693,15 +713,12 @@ class ScriptSource {
// Return a string containing the chars starting at |begin| and ending at
// |begin + len|.
//
// Warning: this is *not* GC-safe! Any chars to be handed out should use
// Warning: this is *not* GC-safe! Any chars to be handed out must use
// PinnedUnits. See comment below.
template <typename Unit>
const Unit* units(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp,
size_t begin, size_t len);
template <typename Unit>
void movePendingCompressedSource();
public:
// When creating a JSString* from TwoByte source characters, we don't try to
// to deflate to Latin1 for longer strings, because this can be slow.
@ -721,7 +738,6 @@ class ScriptSource {
introductionType_(nullptr),
xdrEncoder_(nullptr),
id_(++idCount_),
sourceRetrievable_(false),
hasIntroductionOffset_(false),
containsAsmJS_(false) {}
@ -760,7 +776,6 @@ class ScriptSource {
const JS::ReadOnlyCompileOptions& options,
JS::SourceText<Unit>& srcBuf);
bool sourceRetrievable() const { return sourceRetrievable_; }
bool hasSourceText() const {
return hasUncompressedSource() || hasCompressedSource();
}
@ -778,10 +793,14 @@ class ScriptSource {
private:
template <typename Unit>
struct UncompressedDataMatcher {
const Unit* operator()(const Uncompressed<Unit>& u) { return u.units(); }
template <SourceRetrievable CanRetrieve>
const UncompressedData<Unit>* operator()(
const Uncompressed<Unit, CanRetrieve>& u) {
return &u;
}
template <typename T>
const Unit* operator()(const T&) {
const UncompressedData<Unit>* operator()(const T&) {
MOZ_CRASH(
"attempting to access uncompressed data in a ScriptSource not "
"containing it");
@ -791,19 +810,21 @@ class ScriptSource {
public:
template <typename Unit>
const Unit* uncompressedData() {
const UncompressedData<Unit>* uncompressedData() {
return data.match(UncompressedDataMatcher<Unit>());
}
private:
template <typename Unit>
struct CompressedDataMatcher {
char* operator()(const Compressed<Unit>& c) {
return const_cast<char*>(c.raw.chars());
template <SourceRetrievable CanRetrieve>
const CompressedData<Unit>* operator()(
const Compressed<Unit, CanRetrieve>& c) {
return &c;
}
template <typename T>
char* operator()(const T&) {
const CompressedData<Unit>* operator()(const T&) {
MOZ_CRASH(
"attempting to access compressed data in a ScriptSource not "
"containing it");
@ -813,7 +834,7 @@ class ScriptSource {
public:
template <typename Unit>
char* compressedData() {
const CompressedData<Unit>* compressedData() {
return data.match(CompressedDataMatcher<Unit>());
}
@ -837,13 +858,13 @@ class ScriptSource {
private:
struct HasUncompressedSource {
template <typename Unit>
bool operator()(const Uncompressed<Unit>&) {
template <typename Unit, SourceRetrievable CanRetrieve>
bool operator()(const Uncompressed<Unit, CanRetrieve>&) {
return true;
}
template <typename Unit>
bool operator()(const Compressed<Unit>&) {
template <typename Unit, SourceRetrievable CanRetrieve>
bool operator()(const Compressed<Unit, CanRetrieve>&) {
return false;
}
@ -862,53 +883,84 @@ class ScriptSource {
return data.match(HasUncompressedSource());
}
private:
template <typename Unit>
bool uncompressedSourceIs() const {
MOZ_ASSERT(hasUncompressedSource());
return data.is<Uncompressed<Unit>>();
struct IsUncompressed {
template <SourceRetrievable CanRetrieve>
bool operator()(const Uncompressed<Unit, CanRetrieve>&) {
return true;
}
template <typename T>
bool operator()(const T&) {
return false;
}
};
public:
template <typename Unit>
bool isUncompressed() const {
return data.match(IsUncompressed<Unit>());
}
private:
struct HasCompressedSource {
template <typename Unit>
bool operator()(const Compressed<Unit>&) {
template <typename Unit, SourceRetrievable CanRetrieve>
bool operator()(const Compressed<Unit, CanRetrieve>&) {
return true;
}
template <typename Unit>
bool operator()(const Uncompressed<Unit>&) {
template <typename T>
bool operator()(const T&) {
return false;
}
template <typename Unit>
bool operator()(const Retrievable<Unit>&) {
return false;
}
bool operator()(const BinAST&) { return false; }
bool operator()(const Missing&) { return false; }
};
public:
bool hasCompressedSource() const { return data.match(HasCompressedSource()); }
private:
template <typename Unit>
bool compressedSourceIs() const {
MOZ_ASSERT(hasCompressedSource());
return data.is<Compressed<Unit>>();
struct IsCompressed {
template <SourceRetrievable CanRetrieve>
bool operator()(const Compressed<Unit, CanRetrieve>&) {
return true;
}
template <typename T>
bool operator()(const T&) {
return false;
}
};
public:
template <typename Unit>
bool isCompressed() const {
return data.match(IsCompressed<Unit>());
}
private:
template <typename Unit>
struct SourceTypeMatcher {
template <template <typename C> class Data>
bool operator()(const Data<Unit>&) {
template <template <typename C, SourceRetrievable R> class Data,
SourceRetrievable CanRetrieve>
bool operator()(const Data<Unit, CanRetrieve>&) {
return true;
}
template <template <typename C> class Data, typename NotUnit>
bool operator()(const Data<NotUnit>&) {
template <template <typename C, SourceRetrievable R> class Data,
typename NotUnit, SourceRetrievable CanRetrieve>
bool operator()(const Data<NotUnit, CanRetrieve>&) {
return false;
}
bool operator()(const Retrievable<Unit>&) {
MOZ_CRASH("source type only applies where actual text is available");
return false;
}
template <typename NotUnit>
bool operator()(const Retrievable<NotUnit>&) {
return false;
}
@ -931,13 +983,13 @@ class ScriptSource {
private:
struct UncompressedLengthMatcher {
template <typename Unit>
size_t operator()(const Uncompressed<Unit>& u) {
template <typename Unit, SourceRetrievable CanRetrieve>
size_t operator()(const Uncompressed<Unit, CanRetrieve>& u) {
return u.length();
}
template <typename Unit>
size_t operator()(const Compressed<Unit>& u) {
template <typename Unit, SourceRetrievable CanRetrieve>
size_t operator()(const Compressed<Unit, CanRetrieve>& u) {
return u.uncompressedLength;
}
@ -982,14 +1034,14 @@ class ScriptSource {
template <typename Unit>
MOZ_MUST_USE bool setUncompressedSourceHelper(JSContext* cx,
EntryUnits<Unit>&& source,
size_t length);
size_t length,
SourceRetrievable retrievable);
public:
// Initialize a fresh |ScriptSource| with uncompressed source.
// Initialize a fresh |ScriptSource| with unretrievable, uncompressed source.
template <typename Unit>
MOZ_MUST_USE bool initializeUncompressedSource(JSContext* cx,
EntryUnits<Unit>&& source,
size_t length);
MOZ_MUST_USE bool initializeUnretrievableUncompressedSource(
JSContext* cx, EntryUnits<Unit>&& source, size_t length);
// Set the retrieved source for a |ScriptSource| whose source was recorded as
// missing but retrievable.
@ -999,20 +1051,24 @@ class ScriptSource {
MOZ_MUST_USE bool tryCompressOffThread(JSContext* cx);
// Convert this ScriptSource from storing uncompressed source of the given
// type, to storing compressed source. (Raw compressed source is always
// single-byte; |Unit| just records the encoding of the uncompressed source.)
// *Trigger* the conversion of this ScriptSource from containing uncompressed
// |Unit|-encoded source to containing compressed source. Conversion may not
// be complete when this function returns: it'll be delayed if there's ongoing
// use of the uncompressed source via |PinnedUnits|, in which case conversion
// won't occur until the outermost |PinnedUnits| is destroyed.
//
// Compressed source is in bytes, no matter that |Unit| might be |char16_t|.
// |sourceLength| is the length in code units (not bytes) of the uncompressed
// source.
template <typename Unit>
void convertToCompressedSource(SharedImmutableString compressed,
size_t sourceLength);
void triggerConvertToCompressedSource(SharedImmutableString compressed,
size_t sourceLength);
// Initialize a fresh ScriptSource as containing compressed source of the
// indicated original encoding.
// Initialize a fresh ScriptSource as containing unretrievable compressed
// source of the indicated original encoding.
template <typename Unit>
MOZ_MUST_USE bool initializeWithCompressedSource(JSContext* cx,
UniqueChars&& raw,
size_t rawLength,
size_t sourceLength);
MOZ_MUST_USE bool initializeWithUnretrievableCompressedSource(
JSContext* cx, UniqueChars&& raw, size_t rawLength, size_t sourceLength);
#if defined(JS_BUILD_BINAST)
@ -1031,22 +1087,22 @@ class ScriptSource {
private:
void performTaskWork(SourceCompressionTask* task);
struct ConvertToCompressedSourceFromTask {
struct TriggerConvertToCompressedSourceFromTask {
ScriptSource* const source_;
SharedImmutableString& compressed_;
ConvertToCompressedSourceFromTask(ScriptSource* source,
SharedImmutableString& compressed)
TriggerConvertToCompressedSourceFromTask(ScriptSource* source,
SharedImmutableString& compressed)
: source_(source), compressed_(compressed) {}
template <typename Unit>
void operator()(const Uncompressed<Unit>&) {
source_->convertToCompressedSource<Unit>(std::move(compressed_),
source_->length());
template <typename Unit, SourceRetrievable CanRetrieve>
void operator()(const Uncompressed<Unit, CanRetrieve>&) {
source_->triggerConvertToCompressedSource<Unit>(std::move(compressed_),
source_->length());
}
template <typename Unit>
void operator()(const Compressed<Unit>&) {
template <typename Unit, SourceRetrievable CanRetrieve>
void operator()(const Compressed<Unit, CanRetrieve>&) {
MOZ_CRASH(
"can't set compressed source when source is already compressed -- "
"ScriptSource::tryCompressOffThread shouldn't have queued up this "
@ -1070,7 +1126,15 @@ class ScriptSource {
}
};
void convertToCompressedSourceFromTask(SharedImmutableString compressed);
template <typename Unit>
void convertToCompressedSource(SharedImmutableString compressed,
size_t uncompressedLength);
template <typename Unit>
void performDelayedConvertToCompressedSource();
void triggerConvertToCompressedSourceFromTask(
SharedImmutableString compressed);
private:
// It'd be better to make this function take <XDRMode, Unit>, as both
@ -1079,9 +1143,8 @@ class ScriptSource {
// we'd need template function partial specialization to hold XDRMode
// constant while varying Unit, so that idea's no dice.
template <XDRMode mode>
MOZ_MUST_USE XDRResult xdrUncompressedSource(XDRState<mode>* xdr,
uint8_t sourceCharSize,
uint32_t uncompressedLength);
MOZ_MUST_USE XDRResult xdrUnretrievableUncompressedSource(
XDRState<mode>* xdr, uint8_t sourceCharSize, uint32_t uncompressedLength);
public:
MOZ_MUST_USE bool setFilename(JSContext* cx, const char* filename);
@ -1162,15 +1225,18 @@ class ScriptSource {
}
private:
template <typename Unit,
template <typename U, SourceRetrievable CanRetrieve> class Data,
XDRMode mode>
static void codeRetrievable(ScriptSource* ss);
template <typename Unit, XDRMode mode>
static MOZ_MUST_USE XDRResult codeUncompressedData(XDRState<mode>* const xdr,
ScriptSource* const ss,
bool retrievable);
ScriptSource* const ss);
template <typename Unit, XDRMode mode>
static MOZ_MUST_USE XDRResult codeCompressedData(XDRState<mode>* const xdr,
ScriptSource* const ss,
bool retrievable);
ScriptSource* const ss);
template <XDRMode mode>
static MOZ_MUST_USE XDRResult codeBinASTData(XDRState<mode>* const xdr,

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

@ -120,43 +120,20 @@ class nsPrintJob final : public nsIObserver,
nsresult GetDocumentName(nsAString& aDocName);
already_AddRefed<nsIPrintSettings> GetCurrentPrintSettings();
// The setters here also update the DocViewer
void SetIsPrinting(bool aIsPrinting);
bool GetIsPrinting() { return mIsDoingPrinting; }
void SetIsPrintPreview(bool aIsPrintPreview);
bool GetIsPrintPreview() { return mIsDoingPrintPreview; }
bool GetIsCreatingPrintPreview() { return mIsCreatingPrintPreview; }
// This enum tells indicates what the default should be for the title
// if the title from the document is null
enum eDocTitleDefault { eDocTitleDefBlank, eDocTitleDefURLDoc };
void Destroy();
void DestroyPrintingData();
nsresult GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount);
//
// The following three methods are used for printing...
//
nsresult DocumentReadyForPrinting();
nsresult GetSelectionDocument(nsIDeviceContextSpec* aDevSpec,
mozilla::dom::Document** aNewDoc);
nsresult SetupToPrintContent();
nsresult EnablePOsForPrinting();
nsPrintObject* FindSmallestSTF();
bool PrintDocContent(const mozilla::UniquePtr<nsPrintObject>& aPO,
nsresult& aStatus);
nsresult DoPrint(const mozilla::UniquePtr<nsPrintObject>& aPO);
void SetPrintPO(nsPrintObject* aPO, bool aPrint);
void TurnScriptingOn(bool aDoTurnOn);
bool CheckDocumentForPPCaching();
/**
* Filters out certain user events while Print Preview is open to prevent
* the user from interacting with the Print Preview document and breaking
* printing invariants.
*/
void SuppressPrintPreviewUserEvents();
// nsIDocumentViewerPrint Printing Methods:
/**
* Checks to see if the document this print engine is associated with has any
@ -168,7 +145,48 @@ class nsPrintJob final : public nsIObserver,
bool PrintPage(nsPrintObject* aPOect, bool& aInRange);
bool DonePrintingPages(nsPrintObject* aPO, nsresult aResult);
//---------------------------------------------------------------------
nsresult CleanupOnFailure(nsresult aResult, bool aIsPrinting);
// If FinishPrintPreview() fails, caller may need to reset the state of the
// object, for example by calling CleanupOnFailure().
nsresult FinishPrintPreview();
void FirePrintingErrorEvent(nsresult aPrintError);
bool CheckBeforeDestroy() const { return mPrt && mPrt->mPreparingForPrint; }
mozilla::PresShell* GetPrintPreviewPresShell() {
return mPrtPreview->mPrintObject->mPresShell;
}
float GetPrintPreviewScale() {
return mPrtPreview->mPrintObject->mPresContext->GetPrintPreviewScale();
}
nsresult Cancel();
void Destroy();
void DestroyPrintingData();
private:
nsPrintJob& operator=(const nsPrintJob& aOther) = delete;
~nsPrintJob();
nsresult DocumentReadyForPrinting();
nsresult SetupToPrintContent();
nsresult EnablePOsForPrinting();
nsPrintObject* FindSmallestSTF();
bool PrintDocContent(const mozilla::UniquePtr<nsPrintObject>& aPO,
nsresult& aStatus);
nsresult DoPrint(const mozilla::UniquePtr<nsPrintObject>& aPO);
void SetPrintPO(nsPrintObject* aPO, bool aPrint);
/**
* Filters out certain user events while Print Preview is open to prevent
* the user from interacting with the Print Preview document and breaking
* printing invariants.
*/
void SuppressPrintPreviewUserEvents();
nsresult ReflowDocList(const mozilla::UniquePtr<nsPrintObject>& aPO,
bool aSetPixelScale);
@ -177,10 +195,6 @@ class nsPrintJob final : public nsIObserver,
void CalcNumPrintablePages(int32_t& aNumPages);
void ShowPrintProgress(bool aIsForPrinting, bool& aDoNotify);
nsresult CleanupOnFailure(nsresult aResult, bool aIsPrinting);
// If FinishPrintPreview() fails, caller may need to reset the state of the
// object, for example by calling CleanupOnFailure().
nsresult FinishPrintPreview();
void SetURLAndTitleOnProgressParams(
const mozilla::UniquePtr<nsPrintObject>& aPO,
nsIPrintProgressParams* aParams);
@ -188,10 +202,6 @@ class nsPrintJob final : public nsIObserver,
bool IsThereARangeSelection(nsPIDOMWindowOuter* aDOMWin);
void FirePrintingErrorEvent(nsresult aPrintError);
//---------------------------------------------------------------------
// Timer Methods
nsresult StartPagePrintTimer(const mozilla::UniquePtr<nsPrintObject>& aPO);
bool IsWindowsInOurSubTree(nsPIDOMWindowOuter* aDOMWindow) const;
@ -206,30 +216,6 @@ class nsPrintJob final : public nsIObserver,
nsAString& aTitle, nsAString& aURLStr,
eDocTitleDefault aDefType);
bool CheckBeforeDestroy() const { return mPrt && mPrt->mPreparingForPrint; }
nsresult Cancel();
mozilla::PresShell* GetPrintPreviewPresShell() {
return mPrtPreview->mPrintObject->mPresShell;
}
float GetPrintPreviewScale() {
return mPrtPreview->mPrintObject->mPresContext->GetPrintPreviewScale();
}
// These calls also update the DocViewer
void SetIsPrinting(bool aIsPrinting);
bool GetIsPrinting() { return mIsDoingPrinting; }
void SetIsPrintPreview(bool aIsPrintPreview);
bool GetIsPrintPreview() { return mIsDoingPrintPreview; }
bool GetIsCreatingPrintPreview() { return mIsCreatingPrintPreview; }
private:
nsPrintJob& operator=(const nsPrintJob& aOther) = delete;
~nsPrintJob();
nsresult CommonPrint(bool aIsPrintPreview, nsIPrintSettings* aPrintSettings,
nsIWebProgressListener* aWebProgressListener,
mozilla::dom::Document* aSourceDoc);

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

@ -600,7 +600,7 @@ class MozBrowser extends MozElements.MozElementMixin(XULFrameElement) {
set characterSet(val) {
if (this.isRemoteBrowser) {
this.messageManager.sendAsyncMessage("UpdateCharacterSet", { value: val });
this.sendMessageToActor("UpdateCharacterSet", { value: val }, "BrowserTab");
this._characterSet = val;
} else {
this.docShell.charset = val;
@ -1862,6 +1862,41 @@ class MozBrowser extends MozElements.MozElementMixin(XULFrameElement) {
this.docShell.getContentBlockingLog() :
Promise.reject("docshell isn't available");
}
// Send an asynchronous message to the remote child via an actor.
// Note: use this only for messages through an actor. For old-style
// messages, use the message manager. If 'all' is true, then send
// a message to all descendant processes.
sendMessageToActor(messageName, args, actorName, all) {
if (!this.frameLoader) {
return;
}
let windowGlobal = this.browsingContext.currentWindowGlobal;
if (!windowGlobal) {
// Workaround for bug 1523638 where about:blank is loaded in a tab.
if (messageName == "Browser:AppTab") {
setTimeout(() => { this.sendMessageToActor(messageName, args, actorName); }, 0);
}
return;
}
function sendToChildren(browsingContext, checkRoot) {
let windowGlobal = browsingContext.currentWindowGlobal;
if (windowGlobal && (!checkRoot || windowGlobal.isProcessRoot)) {
windowGlobal.getActor(actorName).sendAsyncMessage(messageName, args);
}
if (all) {
let contexts = browsingContext.getChildren();
for (let context of contexts) {
sendToChildren(context, true);
}
}
}
sendToChildren(this.browsingContext, false);
}
}
MozXULElement.implementCustomInterface(MozBrowser, [Ci.nsIBrowser]);

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

@ -178,15 +178,15 @@ class NS_NO_VTABLE nsCycleCollectionParticipant {
// outgoing edges at all in the cycle collection graph or they know for sure
// they're alive _and_ none of their outgoing edges are to gray (in the GC
// sense) gcthings. See also nsWrapperCache::HasNothingToTrace and
// nsWrapperCache::IsBlackAndDoesNotNeedTracing. The restriction on not
// having outgoing edges to gray gcthings is because if we _do_ have them that
// means we have a "strong" edge to a JS thing and since we're alive we need
// to trace through it and mark keep them alive. Outgoing edges to C++ things
// don't matter here, because the criteria for when a CC participant is
// considered alive are slightly different for JS and C++ things: JS things
// are only considered alive when reachable via an edge from a live thing,
// while C++ things are also considered alive when their refcount exceeds the
// number of edges via which they are reachable.
// nsWrapperCache::HasKnownLiveWrapperAndDoesNotNeedTracing. The restriction
// on not having outgoing edges to gray gcthings is because if we _do_ have
// them that means we have a "strong" edge to a JS thing and since we're alive
// we need to trace through it and mark keep them alive. Outgoing edges to
// C++ things don't matter here, because the criteria for when a CC
// participant is considered alive are slightly different for JS and C++
// things: JS things are only considered alive when reachable via an edge from
// a live thing, while C++ things are also considered alive when their
// refcount exceeds the number of edges via which they are reachable.
bool CanSkipInCC(void* aPtr) {
return mMightSkip ? CanSkipInCCReal(aPtr) : false;
}