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

This commit is contained in:
Coroiu Cristina 2018-12-12 07:26:06 +02:00
Родитель d1d958a798 3160ddc1f0
Коммит 51cd68a168
82 изменённых файлов: 2047 добавлений и 1135 удалений

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

@ -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;

70
dom/ipc/MMPrinter.cpp Normal file
Просмотреть файл

@ -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

31
dom/ipc/MMPrinter.h Normal file
Просмотреть файл

@ -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;
}

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

@ -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) {