diff --git a/accessible/src/msaa/AccessibleWrap.cpp b/accessible/src/msaa/AccessibleWrap.cpp index 3ddc0e776a6..5d51114f493 100644 --- a/accessible/src/msaa/AccessibleWrap.cpp +++ b/accessible/src/msaa/AccessibleWrap.cpp @@ -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; } diff --git a/accessible/src/msaa/AccessibleWrap.h b/accessible/src/msaa/AccessibleWrap.h index 7a356494c2d..a86552febe3 100644 --- a/accessible/src/msaa/AccessibleWrap.h +++ b/accessible/src/msaa/AccessibleWrap.h @@ -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 { diff --git a/accessible/src/msaa/Makefile.in b/accessible/src/msaa/Makefile.in index 96081ebfdfd..ed8d7b23e11 100644 --- a/accessible/src/msaa/Makefile.in +++ b/accessible/src/msaa/Makefile.in @@ -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 \ diff --git a/accessible/src/msaa/CAccessibleHyperlink.cpp b/accessible/src/msaa/ia2AccessibleHyperlink.cpp similarity index 82% rename from accessible/src/msaa/CAccessibleHyperlink.cpp rename to accessible/src/msaa/ia2AccessibleHyperlink.cpp index 9c3e8ae67aa..f47e569a40d 100644 --- a/accessible/src/msaa/CAccessibleHyperlink.cpp +++ b/accessible/src/msaa/ia2AccessibleHyperlink.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 thisObj = do_QueryObject(this); - if (!thisObj->IsLink()) + if (!static_cast(this)->IsLink()) return E_NOINTERFACE; *ppv = static_cast(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 thisObj = do_QueryObject(this); + Accessible* thisObj = static_cast(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 thisObj = do_QueryObject(this); + Accessible* thisObj = static_cast(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 thisObj = do_QueryObject(this); + Accessible* thisObj = static_cast(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 thisObj = do_QueryObject(this); + Accessible* thisObj = static_cast(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 thisObj = do_QueryObject(this); + Accessible* thisObj = static_cast(this); if (thisObj->IsDefunct()) return CO_E_OBJNOTCONNECTED; diff --git a/accessible/src/msaa/CAccessibleHyperlink.h b/accessible/src/msaa/ia2AccessibleHyperlink.h similarity index 91% rename from accessible/src/msaa/CAccessibleHyperlink.h rename to accessible/src/msaa/ia2AccessibleHyperlink.h index 2d76e678f8d..d9c6e585900 100644 --- a/accessible/src/msaa/CAccessibleHyperlink.h +++ b/accessible/src/msaa/ia2AccessibleHyperlink.h @@ -13,8 +13,8 @@ #include "ia2AccessibleAction.h" #include "AccessibleHyperlink.h" -class CAccessibleHyperlink: public ia2AccessibleAction, - public IAccessibleHyperlink +class ia2AccessibleHyperlink : public ia2AccessibleAction, + public IAccessibleHyperlink { public: diff --git a/b2g/chrome/content/forms.js b/b2g/chrome/content/forms.js index cd4ae362846..5e844922598 100644 --- a/b2g/chrome/content/forms.js +++ b/b2g/chrome/content/forms.js @@ -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; diff --git a/b2g/chrome/content/screen.js b/b2g/chrome/content/screen.js index 064140001fd..150d895a3b1 100644 --- a/b2g/chrome/content/screen.js +++ b/b2g/chrome/content/screen.js @@ -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 == '') diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 160953af6c1..87efb4981f7 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -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); diff --git a/dom/base/nsWindowMemoryReporter.cpp b/dom/base/nsWindowMemoryReporter.cpp index 8f80f4563e8..02e225434cc 100644 --- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -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 diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 6d0a03d738f..96ad1ba592f 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -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(""), 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 = """ diff --git a/dom/bindings/test/Makefile.in b/dom/bindings/test/Makefile.in index cb3c7ddb9f7..db9e997a867 100644 --- a/dom/bindings/test/Makefile.in +++ b/dom/bindings/test/Makefile.in @@ -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 \ diff --git a/dom/bindings/test/test_integers.html b/dom/bindings/test/test_integers.html new file mode 100644 index 00000000000..6799fd791a8 --- /dev/null +++ b/dom/bindings/test/test_integers.html @@ -0,0 +1,45 @@ + + + + + + + + +

+ +
+
+
+ + diff --git a/dom/system/gonk/nsIRadioInterfaceLayer.idl b/dom/system/gonk/nsIRadioInterfaceLayer.idl index 3c77645f9e9..3c11d41b95e 100644 --- a/dom/system/gonk/nsIRadioInterfaceLayer.idl +++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl @@ -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. */ diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index ac041706bbd..9867e893e42 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -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; } diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 6ddee726979..cde28f8a66e 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -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& 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& 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& aRanges, } } } - aRanges[aRanges.Length() - 1].end = aLength; + + aRanges[lastRangeIndex].end = aLength; } gfxUserFontSet* diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index e492fb5a2eb..b8c3e5d678f 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -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 compartmentStatsVector; CompartmentStats *currCompartmentStats; diff --git a/js/src/MemoryMetrics.cpp b/js/src/MemoryMetrics.cpp index d0024916a17..986c2a98e8e 100644 --- a/js/src/MemoryMetrics.cpp +++ b/js/src/MemoryMetrics.cpp @@ -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(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(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; } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 84617dacb56..43134e65f5a 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -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) { diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d004119c7a4..c2a4b8c03ce 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -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 diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index e22d2c4af03..8a84878cc7a 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -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 -inline const nsCString -MakePath(const nsACString &pathPrefix, const JS::CompartmentStats &cStats, - const char (&reporterName)[N]) -{ - const char *name = static_cast(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(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; } diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 22d271ed6ee..9b1d4c67ed1 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -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(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(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(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(doubleval); + else + *result = 0; } return true; } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 48f119d1c0d..523d05c614b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -5001,6 +5001,14 @@ nsIFrame::GetVisualOverflowRectRelativeToSelf() const return GetVisualOverflowRect(); } +nsRect +nsIFrame::GetPreEffectsVisualOverflowRect() const +{ + nsRect* r = static_cast + (Properties().Get(nsIFrame::PreEffectsBBoxProperty())); + return r ? *r : GetVisualOverflowRectRelativeToSelf(); +} + /* virtual */ bool nsFrame::UpdateOverflow() { diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 08629a7c5de..b79d01872aa 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -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 diff --git a/layout/svg/base/src/nsSVGFilterFrame.cpp b/layout/svg/base/src/nsSVGFilterFrame.cpp index a044b22a51a..3063553141b 100644 --- a/layout/svg/base/src/nsSVGFilterFrame.cpp +++ b/layout/svg/base/src/nsSVGFilterFrame.cpp @@ -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(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- 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 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- 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 diff --git a/layout/svg/base/src/nsSVGFilterFrame.h b/layout/svg/base/src/nsSVGFilterFrame.h index c5ae6dd2b0e..edc0543e526 100644 --- a/layout/svg/base/src/nsSVGFilterFrame.h +++ b/layout/svg/base/src/nsSVGFilterFrame.h @@ -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- 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- 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- 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, diff --git a/layout/svg/base/src/nsSVGFilterInstance.h b/layout/svg/base/src/nsSVGFilterInstance.h index e899c93c15d..262e8afb8a2 100644 --- a/layout/svg/base/src/nsSVGFilterInstance.h +++ b/layout/svg/base/src/nsSVGFilterInstance.h @@ -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; diff --git a/layout/svg/base/src/nsSVGIntegrationUtils.cpp b/layout/svg/base/src/nsSVGIntegrationUtils.cpp index d439ec11f40..cc1cfb29621 100644 --- a/layout/svg/base/src/nsSVGIntegrationUtils.cpp +++ b/layout/svg/base/src/nsSVGIntegrationUtils.cpp @@ -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()); diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 34b13ec6a0a..a608d82285b 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -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(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(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- 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); } diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index 184da264844..afc986b41b5 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -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- 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. diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 92397de3d5a..7ac8a9f3303 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -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"); } diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul index 6aae2952dbd..d7c9a253d3c 100644 --- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul @@ -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"); diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index 0cdd06f6f4c..1204e51b472 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -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;