зеркало из https://github.com/mozilla/pjs.git
Merge last PGO-green changeset of mozilla-inbound to mozilla-central
This commit is contained in:
Коммит
3bbd395f07
|
@ -103,7 +103,7 @@ __try {
|
|||
}
|
||||
|
||||
if (NULL == *ppv) {
|
||||
HRESULT hr = CAccessibleHyperlink::QueryInterface(iid, ppv);
|
||||
HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv);
|
||||
if (SUCCEEDED(hr))
|
||||
return hr;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "Accessible.h"
|
||||
#include "Accessible2.h"
|
||||
#include "ia2AccessibleComponent.h"
|
||||
#include "CAccessibleHyperlink.h"
|
||||
#include "ia2AccessibleHyperlink.h"
|
||||
#include "CAccessibleValue.h"
|
||||
|
||||
#define DECL_IUNKNOWN_INHERITED \
|
||||
|
@ -65,7 +65,7 @@ Class::QueryInterface(REFIID iid, void** ppv) \
|
|||
|
||||
class AccessibleWrap : public Accessible,
|
||||
public ia2AccessibleComponent,
|
||||
public CAccessibleHyperlink,
|
||||
public ia2AccessibleHyperlink,
|
||||
public CAccessibleValue,
|
||||
public IAccessible2
|
||||
{
|
||||
|
|
|
@ -26,7 +26,6 @@ CPPSRCS = \
|
|||
nsAccessNodeWrap.cpp \
|
||||
nsHTMLWin32ObjectAccessible.cpp \
|
||||
nsWinUtils.cpp \
|
||||
CAccessibleHyperlink.cpp \
|
||||
CAccessibleTable.cpp \
|
||||
CAccessibleTableCell.cpp \
|
||||
CAccessibleValue.cpp \
|
||||
|
@ -36,6 +35,7 @@ CPPSRCS = \
|
|||
ia2AccessibleComponent.cpp \
|
||||
ia2AccessibleEditableText.cpp \
|
||||
ia2AccessibleImage.cpp \
|
||||
ia2AccessibleHyperlink.cpp \
|
||||
ia2AccessibleHypertext.cpp \
|
||||
ia2AccessibleRelation.cpp \
|
||||
ia2AccessibleText.cpp \
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
* 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 "CAccessibleHyperlink.h"
|
||||
|
||||
#include "Accessible2.h"
|
||||
#include "AccessibleHyperlink.h"
|
||||
#include "AccessibleHyperlink_i.c"
|
||||
|
@ -17,13 +15,12 @@
|
|||
// IUnknown
|
||||
|
||||
STDMETHODIMP
|
||||
CAccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
|
||||
ia2AccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
|
||||
{
|
||||
*ppv = NULL;
|
||||
|
||||
if (IID_IAccessibleHyperlink == iid) {
|
||||
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
|
||||
if (!thisObj->IsLink())
|
||||
if (!static_cast<AccessibleWrap*>(this)->IsLink())
|
||||
return E_NOINTERFACE;
|
||||
|
||||
*ppv = static_cast<IAccessibleHyperlink*>(this);
|
||||
|
@ -37,12 +34,12 @@ CAccessibleHyperlink::QueryInterface(REFIID iid, void** ppv)
|
|||
// IAccessibleHyperlink
|
||||
|
||||
STDMETHODIMP
|
||||
CAccessibleHyperlink::get_anchor(long aIndex, VARIANT *aAnchor)
|
||||
ia2AccessibleHyperlink::get_anchor(long aIndex, VARIANT* aAnchor)
|
||||
{
|
||||
__try {
|
||||
VariantInit(aAnchor);
|
||||
|
||||
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
|
||||
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
|
||||
if (thisObj->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
|
@ -72,12 +69,12 @@ __try {
|
|||
}
|
||||
|
||||
STDMETHODIMP
|
||||
CAccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT *aAnchorTarget)
|
||||
ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget)
|
||||
{
|
||||
__try {
|
||||
VariantInit(aAnchorTarget);
|
||||
|
||||
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
|
||||
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
|
||||
if (thisObj->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
|
@ -115,12 +112,12 @@ __try {
|
|||
}
|
||||
|
||||
STDMETHODIMP
|
||||
CAccessibleHyperlink::get_startIndex(long *aIndex)
|
||||
ia2AccessibleHyperlink::get_startIndex(long* aIndex)
|
||||
{
|
||||
__try {
|
||||
*aIndex = 0;
|
||||
|
||||
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
|
||||
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
|
||||
if (thisObj->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
|
@ -135,12 +132,12 @@ __try {
|
|||
}
|
||||
|
||||
STDMETHODIMP
|
||||
CAccessibleHyperlink::get_endIndex(long *aIndex)
|
||||
ia2AccessibleHyperlink::get_endIndex(long* aIndex)
|
||||
{
|
||||
__try {
|
||||
*aIndex = 0;
|
||||
|
||||
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
|
||||
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
|
||||
if (thisObj->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
|
@ -155,12 +152,12 @@ __try {
|
|||
}
|
||||
|
||||
STDMETHODIMP
|
||||
CAccessibleHyperlink::get_valid(boolean *aValid)
|
||||
ia2AccessibleHyperlink::get_valid(boolean* aValid)
|
||||
{
|
||||
__try {
|
||||
*aValid = false;
|
||||
|
||||
nsRefPtr<Accessible> thisObj = do_QueryObject(this);
|
||||
Accessible* thisObj = static_cast<AccessibleWrap*>(this);
|
||||
if (thisObj->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
|
@ -13,8 +13,8 @@
|
|||
#include "ia2AccessibleAction.h"
|
||||
#include "AccessibleHyperlink.h"
|
||||
|
||||
class CAccessibleHyperlink: public ia2AccessibleAction,
|
||||
public IAccessibleHyperlink
|
||||
class ia2AccessibleHyperlink : public ia2AccessibleAction,
|
||||
public IAccessibleHyperlink
|
||||
{
|
||||
public:
|
||||
|
|
@ -71,9 +71,20 @@ let FormAssistant = {
|
|||
if (evt.target != target || this.isKeyboardOpened)
|
||||
return;
|
||||
|
||||
if (!(evt.target instanceof HTMLInputElement ||
|
||||
evt.target instanceof HTMLTextAreaElement))
|
||||
let ignore = {
|
||||
button: true,
|
||||
checkbox: true,
|
||||
file: true,
|
||||
radio: true,
|
||||
reset: true,
|
||||
submit: true
|
||||
};
|
||||
|
||||
if ((target instanceof HTMLInputElement && ignore[target.type]) ||
|
||||
!(target instanceof HTMLInputElement ||
|
||||
target instanceof HTMLTextAreaElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isKeyboardOpened = this.tryShowIme(evt.target);
|
||||
break;
|
||||
|
|
|
@ -18,6 +18,8 @@ window.addEventListener('ContentStart', function() {
|
|||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
let hostDPI = windowUtils.displayDPI;
|
||||
|
||||
let DEFAULT_SCREEN = "320x480";
|
||||
|
||||
// This is a somewhat random selection of named screens.
|
||||
// Add more to this list when we support more hardware.
|
||||
// Data from: http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density
|
||||
|
@ -62,9 +64,9 @@ window.addEventListener('ContentStart', function() {
|
|||
try {
|
||||
screenarg = args.handleFlagWithParam('screen', false);
|
||||
|
||||
// If there isn't one, we don't need to do anything
|
||||
// If there isn't one, use the default screen
|
||||
if (screenarg === null)
|
||||
return;
|
||||
screenarg = DEFAULT_SCREEN;
|
||||
|
||||
// With no value, tell the user how to use it
|
||||
if (screenarg == '')
|
||||
|
|
|
@ -140,7 +140,6 @@ var shell = {
|
|||
addPermissions(domains.split(","));
|
||||
|
||||
CustomEventManager.init();
|
||||
|
||||
WebappsHelper.init();
|
||||
|
||||
// XXX could factor out into a settings->pref map. Not worth it yet.
|
||||
|
@ -216,7 +215,7 @@ var shell = {
|
|||
case evt.DOM_VK_PAGE_DOWN:
|
||||
this.changeVolume(-1);
|
||||
break;
|
||||
|
||||
|
||||
case evt.DOM_VK_PAGE_UP:
|
||||
this.changeVolume(1);
|
||||
break;
|
||||
|
@ -249,6 +248,9 @@ var shell = {
|
|||
}
|
||||
break;
|
||||
case 'mozbrowserloadstart':
|
||||
if (content.document.location == 'about:blank')
|
||||
return;
|
||||
|
||||
this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
|
||||
|
||||
let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
|
|
|
@ -236,9 +236,9 @@ CollectWindowReports(nsGlobalWindow *aWindow,
|
|||
REPORT("/layout/frames/" # classname, frameSize, \
|
||||
"Memory used by frames of " \
|
||||
"type " #classname " within a window."); \
|
||||
aWindowTotalSizes->mArenaStats.FRAME_ID_STAT_FIELD(classname) \
|
||||
+= frameSize; \
|
||||
} \
|
||||
aWindowTotalSizes->mArenaStats.FRAME_ID_STAT_FIELD(classname) \
|
||||
+= frameSize; \
|
||||
}
|
||||
#include "nsFrameIdList.h"
|
||||
#undef FRAME_ID
|
||||
|
|
|
@ -2370,11 +2370,14 @@ class Tokenizer(object):
|
|||
lexpos=self.lexer.lexpos,
|
||||
filename = self.filename))
|
||||
|
||||
def __init__(self, outputdir):
|
||||
self.lexer = lex.lex(object=self,
|
||||
outputdir=outputdir,
|
||||
lextab='webidllex',
|
||||
reflags=re.DOTALL)
|
||||
def __init__(self, outputdir, lexer=None):
|
||||
if lexer:
|
||||
self.lexer = lexer
|
||||
else:
|
||||
self.lexer = lex.lex(object=self,
|
||||
outputdir=outputdir,
|
||||
lextab='webidllex',
|
||||
reflags=re.DOTALL)
|
||||
|
||||
class Parser(Tokenizer):
|
||||
def getLocation(self, p, i):
|
||||
|
@ -3459,11 +3462,13 @@ class Parser(Tokenizer):
|
|||
else:
|
||||
raise WebIDLError("invalid syntax", Location(self.lexer, p.lineno, p.lexpos, self._filename))
|
||||
|
||||
def __init__(self, outputdir=''):
|
||||
Tokenizer.__init__(self, outputdir)
|
||||
def __init__(self, outputdir='', lexer=None):
|
||||
Tokenizer.__init__(self, outputdir, lexer)
|
||||
self.parser = yacc.yacc(module=self,
|
||||
outputdir=outputdir,
|
||||
tabmodule='webidlyacc')
|
||||
tabmodule='webidlyacc',
|
||||
errorlog=yacc.NullLogger(),
|
||||
picklefile='WebIDLGrammar.pkl')
|
||||
self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
|
||||
self._installBuiltins(self._globalScope)
|
||||
self._productions = []
|
||||
|
@ -3535,7 +3540,7 @@ class Parser(Tokenizer):
|
|||
return result
|
||||
|
||||
def reset(self):
|
||||
return Parser()
|
||||
return Parser(lexer=self.lexer)
|
||||
|
||||
# Builtin IDL defined by WebIDL
|
||||
_builtins = """
|
||||
|
|
|
@ -58,6 +58,7 @@ $(CPPSRCS): ../%Binding.cpp: $(bindinggen_dependencies) \
|
|||
|
||||
_TEST_FILES = \
|
||||
test_enums.html \
|
||||
test_integers.html \
|
||||
test_interfaceToString.html \
|
||||
test_lookupGetter.html \
|
||||
test_InstanceOf.html \
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<canvas id="c" width="1" height="1"></canvas>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
function testInt64NonFinite(arg) {
|
||||
// We can use a WebGLRenderingContext to test conversion to 64-bit signed
|
||||
// ints edge cases.
|
||||
try {
|
||||
var gl = $("c").getContext("experimental-webgl");
|
||||
} catch (ex) {
|
||||
// No WebGL support on MacOS 10.5. Just skip this test
|
||||
todo(false, "WebGL not supported");
|
||||
return;
|
||||
}
|
||||
is(gl.getError(), 0, "Should not start in an error state");
|
||||
|
||||
var b = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, b);
|
||||
|
||||
var a = new Float32Array(1);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, a, gl.STATIC_DRAW);
|
||||
|
||||
gl.bufferSubData(gl.ARRAY_BUFFER, arg, a);
|
||||
|
||||
is(gl.getError(), 0, "Should have treated non-finite double as 0");
|
||||
}
|
||||
|
||||
testInt64NonFinite(NaN);
|
||||
testInt64NonFinite(Infinity);
|
||||
testInt64NonFinite(-Infinity);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -65,6 +65,10 @@ interface nsIRILTelephonyCallback : nsISupports
|
|||
[scriptable, uuid(8a711703-1ee5-4675-9d9a-0b188e944cfe)]
|
||||
interface nsIRILDataCallInfo : nsISupports
|
||||
{
|
||||
/**
|
||||
* Current data call state, one of the
|
||||
* nsINetworkInterface::NETWORK_STATE_* constants.
|
||||
*/
|
||||
readonly attribute unsigned long state;
|
||||
readonly attribute AString cid;
|
||||
readonly attribute AString apn;
|
||||
|
@ -217,7 +221,7 @@ interface nsIRilContext : nsISupports
|
|||
readonly attribute nsIDOMMozMobileConnectionInfo data;
|
||||
};
|
||||
|
||||
[scriptable, uuid(92bea0af-8d75-4592-87d0-1cab88e36904)]
|
||||
[scriptable, uuid(8b649965-6687-46a8-88fa-a5495ce90735)]
|
||||
interface nsIRadioInterfaceLayer : nsISupports
|
||||
{
|
||||
const unsigned short CALL_STATE_UNKNOWN = 0;
|
||||
|
@ -233,13 +237,6 @@ interface nsIRadioInterfaceLayer : nsISupports
|
|||
const unsigned short CALL_STATE_DISCONNECTED = 10;
|
||||
const unsigned short CALL_STATE_INCOMING = 11;
|
||||
|
||||
// Keep consistent with GECKO_DATACALL_STATE_* values in ril_consts.js
|
||||
const unsigned short DATACALL_STATE_UNKNOWN = 0;
|
||||
const unsigned short DATACALL_STATE_CONNECTING = 1;
|
||||
const unsigned short DATACALL_STATE_CONNECTED = 2;
|
||||
const unsigned short DATACALL_STATE_DISCONNECTING = 3;
|
||||
const unsigned short DATACALL_STATE_DISCONNECTED = 4;
|
||||
|
||||
/**
|
||||
* Activates or deactivates radio power.
|
||||
*/
|
||||
|
|
|
@ -1756,7 +1756,7 @@ let RIL = {
|
|||
* String containing PDP type to request. ("IP", "IPV6", ...)
|
||||
*/
|
||||
setupDataCall: function setupDataCall(options) {
|
||||
let token = Buf.newParcel(REQUEST_SETUP_DATA_CALL);
|
||||
let token = Buf.newParcel(REQUEST_SETUP_DATA_CALL, options);
|
||||
Buf.writeUint32(7);
|
||||
Buf.writeString(options.radioTech.toString());
|
||||
Buf.writeString(DATACALL_PROFILE_DEFAULT.toString());
|
||||
|
@ -3297,41 +3297,43 @@ RIL[REQUEST_QUERY_CLIP] = null;
|
|||
RIL[REQUEST_LAST_DATA_CALL_FAIL_CAUSE] = null;
|
||||
|
||||
RIL.readDataCall_v5 = function readDataCall_v5() {
|
||||
return {
|
||||
cid: Buf.readUint32().toString(),
|
||||
active: Buf.readUint32(), // DATACALL_ACTIVE_*
|
||||
type: Buf.readString(),
|
||||
apn: Buf.readString(),
|
||||
address: Buf.readString()
|
||||
};
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
cid = Buf.readUint32().toString();
|
||||
active = Buf.readUint32(); // DATACALL_ACTIVE_*
|
||||
type = Buf.readString();
|
||||
apn = Buf.readString();
|
||||
address = Buf.readString();
|
||||
return options;
|
||||
};
|
||||
|
||||
RIL.readDataCall_v6 = function readDataCall_v6(obj) {
|
||||
if (!obj) {
|
||||
obj = {};
|
||||
RIL.readDataCall_v6 = function readDataCall_v6(options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
obj.status = Buf.readUint32(); // DATACALL_FAIL_*
|
||||
options.status = Buf.readUint32(); // DATACALL_FAIL_*
|
||||
if (!RILQUIRKS_DATACALLSTATE_NO_SUGGESTEDRETRYTIME) {
|
||||
obj.suggestedRetryTime = Buf.readUint32();
|
||||
options.suggestedRetryTime = Buf.readUint32();
|
||||
}
|
||||
obj.cid = Buf.readUint32().toString();
|
||||
obj.active = Buf.readUint32(); // DATACALL_ACTIVE_*
|
||||
obj.type = Buf.readString();
|
||||
obj.ifname = Buf.readString();
|
||||
obj.ipaddr = Buf.readString();
|
||||
obj.dns = Buf.readString();
|
||||
obj.gw = Buf.readString();
|
||||
if (obj.dns) {
|
||||
obj.dns = obj.dns.split(" ");
|
||||
options.cid = Buf.readUint32().toString();
|
||||
options.active = Buf.readUint32(); // DATACALL_ACTIVE_*
|
||||
options.type = Buf.readString();
|
||||
options.ifname = Buf.readString();
|
||||
options.ipaddr = Buf.readString();
|
||||
options.dns = Buf.readString();
|
||||
options.gw = Buf.readString();
|
||||
if (options.dns) {
|
||||
options.dns = options.dns.split(" ");
|
||||
}
|
||||
//TODO for now we only support one address and gateway
|
||||
if (obj.ipaddr) {
|
||||
obj.ipaddr = obj.ipaddr.split(" ")[0];
|
||||
if (options.ipaddr) {
|
||||
options.ipaddr = options.ipaddr.split(" ")[0];
|
||||
}
|
||||
if (obj.gw) {
|
||||
obj.gw = obj.gw.split(" ")[0];
|
||||
if (options.gw) {
|
||||
options.gw = options.gw.split(" ")[0];
|
||||
}
|
||||
return obj;
|
||||
return options;
|
||||
};
|
||||
|
||||
RIL[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(length, options) {
|
||||
|
@ -3354,9 +3356,9 @@ RIL[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(length, options) {
|
|||
for (let i = 0; i < num; i++) {
|
||||
let datacall;
|
||||
if (version < 6) {
|
||||
datacall = this.readDataCall_v5();
|
||||
datacall = this.readDataCall_v5(options);
|
||||
} else {
|
||||
datacall = this.readDataCall_v6();
|
||||
datacall = this.readDataCall_v6(options);
|
||||
}
|
||||
datacalls[datacall.cid] = datacall;
|
||||
}
|
||||
|
|
|
@ -3411,6 +3411,8 @@ gfxFontGroup::InitTextRun(gfxContext *aContext,
|
|||
const T *aString,
|
||||
PRUint32 aLength)
|
||||
{
|
||||
NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
|
||||
|
||||
// we need to do numeral processing even on 8-bit text,
|
||||
// in case we're converting Western to Hindi/Arabic digits
|
||||
PRInt32 numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
|
||||
|
@ -3527,6 +3529,9 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
|
|||
PRUint32 aScriptRunEnd,
|
||||
PRInt32 aRunScript)
|
||||
{
|
||||
NS_ASSERTION(aScriptRunEnd > aScriptRunStart,
|
||||
"don't call InitScriptRun for a zero-length run");
|
||||
|
||||
gfxFont *mainFont = GetFontAt(0);
|
||||
|
||||
PRUint32 runStart = aScriptRunStart;
|
||||
|
@ -3763,14 +3768,12 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
const T *aString, PRUint32 aLength,
|
||||
PRInt32 aRunScript)
|
||||
{
|
||||
aRanges.Clear();
|
||||
|
||||
if (aLength == 0) {
|
||||
return;
|
||||
}
|
||||
NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
|
||||
NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
|
||||
|
||||
PRUint32 prevCh = 0;
|
||||
PRUint8 matchType = 0;
|
||||
PRInt32 lastRangeIndex = -1;
|
||||
|
||||
// initialize prevFont to the group's primary font, so that this will be
|
||||
// used for string-initial control chars, etc rather than risk hitting font
|
||||
|
@ -3803,18 +3806,20 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
|
||||
prevCh = ch;
|
||||
|
||||
if (aRanges.Length() == 0) {
|
||||
if (lastRangeIndex == -1) {
|
||||
// first char ==> make a new range
|
||||
aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
|
||||
lastRangeIndex++;
|
||||
prevFont = font;
|
||||
} else {
|
||||
// if font has changed, make a new range
|
||||
gfxTextRange& prevRange = aRanges[aRanges.Length() - 1];
|
||||
gfxTextRange& prevRange = aRanges[lastRangeIndex];
|
||||
if (prevRange.font != font || prevRange.matchType != matchType) {
|
||||
// close out the previous range
|
||||
prevRange.end = origI;
|
||||
aRanges.AppendElement(gfxTextRange(origI, i + 1,
|
||||
font, matchType));
|
||||
lastRangeIndex++;
|
||||
|
||||
// update prevFont for the next match, *unless* we switched
|
||||
// fonts on a ZWJ, in which case propagating the changed font
|
||||
|
@ -3827,7 +3832,8 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
}
|
||||
}
|
||||
}
|
||||
aRanges[aRanges.Length() - 1].end = aLength;
|
||||
|
||||
aRanges[lastRangeIndex].end = aLength;
|
||||
}
|
||||
|
||||
gfxUserFontSet*
|
||||
|
|
|
@ -30,6 +30,13 @@ struct TypeInferenceSizes
|
|||
size_t objects;
|
||||
size_t tables;
|
||||
size_t temporary;
|
||||
|
||||
void add(TypeInferenceSizes &sizes) {
|
||||
this->scripts += sizes.scripts;
|
||||
this->objects += sizes.objects;
|
||||
this->tables += sizes.tables;
|
||||
this->temporary += sizes.temporary;
|
||||
}
|
||||
};
|
||||
|
||||
// These measurements relate directly to the JSRuntime, and not to
|
||||
|
@ -79,9 +86,11 @@ struct CompartmentStats
|
|||
}
|
||||
|
||||
void *extra;
|
||||
size_t gcHeapArenaHeaders;
|
||||
size_t gcHeapArenaPadding;
|
||||
size_t gcHeapArenaUnused;
|
||||
|
||||
// If you add a new number, remember to update add() and maybe
|
||||
// gcHeapThingsSize()!
|
||||
size_t gcHeapArenaAdmin;
|
||||
size_t gcHeapUnusedGcThings;
|
||||
|
||||
size_t gcHeapObjectsNonFunction;
|
||||
size_t gcHeapObjectsFunction;
|
||||
|
@ -108,6 +117,45 @@ struct CompartmentStats
|
|||
size_t crossCompartmentWrappers;
|
||||
|
||||
TypeInferenceSizes typeInferenceSizes;
|
||||
|
||||
// Add cStats's numbers to this object's numbers.
|
||||
void add(CompartmentStats &cStats) {
|
||||
#define ADD(x) this->x += cStats.x
|
||||
|
||||
ADD(gcHeapArenaAdmin);
|
||||
ADD(gcHeapUnusedGcThings);
|
||||
|
||||
ADD(gcHeapObjectsNonFunction);
|
||||
ADD(gcHeapObjectsFunction);
|
||||
ADD(gcHeapStrings);
|
||||
ADD(gcHeapShapesTree);
|
||||
ADD(gcHeapShapesDict);
|
||||
ADD(gcHeapShapesBase);
|
||||
ADD(gcHeapScripts);
|
||||
ADD(gcHeapTypeObjects);
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
ADD(gcHeapXML);
|
||||
#endif
|
||||
|
||||
ADD(objectSlots);
|
||||
ADD(objectElements);
|
||||
ADD(objectMisc);
|
||||
ADD(stringChars);
|
||||
ADD(shapesExtraTreeTables);
|
||||
ADD(shapesExtraDictTables);
|
||||
ADD(shapesExtraTreeShapeKids);
|
||||
ADD(shapesCompartmentTables);
|
||||
ADD(scriptData);
|
||||
ADD(mjitData);
|
||||
ADD(crossCompartmentWrappers);
|
||||
|
||||
#undef ADD
|
||||
|
||||
typeInferenceSizes.add(cStats.typeInferenceSizes);
|
||||
}
|
||||
|
||||
// The size of all the live things in the GC heap.
|
||||
size_t gcHeapThingsSize();
|
||||
};
|
||||
|
||||
struct RuntimeStats
|
||||
|
@ -115,21 +163,13 @@ struct RuntimeStats
|
|||
RuntimeStats(JSMallocSizeOfFun mallocSizeOf)
|
||||
: runtime()
|
||||
, gcHeapChunkTotal(0)
|
||||
, gcHeapCommitted(0)
|
||||
, gcHeapUnused(0)
|
||||
, gcHeapChunkCleanUnused(0)
|
||||
, gcHeapChunkDirtyUnused(0)
|
||||
, gcHeapChunkCleanDecommitted(0)
|
||||
, gcHeapChunkDirtyDecommitted(0)
|
||||
, gcHeapArenaUnused(0)
|
||||
, gcHeapDecommittedArenas(0)
|
||||
, gcHeapUnusedChunks(0)
|
||||
, gcHeapUnusedArenas(0)
|
||||
, gcHeapUnusedGcThings(0)
|
||||
, gcHeapChunkAdmin(0)
|
||||
, totalObjects(0)
|
||||
, totalShapes(0)
|
||||
, totalScripts(0)
|
||||
, totalStrings(0)
|
||||
, totalMjit(0)
|
||||
, totalTypeInference(0)
|
||||
, totalAnalysisTemp(0)
|
||||
, gcHeapGcThings(0)
|
||||
, totals()
|
||||
, compartmentStatsVector()
|
||||
, currCompartmentStats(NULL)
|
||||
, mallocSizeOf(mallocSizeOf)
|
||||
|
@ -137,23 +177,38 @@ struct RuntimeStats
|
|||
|
||||
RuntimeSizes runtime;
|
||||
|
||||
size_t gcHeapChunkTotal;
|
||||
size_t gcHeapCommitted;
|
||||
size_t gcHeapUnused;
|
||||
size_t gcHeapChunkCleanUnused;
|
||||
size_t gcHeapChunkDirtyUnused;
|
||||
size_t gcHeapChunkCleanDecommitted;
|
||||
size_t gcHeapChunkDirtyDecommitted;
|
||||
size_t gcHeapArenaUnused;
|
||||
size_t gcHeapChunkAdmin;
|
||||
size_t totalObjects;
|
||||
size_t totalShapes;
|
||||
size_t totalScripts;
|
||||
size_t totalStrings;
|
||||
size_t totalMjit;
|
||||
size_t totalTypeInference;
|
||||
size_t totalAnalysisTemp;
|
||||
// If you add a new number, remember to update the constructor!
|
||||
|
||||
// Here's a useful breakdown of the GC heap.
|
||||
//
|
||||
// - rtStats.gcHeapChunkTotal
|
||||
// - decommitted bytes
|
||||
// - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks)
|
||||
// - unused bytes
|
||||
// - rtStats.gcHeapUnusedChunks (empty chunks)
|
||||
// - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
|
||||
// - rtStats.total.gcHeapUnusedGcThings (empty GC thing slots within non-empty arenas)
|
||||
// - used bytes
|
||||
// - rtStats.gcHeapChunkAdmin
|
||||
// - rtStats.total.gcHeapArenaAdmin
|
||||
// - rtStats.gcHeapGcThings (in-use GC things)
|
||||
//
|
||||
// It's possible that some arenas in empty chunks may be decommitted, but
|
||||
// we don't count those under rtStats.gcHeapDecommittedArenas because (a)
|
||||
// it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
|
||||
// multiple of the chunk size, which is good.
|
||||
|
||||
size_t gcHeapChunkTotal;
|
||||
size_t gcHeapDecommittedArenas;
|
||||
size_t gcHeapUnusedChunks;
|
||||
size_t gcHeapUnusedArenas;
|
||||
size_t gcHeapUnusedGcThings;
|
||||
size_t gcHeapChunkAdmin;
|
||||
size_t gcHeapGcThings;
|
||||
|
||||
// The sum of all compartment's measurements.
|
||||
CompartmentStats totals;
|
||||
|
||||
js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> compartmentStatsVector;
|
||||
CompartmentStats *currCompartmentStats;
|
||||
|
||||
|
|
|
@ -22,6 +22,34 @@ namespace JS {
|
|||
|
||||
using namespace js;
|
||||
|
||||
size_t
|
||||
CompartmentStats::gcHeapThingsSize()
|
||||
{
|
||||
// These are just the GC-thing measurements.
|
||||
size_t n = 0;
|
||||
n += gcHeapObjectsNonFunction;
|
||||
n += gcHeapObjectsFunction;
|
||||
n += gcHeapStrings;
|
||||
n += gcHeapShapesTree;
|
||||
n += gcHeapShapesDict;
|
||||
n += gcHeapShapesBase;
|
||||
n += gcHeapScripts;
|
||||
n += gcHeapTypeObjects;
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
n += gcHeapXML;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
size_t n2 = n;
|
||||
n2 += gcHeapArenaAdmin;
|
||||
n2 += gcHeapUnusedGcThings;
|
||||
// These numbers should sum to a multiple of the arena size.
|
||||
JS_ASSERT(n2 % gc::ArenaSize == 0);
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
|
||||
{
|
||||
|
@ -44,12 +72,10 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
|
|||
static void
|
||||
StatsChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk)
|
||||
{
|
||||
// Nb: This function is only called for dirty chunks, which is why we
|
||||
// increment gcHeapChunkDirtyDecommitted.
|
||||
RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
|
||||
for (size_t i = 0; i < gc::ArenasPerChunk; i++)
|
||||
if (chunk->decommittedArenas.get(i))
|
||||
rtStats->gcHeapChunkDirtyDecommitted += gc::ArenaSize;
|
||||
rtStats->gcHeapDecommittedArenas += gc::ArenaSize;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -58,15 +84,17 @@ StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena,
|
|||
{
|
||||
RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
|
||||
|
||||
rtStats->currCompartmentStats->gcHeapArenaHeaders += sizeof(gc::ArenaHeader);
|
||||
// The admin space includes (a) the header and (b) the padding between the
|
||||
// end of the header and the start of the first GC thing.
|
||||
size_t allocationSpace = arena->thingsSpan(thingSize);
|
||||
rtStats->currCompartmentStats->gcHeapArenaPadding +=
|
||||
gc::ArenaSize - allocationSpace - sizeof(gc::ArenaHeader);
|
||||
rtStats->currCompartmentStats->gcHeapArenaAdmin +=
|
||||
gc::ArenaSize - allocationSpace;
|
||||
|
||||
// We don't call the callback on unused things. So we compute the
|
||||
// unused space like this: arenaUnused = maxArenaUnused - arenaUsed.
|
||||
// We do this by setting arenaUnused to maxArenaUnused here, and then
|
||||
// subtracting thingSize for every used cell, in StatsCellCallback().
|
||||
rtStats->currCompartmentStats->gcHeapArenaUnused += allocationSpace;
|
||||
rtStats->currCompartmentStats->gcHeapUnusedGcThings += allocationSpace;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -146,7 +174,7 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
|
|||
#endif
|
||||
}
|
||||
// Yes, this is a subtraction: see StatsArenaCallback() for details.
|
||||
cStats->gcHeapArenaUnused -= thingSize;
|
||||
cStats->gcHeapUnusedGcThings -= thingSize;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
|
@ -155,90 +183,46 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
|
|||
if (!rtStats->compartmentStatsVector.reserve(rt->compartments.length()))
|
||||
return false;
|
||||
|
||||
rtStats->gcHeapChunkCleanDecommitted =
|
||||
rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize;
|
||||
rtStats->gcHeapChunkCleanUnused =
|
||||
size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize -
|
||||
rtStats->gcHeapChunkCleanDecommitted;
|
||||
rtStats->gcHeapChunkTotal =
|
||||
size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
|
||||
|
||||
IterateCompartmentsArenasCells(rt, rtStats, StatsCompartmentCallback,
|
||||
StatsArenaCallback, StatsCellCallback);
|
||||
rtStats->gcHeapUnusedChunks =
|
||||
size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize;
|
||||
|
||||
// This just computes rtStats->gcHeapDecommittedArenas.
|
||||
IterateChunks(rt, rtStats, StatsChunkCallback);
|
||||
|
||||
// Take the per-compartment measurements.
|
||||
IterateCompartmentsArenasCells(rt, rtStats, StatsCompartmentCallback,
|
||||
StatsArenaCallback, StatsCellCallback);
|
||||
|
||||
// Take the "explcit/js/runtime/" measurements.
|
||||
rt->sizeOfIncludingThis(rtStats->mallocSizeOf, &rtStats->runtime);
|
||||
|
||||
// This is initialized to all bytes stored in used chunks, and then we
|
||||
// subtract used space from it each time around the loop.
|
||||
rtStats->gcHeapChunkDirtyUnused = rtStats->gcHeapChunkTotal -
|
||||
rtStats->gcHeapChunkCleanUnused -
|
||||
rtStats->gcHeapChunkCleanDecommitted -
|
||||
rtStats->gcHeapChunkDirtyDecommitted;
|
||||
rtStats->gcHeapGcThings = 0;
|
||||
for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
|
||||
CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
|
||||
|
||||
rtStats->totalMjit = rtStats->runtime.mjitCode;
|
||||
|
||||
for (size_t index = 0;
|
||||
index < rtStats->compartmentStatsVector.length();
|
||||
index++) {
|
||||
CompartmentStats &cStats = rtStats->compartmentStatsVector[index];
|
||||
|
||||
size_t used = cStats.gcHeapArenaHeaders +
|
||||
cStats.gcHeapArenaPadding +
|
||||
cStats.gcHeapArenaUnused +
|
||||
cStats.gcHeapObjectsNonFunction +
|
||||
cStats.gcHeapObjectsFunction +
|
||||
cStats.gcHeapStrings +
|
||||
cStats.gcHeapShapesTree +
|
||||
cStats.gcHeapShapesDict +
|
||||
cStats.gcHeapShapesBase +
|
||||
cStats.gcHeapScripts +
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
cStats.gcHeapXML +
|
||||
#endif
|
||||
cStats.gcHeapTypeObjects;
|
||||
|
||||
rtStats->gcHeapChunkDirtyUnused -= used;
|
||||
rtStats->gcHeapArenaUnused += cStats.gcHeapArenaUnused;
|
||||
rtStats->totalObjects += cStats.gcHeapObjectsNonFunction +
|
||||
cStats.gcHeapObjectsFunction +
|
||||
cStats.objectSlots +
|
||||
cStats.objectElements +
|
||||
cStats.objectMisc;
|
||||
rtStats->totalShapes += cStats.gcHeapShapesTree +
|
||||
cStats.gcHeapShapesDict +
|
||||
cStats.gcHeapShapesBase +
|
||||
cStats.shapesExtraTreeTables +
|
||||
cStats.shapesExtraDictTables +
|
||||
cStats.shapesCompartmentTables;
|
||||
rtStats->totalScripts += cStats.gcHeapScripts +
|
||||
cStats.scriptData;
|
||||
rtStats->totalStrings += cStats.gcHeapStrings +
|
||||
cStats.stringChars;
|
||||
rtStats->totalMjit += cStats.mjitData;
|
||||
rtStats->totalTypeInference += cStats.gcHeapTypeObjects +
|
||||
cStats.typeInferenceSizes.objects +
|
||||
cStats.typeInferenceSizes.scripts +
|
||||
cStats.typeInferenceSizes.tables;
|
||||
rtStats->totalAnalysisTemp += cStats.typeInferenceSizes.temporary;
|
||||
rtStats->totals.add(cStats);
|
||||
rtStats->gcHeapGcThings += cStats.gcHeapThingsSize();
|
||||
}
|
||||
|
||||
size_t numDirtyChunks = (rtStats->gcHeapChunkTotal -
|
||||
rtStats->gcHeapChunkCleanUnused) /
|
||||
gc::ChunkSize;
|
||||
size_t numDirtyChunks =
|
||||
(rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize;
|
||||
size_t perChunkAdmin =
|
||||
sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
|
||||
rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
|
||||
rtStats->gcHeapChunkDirtyUnused -= rtStats->gcHeapChunkAdmin;
|
||||
|
||||
rtStats->gcHeapUnused = rtStats->gcHeapChunkDirtyUnused +
|
||||
rtStats->gcHeapChunkCleanUnused +
|
||||
rtStats->gcHeapArenaUnused;
|
||||
|
||||
rtStats->gcHeapCommitted = rtStats->gcHeapChunkTotal -
|
||||
rtStats->gcHeapChunkCleanDecommitted -
|
||||
rtStats->gcHeapChunkDirtyDecommitted;
|
||||
rtStats->gcHeapUnusedArenas -= rtStats->gcHeapChunkAdmin;
|
||||
|
||||
// |gcHeapUnusedArenas| is the only thing left. Compute it in terms of
|
||||
// all the others. See the comment in RuntimeStats for explanation.
|
||||
rtStats->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal -
|
||||
rtStats->gcHeapDecommittedArenas -
|
||||
rtStats->gcHeapUnusedChunks -
|
||||
rtStats->totals.gcHeapUnusedGcThings -
|
||||
rtStats->gcHeapChunkAdmin -
|
||||
rtStats->totals.gcHeapArenaAdmin -
|
||||
rtStats->gcHeapGcThings;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -523,22 +523,6 @@ ChunkPool::expireAndFree(JSRuntime *rt, bool releaseAll)
|
|||
FreeChunkList(expire(rt, releaseAll));
|
||||
}
|
||||
|
||||
JS_FRIEND_API(int64_t)
|
||||
ChunkPool::countCleanDecommittedArenas(JSRuntime *rt)
|
||||
{
|
||||
JS_ASSERT(this == &rt->gcChunkPool);
|
||||
|
||||
int64_t numDecommitted = 0;
|
||||
Chunk *chunk = emptyChunkListHead;
|
||||
while (chunk) {
|
||||
for (uint32_t i = 0; i < ArenasPerChunk; ++i)
|
||||
if (chunk->decommittedArenas.get(i))
|
||||
++numDecommitted;
|
||||
chunk = chunk->info.next;
|
||||
}
|
||||
return numDecommitted;
|
||||
}
|
||||
|
||||
/* static */ Chunk *
|
||||
Chunk::allocate(JSRuntime *rt)
|
||||
{
|
||||
|
|
|
@ -80,9 +80,6 @@ class ChunkPool {
|
|||
|
||||
/* Must be called with the GC lock taken. */
|
||||
void expireAndFree(JSRuntime *rt, bool releaseAll);
|
||||
|
||||
/* Must be called either during the GC or with the GC lock taken. */
|
||||
JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt);
|
||||
};
|
||||
|
||||
static inline JSGCTraceKind
|
||||
|
|
|
@ -1215,7 +1215,7 @@ NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSUserCompartmentCount,
|
|||
|
||||
// The REPORT* macros do an unconditional report. The CREPORT* macros are for
|
||||
// compartments; they aggregate any entries smaller than SUNDRIES_THRESHOLD
|
||||
// into "gc-heap-sundries" and "other-sundries" entries for the compartment.
|
||||
// into "gc-heap/sundries" and "other-sundries" entries for the compartment.
|
||||
|
||||
static const size_t SUNDRIES_THRESHOLD = 8192;
|
||||
|
||||
|
@ -1227,25 +1227,9 @@ static const size_t SUNDRIES_THRESHOLD = 8192;
|
|||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
|
||||
#define CREPORT(_path, _kind, _units, _amount, _desc) \
|
||||
do { \
|
||||
size_t amount = _amount; /* evaluate _amount only once */ \
|
||||
if (amount >= SUNDRIES_THRESHOLD) { \
|
||||
nsresult rv; \
|
||||
rv = cb->Callback(EmptyCString(), _path, _kind, _units, amount, \
|
||||
NS_LITERAL_CSTRING(_desc), closure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} else { \
|
||||
otherSundries += amount; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define REPORT_BYTES(_path, _kind, _amount, _desc) \
|
||||
REPORT(_path, _kind, nsIMemoryReporter::UNITS_BYTES, _amount, _desc);
|
||||
|
||||
#define CREPORT_BYTES(_path, _kind, _amount, _desc) \
|
||||
CREPORT(_path, _kind, nsIMemoryReporter::UNITS_BYTES, _amount, _desc);
|
||||
|
||||
#define REPORT_GC_BYTES(_path, _amount, _desc) \
|
||||
do { \
|
||||
size_t amount = _amount; /* evaluate _amount only once */ \
|
||||
|
@ -1258,6 +1242,23 @@ static const size_t SUNDRIES_THRESHOLD = 8192;
|
|||
gcTotal += amount; \
|
||||
} while (0)
|
||||
|
||||
// Nb: all non-GC compartment reports are currently KIND_HEAP, and this macro
|
||||
// relies on that.
|
||||
#define CREPORT_BYTES(_path, _amount, _desc) \
|
||||
do { \
|
||||
size_t amount = _amount; /* evaluate _amount only once */ \
|
||||
if (amount >= SUNDRIES_THRESHOLD) { \
|
||||
nsresult rv; \
|
||||
rv = cb->Callback(EmptyCString(), _path, \
|
||||
nsIMemoryReporter::KIND_HEAP, \
|
||||
nsIMemoryReporter::UNITS_BYTES, amount, \
|
||||
NS_LITERAL_CSTRING(_desc), closure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} else { \
|
||||
otherSundries += amount; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CREPORT_GC_BYTES(_path, _amount, _desc) \
|
||||
do { \
|
||||
size_t amount = _amount; /* evaluate _amount only once */ \
|
||||
|
@ -1274,18 +1275,16 @@ static const size_t SUNDRIES_THRESHOLD = 8192;
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
template <int N>
|
||||
inline const nsCString
|
||||
MakePath(const nsACString &pathPrefix, const JS::CompartmentStats &cStats,
|
||||
const char (&reporterName)[N])
|
||||
{
|
||||
const char *name = static_cast<char *>(cStats.extra);
|
||||
if (!name)
|
||||
name = "error while initializing compartment name";
|
||||
return pathPrefix + NS_LITERAL_CSTRING("compartment(") +
|
||||
nsDependentCString(name) + NS_LITERAL_CSTRING(")/") +
|
||||
nsDependentCString(reporterName);
|
||||
}
|
||||
#define RREPORT_BYTES(_path, _kind, _amount, _desc) \
|
||||
do { \
|
||||
size_t amount = _amount; /* evaluate _amount only once */ \
|
||||
nsresult rv; \
|
||||
rv = cb->Callback(EmptyCString(), _path, _kind, \
|
||||
nsIMemoryReporter::UNITS_BYTES, amount, \
|
||||
NS_LITERAL_CSTRING(_desc), closure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
rtTotal += amount; \
|
||||
} while (0)
|
||||
|
||||
namespace xpc {
|
||||
|
||||
|
@ -1293,188 +1292,180 @@ static nsresult
|
|||
ReportCompartmentStats(const JS::CompartmentStats &cStats,
|
||||
const nsACString &pathPrefix,
|
||||
nsIMemoryMultiReporterCallback *cb,
|
||||
nsISupports *closure, size_t *gcTotalOut)
|
||||
nsISupports *closure, size_t *gcTotalOut = NULL)
|
||||
{
|
||||
size_t gcTotal = 0, gcHeapSundries = 0, otherSundries = 0;
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/arena/headers"),
|
||||
cStats.gcHeapArenaHeaders,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"heap, within arenas, that is used to hold internal "
|
||||
"bookkeeping information.");
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/arena-admin"),
|
||||
cStats.gcHeapArenaAdmin,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap, within arenas, that is used (a) to hold internal "
|
||||
"bookkeeping information, and (b) to provide padding to "
|
||||
"align GC things.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/arena/padding"),
|
||||
cStats.gcHeapArenaPadding,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"heap, within arenas, that is unused and present only so "
|
||||
"that other data is aligned. This constitutes internal "
|
||||
"fragmentation.");
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-gc-things"),
|
||||
cStats.gcHeapUnusedGcThings,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap taken by empty GC thing slots within non-empty "
|
||||
"arenas.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/arena/unused"),
|
||||
cStats.gcHeapArenaUnused,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"heap, within arenas, that could be holding useful data "
|
||||
"but currently isn't.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/objects/non-function"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/objects/non-function"),
|
||||
cStats.gcHeapObjectsNonFunction,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds non-function objects.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/objects/function"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/objects/function"),
|
||||
cStats.gcHeapObjectsFunction,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds function objects.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/strings"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings"),
|
||||
cStats.gcHeapStrings,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds string headers. String headers contain "
|
||||
"various pieces of information about a string, but do not "
|
||||
"contain (except in the case of very short strings) the "
|
||||
"string characters; characters in longer strings are "
|
||||
"counted " "under 'gc-heap/string-chars' instead.");
|
||||
"counted under 'gc-heap/string-chars' instead.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/scripts"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/scripts"),
|
||||
cStats.gcHeapScripts,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds JSScript instances. A JSScript is "
|
||||
"created for each user-defined function in a script. One "
|
||||
"is also created for the top-level code in a script.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/shapes/tree"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/shapes/tree"),
|
||||
cStats.gcHeapShapesTree,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds shapes that are in a property tree.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/shapes/dict"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/shapes/dict"),
|
||||
cStats.gcHeapShapesDict,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds shapes that are in dictionary mode.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/shapes/base"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/shapes/base"),
|
||||
cStats.gcHeapShapesBase,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that collates data common to many shapes.");
|
||||
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/type-objects"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/type-objects"),
|
||||
cStats.gcHeapTypeObjects,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds type inference information.");
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/xml"),
|
||||
CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/xml"),
|
||||
cStats.gcHeapXML,
|
||||
"Memory on the compartment's garbage-collected JavaScript "
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds E4X XML objects.");
|
||||
#endif
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "objects/slots"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.objectSlots,
|
||||
"Memory allocated for the compartment's non-fixed object "
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("objects/slots"),
|
||||
cStats.objectSlots,
|
||||
"Memory allocated for the non-fixed object "
|
||||
"slot arrays, which are used to represent object properties. "
|
||||
"Some objects also contain a fixed number of slots which are "
|
||||
"stored on the compartment's JavaScript heap; those slots "
|
||||
"stored on the JavaScript heap; those slots "
|
||||
"are not counted here, but in 'gc-heap/objects' instead.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "objects/elements"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.objectElements,
|
||||
"Memory allocated for the compartment's object element "
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("objects/elements"),
|
||||
cStats.objectElements,
|
||||
"Memory allocated for object element "
|
||||
"arrays, which are used to represent indexed object "
|
||||
"properties.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "objects/misc"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.objectMisc,
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("objects/misc"),
|
||||
cStats.objectMisc,
|
||||
"Memory allocated for various small, miscellaneous "
|
||||
"structures that hang off certain kinds of objects.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "string-chars"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.stringChars,
|
||||
"Memory allocated to hold the compartment's string "
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("string-chars"),
|
||||
cStats.stringChars,
|
||||
"Memory allocated to hold string "
|
||||
"characters. Sometimes more memory is allocated than "
|
||||
"necessary, to simplify string concatenation. Each string "
|
||||
"also includes a header which is stored on the "
|
||||
"compartment's JavaScript heap; that header is not counted "
|
||||
"here, but in 'gc-heap/strings' instead.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "shapes-extra/tree-tables"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.shapesExtraTreeTables,
|
||||
"Memory allocated for the compartment's property tables "
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes-extra/tree-tables"),
|
||||
cStats.shapesExtraTreeTables,
|
||||
"Memory allocated for the property tables "
|
||||
"that belong to shapes that are in a property tree.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "shapes-extra/dict-tables"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.shapesExtraDictTables,
|
||||
"Memory allocated for the compartment's property tables "
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes-extra/dict-tables"),
|
||||
cStats.shapesExtraDictTables,
|
||||
"Memory allocated for the property tables "
|
||||
"that belong to shapes that are in dictionary mode.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "shapes-extra/tree-shape-kids"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.shapesExtraTreeShapeKids,
|
||||
"Memory allocated for the compartment's kid hashes that "
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes-extra/tree-shape-kids"),
|
||||
cStats.shapesExtraTreeShapeKids,
|
||||
"Memory allocated for the kid hashes that "
|
||||
"belong to shapes that are in a property tree.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "shapes-extra/compartment-tables"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.shapesCompartmentTables,
|
||||
"Memory used by compartment wide tables storing shape "
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes-extra/compartment-tables"),
|
||||
cStats.shapesCompartmentTables,
|
||||
"Memory used by compartment-wide tables storing shape "
|
||||
"information for use during object construction.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "script-data"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.scriptData,
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("script-data"),
|
||||
cStats.scriptData,
|
||||
"Memory allocated for JSScript bytecode and various "
|
||||
"variable-length tables.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "mjit-data"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.mjitData,
|
||||
"Memory used by the method JIT for the compartment's "
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("mjit-data"),
|
||||
cStats.mjitData,
|
||||
"Memory used by the method JIT for "
|
||||
"compilation data: JITScripts, native maps, and inline "
|
||||
"cache structs.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "cross-compartment-wrappers"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.crossCompartmentWrappers,
|
||||
"Memory used by the compartment's cross-compartment "
|
||||
"wrappers.");
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrappers"),
|
||||
cStats.crossCompartmentWrappers,
|
||||
"Memory used by cross-compartment wrappers.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "type-inference/script-main"),
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-inference/script-main"),
|
||||
cStats.typeInferenceSizes.scripts,
|
||||
"Memory used during type inference to store type sets of "
|
||||
"variables and dynamically observed types.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "type-inference/object-main"),
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-inference/object-main"),
|
||||
cStats.typeInferenceSizes.objects,
|
||||
"Memory used during type inference to store types and "
|
||||
"possible property types of JS objects.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "type-inference/tables"),
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-inference/tables"),
|
||||
cStats.typeInferenceSizes.tables,
|
||||
"Memory used during type inference for compartment-wide "
|
||||
"tables.");
|
||||
|
||||
CREPORT_BYTES(MakePath(pathPrefix, cStats, "analysis-temporary"),
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
CREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("analysis-temporary"),
|
||||
cStats.typeInferenceSizes.temporary,
|
||||
"Memory used during type inference and compilation to hold "
|
||||
"transient analysis information. Cleared on GC.");
|
||||
|
||||
if (gcHeapSundries > 0) {
|
||||
REPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/sundries"),
|
||||
// We deliberately don't use CREPORT_GC_BYTES here.
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/sundries"),
|
||||
gcHeapSundries,
|
||||
"The sum of all this compartment's gc-heap "
|
||||
"The sum of all the gc-heap "
|
||||
"measurements that are too small to be worth showing "
|
||||
"individually.");
|
||||
}
|
||||
|
||||
if (otherSundries > 0) {
|
||||
REPORT_BYTES(MakePath(pathPrefix, cStats, "other-sundries"),
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
otherSundries,
|
||||
"The sum of all this compartment's non-gc-heap "
|
||||
// We deliberately don't use CREPORT_BYTES here.
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("other-sundries"),
|
||||
nsIMemoryReporter::KIND_HEAP, otherSundries,
|
||||
"The sum of all the non-gc-heap "
|
||||
"measurements that are too small to be worth showing "
|
||||
"individually.");
|
||||
}
|
||||
|
||||
*gcTotalOut += gcTotal;
|
||||
if (gcTotalOut) {
|
||||
*gcTotalOut += gcTotal;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1483,101 +1474,114 @@ nsresult
|
|||
ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
|
||||
const nsACString &pathPrefix,
|
||||
nsIMemoryMultiReporterCallback *cb,
|
||||
nsISupports *closure)
|
||||
nsISupports *closure, size_t *rtTotalOut)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Report each compartment's numbers.
|
||||
|
||||
size_t gcTotal = 0;
|
||||
for (size_t index = 0;
|
||||
index < rtStats.compartmentStatsVector.length();
|
||||
index++) {
|
||||
rv = ReportCompartmentStats(rtStats.compartmentStatsVector[index],
|
||||
pathPrefix, cb, closure, &gcTotal);
|
||||
for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
|
||||
JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i];
|
||||
const char *name = static_cast<char *>(cStats.extra);
|
||||
nsCString pathPrefix2 = pathPrefix + NS_LITERAL_CSTRING("compartment(") +
|
||||
nsDependentCString(name) + NS_LITERAL_CSTRING(")/");
|
||||
|
||||
rv = ReportCompartmentStats(cStats, pathPrefix2, cb, closure, &gcTotal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.object,
|
||||
"Memory used by the JSRuntime object.");
|
||||
// Report the rtStats.runtime numbers under "runtime/", and compute their
|
||||
// total for later.
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.atomsTable,
|
||||
"Memory used by the atoms table.");
|
||||
size_t rtTotal = 0;
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.contexts,
|
||||
"Memory used by JSContext objects and certain structures "
|
||||
"hanging off them.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.object,
|
||||
"Memory used by the JSRuntime object.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/dtoa"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.dtoa,
|
||||
"Memory used by DtoaState, which is used for converting "
|
||||
"strings to numbers and vice versa.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.atomsTable,
|
||||
"Memory used by the atoms table.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.temporary,
|
||||
"Memory held transiently in JSRuntime and used during "
|
||||
"compilation. It mostly holds parse nodes.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.contexts,
|
||||
"Memory used by JSContext objects and certain structures "
|
||||
"hanging off them.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/mjit-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.mjitCode,
|
||||
"Memory used by the method JIT to hold the runtime's "
|
||||
"generated code.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/dtoa"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.dtoa,
|
||||
"Memory used by DtoaState, which is used for converting "
|
||||
"strings to numbers and vice versa.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpCode,
|
||||
"Memory used by the regexp JIT to hold generated code.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.temporary,
|
||||
"Memory held transiently in JSRuntime and used during "
|
||||
"compilation. It mostly holds parse nodes.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/unused-code-memory"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.unusedCodeMemory,
|
||||
"Memory allocated by the method and/or regexp JIT to hold the "
|
||||
"runtime's code, but which is currently unused.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/mjit-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.mjitCode,
|
||||
"Memory used by the method JIT to hold the runtime's "
|
||||
"generated code.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.stackCommitted,
|
||||
"Memory used for the JS call stack. This is the committed "
|
||||
"portion of the stack; the uncommitted portion is not "
|
||||
"measured because it hardly costs anything.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpCode,
|
||||
"Memory used by the regexp JIT to hold generated code.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/gc-marker"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.gcMarker,
|
||||
"Memory used for the GC mark stack and gray roots.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/unused-code-memory"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.unusedCodeMemory,
|
||||
"Memory allocated by the method and/or regexp JIT to hold the "
|
||||
"runtime's code, but which is currently unused.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/math-cache"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.mathCache,
|
||||
"Memory used for the math cache.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.stackCommitted,
|
||||
"Memory used for the JS call stack. This is the committed "
|
||||
"portion of the stack; the uncommitted portion is not "
|
||||
"measured because it hardly costs anything.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/script-filenames"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.scriptFilenames,
|
||||
"Memory used for the table holding script filenames.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/gc-marker"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.gcMarker,
|
||||
"Memory used for the GC mark stack and gray roots.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/compartment-objects"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.compartmentObjects,
|
||||
"Memory used for JSCompartment objects. These are fairly "
|
||||
"small and all the same size, so they're not worth reporting "
|
||||
"on a per-compartment basis.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/math-cache"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.mathCache,
|
||||
"Memory used for the math cache.");
|
||||
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
|
||||
rtStats.gcHeapChunkDirtyUnused,
|
||||
"Memory on the garbage-collected JavaScript heap, within "
|
||||
"chunks with at least one allocated GC thing, that could "
|
||||
"be holding useful data but currently isn't. Memory here "
|
||||
"is mutually exclusive with memory reported under "
|
||||
"'explicit/js/gc-heap-decommitted'.");
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/script-filenames"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.scriptFilenames,
|
||||
"Memory used for the table holding script filenames.");
|
||||
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-chunk-clean-unused"),
|
||||
rtStats.gcHeapChunkCleanUnused,
|
||||
RREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/compartment-objects"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.compartmentObjects,
|
||||
"Memory used for JSCompartment objects. These are fairly "
|
||||
"small and all the same size, so they're not worth reporting "
|
||||
"on a per-compartment basis.");
|
||||
|
||||
if (rtTotalOut) {
|
||||
*rtTotalOut = rtTotal;
|
||||
}
|
||||
|
||||
// Report GC numbers that don't belong to a compartment.
|
||||
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-arenas"),
|
||||
rtStats.gcHeapUnusedArenas,
|
||||
"Memory on the garbage-collected JavaScript heap taken by "
|
||||
"completely empty chunks, that soon will be released "
|
||||
"unless claimed for new allocations. Memory here is "
|
||||
"mutually exclusive with memory reported under "
|
||||
"'explicit/js/gc-heap-decommitted'.");
|
||||
"empty arenas within non-empty chunks.");
|
||||
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-decommitted"),
|
||||
rtStats.gcHeapChunkCleanDecommitted + rtStats.gcHeapChunkDirtyDecommitted,
|
||||
"Memory in the address space of the garbage-collected "
|
||||
"JavaScript heap that is currently returned to the OS.");
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-chunks"),
|
||||
rtStats.gcHeapUnusedChunks,
|
||||
"Memory on the garbage-collected JavaScript heap taken by "
|
||||
"empty chunks, which will soon be released unless claimed "
|
||||
"for new allocations.");
|
||||
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-chunk-admin"),
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"),
|
||||
rtStats.gcHeapDecommittedArenas,
|
||||
"Memory on the garbage-collected JavaScript heap, "
|
||||
"in arenas in non-empty chunks, that is returned to the OS. "
|
||||
"This means it takes up address space but no physical "
|
||||
"memory or swap space.");
|
||||
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/chunk-admin"),
|
||||
rtStats.gcHeapChunkAdmin,
|
||||
"Memory on the garbage-collected JavaScript heap, within "
|
||||
"chunks, that is used to hold internal bookkeeping "
|
||||
|
@ -1703,131 +1707,89 @@ public:
|
|||
xpcrt->SizeOfIncludingThis(JsMallocSizeOf) +
|
||||
XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JsMallocSizeOf);
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(pathPrefix, "explicit/js/");
|
||||
NS_NAMED_LITERAL_CSTRING(explicitJs, "explicit/js/");
|
||||
|
||||
// This is the second step (see above). First we report stuff in the
|
||||
// "explicit" tree, then we report other stuff.
|
||||
|
||||
size_t rtTotal = 0;
|
||||
nsresult rv =
|
||||
xpc::ReportJSRuntimeExplicitTreeStats(rtStats, pathPrefix, cb,
|
||||
closure);
|
||||
xpc::ReportJSRuntimeExplicitTreeStats(rtStats, explicitJs, cb,
|
||||
closure, &rtTotal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("xpconnect"),
|
||||
// Report the sums of the compartment numbers.
|
||||
rv = ReportCompartmentStats(rtStats.totals,
|
||||
NS_LITERAL_CSTRING("js-main-runtime/compartments/"),
|
||||
cb, closure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Report the sum of the runtime/ numbers.
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
|
||||
nsIMemoryReporter::KIND_OTHER, rtTotal,
|
||||
"The sum of all measurements under 'explicit/js/runtime/'.");
|
||||
|
||||
// Report the numbers for memory outside of compartments.
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/decommitted-arenas"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapDecommittedArenas,
|
||||
"The same as 'explicit/js/gc-heap/decommitted-arenas'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapUnusedChunks,
|
||||
"The same as 'explicit/js/gc-heap/unused-chunks'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapUnusedArenas,
|
||||
"The same as 'explicit/js/gc-heap/unused-arenas'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapChunkAdmin,
|
||||
"The same as 'explicit/js/gc-heap/chunk-admin'.");
|
||||
|
||||
// Report a breakdown of the committed GC space.
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapUnusedChunks,
|
||||
"The same as 'explicit/js/gc-heap/unused-chunks'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapUnusedArenas,
|
||||
"The same as 'explicit/js/gc-heap/unused-arenas'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.totals.gcHeapUnusedGcThings,
|
||||
"The same as 'js-main-runtime/compartments/gc-heap/unused-gc-things'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapChunkAdmin,
|
||||
"The same as 'explicit/js/gc-heap/chunk-admin'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.totals.gcHeapArenaAdmin,
|
||||
"The same as 'js-main-runtime/compartments/gc-heap/arena-admin'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapGcThings,
|
||||
"Memory on the garbage-collected JavaScript heap that holds GC things such "
|
||||
"as objects, strings, scripts, etc.")
|
||||
|
||||
// Report xpconnect.
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect"),
|
||||
nsIMemoryReporter::KIND_HEAP, xpconnect,
|
||||
"Memory used by XPConnect.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-chunk-dirty-unused"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapChunkDirtyUnused,
|
||||
"The same as 'explicit/js/gc-heap-chunk-dirty-unused'. "
|
||||
"Shown here for easy comparison with other 'js-gc' "
|
||||
"reporters.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-chunk-clean-unused"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapChunkCleanUnused,
|
||||
"The same as 'explicit/js/gc-heap-chunk-clean-unused'. "
|
||||
"Shown here for easy comparison with other 'js-gc' "
|
||||
"reporters.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-arena-unused"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapArenaUnused,
|
||||
"Memory on the main JSRuntime's garbage-collected "
|
||||
"JavaScript heap, within arenas, that could be holding "
|
||||
"useful data but currently isn't. This is the sum of all "
|
||||
"compartments' 'gc-heap/arena-unused' numbers.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed-unused"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapUnused,
|
||||
"Amount of the GC heap that's committed, but that is "
|
||||
"neither part of an active allocation nor being used for "
|
||||
"bookkeeping. Equal to 'gc-heap-chunk-dirty-unused' + "
|
||||
"'gc-heap-chunk-clean-unused' + 'gc-heap-arena-unused'.");
|
||||
|
||||
// Why 10000x? 100x because it's a percentage, and another 100x
|
||||
// because nsIMemoryReporter requires that for UNITS_PERCENTAGE
|
||||
// reporters so we can get two decimal places out of the integer value.
|
||||
REPORT(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed-unused-ratio"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
nsIMemoryReporter::UNITS_PERCENTAGE,
|
||||
(PRInt64) 10000 * rtStats.gcHeapUnused /
|
||||
((double) rtStats.gcHeapCommitted - rtStats.gcHeapUnused),
|
||||
"Ratio of committed, unused bytes to allocated bytes; i.e. "
|
||||
"'gc-heap-committed-unused' / 'gc-heap-allocated'. This "
|
||||
"measures the overhead of the GC heap allocator relative to the "
|
||||
"amount of memory allocated.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapCommitted,
|
||||
"Committed memory (i.e., in physical memory or swap) "
|
||||
"used by the garbage-collected JavaScript heap.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-allocated"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
(rtStats.gcHeapCommitted - rtStats.gcHeapUnused),
|
||||
"Amount of the GC heap used for active allocations and "
|
||||
"bookkeeping. This is calculated as 'gc-heap-committed' "
|
||||
"- 'gc-heap-unused'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-decommitted"),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
rtStats.gcHeapChunkCleanDecommitted + rtStats.gcHeapChunkDirtyDecommitted,
|
||||
"The same as 'explicit/js/gc-heap-decommitted'. Shown "
|
||||
"here for easy comparison with other 'js-gc' reporters.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-objects"),
|
||||
nsIMemoryReporter::KIND_OTHER, rtStats.totalObjects,
|
||||
"Memory used for all object-related data in the main "
|
||||
"JSRuntime. This is the sum of all compartments' "
|
||||
"'gc-heap/objects-non-function', "
|
||||
"'gc-heap/objects-function' and 'object-slots' numbers.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-shapes"),
|
||||
nsIMemoryReporter::KIND_OTHER, rtStats.totalShapes,
|
||||
"Memory used for all shape-related data in the main "
|
||||
"JSRuntime. This is the sum of all compartments' "
|
||||
"'gc-heap/shapes/tree', 'gc-heap/shapes/dict', "
|
||||
"'gc-heap/shapes/base', 'shapes-extra/tree-tables', "
|
||||
"'shapes-extra/dict-tables', "
|
||||
"'shapes-extra/tree-shape-kids' and "
|
||||
"'shapes-extra/empty-shape-arrays'.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-scripts"),
|
||||
nsIMemoryReporter::KIND_OTHER, rtStats.totalScripts,
|
||||
"Memory used for all script-related data in the main "
|
||||
"JSRuntime. This is the sum of all compartments' "
|
||||
"'gc-heap/scripts' and 'script-data' numbers.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-strings"),
|
||||
nsIMemoryReporter::KIND_OTHER, rtStats.totalStrings,
|
||||
"Memory used for all string-related data in the main "
|
||||
"JSRuntime. This is the sum of all compartments' "
|
||||
"'gc-heap/strings' and 'string-chars' numbers.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-mjit"),
|
||||
nsIMemoryReporter::KIND_OTHER, rtStats.totalMjit,
|
||||
"Memory used by the method JIT in the main JSRuntime. "
|
||||
"This is the sum of all compartments' 'mjit/code', and "
|
||||
"'mjit/data' numbers.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-type-inference"),
|
||||
nsIMemoryReporter::KIND_OTHER, rtStats.totalTypeInference,
|
||||
"Non-transient memory used by type inference in the main "
|
||||
"JSRuntime. This is the sum of all compartments' "
|
||||
"'gc-heap/type-objects', 'type-inference/script-main', "
|
||||
"'type-inference/object-main' and "
|
||||
"'type-inference/tables' numbers.");
|
||||
|
||||
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-analysis-temporary"),
|
||||
nsIMemoryReporter::KIND_OTHER, rtStats.totalAnalysisTemp,
|
||||
"Memory used transiently during type inference and "
|
||||
"compilation in the main JSRuntime. This is the sum of "
|
||||
"all compartments' 'analysis-temporary' numbers.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/dom/DOMJSClass.h"
|
||||
#include "nsMathUtils.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
class nsIXPConnectWrappedJS;
|
||||
|
@ -267,7 +268,7 @@ nsresult
|
|||
ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
|
||||
const nsACString &pathPrefix,
|
||||
nsIMemoryMultiReporterCallback *cb,
|
||||
nsISupports *closure);
|
||||
nsISupports *closure, size_t *rtTotal = NULL);
|
||||
|
||||
/**
|
||||
* Convert a jsval to PRInt64. Return true on success.
|
||||
|
@ -284,7 +285,12 @@ ValueToInt64(JSContext *cx, JS::Value v, int64_t *result)
|
|||
double doubleval;
|
||||
if (!JS_ValueToNumber(cx, v, &doubleval))
|
||||
return false;
|
||||
*result = static_cast<int64_t>(doubleval);
|
||||
// Be careful with non-finite doubles
|
||||
if (NS_finite(doubleval))
|
||||
// XXXbz this isn't quite right either; need to do the mod thing
|
||||
*result = static_cast<int64_t>(doubleval);
|
||||
else
|
||||
*result = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -304,7 +310,12 @@ ValueToUint64(JSContext *cx, JS::Value v, uint64_t *result)
|
|||
double doubleval;
|
||||
if (!JS_ValueToNumber(cx, v, &doubleval))
|
||||
return false;
|
||||
*result = static_cast<uint64_t>(doubleval);
|
||||
// Be careful with non-finite doubles
|
||||
if (NS_finite(doubleval))
|
||||
// XXXbz this isn't quite right either; need to do the mod thing
|
||||
*result = static_cast<uint64_t>(doubleval);
|
||||
else
|
||||
*result = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5001,6 +5001,14 @@ nsIFrame::GetVisualOverflowRectRelativeToSelf() const
|
|||
return GetVisualOverflowRect();
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsIFrame::GetPreEffectsVisualOverflowRect() const
|
||||
{
|
||||
nsRect* r = static_cast<nsRect*>
|
||||
(Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
|
||||
return r ? *r : GetVisualOverflowRectRelativeToSelf();
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
nsFrame::UpdateOverflow()
|
||||
{
|
||||
|
|
|
@ -2298,6 +2298,13 @@ public:
|
|||
*/
|
||||
nsRect GetVisualOverflowRectRelativeToSelf() const;
|
||||
|
||||
/**
|
||||
* Returns this frame's visual overflow rect as it would be before taking
|
||||
* account of SVG effects or transforms. The rect returned is relative to
|
||||
* this frame.
|
||||
*/
|
||||
nsRect GetPreEffectsVisualOverflowRect() const;
|
||||
|
||||
/**
|
||||
* Store the overflow area in the frame's mOverflow.mVisualDeltas
|
||||
* fields or as a frame property in the frame manager so that it can
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsGkAtoms.h"
|
||||
#include "nsRenderingContext.h"
|
||||
#include "nsSVGEffects.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsSVGFilterElement.h"
|
||||
#include "nsSVGFilterInstance.h"
|
||||
#include "nsSVGFilterPaintCallback.h"
|
||||
|
@ -27,27 +28,70 @@ NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|||
NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame)
|
||||
|
||||
/**
|
||||
* Returns the entire filter size if aDeviceRect is null, or if
|
||||
* the result is too large to be stored in an nsIntRect.
|
||||
* Converts an nsRect that is relative to a filtered frame's origin (i.e. the
|
||||
* top-left corner of its border box) into filter space.
|
||||
* Returns the entire filter region (a rect the width/height of aFilterRes) if
|
||||
* aFrameRect is null, or if the result is too large to be stored in an
|
||||
* nsIntRect.
|
||||
*/
|
||||
static nsIntRect
|
||||
MapDeviceRectToFilterSpace(const gfxMatrix& aMatrix,
|
||||
const gfxIntSize& aFilterSize,
|
||||
const nsIntRect* aDeviceRect)
|
||||
MapFrameRectToFilterSpace(const nsRect* aRect,
|
||||
PRInt32 aAppUnitsPerCSSPx,
|
||||
const gfxMatrix& aFrameSpaceInCSSPxToFilterSpace,
|
||||
const gfxIntSize& aFilterRes)
|
||||
{
|
||||
nsIntRect rect(0, 0, aFilterSize.width, aFilterSize.height);
|
||||
if (aDeviceRect) {
|
||||
gfxRect r = aMatrix.TransformBounds(gfxRect(aDeviceRect->x, aDeviceRect->y,
|
||||
aDeviceRect->width, aDeviceRect->height));
|
||||
r.RoundOut();
|
||||
nsIntRect rect(0, 0, aFilterRes.width, aFilterRes.height);
|
||||
if (aRect) {
|
||||
gfxRect rectInCSSPx =
|
||||
nsLayoutUtils::RectToGfxRect(*aRect, aAppUnitsPerCSSPx);
|
||||
gfxRect rectInFilterSpace =
|
||||
aFrameSpaceInCSSPxToFilterSpace.TransformBounds(rectInCSSPx);
|
||||
rectInFilterSpace.RoundOut();
|
||||
nsIntRect intRect;
|
||||
if (gfxUtils::GfxRectToIntRect(r, &intRect)) {
|
||||
if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
|
||||
rect = intRect;
|
||||
}
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transform from frame space to the coordinate space that
|
||||
* GetCanvasTM transforms to. "Frame space" is the origin of a frame, aka the
|
||||
* top-left corner of its border box, aka the top left corner of its mRect.
|
||||
*/
|
||||
static gfxMatrix
|
||||
GetUserToFrameSpaceInCSSPxTransform(nsIFrame *aFrame)
|
||||
{
|
||||
gfxMatrix userToFrameSpaceInCSSPx;
|
||||
|
||||
if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
|
||||
PRInt32 appUnitsPerCSSPx = aFrame->PresContext()->AppUnitsPerCSSPixel();
|
||||
// As currently implemented by Mozilla for the purposes of filters, user
|
||||
// space is the coordinate system established by GetCanvasTM(), since
|
||||
// that's what we use to set filterToDeviceSpace above. In other words,
|
||||
// for SVG, user space is actually the coordinate system aTarget
|
||||
// establishes for _its_ children (i.e. after taking account of any x/y
|
||||
// and viewBox attributes), not the coordinate system that is established
|
||||
// for it by its 'transform' attribute (or by its _parent_) as it's
|
||||
// normally defined. (XXX We should think about fixing this.) The only
|
||||
// frame type for which these extra transforms are not simply an x/y
|
||||
// translation is nsSVGInnerSVGFrame, hence we treat it specially here.
|
||||
if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame) {
|
||||
userToFrameSpaceInCSSPx =
|
||||
static_cast<nsSVGElement*>(aFrame->GetContent())->
|
||||
PrependLocalTransformsTo(gfxMatrix());
|
||||
} else {
|
||||
gfxPoint targetsUserSpaceOffset =
|
||||
nsLayoutUtils::RectToGfxRect(aFrame->GetRect(), appUnitsPerCSSPx).
|
||||
TopLeft();
|
||||
userToFrameSpaceInCSSPx.Translate(-targetsUserSpaceOffset);
|
||||
}
|
||||
}
|
||||
// else, for all other frames, leave as the identity matrix
|
||||
return userToFrameSpaceInCSSPx;
|
||||
}
|
||||
|
||||
class nsSVGFilterFrame::AutoFilterReferencer
|
||||
{
|
||||
public:
|
||||
|
@ -71,10 +115,10 @@ public:
|
|||
nsAutoFilterInstance(nsIFrame *aTarget,
|
||||
nsSVGFilterFrame *aFilterFrame,
|
||||
nsSVGFilterPaintCallback *aPaint,
|
||||
const nsIntRect *aPostFilterDirtyRect,
|
||||
const nsIntRect *aPreFilterDirtyRect,
|
||||
const gfxRect *aOverrideBBox,
|
||||
const gfxMatrix *aOverrideUserToDeviceSpace = nsnull);
|
||||
const nsRect *aPostFilterDirtyRect,
|
||||
const nsRect *aPreFilterDirtyRect,
|
||||
const nsRect *aOverridePreFilterVisualOverflowRect,
|
||||
const gfxRect *aOverrideBBox = nsnull);
|
||||
~nsAutoFilterInstance() {}
|
||||
|
||||
// If this returns null, then draw nothing. Either the filter draws
|
||||
|
@ -88,10 +132,10 @@ private:
|
|||
nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
|
||||
nsSVGFilterFrame *aFilterFrame,
|
||||
nsSVGFilterPaintCallback *aPaint,
|
||||
const nsIntRect *aPostFilterDirtyRect,
|
||||
const nsIntRect *aPreFilterDirtyRect,
|
||||
const gfxRect *aOverrideBBox,
|
||||
const gfxMatrix *aOverrideUserToDeviceSpace)
|
||||
const nsRect *aPostFilterDirtyRect,
|
||||
const nsRect *aPreFilterDirtyRect,
|
||||
const nsRect *aPreFilterVisualOverflowRectOverride,
|
||||
const gfxRect *aOverrideBBox)
|
||||
{
|
||||
const nsSVGFilterElement *filter = aFilterFrame->GetFilterContent();
|
||||
|
||||
|
@ -122,6 +166,7 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
|
|||
XYWH[1] = *aFilterFrame->GetLengthValue(nsSVGFilterElement::Y);
|
||||
XYWH[2] = *aFilterFrame->GetLengthValue(nsSVGFilterElement::WIDTH);
|
||||
XYWH[3] = *aFilterFrame->GetLengthValue(nsSVGFilterElement::HEIGHT);
|
||||
// The filter region in user space, in user units:
|
||||
gfxRect filterRegion = nsSVGUtils::GetRelativeRect(filterUnits,
|
||||
XYWH, bbox, aTarget);
|
||||
|
||||
|
@ -131,15 +176,11 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
|
|||
return;
|
||||
}
|
||||
|
||||
gfxMatrix userToDeviceSpace;
|
||||
if (aOverrideUserToDeviceSpace) {
|
||||
userToDeviceSpace = *aOverrideUserToDeviceSpace;
|
||||
} else {
|
||||
userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
|
||||
}
|
||||
|
||||
// Calculate filterRes (the width and height of the pixel buffer of the
|
||||
// temporary offscreen surface that we'll paint into):
|
||||
// temporary offscreen surface that we would/will create to paint into when
|
||||
// painting the entire filtered element) and, if necessary, adjust
|
||||
// filterRegion out slightly so that it aligns with pixel boundaries of this
|
||||
// buffer:
|
||||
|
||||
gfxIntSize filterRes;
|
||||
const nsSVGIntegerPair* filterResAttrs =
|
||||
|
@ -183,52 +224,55 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
|
|||
filterRegion.Scale(1.0 / scale);
|
||||
}
|
||||
|
||||
// XXX we haven't taken account of the fact that filterRegion may be
|
||||
// partially or entirely outside the current clip region. :-/
|
||||
|
||||
// Convert the dirty rects to filter space, and create our nsSVGFilterInstance:
|
||||
// Get various transforms:
|
||||
|
||||
gfxMatrix filterToUserSpace(filterRegion.Width() / filterRes.width, 0.0f,
|
||||
0.0f, filterRegion.Height() / filterRes.height,
|
||||
filterRegion.X(), filterRegion.Y());
|
||||
gfxMatrix filterToDeviceSpace = filterToUserSpace * userToDeviceSpace;
|
||||
|
||||
// filterToDeviceSpace is always invertible
|
||||
gfxMatrix deviceToFilterSpace = filterToDeviceSpace;
|
||||
deviceToFilterSpace.Invert();
|
||||
|
||||
// Only used (so only set) when we paint:
|
||||
gfxMatrix filterToDeviceSpace;
|
||||
if (aPaint) {
|
||||
filterToDeviceSpace =
|
||||
filterToUserSpace * nsSVGUtils::GetCanvasTM(aTarget);
|
||||
}
|
||||
|
||||
// Convert the passed in rects from frame to filter space:
|
||||
|
||||
PRInt32 appUnitsPerCSSPx = aTarget->PresContext()->AppUnitsPerCSSPixel();
|
||||
|
||||
gfxMatrix filterToFrameSpaceInCSSPx =
|
||||
filterToUserSpace * GetUserToFrameSpaceInCSSPxTransform(aTarget);
|
||||
// filterToFrameSpaceInCSSPx is always invertible
|
||||
gfxMatrix frameSpaceInCSSPxTofilterSpace = filterToFrameSpaceInCSSPx;
|
||||
frameSpaceInCSSPxTofilterSpace.Invert();
|
||||
|
||||
nsIntRect postFilterDirtyRect =
|
||||
MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aPostFilterDirtyRect);
|
||||
MapFrameRectToFilterSpace(aPostFilterDirtyRect, appUnitsPerCSSPx,
|
||||
frameSpaceInCSSPxTofilterSpace, filterRes);
|
||||
nsIntRect preFilterDirtyRect =
|
||||
MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aPreFilterDirtyRect);
|
||||
nsIntRect targetBoundsDeviceSpace;
|
||||
nsISVGChildFrame* svgTarget = do_QueryFrame(aTarget);
|
||||
if (svgTarget) {
|
||||
if (aOverrideUserToDeviceSpace) {
|
||||
// If aOverrideUserToDeviceSpace is specified, it is a simple
|
||||
// CSS-px-to-dev-px transform passed by nsSVGFilterFrame::
|
||||
// GetPostFilterBounds() when requesting the filter expansion of the
|
||||
// overflow rects in frame space. In this case GetCoveredRegion() is not
|
||||
// what we want since it is in outer-<svg> space, so GetPostFilterBounds
|
||||
// passes in the pre-filter bounds of the frame in frame space for us to
|
||||
// use instead.
|
||||
NS_ASSERTION(aPreFilterDirtyRect, "Who passed aOverrideUserToDeviceSpace?");
|
||||
targetBoundsDeviceSpace = *aPreFilterDirtyRect;
|
||||
} else {
|
||||
targetBoundsDeviceSpace =
|
||||
svgTarget->GetCoveredRegion().ToOutsidePixels(aTarget->
|
||||
PresContext()->AppUnitsPerDevPixel());
|
||||
}
|
||||
MapFrameRectToFilterSpace(aPreFilterDirtyRect, appUnitsPerCSSPx,
|
||||
frameSpaceInCSSPxTofilterSpace, filterRes);
|
||||
nsIntRect preFilterVisualOverflowRect;
|
||||
if (aPreFilterVisualOverflowRectOverride) {
|
||||
preFilterVisualOverflowRect =
|
||||
MapFrameRectToFilterSpace(aPreFilterVisualOverflowRectOverride,
|
||||
appUnitsPerCSSPx,
|
||||
frameSpaceInCSSPxTofilterSpace, filterRes);
|
||||
} else {
|
||||
nsRect preFilterVOR = aTarget->GetPreEffectsVisualOverflowRect();
|
||||
preFilterVisualOverflowRect =
|
||||
MapFrameRectToFilterSpace(&preFilterVOR, appUnitsPerCSSPx,
|
||||
frameSpaceInCSSPxTofilterSpace, filterRes);
|
||||
}
|
||||
nsIntRect targetBoundsFilterSpace =
|
||||
MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, &targetBoundsDeviceSpace);
|
||||
|
||||
// Setup instance data
|
||||
mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterRegion,
|
||||
nsIntSize(filterRes.width, filterRes.height),
|
||||
filterToDeviceSpace, targetBoundsFilterSpace,
|
||||
postFilterDirtyRect, preFilterDirtyRect,
|
||||
primitiveUnits);
|
||||
mInstance =
|
||||
new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterRegion,
|
||||
nsIntSize(filterRes.width, filterRes.height),
|
||||
filterToDeviceSpace, filterToFrameSpaceInCSSPx,
|
||||
preFilterVisualOverflowRect, postFilterDirtyRect,
|
||||
preFilterDirtyRect, primitiveUnits);
|
||||
}
|
||||
|
||||
PRUint16
|
||||
|
@ -388,13 +432,13 @@ nsresult
|
|||
nsSVGFilterFrame::PaintFilteredFrame(nsRenderingContext *aContext,
|
||||
nsIFrame *aFilteredFrame,
|
||||
nsSVGFilterPaintCallback *aPaintCallback,
|
||||
const nsIntRect *aDirtyArea)
|
||||
const nsRect *aDirtyArea)
|
||||
{
|
||||
nsAutoFilterInstance instance(aFilteredFrame, this, aPaintCallback,
|
||||
aDirtyArea, nsnull, nsnull);
|
||||
if (!instance.get())
|
||||
if (!instance.get()) {
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
nsRefPtr<gfxASurface> result;
|
||||
nsresult rv = instance.get()->Render(getter_AddRefs(result));
|
||||
if (NS_SUCCEEDED(rv) && result) {
|
||||
|
@ -404,118 +448,75 @@ nsSVGFilterFrame::PaintFilteredFrame(nsRenderingContext *aContext,
|
|||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns NS_ERROR_FAILURE if the result is too large to be stored
|
||||
* in an nsIntRect.
|
||||
*/
|
||||
static nsresult
|
||||
TransformFilterSpaceToDeviceSpace(nsSVGFilterInstance *aInstance,
|
||||
nsIntRect *aRect)
|
||||
static nsRect
|
||||
TransformFilterSpaceToFrameSpace(nsSVGFilterInstance *aInstance,
|
||||
nsIntRect *aRect)
|
||||
{
|
||||
gfxMatrix m = aInstance->GetFilterSpaceToDeviceSpaceTransform();
|
||||
gfxMatrix m = aInstance->GetFilterSpaceToFrameSpaceInCSSPxTransform();
|
||||
gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
|
||||
r = m.TransformBounds(r);
|
||||
r.RoundOut();
|
||||
nsIntRect deviceRect;
|
||||
if (!gfxUtils::GfxRectToIntRect(r, &deviceRect))
|
||||
return NS_ERROR_FAILURE;
|
||||
*aRect = deviceRect;
|
||||
return NS_OK;
|
||||
return nsLayoutUtils::RoundGfxRectToAppRect(r, aInstance->AppUnitsPerCSSPixel());
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsRect
|
||||
nsSVGFilterFrame::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
|
||||
const nsIntRect& aPreFilterDirtyRect)
|
||||
const nsRect& aPreFilterDirtyRect)
|
||||
{
|
||||
bool overrideCTM = false;
|
||||
gfxMatrix ctm;
|
||||
|
||||
if (aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
|
||||
// In the case of this method we want to input a rect that is relative to
|
||||
// aFilteredFrame and get back a rect that is relative to aFilteredFrame.
|
||||
// To do that we need to provide an override canvanTM to prevent the filter
|
||||
// code from calling GetCanvasTM and using that TM as normal.
|
||||
overrideCTM = true;
|
||||
ctm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFilteredFrame);
|
||||
}
|
||||
|
||||
nsAutoFilterInstance instance(aFilteredFrame, this, nsnull, nsnull,
|
||||
&aPreFilterDirtyRect, nsnull,
|
||||
overrideCTM ? &ctm : nsnull);
|
||||
if (!instance.get())
|
||||
return nsIntRect();
|
||||
|
||||
&aPreFilterDirtyRect, nsnull);
|
||||
if (!instance.get()) {
|
||||
return nsRect();
|
||||
}
|
||||
// We've passed in the source's dirty area so the instance knows about it.
|
||||
// Now we can ask the instance to compute the area of the filter output
|
||||
// that's dirty.
|
||||
nsIntRect dirtyRect;
|
||||
nsresult rv = instance.get()->ComputePostFilterDirtyRect(&dirtyRect);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = TransformFilterSpaceToDeviceSpace(instance.get(), &dirtyRect);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
return dirtyRect;
|
||||
return TransformFilterSpaceToFrameSpace(instance.get(), &dirtyRect);
|
||||
}
|
||||
|
||||
return nsIntRect();
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsRect
|
||||
nsSVGFilterFrame::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
|
||||
const nsIntRect& aPostFilterDirtyRect)
|
||||
const nsRect& aPostFilterDirtyRect)
|
||||
{
|
||||
nsAutoFilterInstance instance(aFilteredFrame, this, nsnull,
|
||||
&aPostFilterDirtyRect, nsnull, nsnull);
|
||||
if (!instance.get())
|
||||
return nsIntRect();
|
||||
|
||||
if (!instance.get()) {
|
||||
return nsRect();
|
||||
}
|
||||
// Now we can ask the instance to compute the area of the source
|
||||
// that's needed.
|
||||
nsIntRect neededRect;
|
||||
nsresult rv = instance.get()->ComputeSourceNeededRect(&neededRect);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = TransformFilterSpaceToDeviceSpace(instance.get(), &neededRect);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
return neededRect;
|
||||
return TransformFilterSpaceToFrameSpace(instance.get(), &neededRect);
|
||||
}
|
||||
|
||||
return nsIntRect();
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsRect
|
||||
nsSVGFilterFrame::GetPostFilterBounds(nsIFrame *aFilteredFrame,
|
||||
const gfxRect *aOverrideBBox,
|
||||
const nsIntRect *aPreFilterBounds)
|
||||
const nsRect *aPreFilterBounds)
|
||||
{
|
||||
bool overrideCTM = false;
|
||||
gfxMatrix ctm;
|
||||
|
||||
if (aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
|
||||
// For most filter operations on SVG frames we want information in
|
||||
// outer-<svg> device space, but in this case we want the visual overflow
|
||||
// rect relative to aTarget itself. For that we need to prevent the filter
|
||||
// code using GetCanvasTM().
|
||||
overrideCTM = true;
|
||||
ctm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFilteredFrame);
|
||||
}
|
||||
|
||||
nsAutoFilterInstance instance(aFilteredFrame, this, nsnull, nsnull,
|
||||
aPreFilterBounds, aOverrideBBox,
|
||||
overrideCTM ? &ctm : nsnull);
|
||||
if (!instance.get())
|
||||
return nsIntRect();
|
||||
|
||||
aPreFilterBounds, aPreFilterBounds,
|
||||
aOverrideBBox);
|
||||
if (!instance.get()) {
|
||||
return nsRect();
|
||||
}
|
||||
// We've passed in the source's bounding box so the instance knows about
|
||||
// it. Now we can ask the instance to compute the bounding box of
|
||||
// the filter output.
|
||||
nsIntRect bbox;
|
||||
nsresult rv = instance.get()->ComputeOutputBBox(&bbox);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = TransformFilterSpaceToDeviceSpace(instance.get(), &bbox);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
return bbox;
|
||||
return TransformFilterSpaceToFrameSpace(instance.get(), &bbox);
|
||||
}
|
||||
|
||||
return nsIntRect();
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -45,41 +45,46 @@ public:
|
|||
nsIAtom* aAttribute,
|
||||
PRInt32 aModType);
|
||||
|
||||
/**
|
||||
* Paint the given filtered frame.
|
||||
* @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
|
||||
* frame space (i.e. relative to its origin, the top-left corner of its
|
||||
* border box).
|
||||
*/
|
||||
nsresult PaintFilteredFrame(nsRenderingContext *aContext,
|
||||
nsIFrame *aFilteredFrame,
|
||||
nsSVGFilterPaintCallback *aPaintCallback,
|
||||
const nsIntRect* aDirtyArea);
|
||||
const nsRect* aDirtyArea);
|
||||
|
||||
/**
|
||||
* Returns the post-filter area that could be dirtied when the given
|
||||
* pre-filter area of aFilteredFrame changes. The rects are in device pixels,
|
||||
* relative to the origin of the outer-<svg> if aFilteredFrame is SVG, or
|
||||
* else relative to aFilteredFrame itself.
|
||||
* pre-filter area of aFilteredFrame changes.
|
||||
* @param aPreFilterDirtyRect The pre-filter area of aFilteredFrame that has
|
||||
* changed, relative to aFilteredFrame, in app units.
|
||||
*/
|
||||
nsIntRect GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
|
||||
const nsIntRect& aPreFilterDirtyRect);
|
||||
nsRect GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
|
||||
const nsRect& aPreFilterDirtyRect);
|
||||
|
||||
/**
|
||||
* Returns the pre-filter area that is needed from aFilteredFrame when the
|
||||
* given post-filter area needs to be repainted. The rects are in device
|
||||
* pixels, relative to the origin of the outer-<svg> if aFilteredFrame is
|
||||
* SVG, or else relative to aFilteredFrame itself.
|
||||
* given post-filter area needs to be repainted.
|
||||
* @param aPostFilterDirtyRect The post-filter area that is dirty, relative
|
||||
* to aFilteredFrame, in app units.
|
||||
*/
|
||||
nsIntRect GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
|
||||
const nsIntRect& aPostFilterDirtyRect);
|
||||
nsRect GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
|
||||
const nsRect& aPostFilterDirtyRect);
|
||||
|
||||
/**
|
||||
* Returns the post-filter paint bounds of aFilteredFrame. The rects are
|
||||
* relative to the origin of the outer-<svg> if aFilteredFrame is SVG, or
|
||||
* else relative to aFilteredFrame itself.
|
||||
* @param aOverrideBBox A user space rect that should be used as
|
||||
* aFilteredFrame's bbox, if non-null.
|
||||
* Returns the post-filter visual overflow rect (paint bounds) of
|
||||
* aFilteredFrame.
|
||||
* @param aOverrideBBox A user space rect, in user units, that should be used
|
||||
* as aFilteredFrame's bbox ('bbox' is a specific SVG term), if non-null.
|
||||
* @param aPreFilterBounds The pre-filter visual overflow rect of
|
||||
* aFilteredFrame in device pixels, if non-null.
|
||||
* aFilteredFrame, if non-null.
|
||||
*/
|
||||
nsIntRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
|
||||
const gfxRect *aOverrideBBox = nsnull,
|
||||
const nsIntRect *aPreFilterBounds = nsnull);
|
||||
nsRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
|
||||
const gfxRect *aOverrideBBox = nsnull,
|
||||
const nsRect *aPreFilterBounds = nsnull);
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_IMETHOD Init(nsIContent* aContent,
|
||||
|
|
|
@ -78,6 +78,7 @@ public:
|
|||
const gfxRect& aFilterRegion,
|
||||
const nsIntSize& aFilterSpaceSize,
|
||||
const gfxMatrix &aFilterSpaceToDeviceSpaceTransform,
|
||||
const gfxMatrix &aFilterSpaceToFrameSpaceInCSSPxTransform,
|
||||
const nsIntRect& aTargetBounds,
|
||||
const nsIntRect& aPostFilterDirtyRect,
|
||||
const nsIntRect& aPreFilterDirtyRect,
|
||||
|
@ -87,6 +88,7 @@ public:
|
|||
mFilterElement(aFilterElement),
|
||||
mTargetBBox(aTargetBBox),
|
||||
mFilterSpaceToDeviceSpaceTransform(aFilterSpaceToDeviceSpaceTransform),
|
||||
mFilterSpaceToFrameSpaceInCSSPxTransform(aFilterSpaceToFrameSpaceInCSSPxTransform),
|
||||
mFilterRegion(aFilterRegion),
|
||||
mFilterSpaceSize(aFilterSpaceSize),
|
||||
mSurfaceRect(nsIntPoint(0, 0), aFilterSpaceSize),
|
||||
|
@ -192,6 +194,21 @@ public:
|
|||
|
||||
gfxPoint FilterSpaceToUserSpace(const gfxPoint& aPt) const;
|
||||
|
||||
/**
|
||||
* Returns the transform from filter space to frame space, in CSS px. This
|
||||
* transform does not transform to frame space in its normal app units, since
|
||||
* app units are ints, requiring appropriate rounding which can't be done by
|
||||
* a transform matrix. Callers have to do that themselves as appropriate for
|
||||
* their needs.
|
||||
*/
|
||||
gfxMatrix GetFilterSpaceToFrameSpaceInCSSPxTransform() const {
|
||||
return mFilterSpaceToFrameSpaceInCSSPxTransform;
|
||||
}
|
||||
|
||||
PRInt32 AppUnitsPerCSSPixel() const {
|
||||
return mTargetFrame->PresContext()->AppUnitsPerCSSPixel();
|
||||
}
|
||||
|
||||
private:
|
||||
typedef nsSVGFE::Image Image;
|
||||
typedef nsSVGFE::ColorModel ColorModel;
|
||||
|
@ -364,6 +381,7 @@ private:
|
|||
gfxRect mTargetBBox;
|
||||
|
||||
gfxMatrix mFilterSpaceToDeviceSpaceTransform;
|
||||
gfxMatrix mFilterSpaceToFrameSpaceInCSSPxTransform;
|
||||
gfxRect mFilterRegion;
|
||||
nsIntSize mFilterSpaceSize;
|
||||
nsIntRect mSurfaceRect;
|
||||
|
|
|
@ -260,8 +260,7 @@ nsRect
|
|||
overrideBBox.RoundOut();
|
||||
|
||||
nsRect overflowRect =
|
||||
filterFrame->GetPostFilterBounds(firstFrame, &overrideBBox).
|
||||
ToAppUnits(aFrame->PresContext()->AppUnitsPerDevPixel());
|
||||
filterFrame->GetPostFilterBounds(firstFrame, &overrideBBox);
|
||||
|
||||
// Return overflowRect relative to aFrame, rather than "user space":
|
||||
return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToUserSpace);
|
||||
|
@ -293,18 +292,14 @@ nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
|
|||
return aFrame->GetVisualOverflowRect();
|
||||
}
|
||||
|
||||
// Convert aInvalidRect into "user space" in dev pixels:
|
||||
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
// Convert aInvalidRect into "user space" in app units:
|
||||
nsPoint toUserSpace =
|
||||
aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
|
||||
nsIntRect preEffectsRect =
|
||||
(aInvalidRect + toUserSpace).ToOutsidePixels(appUnitsPerDevPixel);
|
||||
nsRect preEffectsRect = aInvalidRect + toUserSpace;
|
||||
|
||||
nsIntRect postEffectsRect =
|
||||
filterFrame->GetPostFilterDirtyArea(firstFrame, preEffectsRect);
|
||||
|
||||
// Return result relative to aFrame, rather than "user space":
|
||||
return postEffectsRect.ToAppUnits(appUnitsPerDevPixel) - toUserSpace;
|
||||
// Return ther result, relative to aFrame, not in user space:
|
||||
return filterFrame->GetPostFilterDirtyArea(firstFrame, preEffectsRect) -
|
||||
toUserSpace;
|
||||
}
|
||||
|
||||
nsRect
|
||||
|
@ -320,18 +315,14 @@ nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
|
|||
if (!filterFrame)
|
||||
return aDirtyRect;
|
||||
|
||||
// Convert aDirtyRect into "user space" in dev pixels:
|
||||
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
// Convert aDirtyRect into "user space" in app units:
|
||||
nsPoint toUserSpace =
|
||||
aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
|
||||
nsIntRect postEffectsRect =
|
||||
(aDirtyRect + toUserSpace).ToOutsidePixels(appUnitsPerDevPixel);
|
||||
nsRect postEffectsRect = aDirtyRect + toUserSpace;
|
||||
|
||||
nsIntRect preEffectsRect =
|
||||
filterFrame->GetPreFilterNeededArea(firstFrame, postEffectsRect);
|
||||
|
||||
// Return result relative to aFrame, rather than "user space":
|
||||
return preEffectsRect.ToAppUnits(appUnitsPerDevPixel) - toUserSpace;
|
||||
// Return ther result, relative to aFrame, not in user space:
|
||||
return filterFrame->GetPreFilterNeededArea(firstFrame, postEffectsRect) -
|
||||
toUserSpace;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -453,8 +444,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
|
|||
if (filterFrame) {
|
||||
RegularFramePaintCallback callback(aBuilder, aInnerList, aEffectsFrame,
|
||||
offset);
|
||||
nsIntRect dirtyRect = (aDirtyRect - offset)
|
||||
.ToOutsidePixels(appUnitsPerDevPixel);
|
||||
nsRect dirtyRect = aDirtyRect - offset;
|
||||
filterFrame->PaintFilteredFrame(aCtx, aEffectsFrame, &callback, &dirtyRect);
|
||||
} else {
|
||||
gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
|
||||
|
|
|
@ -591,12 +591,8 @@ nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
|
|||
return aPreFilterRect;
|
||||
}
|
||||
|
||||
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
nsIntRect preFilterRect =
|
||||
aPreFilterRect.ToOutsidePixels(appUnitsPerDevPixel);
|
||||
nsIntRect rect = filter->GetPostFilterBounds(aFrame, nsnull, &preFilterRect);
|
||||
nsRect r = rect.ToAppUnits(appUnitsPerDevPixel) - aFrame->GetPosition();
|
||||
return r;
|
||||
return filter->GetPostFilterBounds(aFrame, nsnull, &aPreFilterRect) -
|
||||
aFrame->GetPosition();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -659,7 +655,6 @@ nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate,
|
|||
aFrame = aFrame->GetParent();
|
||||
}
|
||||
|
||||
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
PRInt32 appUnitsPerCSSPx = aFrame->PresContext()->AppUnitsPerCSSPixel();
|
||||
|
||||
while (aFrame) {
|
||||
|
@ -690,9 +685,7 @@ nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate,
|
|||
nsSVGFilterFrame *filterFrame = nsSVGEffects::GetFilterFrame(aFrame);
|
||||
if (filterFrame) {
|
||||
invalidArea =
|
||||
filterFrame->GetPostFilterDirtyArea(aFrame,
|
||||
invalidArea.ToOutsidePixels(appUnitsPerDevPixel)).
|
||||
ToAppUnits(appUnitsPerDevPixel);
|
||||
filterFrame->GetPostFilterDirtyArea(aFrame, invalidArea);
|
||||
}
|
||||
if (aFrame->IsTransformed()) {
|
||||
invalidArea =
|
||||
|
@ -1042,6 +1035,21 @@ nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
|
|||
return static_cast<nsSVGGeometryFrame*>(aFrame)->GetCanvasTM();
|
||||
}
|
||||
|
||||
gfxMatrix
|
||||
nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame)
|
||||
{
|
||||
nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
|
||||
NS_ASSERTION(svgFrame, "bad frame");
|
||||
|
||||
gfxMatrix tm;
|
||||
if (svgFrame) {
|
||||
nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
|
||||
tm = content->PrependLocalTransformsTo(GetCanvasTM(aFrame->GetParent()),
|
||||
nsSVGElement::eUserSpaceToParent);
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, PRUint32 aFlags)
|
||||
{
|
||||
|
@ -1219,9 +1227,28 @@ nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
|
|||
|
||||
/* Paint the child */
|
||||
if (filterFrame) {
|
||||
nsRect* dirtyRect = nsnull;
|
||||
nsRect tmpDirtyRect;
|
||||
if (aDirtyRect) {
|
||||
// aDirtyRect is in outer-<svg> device pixels, but the filter code needs
|
||||
// it in frame space.
|
||||
gfxMatrix userToDeviceSpace = GetUserToCanvasTM(aFrame);
|
||||
if (userToDeviceSpace.IsSingular()) {
|
||||
return;
|
||||
}
|
||||
gfxMatrix deviceToUserSpace = userToDeviceSpace;
|
||||
deviceToUserSpace.Invert();
|
||||
gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
|
||||
gfxRect(aDirtyRect->x, aDirtyRect->y,
|
||||
aDirtyRect->width, aDirtyRect->height));
|
||||
tmpDirtyRect =
|
||||
nsLayoutUtils::RoundGfxRectToAppRect(
|
||||
dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
|
||||
aFrame->GetPosition();
|
||||
dirtyRect = &tmpDirtyRect;
|
||||
}
|
||||
SVGPaintCallback paintCallback;
|
||||
filterFrame->PaintFilteredFrame(aContext, aFrame, &paintCallback,
|
||||
aDirtyRect);
|
||||
filterFrame->PaintFilteredFrame(aContext, aFrame, &paintCallback, dirtyRect);
|
||||
} else {
|
||||
svgChildFrame->PaintSVG(aContext, aDirtyRect);
|
||||
}
|
||||
|
|
|
@ -472,6 +472,17 @@ public:
|
|||
*/
|
||||
static gfxMatrix GetCanvasTM(nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Returns the transform from aFrame's user space to canvas space. Only call
|
||||
* with SVG frames. This is like GetCanvasTM, except that it only includes
|
||||
* the transforms from aFrame's user space (i.e. the coordinate context
|
||||
* established by its 'transform' attribute, or else the coordinate context
|
||||
* that its _parent_ establishes for its children) to outer-<svg> device
|
||||
* space. Specifically, it does not include any other transforms introduced
|
||||
* by the frame such as x/y offsets and viewBox attributes.
|
||||
*/
|
||||
static gfxMatrix GetUserToCanvasTM(nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Notify the descendants of aFrame of a change to one of their ancestors
|
||||
* that might affect them.
|
||||
|
|
|
@ -511,7 +511,6 @@ function getTreesByProcess(aMgr, aTreesByProcess, aDegeneratesByProcess,
|
|||
assert(aDescription !== "", "empty smaps description");
|
||||
|
||||
} else {
|
||||
assert(aKind === KIND_OTHER, "bad other kind");
|
||||
assert(gSentenceRegExp.test(aDescription),
|
||||
"non-sentence other description");
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
vsizeAmounts.push(aAmount);
|
||||
} else if (aPath === "resident") {
|
||||
residentAmounts.push(aAmount);
|
||||
} else if (aPath === "js-main-runtime-gc-heap-committed") {
|
||||
} else if (aPath === "js-main-runtime-gc-heap-committed/used/gc-things") {
|
||||
jsGcHeapAmounts.push(aAmount);
|
||||
} else if (aPath === "heap-allocated") {
|
||||
heapAllocatedAmounts.push(aAmount);
|
||||
|
@ -141,7 +141,7 @@
|
|||
}
|
||||
checkSpecialReport("vsize", vsizeAmounts);
|
||||
checkSpecialReport("resident", residentAmounts);
|
||||
checkSpecialReport("js-main-runtime-gc-heap-committed", jsGcHeapAmounts);
|
||||
checkSpecialReport("js-main-runtime-gc-heap-committed/used/gc-things", jsGcHeapAmounts);
|
||||
checkSpecialReport("storage-sqlite", storageSqliteAmounts);
|
||||
|
||||
ok(areJsCompartmentsPresent, "js compartments are present");
|
||||
|
|
|
@ -45,8 +45,8 @@ interface nsISimpleEnumerator;
|
|||
* OTHER, units COUNT, an amount of 1, and a description that's an empty
|
||||
* string.
|
||||
*
|
||||
* - All other reports must have kind OTHER, and a description that is a
|
||||
* sentence.
|
||||
* - All other reports are unconstrained except that they must have a
|
||||
* description that is a sentence.
|
||||
*/
|
||||
[scriptable, uuid(b2c39f65-1799-4b92-a806-ab3cf6af3cfa)]
|
||||
interface nsIMemoryReporter : nsISupports
|
||||
|
@ -114,6 +114,9 @@ interface nsIMemoryReporter : nsISupports
|
|||
*
|
||||
* - OTHER: reporters which don't fit into either of these categories.
|
||||
* They can have any units.
|
||||
*
|
||||
* The kind only matters for reporters in the "explicit" tree;
|
||||
* aboutMemory.js uses it to calculate "heap-unclassified".
|
||||
*/
|
||||
const PRInt32 KIND_NONHEAP = 0;
|
||||
const PRInt32 KIND_HEAP = 1;
|
||||
|
|
Загрузка…
Ссылка в новой задаче