зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland a=merge on a CLOSED TREE
This commit is contained in:
Коммит
51cd68a168
|
@ -5582,6 +5582,10 @@ nsBrowserAccess.prototype = {
|
|||
canClose() {
|
||||
return CanCloseWindow();
|
||||
},
|
||||
|
||||
get tabCount() {
|
||||
return gBrowser.tabs.length;
|
||||
},
|
||||
};
|
||||
|
||||
function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
|
||||
|
|
|
@ -65,7 +65,6 @@ const whitelist = {
|
|||
|
||||
// Telemetry
|
||||
"resource://gre/modules/TelemetryController.jsm", // bug 1470339
|
||||
"resource://gre/modules/MemoryTelemetry.jsm", // bug 1481812
|
||||
"resource://gre/modules/TelemetryUtils.jsm", // bug 1470339
|
||||
|
||||
// Extensions
|
||||
|
|
|
@ -212,7 +212,7 @@ add_task(async function test_hiddenNonShippingFieldsPreservedUponEdit() {
|
|||
|
||||
Object.assign(expected, PTU.Addresses.TimBR, {
|
||||
"given-name": "Timothy-edit",
|
||||
"name": "Timothy-edit João Berners-Lee",
|
||||
"name": "Timothy-edit Jo\u{00E3}o Berners-Lee",
|
||||
});
|
||||
let actual = await formAutofillStorage.addresses.get(prefilledGuids.address1GUID);
|
||||
isnot(actual.email, "", "Check email isn't empty");
|
||||
|
@ -335,7 +335,7 @@ add_task(async function test_hiddenNonBillingAddressFieldsPreservedUponEdit() {
|
|||
|
||||
Object.assign(expected, PTU.Addresses.TimBR, {
|
||||
"given-name": "Timothy-edit",
|
||||
"name": "Timothy-edit João Berners-Lee",
|
||||
"name": "Timothy-edit Jo\u{00E3}o Berners-Lee",
|
||||
});
|
||||
let actual = await formAutofillStorage.addresses.get(prefilledGuids.address1GUID);
|
||||
// timeLastModified changes and isn't relevant
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Cu.import loads jsm files based on ISO-Latin-1 for now (see bug 530257).
|
||||
// However, the references about name parts include multi-byte characters.
|
||||
// Thus, we use |loadSubScript| to load the references instead.
|
||||
const NAME_REFERENCES = "chrome://formautofill/content/nameReferences.js";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["FormAutofillNameUtils"];
|
||||
|
||||
ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
|
@ -16,12 +11,138 @@ ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
|
|||
// FormAutofillNameUtils is initially translated from
|
||||
// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util.cc?rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
|
||||
var FormAutofillNameUtils = {
|
||||
// Will be loaded from NAME_REFERENCES.
|
||||
NAME_PREFIXES: [],
|
||||
NAME_SUFFIXES: [],
|
||||
FAMILY_NAME_PREFIXES: [],
|
||||
COMMON_CJK_MULTI_CHAR_SURNAMES: [],
|
||||
KOREAN_MULTI_CHAR_SURNAMES: [],
|
||||
NAME_PREFIXES: [
|
||||
"1lt",
|
||||
"1st",
|
||||
"2lt",
|
||||
"2nd",
|
||||
"3rd",
|
||||
"admiral",
|
||||
"capt",
|
||||
"captain",
|
||||
"col",
|
||||
"cpt",
|
||||
"dr",
|
||||
"gen",
|
||||
"general",
|
||||
"lcdr",
|
||||
"lt",
|
||||
"ltc",
|
||||
"ltg",
|
||||
"ltjg",
|
||||
"maj",
|
||||
"major",
|
||||
"mg",
|
||||
"mr",
|
||||
"mrs",
|
||||
"ms",
|
||||
"pastor",
|
||||
"prof",
|
||||
"rep",
|
||||
"reverend",
|
||||
"rev",
|
||||
"sen",
|
||||
"st",
|
||||
],
|
||||
|
||||
NAME_SUFFIXES: [
|
||||
"b.a",
|
||||
"ba",
|
||||
"d.d.s",
|
||||
"dds",
|
||||
"i",
|
||||
"ii",
|
||||
"iii",
|
||||
"iv",
|
||||
"ix",
|
||||
"jr",
|
||||
"m.a",
|
||||
"m.d",
|
||||
"ma",
|
||||
"md",
|
||||
"ms",
|
||||
"ph.d",
|
||||
"phd",
|
||||
"sr",
|
||||
"v",
|
||||
"vi",
|
||||
"vii",
|
||||
"viii",
|
||||
"x",
|
||||
],
|
||||
|
||||
FAMILY_NAME_PREFIXES: [
|
||||
"d'",
|
||||
"de",
|
||||
"del",
|
||||
"der",
|
||||
"di",
|
||||
"la",
|
||||
"le",
|
||||
"mc",
|
||||
"san",
|
||||
"st",
|
||||
"ter",
|
||||
"van",
|
||||
"von",
|
||||
],
|
||||
|
||||
// The common and non-ambiguous CJK surnames (last names) that have more than
|
||||
// one character.
|
||||
COMMON_CJK_MULTI_CHAR_SURNAMES: [
|
||||
// Korean, taken from the list of surnames:
|
||||
// https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD%EC%9D%98_%EC%84%B1%EC%94%A8_%EB%AA%A9%EB%A1%9D
|
||||
"남궁",
|
||||
"사공",
|
||||
"서문",
|
||||
"선우",
|
||||
"제갈",
|
||||
"황보",
|
||||
"독고",
|
||||
"망절",
|
||||
|
||||
// Chinese, taken from the top 10 Chinese 2-character surnames:
|
||||
// https://zh.wikipedia.org/wiki/%E8%A4%87%E5%A7%93#.E5.B8.B8.E8.A6.8B.E7.9A.84.E8.A4.87.E5.A7.93
|
||||
// Simplified Chinese (mostly mainland China)
|
||||
"欧阳",
|
||||
"令狐",
|
||||
"皇甫",
|
||||
"上官",
|
||||
"司徒",
|
||||
"诸葛",
|
||||
"司马",
|
||||
"宇文",
|
||||
"呼延",
|
||||
"端木",
|
||||
// Traditional Chinese (mostly Taiwan)
|
||||
"張簡",
|
||||
"歐陽",
|
||||
"諸葛",
|
||||
"申屠",
|
||||
"尉遲",
|
||||
"司馬",
|
||||
"軒轅",
|
||||
"夏侯",
|
||||
],
|
||||
|
||||
// All Korean surnames that have more than one character, even the
|
||||
// rare/ambiguous ones.
|
||||
KOREAN_MULTI_CHAR_SURNAMES: [
|
||||
"강전",
|
||||
"남궁",
|
||||
"독고",
|
||||
"동방",
|
||||
"망절",
|
||||
"사공",
|
||||
"서문",
|
||||
"선우",
|
||||
"소봉",
|
||||
"어금",
|
||||
"장곡",
|
||||
"제갈",
|
||||
"황목",
|
||||
"황보",
|
||||
],
|
||||
|
||||
// The whitespace definition based on
|
||||
// https://cs.chromium.org/chromium/src/base/strings/string_util_constants.cc?l=9&rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
|
||||
|
@ -204,8 +325,6 @@ var FormAutofillNameUtils = {
|
|||
if (this._dataLoaded) {
|
||||
return;
|
||||
}
|
||||
let sandbox = FormAutofillUtils.loadDataFromScript(NAME_REFERENCES);
|
||||
Object.assign(this, sandbox.nameReferences);
|
||||
this._dataLoaded = true;
|
||||
|
||||
this.reCJK = new RegExp("[" + this.CJK_RANGE.join("") + "]", "u");
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
/* exported nameReferences */
|
||||
|
||||
"use strict";
|
||||
|
||||
// The data below is initially copied from
|
||||
// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util.cc?rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
|
||||
var nameReferences = {
|
||||
NAME_PREFIXES: [
|
||||
"1lt",
|
||||
"1st",
|
||||
"2lt",
|
||||
"2nd",
|
||||
"3rd",
|
||||
"admiral",
|
||||
"capt",
|
||||
"captain",
|
||||
"col",
|
||||
"cpt",
|
||||
"dr",
|
||||
"gen",
|
||||
"general",
|
||||
"lcdr",
|
||||
"lt",
|
||||
"ltc",
|
||||
"ltg",
|
||||
"ltjg",
|
||||
"maj",
|
||||
"major",
|
||||
"mg",
|
||||
"mr",
|
||||
"mrs",
|
||||
"ms",
|
||||
"pastor",
|
||||
"prof",
|
||||
"rep",
|
||||
"reverend",
|
||||
"rev",
|
||||
"sen",
|
||||
"st",
|
||||
],
|
||||
|
||||
NAME_SUFFIXES: [
|
||||
"b.a",
|
||||
"ba",
|
||||
"d.d.s",
|
||||
"dds",
|
||||
"i",
|
||||
"ii",
|
||||
"iii",
|
||||
"iv",
|
||||
"ix",
|
||||
"jr",
|
||||
"m.a",
|
||||
"m.d",
|
||||
"ma",
|
||||
"md",
|
||||
"ms",
|
||||
"ph.d",
|
||||
"phd",
|
||||
"sr",
|
||||
"v",
|
||||
"vi",
|
||||
"vii",
|
||||
"viii",
|
||||
"x",
|
||||
],
|
||||
|
||||
FAMILY_NAME_PREFIXES: [
|
||||
"d'",
|
||||
"de",
|
||||
"del",
|
||||
"der",
|
||||
"di",
|
||||
"la",
|
||||
"le",
|
||||
"mc",
|
||||
"san",
|
||||
"st",
|
||||
"ter",
|
||||
"van",
|
||||
"von",
|
||||
],
|
||||
|
||||
// The common and non-ambiguous CJK surnames (last names) that have more than
|
||||
// one character.
|
||||
COMMON_CJK_MULTI_CHAR_SURNAMES: [
|
||||
// Korean, taken from the list of surnames:
|
||||
// https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD%EC%9D%98_%EC%84%B1%EC%94%A8_%EB%AA%A9%EB%A1%9D
|
||||
"남궁",
|
||||
"사공",
|
||||
"서문",
|
||||
"선우",
|
||||
"제갈",
|
||||
"황보",
|
||||
"독고",
|
||||
"망절",
|
||||
|
||||
// Chinese, taken from the top 10 Chinese 2-character surnames:
|
||||
// https://zh.wikipedia.org/wiki/%E8%A4%87%E5%A7%93#.E5.B8.B8.E8.A6.8B.E7.9A.84.E8.A4.87.E5.A7.93
|
||||
// Simplified Chinese (mostly mainland China)
|
||||
"欧阳",
|
||||
"令狐",
|
||||
"皇甫",
|
||||
"上官",
|
||||
"司徒",
|
||||
"诸葛",
|
||||
"司马",
|
||||
"宇文",
|
||||
"呼延",
|
||||
"端木",
|
||||
// Traditional Chinese (mostly Taiwan)
|
||||
"張簡",
|
||||
"歐陽",
|
||||
"諸葛",
|
||||
"申屠",
|
||||
"尉遲",
|
||||
"司馬",
|
||||
"軒轅",
|
||||
"夏侯",
|
||||
],
|
||||
|
||||
// All Korean surnames that have more than one character, even the
|
||||
// rare/ambiguous ones.
|
||||
KOREAN_MULTI_CHAR_SURNAMES: [
|
||||
"강전",
|
||||
"남궁",
|
||||
"독고",
|
||||
"동방",
|
||||
"망절",
|
||||
"사공",
|
||||
"서문",
|
||||
"선우",
|
||||
"소봉",
|
||||
"어금",
|
||||
"장곡",
|
||||
"제갈",
|
||||
"황목",
|
||||
"황보",
|
||||
],
|
||||
};
|
|
@ -9803,29 +9803,18 @@ nsresult nsDocShell::DoURILoad(
|
|||
}
|
||||
|
||||
if (IsFrame()) {
|
||||
bool doesNotReturnData = false;
|
||||
NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
|
||||
&doesNotReturnData);
|
||||
|
||||
if (doesNotReturnData) {
|
||||
// If this is an iframe, it must have a parent. Let's count the
|
||||
// no-data-URL telemetry on the parent document, because probably this one
|
||||
// is an about page.
|
||||
nsCOMPtr<nsIDocShellTreeItem> parent;
|
||||
GetSameTypeParent(getter_AddRefs(parent));
|
||||
MOZ_ASSERT(parent);
|
||||
|
||||
nsIDocument* parentDocument = parent->GetDocument();
|
||||
if (parentDocument) {
|
||||
parentDocument->SetDocumentAndPageUseCounter(
|
||||
eUseCounter_custom_no_data_URL);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
|
||||
"DoURILoad thinks this is a frame and InternalLoad does not");
|
||||
|
||||
// Only allow URLs able to return data in iframes.
|
||||
bool doesNotReturnData = false;
|
||||
NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
|
||||
&doesNotReturnData);
|
||||
if (doesNotReturnData) {
|
||||
return NS_ERROR_UNKNOWN_PROTOCOL;
|
||||
}
|
||||
|
||||
// Only allow view-source scheme in top-level docshells. view-source is
|
||||
// the only scheme to which this applies at the moment due to potential
|
||||
// timing attacks to read data from cross-origin iframes. If this widens
|
||||
|
|
|
@ -60,9 +60,6 @@ attribute Window.sidebar
|
|||
// External interface
|
||||
method External.AddSearchProvider
|
||||
|
||||
// no-data URLs for iframes
|
||||
custom no_data_URL used in iframes
|
||||
|
||||
// AppCache API
|
||||
method OfflineResourceList.swapCache
|
||||
method OfflineResourceList.update
|
||||
|
|
|
@ -352,6 +352,8 @@ bool nsContentUtils::sAntiTrackingControlCenterUIEnabled = false;
|
|||
mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
|
||||
|
||||
PopupControlState nsContentUtils::sPopupControlState = openAbused;
|
||||
uint32_t nsContentUtils::sPopupStatePusherCount = 0;
|
||||
bool nsContentUtils::sUnusedPopupToken = false;
|
||||
|
||||
int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
|
||||
uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
|
||||
|
@ -10365,7 +10367,8 @@ nsContentUtils::TryGetTabChildGlobal(nsISupports* aFrom) {
|
|||
--sInnerOrOuterWindowCount;
|
||||
}
|
||||
|
||||
/* static */ bool nsContentUtils::CanShowPopup(nsIPrincipal* aPrincipal) {
|
||||
/* static */ bool nsContentUtils::CanShowPopupByPermission(
|
||||
nsIPrincipal* aPrincipal) {
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
uint32_t permit;
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
|
@ -10385,6 +10388,17 @@ nsContentUtils::TryGetTabChildGlobal(nsISupports* aFrom) {
|
|||
return !sDisablePopups;
|
||||
}
|
||||
|
||||
/* static */ bool nsContentUtils::TryUsePopupOpeningToken() {
|
||||
MOZ_ASSERT(sPopupStatePusherCount);
|
||||
|
||||
if (!sUnusedPopupToken) {
|
||||
sUnusedPopupToken = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
|
||||
nsAString* result = static_cast<nsAString*>(aData);
|
||||
result->Append(static_cast<const char16_t*>(aBuf),
|
||||
|
@ -10487,3 +10501,15 @@ static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
|
|||
nsDependentCSubstring(host, startIndexOfNextLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void nsContentUtils::PopupStatePusherCreated() {
|
||||
++sPopupStatePusherCount;
|
||||
}
|
||||
|
||||
/* static */ void nsContentUtils::PopupStatePusherDestroyed() {
|
||||
MOZ_ASSERT(sPopupStatePusherCount);
|
||||
|
||||
if (!--sPopupStatePusherCount) {
|
||||
sUnusedPopupToken = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3292,6 +3292,9 @@ class nsContentUtils {
|
|||
|
||||
static PopupControlState GetPopupControlState() { return sPopupControlState; }
|
||||
|
||||
static void PopupStatePusherCreated();
|
||||
static void PopupStatePusherDestroyed();
|
||||
|
||||
// Get a serial number for a newly created inner or outer window.
|
||||
static uint32_t InnerOrOuterWindowCreated();
|
||||
// Record that an inner or outer window has been destroyed.
|
||||
|
@ -3301,7 +3304,14 @@ class nsContentUtils {
|
|||
return sInnerOrOuterWindowCount;
|
||||
}
|
||||
|
||||
static bool CanShowPopup(nsIPrincipal* aPrincipal);
|
||||
// This method checks if the principal is allowed by open popups by user
|
||||
// permissions. In this case, the caller should not block popups.
|
||||
static bool CanShowPopupByPermission(nsIPrincipal* aPrincipal);
|
||||
|
||||
// This method returns true if the caller is allowed to show a popup, and it
|
||||
// consumes the popup token for the current event. There is just 1 popup
|
||||
// allowed per event.
|
||||
static bool TryUsePopupOpeningToken();
|
||||
|
||||
/**
|
||||
* Serializes a JSON-like JS::Value into a string.
|
||||
|
@ -3495,6 +3505,11 @@ class nsContentUtils {
|
|||
static mozilla::LazyLogModule sDOMDumpLog;
|
||||
|
||||
static PopupControlState sPopupControlState;
|
||||
static uint32_t sPopupStatePusherCount;
|
||||
|
||||
// This token is by default set to false. When a popup/filePicker is shown, it
|
||||
// is set to true.
|
||||
static bool sUnusedPopupToken;
|
||||
|
||||
static int32_t sInnerOrOuterWindowCount;
|
||||
static uint32_t sInnerOrOuterWindowSerialCounter;
|
||||
|
|
|
@ -5129,7 +5129,7 @@ bool nsGlobalWindowOuter::CanSetProperty(const char* aPrefName) {
|
|||
}
|
||||
|
||||
bool nsGlobalWindowOuter::PopupWhitelisted() {
|
||||
if (mDoc && nsContentUtils::CanShowPopup(mDoc->NodePrincipal())) {
|
||||
if (mDoc && nsContentUtils::CanShowPopupByPermission(mDoc->NodePrincipal())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5183,8 +5183,9 @@ PopupControlState nsGlobalWindowOuter::RevisePopupAbuseLevel(
|
|||
// If this popup is allowed, let's block any other for this event, forcing
|
||||
// openBlocked state.
|
||||
if ((abuse == openAllowed || abuse == openControlled) &&
|
||||
StaticPrefs::dom_block_multiple_popups() && !PopupWhitelisted()) {
|
||||
nsContentUtils::PushPopupControlState(openBlocked, true);
|
||||
StaticPrefs::dom_block_multiple_popups() && !PopupWhitelisted() &&
|
||||
!nsContentUtils::TryUsePopupOpeningToken()) {
|
||||
abuse = openBlocked;
|
||||
}
|
||||
|
||||
return abuse;
|
||||
|
@ -7390,10 +7391,13 @@ nsPIDOMWindowOuter::~nsPIDOMWindowOuter() {}
|
|||
|
||||
nsAutoPopupStatePusherInternal::nsAutoPopupStatePusherInternal(
|
||||
PopupControlState aState, bool aForce)
|
||||
: mOldState(nsContentUtils::PushPopupControlState(aState, aForce)) {}
|
||||
: mOldState(nsContentUtils::PushPopupControlState(aState, aForce)) {
|
||||
nsContentUtils::PopupStatePusherCreated();
|
||||
}
|
||||
|
||||
nsAutoPopupStatePusherInternal::~nsAutoPopupStatePusherInternal() {
|
||||
nsContentUtils::PopPopupControlState(mOldState);
|
||||
nsContentUtils::PopupStatePusherDestroyed();
|
||||
}
|
||||
|
||||
mozilla::dom::BrowsingContext* nsPIDOMWindowOuter::GetBrowsingContext() const {
|
||||
|
|
|
@ -652,10 +652,11 @@ bool HTMLInputElement::IsPopupBlocked() const {
|
|||
|
||||
// Check if page can open a popup without abuse regardless of allowed events
|
||||
if (win->GetPopupControlState() <= openBlocked) {
|
||||
return false;
|
||||
return !nsContentUtils::TryUsePopupOpeningToken();
|
||||
}
|
||||
|
||||
return !nsContentUtils::CanShowPopup(OwnerDoc()->NodePrincipal());
|
||||
return !nsContentUtils::CanShowPopupByPermission(
|
||||
OwnerDoc()->NodePrincipal());
|
||||
}
|
||||
|
||||
nsresult HTMLInputElement::InitColorPicker() {
|
||||
|
|
|
@ -6,3 +6,4 @@ support-files =
|
|||
[test_anchor_ping.html]
|
||||
skip-if = os == 'android'
|
||||
[test_bug1414077.html]
|
||||
[test_multipleFilePicker.html]
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for single filepicker per event</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id='foo'><a href='#'>Click here to test this issue</a></div>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let clickCount = 0;
|
||||
let foo = document.getElementById('foo');
|
||||
foo.addEventListener('click', _ => {
|
||||
if (++clickCount < 10) {
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
foo.appendChild(input);
|
||||
input.click();
|
||||
}
|
||||
});
|
||||
|
||||
let MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(window);
|
||||
|
||||
let pickerCount = 0;
|
||||
|
||||
// Let's do the first click.
|
||||
(new Promise(resolve => {
|
||||
MockFilePicker.showCallback = function(filepicker) {
|
||||
++pickerCount;
|
||||
resolve();
|
||||
}
|
||||
setTimeout(_ => {
|
||||
is(pickerCount, 0, "No file picker initially");
|
||||
sendMouseEvent({type:'click'}, 'foo');
|
||||
}, 0);
|
||||
}))
|
||||
|
||||
// Let's wait a bit more, then let's do a click.
|
||||
.then(_ => {
|
||||
return new Promise(resolve => {
|
||||
MockFilePicker.showCallback = function(filepicker) {
|
||||
++pickerCount;
|
||||
resolve();
|
||||
}
|
||||
|
||||
setTimeout(_ => {
|
||||
is(pickerCount, 1, "Only 1 file picker");
|
||||
is(clickCount, 10, "10 clicks triggered");
|
||||
clickCount = 0;
|
||||
pickerCount = 0;
|
||||
synthesizeMouseAtCenter(foo, {});
|
||||
}, 1000);
|
||||
});
|
||||
})
|
||||
|
||||
// Another click...
|
||||
.then(_ => {
|
||||
setTimeout(_ => {
|
||||
is(pickerCount, 1, "Only 1 file picker");
|
||||
is(clickCount, 10, "10 clicks triggered");
|
||||
MockFilePicker.cleanup();
|
||||
SimpleTest.finish();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -173,5 +173,11 @@ interface nsIBrowserDOMWindow : nsISupports
|
|||
* BrowserUtils.jsm for a simple implementation of this method.
|
||||
*/
|
||||
boolean canClose();
|
||||
|
||||
/**
|
||||
* The number browser tabs in the window. This number currently includes
|
||||
* lazy tabs, though for most uses it probably should not.
|
||||
*/
|
||||
readonly attribute unsigned long tabCount;
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BackgroundHangMonitor.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/MemoryTelemetry.h"
|
||||
#include "mozilla/NullPrincipal.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ProcessHangMonitorIPC.h"
|
||||
|
@ -241,6 +242,7 @@
|
|||
#include "VRManagerChild.h"
|
||||
#include "private/pprio.h"
|
||||
#include "nsString.h"
|
||||
#include "MMPrinter.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#include "nsAppRunner.h"
|
||||
|
@ -2258,6 +2260,7 @@ mozilla::ipc::IPCResult ContentChild::RecvAsyncMessage(
|
|||
const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentChild::RecvAsyncMessage",
|
||||
OTHER, aMsg);
|
||||
MMPrinter::Print("ContentChild::RecvAsyncMessage", aMsg, aData);
|
||||
|
||||
CrossProcessCpowHolder cpows(this, aCpows);
|
||||
RefPtr<nsFrameMessageManager> cpm =
|
||||
|
@ -3376,6 +3379,12 @@ mozilla::ipc::IPCResult ContentChild::RecvFlushCodeCoverageCounters(
|
|||
#endif
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentChild::RecvGetMemoryUniqueSetSize(
|
||||
GetMemoryUniqueSetSizeResolver&& aResolver) {
|
||||
MemoryTelemetry::Get().GetUniqueSetSize(std::move(aResolver));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentChild::RecvSetInputEventQueueEnabled() {
|
||||
nsThreadManager::get().EnableMainThreadEventPrioritization();
|
||||
return IPC_OK();
|
||||
|
|
|
@ -612,6 +612,9 @@ class ContentChild final : public PContentChild,
|
|||
virtual mozilla::ipc::IPCResult RecvFlushCodeCoverageCounters(
|
||||
FlushCodeCoverageCountersResolver&& aResolver) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvGetMemoryUniqueSetSize(
|
||||
GetMemoryUniqueSetSizeResolver&& aResolver) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvSetInputEventQueueEnabled() override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvFlushInputEventQueue() override;
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MMPrinter.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
LazyLogModule MMPrinter::sMMLog("MessageManager");
|
||||
|
||||
/* static */
|
||||
void MMPrinter::PrintImpl(char const* aLocation, const nsAString& aMsg,
|
||||
ClonedMessageData const& aData) {
|
||||
NS_ConvertUTF16toUTF8 charMsg(aMsg);
|
||||
|
||||
/*
|
||||
* The topic will be skipped if the topic name appears anywhere as a substring
|
||||
* of the filter.
|
||||
*
|
||||
* Example:
|
||||
* MOZ_LOG_MESSAGEMANAGER_SKIP="foobar|extension"
|
||||
* Will match the topics 'foobar', 'foo', 'bar', and 'ten' (even though
|
||||
* you may not have intended to match the latter three) and it will not match
|
||||
* the topics 'extensionresult' or 'Foo'.
|
||||
*/
|
||||
char* mmSkipLog = PR_GetEnv("MOZ_LOG_MESSAGEMANAGER_SKIP");
|
||||
|
||||
if (mmSkipLog && strstr(mmSkipLog, charMsg.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(MMPrinter::sMMLog, LogLevel::Debug,
|
||||
("%s Message: %s in process type: %s", aLocation, charMsg.get(),
|
||||
XRE_ChildProcessTypeToString(XRE_GetProcessType())));
|
||||
|
||||
if (!MOZ_LOG_TEST(sMMLog, LogLevel::Verbose)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
MOZ_ALWAYS_TRUE(jsapi.Init(xpc::UnprivilegedJunkScope()));
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
ipc::StructuredCloneData data;
|
||||
ipc::UnpackClonedMessageDataForChild(aData, data);
|
||||
|
||||
/* Read original StructuredCloneData. */
|
||||
JS::RootedValue scdContent(cx);
|
||||
data.Read(cx, &scdContent, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
return;
|
||||
}
|
||||
|
||||
JS::RootedString unevalObj(cx, JS_ValueToSource(cx, scdContent));
|
||||
nsAutoJSString srcString;
|
||||
if (!srcString.init(cx, unevalObj)) return;
|
||||
|
||||
MOZ_LOG(MMPrinter::sMMLog, LogLevel::Verbose,
|
||||
(" %s", NS_ConvertUTF16toUTF8(srcString).get()));
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MMPrinter_h
|
||||
#define MMPrinter_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MMPrinter {
|
||||
public:
|
||||
static void Print(char const* aLocation, const nsAString& aMsg,
|
||||
ClonedMessageData const& aData) {
|
||||
if (MOZ_UNLIKELY(MOZ_LOG_TEST(MMPrinter::sMMLog, LogLevel::Debug))) {
|
||||
MMPrinter::PrintImpl(aLocation, aMsg, aData);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static LazyLogModule sMMLog;
|
||||
static void PrintImpl(char const* aLocation, const nsAString& aMsg,
|
||||
ClonedMessageData const& aData);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MMPrinter_h */
|
|
@ -652,6 +652,8 @@ child:
|
|||
async ShareCodeCoverageMutex(CrossProcessMutexHandle handle);
|
||||
async FlushCodeCoverageCounters() returns (bool unused);
|
||||
|
||||
async GetMemoryUniqueSetSize() returns (int64_t uss);
|
||||
|
||||
/*
|
||||
* IPC message to enable the input event queue on the main thread of the
|
||||
* content process.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/dom/ContentProcessMessageManager.h"
|
||||
#include "mozilla/dom/IPCBlobUtils.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/ScriptPreloader.h"
|
||||
|
||||
using namespace mozilla::loader;
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@
|
|||
#include "nsDocShellLoadState.h"
|
||||
#include "nsWebBrowser.h"
|
||||
#include "mozilla/dom/WindowGlobalChild.h"
|
||||
#include "MMPrinter.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/plugins/PluginWidgetChild.h"
|
||||
|
@ -2056,6 +2057,7 @@ mozilla::ipc::IPCResult TabChild::RecvAsyncMessage(
|
|||
const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("TabChild::RecvAsyncMessage",
|
||||
OTHER, aMessage);
|
||||
MMPrinter::Print("TabChild::RecvAsyncMessage", aMessage, aData);
|
||||
|
||||
CrossProcessCpowHolder cpows(Manager(), aCpows);
|
||||
if (!mTabChildMessageManager) {
|
||||
|
|
|
@ -1579,6 +1579,7 @@ mozilla::ipc::IPCResult TabParent::RecvSyncMessage(
|
|||
nsTArray<StructuredCloneData>* aRetVal) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("TabParent::RecvSyncMessage",
|
||||
OTHER, aMessage);
|
||||
MMPrinter::Print("TabParent::RecvSyncMessage", aMessage, aData);
|
||||
|
||||
StructuredCloneData data;
|
||||
ipc::UnpackClonedMessageDataForParent(aData, data);
|
||||
|
@ -1596,6 +1597,7 @@ mozilla::ipc::IPCResult TabParent::RecvRpcMessage(
|
|||
nsTArray<StructuredCloneData>* aRetVal) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("TabParent::RecvRpcMessage", OTHER,
|
||||
aMessage);
|
||||
MMPrinter::Print("TabParent::RecvRpcMessage", aMessage, aData);
|
||||
|
||||
StructuredCloneData data;
|
||||
ipc::UnpackClonedMessageDataForParent(aData, data);
|
||||
|
@ -1612,6 +1614,7 @@ mozilla::ipc::IPCResult TabParent::RecvAsyncMessage(
|
|||
const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("TabParent::RecvAsyncMessage",
|
||||
OTHER, aMessage);
|
||||
MMPrinter::Print("TabParent::RecvAsyncMessage", aMessage, aData);
|
||||
|
||||
StructuredCloneData data;
|
||||
ipc::UnpackClonedMessageDataForParent(aData, data);
|
||||
|
|
|
@ -72,6 +72,7 @@ UNIFIED_SOURCES += [
|
|||
'FilePickerParent.cpp',
|
||||
'MemMapSnapshot.cpp',
|
||||
'MemoryReportRequest.cpp',
|
||||
'MMPrinter.cpp',
|
||||
'nsIContentChild.cpp',
|
||||
'nsIContentParent.cpp',
|
||||
'PermissionMessageUtils.cpp',
|
||||
|
@ -148,6 +149,7 @@ LOCAL_INCLUDES += [
|
|||
'/gfx/2d',
|
||||
'/hal/sandbox',
|
||||
'/js/xpconnect/loader',
|
||||
'/js/xpconnect/src',
|
||||
'/layout/base',
|
||||
'/media/webrtc',
|
||||
'/netwerk/base',
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "mozilla/ipc/PParentToChildStreamChild.h"
|
||||
#include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
|
||||
|
||||
#include "MMPrinter.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
|
@ -147,6 +148,7 @@ mozilla::ipc::IPCResult nsIContentChild::RecvAsyncMessage(
|
|||
const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
||||
"nsIContentChild::RecvAsyncMessage", OTHER, aMsg);
|
||||
MMPrinter::Print("nsIContentChild::RecvAsyncMessage", aMsg, aData);
|
||||
|
||||
CrossProcessCpowHolder cpows(this, aCpows);
|
||||
RefPtr<nsFrameMessageManager> cpm =
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "mozilla/ipc/IPCStreamSource.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "MMPrinter.h"
|
||||
#include "nsIWebBrowserChrome.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "xpcpublic.h"
|
||||
|
@ -235,6 +236,7 @@ mozilla::ipc::IPCResult nsIContentParent::RecvSyncMessage(
|
|||
nsTArray<ipc::StructuredCloneData>* aRetvals) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
||||
"nsIContentParent::RecvSyncMessage", OTHER, aMsg);
|
||||
MMPrinter::Print("nsIContentParent::RecvSyncMessage", aMsg, aData);
|
||||
|
||||
CrossProcessCpowHolder cpows(this, aCpows);
|
||||
RefPtr<nsFrameMessageManager> ppm = mMessageManager;
|
||||
|
@ -254,6 +256,7 @@ mozilla::ipc::IPCResult nsIContentParent::RecvRpcMessage(
|
|||
nsTArray<ipc::StructuredCloneData>* aRetvals) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("nsIContentParent::RecvRpcMessage",
|
||||
OTHER, aMsg);
|
||||
MMPrinter::Print("nsIContentParent::RecvRpcMessage", aMsg, aData);
|
||||
|
||||
CrossProcessCpowHolder cpows(this, aCpows);
|
||||
RefPtr<nsFrameMessageManager> ppm = mMessageManager;
|
||||
|
@ -305,6 +308,7 @@ mozilla::ipc::IPCResult nsIContentParent::RecvAsyncMessage(
|
|||
const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
||||
"nsIContentParent::RecvAsyncMessage", OTHER, aMsg);
|
||||
MMPrinter::Print("nsIContentParent::RecvAsyncMessage", aMsg, aData);
|
||||
|
||||
CrossProcessCpowHolder cpows(this, aCpows);
|
||||
RefPtr<nsFrameMessageManager> ppm = mMessageManager;
|
||||
|
|
|
@ -1075,5 +1075,16 @@ void CriticalLogger::CrashAction(LogReason aReason) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
void LogWStr(const wchar_t* aWStr, std::stringstream& aOut) {
|
||||
int n = WideCharToMultiByte(CP_ACP, 0, aWStr, -1, nullptr, 0, nullptr, nullptr);
|
||||
if (n > 1) {
|
||||
std::vector<char> str(n);
|
||||
WideCharToMultiByte(CP_ACP, 0, aWStr, -1, str.data(), n, nullptr, nullptr);
|
||||
aOut << str.data();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -252,6 +252,10 @@ Hexa<T> hexa(T val) {
|
|||
return Hexa<T>(val);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
void LogWStr(const wchar_t* aStr, std::stringstream& aOut);
|
||||
#endif
|
||||
|
||||
template <int L, typename Logger = BasicLogger>
|
||||
class Log {
|
||||
public:
|
||||
|
@ -302,6 +306,14 @@ class Log {
|
|||
}
|
||||
return *this;
|
||||
}
|
||||
#ifdef WIN32
|
||||
Log& operator<<(const wchar_t aWStr[]) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
LogWStr(aWStr, mMessage);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
Log& operator<<(bool aBool) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << (aBool ? "true" : "false");
|
||||
|
|
|
@ -385,6 +385,56 @@ bool UnscaledFontDWrite::GetWRFontDescriptor(WRFontDescriptorOutput aCb,
|
|||
return false;
|
||||
}
|
||||
|
||||
// FIXME: Debugging kluge for bug 1455848. Remove once debugged!
|
||||
UINT32 numFiles;
|
||||
hr = mFontFace->GetFiles(&numFiles, nullptr);
|
||||
if (FAILED(hr) || !numFiles) {
|
||||
return false;
|
||||
}
|
||||
std::vector<RefPtr<IDWriteFontFile>> files;
|
||||
files.resize(numFiles);
|
||||
hr = mFontFace->GetFiles(&numFiles, getter_AddRefs(files[0]));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(familyName.size() >= 1 && familyName.back() == 0);
|
||||
for(auto& file : files) {
|
||||
const void* key;
|
||||
UINT32 keySize;
|
||||
hr = file->GetReferenceKey(&key, &keySize);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
RefPtr<IDWriteFontFileLoader> loader;
|
||||
hr = file->GetLoader(getter_AddRefs(loader));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
RefPtr<IDWriteLocalFontFileLoader> localLoader;
|
||||
loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void**)getter_AddRefs(localLoader));
|
||||
if (!localLoader) {
|
||||
return false;
|
||||
}
|
||||
UINT32 pathLen;
|
||||
hr = localLoader->GetFilePathLengthFromKey(key, keySize, &pathLen);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
size_t offset = familyName.size();
|
||||
familyName.resize(offset + pathLen + 1);
|
||||
hr = localLoader->GetFilePathFromKey(key, keySize, &familyName[offset], pathLen + 1);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(familyName.back() == 0);
|
||||
DWORD attribs = GetFileAttributesW(&familyName[offset]);
|
||||
if (attribs == INVALID_FILE_ATTRIBUTES) {
|
||||
gfxCriticalNote << "sending font family \"" << &familyName[0]
|
||||
<< "\" with invalid file \"" << &familyName[offset]
|
||||
<< "\"";
|
||||
}
|
||||
}
|
||||
|
||||
// The style information that identifies the font can be encoded easily in
|
||||
// less than 32 bits. Since the index is needed for font descriptors, only
|
||||
// the family name and style information, pass along the style in the index
|
||||
|
@ -392,7 +442,7 @@ bool UnscaledFontDWrite::GetWRFontDescriptor(WRFontDescriptorOutput aCb,
|
|||
// the data payload.
|
||||
uint32_t index = weight | (stretch << 16) | (style << 24);
|
||||
aCb(reinterpret_cast<const uint8_t*>(familyName.data()),
|
||||
(familyName.size() - 1) * sizeof(WCHAR), index, aBaton);
|
||||
familyName.size() * sizeof(WCHAR), index, aBaton);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,9 @@ parent:
|
|||
async FlushApzRepaints();
|
||||
sync GetAPZTestData() returns (APZTestData data);
|
||||
|
||||
// Debugging routine for bug 1455848.
|
||||
async ValidateFontDescriptor(uint8_t[] desc);
|
||||
|
||||
async Shutdown();
|
||||
sync ShutdownSync();
|
||||
child:
|
||||
|
|
|
@ -225,6 +225,14 @@ static void WriteFontDescriptor(const uint8_t* aData, uint32_t aLength,
|
|||
|
||||
*sink->mFontKey = sink->mWrBridge->GetNextFontKey();
|
||||
|
||||
#ifdef XP_WIN
|
||||
// FIXME: Debugging kluge for bug 1455848. Remove once debugged!
|
||||
nsTArray<uint8_t> data;
|
||||
data.AppendElements(aData, aLength);
|
||||
sink->mWrBridge->SendValidateFontDescriptor(data);
|
||||
aLength = uint32_t(wcsnlen_s((const wchar_t*)aData, aLength / sizeof(wchar_t)) * sizeof(wchar_t));
|
||||
#endif
|
||||
|
||||
sink->mResources->AddFontDescriptor(
|
||||
*sink->mFontKey, Range<uint8_t>(const_cast<uint8_t*>(aData), aLength),
|
||||
aIndex);
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
#include "mozilla/webrender/RenderThread.h"
|
||||
#include "mozilla/widget/CompositorWidget.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "dwrite.h"
|
||||
#endif
|
||||
|
||||
using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON;
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
@ -697,6 +701,48 @@ void WebRenderBridgeParent::ObserveSharedSurfaceRelease(
|
|||
}
|
||||
}
|
||||
|
||||
// Debugging kluge for bug 1455848. Remove once debugged!
|
||||
mozilla::ipc::IPCResult WebRenderBridgeParent::RecvValidateFontDescriptor(
|
||||
nsTArray<uint8_t>&& aData) {
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
nsTArray<uint8_t> data(aData);
|
||||
wchar_t* family = (wchar_t*)data.Elements();
|
||||
size_t remaining = data.Length() / sizeof(wchar_t);
|
||||
size_t familyLength = wcsnlen_s(family, remaining);
|
||||
MOZ_ASSERT(familyLength < remaining && family[familyLength] == 0);
|
||||
remaining -= familyLength + 1;
|
||||
wchar_t* files = family + familyLength + 1;
|
||||
BOOL exists = FALSE;
|
||||
if (RefPtr<IDWriteFontCollection> systemFonts = Factory::GetDWriteSystemFonts()) {
|
||||
UINT32 idx;
|
||||
systemFonts->FindFamilyName(family, &idx, &exists);
|
||||
}
|
||||
if (!remaining) {
|
||||
gfxCriticalNote << (exists ? "found" : "MISSING")
|
||||
<< " font family \"" << family
|
||||
<< "\" has no files!";
|
||||
}
|
||||
while (remaining > 0) {
|
||||
size_t fileLength = wcsnlen_s(files, remaining);
|
||||
MOZ_ASSERT(fileLength < remaining && files[fileLength] == 0);
|
||||
DWORD attribs = GetFileAttributesW(files);
|
||||
if (!exists || attribs == INVALID_FILE_ATTRIBUTES) {
|
||||
gfxCriticalNote << (exists ? "found" : "MISSING")
|
||||
<< " font family \"" << family
|
||||
<< "\" has " << (attribs == INVALID_FILE_ATTRIBUTES ? "INVALID" : "valid")
|
||||
<< " file \"" << files
|
||||
<< "\"";
|
||||
}
|
||||
remaining -= fileLength + 1;
|
||||
files += fileLength + 1;
|
||||
}
|
||||
#endif
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult WebRenderBridgeParent::RecvUpdateResources(
|
||||
nsTArray<OpUpdateResource>&& aResourceUpdates,
|
||||
nsTArray<RefCountedShmem>&& aSmallShmems,
|
||||
|
|
|
@ -123,6 +123,9 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
|
|||
nsTArray<WebRenderParentCommand>&& commands) override;
|
||||
mozilla::ipc::IPCResult RecvGetSnapshot(PTextureParent* aTexture) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvValidateFontDescriptor(
|
||||
nsTArray<uint8_t>&& aData) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSetLayersObserverEpoch(
|
||||
const LayersObserverEpoch& aChildEpoch) override;
|
||||
|
||||
|
|
|
@ -277,6 +277,15 @@ extern JS_PUBLIC_API bool CompileLatin1ForNonSyntacticScope(
|
|||
JSContext* cx, const ReadOnlyCompileOptions& options, const char* bytes,
|
||||
size_t length, MutableHandle<JSScript*> script);
|
||||
|
||||
/**
|
||||
* Compile the given UTF-8 data for non-syntactic scope.
|
||||
*
|
||||
* An exception is thrown if the data isn't valid UTF-8.
|
||||
*/
|
||||
extern JS_PUBLIC_API bool CompileUtf8ForNonSyntacticScope(
|
||||
JSContext* cx, const ReadOnlyCompileOptions& options, const char* bytes,
|
||||
size_t length, MutableHandle<JSScript*> script);
|
||||
|
||||
/**
|
||||
* Compile a function with envChain plus the global as its scope chain.
|
||||
* envChain must contain objects in the current compartment of cx. The actual
|
||||
|
|
|
@ -774,10 +774,10 @@ bool NodeBuilder::newNodeLoc(TokenPos* pos, MutableHandleValue dst) {
|
|||
|
||||
uint32_t startLineNum, startColumnIndex;
|
||||
uint32_t endLineNum, endColumnIndex;
|
||||
parser->anyChars.srcCoords.lineNumAndColumnIndex(pos->begin, &startLineNum,
|
||||
&startColumnIndex);
|
||||
parser->anyChars.srcCoords.lineNumAndColumnIndex(pos->end, &endLineNum,
|
||||
&endColumnIndex);
|
||||
parser->tokenStream.computeLineAndColumn(pos->begin, &startLineNum,
|
||||
&startColumnIndex);
|
||||
parser->tokenStream.computeLineAndColumn(pos->end, &endLineNum,
|
||||
&endColumnIndex);
|
||||
|
||||
if (!newObject(&to)) {
|
||||
return false;
|
||||
|
@ -1869,8 +1869,9 @@ bool ASTSerializer::blockStatement(ListNode* node, MutableHandleValue dst) {
|
|||
bool ASTSerializer::program(ListNode* node, MutableHandleValue dst) {
|
||||
#ifdef DEBUG
|
||||
{
|
||||
const auto& srcCoords = parser->anyChars.srcCoords;
|
||||
MOZ_ASSERT(srcCoords.lineNum(node->pn_pos.begin) == lineno);
|
||||
const TokenStreamAnyChars& anyChars = parser->anyChars;
|
||||
auto lineToken = anyChars.lineToken(node->pn_pos.begin);
|
||||
MOZ_ASSERT(anyChars.lineNumber(lineToken) == lineno);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -85,6 +85,13 @@ struct ParserNewObjectBox {
|
|||
}
|
||||
};
|
||||
|
||||
template <class TokenStream>
|
||||
struct TokenStreamComputeLineAndColumn {
|
||||
static constexpr auto get() -> decltype(&TokenStream::computeLineAndColumn) {
|
||||
return &TokenStream::computeLineAndColumn;
|
||||
}
|
||||
};
|
||||
|
||||
// Generic matchers.
|
||||
|
||||
struct ParseHandlerMatcher {
|
||||
|
@ -94,13 +101,6 @@ struct ParseHandlerMatcher {
|
|||
}
|
||||
};
|
||||
|
||||
struct AnyCharsMatcher {
|
||||
template <class Parser>
|
||||
frontend::TokenStreamAnyChars& match(Parser* parser) {
|
||||
return parser->anyChars;
|
||||
}
|
||||
};
|
||||
|
||||
struct ParserBaseMatcher {
|
||||
template <class Parser>
|
||||
frontend::ParserBase& match(Parser* parser) {
|
||||
|
@ -162,13 +162,13 @@ class EitherParser : public BCEParserHandle {
|
|||
return parser.match(std::move(matcher));
|
||||
}
|
||||
|
||||
const TokenStreamAnyChars& anyChars() const {
|
||||
return parser.match(detail::AnyCharsMatcher());
|
||||
}
|
||||
|
||||
void computeLineAndColumn(uint32_t offset, uint32_t* line,
|
||||
uint32_t* column) const {
|
||||
return anyChars().lineAndColumnAt(offset, line, column);
|
||||
InvokeMemberFunction<detail::GetTokenStream,
|
||||
detail::TokenStreamComputeLineAndColumn, uint32_t,
|
||||
uint32_t*, uint32_t*>
|
||||
matcher{offset, line, column};
|
||||
return parser.match(std::move(matcher));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -617,7 +617,7 @@ void GeneralParser<ParseHandler, Unit>::reportMissingClosing(
|
|||
}
|
||||
|
||||
uint32_t line, column;
|
||||
anyChars.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
|
||||
tokenStream.computeLineAndColumn(openedPos, &line, &column);
|
||||
|
||||
const size_t MaxWidth = sizeof("4294967295");
|
||||
char columnNumber[MaxWidth];
|
||||
|
@ -656,7 +656,7 @@ void GeneralParser<ParseHandler, Unit>::reportRedeclaration(
|
|||
}
|
||||
|
||||
uint32_t line, column;
|
||||
anyChars.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
|
||||
tokenStream.computeLineAndColumn(prevPos, &line, &column);
|
||||
|
||||
const size_t MaxWidth = sizeof("4294967295");
|
||||
char columnNumber[MaxWidth];
|
||||
|
@ -2437,7 +2437,7 @@ bool GeneralParser<ParseHandler, Unit>::functionArguments(
|
|||
|
||||
// Record the start of function source (for FunctionToString). If we
|
||||
// are parenFreeArrow, we will set this below, after consuming the NAME.
|
||||
funbox->setStart(anyChars);
|
||||
tokenStream.setFunctionStart(funbox);
|
||||
} else {
|
||||
// When delazifying, we may not have a current token and pos() is
|
||||
// garbage. In that case, substitute the first token's position.
|
||||
|
@ -2552,7 +2552,7 @@ bool GeneralParser<ParseHandler, Unit>::functionArguments(
|
|||
}
|
||||
|
||||
if (parenFreeArrow) {
|
||||
funbox->setStart(anyChars);
|
||||
tokenStream.setFunctionStart(funbox);
|
||||
}
|
||||
|
||||
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
|
||||
|
@ -3302,6 +3302,7 @@ bool GeneralParser<ParseHandler, Unit>::functionFormalParametersAndBody(
|
|||
openedPos);
|
||||
return false;
|
||||
}
|
||||
|
||||
funbox->setEnd(anyChars);
|
||||
} else {
|
||||
MOZ_ASSERT(kind == FunctionSyntaxKind::Arrow);
|
||||
|
@ -3309,9 +3310,13 @@ bool GeneralParser<ParseHandler, Unit>::functionFormalParametersAndBody(
|
|||
if (anyChars.hadError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
funbox->setEnd(anyChars);
|
||||
if (kind == FunctionSyntaxKind::Statement && !matchOrInsertSemicolon()) {
|
||||
return false;
|
||||
|
||||
if (kind == FunctionSyntaxKind::Statement) {
|
||||
if (!matchOrInsertSemicolon()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -527,14 +527,10 @@ class FunctionBox : public ObjectBox, public SharedContext {
|
|||
// for validated asm.js.
|
||||
bool useAsmOrInsideUseAsm() const { return useAsm; }
|
||||
|
||||
void setStart(const TokenStreamAnyChars& anyChars) {
|
||||
uint32_t offset = anyChars.currentToken().pos.begin;
|
||||
setStart(anyChars, offset);
|
||||
}
|
||||
|
||||
void setStart(const TokenStreamAnyChars& anyChars, uint32_t offset) {
|
||||
void setStart(uint32_t offset, uint32_t line, uint32_t column) {
|
||||
bufStart = offset;
|
||||
anyChars.srcCoords.lineNumAndColumnIndex(offset, &startLine, &startColumn);
|
||||
startLine = line;
|
||||
startColumn = column;
|
||||
}
|
||||
|
||||
void setEnd(const TokenStreamAnyChars& anyChars) {
|
||||
|
@ -549,6 +545,19 @@ class FunctionBox : public ObjectBox, public SharedContext {
|
|||
void trace(JSTracer* trc) override;
|
||||
};
|
||||
|
||||
template <typename Unit, class AnyCharsAccess>
|
||||
inline void GeneralTokenStreamChars<Unit, AnyCharsAccess>::setFunctionStart(
|
||||
FunctionBox* funbox) const {
|
||||
const TokenStreamAnyChars& anyChars = anyCharsAccess();
|
||||
|
||||
uint32_t bufStart = anyChars.currentToken().pos.begin;
|
||||
|
||||
uint32_t startLine, startColumn;
|
||||
computeLineAndColumn(bufStart, &startLine, &startColumn);
|
||||
|
||||
funbox->setStart(bufStart, startLine, startColumn);
|
||||
}
|
||||
|
||||
inline FunctionBox* SharedContext::asFunctionBox() {
|
||||
MOZ_ASSERT(isFunctionBox());
|
||||
return static_cast<FunctionBox*>(this);
|
||||
|
|
|
@ -327,13 +327,14 @@ PropertyName* TokenStreamAnyChars::reservedWordToPropertyName(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
TokenStreamAnyChars::SourceCoords::SourceCoords(JSContext* cx, uint32_t ln,
|
||||
uint32_t col,
|
||||
uint32_t initialLineOffset)
|
||||
TokenStreamAnyChars::SourceCoords::SourceCoords(JSContext* cx,
|
||||
uint32_t initialLineNumber,
|
||||
uint32_t initialColumnNumber,
|
||||
uint32_t initialOffset)
|
||||
: lineStartOffsets_(cx),
|
||||
initialLineNum_(ln),
|
||||
initialColumn_(col),
|
||||
lastLineIndex_(0) {
|
||||
initialLineNum_(initialLineNumber),
|
||||
initialColumn_(initialColumnNumber),
|
||||
lastIndex_(0) {
|
||||
// This is actually necessary! Removing it causes compile errors on
|
||||
// GCC and clang. You could try declaring this:
|
||||
//
|
||||
|
@ -343,24 +344,24 @@ TokenStreamAnyChars::SourceCoords::SourceCoords(JSContext* cx, uint32_t ln,
|
|||
//
|
||||
uint32_t maxPtr = MAX_PTR;
|
||||
|
||||
// The first line begins at buffer offset |initialLineOffset|. MAX_PTR is
|
||||
// the sentinel. The appends cannot fail because |lineStartOffsets_| has
|
||||
// The first line begins at buffer offset |initialOffset|. MAX_PTR is the
|
||||
// sentinel. The appends cannot fail because |lineStartOffsets_| has
|
||||
// statically-allocated elements.
|
||||
MOZ_ASSERT(lineStartOffsets_.capacity() >= 2);
|
||||
MOZ_ALWAYS_TRUE(lineStartOffsets_.reserve(2));
|
||||
lineStartOffsets_.infallibleAppend(initialLineOffset);
|
||||
lineStartOffsets_.infallibleAppend(initialOffset);
|
||||
lineStartOffsets_.infallibleAppend(maxPtr);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool TokenStreamAnyChars::SourceCoords::add(
|
||||
uint32_t lineNum, uint32_t lineStartOffset) {
|
||||
uint32_t lineIndex = lineNumToIndex(lineNum);
|
||||
uint32_t index = indexFromLineNumber(lineNum);
|
||||
uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
|
||||
|
||||
MOZ_ASSERT(lineStartOffsets_[0] <= lineStartOffset &&
|
||||
lineStartOffsets_[sentinelIndex] == MAX_PTR);
|
||||
MOZ_ASSERT(lineStartOffsets_[0] <= lineStartOffset);
|
||||
MOZ_ASSERT(lineStartOffsets_[sentinelIndex] == MAX_PTR);
|
||||
|
||||
if (lineIndex == sentinelIndex) {
|
||||
if (index == sentinelIndex) {
|
||||
// We haven't seen this newline before. Update lineStartOffsets_
|
||||
// only if lineStartOffsets_.append succeeds, to keep sentinel.
|
||||
// Otherwise return false to tell TokenStream about OOM.
|
||||
|
@ -373,13 +374,13 @@ MOZ_ALWAYS_INLINE bool TokenStreamAnyChars::SourceCoords::add(
|
|||
return false;
|
||||
}
|
||||
|
||||
lineStartOffsets_[lineIndex] = lineStartOffset;
|
||||
lineStartOffsets_[index] = lineStartOffset;
|
||||
} else {
|
||||
// We have seen this newline before (and ungot it). Do nothing (other
|
||||
// than checking it hasn't mysteriously changed).
|
||||
// This path can be executed after hitting OOM, so check lineIndex.
|
||||
MOZ_ASSERT_IF(lineIndex < sentinelIndex,
|
||||
lineStartOffsets_[lineIndex] == lineStartOffset);
|
||||
// This path can be executed after hitting OOM, so check index.
|
||||
MOZ_ASSERT_IF(index < sentinelIndex,
|
||||
lineStartOffsets_[index] == lineStartOffset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -407,33 +408,33 @@ MOZ_ALWAYS_INLINE bool TokenStreamAnyChars::SourceCoords::fill(
|
|||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uint32_t
|
||||
TokenStreamAnyChars::SourceCoords::lineIndexOf(uint32_t offset) const {
|
||||
TokenStreamAnyChars::SourceCoords::indexFromOffset(uint32_t offset) const {
|
||||
uint32_t iMin, iMax, iMid;
|
||||
|
||||
if (lineStartOffsets_[lastLineIndex_] <= offset) {
|
||||
if (lineStartOffsets_[lastIndex_] <= offset) {
|
||||
// If we reach here, offset is on a line the same as or higher than
|
||||
// last time. Check first for the +0, +1, +2 cases, because they
|
||||
// typically cover 85--98% of cases.
|
||||
if (offset < lineStartOffsets_[lastLineIndex_ + 1]) {
|
||||
return lastLineIndex_; // lineIndex is same as last time
|
||||
if (offset < lineStartOffsets_[lastIndex_ + 1]) {
|
||||
return lastIndex_; // index is same as last time
|
||||
}
|
||||
|
||||
// If we reach here, there must be at least one more entry (plus the
|
||||
// sentinel). Try it.
|
||||
lastLineIndex_++;
|
||||
if (offset < lineStartOffsets_[lastLineIndex_ + 1]) {
|
||||
return lastLineIndex_; // lineIndex is one higher than last time
|
||||
lastIndex_++;
|
||||
if (offset < lineStartOffsets_[lastIndex_ + 1]) {
|
||||
return lastIndex_; // index is one higher than last time
|
||||
}
|
||||
|
||||
// The same logic applies here.
|
||||
lastLineIndex_++;
|
||||
if (offset < lineStartOffsets_[lastLineIndex_ + 1]) {
|
||||
return lastLineIndex_; // lineIndex is two higher than last time
|
||||
lastIndex_++;
|
||||
if (offset < lineStartOffsets_[lastIndex_ + 1]) {
|
||||
return lastIndex_; // index is two higher than last time
|
||||
}
|
||||
|
||||
// No luck. Oh well, we have a better-than-default starting point for
|
||||
// the binary search.
|
||||
iMin = lastLineIndex_ + 1;
|
||||
iMin = lastIndex_ + 1;
|
||||
MOZ_ASSERT(iMin <
|
||||
lineStartOffsets_.length() - 1); // -1 due to the sentinel
|
||||
|
||||
|
@ -454,27 +455,18 @@ TokenStreamAnyChars::SourceCoords::lineIndexOf(uint32_t offset) const {
|
|||
iMax = iMid; // offset is below or within lineStartOffsets_[iMid]
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(iMax == iMin);
|
||||
MOZ_ASSERT(lineStartOffsets_[iMin] <= offset &&
|
||||
offset < lineStartOffsets_[iMin + 1]);
|
||||
lastLineIndex_ = iMin;
|
||||
MOZ_ASSERT(lineStartOffsets_[iMin] <= offset);
|
||||
MOZ_ASSERT(offset < lineStartOffsets_[iMin + 1]);
|
||||
|
||||
lastIndex_ = iMin;
|
||||
return iMin;
|
||||
}
|
||||
|
||||
uint32_t TokenStreamAnyChars::SourceCoords::lineNum(uint32_t offset) const {
|
||||
uint32_t lineIndex = lineIndexOf(offset);
|
||||
return lineIndexToNum(lineIndex);
|
||||
}
|
||||
|
||||
uint32_t TokenStreamAnyChars::SourceCoords::columnIndex(uint32_t offset) const {
|
||||
return lineIndexAndOffsetToColumn(lineIndexOf(offset), offset);
|
||||
}
|
||||
|
||||
void TokenStreamAnyChars::SourceCoords::lineNumAndColumnIndex(
|
||||
uint32_t offset, uint32_t* lineNum, uint32_t* column) const {
|
||||
uint32_t lineIndex = lineIndexOf(offset);
|
||||
*lineNum = lineIndexToNum(lineIndex);
|
||||
*column = lineIndexAndOffsetToColumn(lineIndex, offset);
|
||||
TokenStreamAnyChars::SourceCoords::LineToken
|
||||
TokenStreamAnyChars::SourceCoords::lineToken(uint32_t offset) const {
|
||||
return LineToken(indexFromOffset(offset), offset);
|
||||
}
|
||||
|
||||
TokenStreamAnyChars::TokenStreamAnyChars(JSContext* cx,
|
||||
|
@ -678,8 +670,8 @@ MOZ_COLD void TokenStreamChars<Utf8Unit, AnyCharsAccess>::internalEncodingError(
|
|||
|
||||
TokenStreamAnyChars& anyChars = anyCharsAccess();
|
||||
|
||||
bool hasLineOfContext = anyChars.fillExcludingContext(&err, offset);
|
||||
if (hasLineOfContext) {
|
||||
bool canAddLineOfContext = fillExceptingContext(&err, offset);
|
||||
if (canAddLineOfContext) {
|
||||
if (!internalComputeLineOfContext(&err, offset)) {
|
||||
break;
|
||||
}
|
||||
|
@ -717,7 +709,7 @@ MOZ_COLD void TokenStreamChars<Utf8Unit, AnyCharsAccess>::internalEncodingError(
|
|||
ptr[-1] = '\0';
|
||||
|
||||
uint32_t line, column;
|
||||
anyChars.srcCoords.lineNumAndColumnIndex(offset, &line, &column);
|
||||
computeLineAndColumn(offset, &line, &column);
|
||||
|
||||
if (!notes->addNoteASCII(anyChars.cx, anyChars.getFilename(), line, column,
|
||||
GetErrorMessage, nullptr, JSMSG_BAD_CODE_UNITS,
|
||||
|
@ -1318,7 +1310,7 @@ void TokenStreamAnyChars::computeErrorMetadataNoOffset(ErrorMetadata* err) {
|
|||
MOZ_ASSERT(err->lineOfContext == nullptr);
|
||||
}
|
||||
|
||||
bool TokenStreamAnyChars::fillExcludingContext(ErrorMetadata* err,
|
||||
bool TokenStreamAnyChars::fillExceptingContext(ErrorMetadata* err,
|
||||
uint32_t offset) {
|
||||
err->isMuted = mutedErrors;
|
||||
|
||||
|
@ -1336,7 +1328,6 @@ bool TokenStreamAnyChars::fillExcludingContext(ErrorMetadata* err,
|
|||
|
||||
// Otherwise use this TokenStreamAnyChars's location information.
|
||||
err->filename = filename_;
|
||||
srcCoords.lineNumAndColumnIndex(offset, &err->lineNumber, &err->columnNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1346,19 +1337,6 @@ bool TokenStreamSpecific<Unit, AnyCharsAccess>::hasTokenizationStarted() const {
|
|||
return anyChars.isCurrentTokenType(TokenKind::Eof) && !anyChars.isEOF();
|
||||
}
|
||||
|
||||
void TokenStreamAnyChars::lineAndColumnAt(size_t offset, uint32_t* line,
|
||||
uint32_t* column) const {
|
||||
srcCoords.lineNumAndColumnIndex(offset, line, column);
|
||||
}
|
||||
|
||||
template <typename Unit, class AnyCharsAccess>
|
||||
void TokenStreamSpecific<Unit, AnyCharsAccess>::currentLineAndColumn(
|
||||
uint32_t* line, uint32_t* column) const {
|
||||
const TokenStreamAnyChars& anyChars = anyCharsAccess();
|
||||
uint32_t offset = anyChars.currentToken().pos.begin;
|
||||
anyChars.srcCoords.lineNumAndColumnIndex(offset, line, column);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void SourceUnits<char16_t>::computeWindowOffsetAndLength(
|
||||
const char16_t* encodedWindow, size_t encodedTokenOffset,
|
||||
|
@ -1507,15 +1485,15 @@ bool TokenStreamSpecific<Unit, AnyCharsAccess>::computeErrorMetadata(
|
|||
}
|
||||
|
||||
// This function's return value isn't a success/failure indication: it
|
||||
// returns true if this TokenStream's location information could be used,
|
||||
// and it returns false when that information can't be used (and so we
|
||||
// can't provide a line of context).
|
||||
if (!anyCharsAccess().fillExcludingContext(err, offset)) {
|
||||
return true;
|
||||
// returns true if this TokenStream can be used to provide a line of
|
||||
// context.
|
||||
if (fillExceptingContext(err, offset)) {
|
||||
// Add a line of context from this TokenStream to help with debugging.
|
||||
return internalComputeLineOfContext(err, offset);
|
||||
}
|
||||
|
||||
// Add a line of context from this TokenStream to help with debugging.
|
||||
return internalComputeLineOfContext(err, offset);
|
||||
// We can't fill in any more here.
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Unit, class AnyCharsAccess>
|
||||
|
@ -3509,9 +3487,21 @@ const char* TokenKindToString(TokenKind tt) {
|
|||
template class TokenStreamCharsBase<Utf8Unit>;
|
||||
template class TokenStreamCharsBase<char16_t>;
|
||||
|
||||
template class GeneralTokenStreamChars<char16_t, TokenStreamAnyCharsAccess>;
|
||||
template class TokenStreamChars<char16_t, TokenStreamAnyCharsAccess>;
|
||||
template class TokenStreamSpecific<char16_t, TokenStreamAnyCharsAccess>;
|
||||
|
||||
template class GeneralTokenStreamChars<
|
||||
Utf8Unit, ParserAnyCharsAccess<GeneralParser<FullParseHandler, Utf8Unit>>>;
|
||||
template class GeneralTokenStreamChars<
|
||||
Utf8Unit,
|
||||
ParserAnyCharsAccess<GeneralParser<SyntaxParseHandler, Utf8Unit>>>;
|
||||
template class GeneralTokenStreamChars<
|
||||
char16_t, ParserAnyCharsAccess<GeneralParser<FullParseHandler, char16_t>>>;
|
||||
template class GeneralTokenStreamChars<
|
||||
char16_t,
|
||||
ParserAnyCharsAccess<GeneralParser<SyntaxParseHandler, char16_t>>>;
|
||||
|
||||
template class TokenStreamChars<
|
||||
Utf8Unit, ParserAnyCharsAccess<GeneralParser<FullParseHandler, Utf8Unit>>>;
|
||||
template class TokenStreamChars<
|
||||
|
|
|
@ -223,6 +223,8 @@ class AutoKeepAtoms;
|
|||
|
||||
namespace frontend {
|
||||
|
||||
class FunctionBox;
|
||||
|
||||
struct TokenPos {
|
||||
uint32_t begin; // Offset of the token's first code unit.
|
||||
uint32_t end; // Offset of 1 past the token's last code unit.
|
||||
|
@ -729,7 +731,8 @@ class TokenStreamAnyChars : public TokenStreamShared {
|
|||
char16_t* sourceMapURL() { return sourceMapURL_.get(); }
|
||||
|
||||
// This class maps a sourceUnits offset (which is 0-indexed) to a line
|
||||
// number (which is 1-indexed) and a column index (which is 0-indexed).
|
||||
// number (which is 1-indexed) and an offset (which is 0-indexed) in
|
||||
// code *units* (not code points, not bytes) into the line.
|
||||
class SourceCoords {
|
||||
// For a given buffer holding source code, |lineStartOffsets_| has one
|
||||
// element per line of source code, plus one sentinel element. Each
|
||||
|
@ -746,87 +749,196 @@ class TokenStreamAnyChars : public TokenStreamShared {
|
|||
//
|
||||
// [0, 7, 14, 15, MAX_PTR]
|
||||
//
|
||||
// To convert a "line number" to a "line index" (i.e. an index into
|
||||
// |lineStartOffsets_|), subtract |initialLineNum_|. E.g. line 3's
|
||||
// line index is (3 - initialLineNum_), which is 2. Therefore
|
||||
// lineStartOffsets_[2] holds the buffer offset for the start of line 3,
|
||||
// which is 14. (Note that |initialLineNum_| is often 1, but not
|
||||
// always.)
|
||||
// To convert a "line number" to an "index" into |lineStartOffsets_|,
|
||||
// subtract |initialLineNum_|. E.g. line 3's index is
|
||||
// (3 - initialLineNum_), which is 2. Therefore lineStartOffsets_[2]
|
||||
// holds the buffer offset for the start of line 3, which is 14. (Note
|
||||
// that |initialLineNum_| is often 1, but not always.
|
||||
//
|
||||
// The first element is always initialLineOffset, passed to the
|
||||
// constructor, and the last element is always the MAX_PTR sentinel.
|
||||
//
|
||||
// offset-to-line/column lookups are O(log n) in the worst case (binary
|
||||
// search), but in practice they're heavily clustered and we do better
|
||||
// than that by using the previous lookup's result (lastLineIndex_) as
|
||||
// a starting point.
|
||||
// Offset-to-{line,offset-into-line} lookups are O(log n) in the worst
|
||||
// case (binary search), but in practice they're heavily clustered and
|
||||
// we do better than that by using the previous lookup's result
|
||||
// (lastIndex_) as a starting point.
|
||||
//
|
||||
// Checking if an offset lies within a particular line number
|
||||
// (isOnThisLine()) is O(1).
|
||||
//
|
||||
Vector<uint32_t, 128> lineStartOffsets_;
|
||||
|
||||
/** The line number on which the source text begins. */
|
||||
uint32_t initialLineNum_;
|
||||
|
||||
/** The column number at which the source text begins. */
|
||||
uint32_t initialColumn_;
|
||||
|
||||
// This is mutable because it's modified on every search, but that fact
|
||||
// isn't visible outside this class.
|
||||
mutable uint32_t lastLineIndex_;
|
||||
/**
|
||||
* The index corresponding to the last offset lookup -- used so that if
|
||||
* offset lookups proceed in increasing order, and and the offset appears
|
||||
* in the next couple lines from the last offset, we can avoid a full
|
||||
* binary-search.
|
||||
*
|
||||
* This is mutable because it's modified on every search, but that fact
|
||||
* isn't visible outside this class.
|
||||
*/
|
||||
mutable uint32_t lastIndex_;
|
||||
|
||||
uint32_t lineIndexOf(uint32_t offset) const;
|
||||
uint32_t indexFromOffset(uint32_t offset) const;
|
||||
|
||||
static const uint32_t MAX_PTR = UINT32_MAX;
|
||||
|
||||
uint32_t lineIndexToNum(uint32_t lineIndex) const {
|
||||
return lineIndex + initialLineNum_;
|
||||
uint32_t lineNumberFromIndex(uint32_t index) const {
|
||||
return index + initialLineNum_;
|
||||
}
|
||||
uint32_t lineNumToIndex(uint32_t lineNum) const {
|
||||
|
||||
uint32_t indexFromLineNumber(uint32_t lineNum) const {
|
||||
return lineNum - initialLineNum_;
|
||||
}
|
||||
uint32_t lineIndexAndOffsetToColumn(uint32_t lineIndex,
|
||||
uint32_t offset) const {
|
||||
uint32_t lineStartOffset = lineStartOffsets_[lineIndex];
|
||||
|
||||
uint32_t lineOffsetFromIndexAndOffset(uint32_t index,
|
||||
uint32_t offset) const {
|
||||
uint32_t lineStartOffset = lineStartOffsets_[index];
|
||||
MOZ_RELEASE_ASSERT(offset >= lineStartOffset);
|
||||
uint32_t column = offset - lineStartOffset;
|
||||
if (lineIndex == 0) {
|
||||
return column + initialColumn_;
|
||||
}
|
||||
return column;
|
||||
return offset - lineStartOffset;
|
||||
}
|
||||
|
||||
// This function is BAD because it's lies in the presence of multi-unit
|
||||
// code points -- unlike lineOffsetFromIndexAndOffset that doesn't
|
||||
// promise a column number. This name segregates the initial-line
|
||||
// column adjustment and the false "column" sense to a function that
|
||||
// will be removed later in this patch stack.
|
||||
uint32_t columnFromIndexAndOffset(uint32_t index, uint32_t offset) const {
|
||||
uint32_t lineOffset = lineOffsetFromIndexAndOffset(index, offset);
|
||||
return (index == 0 ? initialColumn_ : 0) + lineOffset;
|
||||
}
|
||||
|
||||
public:
|
||||
SourceCoords(JSContext* cx, uint32_t ln, uint32_t col,
|
||||
uint32_t initialLineOffset);
|
||||
SourceCoords(JSContext* cx, uint32_t initialLineNumber,
|
||||
uint32_t initialColumnNumber, uint32_t initialOffset);
|
||||
|
||||
MOZ_MUST_USE bool add(uint32_t lineNum, uint32_t lineStartOffset);
|
||||
MOZ_MUST_USE bool fill(const SourceCoords& other);
|
||||
|
||||
bool isOnThisLine(uint32_t offset, uint32_t lineNum,
|
||||
bool* onThisLine) const {
|
||||
uint32_t lineIndex = lineNumToIndex(lineNum);
|
||||
if (lineIndex + 1 >= lineStartOffsets_.length()) { // +1 due to sentinel
|
||||
uint32_t index = indexFromLineNumber(lineNum);
|
||||
if (index + 1 >= lineStartOffsets_.length()) { // +1 due to sentinel
|
||||
return false;
|
||||
}
|
||||
*onThisLine = lineStartOffsets_[lineIndex] <= offset &&
|
||||
offset < lineStartOffsets_[lineIndex + 1];
|
||||
*onThisLine = lineStartOffsets_[index] <= offset &&
|
||||
offset < lineStartOffsets_[index + 1];
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t lineNum(uint32_t offset) const;
|
||||
uint32_t columnIndex(uint32_t offset) const;
|
||||
void lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum,
|
||||
uint32_t* column) const;
|
||||
/**
|
||||
* A token, computed for an offset in source text, that can be used to
|
||||
* access line number and line-offset information for that offset.
|
||||
*
|
||||
* LineToken *alone* exposes whether the corresponding offset is in the
|
||||
* the first line of source (which may not be 1, depending on
|
||||
* |initialLineNumber|), and whether it's in the same line as
|
||||
* another LineToken.
|
||||
*/
|
||||
class LineToken {
|
||||
uint32_t index;
|
||||
#ifdef DEBUG
|
||||
uint32_t offset_; // stored for consistency-of-use assertions
|
||||
#endif
|
||||
|
||||
friend class SourceCoords;
|
||||
|
||||
public:
|
||||
explicit LineToken(uint32_t index, uint32_t offset)
|
||||
: index(index)
|
||||
#ifdef DEBUG
|
||||
,
|
||||
offset_(offset)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
bool isFirstLine() const { return index == 0; }
|
||||
|
||||
bool isSameLine(LineToken other) const { return index == other.index; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute a token usable to access information about the line at the
|
||||
* given offset.
|
||||
*
|
||||
* The only information directly accessible in a token is whether it
|
||||
* corresponds to the first line of source text (which may not be line
|
||||
* 1, depending on the |initialLineNumber| value used to construct
|
||||
* this). Use |lineNumber(LineToken)| to compute the actual line
|
||||
* number (incorporating the contribution of |initialLineNumber|).
|
||||
*/
|
||||
LineToken lineToken(uint32_t offset) const;
|
||||
|
||||
/** Compute the line number for the given token. */
|
||||
uint32_t lineNumber(LineToken lineToken) const {
|
||||
return lineNumberFromIndex(lineToken.index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the offset *in code units* of |offset| from the start of the
|
||||
* line containing it, plus any contribution from |initialColumnNumber|
|
||||
* passed to the |SourceCoords| constructor.
|
||||
*
|
||||
* This is only really a "column". A subsequent patch in this stack
|
||||
* removes it, computing a multi-unit-aware column number elsewhere in
|
||||
* Unit-sensitive manner.
|
||||
*/
|
||||
uint32_t columnIndex(LineToken lineToken, uint32_t offset) const {
|
||||
MOZ_ASSERT(lineToken.offset_ == offset, "use a consistent token");
|
||||
|
||||
uint32_t lineStartOffset = lineStartOffsets_[lineToken.index];
|
||||
MOZ_RELEASE_ASSERT(offset >= lineStartOffset);
|
||||
|
||||
uint32_t relative = offset - lineStartOffset;
|
||||
return (lineToken.isFirstLine() ? initialColumn_ : 0) + relative;
|
||||
}
|
||||
};
|
||||
|
||||
SourceCoords srcCoords;
|
||||
|
||||
JSContext* context() const { return cx; }
|
||||
|
||||
using LineToken = SourceCoords::LineToken;
|
||||
|
||||
LineToken lineToken(uint32_t offset) const {
|
||||
return srcCoords.lineToken(offset);
|
||||
}
|
||||
|
||||
uint32_t lineNumber(LineToken lineToken) const {
|
||||
return srcCoords.lineNumber(lineToken);
|
||||
}
|
||||
|
||||
uint32_t columnIndex(LineToken lineToken, uint32_t offset) const {
|
||||
return srcCoords.columnIndex(lineToken, offset);
|
||||
}
|
||||
|
||||
// A helper function if you want an offset's line *and* "column" info.
|
||||
void lineAndColumnAt(uint32_t offset, uint32_t* lineNum,
|
||||
uint32_t* column) const {
|
||||
LineToken token = srcCoords.lineToken(offset);
|
||||
*lineNum = srcCoords.lineNumber(token);
|
||||
*column = srcCoords.columnIndex(token, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in |err|, excepting line-of-context-related fields. If the token
|
||||
* stream has location information, use that and return true. If it does
|
||||
* not, use the caller's location information and return false.
|
||||
* Fill in |err|.
|
||||
*
|
||||
* If the token stream doesn't have location info for this error, use the
|
||||
* caller's location (including line/column number) and return false. (No
|
||||
* line of context is set.)
|
||||
*
|
||||
* Otherwise fill in everything in |err| except 1) line/column numbers and
|
||||
* 2) line-of-context-related fields and return true. The caller *must*
|
||||
* fill in the line/column number; filling the line of context is optional.
|
||||
*/
|
||||
bool fillExcludingContext(ErrorMetadata* err, uint32_t offset);
|
||||
bool fillExceptingContext(ErrorMetadata* err, uint32_t offset);
|
||||
|
||||
MOZ_ALWAYS_INLINE void updateFlagsForEOL() { flags.isDirtyLine = false; }
|
||||
|
||||
|
@ -875,8 +987,6 @@ class TokenStreamAnyChars : public TokenStreamShared {
|
|||
|
||||
// ErrorReporter API Helpers
|
||||
|
||||
void lineAndColumnAt(size_t offset, uint32_t* line, uint32_t* column) const;
|
||||
|
||||
// This is just straight up duplicated from TokenStreamSpecific's inheritance
|
||||
// of ErrorReporter's reportErrorNoOffset. varargs delenda est.
|
||||
void reportErrorNoOffset(unsigned errorNumber, ...);
|
||||
|
@ -1858,6 +1968,25 @@ class GeneralTokenStreamChars : public SpecializedTokenStreamCharsBase<Unit> {
|
|||
return static_cast<TokenStreamSpecific*>(this);
|
||||
}
|
||||
|
||||
void computeLineAndColumn(uint32_t offset, uint32_t* line,
|
||||
uint32_t* column) const {
|
||||
anyCharsAccess().lineAndColumnAt(offset, line, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in |err| completely, except for line-of-context information.
|
||||
*
|
||||
* Return true if the caller can compute a line of context from the token
|
||||
* stream. Otherwise return false.
|
||||
*/
|
||||
MOZ_MUST_USE bool fillExceptingContext(ErrorMetadata* err, uint32_t offset) {
|
||||
if (anyCharsAccess().fillExceptingContext(err, offset)) {
|
||||
computeLineAndColumn(offset, &err->lineNumber, &err->columnNumber);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void newSimpleToken(TokenKind kind, TokenStart start,
|
||||
TokenStreamShared::Modifier modifier, TokenKind* out) {
|
||||
newToken(kind, start, modifier, out);
|
||||
|
@ -2023,6 +2152,8 @@ class GeneralTokenStreamChars : public SpecializedTokenStreamCharsBase<Unit> {
|
|||
|
||||
return drainCharBufferIntoAtom(anyChars.cx);
|
||||
}
|
||||
|
||||
inline void setFunctionStart(FunctionBox* funbox) const;
|
||||
};
|
||||
|
||||
template <typename Unit, class AnyCharsAccess>
|
||||
|
@ -2095,6 +2226,8 @@ class TokenStreamChars<mozilla::Utf8Unit, AnyCharsAccess>
|
|||
|
||||
protected:
|
||||
using GeneralCharsBase::anyCharsAccess;
|
||||
using GeneralCharsBase::computeLineAndColumn;
|
||||
using GeneralCharsBase::fillExceptingContext;
|
||||
using GeneralCharsBase::internalComputeLineOfContext;
|
||||
using TokenStreamCharsShared::isAsciiCodePoint;
|
||||
// Deliberately don't |using| |sourceUnits| because of bug 1472569. :-(
|
||||
|
@ -2293,6 +2426,7 @@ class MOZ_STACK_CLASS TokenStreamSpecific
|
|||
// cost of this ever-changing laundry list of |using|s. So it goes.
|
||||
public:
|
||||
using GeneralCharsBase::anyCharsAccess;
|
||||
using GeneralCharsBase::computeLineAndColumn;
|
||||
|
||||
private:
|
||||
using typename CharsBase::SourceUnits;
|
||||
|
@ -2306,6 +2440,7 @@ class MOZ_STACK_CLASS TokenStreamSpecific
|
|||
using CharsBase::fillCharBufferFromSourceNormalizingAsciiLineBreaks;
|
||||
using CharsBase::matchCodeUnit;
|
||||
using CharsBase::matchLineTerminator;
|
||||
using GeneralCharsBase::fillExceptingContext;
|
||||
using GeneralCharsBase::getCodeUnit;
|
||||
using GeneralCharsBase::getFullAsciiCodePoint;
|
||||
using GeneralCharsBase::internalComputeLineOfContext;
|
||||
|
@ -2369,17 +2504,26 @@ class MOZ_STACK_CLASS TokenStreamSpecific
|
|||
anyCharsAccess().lineAndColumnAt(offset, line, column);
|
||||
}
|
||||
|
||||
void currentLineAndColumn(uint32_t* line, uint32_t* column) const final;
|
||||
void currentLineAndColumn(uint32_t* line, uint32_t* column) const final {
|
||||
computeLineAndColumn(anyCharsAccess().currentToken().pos.begin, line,
|
||||
column);
|
||||
}
|
||||
|
||||
bool isOnThisLine(size_t offset, uint32_t lineNum,
|
||||
bool* onThisLine) const final {
|
||||
return anyCharsAccess().srcCoords.isOnThisLine(offset, lineNum, onThisLine);
|
||||
}
|
||||
|
||||
uint32_t lineAt(size_t offset) const final {
|
||||
return anyCharsAccess().srcCoords.lineNum(offset);
|
||||
const auto& anyChars = anyCharsAccess();
|
||||
auto lineToken = anyChars.lineToken(offset);
|
||||
return anyChars.lineNumber(lineToken);
|
||||
}
|
||||
|
||||
uint32_t columnAt(size_t offset) const final {
|
||||
return anyCharsAccess().srcCoords.columnIndex(offset);
|
||||
const TokenStreamAnyChars& anyChars = anyCharsAccess();
|
||||
auto lineToken = anyChars.lineToken(offset);
|
||||
return anyChars.columnIndex(lineToken, offset);
|
||||
}
|
||||
|
||||
bool hasTokenizationStarted() const final;
|
||||
|
@ -2606,13 +2750,18 @@ class MOZ_STACK_CLASS TokenStreamSpecific
|
|||
if (!getToken(&tmp, modifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Token& next = anyChars.currentToken();
|
||||
anyChars.ungetToken();
|
||||
|
||||
const auto& srcCoords = anyChars.srcCoords;
|
||||
*ttp = srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
|
||||
? next.type
|
||||
: TokenKind::Eol;
|
||||
// Careful, |next| points to an initialized-but-not-allocated Token!
|
||||
// This is safe because we don't modify token data below.
|
||||
|
||||
auto currentEndToken = anyChars.lineToken(curr.pos.end);
|
||||
auto nextBeginToken = anyChars.lineToken(next.pos.begin);
|
||||
|
||||
*ttp =
|
||||
currentEndToken.isSameLine(nextBeginToken) ? next.type : TokenKind::Eol;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
143
js/src/gc/GC.cpp
143
js/src/gc/GC.cpp
|
@ -161,10 +161,10 @@
|
|||
*
|
||||
* During incremental collection it is possible, although unlikely, for
|
||||
* conditions to change such that incremental collection is no longer safe. In
|
||||
* this case, the collection is 'reset' by ResetIncrementalGC(). If we are in
|
||||
* this case, the collection is 'reset' by resetIncrementalGC(). If we are in
|
||||
* the mark state, this just stops marking, but if we have started sweeping
|
||||
* already, we continue until we have swept the current sweep group. Following a
|
||||
* reset, a new non-incremental collection is started.
|
||||
* already, we continue non-incrementally until we have swept the current sweep
|
||||
* group. Following a reset, a new collection is started.
|
||||
*
|
||||
* Compacting GC
|
||||
* -------------
|
||||
|
@ -4959,21 +4959,22 @@ void GCRuntime::getNextSweepGroup() {
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(abortSweepAfterCurrentGroup, !isIncremental);
|
||||
if (!isIncremental) {
|
||||
ZoneComponentFinder::mergeGroups(currentSweepGroup);
|
||||
}
|
||||
|
||||
for (Zone* zone = currentSweepGroup; zone; zone = zone->nextNodeInGroup()) {
|
||||
MOZ_ASSERT(zone->isGCMarking());
|
||||
MOZ_ASSERT(!zone->isQueuedForBackgroundSweep());
|
||||
}
|
||||
|
||||
if (!isIncremental) {
|
||||
ZoneComponentFinder::mergeGroups(currentSweepGroup);
|
||||
}
|
||||
|
||||
if (abortSweepAfterCurrentGroup) {
|
||||
MOZ_ASSERT(!isIncremental);
|
||||
for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
|
||||
MOZ_ASSERT(!zone->gcNextGraphComponent);
|
||||
zone->setNeedsIncrementalBarrier(false);
|
||||
zone->changeGCState(Zone::Mark, Zone::NoGC);
|
||||
zone->arenas.unmarkPreMarkedFreeCells();
|
||||
zone->gcGrayRoots().clearAndFree();
|
||||
}
|
||||
|
||||
|
@ -6663,8 +6664,9 @@ void GCRuntime::finishCollection() {
|
|||
zone->notifyObservingDebuggers();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!zone->isCollectingFromAnyThread());
|
||||
MOZ_ASSERT(!zone->wasGCStarted());
|
||||
MOZ_ASSERT(!zone->needsIncrementalBarrier());
|
||||
MOZ_ASSERT(!zone->isOnList());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(zonesToMaybeCompact.ref().isEmpty());
|
||||
|
@ -6715,24 +6717,24 @@ JS_PUBLIC_API JS::HeapState JS::RuntimeHeapState() {
|
|||
|
||||
GCRuntime::IncrementalResult GCRuntime::resetIncrementalGC(
|
||||
gc::AbortReason reason) {
|
||||
// Drop as much work as possible from an ongoing incremental GC so
|
||||
// we can start a new GC after it has finished.
|
||||
if (incrementalState == State::NotActive) {
|
||||
return IncrementalResult::Ok;
|
||||
}
|
||||
|
||||
minorGC(JS::gcreason::RESET, gcstats::PhaseKind::EVICT_NURSERY_FOR_MAJOR_GC);
|
||||
|
||||
AutoGCSession session(rt, JS::HeapState::MajorCollecting);
|
||||
|
||||
switch (incrementalState) {
|
||||
case State::NotActive:
|
||||
case State::MarkRoots:
|
||||
case State::Finish:
|
||||
MOZ_CRASH("Unexpected GC state in resetIncrementalGC");
|
||||
break;
|
||||
|
||||
case State::Mark: {
|
||||
/* Cancel any ongoing marking. */
|
||||
// Cancel any ongoing marking.
|
||||
marker.reset();
|
||||
marker.stop();
|
||||
clearBufferedGrayRoots();
|
||||
|
||||
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
|
@ -6751,7 +6753,7 @@ GCRuntime::IncrementalResult GCRuntime::resetIncrementalGC(
|
|||
}
|
||||
|
||||
lastMarkSlice = false;
|
||||
incrementalState = State::NotActive;
|
||||
incrementalState = State::Finish;
|
||||
|
||||
MOZ_ASSERT(!marker.shouldCheckCompartments());
|
||||
|
||||
|
@ -6759,90 +6761,39 @@ GCRuntime::IncrementalResult GCRuntime::resetIncrementalGC(
|
|||
}
|
||||
|
||||
case State::Sweep: {
|
||||
// Finish sweeping the current sweep group, then abort.
|
||||
marker.reset();
|
||||
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
c->gcState.scheduledForDestruction = false;
|
||||
}
|
||||
|
||||
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
|
||||
if (zone->isGCMarking()) {
|
||||
zone->arenas.unmarkPreMarkedFreeCells();
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish sweeping the current sweep group, then abort. */
|
||||
abortSweepAfterCurrentGroup = true;
|
||||
|
||||
/* Don't perform any compaction after sweeping. */
|
||||
bool wasCompacting = isCompacting;
|
||||
isCompacting = false;
|
||||
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
incrementalSlice(unlimited, JS::gcreason::RESET, session);
|
||||
|
||||
isCompacting = wasCompacting;
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap(stats(),
|
||||
gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
|
||||
waitBackgroundSweepOrAllocEnd();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Finalize: {
|
||||
{
|
||||
gcstats::AutoPhase ap(stats(),
|
||||
gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
|
||||
waitBackgroundSweepOrAllocEnd();
|
||||
}
|
||||
|
||||
bool wasCompacting = isCompacting;
|
||||
isCompacting = false;
|
||||
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
incrementalSlice(unlimited, JS::gcreason::RESET, session);
|
||||
|
||||
isCompacting = wasCompacting;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Compact: {
|
||||
bool wasCompacting = isCompacting;
|
||||
|
||||
isCompacting = true;
|
||||
// Skip any remaining zones that would have been compacted.
|
||||
MOZ_ASSERT(isCompacting);
|
||||
startedCompacting = true;
|
||||
zonesToMaybeCompact.ref().clear();
|
||||
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
incrementalSlice(unlimited, JS::gcreason::RESET, session);
|
||||
|
||||
isCompacting = wasCompacting;
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Decommit: {
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
incrementalSlice(unlimited, JS::gcreason::RESET, session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stats().reset(reason);
|
||||
|
||||
#ifdef DEBUG
|
||||
assertBackgroundSweepingFinished();
|
||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||
MOZ_ASSERT(!zone->isCollectingFromAnyThread());
|
||||
MOZ_ASSERT(!zone->needsIncrementalBarrier());
|
||||
MOZ_ASSERT(!zone->isOnList());
|
||||
}
|
||||
MOZ_ASSERT(zonesToMaybeCompact.ref().isEmpty());
|
||||
MOZ_ASSERT(incrementalState == State::NotActive);
|
||||
#endif
|
||||
|
||||
return IncrementalResult::ResetIncremental;
|
||||
}
|
||||
|
||||
|
@ -6912,7 +6863,7 @@ static bool ShouldCleanUpEverything(JS::gcreason::Reason reason,
|
|||
return IsShutdownGC(reason) || gckind == GC_SHRINK;
|
||||
}
|
||||
|
||||
GCRuntime::IncrementalResult GCRuntime::incrementalSlice(
|
||||
void GCRuntime::incrementalSlice(
|
||||
SliceBudget& budget, JS::gcreason::Reason reason, AutoGCSession& session) {
|
||||
AutoDisableBarriers disableBarriers(rt);
|
||||
|
||||
|
@ -6979,7 +6930,7 @@ GCRuntime::IncrementalResult GCRuntime::incrementalSlice(
|
|||
case State::MarkRoots:
|
||||
if (!beginMarkPhase(reason, session)) {
|
||||
incrementalState = State::NotActive;
|
||||
return IncrementalResult::Ok;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we needed delayed marking for gray roots, then collect until done.
|
||||
|
@ -7063,20 +7014,21 @@ GCRuntime::IncrementalResult GCRuntime::incrementalSlice(
|
|||
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
case State::Finalize: {
|
||||
gcstats::AutoPhase ap(stats(),
|
||||
gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
|
||||
case State::Finalize:
|
||||
{
|
||||
gcstats::AutoPhase ap(stats(),
|
||||
gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
|
||||
|
||||
// Yield until background finalization is done.
|
||||
if (!budget.isUnlimited()) {
|
||||
// Poll for end of background sweeping
|
||||
if (isBackgroundSweeping()) {
|
||||
break;
|
||||
// Yield until background finalization is done.
|
||||
if (!budget.isUnlimited()) {
|
||||
// Poll for end of background sweeping
|
||||
if (isBackgroundSweeping()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
waitBackgroundSweepEnd();
|
||||
}
|
||||
} else {
|
||||
waitBackgroundSweepEnd();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Re-sweep the zones list, now that background finalization is
|
||||
|
@ -7130,16 +7082,19 @@ GCRuntime::IncrementalResult GCRuntime::incrementalSlice(
|
|||
}
|
||||
|
||||
decommitTask.join();
|
||||
|
||||
incrementalState = State::Finish;
|
||||
|
||||
MOZ_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case State::Finish:
|
||||
finishCollection();
|
||||
incrementalState = State::NotActive;
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(safeToYield);
|
||||
|
||||
return IncrementalResult::Ok;
|
||||
}
|
||||
|
||||
gc::AbortReason gc::IsIncrementalGCUnsafe(JSRuntime* rt) {
|
||||
|
@ -7243,6 +7198,7 @@ GCRuntime::IncrementalResult GCRuntime::budgetIncrementalGC(
|
|||
}
|
||||
|
||||
if (reset) {
|
||||
budget.makeUnlimited();
|
||||
return resetIncrementalGC(AbortReason::ZoneChange);
|
||||
}
|
||||
|
||||
|
@ -7341,7 +7297,7 @@ void GCRuntime::maybeCallGCCallback(JSGCStatus status) {
|
|||
* implementation.
|
||||
*/
|
||||
MOZ_NEVER_INLINE GCRuntime::IncrementalResult GCRuntime::gcCycle(
|
||||
bool nonincrementalByAPI, SliceBudget& budget,
|
||||
bool nonincrementalByAPI, SliceBudget budget,
|
||||
JS::gcreason::Reason reason) {
|
||||
// Assert if this is a GC unsafe region.
|
||||
rt->mainContextFromOwnThread()->verifyIsSafeToGC();
|
||||
|
@ -7357,11 +7313,8 @@ MOZ_NEVER_INLINE GCRuntime::IncrementalResult GCRuntime::gcCycle(
|
|||
reason);
|
||||
|
||||
auto result = budgetIncrementalGC(nonincrementalByAPI, reason, budget);
|
||||
|
||||
// If an ongoing incremental GC was reset, we may need to restart.
|
||||
if (result == IncrementalResult::ResetIncremental) {
|
||||
MOZ_ASSERT(!isIncrementalGCInProgress());
|
||||
return result;
|
||||
reason = JS::gcreason::RESET;
|
||||
}
|
||||
|
||||
if (shouldCollectNurseryForSlice(nonincrementalByAPI, budget)) {
|
||||
|
@ -7397,7 +7350,7 @@ MOZ_NEVER_INLINE GCRuntime::IncrementalResult GCRuntime::gcCycle(
|
|||
|
||||
gcTracer.traceMajorGCStart();
|
||||
|
||||
result = incrementalSlice(budget, reason, session);
|
||||
incrementalSlice(budget, reason, session);
|
||||
|
||||
chunkAllocationSinceLastGC = false;
|
||||
|
||||
|
@ -7408,6 +7361,9 @@ MOZ_NEVER_INLINE GCRuntime::IncrementalResult GCRuntime::gcCycle(
|
|||
|
||||
gcTracer.traceMajorGCEnd();
|
||||
|
||||
MOZ_ASSERT_IF(result == IncrementalResult::ResetIncremental,
|
||||
!isIncrementalGCInProgress());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -7430,10 +7386,13 @@ bool GCRuntime::shouldCollectNurseryForSlice(bool nonincrementalByAPI,
|
|||
nursery().freeSpace() <
|
||||
tunables.nurseryFreeThresholdForIdleCollection() ||
|
||||
hasIncrementalTwoSliceZealMode());
|
||||
default:
|
||||
// State::MarkRoots can't ever happen here.
|
||||
MOZ_CRASH("Unhandled GC state");
|
||||
case State::Finish:
|
||||
return false;
|
||||
case State::MarkRoots:
|
||||
MOZ_CRASH("Unexpected GC state");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
|
|
|
@ -27,7 +27,8 @@ enum class MarkColor : uint32_t { Black = 0, Gray };
|
|||
D(Sweep) \
|
||||
D(Finalize) \
|
||||
D(Compact) \
|
||||
D(Decommit)
|
||||
D(Decommit) \
|
||||
D(Finish)
|
||||
enum class State {
|
||||
#define MAKE_STATE(name) name,
|
||||
GCSTATES(MAKE_STATE)
|
||||
|
|
|
@ -591,12 +591,12 @@ class GCRuntime {
|
|||
* * Ok otherwise.
|
||||
*/
|
||||
MOZ_MUST_USE IncrementalResult gcCycle(bool nonincrementalByAPI,
|
||||
SliceBudget& budget,
|
||||
SliceBudget budget,
|
||||
JS::gcreason::Reason reason);
|
||||
bool shouldRepeatForDeadZone(JS::gcreason::Reason reason);
|
||||
IncrementalResult incrementalSlice(SliceBudget& budget,
|
||||
JS::gcreason::Reason reason,
|
||||
AutoGCSession& session);
|
||||
void incrementalSlice(SliceBudget& budget,
|
||||
JS::gcreason::Reason reason,
|
||||
AutoGCSession& session);
|
||||
MOZ_MUST_USE bool shouldCollectNurseryForSlice(bool nonincrementalByAPI,
|
||||
SliceBudget& budget);
|
||||
|
||||
|
|
|
@ -314,6 +314,10 @@ static inline bool ShouldMarkCrossCompartment(GCMarker* marker, JSObject* src,
|
|||
}
|
||||
|
||||
if (color == MarkColor::Black) {
|
||||
// Check our sweep groups are correct: we should never have to
|
||||
// mark something in a zone that we have started sweeping.
|
||||
MOZ_ASSERT_IF(!dst.isMarkedBlack(), !dstZone->isGCSweeping());
|
||||
|
||||
/*
|
||||
* Having black->gray edges violates our promise to the cycle
|
||||
* collector. This can happen if we're collecting a compartment and it
|
||||
|
@ -326,8 +330,12 @@ static inline bool ShouldMarkCrossCompartment(GCMarker* marker, JSObject* src,
|
|||
UnmarkGrayGCThing(marker->runtime(),
|
||||
JS::GCCellPtr(&dst, dst.getTraceKind()));
|
||||
}
|
||||
|
||||
return dstZone->isGCMarking();
|
||||
} else {
|
||||
// Check our sweep groups are correct as above.
|
||||
MOZ_ASSERT_IF(!dst.isMarkedAny(), !dstZone->isGCSweeping());
|
||||
|
||||
if (dstZone->isGCMarkingBlack()) {
|
||||
/*
|
||||
* The destination compartment is being not being marked gray now,
|
||||
|
@ -339,6 +347,7 @@ static inline bool ShouldMarkCrossCompartment(GCMarker* marker, JSObject* src,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return dstZone->isGCMarkingGray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -764,13 +764,13 @@ static JSObject* MaybeGetDelegate(Cell* cell) {
|
|||
|
||||
bool js::gc::CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key,
|
||||
Cell* value) {
|
||||
Zone* zone = map->zone();
|
||||
DebugOnly<Zone*> zone = map->zone();
|
||||
|
||||
JSObject* object = map->memberOf;
|
||||
MOZ_ASSERT_IF(object, object->zone() == zone);
|
||||
|
||||
// Debugger weak maps can have keys in different zones.
|
||||
DebugOnly<Zone*> keyZone = GetCellZone(key);
|
||||
Zone* keyZone = GetCellZone(key);
|
||||
MOZ_ASSERT_IF(!map->allowKeysInOtherZones(),
|
||||
keyZone == zone || keyZone->isAtomsZone());
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "gc/GC.h"
|
||||
#include "irregexp/RegExpCharacters.h"
|
||||
#include "util/StringBuffer.h"
|
||||
#include "util/Unicode.h"
|
||||
#include "vm/ErrorReporting.h"
|
||||
|
||||
using namespace js;
|
||||
|
@ -263,7 +264,13 @@ RegExpParser<CharT>::SyntaxError(unsigned errorNumber, ...)
|
|||
{
|
||||
ErrorMetadata err;
|
||||
|
||||
ts.fillExcludingContext(&err, ts.currentToken().pos.begin);
|
||||
// Ordinarily this indicates whether line-of-context information can be
|
||||
// added, but we entirely ignore that here because we create a
|
||||
// a line of context based on the expression source.
|
||||
uint32_t location = ts.currentToken().pos.begin;
|
||||
if (ts.fillExceptingContext(&err, location)) {
|
||||
ts.lineAndColumnAt(location, &err.lineNumber, &err.columnNumber);
|
||||
}
|
||||
|
||||
// For most error reporting, the line of context derives from the token
|
||||
// stream. So when location information doesn't come from the token
|
||||
|
|
|
@ -4522,43 +4522,30 @@ extern JS_PUBLIC_API void JS_SetParallelParsingEnabled(JSContext* cx,
|
|||
extern JS_PUBLIC_API void JS_SetOffthreadIonCompilationEnabled(JSContext* cx,
|
||||
bool enabled);
|
||||
|
||||
#define JIT_COMPILER_OPTIONS(Register) \
|
||||
Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") Register( \
|
||||
ION_WARMUP_TRIGGER, "ion.warmup.trigger") Register(ION_GVN_ENABLE, \
|
||||
"ion.gvn.enable") \
|
||||
Register(ION_FORCE_IC, "ion.forceinlineCaches") Register( \
|
||||
ION_ENABLE, "ion.enable") Register(ION_CHECK_RANGE_ANALYSIS, \
|
||||
"ion.check-range-analysis") \
|
||||
Register(ION_FREQUENT_BAILOUT_THRESHOLD, \
|
||||
"ion.frequent-bailout-threshold") \
|
||||
Register(BASELINE_ENABLE, "baseline.enable") Register( \
|
||||
OFFTHREAD_COMPILATION_ENABLE, \
|
||||
"offthread-compilation.enable") Register(FULL_DEBUG_CHECKS, \
|
||||
"jit.full-debug-" \
|
||||
"checks") \
|
||||
Register(JUMP_THRESHOLD, "jump-threshold") Register( \
|
||||
TRACK_OPTIMIZATIONS, "jit.track-optimizations") \
|
||||
Register(SIMULATOR_ALWAYS_INTERRUPT, \
|
||||
"simulator.always-interrupt") \
|
||||
Register(SPECTRE_INDEX_MASKING, \
|
||||
"spectre.index-masking") \
|
||||
Register(SPECTRE_OBJECT_MITIGATIONS_BARRIERS, \
|
||||
"spectre.object-mitigations.barriers") \
|
||||
Register(SPECTRE_OBJECT_MITIGATIONS_MISC, \
|
||||
"spectre.object-mitigations.misc") \
|
||||
Register(SPECTRE_STRING_MITIGATIONS, \
|
||||
"spectre.string-mitigations") \
|
||||
Register(SPECTRE_VALUE_MASKING, \
|
||||
"spectre.value-masking") \
|
||||
Register( \
|
||||
SPECTRE_JIT_TO_CXX_CALLS, \
|
||||
"spectre.jit-to-C++-calls") \
|
||||
Register( \
|
||||
WASM_FOLD_OFFSETS, \
|
||||
"wasm.fold-offsets") \
|
||||
Register( \
|
||||
WASM_DELAY_TIER2, \
|
||||
"wasm.delay-tier2")
|
||||
// clang-format off
|
||||
#define JIT_COMPILER_OPTIONS(Register) \
|
||||
Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") \
|
||||
Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger") \
|
||||
Register(ION_GVN_ENABLE, "ion.gvn.enable") \
|
||||
Register(ION_FORCE_IC, "ion.forceinlineCaches") \
|
||||
Register(ION_ENABLE, "ion.enable") \
|
||||
Register(ION_CHECK_RANGE_ANALYSIS, "ion.check-range-analysis") \
|
||||
Register(ION_FREQUENT_BAILOUT_THRESHOLD, "ion.frequent-bailout-threshold") \
|
||||
Register(BASELINE_ENABLE, "baseline.enable") \
|
||||
Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \
|
||||
Register(FULL_DEBUG_CHECKS, "jit.full-debug-checks") \
|
||||
Register(JUMP_THRESHOLD, "jump-threshold") \
|
||||
Register(TRACK_OPTIMIZATIONS, "jit.track-optimizations")\
|
||||
Register(SIMULATOR_ALWAYS_INTERRUPT, "simulator.always-interrupt") \
|
||||
Register(SPECTRE_INDEX_MASKING, "spectre.index-masking") \
|
||||
Register(SPECTRE_OBJECT_MITIGATIONS_BARRIERS, "spectre.object-mitigations.barriers") \
|
||||
Register(SPECTRE_OBJECT_MITIGATIONS_MISC, "spectre.object-mitigations.misc") \
|
||||
Register(SPECTRE_STRING_MITIGATIONS, "spectre.string-mitigations") \
|
||||
Register(SPECTRE_VALUE_MASKING, "spectre.value-masking") \
|
||||
Register(SPECTRE_JIT_TO_CXX_CALLS, "spectre.jit-to-C++-calls") \
|
||||
Register(WASM_FOLD_OFFSETS, "wasm.fold-offsets") \
|
||||
Register(WASM_DELAY_TIER2, "wasm.delay-tier2")
|
||||
// clang-format on
|
||||
|
||||
typedef enum JSJitCompilerOption {
|
||||
#define JIT_COMPILER_DECLARE(key, str) JSJITCOMPILER_##key,
|
||||
|
|
|
@ -6,17 +6,29 @@
|
|||
|
||||
#include "util/Text.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Utf8.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gc/GC.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "util/Unicode.h"
|
||||
#include "vm/JSContext.h"
|
||||
#include "vm/StringType.h"
|
||||
|
||||
using namespace JS;
|
||||
using namespace js;
|
||||
|
||||
using js::gc::AutoSuppressGC;
|
||||
using mozilla::DecodeOneUtf8CodePoint;
|
||||
using mozilla::IsAscii;
|
||||
using mozilla::Maybe;
|
||||
using mozilla::PodCopy;
|
||||
using mozilla::Utf8Unit;
|
||||
|
||||
template <typename CharT>
|
||||
const CharT* js_strchr_limit(const CharT* s, char16_t c, const CharT* limit) {
|
||||
|
@ -307,3 +319,51 @@ template size_t js::PutEscapedString(char* buffer, size_t bufferSize,
|
|||
template size_t js::PutEscapedString(char* buffer, size_t bufferSize,
|
||||
const char16_t* chars, size_t length,
|
||||
uint32_t quote);
|
||||
|
||||
size_t js::unicode::CountCodePoints(const Utf8Unit* begin,
|
||||
const Utf8Unit* end) {
|
||||
MOZ_ASSERT(begin <= end);
|
||||
|
||||
size_t count = 0;
|
||||
const Utf8Unit* ptr = begin;
|
||||
while (ptr < end) {
|
||||
count++;
|
||||
|
||||
Utf8Unit lead = *ptr++;
|
||||
if (IsAscii(lead)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
Maybe<char32_t> cp =
|
||||
#endif
|
||||
DecodeOneUtf8CodePoint(lead, &ptr, end);
|
||||
MOZ_ASSERT(cp.isSome());
|
||||
}
|
||||
MOZ_ASSERT(ptr == end, "bad code unit count in line?");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t js::unicode::CountCodePoints(const char16_t* begin,
|
||||
const char16_t* end) {
|
||||
MOZ_ASSERT(begin <= end);
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
const char16_t* ptr = begin;
|
||||
while (ptr < end) {
|
||||
count++;
|
||||
|
||||
if (!IsLeadSurrogate(*ptr++)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptr < end && IsTrailSurrogate(*ptr)) {
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(ptr == end, "should have consumed the full range");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TextUtils.h"
|
||||
#include "mozilla/Utf8.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
|
@ -208,6 +209,23 @@ JSString* EncodeURI(JSContext* cx, const char* chars, size_t length);
|
|||
// Return true if input string contains a given flag in a comma separated list.
|
||||
bool ContainsFlag(const char* str, const char* flag);
|
||||
|
||||
namespace unicode {
|
||||
|
||||
/** Compute the number of code points in the valid UTF-8 range [begin, end). */
|
||||
extern size_t CountCodePoints(const mozilla::Utf8Unit* begin,
|
||||
const mozilla::Utf8Unit* end);
|
||||
|
||||
/**
|
||||
* Count the number of code points in [begin, end).
|
||||
*
|
||||
* Unlike the UTF-8 case above, consistent with legacy ECMAScript practice,
|
||||
* every sequence of 16-bit units is considered valid. Lone surrogates are
|
||||
* treated as if they represented a code point of the same value.
|
||||
*/
|
||||
extern size_t CountCodePoints(const char16_t* begin, const char16_t* end);
|
||||
|
||||
} // namespace unicode
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // util_Text_h
|
||||
|
|
|
@ -195,6 +195,15 @@ bool JS::CompileForNonSyntacticScope(JSContext* cx,
|
|||
return CompileSourceBuffer(cx, options, srcBuf, script);
|
||||
}
|
||||
|
||||
bool JS::CompileUtf8ForNonSyntacticScope(
|
||||
JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char* bytes,
|
||||
size_t length, JS::MutableHandleScript script) {
|
||||
CompileOptions options(cx, optionsArg);
|
||||
options.setNonSyntacticScope(true);
|
||||
|
||||
return ::CompileUtf8(cx, options, bytes, length, script);
|
||||
}
|
||||
|
||||
bool JS::CompileLatin1ForNonSyntacticScope(
|
||||
JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char* bytes,
|
||||
size_t length, JS::MutableHandleScript script) {
|
||||
|
|
|
@ -2559,23 +2559,25 @@ class MOZ_STACK_CLASS FunctionValidator {
|
|||
return false;
|
||||
}
|
||||
|
||||
TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
|
||||
return callSiteLineNums_.append(
|
||||
anyChars.srcCoords.lineNum(pn->pn_pos.begin));
|
||||
return appendCallSiteLineNumber(pn);
|
||||
}
|
||||
MOZ_MUST_USE bool writeCall(ParseNode* pn, MozOp op) {
|
||||
if (!encoder().writeOp(op)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
|
||||
return callSiteLineNums_.append(
|
||||
anyChars.srcCoords.lineNum(pn->pn_pos.begin));
|
||||
return appendCallSiteLineNumber(pn);
|
||||
}
|
||||
MOZ_MUST_USE bool prepareCall(ParseNode* pn) {
|
||||
TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
|
||||
return callSiteLineNums_.append(
|
||||
anyChars.srcCoords.lineNum(pn->pn_pos.begin));
|
||||
return appendCallSiteLineNumber(pn);
|
||||
}
|
||||
|
||||
private:
|
||||
MOZ_MUST_USE bool appendCallSiteLineNumber(ParseNode* node) {
|
||||
const TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
|
||||
|
||||
auto lineToken = anyChars.lineToken(node->pn_pos.begin);
|
||||
return callSiteLineNums_.append(anyChars.lineNumber(lineToken));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5838,7 +5840,7 @@ static bool ParseFunction(ModuleValidator& m, CodeNode** funNodeOut,
|
|||
|
||||
auto& anyChars = tokenStream.anyCharsAccess();
|
||||
uint32_t toStringStart = anyChars.currentToken().pos.begin;
|
||||
*line = anyChars.srcCoords.lineNum(anyChars.currentToken().pos.end);
|
||||
*line = anyChars.lineNumber(anyChars.lineToken(toStringStart));
|
||||
|
||||
TokenKind tk;
|
||||
if (!tokenStream.getToken(&tk, TokenStreamShared::Operand)) {
|
||||
|
|
|
@ -33,10 +33,9 @@ interface mozIJSSubScriptLoader : nsISupports
|
|||
* This method should only be called from JS!
|
||||
* In JS, the signature looks like:
|
||||
* rv = loadSubScript (url, optionsObject)
|
||||
* @param url the url of the sub-script, it MUST be either a file:,
|
||||
* resource:, blob:, or chrome: url, and MUST be local.
|
||||
* @param url the url of the UTF-8-encoded sub-script, which MUST be either
|
||||
* a file:, resource:, blob:, or chrome: url, and MUST be local.
|
||||
* @param optionsObject an object with parameters. Valid parameters are:
|
||||
* - charset: specifying the character encoding of the file (default: ASCII)
|
||||
* - target: an object to evaluate onto (default: global object of the caller)
|
||||
* - ignoreCache: if set to true, will bypass the cache for reading the file.
|
||||
* - async: if set to true, the script will be loaded
|
||||
|
|
|
@ -847,20 +847,20 @@ nsresult mozJSComponentLoader::ObjectForLocation(
|
|||
// don't early return for them here.
|
||||
auto buf = map.get<char>();
|
||||
if (reuseGlobal) {
|
||||
CompileLatin1ForNonSyntacticScope(cx, options, buf.get(), map.size(),
|
||||
&script);
|
||||
CompileUtf8ForNonSyntacticScope(cx, options, buf.get(), map.size(),
|
||||
&script);
|
||||
} else {
|
||||
CompileLatin1(cx, options, buf.get(), map.size(), &script);
|
||||
CompileUtf8(cx, options, buf.get(), map.size(), &script);
|
||||
}
|
||||
} else {
|
||||
nsCString str;
|
||||
MOZ_TRY_VAR(str, ReadScript(aInfo));
|
||||
|
||||
if (reuseGlobal) {
|
||||
CompileLatin1ForNonSyntacticScope(cx, options, str.get(), str.Length(),
|
||||
&script);
|
||||
CompileUtf8ForNonSyntacticScope(cx, options, str.get(), str.Length(),
|
||||
&script);
|
||||
} else {
|
||||
CompileLatin1(cx, options, str.get(), str.Length(), &script);
|
||||
CompileUtf8(cx, options, str.get(), str.Length(), &script);
|
||||
}
|
||||
}
|
||||
// Propagate the exception, if one exists. Also, don't leave the stale
|
||||
|
|
|
@ -56,7 +56,7 @@ class MOZ_STACK_CLASS LoadSubScriptOptions : public OptionsBase {
|
|||
wantReturnValue(false) {}
|
||||
|
||||
virtual bool Parse() override {
|
||||
return ParseObject("target", &target) && ParseString("charset", charset) &&
|
||||
return ParseObject("target", &target) &&
|
||||
ParseBoolean("ignoreCache", &ignoreCache) &&
|
||||
ParseBoolean("async", &async) &&
|
||||
ParseBoolean("wantReturnValue", &wantReturnValue);
|
||||
|
@ -557,6 +557,8 @@ mozJSSubScriptLoader::LoadSubScriptWithOptions(const nsAString& url,
|
|||
if (!options.Parse()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
options.charset.AssignLiteral("UTF-8");
|
||||
return DoLoadSubScriptWithOptions(url, options, cx, retval);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ isnot(src.indexOf("return 42;"), -1, "encoded subscript should have correct sour
|
|||
|
||||
ns = {};
|
||||
Services.scriptloader.loadSubScriptWithOptions(resolvedBase + "utf8_subscript.js",
|
||||
{target: ns, charset: "UTF-8", ignoreCache: true});
|
||||
{target: ns, ignoreCache: true});
|
||||
src = ns.f.toSource();
|
||||
isnot(src.indexOf("return 42;"), -1, "encoded subscript should have correct source");
|
||||
|
||||
|
|
|
@ -4907,8 +4907,9 @@ inline static bool FormControlShrinksForPercentISize(nsIFrame* aFrame) {
|
|||
}
|
||||
|
||||
LayoutFrameType fType = aFrame->Type();
|
||||
if (fType == LayoutFrameType::Meter || fType == LayoutFrameType::Progress) {
|
||||
// progress and meter do have this shrinking behavior
|
||||
if (fType == LayoutFrameType::Meter || fType == LayoutFrameType::Progress ||
|
||||
fType == LayoutFrameType::Range) {
|
||||
// progress, meter and range do have this shrinking behavior
|
||||
// FIXME: Maybe these should be nsIFormControlFrame?
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -741,7 +741,7 @@ LogicalSize nsRangeFrame::ComputeAutoSize(
|
|||
}
|
||||
|
||||
nscoord nsRangeFrame::GetMinISize(gfxContext* aRenderingContext) {
|
||||
return nscoord(0);
|
||||
return GetPrefISize(aRenderingContext);
|
||||
}
|
||||
|
||||
nscoord nsRangeFrame::GetPrefISize(gfxContext* aRenderingContext) {
|
||||
|
|
|
@ -31,7 +31,7 @@ skip-if = toolkit == 'android'
|
|||
[test_bug564115.html]
|
||||
skip-if = toolkit == 'android' #TIMED_OUT
|
||||
[test_bug571352.html]
|
||||
skip-if = (os == 'mac' && os_version == '10.10') || toolkit == 'android' #TIMED_OUT # OS X 10.10 - bug 947690
|
||||
skip-if = toolkit == 'android' #TIMED_OUT
|
||||
[test_bug572406.html]
|
||||
[test_bug572649.html]
|
||||
skip-if = toolkit == 'android' #TIMED_OUT
|
||||
|
|
|
@ -11,7 +11,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=571352
|
|||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=571352">Mozilla Bug 571352</a>
|
||||
<p id="display"></p>
|
||||
<p id="display"><select multiple style="width:300px/*to avoid any overlay scrollbar messing with our mouse clicks*/"><option>0<option>1<option>2<option>3<option>4<option>5</select></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
|
@ -22,8 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=571352
|
|||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function test() {
|
||||
function createList() {
|
||||
$('display').innerHTML = '<select multiple><option>0<option>1<option>2<option>3<option>4<option>5</select>';
|
||||
function focusList() {
|
||||
$('display').firstChild.focus();
|
||||
}
|
||||
function option(index) {
|
||||
|
@ -32,6 +31,7 @@ SimpleTest.waitForFocus(function test() {
|
|||
function remove(index) {
|
||||
var sel = $('display').firstChild;
|
||||
sel.removeChild(sel.childNodes[index]);
|
||||
document.body.clientHeight;
|
||||
}
|
||||
function up() { synthesizeKey("KEY_ArrowUp"); }
|
||||
function shiftUp() { synthesizeKey("KEY_ArrowUp", {shiftKey:true}); }
|
||||
|
@ -43,10 +43,10 @@ SimpleTest.waitForFocus(function test() {
|
|||
|
||||
const click = {};
|
||||
const shiftClick = {shiftKey:true};
|
||||
const CtrlClick = {CtrlKey:true};
|
||||
createList();
|
||||
focusList();
|
||||
mouseEvent(0,click)
|
||||
is(document.activeElement,$('display').firstChild,"<select> is focused");
|
||||
ok(option(0).selected,"first option is selected");
|
||||
mouseEvent(2,shiftClick)
|
||||
remove(0);
|
||||
ok(option(0).selected && option(1).selected,"first two options are selected");
|
||||
|
|
|
@ -701,13 +701,17 @@ nsresult ProxyAutoConfig::SetupJS() {
|
|||
|
||||
JS::Rooted<JSObject *> global(cx, mJSContext->Global());
|
||||
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(mPACURI.get(), 1);
|
||||
auto CompilePACScript = [this](JSContext *cx,
|
||||
JS::MutableHandle<JSScript *> script) {
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(this->mPACURI.get(), 1);
|
||||
|
||||
return JS::CompileLatin1(cx, options, this->mPACScript.get(),
|
||||
this->mPACScript.Length(), script);
|
||||
};
|
||||
|
||||
JS::Rooted<JSScript *> script(cx);
|
||||
if (!JS::CompileLatin1(cx, options, mPACScript.get(), mPACScript.Length(),
|
||||
&script) ||
|
||||
!JS_ExecuteScript(cx, script)) {
|
||||
if (!CompilePACScript(cx, &script) || !JS_ExecuteScript(cx, script)) {
|
||||
nsString alertMessage(
|
||||
NS_LITERAL_STRING("PAC file failed to install from "));
|
||||
if (isDataURI) {
|
||||
|
|
|
@ -994,8 +994,12 @@ class Artifacts(object):
|
|||
target_suffix = '-opt'
|
||||
|
||||
if self._substs.get('MOZ_BUILD_APP', '') == 'mobile/android':
|
||||
if self._substs['ANDROID_CPU_ARCH'] == 'x86_64':
|
||||
return 'android-x86_64' + target_suffix
|
||||
if self._substs['ANDROID_CPU_ARCH'] == 'x86':
|
||||
return 'android-x86-opt'
|
||||
return 'android-x86' + target_suffix
|
||||
if self._substs['ANDROID_CPU_ARCH'] == 'arm64-v8a':
|
||||
return 'android-aarch64' + target_suffix
|
||||
return 'android-api-16' + target_suffix
|
||||
|
||||
target_64bit = False
|
||||
|
|
|
@ -85,6 +85,7 @@ fn main() {
|
|||
for line in r.lines() {
|
||||
let s = line
|
||||
.unwrap()
|
||||
.replace("#[no_mangle]", "")
|
||||
.replace("pub extern \"C\" fn", "pub unsafe extern \"C\" fn");
|
||||
w.write_all(s.as_bytes()).unwrap();
|
||||
w.write_all(b"\n").unwrap();
|
||||
|
|
|
@ -16,11 +16,15 @@ mod error_reporter;
|
|||
#[path = "../../../ports/geckolib/stylesheet_loader.rs"]
|
||||
mod stylesheet_loader;
|
||||
|
||||
#[allow(non_snake_case, unused_unsafe, private_no_mangle_fns)]
|
||||
#[allow(non_snake_case, unused_unsafe)]
|
||||
mod glue {
|
||||
// this module pretends to be glue.rs, with the safe functions swapped for unsafe ones. This is
|
||||
// a hack to compensate for the fact that `fn` types cannot coerce to `unsafe fn` types. The
|
||||
// imports are populated with the same things so the type assertion should be equivalent
|
||||
// this module pretends to be glue.rs, with the safe functions swapped for
|
||||
// unsafe ones. This is a hack to compensate for the fact that `fn` types
|
||||
// cannot coerce to `unsafe fn` types. The imports are populated with the
|
||||
// same things so the type assertion should be equivalent.
|
||||
//
|
||||
// We also rely on #[no_mangle] being stripped out so that it can link on
|
||||
// Windows without linking to Gecko, see bug 1512271.
|
||||
use geckoservo::*;
|
||||
include!(concat!(env!("OUT_DIR"), "/glue.rs"));
|
||||
}
|
||||
|
|
|
@ -493,7 +493,7 @@ linux64-rust-1.30:
|
|||
treeherder:
|
||||
kind: build
|
||||
platform: toolchains/opt
|
||||
symbol: TL(rust)
|
||||
symbol: TL(rust-1.30)
|
||||
tier: 1
|
||||
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
|
||||
worker:
|
||||
|
@ -509,6 +509,29 @@ linux64-rust-1.30:
|
|||
'--target', 'x86_64-unknown-linux-gnu',
|
||||
'--target', 'i686-unknown-linux-gnu',
|
||||
]
|
||||
toolchain-artifact: public/build/rustc.tar.xz
|
||||
|
||||
linux64-rust-1.31:
|
||||
description: "rust repack"
|
||||
treeherder:
|
||||
kind: build
|
||||
platform: toolchains/opt
|
||||
symbol: TL(rust)
|
||||
tier: 1
|
||||
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
|
||||
worker:
|
||||
max-run-time: 7200
|
||||
env:
|
||||
UPLOAD_DIR: artifacts
|
||||
run:
|
||||
using: toolchain-script
|
||||
script: repack_rust.py
|
||||
arguments: [
|
||||
'--channel', '1.31.0',
|
||||
'--host', 'x86_64-unknown-linux-gnu',
|
||||
'--target', 'x86_64-unknown-linux-gnu',
|
||||
'--target', 'i686-unknown-linux-gnu',
|
||||
]
|
||||
toolchain-alias: linux64-rust
|
||||
toolchain-artifact: public/build/rustc.tar.xz
|
||||
|
||||
|
@ -535,7 +558,7 @@ linux64-rust-nightly:
|
|||
]
|
||||
toolchain-artifact: public/build/rustc.tar.xz
|
||||
|
||||
linux64-rust-macos-1.30:
|
||||
linux64-rust-macos-1.31:
|
||||
description: "rust repack with macos-cross support"
|
||||
treeherder:
|
||||
kind: build
|
||||
|
@ -551,7 +574,7 @@ linux64-rust-macos-1.30:
|
|||
using: toolchain-script
|
||||
script: repack_rust.py
|
||||
arguments: [
|
||||
'--channel', '1.30.0',
|
||||
'--channel', '1.31.0',
|
||||
'--host', 'x86_64-unknown-linux-gnu',
|
||||
'--target', 'x86_64-unknown-linux-gnu',
|
||||
'--target', 'x86_64-apple-darwin',
|
||||
|
@ -582,7 +605,7 @@ linux64-rust-nightly-macos:
|
|||
]
|
||||
toolchain-artifact: public/build/rustc.tar.xz
|
||||
|
||||
linux64-rust-android-1.30:
|
||||
linux64-rust-android-1.31:
|
||||
description: "rust repack with android-cross support"
|
||||
treeherder:
|
||||
kind: build
|
||||
|
@ -598,7 +621,7 @@ linux64-rust-android-1.30:
|
|||
using: toolchain-script
|
||||
script: repack_rust.py
|
||||
arguments: [
|
||||
'--channel', '1.30.0',
|
||||
'--channel', '1.31.0',
|
||||
'--host', 'x86_64-unknown-linux-gnu',
|
||||
'--target', 'x86_64-unknown-linux-gnu',
|
||||
'--target', 'armv7-linux-androideabi',
|
||||
|
|
|
@ -177,4 +177,4 @@ macosx64-cbindgen:
|
|||
- linux64-cctools-port
|
||||
- linux64-clang-7
|
||||
- linux64-llvm-dsymutil
|
||||
- linux64-rust-macos-1.30
|
||||
- linux64-rust-macos-1.31
|
||||
|
|
|
@ -100,7 +100,7 @@ win64-rust-1.28:
|
|||
]
|
||||
toolchain-artifact: public/build/rustc.tar.bz2
|
||||
|
||||
win64-rust-1.30:
|
||||
win64-rust-1.31:
|
||||
description: "rust repack"
|
||||
treeherder:
|
||||
kind: build
|
||||
|
@ -117,7 +117,7 @@ win64-rust-1.30:
|
|||
using: toolchain-script
|
||||
script: repack_rust.py
|
||||
arguments: [
|
||||
'--channel', '1.30.0',
|
||||
'--channel', '1.31.0',
|
||||
'--host', 'x86_64-pc-windows-msvc',
|
||||
'--target', 'x86_64-pc-windows-msvc',
|
||||
'--target', 'i686-pc-windows-msvc',
|
||||
|
@ -191,7 +191,7 @@ win64-node:
|
|||
arguments: ['win64']
|
||||
toolchain-artifact: public/build/node.tar.bz2
|
||||
|
||||
mingw32-rust-1.30:
|
||||
mingw32-rust-1.31:
|
||||
description: "rust repack"
|
||||
treeherder:
|
||||
kind: build
|
||||
|
@ -208,7 +208,7 @@ mingw32-rust-1.30:
|
|||
using: toolchain-script
|
||||
script: repack_rust.py
|
||||
arguments: [
|
||||
'--channel', '1.30.0',
|
||||
'--channel', '1.31.0',
|
||||
'--host', 'x86_64-unknown-linux-gnu',
|
||||
'--target', 'i686-unknown-linux-gnu',
|
||||
'--target', 'i686-pc-windows-gnu',
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html><head>
|
||||
<meta charset="utf-8">
|
||||
<title>Reference: type=range intrinsic size</title>
|
||||
<link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1512066">
|
||||
<style>
|
||||
html,body {
|
||||
color:black; background-color:white; font:16px/1 monospace;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: inline-flex;
|
||||
width: 0;
|
||||
border: 1px solid;
|
||||
justify-items:start;
|
||||
}
|
||||
.flex2 {
|
||||
display: inline-flex;
|
||||
border: 1px solid;
|
||||
justify-items:start;
|
||||
}
|
||||
.grid {
|
||||
display: inline-grid;
|
||||
grid: auto / 0;
|
||||
border: 1px solid;
|
||||
justify-items:start;
|
||||
}
|
||||
.grid2 {
|
||||
display: inline-grid;
|
||||
border: 1px solid;
|
||||
justify-items:start;
|
||||
}
|
||||
.ib {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
border: 1px solid;
|
||||
justify-items:start;
|
||||
}
|
||||
|
||||
input {
|
||||
width: -moz-max-content;
|
||||
width: max-content;
|
||||
min-width: 0;
|
||||
}
|
||||
input.min {
|
||||
min-width: -moz-min-content;
|
||||
min-width: min-content;
|
||||
}
|
||||
input.mbp0 {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="flex"><input type="range" class="min"></div><br>
|
||||
<div class="flex"><input type="range" style="width:0"></div><br>
|
||||
<div class="flex"><input type="range" class="min"></div><br>
|
||||
<div class="flex"><input type="range" class="min"></div><br>
|
||||
<div class="flex"><input type="range" class="min"></div><br>
|
||||
<br>
|
||||
|
||||
<div class="flex2"><input type="range"></div>
|
||||
<div class="flex2" style="width:3px"><input type="range" style="width:3px" class="mbp0"></div>
|
||||
<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
|
||||
<div class="flex2"><input type="range"></div>
|
||||
<div class="flex2"><input type="range"></div>
|
||||
<div class="flex2"><input type="range"></div>
|
||||
<div class="flex2"><input type="range"></div>
|
||||
<br>
|
||||
|
||||
<div class="grid"><input type="range" style="width:0"></div><br>
|
||||
<div class="grid"><input type="range" style="width:0"></div><br>
|
||||
<div class="grid" style="justify-items:start"><input type="range"></div><br>
|
||||
|
||||
<div class="grid2"><input type="range"></div>
|
||||
<div class="grid2"><input type="range" style="min-width:0"></div>
|
||||
<div class="grid2" style="width:3px"><input type="range" style="width:3px" class="mbp0"></div>
|
||||
<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
|
||||
<div class="flex2" style="width:30px"><input type="range" class="mbp0"></div>
|
||||
<div class="grid2" style="justify-items:start"><input type="range"></div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="ib"><input type="range"></div><br>
|
||||
<div class="ib"><input type="range"></div><br>
|
||||
|
||||
<input type="range">
|
||||
<input type="range"
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,85 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html><head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test: type=range intrinsic size</title>
|
||||
<link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1512066">
|
||||
<link rel="match" href="range-intrinsic-size-ref.html">
|
||||
<style>
|
||||
html,body {
|
||||
color:black; background-color:white; font:16px/1 monospace;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: inline-flex;
|
||||
width: 0;
|
||||
border: 1px solid;
|
||||
}
|
||||
.flex2 {
|
||||
display: inline-flex;
|
||||
border: 1px solid;
|
||||
}
|
||||
.grid {
|
||||
display: inline-grid;
|
||||
grid: auto / 0;
|
||||
border: 1px solid;
|
||||
}
|
||||
.grid2 {
|
||||
display: inline-grid;
|
||||
border: 1px solid;
|
||||
}
|
||||
.ib {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
border: 1px solid;
|
||||
}
|
||||
input.mbp0 {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="flex"><input type="range"></div><br>
|
||||
<div class="flex"><input type="range" style="min-width:0"></div><br>
|
||||
<div class="flex" style="justify-items:start"><input type="range"></div><br>
|
||||
<div class="flex" style="-webkit-box-pack: start"><input type="range"></div><br>
|
||||
<div class="flex" style="-webkit-box-pack: start; justify-content: flex-start;"><input type="range"></div><br>
|
||||
<br>
|
||||
|
||||
<div class="flex2"><input type="range"></div>
|
||||
<div class="flex2" style="width:3px"><input type="range" style="min-width:0" class="mbp0"></div>
|
||||
<div class="flex2" style="width:30px"><input type="range" style="min-width:0" class="mbp0"></div>
|
||||
<div class="flex2"><input type="range" style="min-width:0"></div>
|
||||
<div class="flex2" style="justify-items:start"><input type="range"></div>
|
||||
<div class="flex2" style="-webkit-box-pack: start"><input type="range"></div>
|
||||
<div class="flex2" style="-webkit-box-pack: start; justify-content: flex-start;"><input type="range"></div>
|
||||
<br>
|
||||
|
||||
<div class="grid"><input type="range"></div><br>
|
||||
<div class="grid"><input type="range" style="min-width:0"></div><br>
|
||||
<div class="grid" style="justify-items:start"><input type="range"></div><br>
|
||||
|
||||
<div class="grid2"><input type="range"></div>
|
||||
<div class="grid2"><input type="range" style="min-width:0"></div>
|
||||
<div class="grid2" style="width:3px"><input type="range" style="min-width:0" class="mbp0"></div>
|
||||
<div class="grid2" style="width:30px"><input type="range" style="min-width:0" class="mbp0"></div>
|
||||
<div class="grid2" style="grid:auto/30px"><input type="range" class="mbp0"></div>
|
||||
<div class="grid2" style="justify-items:start"><input type="range"></div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="ib"><input type="range"></div><br>
|
||||
<div class="ib"><input type="range" style="min-width:0"></div><br>
|
||||
|
||||
<input type="range" style="width:-moz-min-content; width:min-content;">
|
||||
<input type="range" style="width:-moz-max-content; width:max-content;">
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1016,14 +1016,15 @@
|
|||
},
|
||||
"MEMORY_TOTAL": {
|
||||
"record_in_processes": ["main"],
|
||||
"alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
|
||||
"bug_numbers": [1198209],
|
||||
"alert_emails": ["memshrink-telemetry-alerts@mozilla.com", "erahm@mozilla.com"],
|
||||
"bug_numbers": [1198209, 1511918],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 32768,
|
||||
"high": 16777216,
|
||||
"n_buckets": 100,
|
||||
"description": "Total Memory Across All Processes (KB)"
|
||||
"description": "Total Memory Across All Processes (KB)",
|
||||
"releaseChannelCollection": "opt-out"
|
||||
},
|
||||
"MEMORY_DISTRIBUTION_AMONG_CONTENT": {
|
||||
"record_in_processes": ["main"],
|
||||
|
@ -6528,13 +6529,14 @@
|
|||
},
|
||||
"INPUT_EVENT_RESPONSE_MS": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"alert_emails": ["perf-telemetry-alerts@mozilla.com"],
|
||||
"bug_numbers": [1235908],
|
||||
"alert_emails": ["perf-telemetry-alerts@mozilla.com", "chutten@mozilla.com"],
|
||||
"bug_numbers": [1235908, 1511919],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": 10000,
|
||||
"n_buckets": 50,
|
||||
"description": "Time (ms) from the Input event being created to the end of it being handled"
|
||||
"description": "Time (ms) from the Input event being created to the end of it being handled",
|
||||
"releaseChannelCollection": "opt-out"
|
||||
},
|
||||
"INPUT_EVENT_RESPONSE_COALESCED_MS": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
|
|
|
@ -58,7 +58,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
|
||||
TelemetryArchive: "resource://gre/modules/TelemetryArchive.jsm",
|
||||
TelemetrySession: "resource://gre/modules/TelemetrySession.jsm",
|
||||
MemoryTelemetry: "resource://gre/modules/MemoryTelemetry.jsm",
|
||||
TelemetrySend: "resource://gre/modules/TelemetrySend.jsm",
|
||||
TelemetryReportingPolicy: "resource://gre/modules/TelemetryReportingPolicy.jsm",
|
||||
TelemetryModules: "resource://gre/modules/ModulesPing.jsm",
|
||||
|
@ -639,7 +638,7 @@ var Impl = {
|
|||
// Perform a lightweight, early initialization for the component, just registering
|
||||
// a few observers and initializing the session.
|
||||
TelemetrySession.earlyInit(this._testMode);
|
||||
MemoryTelemetry.earlyInit(this._testMode);
|
||||
Services.telemetry.earlyInit();
|
||||
|
||||
// Annotate crash reports so that we get pings for startup crashes
|
||||
TelemetrySend.earlyInit();
|
||||
|
@ -689,7 +688,7 @@ var Impl = {
|
|||
|
||||
// Perform TelemetrySession delayed init.
|
||||
await TelemetrySession.delayedInit();
|
||||
await MemoryTelemetry.delayedInit();
|
||||
await Services.telemetry.delayedInit();
|
||||
|
||||
if (Services.prefs.getBoolPref(TelemetryUtils.Preferences.NewProfilePingEnabled, false) &&
|
||||
!TelemetrySession.newProfilePingSent) {
|
||||
|
@ -755,7 +754,15 @@ var Impl = {
|
|||
this._log.trace("setupContentTelemetry - Content process recording disabled.");
|
||||
return;
|
||||
}
|
||||
MemoryTelemetry.setupContent(testing);
|
||||
Services.telemetry.earlyInit();
|
||||
|
||||
// FIXME: This is a terrible abuse of DeferredTask.
|
||||
let delayedTask = new DeferredTask(() => {
|
||||
Services.telemetry.delayedInit();
|
||||
}, testing ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY,
|
||||
testing ? 0 : undefined);
|
||||
|
||||
delayedTask.arm();
|
||||
},
|
||||
|
||||
// Do proper shutdown waiting and cleanup.
|
||||
|
@ -788,7 +795,7 @@ var Impl = {
|
|||
await TelemetryHealthPing.shutdown();
|
||||
|
||||
await TelemetrySession.shutdown();
|
||||
await MemoryTelemetry.shutdown();
|
||||
await Services.telemetry.shutdown();
|
||||
|
||||
// First wait for clients processing shutdown.
|
||||
await this._shutdownBarrier.wait();
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/MemoryTelemetry.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/PoisonIOInterposer.h"
|
||||
|
@ -1736,6 +1737,39 @@ TelemetryImpl::FlushBatchedChildTelemetry() {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::EarlyInit() {
|
||||
Unused << MemoryTelemetry::Get();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::DelayedInit() {
|
||||
MemoryTelemetry::Get().DelayedInit();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::Shutdown() {
|
||||
MemoryTelemetry::Get().Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::GatherMemory(JSContext* aCx, Promise** aResult) {
|
||||
ErrorResult rv;
|
||||
RefPtr<Promise> promise = Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
|
||||
if (rv.Failed()) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
MemoryTelemetry::Get().GatherReports(
|
||||
[promise]() { promise->MaybeResolve(JS::UndefinedHandleValue); });
|
||||
|
||||
promise.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -603,4 +603,33 @@ interface nsITelemetry : nsISupports
|
|||
* Please note that this is only intended to be used by GeckoViewTelemetryController.
|
||||
*/
|
||||
void clearProbes();
|
||||
|
||||
/**
|
||||
* Does early, cheap initialization for native telemetry data providers.
|
||||
* Currently, this includes only MemoryTelemetry.
|
||||
*/
|
||||
void earlyInit();
|
||||
|
||||
/**
|
||||
* Does late, expensive initialization for native telemetry data providers.
|
||||
* Currently, this includes only MemoryTelemetry.
|
||||
*
|
||||
* This should only be called after startup has completed and the event loop
|
||||
* is idle.
|
||||
*/
|
||||
void delayedInit();
|
||||
|
||||
/**
|
||||
* Shuts down native telemetry providers. Currently, this includes only
|
||||
* MemoryTelemetry.
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Gathers telemetry data for memory usage and records it to the data store.
|
||||
* Returns a promise which resolves when asynchronous data collection has
|
||||
* completed and all data has been recorded.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
Promise gatherMemory();
|
||||
};
|
||||
|
|
|
@ -45,6 +45,7 @@ BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
|
|||
|
||||
XPIDL_SOURCES += [
|
||||
'core/nsITelemetry.idl',
|
||||
'other/GCTelemetry.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'telemetry'
|
||||
|
@ -107,7 +108,6 @@ EXTRA_JS_MODULES += [
|
|||
'app/TelemetryTimestamps.jsm',
|
||||
'app/TelemetryUtils.jsm',
|
||||
'other/GCTelemetry.jsm',
|
||||
'other/MemoryTelemetry.jsm',
|
||||
'other/UITelemetry.jsm',
|
||||
'pings/CoveragePing.jsm',
|
||||
'pings/EventPing.jsm',
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(6ab1c3c1-31cf-4a32-8484-97b5ef0627af)]
|
||||
interface mozIGCTelemetry : nsISupports {
|
||||
void init();
|
||||
|
||||
void shutdown();
|
||||
};
|
||||
|
||||
[scriptable, uuid(93b2a0ca-6306-41c1-b296-c57cad5175c7)]
|
||||
interface mozIGCTelemetryJSM : nsISupports {
|
||||
readonly attribute mozIGCTelemetry GCTelemetry;
|
||||
};
|
|
@ -1,540 +0,0 @@
|
|||
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
|
||||
/* 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";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Log.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/DeferredTask.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/Timer.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
GCTelemetry: "resource://gre/modules/GCTelemetry.jsm",
|
||||
});
|
||||
|
||||
const Utils = TelemetryUtils;
|
||||
|
||||
const LOGGER_NAME = "Toolkit.Telemetry";
|
||||
const LOGGER_PREFIX = "MemoryTelemetry" + (Utils.isContentProcess ? "#content::" : "::");
|
||||
|
||||
const MESSAGE_TELEMETRY_USS = "Telemetry:USS";
|
||||
const MESSAGE_TELEMETRY_GET_CHILD_USS = "Telemetry:GetChildUSS";
|
||||
|
||||
// Do not gather data more than once a minute (ms)
|
||||
const TELEMETRY_INTERVAL = 60 * 1000;
|
||||
// Delay before intializing telemetry (ms)
|
||||
const TELEMETRY_DELAY = Services.prefs.getIntPref("toolkit.telemetry.initDelay", 60) * 1000;
|
||||
// Delay before initializing telemetry if we're testing (ms)
|
||||
const TELEMETRY_TEST_DELAY = 1;
|
||||
|
||||
const TOPIC_CYCLE_COLLECTOR_BEGIN = "cycle-collector-begin";
|
||||
|
||||
// How long to wait in millis for all the child memory reports to come in
|
||||
const TOTAL_MEMORY_COLLECTOR_TIMEOUT = 200;
|
||||
|
||||
var gLastMemoryPoll = null;
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetters(this, {
|
||||
Telemetry: ["@mozilla.org/base/telemetry;1", "nsITelemetry"],
|
||||
});
|
||||
|
||||
var EXPORTED_SYMBOLS = ["MemoryTelemetry"];
|
||||
|
||||
var MemoryTelemetry = Object.freeze({
|
||||
/**
|
||||
* Pull values from about:memory into corresponding histograms
|
||||
*/
|
||||
gatherMemory() {
|
||||
return Impl.gatherMemory();
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers shutdown of the module.
|
||||
*/
|
||||
shutdown() {
|
||||
return Impl.shutdown();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up components used in the content process.
|
||||
*/
|
||||
setupContent(testing = false) {
|
||||
return Impl.setupContent(testing);
|
||||
},
|
||||
|
||||
/**
|
||||
* Lightweight init function, called as soon as Firefox starts.
|
||||
*/
|
||||
earlyInit(aTesting = false) {
|
||||
return Impl.earlyInit(aTesting);
|
||||
},
|
||||
|
||||
/**
|
||||
* Does the "heavy" Telemetry initialization later on, so we
|
||||
* don't impact startup performance.
|
||||
* @return {Promise} Resolved when the initialization completes.
|
||||
*/
|
||||
delayedInit() {
|
||||
return Impl.delayedInit();
|
||||
},
|
||||
});
|
||||
|
||||
var Impl = {
|
||||
_initialized: false,
|
||||
_logger: null,
|
||||
_prevValues: {},
|
||||
// A task performing delayed initialization of the chrome process
|
||||
_delayedInitTask: null,
|
||||
// Need a timeout in case children are tardy in giving back their memory reports.
|
||||
_totalMemoryTimeout: undefined,
|
||||
_testing: false,
|
||||
// An accumulator of total memory across all processes. Only valid once the final child reports.
|
||||
_totalMemory: null,
|
||||
// A Set of outstanding USS report ids
|
||||
_childrenToHearFrom: null,
|
||||
// monotonically-increasing id for USS reports
|
||||
_nextTotalMemoryId: 1,
|
||||
_USSFromChildProcesses: null,
|
||||
// Keep track of the active observers
|
||||
_observedTopics: new Set(),
|
||||
|
||||
addObserver(aTopic) {
|
||||
Services.obs.addObserver(this, aTopic);
|
||||
this._observedTopics.add(aTopic);
|
||||
},
|
||||
|
||||
removeObserver(aTopic) {
|
||||
Services.obs.removeObserver(this, aTopic);
|
||||
this._observedTopics.delete(aTopic);
|
||||
},
|
||||
|
||||
get _log() {
|
||||
if (!this._logger) {
|
||||
this._logger = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
|
||||
}
|
||||
return this._logger;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pull values from about:memory into corresponding histograms
|
||||
*/
|
||||
gatherMemory: function gatherMemory() {
|
||||
let mgr;
|
||||
try {
|
||||
mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
} catch (e) {
|
||||
// OK to skip memory reporters in xpcshell
|
||||
return;
|
||||
}
|
||||
|
||||
let histogram = Telemetry.getHistogramById("TELEMETRY_MEMORY_REPORTER_MS");
|
||||
let startTime = new Date();
|
||||
|
||||
// Get memory measurements from distinguished amount attributes. We used
|
||||
// to measure "explicit" too, but it could cause hangs, and the data was
|
||||
// always really noisy anyway. See bug 859657.
|
||||
//
|
||||
// test_TelemetrySession.js relies on some of these histograms being
|
||||
// here. If you remove any of the following histograms from here, you'll
|
||||
// have to modify test_TelemetrySession.js:
|
||||
//
|
||||
// * MEMORY_JS_GC_HEAP, and
|
||||
// * MEMORY_JS_COMPARTMENTS_SYSTEM.
|
||||
//
|
||||
// The distinguished amount attribute names don't match the telemetry id
|
||||
// names in some cases due to a combination of (a) historical reasons, and
|
||||
// (b) the fact that we can't change telemetry id names without breaking
|
||||
// data continuity.
|
||||
//
|
||||
let boundHandleMemoryReport = this.handleMemoryReport.bind(this);
|
||||
let h = (id, units, amountName) => {
|
||||
try {
|
||||
// If mgr[amountName] throws an exception, just move on -- some amounts
|
||||
// aren't available on all platforms. But if the attribute simply
|
||||
// isn't present, that indicates the distinguished amounts have changed
|
||||
// and this file hasn't been updated appropriately.
|
||||
let amount = mgr[amountName];
|
||||
if (amount === undefined) {
|
||||
this._log.error("gatherMemory - telemetry accessed an unknown distinguished amount");
|
||||
}
|
||||
boundHandleMemoryReport(id, units, amount);
|
||||
} catch (e) {
|
||||
}
|
||||
};
|
||||
let b = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_BYTES, n);
|
||||
let c = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_COUNT, n);
|
||||
let cc = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE, n);
|
||||
let p = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_PERCENTAGE, n);
|
||||
|
||||
// GHOST_WINDOWS is opt-out as of Firefox 55
|
||||
c("GHOST_WINDOWS", "ghostWindows");
|
||||
|
||||
if (!Telemetry.canRecordExtended) {
|
||||
return;
|
||||
}
|
||||
|
||||
b("MEMORY_VSIZE", "vsize");
|
||||
if (!Services.appinfo.is64Bit || AppConstants.platform !== "win") {
|
||||
b("MEMORY_VSIZE_MAX_CONTIGUOUS", "vsizeMaxContiguous");
|
||||
}
|
||||
b("MEMORY_RESIDENT_FAST", "residentFast");
|
||||
b("MEMORY_UNIQUE", "residentUnique");
|
||||
p("MEMORY_HEAP_OVERHEAD_FRACTION", "heapOverheadFraction");
|
||||
b("MEMORY_JS_GC_HEAP", "JSMainRuntimeGCHeap");
|
||||
c("MEMORY_JS_COMPARTMENTS_SYSTEM", "JSMainRuntimeRealmsSystem");
|
||||
c("MEMORY_JS_COMPARTMENTS_USER", "JSMainRuntimeRealmsUser");
|
||||
b("MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED", "imagesContentUsedUncompressed");
|
||||
b("MEMORY_STORAGE_SQLITE", "storageSQLite");
|
||||
cc("LOW_MEMORY_EVENTS_VIRTUAL", "lowMemoryEventsVirtual");
|
||||
cc("LOW_MEMORY_EVENTS_COMMIT_SPACE", "lowMemoryEventsCommitSpace");
|
||||
cc("LOW_MEMORY_EVENTS_PHYSICAL", "lowMemoryEventsPhysical");
|
||||
cc("PAGE_FAULTS_HARD", "pageFaultsHard");
|
||||
|
||||
try {
|
||||
mgr.getHeapAllocatedAsync(heapAllocated => {
|
||||
boundHandleMemoryReport("MEMORY_HEAP_ALLOCATED",
|
||||
Ci.nsIMemoryReporter.UNITS_BYTES,
|
||||
heapAllocated);
|
||||
});
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
if (!Utils.isContentProcess && !this._totalMemoryTimeout) {
|
||||
// Only the chrome process should gather total memory
|
||||
// total = parent RSS + sum(child USS)
|
||||
this._totalMemory = mgr.residentFast;
|
||||
if (Services.ppmm.childCount > 1) {
|
||||
// Do not report If we time out waiting for the children to call
|
||||
this._totalMemoryTimeout = setTimeout(
|
||||
() => {
|
||||
this._totalMemoryTimeout = undefined;
|
||||
this._childrenToHearFrom.clear();
|
||||
},
|
||||
TOTAL_MEMORY_COLLECTOR_TIMEOUT);
|
||||
this._USSFromChildProcesses = [];
|
||||
this._childrenToHearFrom = new Set();
|
||||
for (let i = 1; i < Services.ppmm.childCount; i++) {
|
||||
let child = Services.ppmm.getChildAt(i);
|
||||
try {
|
||||
child.sendAsyncMessage(MESSAGE_TELEMETRY_GET_CHILD_USS, {id: this._nextTotalMemoryId});
|
||||
this._childrenToHearFrom.add(this._nextTotalMemoryId);
|
||||
this._nextTotalMemoryId++;
|
||||
} catch (ex) {
|
||||
// If a content process has just crashed, then attempting to send it
|
||||
// an async message will throw an exception.
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
boundHandleMemoryReport(
|
||||
"MEMORY_TOTAL",
|
||||
Ci.nsIMemoryReporter.UNITS_BYTES,
|
||||
this._totalMemory);
|
||||
}
|
||||
}
|
||||
|
||||
histogram.add(new Date() - startTime);
|
||||
},
|
||||
|
||||
handleMemoryReport(id, units, amount, key) {
|
||||
let val;
|
||||
if (units == Ci.nsIMemoryReporter.UNITS_BYTES) {
|
||||
val = Math.floor(amount / 1024);
|
||||
} else if (units == Ci.nsIMemoryReporter.UNITS_PERCENTAGE) {
|
||||
// UNITS_PERCENTAGE amounts are 100x greater than their raw value.
|
||||
val = Math.floor(amount / 100);
|
||||
} else if (units == Ci.nsIMemoryReporter.UNITS_COUNT) {
|
||||
val = amount;
|
||||
} else if (units == Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE) {
|
||||
// If the reporter gives us a cumulative count, we'll report the
|
||||
// difference in its value between now and our previous ping.
|
||||
|
||||
if (!(id in this._prevValues)) {
|
||||
// If this is the first time we're reading this reporter, store its
|
||||
// current value but don't report it in the telemetry ping, so we
|
||||
// ignore the effect startup had on the reporter.
|
||||
this._prevValues[id] = amount;
|
||||
return;
|
||||
}
|
||||
|
||||
val = amount - this._prevValues[id];
|
||||
this._prevValues[id] = amount;
|
||||
} else {
|
||||
this._log.error("handleMemoryReport - Can't handle memory reporter with units " + units);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key) {
|
||||
Telemetry.getKeyedHistogramById(id).add(key, val);
|
||||
return;
|
||||
}
|
||||
|
||||
Telemetry.getHistogramById(id).add(val);
|
||||
},
|
||||
|
||||
attachObservers: function attachObservers() {
|
||||
if (!this._initialized)
|
||||
return;
|
||||
if (Telemetry.canRecordExtended) {
|
||||
this.addObserver(TOPIC_CYCLE_COLLECTOR_BEGIN);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Lightweight init function, called as soon as Firefox starts.
|
||||
*/
|
||||
earlyInit(testing) {
|
||||
this._log.trace("earlyInit");
|
||||
|
||||
this._initStarted = true;
|
||||
this._testing = testing;
|
||||
|
||||
if (this._initialized && !testing) {
|
||||
this._log.error("earlyInit - already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Telemetry.canRecordBase && !testing) {
|
||||
this._log.config("earlyInit - Telemetry recording is disabled, skipping Chrome process setup.");
|
||||
return;
|
||||
}
|
||||
|
||||
Services.ppmm.addMessageListener(MESSAGE_TELEMETRY_USS, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Does the "heavy" Telemetry initialization later on, so we
|
||||
* don't impact startup performance.
|
||||
* @return {Promise} Resolved when the initialization completes.
|
||||
*/
|
||||
delayedInit() {
|
||||
this._log.trace("delayedInit");
|
||||
|
||||
this._delayedInitTask = (async () => {
|
||||
try {
|
||||
this._initialized = true;
|
||||
|
||||
this.attachObservers();
|
||||
this.gatherMemory();
|
||||
|
||||
if (Telemetry.canRecordExtended) {
|
||||
GCTelemetry.init();
|
||||
}
|
||||
|
||||
this._delayedInitTask = null;
|
||||
} catch (e) {
|
||||
this._delayedInitTask = null;
|
||||
throw e;
|
||||
}
|
||||
})();
|
||||
|
||||
return this._delayedInitTask;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes telemetry for a content process.
|
||||
*/
|
||||
setupContent: function setupContent(testing) {
|
||||
this._log.trace("setupContent");
|
||||
this._testing = testing;
|
||||
|
||||
if (!Telemetry.canRecordBase) {
|
||||
this._log.trace("setupContent - base recording is disabled, not initializing");
|
||||
return;
|
||||
}
|
||||
|
||||
this.addObserver("content-child-shutdown");
|
||||
Services.cpmm.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_USS, this);
|
||||
|
||||
let delayedTask = new DeferredTask(() => {
|
||||
this._initialized = true;
|
||||
|
||||
this.attachObservers();
|
||||
this.gatherMemory();
|
||||
|
||||
if (Telemetry.canRecordExtended) {
|
||||
GCTelemetry.init();
|
||||
}
|
||||
}, testing ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY,
|
||||
testing ? 0 : undefined);
|
||||
|
||||
delayedTask.arm();
|
||||
},
|
||||
|
||||
getOpenTabsCount: function getOpenTabsCount() {
|
||||
let tabCount = 0;
|
||||
|
||||
for (let win of Services.wm.getEnumerator("navigator:browser")) {
|
||||
tabCount += win.gBrowser.tabs.length;
|
||||
}
|
||||
|
||||
return tabCount;
|
||||
},
|
||||
|
||||
receiveMessage: function receiveMessage(message) {
|
||||
this._log.trace("receiveMessage - Message name " + message.name);
|
||||
switch (message.name) {
|
||||
case MESSAGE_TELEMETRY_USS:
|
||||
{
|
||||
// In parent process, receive the USS report from the child
|
||||
if (this._totalMemoryTimeout && this._childrenToHearFrom.delete(message.data.id)) {
|
||||
let uss = message.data.bytes;
|
||||
this._totalMemory += uss;
|
||||
this._USSFromChildProcesses.push(uss);
|
||||
if (this._childrenToHearFrom.size == 0) {
|
||||
clearTimeout(this._totalMemoryTimeout);
|
||||
this._totalMemoryTimeout = undefined;
|
||||
this.handleMemoryReport(
|
||||
"MEMORY_TOTAL",
|
||||
Ci.nsIMemoryReporter.UNITS_BYTES,
|
||||
this._totalMemory);
|
||||
|
||||
let length = this._USSFromChildProcesses.length;
|
||||
if (length > 1) {
|
||||
// Mean of the USS of all the content processes.
|
||||
let mean = this._USSFromChildProcesses.reduce((a, b) => a + b, 0) / length;
|
||||
// Absolute error of USS for each content process, normalized by the mean (*100 to get it in percentage).
|
||||
// 20% means for a content process that it is using 20% more or 20% less than the mean.
|
||||
let diffs = this._USSFromChildProcesses.map(value => Math.floor(Math.abs(value - mean) * 100 / mean));
|
||||
let tabsCount = this.getOpenTabsCount();
|
||||
let key;
|
||||
if (tabsCount < 11) {
|
||||
key = "0 - 10 tabs";
|
||||
} else if (tabsCount < 501) {
|
||||
key = "11 - 500 tabs";
|
||||
} else {
|
||||
key = "more tabs";
|
||||
}
|
||||
|
||||
diffs.forEach(value => {
|
||||
this.handleMemoryReport(
|
||||
"MEMORY_DISTRIBUTION_AMONG_CONTENT",
|
||||
Ci.nsIMemoryReporter.UNITS_COUNT,
|
||||
value,
|
||||
key);
|
||||
});
|
||||
|
||||
// This notification is for testing only.
|
||||
Services.obs.notifyObservers(null, "gather-memory-telemetry-finished");
|
||||
}
|
||||
this._USSFromChildProcesses = undefined;
|
||||
}
|
||||
} else {
|
||||
this._log.trace("Child USS report was missed");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TELEMETRY_GET_CHILD_USS:
|
||||
{
|
||||
// In child process, send the requested USS report
|
||||
this.sendContentProcessUSS(message.data.id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error("Telemetry.receiveMessage: bad message name");
|
||||
}
|
||||
},
|
||||
|
||||
sendContentProcessUSS: function sendContentProcessUSS(aMessageId) {
|
||||
this._log.trace("sendContentProcessUSS");
|
||||
|
||||
let mgr;
|
||||
try {
|
||||
mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
} catch (e) {
|
||||
// OK to skip memory reporters in xpcshell
|
||||
return;
|
||||
}
|
||||
|
||||
Services.cpmm.sendAsyncMessage(
|
||||
MESSAGE_TELEMETRY_USS,
|
||||
{bytes: mgr.residentUnique, id: aMessageId}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Do some shutdown work that is common to all process types.
|
||||
*/
|
||||
uninstall() {
|
||||
for (let topic of this._observedTopics) {
|
||||
try {
|
||||
// Tests may flip Telemetry.canRecordExtended on and off. It can be the case
|
||||
// that the observer TOPIC_CYCLE_COLLECTOR_BEGIN was not added.
|
||||
this.removeObserver(topic);
|
||||
} catch (e) {
|
||||
this._log.warn("uninstall - Failed to remove " + topic, e);
|
||||
}
|
||||
}
|
||||
|
||||
GCTelemetry.shutdown();
|
||||
},
|
||||
|
||||
observe(aSubject, aTopic, aData) {
|
||||
// Prevent the cycle collector begin topic from cluttering the log.
|
||||
if (aTopic != TOPIC_CYCLE_COLLECTOR_BEGIN) {
|
||||
this._log.trace("observe - " + aTopic + " notified.");
|
||||
}
|
||||
|
||||
switch (aTopic) {
|
||||
case "content-child-shutdown":
|
||||
// content-child-shutdown is only registered for content processes.
|
||||
this.uninstall();
|
||||
Telemetry.flushBatchedChildTelemetry();
|
||||
break;
|
||||
case TOPIC_CYCLE_COLLECTOR_BEGIN:
|
||||
let now = new Date();
|
||||
if (!gLastMemoryPoll
|
||||
|| (TELEMETRY_INTERVAL <= now - gLastMemoryPoll)) {
|
||||
gLastMemoryPoll = now;
|
||||
|
||||
this._log.trace("Dispatching idle gatherMemory task");
|
||||
Services.tm.idleDispatchToMainThread(() => {
|
||||
this._log.trace("Running idle gatherMemory task");
|
||||
this.gatherMemory();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
shutdown() {
|
||||
this._log.trace("shutdown");
|
||||
|
||||
let cleanup = () => {
|
||||
this.uninstall();
|
||||
|
||||
this._initStarted = false;
|
||||
this._initialized = false;
|
||||
};
|
||||
|
||||
// We can be in one the following states here:
|
||||
// 1) delayedInit was never called
|
||||
// or it was called and
|
||||
// 2) _delayedInitTask is running now.
|
||||
// 3) _delayedInitTask finished running already.
|
||||
|
||||
// This handles 1).
|
||||
if (!this._initStarted) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// This handles 3).
|
||||
if (!this._delayedInitTask) {
|
||||
// We already ran the delayed initialization.
|
||||
return cleanup();
|
||||
}
|
||||
|
||||
// This handles 2).
|
||||
return this._delayedInitTask.then(cleanup);
|
||||
},
|
||||
};
|
|
@ -16,7 +16,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
|
||||
TelemetryController: "resource://gre/modules/TelemetryController.jsm",
|
||||
TelemetryStorage: "resource://gre/modules/TelemetryStorage.jsm",
|
||||
MemoryTelemetry: "resource://gre/modules/MemoryTelemetry.jsm",
|
||||
UITelemetry: "resource://gre/modules/UITelemetry.jsm",
|
||||
GCTelemetry: "resource://gre/modules/GCTelemetry.jsm",
|
||||
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
|
||||
|
@ -1048,10 +1047,10 @@ var Impl = {
|
|||
/**
|
||||
* Send data to the server. Record success/send-time in histograms
|
||||
*/
|
||||
send: function send(reason) {
|
||||
send: async function send(reason) {
|
||||
this._log.trace("send - Reason " + reason);
|
||||
// populate histograms one last time
|
||||
MemoryTelemetry.gatherMemory();
|
||||
await Services.telemetry.gatherMemory();
|
||||
|
||||
const isSubsession = !this._isClassicReason(reason);
|
||||
let payload = this.getSessionPayload(reason, isSubsession);
|
||||
|
@ -1138,7 +1137,7 @@ var Impl = {
|
|||
await TelemetryStorage.saveSessionData(this._getSessionDataObject());
|
||||
|
||||
this.addObserver("idle-daily");
|
||||
MemoryTelemetry.gatherMemory();
|
||||
await Services.telemetry.gatherMemory();
|
||||
|
||||
Telemetry.asyncFetchTelemetryData(function() {});
|
||||
|
||||
|
@ -1284,7 +1283,7 @@ var Impl = {
|
|||
if (Object.keys(this._slowSQLStartup).length == 0) {
|
||||
this._slowSQLStartup = Telemetry.slowSQL;
|
||||
}
|
||||
MemoryTelemetry.gatherMemory();
|
||||
Services.telemetry.gatherMemory();
|
||||
return this.getSessionPayload(reason, clearSubsession);
|
||||
},
|
||||
|
||||
|
|
|
@ -366,6 +366,7 @@ function checkPayload(payload, reason, successfulPings) {
|
|||
// Telemetry doesn't touch a memory reporter with these units that's
|
||||
// available on all platforms.
|
||||
|
||||
Assert.ok("MEMORY_TOTAL" in payload.histograms); // UNITS_BYTES
|
||||
Assert.ok("MEMORY_JS_GC_HEAP" in payload.histograms); // UNITS_BYTES
|
||||
Assert.ok("MEMORY_JS_COMPARTMENTS_SYSTEM" in payload.histograms); // UNITS_COUNT
|
||||
|
||||
|
|
|
@ -3020,7 +3020,6 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
<ul>
|
||||
#ifndef RELEASE_OR_BETA
|
||||
<li><code>browser/extensions/formautofill/content/heuristicsRegexp.js</code></li>
|
||||
<li><code>browser/extensions/formautofill/content/nameReferences.js</code></li>
|
||||
<li><code>browser/extensions/formautofill/FormAutofillHeuristics.jsm</code></li>
|
||||
<li><code>browser/extensions/formautofill/FormAutofillNameUtils.jsm</code></li>
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,525 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MemoryTelemetry.h"
|
||||
#include "nsMemoryReporterManager.h"
|
||||
|
||||
#include "GCTelemetry.h"
|
||||
#include "mozJSComponentLoader.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/SimpleEnumerator.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIBrowserDOMWindow.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIWindowMediator.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsObserverService.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
using mozilla::dom::AutoJSAPI;
|
||||
using mozilla::dom::ContentParent;
|
||||
|
||||
// Do not gather data more than once a minute (ms)
|
||||
static constexpr uint32_t kTelemetryInterval = 60 * 1000;
|
||||
|
||||
static constexpr const char* kTopicCycleCollectorBegin =
|
||||
"cycle-collector-begin";
|
||||
|
||||
// How long to wait in millis for all the child memory reports to come in
|
||||
static constexpr uint32_t kTotalMemoryCollectorTimeout = 200;
|
||||
|
||||
static Result<nsCOMPtr<mozIGCTelemetry>, nsresult> GetGCTelemetry() {
|
||||
AutoJSAPI jsapi;
|
||||
MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JS::RootedObject global(cx);
|
||||
JS::RootedObject exports(cx);
|
||||
MOZ_TRY(mozJSComponentLoader::Get()->Import(
|
||||
cx, NS_LITERAL_CSTRING("resource://gre/modules/GCTelemetry.jsm"), &global,
|
||||
&exports));
|
||||
|
||||
nsCOMPtr<mozIGCTelemetryJSM> jsm;
|
||||
MOZ_TRY(nsContentUtils::XPConnect()->WrapJS(
|
||||
cx, exports, NS_GET_IID(mozIGCTelemetryJSM), getter_AddRefs(jsm)));
|
||||
|
||||
nsCOMPtr<mozIGCTelemetry> gcTelemetry;
|
||||
MOZ_TRY(jsm->GetGCTelemetry(getter_AddRefs(gcTelemetry)));
|
||||
|
||||
return std::move(gcTelemetry);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
enum class PrevValue : uint32_t {
|
||||
#ifdef XP_WIN
|
||||
LOW_MEMORY_EVENTS_VIRTUAL,
|
||||
LOW_MEMORY_EVENTS_COMMIT_SPACE,
|
||||
LOW_MEMORY_EVENTS_PHYSICAL,
|
||||
#endif
|
||||
#if defined(XP_LINUX) && !defined(ANDROID)
|
||||
PAGE_FAULTS_HARD,
|
||||
#endif
|
||||
SIZE_,
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
constexpr uint32_t kUninitialized = ~0;
|
||||
|
||||
static uint32_t gPrevValues[uint32_t(PrevValue::SIZE_)];
|
||||
|
||||
static uint32_t PrevValueIndex(Telemetry::HistogramID aId) {
|
||||
switch (aId) {
|
||||
#ifdef XP_WIN
|
||||
case Telemetry::LOW_MEMORY_EVENTS_VIRTUAL:
|
||||
return uint32_t(PrevValue::LOW_MEMORY_EVENTS_VIRTUAL);
|
||||
case Telemetry::LOW_MEMORY_EVENTS_COMMIT_SPACE:
|
||||
return uint32_t(PrevValue::LOW_MEMORY_EVENTS_COMMIT_SPACE);
|
||||
case Telemetry::LOW_MEMORY_EVENTS_PHYSICAL:
|
||||
return uint32_t(PrevValue::LOW_MEMORY_EVENTS_PHYSICAL);
|
||||
#endif
|
||||
#if defined(XP_LINUX) && !defined(ANDROID)
|
||||
case Telemetry::PAGE_FAULTS_HARD:
|
||||
return uint32_t(PrevValue::PAGE_FAULTS_HARD);
|
||||
#endif
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected histogram ID");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(MemoryTelemetry, nsIObserver, nsISupportsWeakReference)
|
||||
|
||||
MemoryTelemetry::MemoryTelemetry()
|
||||
: mThreadPool(do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID)) {}
|
||||
|
||||
void MemoryTelemetry::Init() {
|
||||
for (auto& val : gPrevValues) {
|
||||
val = kUninitialized;
|
||||
}
|
||||
|
||||
if (XRE_IsContentProcess()) {
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(obs);
|
||||
|
||||
obs->AddObserver(this, "content-child-shutdown", true);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ MemoryTelemetry& MemoryTelemetry::Get() {
|
||||
static RefPtr<MemoryTelemetry> sInstance;
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sInstance) {
|
||||
sInstance = new MemoryTelemetry();
|
||||
sInstance->Init();
|
||||
ClearOnShutdown(&sInstance);
|
||||
}
|
||||
return *sInstance;
|
||||
}
|
||||
|
||||
nsresult MemoryTelemetry::DelayedInit() {
|
||||
if (Telemetry::CanRecordExtended()) {
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(obs);
|
||||
|
||||
obs->AddObserver(this, kTopicCycleCollectorBegin, true);
|
||||
}
|
||||
|
||||
GatherReports();
|
||||
|
||||
if (Telemetry::CanRecordExtended()) {
|
||||
nsCOMPtr<mozIGCTelemetry> gcTelemetry;
|
||||
MOZ_TRY_VAR(gcTelemetry, GetGCTelemetry());
|
||||
|
||||
MOZ_TRY(gcTelemetry->Init());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult MemoryTelemetry::Shutdown() {
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(obs);
|
||||
|
||||
obs->RemoveObserver(this, kTopicCycleCollectorBegin);
|
||||
|
||||
if (Telemetry::CanRecordExtended()) {
|
||||
nsCOMPtr<mozIGCTelemetry> gcTelemetry;
|
||||
MOZ_TRY_VAR(gcTelemetry, GetGCTelemetry());
|
||||
|
||||
MOZ_TRY(gcTelemetry->Shutdown());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static inline void HandleMemoryReport(Telemetry::HistogramID aId,
|
||||
int32_t aUnits, uint64_t aAmount,
|
||||
const nsCString& aKey = VoidCString()) {
|
||||
uint32_t val;
|
||||
switch (aUnits) {
|
||||
case nsIMemoryReporter::UNITS_BYTES:
|
||||
val = uint32_t(aAmount / 1024);
|
||||
break;
|
||||
|
||||
case nsIMemoryReporter::UNITS_PERCENTAGE:
|
||||
// UNITS_PERCENTAGE amounts are 100x greater than their raw value.
|
||||
val = uint32_t(aAmount / 100);
|
||||
break;
|
||||
|
||||
case nsIMemoryReporter::UNITS_COUNT:
|
||||
val = uint32_t(aAmount);
|
||||
break;
|
||||
|
||||
case nsIMemoryReporter::UNITS_COUNT_CUMULATIVE: {
|
||||
// If the reporter gives us a cumulative count, we'll report the
|
||||
// difference in its value between now and our previous ping.
|
||||
|
||||
uint32_t idx = PrevValueIndex(aId);
|
||||
uint32_t prev = gPrevValues[idx];
|
||||
gPrevValues[idx] = aAmount;
|
||||
|
||||
if (prev == kUninitialized) {
|
||||
// If this is the first time we're reading this reporter, store its
|
||||
// current value but don't report it in the telemetry ping, so we
|
||||
// ignore the effect startup had on the reporter.
|
||||
return;
|
||||
}
|
||||
val = aAmount - prev;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected aUnits value");
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: The reference equality check here should allow the compiler to
|
||||
// optimize this case out at compile time when we weren't given a key,
|
||||
// while IsEmpty() or IsVoid() most likely will not.
|
||||
if (&aKey == &VoidCString()) {
|
||||
Telemetry::Accumulate(aId, val);
|
||||
} else {
|
||||
Telemetry::Accumulate(aId, aKey, val);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult MemoryTelemetry::GatherReports(
|
||||
const std::function<void()>& aCompletionCallback) {
|
||||
RefPtr<nsMemoryReporterManager> mgr = nsMemoryReporterManager::GetOrCreate();
|
||||
MOZ_DIAGNOSTIC_ASSERT(mgr);
|
||||
NS_ENSURE_TRUE(mgr, NS_ERROR_FAILURE);
|
||||
|
||||
auto startTime = TimeStamp::Now();
|
||||
|
||||
#define RECORD(id, metric, units) \
|
||||
do { \
|
||||
int64_t amt; \
|
||||
nsresult rv = mgr->Get##metric(&amt); \
|
||||
if (!NS_WARN_IF(NS_FAILED(rv))) { \
|
||||
HandleMemoryReport(Telemetry::id, nsIMemoryReporter::units, amt); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// GHOST_WINDOWS is opt-out as of Firefox 55
|
||||
RECORD(GHOST_WINDOWS, GhostWindows, UNITS_COUNT);
|
||||
|
||||
if (!Telemetry::CanRecordReleaseData()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Get memory measurements from distinguished amount attributes. We used
|
||||
// to measure "explicit" too, but it could cause hangs, and the data was
|
||||
// always really noisy anyway. See bug 859657.
|
||||
//
|
||||
// test_TelemetrySession.js relies on some of these histograms being
|
||||
// here. If you remove any of the following histograms from here, you'll
|
||||
// have to modify test_TelemetrySession.js:
|
||||
//
|
||||
// * MEMORY_TOTAL,
|
||||
// * MEMORY_JS_GC_HEAP, and
|
||||
// * MEMORY_JS_COMPARTMENTS_SYSTEM.
|
||||
//
|
||||
// The distinguished amount attribute names don't match the telemetry id
|
||||
// names in some cases due to a combination of (a) historical reasons, and
|
||||
// (b) the fact that we can't change telemetry id names without breaking
|
||||
// data continuity.
|
||||
|
||||
// Collect cheap or main-thread only metrics synchronously, on the main
|
||||
// thread.
|
||||
RECORD(MEMORY_JS_GC_HEAP, JSMainRuntimeGCHeap, UNITS_BYTES);
|
||||
RECORD(MEMORY_JS_COMPARTMENTS_SYSTEM, JSMainRuntimeRealmsSystem, UNITS_COUNT);
|
||||
RECORD(MEMORY_JS_COMPARTMENTS_USER, JSMainRuntimeRealmsUser, UNITS_COUNT);
|
||||
RECORD(MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED, ImagesContentUsedUncompressed,
|
||||
UNITS_BYTES);
|
||||
RECORD(MEMORY_STORAGE_SQLITE, StorageSQLite, UNITS_BYTES);
|
||||
#ifdef XP_WIN
|
||||
RECORD(LOW_MEMORY_EVENTS_VIRTUAL, LowMemoryEventsVirtual,
|
||||
UNITS_COUNT_CUMULATIVE);
|
||||
RECORD(LOW_MEMORY_EVENTS_COMMIT_SPACE, LowMemoryEventsCommitSpace,
|
||||
UNITS_COUNT_CUMULATIVE);
|
||||
RECORD(LOW_MEMORY_EVENTS_PHYSICAL, LowMemoryEventsPhysical,
|
||||
UNITS_COUNT_CUMULATIVE);
|
||||
#endif
|
||||
#if defined(XP_LINUX) && !defined(ANDROID)
|
||||
RECORD(PAGE_FAULTS_HARD, PageFaultsHard, UNITS_COUNT_CUMULATIVE);
|
||||
#endif
|
||||
|
||||
RefPtr<Runnable> completionRunnable;
|
||||
if (aCompletionCallback) {
|
||||
completionRunnable = NS_NewRunnableFunction(__func__, aCompletionCallback);
|
||||
}
|
||||
|
||||
// Collect expensive metrics that can be calculated off-main-thread
|
||||
// asynchronously, on a background thread.
|
||||
RefPtr<Runnable> runnable = NS_NewRunnableFunction(
|
||||
"MemoryTelemetry::GatherReports", [mgr, completionRunnable]() mutable {
|
||||
RECORD(MEMORY_VSIZE, Vsize, UNITS_BYTES);
|
||||
#if !defined(HAVE_64BIT_BUILD) || !defined(XP_WIN)
|
||||
RECORD(MEMORY_VSIZE_MAX_CONTIGUOUS, VsizeMaxContiguous, UNITS_BYTES);
|
||||
#endif
|
||||
RECORD(MEMORY_RESIDENT_FAST, ResidentFast, UNITS_BYTES);
|
||||
RECORD(MEMORY_UNIQUE, ResidentUnique, UNITS_BYTES);
|
||||
RECORD(MEMORY_HEAP_ALLOCATED, HeapAllocated, UNITS_BYTES);
|
||||
RECORD(MEMORY_HEAP_OVERHEAD_FRACTION, HeapOverheadFraction,
|
||||
UNITS_PERCENTAGE);
|
||||
|
||||
if (completionRunnable) {
|
||||
NS_DispatchToMainThread(completionRunnable.forget(),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
});
|
||||
|
||||
#undef RECORD
|
||||
|
||||
nsresult rv = mThreadPool->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
// If we're running in the parent process, collect data from all processes for
|
||||
// the MEMORY_TOTAL histogram.
|
||||
if (XRE_IsParentProcess() && !mTotalMemoryGatherer) {
|
||||
mTotalMemoryGatherer = new TotalMemoryGatherer();
|
||||
mTotalMemoryGatherer->Begin(mThreadPool);
|
||||
}
|
||||
|
||||
Telemetry::AccumulateTimeDelta(
|
||||
Telemetry::HistogramID::TELEMETRY_MEMORY_REPORTER_MS, startTime,
|
||||
TimeStamp::Now());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(MemoryTelemetry::TotalMemoryGatherer, nsITimerCallback)
|
||||
|
||||
/**
|
||||
* Polls all child processes for their unique set size, and populates the
|
||||
* MEMORY_TOTAL and MEMORY_DISTRIBUTION_AMONG_CONTENT histograms with the
|
||||
* results.
|
||||
*/
|
||||
void MemoryTelemetry::TotalMemoryGatherer::Begin(nsIEventTarget* aThreadPool) {
|
||||
nsCOMPtr<nsISerialEventTarget> target =
|
||||
SystemGroup::EventTargetFor(TaskCategory::Other);
|
||||
|
||||
nsTArray<ContentParent*> parents;
|
||||
ContentParent::GetAll(parents);
|
||||
for (auto& parent : parents) {
|
||||
mRemainingChildCount++;
|
||||
parent->SendGetMemoryUniqueSetSize()->Then(
|
||||
target, "TotalMemoryGather::Begin", this,
|
||||
&TotalMemoryGatherer::CollectResult, &TotalMemoryGatherer::OnFailure);
|
||||
}
|
||||
|
||||
mChildSizes.SetCapacity(mRemainingChildCount);
|
||||
|
||||
RefPtr<TotalMemoryGatherer> self{this};
|
||||
|
||||
aThreadPool->Dispatch(
|
||||
NS_NewRunnableFunction(
|
||||
"TotalMemoryGather::Begin",
|
||||
[self]() {
|
||||
RefPtr<nsMemoryReporterManager> mgr =
|
||||
nsMemoryReporterManager::GetOrCreate();
|
||||
MOZ_RELEASE_ASSERT(mgr);
|
||||
|
||||
NS_DispatchToMainThread(NewRunnableMethod<int64_t>(
|
||||
"TotalMemoryGather::CollectParentSize", self,
|
||||
&TotalMemoryGatherer::CollectParentSize, mgr->ResidentFast()));
|
||||
}),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
NS_NewTimerWithCallback(getter_AddRefs(mTimeout), this,
|
||||
kTotalMemoryCollectorTimeout,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
nsresult MemoryTelemetry::TotalMemoryGatherer::MaybeFinish() {
|
||||
// If we timed out waiting for a response from any child, we don't report
|
||||
// anything for this attempt.
|
||||
if (!mTimeout || !mHaveParentSize || mRemainingChildCount) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mTimeout = nullptr;
|
||||
MemoryTelemetry::Get().mTotalMemoryGatherer = nullptr;
|
||||
|
||||
HandleMemoryReport(Telemetry::MEMORY_TOTAL, nsIMemoryReporter::UNITS_BYTES,
|
||||
mTotalMemory);
|
||||
|
||||
if (mChildSizes.Length() > 1) {
|
||||
int32_t tabsCount;
|
||||
MOZ_TRY_VAR(tabsCount, GetOpenTabsCount());
|
||||
|
||||
nsCString key;
|
||||
if (tabsCount <= 10) {
|
||||
key = "0 - 10 tabs";
|
||||
} else if (tabsCount <= 500) {
|
||||
key = "11 - 500 tabs";
|
||||
} else {
|
||||
key = "more tabs";
|
||||
}
|
||||
|
||||
// Mean of the USS of all the content processes.
|
||||
int64_t mean = 0;
|
||||
for (auto size : mChildSizes) {
|
||||
mean += size;
|
||||
}
|
||||
mean /= mChildSizes.Length();
|
||||
|
||||
// Absolute error of USS for each content process, normalized by the mean
|
||||
// (*100 to get it in percentage). 20% means for a content process that it
|
||||
// is using 20% more or 20% less than the mean.
|
||||
for (auto size : mChildSizes) {
|
||||
int64_t diff = llabs(size - mean) * 100 / mean;
|
||||
|
||||
HandleMemoryReport(Telemetry::MEMORY_DISTRIBUTION_AMONG_CONTENT,
|
||||
nsIMemoryReporter::UNITS_COUNT, diff, key);
|
||||
}
|
||||
}
|
||||
|
||||
// This notification is for testing only.
|
||||
if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
|
||||
obs->NotifyObservers(nullptr, "gather-memory-telemetry-finished", nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void MemoryTelemetry::TotalMemoryGatherer::CollectParentSize(
|
||||
int64_t aResident) {
|
||||
mTotalMemory += aResident;
|
||||
mHaveParentSize = true;
|
||||
|
||||
MaybeFinish();
|
||||
}
|
||||
|
||||
void MemoryTelemetry::TotalMemoryGatherer::CollectResult(int64_t aChildUSS) {
|
||||
mChildSizes.AppendElement(aChildUSS);
|
||||
|
||||
mTotalMemory += aChildUSS;
|
||||
mRemainingChildCount--;
|
||||
|
||||
MaybeFinish();
|
||||
}
|
||||
|
||||
void MemoryTelemetry::TotalMemoryGatherer::OnFailure(
|
||||
mozilla::ipc::ResponseRejectReason aReason) {
|
||||
// Treat failure of any request the same as a timeout.
|
||||
Notify(nullptr);
|
||||
}
|
||||
|
||||
nsresult MemoryTelemetry::TotalMemoryGatherer::Notify(nsITimer* aTimer) {
|
||||
// Set mTimeout null to indicate the timeout has fired. After this, all
|
||||
// results for this attempt will be ignored.
|
||||
mTimeout = nullptr;
|
||||
MemoryTelemetry::Get().mTotalMemoryGatherer = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ Result<uint32_t, nsresult> MemoryTelemetry::GetOpenTabsCount() {
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIWindowMediator> windowMediator(
|
||||
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
|
||||
MOZ_TRY(rv);
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> enumerator;
|
||||
MOZ_TRY(windowMediator->GetEnumerator(u"navigator:browser",
|
||||
getter_AddRefs(enumerator)));
|
||||
|
||||
uint32_t total = 0;
|
||||
for (auto& window : SimpleEnumerator<nsIDOMChromeWindow>(enumerator)) {
|
||||
nsCOMPtr<nsIBrowserDOMWindow> browserWin;
|
||||
MOZ_TRY(window->GetBrowserDOMWindow(getter_AddRefs(browserWin)));
|
||||
|
||||
NS_ENSURE_TRUE(browserWin, Err(NS_ERROR_UNEXPECTED));
|
||||
|
||||
uint32_t tabCount;
|
||||
MOZ_TRY(browserWin->GetTabCount(&tabCount));
|
||||
total += tabCount;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
void MemoryTelemetry::GetUniqueSetSize(
|
||||
std::function<void(const int64_t&)>&& aCallback) {
|
||||
mThreadPool->Dispatch(
|
||||
NS_NewRunnableFunction(
|
||||
"MemoryTelemetry::GetUniqueSetSize",
|
||||
[callback = std::move(aCallback)]() mutable {
|
||||
RefPtr<nsMemoryReporterManager> mgr =
|
||||
nsMemoryReporterManager::GetOrCreate();
|
||||
MOZ_RELEASE_ASSERT(mgr);
|
||||
|
||||
int64_t uss = mgr->ResidentUnique();
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"MemoryTelemetry::GetUniqueSetSizeResult",
|
||||
[uss, callback = std::move(callback)]() { callback(uss); }));
|
||||
}),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
nsresult MemoryTelemetry::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
if (strcmp(aTopic, kTopicCycleCollectorBegin) == 0) {
|
||||
auto now = TimeStamp::Now();
|
||||
if (!mLastPoll.IsNull() &&
|
||||
(now - mLastPoll).ToMilliseconds() < kTelemetryInterval) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mLastPoll = now;
|
||||
|
||||
NS_IdleDispatchToCurrentThread(NewRunnableMethod<std::function<void()>>(
|
||||
"MemoryTelemetry::GatherReports", this, &MemoryTelemetry::GatherReports,
|
||||
nullptr));
|
||||
} else if (strcmp(aTopic, "content-child-shutdown") == 0) {
|
||||
if (nsCOMPtr<nsITelemetry> telemetry =
|
||||
do_GetService("@mozilla.org/base/telemetry;1")) {
|
||||
telemetry->FlushBatchedChildTelemetry();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_MemoryTelemetry_h
|
||||
#define mozilla_MemoryTelemetry_h
|
||||
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
enum class ResponseRejectReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodically gathers memory usage metrics after cycle collection, and
|
||||
* populates telemetry histograms with their values.
|
||||
*/
|
||||
class MemoryTelemetry final : public nsIObserver,
|
||||
public nsSupportsWeakReference {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static MemoryTelemetry& Get();
|
||||
|
||||
nsresult GatherReports(const std::function<void()>& aCompletionCallback = nullptr);
|
||||
|
||||
void GetUniqueSetSize(std::function<void(const int64_t&)>&& aCallback);
|
||||
|
||||
/**
|
||||
* Does expensive initialization, which should happen only after startup has
|
||||
* completed, and the event loop is idle.
|
||||
*/
|
||||
nsresult DelayedInit();
|
||||
|
||||
nsresult Shutdown();
|
||||
|
||||
private:
|
||||
MemoryTelemetry();
|
||||
|
||||
~MemoryTelemetry() = default;
|
||||
|
||||
void Init();
|
||||
|
||||
static Result<uint32_t, nsresult> GetOpenTabsCount();
|
||||
|
||||
class TotalMemoryGatherer final : public nsITimerCallback {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
TotalMemoryGatherer() {}
|
||||
|
||||
void CollectParentSize(int64_t aResident);
|
||||
void CollectResult(int64_t aChildUSS);
|
||||
void OnFailure(ipc::ResponseRejectReason aReason);
|
||||
|
||||
void Begin(nsIEventTarget* aThreadPool);
|
||||
|
||||
private:
|
||||
~TotalMemoryGatherer() = default;
|
||||
|
||||
nsresult MaybeFinish();
|
||||
|
||||
nsCOMPtr<nsITimer> mTimeout;
|
||||
|
||||
nsTArray<int64_t> mChildSizes;
|
||||
|
||||
int64_t mTotalMemory = 0;
|
||||
uint32_t mRemainingChildCount = 0;
|
||||
|
||||
bool mHaveParentSize = false;
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mThreadPool;
|
||||
RefPtr<TotalMemoryGatherer> mTotalMemoryGatherer;
|
||||
|
||||
TimeStamp mLastPoll{};
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // defined mozilla_MemoryTelemetry_h
|
|
@ -119,6 +119,7 @@ EXPORTS.mozilla += [
|
|||
'MemoryInfo.h',
|
||||
'MemoryMapping.h',
|
||||
'MemoryReportingProcess.h',
|
||||
'MemoryTelemetry.h',
|
||||
'nsMemoryInfoDumper.h',
|
||||
'NSPRLogModulesParser.h',
|
||||
'OwningNonNull.h',
|
||||
|
@ -150,6 +151,7 @@ UNIFIED_SOURCES += [
|
|||
'LogCommandLineHandler.cpp',
|
||||
'Logging.cpp',
|
||||
'LogModulePrefWatcher.cpp',
|
||||
'MemoryTelemetry.cpp',
|
||||
'nsClassInfoImpl.cpp',
|
||||
'nsCOMPtr.cpp',
|
||||
'nsConsoleMessage.cpp',
|
||||
|
@ -217,6 +219,7 @@ FINAL_LIBRARY = 'xul'
|
|||
LOCAL_INCLUDES += [
|
||||
'../build',
|
||||
'/dom/base',
|
||||
'/js/xpconnect/loader', # for mozJSComponentLoader.h
|
||||
'/mfbt',
|
||||
'/xpcom/ds',
|
||||
]
|
||||
|
|
|
@ -419,12 +419,6 @@ interface nsIMemoryReporterManager : nsISupports
|
|||
[infallible] readonly attribute boolean isDMDEnabled;
|
||||
[infallible] readonly attribute boolean isDMDRunning;
|
||||
|
||||
/*
|
||||
* Asynchronously gets attribute 'heapAllocated'. The value is returned in
|
||||
* the callback argument.
|
||||
*/
|
||||
[must_use] void getHeapAllocatedAsync(in nsIHeapAllocatedCallback callback);
|
||||
|
||||
/*
|
||||
* Run a series of GC/CC's in an attempt to minimize the application's memory
|
||||
* usage. When we're finished, we invoke the given runnable if it's not
|
||||
|
|
|
@ -2404,49 +2404,6 @@ nsMemoryReporterManager::GetHeapAllocated(int64_t* aAmount) {
|
|||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::GetHeapAllocatedAsync(
|
||||
nsIHeapAllocatedCallback* aCallback) {
|
||||
#ifdef HAVE_JEMALLOC_STATS
|
||||
if (!mThreadPool) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
RefPtr<nsIMemoryReporterManager> self{this};
|
||||
nsMainThreadPtrHandle<nsIHeapAllocatedCallback> mainThreadCallback(
|
||||
new nsMainThreadPtrHolder<nsIHeapAllocatedCallback>(
|
||||
"HeapAllocatedCallback", aCallback));
|
||||
|
||||
nsCOMPtr<nsIRunnable> getHeapAllocatedRunnable = NS_NewRunnableFunction(
|
||||
"nsMemoryReporterManager::GetHeapAllocatedAsync",
|
||||
[self, mainThreadCallback]() mutable {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
int64_t heapAllocated = 0;
|
||||
nsresult rv = self->GetHeapAllocated(&heapAllocated);
|
||||
|
||||
nsCOMPtr<nsIRunnable> resultCallbackRunnable = NS_NewRunnableFunction(
|
||||
"nsMemoryReporterManager::GetHeapAllocatedAsync",
|
||||
[mainThreadCallback, heapAllocated, rv]() mutable {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mainThreadCallback->Callback(0);
|
||||
return;
|
||||
}
|
||||
|
||||
mainThreadCallback->Callback(heapAllocated);
|
||||
}); // resultCallbackRunnable.
|
||||
|
||||
Unused << NS_DispatchToMainThread(resultCallbackRunnable);
|
||||
}); // getHeapAllocatedRunnable.
|
||||
|
||||
return mThreadPool->Dispatch(getHeapAllocatedRunnable, NS_DISPATCH_NORMAL);
|
||||
#else
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This has UNITS_PERCENTAGE, so it is multiplied by 100x.
|
||||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::GetHeapOverheadFraction(int64_t* aAmount) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче