mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
Merge last green changeset from inbound to mozilla-central
This commit is contained in:
commit
e611812eb2
@ -605,7 +605,7 @@ nsAccessibilityService::TreeViewChanged(nsIPresShell* aPresShell,
|
||||
if (document) {
|
||||
nsAccessible* accessible = document->GetAccessible(aContent);
|
||||
if (accessible) {
|
||||
nsRefPtr<nsXULTreeAccessible> treeAcc = do_QueryObject(accessible);
|
||||
nsXULTreeAccessible* treeAcc = accessible->AsXULTree();
|
||||
if (treeAcc)
|
||||
treeAcc->TreeViewChanged(aView);
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ class TableAccessible;
|
||||
}
|
||||
}
|
||||
class nsTextAccessible;
|
||||
class nsXULTreeAccessible;
|
||||
|
||||
struct nsRect;
|
||||
class nsIContent;
|
||||
@ -467,6 +468,9 @@ public:
|
||||
bool IsImageMapAccessible() const { return mFlags & eImageMapAccessible; }
|
||||
nsHTMLImageMapAccessible* AsImageMap();
|
||||
|
||||
inline bool IsXULTree() const { return mFlags & eXULTreeAccessible; }
|
||||
nsXULTreeAccessible* AsXULTree();
|
||||
|
||||
inline bool IsListControl() const { return mFlags & eListControlAccessible; }
|
||||
|
||||
inline bool IsMenuButton() const { return mFlags & eMenuButtonAccessible; }
|
||||
@ -718,7 +722,8 @@ protected:
|
||||
eMenuButtonAccessible = 1 << 14,
|
||||
eMenuPopupAccessible = 1 << 15,
|
||||
eRootAccessible = 1 << 16,
|
||||
eTextLeafAccessible = 1 << 17
|
||||
eTextLeafAccessible = 1 << 17,
|
||||
eXULTreeAccessible = 1 << 18
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -386,21 +386,16 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
nsINode* targetNode = accessible->GetNode();
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
nsRefPtr<nsXULTreeAccessible> treeAcc;
|
||||
if (targetNode->IsElement() &&
|
||||
targetNode->AsElement()->NodeInfo()->Equals(nsGkAtoms::tree,
|
||||
kNameSpaceID_XUL)) {
|
||||
treeAcc = do_QueryObject(accessible);
|
||||
if (treeAcc) {
|
||||
if (eventType.EqualsLiteral("TreeRowCountChanged")) {
|
||||
HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
|
||||
return;
|
||||
}
|
||||
nsXULTreeAccessible* treeAcc = accessible->AsXULTree();
|
||||
if (treeAcc) {
|
||||
if (eventType.EqualsLiteral("TreeRowCountChanged")) {
|
||||
HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType.EqualsLiteral("TreeInvalidated")) {
|
||||
HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
|
||||
return;
|
||||
}
|
||||
if (eventType.EqualsLiteral("TreeInvalidated")) {
|
||||
HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -403,17 +403,15 @@ GetNativeFromGeckoAccessible(nsIAccessible *anAccessible)
|
||||
return mChildren;
|
||||
|
||||
mChildren = [[NSMutableArray alloc] init];
|
||||
|
||||
|
||||
// get the array of children.
|
||||
nsTArray<nsRefPtr<nsAccessibleWrap> > childrenArray;
|
||||
mGeckoAccessible->GetUnignoredChildren(childrenArray);
|
||||
|
||||
nsAutoTArray<nsAccessible*, 10> childrenArray;
|
||||
mGeckoAccessible->GetUnignoredChildren(&childrenArray);
|
||||
|
||||
// now iterate through the children array, and get each native accessible.
|
||||
int totalCount = childrenArray.Length();
|
||||
int index = 0;
|
||||
|
||||
for (; index < totalCount; index++) {
|
||||
nsAccessibleWrap *curAccessible = childrenArray.ElementAt(index);
|
||||
PRUint32 totalCount = childrenArray.Length();
|
||||
for (PRUint32 idx = 0; idx < totalCount; idx++) {
|
||||
nsAccessible* curAccessible = childrenArray.ElementAt(idx);
|
||||
if (curAccessible) {
|
||||
mozAccessible *curNative = GetNativeFromGeckoAccessible(curAccessible);
|
||||
if (curNative)
|
||||
|
@ -99,7 +99,7 @@ public: // construction, destruction
|
||||
* Returns this accessible's all children, adhering to "flat" accessibles by
|
||||
* not returning their children.
|
||||
*/
|
||||
void GetUnignoredChildren(nsTArray<nsRefPtr<nsAccessibleWrap> >& aChildrenArray);
|
||||
void GetUnignoredChildren(nsTArray<nsAccessible*>* aChildrenArray);
|
||||
nsAccessible* GetUnignoredParent() const;
|
||||
|
||||
protected:
|
||||
|
@ -261,7 +261,7 @@ nsAccessibleWrap::IsIgnored()
|
||||
}
|
||||
|
||||
void
|
||||
nsAccessibleWrap::GetUnignoredChildren(nsTArray<nsRefPtr<nsAccessibleWrap> > &aChildrenArray)
|
||||
nsAccessibleWrap::GetUnignoredChildren(nsTArray<nsAccessible*>* aChildrenArray)
|
||||
{
|
||||
// we're flat; there are no children.
|
||||
if (nsAccUtils::MustPrune(this))
|
||||
@ -272,19 +272,13 @@ nsAccessibleWrap::GetUnignoredChildren(nsTArray<nsRefPtr<nsAccessibleWrap> > &aC
|
||||
nsAccessibleWrap *childAcc =
|
||||
static_cast<nsAccessibleWrap*>(GetChildAt(childIdx));
|
||||
|
||||
// If element is ignored, then add its children as substitutes.
|
||||
if (childAcc->IsIgnored()) {
|
||||
// element is ignored, so try adding its children as substitutes, if it has any.
|
||||
if (!nsAccUtils::MustPrune(childAcc)) {
|
||||
nsTArray<nsRefPtr<nsAccessibleWrap> > children;
|
||||
childAcc->GetUnignoredChildren(children);
|
||||
if (!children.IsEmpty()) {
|
||||
// add the found unignored descendants to the array.
|
||||
aChildrenArray.AppendElements(children);
|
||||
}
|
||||
}
|
||||
} else
|
||||
// simply add the element, since it's not ignored.
|
||||
aChildrenArray.AppendElement(childAcc);
|
||||
childAcc->GetUnignoredChildren(aChildrenArray);
|
||||
continue;
|
||||
}
|
||||
|
||||
aChildrenArray->AppendElement(childAcc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,8 @@ nsXULTreeAccessible::
|
||||
nsXULTreeAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
|
||||
nsAccessibleWrap(aContent, aDoc)
|
||||
{
|
||||
mFlags |= eXULTreeAccessible;
|
||||
|
||||
mTree = nsCoreUtils::GetTreeBoxObject(aContent);
|
||||
if (mTree)
|
||||
mTree->GetView(getter_AddRefs(mTreeView));
|
||||
@ -863,7 +865,7 @@ nsXULTreeItemAccessibleBase::RelationByType(PRUint32 aType)
|
||||
if (parentIndex == -1)
|
||||
return Relation(mParent);
|
||||
|
||||
nsRefPtr<nsXULTreeAccessible> treeAcc = do_QueryObject(mParent);
|
||||
nsXULTreeAccessible* treeAcc = mParent->AsXULTree();
|
||||
return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
|
||||
}
|
||||
|
||||
@ -1283,7 +1285,7 @@ nsXULTreeColumnsAccessible::GetSiblingAtOffset(PRInt32 aOffset,
|
||||
PRInt32 rowCount = 0;
|
||||
treeView->GetRowCount(&rowCount);
|
||||
if (rowCount > 0 && aOffset <= rowCount) {
|
||||
nsRefPtr<nsXULTreeAccessible> treeAcc = do_QueryObject(Parent());
|
||||
nsXULTreeAccessible* treeAcc = Parent()->AsXULTree();
|
||||
|
||||
if (treeAcc)
|
||||
return treeAcc->GetTreeItemAccessible(aOffset - 1);
|
||||
|
@ -320,4 +320,14 @@ protected:
|
||||
nsresult *aError = nsnull) const;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccessible downcasting method
|
||||
|
||||
inline nsXULTreeAccessible*
|
||||
nsAccessible::AsXULTree()
|
||||
{
|
||||
return IsXULTree() ?
|
||||
static_cast<nsXULTreeAccessible*>(this) : nsnull;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -188,10 +188,6 @@ SelectionCopyHelper(nsISelection *aSel, nsIDocument *aDoc,
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Emulate the collateral damage from bug 564737. Remove the following
|
||||
// line to fix bug 739537.
|
||||
plaintextBuffer.Trim(" ", true, false);
|
||||
|
||||
// Now create the version that shows HTML context
|
||||
|
||||
mimeType.AssignLiteral(kHTMLMime);
|
||||
|
@ -811,7 +811,8 @@ nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL)
|
||||
JSAutoRequest ar(mCx);
|
||||
JSObject* global = nsnull;
|
||||
mGlobal->GetJSObject(&global);
|
||||
if (global) {
|
||||
JSAutoEnterCompartment ac;
|
||||
if (global && ac.enter(mCx, global)) {
|
||||
uint32 oldopts = JS_GetOptions(mCx);
|
||||
JS_SetOptions(mCx, oldopts | JSOPTION_NO_SCRIPT_RVAL);
|
||||
|
||||
|
@ -132,35 +132,35 @@ function testCopyPaste () {
|
||||
|
||||
copyChildrenToClipboard("alist");
|
||||
testSelectionToString(" bla\n\n foo\n bar\n\n");
|
||||
testClipboardValue("text/unicode", "bla\n\n foo\n bar\n\n");
|
||||
testClipboardValue("text/unicode", " bla\n\n foo\n bar\n\n");
|
||||
testClipboardValue("text/html", "<div id=\"alist\">\n bla\n <ul>\n <li>foo</li>\n \n <li>bar</li>\n </ul>\n </div>");
|
||||
testPasteText("bla\n\n foo\n bar\n\n");
|
||||
testPasteText(" bla\n\n foo\n bar\n\n");
|
||||
|
||||
copyChildrenToClipboard("blist");
|
||||
testSelectionToString(" mozilla\n\n foo\n bar\n\n");
|
||||
testClipboardValue("text/unicode", "mozilla\n\n foo\n bar\n\n");
|
||||
testClipboardValue("text/unicode", " mozilla\n\n foo\n bar\n\n");
|
||||
testClipboardValue("text/html", "<div id=\"blist\">\n mozilla\n <ol>\n <li>foo</li>\n \n <li>bar</li>\n </ol>\n </div>");
|
||||
testPasteText("mozilla\n\n foo\n bar\n\n");
|
||||
testPasteText(" mozilla\n\n foo\n bar\n\n");
|
||||
|
||||
copyChildrenToClipboard("clist");
|
||||
testSelectionToString(" mzla\n\n foo\n bazzinga!\n bar\n\n");
|
||||
testClipboardValue("text/unicode", "mzla\n\n foo\n bazzinga!\n bar\n\n");
|
||||
testClipboardValue("text/unicode", " mzla\n\n foo\n bazzinga!\n bar\n\n");
|
||||
testClipboardValue("text/html", "<div id=\"clist\">\n mzla\n <ul>\n <li>foo<ul>\n <li>bazzinga!</li>\n </ul></li>\n \n <li>bar</li>\n </ul>\n </div>");
|
||||
testPasteText("mzla\n\n foo\n bazzinga!\n bar\n\n");
|
||||
testPasteText(" mzla\n\n foo\n bazzinga!\n bar\n\n");
|
||||
|
||||
copyChildrenToClipboard("div4");
|
||||
testSelectionToString(" Tt t t ");
|
||||
testClipboardValue("text/unicode", "Tt t t ");
|
||||
testClipboardValue("text/unicode", " Tt t t ");
|
||||
testClipboardValue("text/html", "<div id=\"div4\">\n T<textarea>t t t</textarea>\n</div>");
|
||||
testInnerHTML("div4", "\n T<textarea>t t t</textarea>\n");
|
||||
testPasteText("Tt t t ");
|
||||
testPasteText(" Tt t t ");
|
||||
|
||||
copyChildrenToClipboard("div5");
|
||||
testSelectionToString(" T ");
|
||||
testClipboardValue("text/unicode", "T ");
|
||||
testClipboardValue("text/unicode", " T ");
|
||||
testClipboardValue("text/html", "<div id=\"div5\">\n T<textarea> </textarea>\n</div>");
|
||||
testInnerHTML("div5", "\n T<textarea> </textarea>\n");
|
||||
testPasteText("T ");
|
||||
testPasteText(" T ");
|
||||
|
||||
copyRangeToClipboard($("div6").childNodes[0],0, $("div6").childNodes[1],1);
|
||||
testSelectionToString("");
|
||||
|
@ -37,26 +37,57 @@
|
||||
|
||||
#include "nsWindowMemoryReporter.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsWindowMemoryReporter::nsWindowMemoryReporter()
|
||||
: mCheckForGhostWindowsCallbackPending(false)
|
||||
{
|
||||
mDetachedWindows.Init();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter, nsIMemoryMultiReporter)
|
||||
NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryMultiReporter, nsIObserver,
|
||||
nsSupportsWeakReference)
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsWindowMemoryReporter::Init()
|
||||
{
|
||||
// The memory reporter manager is going to own this object.
|
||||
NS_RegisterMemoryMultiReporter(new nsWindowMemoryReporter());
|
||||
// The memory reporter manager will own this object.
|
||||
nsWindowMemoryReporter *windowReporter = new nsWindowMemoryReporter();
|
||||
NS_RegisterMemoryMultiReporter(windowReporter);
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
// DOM_WINDOW_DESTROYED_TOPIC announces what we call window "detachment",
|
||||
// when a window's docshell is set to NULL.
|
||||
os->AddObserver(windowReporter, DOM_WINDOW_DESTROYED_TOPIC,
|
||||
/* weakRef = */ true);
|
||||
os->AddObserver(windowReporter, "after-minimize-memory-usage",
|
||||
/* weakRef = */ true);
|
||||
}
|
||||
|
||||
GhostURLsReporter *ghostMultiReporter =
|
||||
new GhostURLsReporter(windowReporter);
|
||||
NS_RegisterMemoryMultiReporter(ghostMultiReporter);
|
||||
|
||||
NumGhostsReporter *ghostReporter =
|
||||
new NumGhostsReporter(windowReporter);
|
||||
NS_RegisterMemoryReporter(ghostReporter);
|
||||
}
|
||||
|
||||
static void
|
||||
AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr)
|
||||
static already_AddRefed<nsIURI>
|
||||
GetWindowURI(nsIDOMWindow *aWindow)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
|
||||
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
|
||||
NS_ENSURE_TRUE(pWindow, NULL);
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(pWindow->GetExtantDocument());
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
|
||||
if (doc) {
|
||||
@ -64,13 +95,25 @@ AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr)
|
||||
}
|
||||
|
||||
if (!uri) {
|
||||
nsIPrincipal *principal = aWindow->GetPrincipal();
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrincipal =
|
||||
do_QueryInterface(aWindow);
|
||||
NS_ENSURE_TRUE(scriptObjPrincipal, NULL);
|
||||
|
||||
nsIPrincipal *principal = scriptObjPrincipal->GetPrincipal();
|
||||
|
||||
if (principal) {
|
||||
principal->GetURI(getter_AddRefs(uri));
|
||||
}
|
||||
}
|
||||
|
||||
return uri.forget();
|
||||
}
|
||||
|
||||
static void
|
||||
AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = GetWindowURI(aWindow);
|
||||
|
||||
if (uri) {
|
||||
nsCString spec;
|
||||
uri->GetSpec(spec);
|
||||
@ -82,6 +125,8 @@ AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr)
|
||||
|
||||
aStr += spec;
|
||||
} else {
|
||||
// If we're unable to find a URI, we're dealing with a chrome window with
|
||||
// no document in it (or somesuch), so we call this a "system window".
|
||||
aStr += NS_LITERAL_CSTRING("[system]");
|
||||
}
|
||||
}
|
||||
@ -91,68 +136,36 @@ NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMStyleMallocSizeOf, "windows")
|
||||
static nsresult
|
||||
CollectWindowReports(nsGlobalWindow *aWindow,
|
||||
nsWindowSizes *aWindowTotalSizes,
|
||||
nsTHashtable<nsUint64HashKey> *aGhostWindowIDs,
|
||||
nsIMemoryMultiReporterCallback *aCb,
|
||||
nsISupports *aClosure)
|
||||
{
|
||||
// DOM window objects fall into one of three categories:
|
||||
// - "active" windows are currently either displayed in an active
|
||||
// tab, or a child of such a window.
|
||||
// - "cached" windows are in the fastback cache.
|
||||
// - "other" windows are closed (or navigated away from w/o being
|
||||
// cached) yet held alive by either a website or our code. The
|
||||
// latter case may be a memory leak, but not necessarily.
|
||||
//
|
||||
// For each window we show how much memory the window and its
|
||||
// document, etc, use, and we report those per URI, where the URI is
|
||||
// the document URI, if available, or the codebase of the principal in
|
||||
// the window. In the case where we're unable to find a URI we're
|
||||
// dealing with a chrome window with no document in it (or somesuch),
|
||||
// and for that we make the URI be the string "[system]".
|
||||
//
|
||||
// Outer windows are lumped in with inner windows, because the amount
|
||||
// of memory used by outer windows is small.
|
||||
//
|
||||
// The path we give to the reporter callback for "active" and "cached"
|
||||
// windows (both inner and outer) is as follows:
|
||||
//
|
||||
// explicit/window-objects/top(<top-outer-uri>, id=<top-outer-id>)/<category>/window(<window-uri>)/...
|
||||
//
|
||||
// The path we give for "other" windows is as follows:
|
||||
//
|
||||
// explicit/window-objects/top(none)/window(<window-uri>)/...
|
||||
//
|
||||
// Where:
|
||||
// - <category> is "active" or "cached", as described above.
|
||||
// - <top-outer-id> is the window id (nsPIDOMWindow::WindowID()) of
|
||||
// the top outer window (i.e. tab, or top level chrome window).
|
||||
// - <top-inner-uri> is the URI of the top outer window. Excepting
|
||||
// special windows (such as browser.xul or hiddenWindow.html) it's
|
||||
// what the address bar shows for the tab.
|
||||
// - <window-uri> is the URI of aWindow.
|
||||
//
|
||||
// Exposing the top-outer-id ensures that each tab gets its own
|
||||
// sub-tree, even if multiple tabs are showing the same URI.
|
||||
|
||||
nsCAutoString windowPath("explicit/window-objects/");
|
||||
|
||||
nsGlobalWindow *top = aWindow->GetTop();
|
||||
windowPath += NS_LITERAL_CSTRING("top(");
|
||||
// Avoid calling aWindow->GetTop() if there's no outer window. It will work
|
||||
// just fine, but will spew a lot of warnings.
|
||||
nsGlobalWindow *top = NULL;
|
||||
if (aWindow->GetOuterWindow()) {
|
||||
// Our window should have a null top iff it has a null docshell.
|
||||
MOZ_ASSERT(!!aWindow->GetTop() == !!aWindow->GetDocShell());
|
||||
top = aWindow->GetTop();
|
||||
}
|
||||
|
||||
if (top) {
|
||||
windowPath += NS_LITERAL_CSTRING("top(");
|
||||
AppendWindowURI(top, windowPath);
|
||||
windowPath += NS_LITERAL_CSTRING(", id=");
|
||||
windowPath.AppendInt(top->WindowID());
|
||||
} else {
|
||||
windowPath += NS_LITERAL_CSTRING("none");
|
||||
}
|
||||
windowPath += NS_LITERAL_CSTRING(")/");
|
||||
windowPath += NS_LITERAL_CSTRING(")/");
|
||||
|
||||
nsIDocShell *docShell = aWindow->GetDocShell();
|
||||
if (docShell) {
|
||||
MOZ_ASSERT(top, "'cached' or 'active' window lacks a top window");
|
||||
windowPath += aWindow->IsFrozen() ? NS_LITERAL_CSTRING("cached/")
|
||||
: NS_LITERAL_CSTRING("active/");
|
||||
} else {
|
||||
MOZ_ASSERT(!top, "'other' window has a top window");
|
||||
if (aGhostWindowIDs->Contains(aWindow->WindowID())) {
|
||||
windowPath += NS_LITERAL_CSTRING("top(none)/ghost/");
|
||||
} else {
|
||||
windowPath += NS_LITERAL_CSTRING("top(none)/detached/");
|
||||
}
|
||||
}
|
||||
|
||||
windowPath += NS_LITERAL_CSTRING("window(");
|
||||
@ -233,12 +246,20 @@ nsWindowMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
|
||||
WindowArray windows;
|
||||
windowsById->Enumerate(GetWindows, &windows);
|
||||
|
||||
// Get the IDs of all the "ghost" windows.
|
||||
nsTHashtable<nsUint64HashKey> ghostWindows;
|
||||
ghostWindows.Init();
|
||||
CheckForGhostWindows(&ghostWindows);
|
||||
|
||||
nsCOMPtr<nsIEffectiveTLDService> tldService = do_GetService(
|
||||
NS_EFFECTIVETLDSERVICE_CONTRACTID);
|
||||
NS_ENSURE_STATE(tldService);
|
||||
|
||||
// Collect window memory usage.
|
||||
nsRefPtr<nsGlobalWindow> *w = windows.Elements();
|
||||
nsRefPtr<nsGlobalWindow> *end = w + windows.Length();
|
||||
nsWindowSizes windowTotalSizes(NULL);
|
||||
for (; w != end; ++w) {
|
||||
nsresult rv = CollectWindowReports(*w, &windowTotalSizes, aCb, aClosure);
|
||||
for (PRUint32 i = 0; i < windows.Length(); i++) {
|
||||
nsresult rv = CollectWindowReports(windows[i], &windowTotalSizes,
|
||||
&ghostWindows, aCb, aClosure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
@ -286,4 +307,399 @@ nsWindowMemoryReporter::GetExplicitNonHeap(PRInt64* aAmount)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsWindowMemoryReporter::GetGhostTimeout()
|
||||
{
|
||||
return Preferences::GetUint("memory.ghost_window_timeout_seconds", 60);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
const PRUnichar *aData)
|
||||
{
|
||||
if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) {
|
||||
ObserveDOMWindowDetached(aSubject);
|
||||
} else if (!strcmp(aTopic, "after-minimize-memory-usage")) {
|
||||
ObserveAfterMinimizeMemoryUsage();
|
||||
} else {
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindowMemoryReporter::ObserveDOMWindowDetached(nsISupports* aWindow)
|
||||
{
|
||||
nsWeakPtr weakWindow = do_GetWeakReference(aWindow);
|
||||
if (!weakWindow) {
|
||||
NS_WARNING("Couldn't take weak reference to a window?");
|
||||
return;
|
||||
}
|
||||
|
||||
mDetachedWindows.Put(weakWindow, TimeStamp());
|
||||
|
||||
if (!mCheckForGhostWindowsCallbackPending) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this,
|
||||
&nsWindowMemoryReporter::CheckForGhostWindowsCallback);
|
||||
NS_DispatchToCurrentThread(runnable);
|
||||
mCheckForGhostWindowsCallbackPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
BackdateTimeStampsEnumerator(nsISupports *aKey, TimeStamp &aTimeStamp,
|
||||
void* aClosure)
|
||||
{
|
||||
TimeStamp *minTimeStamp = static_cast<TimeStamp*>(aClosure);
|
||||
|
||||
if (!aTimeStamp.IsNull() && aTimeStamp > *minTimeStamp) {
|
||||
aTimeStamp = *minTimeStamp;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindowMemoryReporter::ObserveAfterMinimizeMemoryUsage()
|
||||
{
|
||||
// Someone claims they've done enough GC/CCs so that all eligible windows
|
||||
// have been free'd. So we deem that any windows which satisfy ghost
|
||||
// criteria (1) and (2) now satisfy criterion (3) as well.
|
||||
//
|
||||
// To effect this change, we'll backdate some of our timestamps.
|
||||
|
||||
TimeStamp minTimeStamp = TimeStamp::Now() -
|
||||
TimeDuration::FromSeconds(GetGhostTimeout());
|
||||
|
||||
mDetachedWindows.Enumerate(BackdateTimeStampsEnumerator,
|
||||
&minTimeStamp);
|
||||
}
|
||||
|
||||
void
|
||||
nsWindowMemoryReporter::CheckForGhostWindowsCallback()
|
||||
{
|
||||
mCheckForGhostWindowsCallbackPending = false;
|
||||
CheckForGhostWindows();
|
||||
}
|
||||
|
||||
struct CheckForGhostWindowsEnumeratorData
|
||||
{
|
||||
nsTHashtable<nsCStringHashKey> *nonDetachedDomains;
|
||||
nsTHashtable<nsUint64HashKey> *ghostWindowIDs;
|
||||
nsIEffectiveTLDService *tldService;
|
||||
PRUint32 ghostTimeout;
|
||||
TimeStamp now;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
CheckForGhostWindowsEnumerator(nsISupports *aKey, TimeStamp& aTimeStamp,
|
||||
void* aClosure)
|
||||
{
|
||||
CheckForGhostWindowsEnumeratorData *data =
|
||||
static_cast<CheckForGhostWindowsEnumeratorData*>(aClosure);
|
||||
|
||||
nsWeakPtr weakKey = do_QueryInterface(aKey);
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(weakKey);
|
||||
if (!window) {
|
||||
// The window object has been destroyed. Stop tracking its weak ref in our
|
||||
// hashtable.
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
// Avoid calling GetTop() if we have no outer window. Nothing will break if
|
||||
// we do, but it will spew debug output, which can cause our test logs to
|
||||
// overflow.
|
||||
nsCOMPtr<nsIDOMWindow> top;
|
||||
if (window->GetOuterWindow()) {
|
||||
window->GetTop(getter_AddRefs(top));
|
||||
}
|
||||
|
||||
if (top) {
|
||||
// The window is no longer detached, so we no longer want to track it.
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = GetWindowURI(window);
|
||||
|
||||
nsCAutoString domain;
|
||||
if (uri) {
|
||||
// GetBaseDomain works fine if |uri| is null, but it outputs a warning
|
||||
// which ends up overrunning the mochitest logs.
|
||||
data->tldService->GetBaseDomain(uri, 0, domain);
|
||||
}
|
||||
|
||||
if (data->nonDetachedDomains->Contains(domain)) {
|
||||
// This window shares a domain with a non-detached window, so reset its
|
||||
// clock.
|
||||
aTimeStamp = TimeStamp();
|
||||
} else {
|
||||
// This window does not share a domain with a non-detached window, so it
|
||||
// meets ghost criterion (2).
|
||||
if (aTimeStamp.IsNull()) {
|
||||
// This may become a ghost window later; start its clock.
|
||||
aTimeStamp = data->now;
|
||||
} else if ((data->now - aTimeStamp).ToSeconds() > data->ghostTimeout) {
|
||||
// This definitely is a ghost window, so add it to ghostWindowIDs, if
|
||||
// that is not null.
|
||||
if (data->ghostWindowIDs) {
|
||||
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(window);
|
||||
if (pWindow) {
|
||||
data->ghostWindowIDs->PutEntry(pWindow->WindowID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
struct GetNonDetachedWindowDomainsEnumeratorData
|
||||
{
|
||||
nsTHashtable<nsCStringHashKey> *nonDetachedDomains;
|
||||
nsIEffectiveTLDService *tldService;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
GetNonDetachedWindowDomainsEnumerator(const PRUint64& aId, nsGlobalWindow* aWindow,
|
||||
void* aClosure)
|
||||
{
|
||||
GetNonDetachedWindowDomainsEnumeratorData *data =
|
||||
static_cast<GetNonDetachedWindowDomainsEnumeratorData*>(aClosure);
|
||||
|
||||
// Null outer window implies null top, but calling GetTop() when there's no
|
||||
// outer window causes us to spew debug warnings.
|
||||
if (!aWindow->GetOuterWindow() || !aWindow->GetTop()) {
|
||||
// This window is detached, so we don't care about its domain.
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = GetWindowURI(aWindow);
|
||||
|
||||
nsCAutoString domain;
|
||||
if (uri) {
|
||||
data->tldService->GetBaseDomain(uri, 0, domain);
|
||||
}
|
||||
|
||||
data->nonDetachedDomains->PutEntry(domain);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over mDetachedWindows and update it to reflect the current state of
|
||||
* the world. In particular:
|
||||
*
|
||||
* - Remove weak refs to windows which no longer exist.
|
||||
*
|
||||
* - Remove references to windows which are no longer detached.
|
||||
*
|
||||
* - Reset the timestamp on detached windows which share a domain with a
|
||||
* non-detached window (they no longer meet ghost criterion (2)).
|
||||
*
|
||||
* - If a window now meets ghost criterion (2) but didn't before, set its
|
||||
* timestamp to now.
|
||||
*
|
||||
* Additionally, if aOutGhostIDs is not null, fill it with the window IDs of
|
||||
* all ghost windows we found.
|
||||
*/
|
||||
void
|
||||
nsWindowMemoryReporter::CheckForGhostWindows(
|
||||
nsTHashtable<nsUint64HashKey> *aOutGhostIDs /* = NULL */)
|
||||
{
|
||||
nsCOMPtr<nsIEffectiveTLDService> tldService = do_GetService(
|
||||
NS_EFFECTIVETLDSERVICE_CONTRACTID);
|
||||
if (!tldService) {
|
||||
NS_WARNING("Couldn't get TLDService.");
|
||||
return;
|
||||
}
|
||||
|
||||
nsGlobalWindow::WindowByIdTable *windowsById =
|
||||
nsGlobalWindow::GetWindowsTable();
|
||||
if (!windowsById) {
|
||||
NS_WARNING("GetWindowsTable returned null");
|
||||
return;
|
||||
}
|
||||
|
||||
nsTHashtable<nsCStringHashKey> nonDetachedWindowDomains;
|
||||
nonDetachedWindowDomains.Init();
|
||||
|
||||
// Populate nonDetachedWindowDomains.
|
||||
GetNonDetachedWindowDomainsEnumeratorData nonDetachedEnumData =
|
||||
{ &nonDetachedWindowDomains, tldService };
|
||||
windowsById->EnumerateRead(GetNonDetachedWindowDomainsEnumerator,
|
||||
&nonDetachedEnumData);
|
||||
|
||||
// Update mDetachedWindows and write the ghost window IDs into aOutGhostIDs,
|
||||
// if it's not null.
|
||||
CheckForGhostWindowsEnumeratorData ghostEnumData =
|
||||
{ &nonDetachedWindowDomains, aOutGhostIDs, tldService,
|
||||
GetGhostTimeout(), TimeStamp::Now() };
|
||||
mDetachedWindows.Enumerate(CheckForGhostWindowsEnumerator,
|
||||
&ghostEnumData);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter::GhostURLsReporter,
|
||||
nsIMemoryMultiReporter)
|
||||
|
||||
nsWindowMemoryReporter::
|
||||
GhostURLsReporter::GhostURLsReporter(
|
||||
nsWindowMemoryReporter* aWindowReporter)
|
||||
: mWindowReporter(aWindowReporter)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
GhostURLsReporter::GetName(nsACString& aName)
|
||||
{
|
||||
aName.AssignLiteral("ghost-windows");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
GhostURLsReporter::GetExplicitNonHeap(PRInt64* aOut)
|
||||
{
|
||||
*aOut = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct ReportGhostWindowsEnumeratorData
|
||||
{
|
||||
nsIMemoryMultiReporterCallback* callback;
|
||||
nsISupports* closure;
|
||||
nsresult rv;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
ReportGhostWindowsEnumerator(nsUint64HashKey* aIDHashKey, void* aClosure)
|
||||
{
|
||||
ReportGhostWindowsEnumeratorData *data =
|
||||
static_cast<ReportGhostWindowsEnumeratorData*>(aClosure);
|
||||
|
||||
nsGlobalWindow::WindowByIdTable* windowsById =
|
||||
nsGlobalWindow::GetWindowsTable();
|
||||
if (!windowsById) {
|
||||
NS_WARNING("Couldn't get window-by-id hashtable?");
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsGlobalWindow* window = windowsById->Get(aIDHashKey->GetKey());
|
||||
if (!window) {
|
||||
NS_WARNING("Could not look up window?");
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCAutoString path;
|
||||
path.AppendLiteral("ghost-windows/");
|
||||
AppendWindowURI(window, path);
|
||||
|
||||
nsresult rv = data->callback->Callback(
|
||||
/* process = */ EmptyCString(),
|
||||
path,
|
||||
nsIMemoryReporter::KIND_SUMMARY,
|
||||
nsIMemoryReporter::UNITS_COUNT,
|
||||
/* amount = */ 1,
|
||||
/* desc = */ EmptyCString(),
|
||||
data->closure);
|
||||
|
||||
if (NS_FAILED(rv) && NS_SUCCEEDED(data->rv)) {
|
||||
data->rv = rv;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
GhostURLsReporter::CollectReports(
|
||||
nsIMemoryMultiReporterCallback* aCb,
|
||||
nsISupports* aClosure)
|
||||
{
|
||||
// Get the IDs of all the ghost windows in existance.
|
||||
nsTHashtable<nsUint64HashKey> ghostWindows;
|
||||
ghostWindows.Init();
|
||||
mWindowReporter->CheckForGhostWindows(&ghostWindows);
|
||||
|
||||
ReportGhostWindowsEnumeratorData reportGhostWindowsEnumData =
|
||||
{ aCb, aClosure, NS_OK };
|
||||
|
||||
// Call aCb->Callback() for each ghost window.
|
||||
ghostWindows.EnumerateEntries(ReportGhostWindowsEnumerator,
|
||||
&reportGhostWindowsEnumData);
|
||||
|
||||
return reportGhostWindowsEnumData.rv;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter::NumGhostsReporter,
|
||||
nsIMemoryReporter)
|
||||
|
||||
nsWindowMemoryReporter::
|
||||
NumGhostsReporter::NumGhostsReporter(
|
||||
nsWindowMemoryReporter *aWindowReporter)
|
||||
: mWindowReporter(aWindowReporter)
|
||||
{}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
NumGhostsReporter::GetProcess(nsACString& aProcess)
|
||||
{
|
||||
aProcess.AssignLiteral("");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
NumGhostsReporter::GetPath(nsACString& aPath)
|
||||
{
|
||||
aPath.AssignLiteral("ghost-windows");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
NumGhostsReporter::GetKind(PRInt32* aKind)
|
||||
{
|
||||
*aKind = KIND_OTHER;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
NumGhostsReporter::GetUnits(PRInt32* aUnits)
|
||||
{
|
||||
*aUnits = nsIMemoryReporter::UNITS_COUNT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
NumGhostsReporter::GetDescription(nsACString& aDesc)
|
||||
{
|
||||
nsPrintfCString str(1024,
|
||||
"The number of ghost windows present (the number of nodes underneath \
|
||||
explicit/window-objects/top(none)/ghost, modulo race conditions). A ghost \
|
||||
window is not shown in any tab, does not share a domain with any non-detached \
|
||||
windows, and has met these criteria for at least %ds \
|
||||
(memory.ghost_window_timeout_seconds) or has survived a round of about:memory's \
|
||||
minimize memory usage button.\n\n\
|
||||
Ghost windows can happen legitimately, but they are often indicative of leaks \
|
||||
in the browser or add-ons.",
|
||||
mWindowReporter->GetGhostTimeout());
|
||||
|
||||
aDesc.Assign(str);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::
|
||||
NumGhostsReporter::GetAmount(PRInt64* aAmount)
|
||||
{
|
||||
nsTHashtable<nsUint64HashKey> ghostWindows;
|
||||
ghostWindows.Init();
|
||||
mWindowReporter->CheckForGhostWindows(&ghostWindows);
|
||||
|
||||
*aAmount = ghostWindows.Count();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -39,6 +39,11 @@
|
||||
#define nsWindowMemoryReporter_h__
|
||||
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
// This should be used for any nsINode sub-class that has fields of its own
|
||||
// that it needs to measure; any sub-class that doesn't use it will inherit
|
||||
@ -49,29 +54,176 @@
|
||||
|
||||
class nsWindowSizes {
|
||||
public:
|
||||
nsWindowSizes(nsMallocSizeOfFun aMallocSizeOf) {
|
||||
memset(this, 0, sizeof(nsWindowSizes));
|
||||
mMallocSizeOf = aMallocSizeOf;
|
||||
}
|
||||
nsMallocSizeOfFun mMallocSizeOf;
|
||||
size_t mDOM;
|
||||
size_t mStyleSheets;
|
||||
size_t mLayoutArenas;
|
||||
size_t mLayoutStyleSets;
|
||||
size_t mLayoutTextRuns;
|
||||
nsWindowSizes(nsMallocSizeOfFun aMallocSizeOf) {
|
||||
memset(this, 0, sizeof(nsWindowSizes));
|
||||
mMallocSizeOf = aMallocSizeOf;
|
||||
}
|
||||
nsMallocSizeOfFun mMallocSizeOf;
|
||||
size_t mDOM;
|
||||
size_t mStyleSheets;
|
||||
size_t mLayoutArenas;
|
||||
size_t mLayoutStyleSets;
|
||||
size_t mLayoutTextRuns;
|
||||
};
|
||||
|
||||
class nsWindowMemoryReporter: public nsIMemoryMultiReporter
|
||||
/**
|
||||
* nsWindowMemoryReporter is responsible for the 'explicit/window-objects'
|
||||
* memory reporter.
|
||||
*
|
||||
* We classify DOM window objects into one of three categories:
|
||||
*
|
||||
* - "active" windows, which are displayed in a tab (as the top-level window
|
||||
* or an iframe),
|
||||
*
|
||||
* - "cached" windows, which are in the fastback cache (aka the bfcache), and
|
||||
*
|
||||
* - "detached" windows, which have a null docshell. A window becomes
|
||||
* detached when its <iframe> or tab containing the window is destroyed --
|
||||
* i.e., when the window is no longer active or cached.
|
||||
*
|
||||
* Additionally, we classify a subset of detached windows as "ghost" windows.
|
||||
* Although ghost windows can happen legitimately (a page can hold a reference
|
||||
* to a cross-domain window and then close its container), the presence of
|
||||
* ghost windows is often indicative of a memory leak.
|
||||
*
|
||||
* A window is a ghost if it meets the following three criteria:
|
||||
*
|
||||
* 1) The window is detached.
|
||||
*
|
||||
* 2) There exist no non-detached windows with the same base domain as
|
||||
* the window's principal. (For example, the base domain of
|
||||
* "wiki.mozilla.co.uk" is "mozilla.co.uk".) This criterion makes us less
|
||||
* likely to flag a legitimately held-alive detached window as a ghost.
|
||||
*
|
||||
* 3) The window has met criteria (1) and (2) above for at least
|
||||
* memory.ghost_window_timeout_seconds. This criterion is in place so we
|
||||
* don't immediately declare a window a ghost before the GC/CC has had a
|
||||
* chance to run.
|
||||
*
|
||||
* nsWindowMemoryReporter observes window detachment and uses mDetachedWindows
|
||||
* to remember when a window first met criteria (1) and (2). When we generate
|
||||
* a memory report, we use this accounting to determine which windows are
|
||||
* ghosts.
|
||||
*
|
||||
*
|
||||
* We use the following memory reporter path for active and cached windows:
|
||||
*
|
||||
* explicit/window-objects/top(<top-outer-uri>, id=<top-outer-id>)/<category>/window(<window-uri>)/...
|
||||
*
|
||||
* For detached and ghost windows, we use
|
||||
*
|
||||
* explicit/window-objects/top(none)/<category>/window(<window-uri>)/...
|
||||
*
|
||||
* Where
|
||||
*
|
||||
* - <category> is "active", "cached", "detached", or "ghost", as described
|
||||
* above.
|
||||
*
|
||||
* - <top-outer-id> is the window id of the top outer window (i.e. the tab, or
|
||||
* the top level chrome window). Exposing this ensures that each tab gets
|
||||
* its own sub-tree, even if multiple tabs are showing the same URI.
|
||||
*
|
||||
* - <top-uri> is the URI of the top window. Excepting special windows (such
|
||||
* as browser.xul or hiddenWindow.html) it's what the address bar shows for
|
||||
* the tab.
|
||||
*
|
||||
*/
|
||||
class nsWindowMemoryReporter: public nsIMemoryMultiReporter,
|
||||
public nsIObserver,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYMULTIREPORTER
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static void Init();
|
||||
|
||||
private:
|
||||
/**
|
||||
* GhostURLsReporter generates the "ghost-windows" multi-report, which
|
||||
* includes a list of all ghost windows' URLs. If you're only interested in
|
||||
* this list, running this report is faster than running
|
||||
* nsWindowMemoryReporter.
|
||||
*/
|
||||
class GhostURLsReporter: public nsIMemoryMultiReporter
|
||||
{
|
||||
public:
|
||||
GhostURLsReporter(nsWindowMemoryReporter* aWindowReporter);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYMULTIREPORTER
|
||||
|
||||
private:
|
||||
nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
|
||||
};
|
||||
|
||||
/**
|
||||
* nsGhostWindowReporter generates the "ghost-windows" single-report, which
|
||||
* counts the number of ghost windows present.
|
||||
*/
|
||||
class NumGhostsReporter: public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
NumGhostsReporter(nsWindowMemoryReporter* aWindowReporter);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
|
||||
private:
|
||||
nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
|
||||
};
|
||||
|
||||
// Protect ctor, use Init() instead.
|
||||
nsWindowMemoryReporter();
|
||||
|
||||
/**
|
||||
* Get the number of seconds for which a window must satisfy ghost criteria
|
||||
* (1) and (2) before we deem that it satisfies criterion (3).
|
||||
*/
|
||||
PRUint32 GetGhostTimeout();
|
||||
|
||||
void ObserveDOMWindowDetached(nsISupports* aWindow);
|
||||
void ObserveAfterMinimizeMemoryUsage();
|
||||
|
||||
/**
|
||||
* When we observe a DOM window being detached, we enqueue an asynchronous
|
||||
* event which calls this method. This method then calls
|
||||
* CheckForGhostWindows.
|
||||
*/
|
||||
void CheckForGhostWindowsCallback();
|
||||
|
||||
/**
|
||||
* Iterate over all weak window pointers in mDetachedWindows and update our
|
||||
* accounting of which windows meet ghost criterion (2).
|
||||
*
|
||||
* This method also cleans up mDetachedWindows, removing entries for windows
|
||||
* which have been destroyed or are no longer detached.
|
||||
*
|
||||
* If aOutGhostIDs is non-null, we populate it with the Window IDs of the
|
||||
* ghost windows.
|
||||
*
|
||||
* This is called asynchronously after we observe a DOM window being detached
|
||||
* from its docshell, and also right before we generate a memory report.
|
||||
*/
|
||||
void CheckForGhostWindows(nsTHashtable<nsUint64HashKey> *aOutGhostIDs = NULL);
|
||||
|
||||
/**
|
||||
* Maps a weak reference to a detached window (nsIWeakReference) to the time
|
||||
* when we observed that the window met ghost criterion (2) above.
|
||||
*
|
||||
* If the window has not yet met criterion (2) it maps to the null timestamp.
|
||||
*
|
||||
* (Although windows are not added to this table until they're detached, it's
|
||||
* possible for a detached window to become non-detached, and we won't
|
||||
* remove it from the table until CheckForGhostWindows runs.)
|
||||
*/
|
||||
nsDataHashtable<nsISupportsHashKey, mozilla::TimeStamp> mDetachedWindows;
|
||||
|
||||
/**
|
||||
* True if we have an asynchronous call to CheckForGhostWindows pending.
|
||||
*/
|
||||
bool mCheckForGhostWindowsCallbackPending;
|
||||
};
|
||||
|
||||
#endif // nsWindowMemoryReporter_h__
|
||||
|
@ -2129,6 +2129,12 @@ gfxFont::GetShapedWord(gfxContext *aContext,
|
||||
PRInt32 aAppUnitsPerDevUnit,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
// if the cache is getting too big, flush it and start over
|
||||
if (mWordCache.Count() > 10000) {
|
||||
NS_WARNING("flushing shaped-word cache");
|
||||
ClearCachedWords();
|
||||
}
|
||||
|
||||
// if there's a cached entry for this word, just return it
|
||||
CacheHashKey key(aText, aLength, aHash,
|
||||
aRunScript,
|
||||
|
@ -1694,7 +1694,7 @@ class JSCompartmentsMultiReporter : public nsIMemoryMultiReporter
|
||||
for (size_t i = 0; i < paths.length(); i++)
|
||||
// These ones don't need a description, hence the "".
|
||||
REPORT(nsCString(paths[i]),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
nsIMemoryReporter::KIND_SUMMARY,
|
||||
nsIMemoryReporter::UNITS_COUNT,
|
||||
1, "");
|
||||
|
||||
|
@ -3527,6 +3527,7 @@ pref("profiler.entries", 100000);
|
||||
// Network API
|
||||
pref("dom.network.enabled", true);
|
||||
pref("dom.network.metered", false);
|
||||
|
||||
#ifdef XP_WIN
|
||||
// On 32-bit Windows, fire a low-memory notification if we have less than this
|
||||
// many mb of virtual address space available.
|
||||
@ -3545,3 +3546,8 @@ pref("memory.low_physical_memory_threshold_mb", 0);
|
||||
// low_memory_notification_interval_ms.
|
||||
pref("memory.low_memory_notification_interval_ms", 10000);
|
||||
#endif
|
||||
|
||||
// How long must we wait before declaring that a window is a "ghost" (i.e., a
|
||||
// likely leak)? This should be longer than it usually takes for an eligible
|
||||
// window to be collected via the GC/CC.
|
||||
pref("memory.ghost_window_timeout_seconds", 60);
|
||||
|
@ -51,6 +51,7 @@ const Cu = Components.utils;
|
||||
const KIND_NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
|
||||
const KIND_HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
|
||||
const KIND_OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
|
||||
const KIND_SUMMARY = Ci.nsIMemoryReporter.KIND_SUMMARY;
|
||||
const UNITS_BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
|
||||
const UNITS_COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
|
||||
const UNITS_COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE;
|
||||
@ -163,10 +164,12 @@ function minimizeMemoryUsage3x(fAfter)
|
||||
.getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(null, "memory-pressure", "heap-minimize");
|
||||
|
||||
if (++i < 3)
|
||||
if (++i < 3) {
|
||||
runSoon(sendHeapMinNotificationsInner);
|
||||
else
|
||||
} else {
|
||||
os.notifyObservers(null, "after-minimize-memory-usage", "about:memory");
|
||||
runSoon(fAfter);
|
||||
}
|
||||
}
|
||||
|
||||
sendHeapMinNotificationsInner();
|
||||
@ -260,7 +263,7 @@ function processMemoryReporters(aMgr, aIgnoreSingle, aIgnoreMulti,
|
||||
// This regexp matches sentences and sentence fragments, i.e. strings that
|
||||
// start with a capital letter and ends with a '.'. (The final sentence may be
|
||||
// in parentheses, so a ')' might appear after the '.'.)
|
||||
const gSentenceRegExp = /^[A-Z].*\.\)?$/;
|
||||
const gSentenceRegExp = /^[A-Z].*\.\)?$/m;
|
||||
|
||||
function checkReport(aUnsafePath, aKind, aUnits, aAmount, aDescription)
|
||||
{
|
||||
@ -275,17 +278,16 @@ function checkReport(aUnsafePath, aKind, aUnits, aAmount, aDescription)
|
||||
assert(aUnits === UNITS_BYTES, "bad smaps units");
|
||||
assert(aDescription !== "", "empty smaps description");
|
||||
|
||||
} else if (aUnsafePath.startsWith("compartments/")) {
|
||||
assert(aKind === KIND_OTHER, "bad compartments kind");
|
||||
assert(aUnits === UNITS_COUNT, "bad compartments units");
|
||||
assert(aAmount === 1, "bad amount");
|
||||
assert(aDescription === "", "bad description");
|
||||
} else if (aKind === KIND_SUMMARY) {
|
||||
assert(!aUnsafePath.startsWith("explicit/") &&
|
||||
!aUnsafePath.startsWith("smaps/"),
|
||||
"bad SUMMARY path");
|
||||
|
||||
} else {
|
||||
assert(aUnsafePath.indexOf("/") === -1, "'other' path contains '/'");
|
||||
assert(aKind === KIND_OTHER, "bad other kind: " + aUnsafePath);
|
||||
assert(aDescription.match(gSentenceRegExp),
|
||||
"non-sentence other description");
|
||||
"non-sentence other description " + aDescription);
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,20 +539,22 @@ Report.prototype = {
|
||||
function getReportsByProcess(aMgr)
|
||||
{
|
||||
// Ignore the "smaps" multi-reporter in non-verbose mode, and the
|
||||
// "compartments" multi-reporter all the time. (Note that reports from these
|
||||
// multi-reporters can reach here as single reports if they were in the child
|
||||
// process.)
|
||||
// "compartments" and "ghost-windows" multi-reporters all the time. (Note
|
||||
// that reports from these multi-reporters can reach here as single reports
|
||||
// if they were in the child process.)
|
||||
|
||||
function ignoreSingle(aPath)
|
||||
{
|
||||
return (aPath.startsWith("smaps/") && !gVerbose) ||
|
||||
(aPath.startsWith("compartments/"))
|
||||
aPath.startsWith("compartments/") ||
|
||||
aPath.startsWith("ghost-windows/");
|
||||
}
|
||||
|
||||
function ignoreMulti(aName)
|
||||
{
|
||||
return ((aName === "smaps" && !gVerbose) ||
|
||||
(aName === "compartments"));
|
||||
return (aName === "smaps" && !gVerbose) ||
|
||||
aName === "compartments" ||
|
||||
aName === "ghost-windows";
|
||||
}
|
||||
|
||||
let reportsByProcess = {};
|
||||
@ -1552,15 +1556,21 @@ function updateAboutCompartments()
|
||||
let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
|
||||
let compartmentsByProcess = getCompartmentsByProcess(mgr);
|
||||
let ghostWindowsByProcess = getGhostWindowsByProcess(mgr);
|
||||
|
||||
function handleProcess(aProcess) {
|
||||
appendProcessAboutCompartmentsElements(body, aProcess,
|
||||
compartmentsByProcess[aProcess],
|
||||
ghostWindowsByProcess[aProcess]);
|
||||
}
|
||||
|
||||
// Generate output for one process at a time. Always start with the
|
||||
// Main process.
|
||||
let compartmentsByProcess = getCompartmentsByProcess(mgr);
|
||||
appendProcessCompartmentsElements(body, "Main",
|
||||
compartmentsByProcess["Main"]);
|
||||
handleProcess('Main');
|
||||
for (let process in compartmentsByProcess) {
|
||||
if (process !== "Main") {
|
||||
appendProcessCompartmentsElements(body, process,
|
||||
compartmentsByProcess[process]);
|
||||
handleProcess(process);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1656,30 +1666,89 @@ function getCompartmentsByProcess(aMgr)
|
||||
return compartmentsByProcess;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
function appendProcessCompartmentsElementsHelper(aP, aCompartments, aKindString)
|
||||
function GhostWindow(aUnsafeURL)
|
||||
{
|
||||
appendElementWithText(aP, "h2", "", aKindString + " Compartments\n");
|
||||
// Call it _unsafeName rather than _unsafeURL for symmetry with the
|
||||
// Compartment object.
|
||||
this._unsafeName = aUnsafeURL;
|
||||
|
||||
let compartmentTextArray = [];
|
||||
let uPre = appendElement(aP, "pre", "entries");
|
||||
for (let name in aCompartments) {
|
||||
let c = aCompartments[name];
|
||||
let isSystemKind = aKindString === "System";
|
||||
if (c._isSystemCompartment === isSystemKind) {
|
||||
let text = flipBackslashes(c._unsafeName);
|
||||
if (c._nMerged) {
|
||||
text += " [" + c._nMerged + "]";
|
||||
}
|
||||
text += "\n";
|
||||
compartmentTextArray.push(text);
|
||||
// this._nMerged is only defined if > 1
|
||||
}
|
||||
|
||||
GhostWindow.prototype = {
|
||||
merge: function(r) {
|
||||
this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
|
||||
}
|
||||
};
|
||||
|
||||
function getGhostWindowsByProcess(aMgr)
|
||||
{
|
||||
function ignoreSingle(aPath)
|
||||
{
|
||||
return !aPath.startsWith('ghost-windows/')
|
||||
}
|
||||
|
||||
function ignoreMulti(aName)
|
||||
{
|
||||
return aName !== "ghost-windows";
|
||||
}
|
||||
|
||||
let ghostWindowsByProcess = {};
|
||||
|
||||
function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
|
||||
aDescription)
|
||||
{
|
||||
let unsafeSplit = aUnsafePath.split('/');
|
||||
assert(unsafeSplit[0] == 'ghost-windows',
|
||||
'Unexpected path in getGhostWindowsByProcess: ' + aUnsafePath);
|
||||
|
||||
let unsafeURL = unsafeSplit[1];
|
||||
let ghostWindow = new GhostWindow(unsafeURL);
|
||||
|
||||
let process = aProcess === "" ? "Main" : aProcess;
|
||||
if (!ghostWindowsByProcess[process]) {
|
||||
ghostWindowsByProcess[process] = {};
|
||||
}
|
||||
|
||||
if (ghostWindowsByProcess[process][unsafeURL]) {
|
||||
ghostWindowsByProcess[process][unsafeURL].merge(ghostWindow);
|
||||
}
|
||||
else {
|
||||
ghostWindowsByProcess[process][unsafeURL] = ghostWindow;
|
||||
}
|
||||
}
|
||||
compartmentTextArray.sort();
|
||||
|
||||
for (let i = 0; i < compartmentTextArray.length; i++) {
|
||||
appendElementWithText(uPre, "span", "", compartmentTextArray[i]);
|
||||
processMemoryReporters(aMgr, ignoreSingle, ignoreMulti, handleReport);
|
||||
|
||||
return ghostWindowsByProcess;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
function appendProcessAboutCompartmentsElementsHelper(aP, aEntries, aKindString)
|
||||
{
|
||||
// aEntries might be null or undefined, e.g. if there are no ghost windows
|
||||
// for this process.
|
||||
aEntries = aEntries ? aEntries : {};
|
||||
|
||||
appendElementWithText(aP, "h2", "", aKindString + "\n");
|
||||
|
||||
let uPre = appendElement(aP, "pre", "entries");
|
||||
|
||||
let lines = [];
|
||||
for (let name in aEntries) {
|
||||
let e = aEntries[name];
|
||||
let line = flipBackslashes(e._unsafeName);
|
||||
if (e._nMerged) {
|
||||
line += ' [' + e._nMerged + ']';
|
||||
}
|
||||
line += '\n';
|
||||
lines.push(line);
|
||||
}
|
||||
lines.sort();
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
appendElementWithText(uPre, "span", "", lines[i]);
|
||||
}
|
||||
|
||||
appendTextNode(aP, "\n"); // gives nice spacing when we cut and paste
|
||||
@ -1694,14 +1763,30 @@ function appendProcessCompartmentsElementsHelper(aP, aCompartments, aKindString)
|
||||
* The name of the process.
|
||||
* @param aCompartments
|
||||
* Table of Compartments for this process, indexed by _unsafeName.
|
||||
* @param aGhostWindows
|
||||
* Array of window URLs of ghost windows.
|
||||
*
|
||||
* @return The generated text.
|
||||
*/
|
||||
function appendProcessCompartmentsElements(aP, aProcess, aCompartments)
|
||||
function appendProcessAboutCompartmentsElements(aP, aProcess, aCompartments, aGhostWindows)
|
||||
{
|
||||
appendElementWithText(aP, "h1", "", aProcess + " Process");
|
||||
appendTextNode(aP, "\n\n"); // gives nice spacing when we cut and paste
|
||||
|
||||
let userCompartments = {};
|
||||
let systemCompartments = {};
|
||||
for (let name in aCompartments) {
|
||||
let c = aCompartments[name];
|
||||
if (c._isSystemCompartment) {
|
||||
systemCompartments[name] = c;
|
||||
}
|
||||
else {
|
||||
userCompartments[name] = c;
|
||||
}
|
||||
}
|
||||
|
||||
appendProcessCompartmentsElementsHelper(aP, aCompartments, "User");
|
||||
appendProcessCompartmentsElementsHelper(aP, aCompartments, "System");
|
||||
appendProcessAboutCompartmentsElementsHelper(aP, userCompartments, "User Compartments");
|
||||
appendProcessAboutCompartmentsElementsHelper(aP, systemCompartments, "System Compartments");
|
||||
appendProcessAboutCompartmentsElementsHelper(aP, aGhostWindows, "Ghost Windows");
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
|
||||
const HEAP = Ci.nsIMemoryReporter.KIND_HEAP;
|
||||
const OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
|
||||
const SUMMARY = Ci.nsIMemoryReporter.KIND_SUMMARY;
|
||||
|
||||
const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
|
||||
const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
|
||||
@ -71,8 +72,8 @@
|
||||
f("", "other2", OTHER, COUNT, 888),
|
||||
|
||||
f("2nd", "explicit/c", HEAP, BYTES, 333 * MB),
|
||||
f("2nd", "compartments/user/child-user-compartment", OTHER, COUNT, 1),
|
||||
f("2nd", "compartments/system/child-system-compartment", OTHER, COUNT, 1)
|
||||
f("2nd", "compartments/user/child-user-compartment", SUMMARY, COUNT, 1),
|
||||
f("2nd", "compartments/system/child-system-compartment", SUMMARY, COUNT, 1)
|
||||
];
|
||||
|
||||
var fakeMultiReporters = [
|
||||
@ -90,7 +91,7 @@
|
||||
{ name: "compartments",
|
||||
collectReports: function(aCbObj, aClosure) {
|
||||
function f(aP) {
|
||||
aCbObj.callback("", aP, OTHER, COUNT, 1, "", aClosure);
|
||||
aCbObj.callback("", aP, SUMMARY, COUNT, 1, "", aClosure);
|
||||
}
|
||||
f("compartments/user/http:\\\\foo.com\\");
|
||||
f("compartments/user/https:\\\\bar.com\\bar?baz");
|
||||
@ -106,6 +107,16 @@
|
||||
},
|
||||
explicitNonHeap: 0
|
||||
},
|
||||
{ name: "ghost-windows",
|
||||
collectReports: function(aCbObj, aClosure) {
|
||||
function f(aP) {
|
||||
aCbObj.callback("", aP, SUMMARY, COUNT, 1, "", aClosure);
|
||||
}
|
||||
f("ghost-windows/https:\\\\very-long-url.com\\very-long\\oh-so-long\\really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789");
|
||||
f("ghost-windows/http:\\\\foobar.com\\foo?bar#baz");
|
||||
},
|
||||
explicitNonHeap: 0
|
||||
},
|
||||
// These shouldn't show up.
|
||||
{ name: "smaps",
|
||||
collectReports: function(aCbObj, aClosure) {
|
||||
@ -149,6 +160,10 @@ System Compartments\n\
|
||||
atoms\n\
|
||||
moz-nullprincipal:{7ddefdaf-34f1-473f-9b03-50a4568ccb06}\n\
|
||||
\n\
|
||||
Ghost Windows\n\
|
||||
http://foobar.com/foo?bar#baz\n\
|
||||
https://very-long-url.com/very-long/oh-so-long/really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789\n\
|
||||
\n\
|
||||
2nd Process\n\
|
||||
\n\
|
||||
User Compartments\n\
|
||||
@ -157,6 +172,8 @@ child-user-compartment\n\
|
||||
System Compartments\n\
|
||||
child-system-compartment\n\
|
||||
\n\
|
||||
Ghost Windows\n\
|
||||
\n\
|
||||
";
|
||||
|
||||
// Verbose mode output is the same when you cut and paste.
|
||||
|
@ -102,6 +102,7 @@ HISTOGRAM(MEMORY_STORAGE_SQLITE, 1024, 512 * 1024, 50, EXPONENTIAL, "Memory used
|
||||
HISTOGRAM(MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Memory used for uncompressed, in-use content images (KB)")
|
||||
HISTOGRAM(MEMORY_HEAP_ALLOCATED, 1024, 1024 * 1024, 50, EXPONENTIAL, "Heap memory allocated (KB)")
|
||||
HISTOGRAM(MEMORY_EXPLICIT, 1024, 1024 * 1024, 50, EXPONENTIAL, "Explicit memory allocations (KB)")
|
||||
HISTOGRAM(GHOST_WINDOWS, 1, 128, 8, EXPONENTIAL, "Number of ghost windows")
|
||||
#if defined(XP_MACOSX)
|
||||
HISTOGRAM(MEMORY_FREE_PURGED_PAGES_MS, 1, 1024, 10, EXPONENTIAL, "Time(ms) to purge MADV_FREE'd heap pages.")
|
||||
#elif defined(XP_WIN)
|
||||
|
@ -68,7 +68,8 @@ const MEM_HISTOGRAMS = {
|
||||
"page-faults-hard": "PAGE_FAULTS_HARD",
|
||||
"low-memory-events-virtual": "LOW_MEMORY_EVENTS_VIRTUAL",
|
||||
"low-memory-events-commit-space": "LOW_MEMORY_EVENTS_COMMIT_SPACE",
|
||||
"low-memory-events-physical": "LOW_MEMORY_EVENTS_PHYSICAL"
|
||||
"low-memory-events-physical": "LOW_MEMORY_EVENTS_PHYSICAL",
|
||||
"ghost-windows": "GHOST_WINDOWS"
|
||||
};
|
||||
// Seconds of idle time before pinging.
|
||||
// On idle-daily a gather-telemetry notification is fired, during it probes can
|
||||
|
@ -26,12 +26,20 @@ function download_progress(addon, value, maxValue) {
|
||||
function finish_test(count) {
|
||||
function wait_for_online() {
|
||||
info("Checking if the browser is still offline...");
|
||||
var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
request.open("GET", TESTROOT + "empty.xpi", true);
|
||||
request.onerror = wait_for_online;
|
||||
request.onload = Harness.finish;
|
||||
request.send(null);
|
||||
|
||||
let tab = gBrowser.selectedTab;
|
||||
tab.linkedBrowser.addEventListener("DOMContentLoaded", function errorLoad() {
|
||||
tab.linkedBrowser.removeEventListener("DOMContentLoaded", errorLoad, true);
|
||||
let url = tab.linkedBrowser.contentDocument.documentURI;
|
||||
info("loaded: " + url);
|
||||
if (/^about:neterror\?e=netOffline/.test(url)) {
|
||||
wait_for_online();
|
||||
} else {
|
||||
gBrowser.removeCurrentTab();
|
||||
Harness.finish();
|
||||
}
|
||||
}, true);
|
||||
tab.linkedBrowser.loadURI("http://example.com/");
|
||||
}
|
||||
|
||||
is(count, 0, "No add-ons should have been installed");
|
||||
@ -42,6 +50,5 @@ function finish_test(count) {
|
||||
|
||||
Services.perms.remove("example.com", "install");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
wait_for_online();
|
||||
}
|
||||
|
@ -117,10 +117,8 @@ interface nsIMemoryReporter : nsISupports
|
||||
* Reporters in this category must have kind NONHEAP, units BYTES, and
|
||||
* a non-empty description.
|
||||
*
|
||||
* - Paths starting with "compartments/" represent the names of JS
|
||||
* compartments. Reporters in this category must paths of the form
|
||||
* "compartments/user/<name>" or "compartments/system/<name>", amount 1,
|
||||
* kind OTHER, units COUNT, and an empty description.
|
||||
* - Reporters with kind SUMMARY may have any path which doesn't start with
|
||||
* "explicit/" or "smaps/".
|
||||
*
|
||||
* - All other paths represent cross-cutting values and may overlap with any
|
||||
* other reporter. Reporters in this category must have paths that do not
|
||||
@ -146,10 +144,22 @@ interface nsIMemoryReporter : nsISupports
|
||||
* - OTHER: reporters which don't fit into either of these categories. Such
|
||||
* reporters must have a path that does not start with "explicit/" or
|
||||
* "smaps/" and may have any units.
|
||||
*
|
||||
* - SUMMARY: reporters which report data that's available in a more
|
||||
* detailed form via other reporters. These reporters are sometimes
|
||||
* useful for efficiency purposes -- for example, a KIND_SUMMARY reporter
|
||||
* might list all the JS compartments without the overhead of the full JS
|
||||
* memory reporter, which walks the JS heap.
|
||||
*
|
||||
* Unlike other reporters, SUMMARY reporters may have empty descriptions.
|
||||
*
|
||||
* SUMMARY reporters must not have a path starting with "explicit/" or
|
||||
* "smaps/".
|
||||
*/
|
||||
const PRInt32 KIND_NONHEAP = 0;
|
||||
const PRInt32 KIND_HEAP = 1;
|
||||
const PRInt32 KIND_OTHER = 2;
|
||||
const PRInt32 KIND_SUMMARY = 3;
|
||||
|
||||
/*
|
||||
* KIND_MAPPED is a deprecated synonym for KIND_NONHEAP. We keep it around
|
||||
|
Loading…
Reference in New Issue
Block a user