Merge last PGO-green changeset of mozilla-inbound to mozilla-central

This commit is contained in:
Ed Morley 2012-06-26 17:22:49 +01:00
commit 2895efcd14
32 changed files with 844 additions and 714 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -13,8 +13,8 @@
#include "ia2AccessibleAction.h"
#include "AccessibleHyperlink.h"
class CAccessibleHyperlink: public ia2AccessibleAction,
public IAccessibleHyperlink
class ia2AccessibleHyperlink : public ia2AccessibleAction,
public IAccessibleHyperlink
{
public:

View File

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

View File

@ -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 == '')

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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