From 848b90e5e5ce104fb38110460ff9636ccbaddea8 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 25 Jun 2012 23:35:42 -0400 Subject: [PATCH 01/19] Bug 768085. Speed up WebIDL tests and make them more usable by pickling the datastructures Yacc produces for the grammar and shutting up the logging it does in the debug mode we force it into. r=khuey --- dom/bindings/parser/WebIDL.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) 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 = """ From 898c8c85044a6ad8b880499977c5a8b8eaa6d788 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 25 Jun 2012 23:37:47 -0400 Subject: [PATCH 02/19] Bug 766796. Make IDL conversions to 64-bit ints treat NaN and Infinity as 0 instead of whatever the compiler decides to do in that undefined-behavior case. r=peterv --- dom/bindings/test/Makefile.in | 1 + dom/bindings/test/test_integers.html | 39 ++++++++++++++++++++++++++++ js/xpconnect/src/xpcpublic.h | 15 +++++++++-- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 dom/bindings/test/test_integers.html 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..1db0867ac2a --- /dev/null +++ b/dom/bindings/test/test_integers.html @@ -0,0 +1,39 @@ + + + + + + + + +

+ +
+
+
+ + diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 22d271ed6ee..3868963e16b 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; @@ -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; } From 40cc84d90d0a0758e64b9f5bda7c701687ab0e38 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 25 Jun 2012 13:00:32 -0400 Subject: [PATCH 03/19] Bug 767835 - Avoid using about:privatebrowsing as the new tab URL when permanent private browsing has been turned on; r=ttaubert --- browser/base/content/browser.js | 4 ++++ browser/base/content/utilityOverlay.js | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 6774d561f67..4794a13bda6 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -7131,6 +7131,10 @@ let gPrivateBrowsingUI = { !this.privateBrowsingEnabled; }, + get autoStarted() { + return this._privateBrowsingService.autoStarted; + }, + get privateBrowsingEnabled() { return this._privateBrowsingService.privateBrowsingEnabled; }, diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index ff0b25ae307..1889049abdc 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -12,7 +12,9 @@ XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () { const TOPIC = "private-browsing-transition-complete"; function getNewTabPageURL() { - if (("gPrivateBrowsingUI" in window) && gPrivateBrowsingUI.privateWindow) + if (("gPrivateBrowsingUI" in window) && + gPrivateBrowsingUI.privateWindow && + !gPrivateBrowsingUI.autoStarted) return "about:privatebrowsing"; else return Services.prefs.getCharPref(PREF) || "about:blank"; From c73cdc8422e550890da8e0f74edf8785e3de3814 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 26 Jun 2012 01:09:31 -0400 Subject: [PATCH 04/19] Bug 766796 followup to deal with lack of WebGL on Mac OS 10.5. No review. --- dom/bindings/test/test_integers.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dom/bindings/test/test_integers.html b/dom/bindings/test/test_integers.html index 1db0867ac2a..6799fd791a8 100644 --- a/dom/bindings/test/test_integers.html +++ b/dom/bindings/test/test_integers.html @@ -16,7 +16,13 @@ function testInt64NonFinite(arg) { // We can use a WebGLRenderingContext to test conversion to 64-bit signed // ints edge cases. - var gl = $("c").getContext("experimental-webgl"); + try { + var gl = $("c").getContext("experimental-webgl"); + } catch (ex) { + // No WebGL support on MacOS 10.5. Just skip this test + todo(false, "WebGL not supported"); + return; + } is(gl.getError(), 0, "Should not start in an error state"); var b = gl.createBuffer(); From 924f3d07a8270d4a83c8e475e8c025ea3027f660 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Tue, 26 Jun 2012 08:42:45 +0200 Subject: [PATCH 05/19] Bug 766549 - Keyboard appear when pressing buttons. r=fabrice --- b2g/chrome/content/forms.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) 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; From 3826fbe79e297a93a4812571ed72f79147bf8d72 Mon Sep 17 00:00:00 2001 From: Etienne Segonzac Date: Tue, 26 Jun 2012 08:42:45 +0200 Subject: [PATCH 06/19] Bug 768238 - Fixing the mozContentEvent listening in B2G. r=vingtetun --- b2g/chrome/content/shell.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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); From 7fc48d2bd76f301d2644905e64de03911d4fe0a7 Mon Sep 17 00:00:00 2001 From: David Flanagan Date: Mon, 25 Jun 2012 11:14:15 -0700 Subject: [PATCH 07/19] Bug 768057 - b2g desktop client default size not exposed by CSS media queries. r=vingtetun --- b2g/chrome/content/screen.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 == '') From 0592983eab6dfba742dc68df874fdec2d7354572 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Tue, 26 Jun 2012 14:43:22 +0800 Subject: [PATCH 08/19] Bug 766891 - Remove nsIRadioInterfaceLayer.DATACALL_STATE_* r=philikon --HG-- extra : rebase_source : ffecde9ccdeed162f6fa7faf48215a251e996e0f --- dom/system/gonk/nsIRadioInterfaceLayer.idl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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. */ From 5e680e1876f8b1061a349a9007376444b8b6859c Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Tue, 26 Jun 2012 14:49:52 +0800 Subject: [PATCH 09/19] Bug 766866 - Save data call information in data call object. r=philikon --HG-- extra : rebase_source : b4500466d39f94b8c5168afbf81a07246d4e1d07 --- dom/system/gonk/ril_worker.js | 60 ++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 29 deletions(-) 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; } From d3fdce16527043d6d70071b780b25a6eb267d5ff Mon Sep 17 00:00:00 2001 From: Mark Capella Date: Tue, 26 Jun 2012 04:18:48 -0400 Subject: [PATCH 10/19] Bug 767264 - remove uses of do_QueryObject() in CAccessibleHyperlink, r=tbsaunde --HG-- rename : accessible/src/msaa/CAccessibleHyperlink.cpp => accessible/src/msaa/ia2AccessibleHyperlink.cpp rename : accessible/src/msaa/CAccessibleHyperlink.h => accessible/src/msaa/ia2AccessibleHyperlink.h --- accessible/src/msaa/AccessibleWrap.cpp | 2 +- accessible/src/msaa/AccessibleWrap.h | 4 +-- accessible/src/msaa/Makefile.in | 2 +- ...perlink.cpp => ia2AccessibleHyperlink.cpp} | 27 +++++++++---------- ...leHyperlink.h => ia2AccessibleHyperlink.h} | 4 +-- 5 files changed, 18 insertions(+), 21 deletions(-) rename accessible/src/msaa/{CAccessibleHyperlink.cpp => ia2AccessibleHyperlink.cpp} (82%) rename accessible/src/msaa/{CAccessibleHyperlink.h => ia2AccessibleHyperlink.h} (91%) 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: From f4ce2e84310f584123f37b748f4f1fbe51780196 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Tue, 26 Jun 2012 09:23:16 +0100 Subject: [PATCH 11/19] Backout 4e195dd3a989 (bug 767835) for mochitest-browser-chrome failures --- browser/base/content/browser.js | 4 ---- browser/base/content/utilityOverlay.js | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 4794a13bda6..6774d561f67 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -7131,10 +7131,6 @@ let gPrivateBrowsingUI = { !this.privateBrowsingEnabled; }, - get autoStarted() { - return this._privateBrowsingService.autoStarted; - }, - get privateBrowsingEnabled() { return this._privateBrowsingService.privateBrowsingEnabled; }, diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index 1889049abdc..ff0b25ae307 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -12,9 +12,7 @@ XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () { const TOPIC = "private-browsing-transition-complete"; function getNewTabPageURL() { - if (("gPrivateBrowsingUI" in window) && - gPrivateBrowsingUI.privateWindow && - !gPrivateBrowsingUI.autoStarted) + if (("gPrivateBrowsingUI" in window) && gPrivateBrowsingUI.privateWindow) return "about:privatebrowsing"; else return Services.prefs.getCharPref(PREF) || "about:blank"; From d9eaa3b40b30a7544b563cd670931df09d14f9a7 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 26 Jun 2012 09:51:37 +0100 Subject: [PATCH 12/19] bug 763873 - avoid repeatedly accessing aRanges.Length() within ComputeRanges. r=jdaggett --- gfx/thebes/gfxFont.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) 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* From fcf54de663f8f24924e596c74165bb0508ffdbda Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Jun 2012 17:06:50 -0700 Subject: [PATCH 13/19] Bug 711130 (part 1) - Fix "window-objects" measurement inconsistency. r=nfroyd. --HG-- extra : rebase_source : d58788d8d47eeac7d4ecac85eb016c5fd6c4b739 --- dom/base/nsWindowMemoryReporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 9b216c7305cdd943172fb6156a2ba7b0c1ee837e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Jun 2012 17:06:50 -0700 Subject: [PATCH 14/19] Bug 711130 (part 2) - Merge "arena/unused" and "arena/padding" into "arena-admin". r=terrence. --HG-- extra : rebase_source : 1c9d318e86eb27c2e40f2ef95554afefb8a6c653 --- js/public/MemoryMetrics.h | 3 +-- js/src/MemoryMetrics.cpp | 11 ++++++----- js/xpconnect/src/XPCJSRuntime.cpp | 16 +++++----------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index e492fb5a2eb..7f1f3397056 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -79,8 +79,7 @@ struct CompartmentStats } void *extra; - size_t gcHeapArenaHeaders; - size_t gcHeapArenaPadding; + size_t gcHeapArenaAdmin; size_t gcHeapArenaUnused; size_t gcHeapObjectsNonFunction; diff --git a/js/src/MemoryMetrics.cpp b/js/src/MemoryMetrics.cpp index d0024916a17..cf5ccf2ea39 100644 --- a/js/src/MemoryMetrics.cpp +++ b/js/src/MemoryMetrics.cpp @@ -58,10 +58,12 @@ 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 @@ -183,8 +185,7 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) index++) { CompartmentStats &cStats = rtStats->compartmentStatsVector[index]; - size_t used = cStats.gcHeapArenaHeaders + - cStats.gcHeapArenaPadding + + size_t used = cStats.gcHeapArenaAdmin + cStats.gcHeapArenaUnused + cStats.gcHeapObjectsNonFunction + cStats.gcHeapObjectsFunction + diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index e22d2c4af03..7b7b3581611 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1297,18 +1297,12 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats, { size_t gcTotal = 0, gcHeapSundries = 0, otherSundries = 0; - CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/arena/headers"), - cStats.gcHeapArenaHeaders, + CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/arena-admin"), + cStats.gcHeapArenaAdmin, "Memory on the compartment's garbage-collected JavaScript " - "heap, within arenas, that is used to hold internal " - "bookkeeping information."); - - 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."); + "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/unused"), cStats.gcHeapArenaUnused, From b7d8c8862eab9d34e5acb02c96dd2fcea0a4d9b0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Jun 2012 17:06:50 -0700 Subject: [PATCH 15/19] Bug 711130 (part 3) - Use consistent names for reports of unused JS memory. r=terrence. --HG-- extra : rebase_source : a8e5a7d77355539f84a7fbb4d312fa66d2fe5ba6 --- js/public/MemoryMetrics.h | 14 ++++---- js/src/MemoryMetrics.cpp | 33 +++++++++--------- js/xpconnect/src/XPCJSRuntime.cpp | 57 ++++++++++++++----------------- 3 files changed, 49 insertions(+), 55 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 7f1f3397056..f2f0580c67b 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -80,7 +80,7 @@ struct CompartmentStats void *extra; size_t gcHeapArenaAdmin; - size_t gcHeapArenaUnused; + size_t gcHeapUnusedGcThings; size_t gcHeapObjectsNonFunction; size_t gcHeapObjectsFunction; @@ -116,11 +116,11 @@ struct RuntimeStats , gcHeapChunkTotal(0) , gcHeapCommitted(0) , gcHeapUnused(0) - , gcHeapChunkCleanUnused(0) - , gcHeapChunkDirtyUnused(0) + , gcHeapUnusedChunks(0) + , gcHeapUnusedArenas(0) , gcHeapChunkCleanDecommitted(0) , gcHeapChunkDirtyDecommitted(0) - , gcHeapArenaUnused(0) + , gcHeapUnusedGcThings(0) , gcHeapChunkAdmin(0) , totalObjects(0) , totalShapes(0) @@ -139,11 +139,11 @@ struct RuntimeStats size_t gcHeapChunkTotal; size_t gcHeapCommitted; size_t gcHeapUnused; - size_t gcHeapChunkCleanUnused; - size_t gcHeapChunkDirtyUnused; + size_t gcHeapUnusedChunks; + size_t gcHeapUnusedArenas; size_t gcHeapChunkCleanDecommitted; size_t gcHeapChunkDirtyDecommitted; - size_t gcHeapArenaUnused; + size_t gcHeapUnusedGcThings; size_t gcHeapChunkAdmin; size_t totalObjects; size_t totalShapes; diff --git a/js/src/MemoryMetrics.cpp b/js/src/MemoryMetrics.cpp index cf5ccf2ea39..30bf4748196 100644 --- a/js/src/MemoryMetrics.cpp +++ b/js/src/MemoryMetrics.cpp @@ -68,7 +68,7 @@ StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena, // 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 @@ -148,7 +148,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) @@ -159,7 +159,7 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) rtStats->gcHeapChunkCleanDecommitted = rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize; - rtStats->gcHeapChunkCleanUnused = + rtStats->gcHeapUnusedChunks = size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize - rtStats->gcHeapChunkCleanDecommitted; rtStats->gcHeapChunkTotal = @@ -173,10 +173,10 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) // 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->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal - + rtStats->gcHeapUnusedChunks - + rtStats->gcHeapChunkCleanDecommitted - + rtStats->gcHeapChunkDirtyDecommitted; rtStats->totalMjit = rtStats->runtime.mjitCode; @@ -186,7 +186,7 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) CompartmentStats &cStats = rtStats->compartmentStatsVector[index]; size_t used = cStats.gcHeapArenaAdmin + - cStats.gcHeapArenaUnused + + cStats.gcHeapUnusedGcThings + cStats.gcHeapObjectsNonFunction + cStats.gcHeapObjectsFunction + cStats.gcHeapStrings + @@ -199,8 +199,8 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) #endif cStats.gcHeapTypeObjects; - rtStats->gcHeapChunkDirtyUnused -= used; - rtStats->gcHeapArenaUnused += cStats.gcHeapArenaUnused; + rtStats->gcHeapUnusedArenas -= used; + rtStats->gcHeapUnusedGcThings += cStats.gcHeapUnusedGcThings; rtStats->totalObjects += cStats.gcHeapObjectsNonFunction + cStats.gcHeapObjectsFunction + cStats.objectSlots + @@ -224,17 +224,16 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) rtStats->totalAnalysisTemp += cStats.typeInferenceSizes.temporary; } - 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->gcHeapUnusedArenas -= rtStats->gcHeapChunkAdmin; - rtStats->gcHeapUnused = rtStats->gcHeapChunkDirtyUnused + - rtStats->gcHeapChunkCleanUnused + - rtStats->gcHeapArenaUnused; + rtStats->gcHeapUnused = rtStats->gcHeapUnusedArenas + + rtStats->gcHeapUnusedChunks + + rtStats->gcHeapUnusedGcThings; rtStats->gcHeapCommitted = rtStats->gcHeapChunkTotal - rtStats->gcHeapChunkCleanDecommitted - diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 7b7b3581611..8128b2cdb69 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1304,11 +1304,11 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats, "bookkeeping information, and (b) to provide padding to " "align GC things."); - CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/arena/unused"), - cStats.gcHeapArenaUnused, + CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/unused-gc-things"), + cStats.gcHeapUnusedGcThings, "Memory on the compartment's garbage-collected JavaScript " - "heap, within arenas, that could be holding useful data " - "but currently isn't."); + "heap taken by empty GC thing slots within non-empty " + "arenas."); CREPORT_GC_BYTES(MakePath(pathPrefix, cStats, "gc-heap/objects/non-function"), cStats.gcHeapObjectsNonFunction, @@ -1550,21 +1550,16 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, "small and all the same size, so they're not worth reporting " "on a per-compartment basis."); - 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'."); - - REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-chunk-clean-unused"), - rtStats.gcHeapChunkCleanUnused, + 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-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-decommitted"), rtStats.gcHeapChunkCleanDecommitted + rtStats.gcHeapChunkDirtyDecommitted, @@ -1711,35 +1706,35 @@ public: nsIMemoryReporter::KIND_HEAP, xpconnect, "Memory used by XPConnect."); - REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-chunk-dirty-unused"), + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-unused-arenas"), nsIMemoryReporter::KIND_OTHER, - rtStats.gcHeapChunkDirtyUnused, - "The same as 'explicit/js/gc-heap-chunk-dirty-unused'. " + rtStats.gcHeapUnusedArenas, + "The same as 'explicit/js/gc-heap-unused-arenas'. " "Shown here for easy comparison with other 'js-gc' " "reporters."); - REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-chunk-clean-unused"), + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-unused-chunks"), nsIMemoryReporter::KIND_OTHER, - rtStats.gcHeapChunkCleanUnused, - "The same as 'explicit/js/gc-heap-chunk-clean-unused'. " + rtStats.gcHeapUnusedChunks, + "The same as 'explicit/js/gc-heap-unused-chunks'. " "Shown here for easy comparison with other 'js-gc' " "reporters."); - REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-arena-unused"), + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-unused-gc-things"), nsIMemoryReporter::KIND_OTHER, - rtStats.gcHeapArenaUnused, + rtStats.gcHeapUnusedGcThings, "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."); + "JavaScript heap taken by empty GC thing slots within " + "non-empty arenas. This is the sum of all compartments' " + "'gc-heap/unused-gc-things' 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'."); + "bookkeeping. Equal to 'gc-heap-unused-chunks' + " + "'gc-heap-unused-arenas' + 'gc-heap-unused-gc-things'."); // Why 10000x? 100x because it's a percentage, and another 100x // because nsIMemoryReporter requires that for UNITS_PERCENTAGE From d7505cab84fa54230d3afa6d54f7d30811d220d9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Jun 2012 17:06:50 -0700 Subject: [PATCH 16/19] Bug 711130 (part 4) - Treeify the non-compartment gc-heap-XYZ measurements. r=terrence. --HG-- extra : rebase_source : ba3ff507e05829ad574e7283c7f3ab48b2416ac1 --- js/xpconnect/src/XPCJSRuntime.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 8128b2cdb69..d2f81023365 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; @@ -1550,23 +1550,23 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, "small and all the same size, so they're not worth reporting " "on a per-compartment basis."); - REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-unused-arenas"), + REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-arenas"), rtStats.gcHeapUnusedArenas, "Memory on the garbage-collected JavaScript heap taken by " "empty arenas within non-empty chunks."); - REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-unused-chunks"), + 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-decommitted"), + 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-chunk-admin"), + 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 " @@ -1692,31 +1692,31 @@ 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. nsresult rv = - xpc::ReportJSRuntimeExplicitTreeStats(rtStats, pathPrefix, cb, + xpc::ReportJSRuntimeExplicitTreeStats(rtStats, explicitJs, cb, closure); NS_ENSURE_SUCCESS(rv, rv); - REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("xpconnect"), + REPORT_BYTES(explicitJs + NS_LITERAL_CSTRING("xpconnect"), nsIMemoryReporter::KIND_HEAP, xpconnect, "Memory used by XPConnect."); 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'. " + "The same as 'explicit/js/gc-heap/unused-arenas'. " "Shown here for easy comparison with other 'js-gc' " "reporters."); 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'. " + "The same as 'explicit/js/gc-heap/unused-chunks'. " "Shown here for easy comparison with other 'js-gc' " "reporters."); @@ -1765,7 +1765,7 @@ public: 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 " + "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"), From a42aea7627565747a9e700be566f80caf4639a8b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Jun 2012 17:08:59 -0700 Subject: [PATCH 17/19] Bug 711130 (part 5) - Overhaul the "other measurements" measurements for JS memory consumption. r=terrence. --HG-- extra : rebase_source : 492af75618c40eb2a92be2756f67db93834729cf --- js/public/MemoryMetrics.h | 98 +++- js/src/MemoryMetrics.cpp | 121 ++--- js/xpconnect/src/XPCJSRuntime.cpp | 485 ++++++++---------- js/xpconnect/src/xpcpublic.h | 2 +- .../aboutmemory/content/aboutMemory.js | 1 - .../tests/test_memoryReporters.xul | 4 +- xpcom/base/nsIMemoryReporter.idl | 7 +- 7 files changed, 368 insertions(+), 350 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index f2f0580c67b..b814993aad5 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,6 +86,9 @@ struct CompartmentStats } void *extra; + + // If you add a new number, remember to update add() and maybe + // gcHeapThingsSize()! size_t gcHeapArenaAdmin; size_t gcHeapUnusedGcThings; @@ -107,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 @@ -114,21 +163,14 @@ struct RuntimeStats RuntimeStats(JSMallocSizeOfFun mallocSizeOf) : runtime() , gcHeapChunkTotal(0) - , gcHeapCommitted(0) - , gcHeapUnused(0) - , gcHeapUnusedChunks(0) - , gcHeapUnusedArenas(0) , gcHeapChunkCleanDecommitted(0) , gcHeapChunkDirtyDecommitted(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) @@ -136,23 +178,35 @@ struct RuntimeStats RuntimeSizes runtime; + // 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.gcHeapChunkCleanDecommitted (decommitted arenas in empty chunks) + // - rtStats.gcHeapChunkDirtyDecommitted (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) + size_t gcHeapChunkTotal; - size_t gcHeapCommitted; - size_t gcHeapUnused; - size_t gcHeapUnusedChunks; - size_t gcHeapUnusedArenas; size_t gcHeapChunkCleanDecommitted; size_t gcHeapChunkDirtyDecommitted; + size_t gcHeapUnusedChunks; + size_t gcHeapUnusedArenas; size_t gcHeapUnusedGcThings; size_t gcHeapChunkAdmin; - size_t totalObjects; - size_t totalShapes; - size_t totalScripts; - size_t totalStrings; - size_t totalMjit; - size_t totalTypeInference; - size_t totalAnalysisTemp; + 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 30bf4748196..fce5822f74f 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) { @@ -157,71 +185,32 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) if (!rtStats->compartmentStatsVector.reserve(rt->compartments.length())) return false; - rtStats->gcHeapChunkCleanDecommitted = - rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize; - rtStats->gcHeapUnusedChunks = - 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->gcHeapChunkCleanDecommitted = + rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize; + + rtStats->gcHeapUnusedChunks = + size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize - + rtStats->gcHeapChunkCleanDecommitted; + + // This just computes rtStats->gcHeapChunkDirtyDecommitted. 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->gcHeapUnusedArenas = rtStats->gcHeapChunkTotal - - rtStats->gcHeapUnusedChunks - - 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.gcHeapArenaAdmin + - cStats.gcHeapUnusedGcThings + - 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->gcHeapUnusedArenas -= used; - rtStats->gcHeapUnusedGcThings += cStats.gcHeapUnusedGcThings; - 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 = @@ -231,14 +220,16 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin; rtStats->gcHeapUnusedArenas -= rtStats->gcHeapChunkAdmin; - rtStats->gcHeapUnused = rtStats->gcHeapUnusedArenas + - rtStats->gcHeapUnusedChunks + - rtStats->gcHeapUnusedGcThings; - - rtStats->gcHeapCommitted = rtStats->gcHeapChunkTotal - - rtStats->gcHeapChunkCleanDecommitted - - rtStats->gcHeapChunkDirtyDecommitted; - + // |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->gcHeapChunkCleanDecommitted - + rtStats->gcHeapChunkDirtyDecommitted - + rtStats->gcHeapUnusedChunks - + rtStats->totals.gcHeapUnusedGcThings - + rtStats->gcHeapChunkAdmin - + rtStats->totals.gcHeapArenaAdmin - + rtStats->gcHeapGcThings; return true; } diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index d2f81023365..38247d8f63b 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -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,182 +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-admin"), + CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/arena-admin"), cStats.gcHeapArenaAdmin, - "Memory on the compartment's garbage-collected JavaScript " + "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/unused-gc-things"), + CREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/unused-gc-things"), cStats.gcHeapUnusedGcThings, - "Memory on the compartment's garbage-collected JavaScript " + "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/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; } @@ -1477,78 +1474,94 @@ 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."); + + RREPORT_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/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, @@ -1697,125 +1710,83 @@ public: // 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, explicitJs, cb, - closure); + closure, &rtTotal); NS_ENSURE_SUCCESS(rv, rv); - REPORT_BYTES(explicitJs + NS_LITERAL_CSTRING("xpconnect"), - nsIMemoryReporter::KIND_HEAP, xpconnect, - "Memory used by 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_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'. " - "Shown here for easy comparison with other 'js-gc' " - "reporters."); + // 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_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'. " - "Shown here for easy comparison with other 'js-gc' " - "reporters."); + // Report the numbers for memory outside of compartments. - REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-unused-gc-things"), - nsIMemoryReporter::KIND_OTHER, - rtStats.gcHeapUnusedGcThings, - "Memory on the main JSRuntime's garbage-collected " - "JavaScript heap taken by empty GC thing slots within " - "non-empty arenas. This is the sum of all compartments' " - "'gc-heap/unused-gc-things' 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-unused-chunks' + " - "'gc-heap-unused-arenas' + 'gc-heap-unused-gc-things'."); - - // 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"), + 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."); + "The same as 'explicit/js/gc-heap/decommitted'."); - 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/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-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/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-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/gc-heap/chunk-admin"), + nsIMemoryReporter::KIND_OTHER, + rtStats.gcHeapChunkAdmin, + "The same as 'explicit/js/gc-heap/chunk-admin'."); - 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 a breakdown of the committed GC space. - 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-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-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-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-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."); + 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."); return NS_OK; } diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 3868963e16b..9b1d4c67ed1 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -268,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. 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; From 6ea0db4f8bf18cd479089ac22fd0e5e8fa36aef2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Jun 2012 17:09:00 -0700 Subject: [PATCH 18/19] Bug 711130 (part 6) - Improve the measurement of decommitted GC memory. r=terrence. --HG-- extra : rebase_source : ad344b58798cb6837a59f3d48f57880b036c300e --- js/public/MemoryMetrics.h | 14 ++++++++------ js/src/MemoryMetrics.cpp | 15 ++++----------- js/src/jsgc.cpp | 16 ---------------- js/src/jsgc.h | 3 --- js/xpconnect/src/XPCJSRuntime.cpp | 16 +++++++++------- 5 files changed, 21 insertions(+), 43 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index b814993aad5..b8c3e5d678f 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -163,8 +163,7 @@ struct RuntimeStats RuntimeStats(JSMallocSizeOfFun mallocSizeOf) : runtime() , gcHeapChunkTotal(0) - , gcHeapChunkCleanDecommitted(0) - , gcHeapChunkDirtyDecommitted(0) + , gcHeapDecommittedArenas(0) , gcHeapUnusedChunks(0) , gcHeapUnusedArenas(0) , gcHeapUnusedGcThings(0) @@ -184,8 +183,7 @@ struct RuntimeStats // // - rtStats.gcHeapChunkTotal // - decommitted bytes - // - rtStats.gcHeapChunkCleanDecommitted (decommitted arenas in empty chunks) - // - rtStats.gcHeapChunkDirtyDecommitted (decommitted arenas in non-empty chunks) + // - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks) // - unused bytes // - rtStats.gcHeapUnusedChunks (empty chunks) // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks) @@ -194,10 +192,14 @@ struct RuntimeStats // - 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 gcHeapChunkCleanDecommitted; - size_t gcHeapChunkDirtyDecommitted; + size_t gcHeapDecommittedArenas; size_t gcHeapUnusedChunks; size_t gcHeapUnusedArenas; size_t gcHeapUnusedGcThings; diff --git a/js/src/MemoryMetrics.cpp b/js/src/MemoryMetrics.cpp index fce5822f74f..986c2a98e8e 100644 --- a/js/src/MemoryMetrics.cpp +++ b/js/src/MemoryMetrics.cpp @@ -72,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 @@ -188,14 +186,10 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) rtStats->gcHeapChunkTotal = size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize; - rtStats->gcHeapChunkCleanDecommitted = - rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize; - rtStats->gcHeapUnusedChunks = - size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize - - rtStats->gcHeapChunkCleanDecommitted; + size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize; - // This just computes rtStats->gcHeapChunkDirtyDecommitted. + // This just computes rtStats->gcHeapDecommittedArenas. IterateChunks(rt, rtStats, StatsChunkCallback); // Take the per-compartment measurements. @@ -223,8 +217,7 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats) // |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->gcHeapChunkCleanDecommitted - - rtStats->gcHeapChunkDirtyDecommitted - + rtStats->gcHeapDecommittedArenas - rtStats->gcHeapUnusedChunks - rtStats->totals.gcHeapUnusedGcThings - rtStats->gcHeapChunkAdmin - 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 38247d8f63b..8a84878cc7a 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1574,10 +1574,12 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, "empty chunks, which will soon be released unless claimed " "for new allocations."); - 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/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, @@ -1729,10 +1731,10 @@ public: // Report the numbers for memory outside of compartments. - REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/decommitted"), + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/decommitted-arenas"), nsIMemoryReporter::KIND_OTHER, - rtStats.gcHeapChunkCleanDecommitted + rtStats.gcHeapChunkDirtyDecommitted, - "The same as 'explicit/js/gc-heap/decommitted'."); + 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, From 9967128cd9e593d83c274fbde7b48fece922a37c Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Tue, 26 Jun 2012 11:49:23 +0100 Subject: [PATCH 19/19] Bug 767823 - Make nsAutoFilterInstance take frame rects and stop using GetCoveredRegion. r=longsonr. --- layout/generic/nsFrame.cpp | 8 + layout/generic/nsIFrame.h | 7 + layout/svg/base/src/nsSVGFilterFrame.cpp | 277 +++++++++--------- layout/svg/base/src/nsSVGFilterFrame.h | 45 +-- layout/svg/base/src/nsSVGFilterInstance.h | 18 ++ layout/svg/base/src/nsSVGIntegrationUtils.cpp | 34 +-- layout/svg/base/src/nsSVGUtils.cpp | 51 +++- layout/svg/base/src/nsSVGUtils.h | 11 + 8 files changed, 259 insertions(+), 192 deletions(-) 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 f8687e6d12e..309edd9ebb7 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 @@ -658,7 +654,6 @@ nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate, aFrame = aFrame->GetParent(); } - PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); PRInt32 appUnitsPerCSSPx = aFrame->PresContext()->AppUnitsPerCSSPixel(); while (aFrame) { @@ -689,9 +684,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 = @@ -1041,6 +1034,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) { @@ -1218,9 +1226,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.