mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge last PGO-green changeset of mozilla-inbound to mozilla-central
This commit is contained in:
commit
13dfef4805
@ -302,32 +302,21 @@ IDRefsIterator::GetElem(const nsDependentSubstring& aID)
|
||||
|
||||
// If content is in anonymous subtree or an element having anonymous subtree
|
||||
// then use "anonid" attribute to get elements in anonymous subtree.
|
||||
nsCOMPtr<nsIDOMElement> refDOMElm;
|
||||
nsCOMPtr<nsIDOMDocumentXBL> xblDocument =
|
||||
do_QueryInterface(mContent->OwnerDoc());
|
||||
|
||||
// Check inside the binding the element is contained in.
|
||||
nsIContent* bindingParent = mContent->GetBindingParent();
|
||||
if (bindingParent) {
|
||||
nsCOMPtr<nsIDOMElement> bindingParentElm = do_QueryInterface(bindingParent);
|
||||
xblDocument->GetAnonymousElementByAttribute(bindingParentElm,
|
||||
NS_LITERAL_STRING("anonid"),
|
||||
aID,
|
||||
getter_AddRefs(refDOMElm));
|
||||
nsCOMPtr<dom::Element> refElm = do_QueryInterface(refDOMElm);
|
||||
nsIContent* refElm = bindingParent->OwnerDoc()->
|
||||
GetAnonymousElementByAttribute(bindingParent, nsGkAtoms::anonid, aID);
|
||||
|
||||
if (refElm)
|
||||
return refElm;
|
||||
}
|
||||
|
||||
// Check inside the binding of the element.
|
||||
if (mContent->OwnerDoc()->BindingManager()->GetBinding(mContent)) {
|
||||
nsCOMPtr<nsIDOMElement> elm = do_QueryInterface(mContent);
|
||||
xblDocument->GetAnonymousElementByAttribute(elm,
|
||||
NS_LITERAL_STRING("anonid"),
|
||||
aID,
|
||||
getter_AddRefs(refDOMElm));
|
||||
nsCOMPtr<dom::Element> refElm = do_QueryInterface(refDOMElm);
|
||||
return refElm;
|
||||
return mContent->OwnerDoc()->
|
||||
GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, aID);
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
|
@ -12,8 +12,6 @@
|
||||
#include "nscore.h"
|
||||
#include "DocAccessible.h"
|
||||
|
||||
#include "nsIDOMDocumentXBL.h"
|
||||
|
||||
/**
|
||||
* AccIterable is a basic interface for iterators over accessibles.
|
||||
*/
|
||||
|
@ -24,13 +24,8 @@
|
||||
#include "StyleInfo.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDOMCSSValue.h"
|
||||
#include "nsIDOMCSSPrimitiveValue.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMDocumentXBL.h"
|
||||
#include "nsIDOMHTMLDocument.h"
|
||||
#include "nsIDOMHTMLFormElement.h"
|
||||
#include "nsIDOMNodeFilter.h"
|
||||
#include "nsIDOMHTMLElement.h"
|
||||
#include "nsIDOMTreeWalker.h"
|
||||
@ -2096,20 +2091,12 @@ nsAccessible::RelationByType(PRUint32 aType)
|
||||
}
|
||||
}
|
||||
if (!buttonEl) { // Check for anonymous accept button in <dialog>
|
||||
nsCOMPtr<nsIDOMDocumentXBL> xblDoc(do_QueryInterface(xulDoc));
|
||||
if (xblDoc) {
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(xulDoc);
|
||||
NS_ASSERTION(domDoc, "No DOM document");
|
||||
nsCOMPtr<nsIDOMElement> rootEl;
|
||||
domDoc->GetDocumentElement(getter_AddRefs(rootEl));
|
||||
if (rootEl) {
|
||||
nsCOMPtr<nsIDOMElement> possibleButtonEl;
|
||||
xblDoc->GetAnonymousElementByAttribute(rootEl,
|
||||
NS_LITERAL_STRING("default"),
|
||||
NS_LITERAL_STRING("true"),
|
||||
getter_AddRefs(possibleButtonEl));
|
||||
buttonEl = do_QueryInterface(possibleButtonEl);
|
||||
}
|
||||
dom::Element* rootElm = mContent->OwnerDoc()->GetRootElement();
|
||||
if (rootElm) {
|
||||
nsIContent* possibleButtonEl = rootElm->OwnerDoc()->
|
||||
GetAnonymousElementByAttribute(rootElm, nsGkAtoms::_default,
|
||||
NS_LITERAL_STRING("true"));
|
||||
buttonEl = do_QueryInterface(possibleButtonEl);
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
|
||||
|
@ -9,8 +9,6 @@
|
||||
#include "Role.h"
|
||||
#include "States.h"
|
||||
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMDocumentXBL.h"
|
||||
#include "nsIFrame.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
@ -42,19 +40,20 @@ nsXULSliderAccessible::NativeRole()
|
||||
PRUint64
|
||||
nsXULSliderAccessible::NativeState()
|
||||
{
|
||||
PRUint64 states = nsAccessibleWrap::NativeState();
|
||||
PRUint64 state = nsAccessibleWrap::NativeState();
|
||||
|
||||
nsCOMPtr<nsIContent> sliderContent(GetSliderNode());
|
||||
NS_ENSURE_STATE(sliderContent);
|
||||
nsIContent* sliderElm = GetSliderElement();
|
||||
if (!sliderElm)
|
||||
return state;
|
||||
|
||||
nsIFrame *frame = sliderContent->GetPrimaryFrame();
|
||||
nsIFrame *frame = sliderElm->GetPrimaryFrame();
|
||||
if (frame && frame->IsFocusable())
|
||||
states |= states::FOCUSABLE;
|
||||
state |= states::FOCUSABLE;
|
||||
|
||||
if (FocusMgr()->IsFocused(this))
|
||||
states |= states::FOCUSED;
|
||||
state |= states::FOCUSED;
|
||||
|
||||
return states;
|
||||
return state;
|
||||
}
|
||||
|
||||
// nsIAccessible
|
||||
@ -87,10 +86,10 @@ nsXULSliderAccessible::DoAction(PRUint8 aIndex)
|
||||
{
|
||||
NS_ENSURE_ARG(aIndex == 0);
|
||||
|
||||
nsCOMPtr<nsIContent> sliderContent(GetSliderNode());
|
||||
NS_ENSURE_STATE(sliderContent);
|
||||
nsIContent* sliderElm = GetSliderElement();
|
||||
if (sliderElm)
|
||||
DoCommand(sliderElm);
|
||||
|
||||
DoCommand(sliderContent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -165,30 +164,17 @@ nsXULSliderAccessible::CanHaveAnonChildren()
|
||||
|
||||
// Utils
|
||||
|
||||
already_AddRefed<nsIContent>
|
||||
nsXULSliderAccessible::GetSliderNode()
|
||||
nsIContent*
|
||||
nsXULSliderAccessible::GetSliderElement()
|
||||
{
|
||||
if (IsDefunct())
|
||||
return nsnull;
|
||||
|
||||
if (!mSliderNode) {
|
||||
nsCOMPtr<nsIDOMDocumentXBL> xblDoc(do_QueryInterface(mContent->OwnerDoc()));
|
||||
if (!xblDoc)
|
||||
return nsnull;
|
||||
|
||||
// XXX: we depend on anonymous content.
|
||||
nsCOMPtr<nsIDOMElement> domElm(do_QueryInterface(mContent));
|
||||
if (!domElm)
|
||||
return nsnull;
|
||||
|
||||
xblDoc->GetAnonymousElementByAttribute(domElm, NS_LITERAL_STRING("anonid"),
|
||||
NS_LITERAL_STRING("slider"),
|
||||
getter_AddRefs(mSliderNode));
|
||||
mSliderNode = mContent->OwnerDoc()->
|
||||
GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid,
|
||||
NS_LITERAL_STRING("slider"));
|
||||
}
|
||||
|
||||
nsIContent *sliderNode = nsnull;
|
||||
nsresult rv = CallQueryInterface(mSliderNode, &sliderNode);
|
||||
return NS_FAILED(rv) ? nsnull : sliderNode;
|
||||
return mSliderNode;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -199,10 +185,10 @@ nsXULSliderAccessible::GetSliderAttr(nsIAtom *aName, nsAString& aValue)
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIContent> sliderNode(GetSliderNode());
|
||||
NS_ENSURE_STATE(sliderNode);
|
||||
nsIContent* sliderElm = GetSliderElement();
|
||||
if (sliderElm)
|
||||
sliderElm->GetAttr(kNameSpaceID_None, aName, aValue);
|
||||
|
||||
sliderNode->GetAttr(kNameSpaceID_None, aName, aValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -212,10 +198,10 @@ nsXULSliderAccessible::SetSliderAttr(nsIAtom *aName, const nsAString& aValue)
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIContent> sliderNode(GetSliderNode());
|
||||
NS_ENSURE_STATE(sliderNode);
|
||||
nsIContent* sliderElm = GetSliderElement();
|
||||
if (sliderElm)
|
||||
sliderElm->SetAttr(kNameSpaceID_None, aName, aValue, true);
|
||||
|
||||
sliderNode->SetAttr(kNameSpaceID_None, aName, aValue, true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,10 @@ public:
|
||||
virtual PRUint8 ActionCount();
|
||||
|
||||
protected:
|
||||
already_AddRefed<nsIContent> GetSliderNode();
|
||||
/**
|
||||
* Return anonymous slider element.
|
||||
*/
|
||||
nsIContent* GetSliderElement();
|
||||
|
||||
nsresult GetSliderAttr(nsIAtom *aName, nsAString& aValue);
|
||||
nsresult SetSliderAttr(nsIAtom *aName, const nsAString& aValue);
|
||||
@ -47,7 +50,7 @@ protected:
|
||||
nsresult SetSliderAttr(nsIAtom *aName, double aValue);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDOMElement> mSliderNode;
|
||||
nsCOMPtr<nsIContent> mSliderNode;
|
||||
};
|
||||
|
||||
|
||||
|
@ -259,6 +259,14 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
#urlbar[pageproxystate="invalid"] > #identity-box > #identity-box-inner > #identity-icon-labels {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
#urlbar[pageproxystate="invalid"] > #identity-box {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#identity-icon-labels {
|
||||
max-width: 18em;
|
||||
}
|
||||
|
@ -8398,10 +8398,10 @@ var gIdentityHandler = {
|
||||
event.keyCode != KeyEvent.DOM_VK_RETURN))
|
||||
return; // Left click, space or enter only
|
||||
|
||||
// Revert the contents of the location bar, see bug 406779
|
||||
gURLBar.handleRevert();
|
||||
|
||||
if (this._mode == this.IDENTITY_MODE_CHROMEUI)
|
||||
// Don't allow left click, space or enter if the location
|
||||
// is chrome UI or the location has been modified.
|
||||
if (this._mode == this.IDENTITY_MODE_CHROMEUI ||
|
||||
gURLBar.getAttribute("pageproxystate") != "valid")
|
||||
return;
|
||||
|
||||
// Make sure that the display:none style we set in xul is removed now that
|
||||
|
@ -1001,15 +1001,15 @@ toolbar[iconsize="small"] #feed-button {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
.verifiedDomain > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.verifiedDomain > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https.png);
|
||||
}
|
||||
|
||||
.verifiedIdentity > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.verifiedIdentity > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-ev.png);
|
||||
}
|
||||
|
||||
.mixedContent > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.mixedContent > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-mixed.png);
|
||||
}
|
||||
|
||||
@ -1054,7 +1054,7 @@ toolbar[iconsize="small"] #feed-button {
|
||||
-moz-padding-end: 5px;
|
||||
}
|
||||
|
||||
#identity-box.verifiedIdentity {
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
|
||||
background-color: #fff;
|
||||
color: hsl(92,100%,30%);
|
||||
-moz-margin-end: 4px;
|
||||
|
@ -983,7 +983,7 @@ toolbar[mode="icons"] #zoom-in-button {
|
||||
padding-right: 10.01px;
|
||||
}
|
||||
|
||||
#identity-box.verifiedIdentity {
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
|
||||
color: hsl(92,100%,30%);
|
||||
-moz-padding-end: 4px;
|
||||
background-image: -moz-linear-gradient(hsla(92,81%,16%,0),
|
||||
@ -1058,15 +1058,15 @@ toolbar[mode="icons"] #zoom-in-button {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
.verifiedDomain > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.verifiedDomain > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https.png);
|
||||
}
|
||||
|
||||
.verifiedIdentity > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.verifiedIdentity > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-ev.png);
|
||||
}
|
||||
|
||||
.mixedContent > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.mixedContent > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-mixed.png);
|
||||
}
|
||||
|
||||
@ -1076,7 +1076,7 @@ toolbar[mode="icons"] #zoom-in-button {
|
||||
}
|
||||
|
||||
#page-proxy-favicon[pageproxystate="invalid"] {
|
||||
opacity: 0.5;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
#wrapper-urlbar-container[place="palette"] {
|
||||
|
@ -1363,7 +1363,7 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
||||
padding-right: 5.01px;
|
||||
}
|
||||
|
||||
#identity-box.verifiedIdentity {
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
|
||||
color: hsl(92,100%,30%);
|
||||
-moz-margin-end: 4px;
|
||||
background-image: -moz-linear-gradient(hsla(92,81%,16%,0),
|
||||
@ -1430,15 +1430,15 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
.verifiedDomain > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.verifiedDomain > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https.png);
|
||||
}
|
||||
|
||||
.verifiedIdentity > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.verifiedIdentity > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-ev.png);
|
||||
}
|
||||
|
||||
.mixedContent > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
.mixedContent > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-mixed.png);
|
||||
}
|
||||
|
||||
@ -1452,7 +1452,7 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
||||
}
|
||||
|
||||
#page-proxy-favicon[pageproxystate="invalid"] {
|
||||
opacity: 0.5;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* autocomplete */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# 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/.
|
||||
|
||||
hwcap_1 = OVERRIDE;
|
||||
|
@ -92,8 +92,8 @@ class Element;
|
||||
} // namespace mozilla
|
||||
|
||||
#define NS_IDOCUMENT_IID \
|
||||
{ 0x88d887da, 0xd228, 0x41c2, \
|
||||
{ 0xb8, 0x0a, 0x42, 0xec, 0xf0, 0xcb, 0xce, 0x37 } }
|
||||
{ 0x7bac702d, 0xca67, 0x4ce1, \
|
||||
{ 0x80, 0x3c, 0x35, 0xde, 0x81, 0x26, 0x04, 0x97 } }
|
||||
|
||||
// Flag for AddStyleSheet().
|
||||
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
|
||||
@ -1113,6 +1113,14 @@ public:
|
||||
virtual nsresult GetContentListFor(nsIContent* aContent,
|
||||
nsIDOMNodeList** aResult) = 0;
|
||||
|
||||
/**
|
||||
* See GetAnonymousElementByAttribute on nsIDOMDocumentXBL.
|
||||
*/
|
||||
virtual nsIContent*
|
||||
GetAnonymousElementByAttribute(nsIContent* aElement,
|
||||
nsIAtom* aAttrName,
|
||||
const nsAString& aAttrValue) const = 0;
|
||||
|
||||
/**
|
||||
* Helper for nsIDOMDocument::elementFromPoint implementation that allows
|
||||
* ignoring the scroll frame and/or avoiding layout flushes.
|
||||
|
@ -4931,29 +4931,52 @@ nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
static nsIContent*
|
||||
GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName,
|
||||
const nsAString& aAttrValue, bool aUniversalMatch,
|
||||
nsIDOMElement** aResult)
|
||||
const nsAString& aAttrValue, bool aUniversalMatch)
|
||||
{
|
||||
if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) :
|
||||
aContent->AttrValueIs(kNameSpaceID_None, aAttrName,
|
||||
aAttrValue, eCaseMatters)) {
|
||||
return CallQueryInterface(aContent, aResult);
|
||||
return aContent;
|
||||
}
|
||||
|
||||
for (nsIContent* child = aContent->GetFirstChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
|
||||
GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch,
|
||||
aResult);
|
||||
|
||||
if (*aResult)
|
||||
return NS_OK;
|
||||
nsIContent* matchedContent =
|
||||
GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch);
|
||||
if (matchedContent)
|
||||
return matchedContent;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsDocument::GetAnonymousElementByAttribute(nsIContent* aElement,
|
||||
nsIAtom* aAttrName,
|
||||
const nsAString& aAttrValue) const
|
||||
{
|
||||
nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
|
||||
if (!nodeList)
|
||||
return nsnull;
|
||||
|
||||
PRUint32 length = 0;
|
||||
nodeList->GetLength(&length);
|
||||
|
||||
bool universalMatch = aAttrValue.EqualsLiteral("*");
|
||||
|
||||
for (PRUint32 i = 0; i < length; ++i) {
|
||||
nsIContent* current = nodeList->GetNodeAt(i);
|
||||
nsIContent* matchedElm =
|
||||
GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch);
|
||||
if (matchedElm)
|
||||
return matchedElm;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -4964,32 +4987,12 @@ nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement,
|
||||
{
|
||||
*aResult = nsnull;
|
||||
|
||||
nsCOMPtr<nsIDOMNodeList> nodeList;
|
||||
GetAnonymousNodes(aElement, getter_AddRefs(nodeList));
|
||||
|
||||
if (!nodeList)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName);
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
|
||||
|
||||
PRUint32 length;
|
||||
nodeList->GetLength(&length);
|
||||
|
||||
bool universalMatch = aAttrValue.EqualsLiteral("*");
|
||||
|
||||
for (PRUint32 i = 0; i < length; ++i) {
|
||||
nsCOMPtr<nsIDOMNode> current;
|
||||
nodeList->Item(i, getter_AddRefs(current));
|
||||
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(current));
|
||||
|
||||
GetElementByAttribute(content, attribute, aAttrValue, universalMatch,
|
||||
aResult);
|
||||
if (*aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
nsIContent* matchedContent =
|
||||
GetAnonymousElementByAttribute(content, attribute, aAttrValue);
|
||||
return matchedContent ? CallQueryInterface(matchedContent, aResult) : NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -796,6 +796,10 @@ public:
|
||||
nsIDOMNodeList** aResult);
|
||||
virtual NS_HIDDEN_(nsresult) GetContentListFor(nsIContent* aContent,
|
||||
nsIDOMNodeList** aResult);
|
||||
virtual NS_HIDDEN_(nsIContent*)
|
||||
GetAnonymousElementByAttribute(nsIContent* aElement,
|
||||
nsIAtom* aAttrName,
|
||||
const nsAString& aAttrValue) const;
|
||||
|
||||
virtual NS_HIDDEN_(nsresult) ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
|
@ -159,6 +159,10 @@ public:
|
||||
// (no data has arrived for a while).
|
||||
void DownloadStalled();
|
||||
|
||||
// Called by the media decoder to indicate whether the media cache has
|
||||
// suspended the channel.
|
||||
void NotifySuspendedByCache(bool aIsSuspended);
|
||||
|
||||
// Called when a "MozAudioAvailable" event listener is added. The media
|
||||
// element will then notify its decoder that it needs to make a copy of
|
||||
// the audio data sent to hardware and dispatch it in "mozaudioavailable"
|
||||
@ -833,7 +837,7 @@ protected:
|
||||
// due to loading a preload:none media. When true, the resource we'll
|
||||
// load when the user initiates either playback or an explicit load is
|
||||
// stored in mPreloadURI.
|
||||
bool mLoadIsSuspended;
|
||||
bool mSuspendedForPreloadNone;
|
||||
|
||||
// True if a same-origin check has been done for the media element and resource.
|
||||
bool mMediaSecurityVerified;
|
||||
@ -843,6 +847,9 @@ protected:
|
||||
|
||||
// True if the media has an audio track
|
||||
bool mHasAudio;
|
||||
|
||||
// True if the media's channel's download has been suspended.
|
||||
bool mDownloadSuspendedByCache;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -593,7 +593,8 @@ void nsHTMLMediaElement::AbortExistingLoads()
|
||||
mSuspendedAfterFirstFrame = false;
|
||||
mAllowSuspendAfterFirstFrame = true;
|
||||
mHaveQueuedSelectResource = false;
|
||||
mLoadIsSuspended = false;
|
||||
mSuspendedForPreloadNone = false;
|
||||
mDownloadSuspendedByCache = false;
|
||||
mSourcePointer = nsnull;
|
||||
|
||||
// TODO: The playback rate must be set to the default playback rate.
|
||||
@ -905,7 +906,7 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
|
||||
|
||||
void nsHTMLMediaElement::SuspendLoad()
|
||||
{
|
||||
mLoadIsSuspended = true;
|
||||
mSuspendedForPreloadNone = true;
|
||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
|
||||
ChangeDelayLoadStatus(false);
|
||||
@ -913,8 +914,9 @@ void nsHTMLMediaElement::SuspendLoad()
|
||||
|
||||
void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction)
|
||||
{
|
||||
NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one");
|
||||
mLoadIsSuspended = false;
|
||||
NS_ASSERTION(mSuspendedForPreloadNone,
|
||||
"Must be halted for preload:none to resume from preload:none suspended load.");
|
||||
mSuspendedForPreloadNone = false;
|
||||
mPreloadAction = aAction;
|
||||
ChangeDelayLoadStatus(true);
|
||||
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
|
||||
@ -987,7 +989,7 @@ void nsHTMLMediaElement::UpdatePreloadAction()
|
||||
|
||||
mPreloadAction = nextAction;
|
||||
if (nextAction == nsHTMLMediaElement::PRELOAD_ENOUGH) {
|
||||
if (mLoadIsSuspended) {
|
||||
if (mSuspendedForPreloadNone) {
|
||||
// Our load was previouly suspended due to the media having preload
|
||||
// value "none". The preload value has changed to preload:auto, so
|
||||
// resume the load.
|
||||
@ -1001,7 +1003,7 @@ void nsHTMLMediaElement::UpdatePreloadAction()
|
||||
} else if (nextAction == nsHTMLMediaElement::PRELOAD_METADATA) {
|
||||
// Ensure that the video can be suspended after first frame.
|
||||
mAllowSuspendAfterFirstFrame = true;
|
||||
if (mLoadIsSuspended) {
|
||||
if (mSuspendedForPreloadNone) {
|
||||
// Our load was previouly suspended due to the media having preload
|
||||
// value "none". The preload value has changed to preload:metadata, so
|
||||
// resume the load. We'll pause the load again after we've read the
|
||||
@ -1650,10 +1652,11 @@ nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
mHasPlayedOrSeeked(false),
|
||||
mHasSelfReference(false),
|
||||
mShuttingDown(false),
|
||||
mLoadIsSuspended(false),
|
||||
mSuspendedForPreloadNone(false),
|
||||
mMediaSecurityVerified(false),
|
||||
mCORSMode(CORS_NONE),
|
||||
mHasAudio(false)
|
||||
mHasAudio(false),
|
||||
mDownloadSuspendedByCache(false)
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
if (!gMediaElementLog) {
|
||||
@ -1733,7 +1736,7 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
|
||||
nsresult rv = Load();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
if (mLoadIsSuspended) {
|
||||
if (mSuspendedForPreloadNone) {
|
||||
ResumeLoad(PRELOAD_ENOUGH);
|
||||
}
|
||||
// Even if we just did Load() or ResumeLoad(), we could already have a decoder
|
||||
@ -2636,6 +2639,20 @@ void nsHTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded)
|
||||
mPreloadAction == nsHTMLMediaElement::PRELOAD_METADATA) {
|
||||
mSuspendedAfterFirstFrame = true;
|
||||
mDecoder->Suspend();
|
||||
} else if (mLoadedFirstFrame &&
|
||||
mDownloadSuspendedByCache &&
|
||||
mDecoder &&
|
||||
!mDecoder->IsEnded()) {
|
||||
// We've already loaded the first frame, and the decoder has signalled
|
||||
// that the download has been suspended by the media cache. So move
|
||||
// readyState into HAVE_ENOUGH_DATA, in case there's script waiting
|
||||
// for a "canplaythrough" event; without this forced transition, we will
|
||||
// never fire the "canplaythrough" event if the media cache is so small
|
||||
// that the download was suspended before the first frame was loaded.
|
||||
// Don't force this transition if the decoder is in ended state; the
|
||||
// readyState should remain at HAVE_CURRENT_DATA in this case.
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2734,6 +2751,7 @@ void nsHTMLMediaElement::PlaybackEnded()
|
||||
void nsHTMLMediaElement::SeekStarted()
|
||||
{
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
||||
FireTimeUpdate(false);
|
||||
}
|
||||
|
||||
@ -2746,6 +2764,11 @@ void nsHTMLMediaElement::SeekCompleted()
|
||||
AddRemoveSelfReference();
|
||||
}
|
||||
|
||||
void nsHTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended)
|
||||
{
|
||||
mDownloadSuspendedByCache = aIsSuspended;
|
||||
}
|
||||
|
||||
void nsHTMLMediaElement::DownloadSuspended()
|
||||
{
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
|
||||
@ -2786,6 +2809,21 @@ void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReadyState > nsIDOMHTMLMediaElement::HAVE_METADATA &&
|
||||
mDownloadSuspendedByCache &&
|
||||
mDecoder &&
|
||||
!mDecoder->IsEnded()) {
|
||||
// The decoder has signalled that the download has been suspended by the
|
||||
// media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
|
||||
// script waiting for a "canplaythrough" event; without this forced
|
||||
// transition, we will never fire the "canplaythrough" event if the
|
||||
// media cache is too small, and scripts are bound to fail. Don't force
|
||||
// this transition if the decoder is in ended state; the readyState
|
||||
// should remain at HAVE_CURRENT_DATA in this case.
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aNextFrame != NEXT_FRAME_AVAILABLE) {
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
||||
if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
|
||||
|
@ -647,10 +647,14 @@ void nsBuiltinDecoder::NotifySuspendedStatusChanged()
|
||||
MediaResource* activeStream;
|
||||
bool suspended = mResource->IsSuspendedByCache(&activeStream);
|
||||
|
||||
if (suspended && mElement) {
|
||||
// if this is an autoplay element, we need to kick off its autoplaying
|
||||
// now so we consume data and hopefully free up cache space
|
||||
mElement->NotifyAutoplayDataReady();
|
||||
if (mElement) {
|
||||
if (suspended) {
|
||||
// If this is an autoplay element, we need to kick off its autoplaying
|
||||
// now so we consume data and hopefully free up cache space.
|
||||
mElement->NotifyAutoplayDataReady();
|
||||
}
|
||||
mElement->NotifySuspendedByCache(suspended);
|
||||
UpdateReadyStateForData();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ class gfxImageSurface;
|
||||
* the content it then releases the read lock.
|
||||
*/
|
||||
class gfxReusableSurfaceWrapper {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxReusableSurfaceWrapper);
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxReusableSurfaceWrapper)
|
||||
public:
|
||||
/**
|
||||
* Pass the gfxASurface to the wrapper.
|
||||
|
@ -22,7 +22,7 @@ CPPSRCS = \
|
||||
nsPosixLocale.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifeq (android,$(MOZ_WIDGET_TOOLKIT))
|
||||
ifeq (Android,$(OS_TARGET))
|
||||
CPPSRCS += nsAndroidCharset.cpp
|
||||
else
|
||||
CPPSRCS += nsUNIXCharset.cpp
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# 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/.
|
||||
|
||||
hwcap_1 = OVERRIDE;
|
||||
|
@ -205,7 +205,13 @@ nsMathMLmpaddedFrame::ParseAttribute(nsString& aString,
|
||||
aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
|
||||
return true;
|
||||
} else {
|
||||
// The REC does not allow the case ["+"|"-"] unsigned-number
|
||||
// case ["+"|"-"] unsigned-number
|
||||
// XXXfredw: should we allow non-zero unitless values? See bug 757703.
|
||||
if (!floatValue) {
|
||||
aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
|
||||
aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (unit.EqualsLiteral("width")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_WIDTH;
|
||||
@ -225,7 +231,6 @@ nsMathMLmpaddedFrame::ParseAttribute(nsString& aString,
|
||||
}
|
||||
|
||||
// see if the input was just a CSS value
|
||||
// We use a specific flag to indicate that the call is made from mpadded.
|
||||
// We are not supposed to have a unitless, percent, negative or namedspace
|
||||
// value here.
|
||||
number.Append(unit); // leave the sign out if it was there
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nsISVGChildFrame.h"
|
||||
#include "nsQueryFrame.h"
|
||||
#include "nsRect.h"
|
||||
#include "nsSVGUtils.h"
|
||||
|
||||
class nsFrameList;
|
||||
class nsIContent;
|
||||
|
@ -32,6 +32,9 @@ endif
|
||||
ifeq ($(OS_ARCH),AIX)
|
||||
DEFINES += -Dalloca=__alloca
|
||||
endif
|
||||
ifeq ($(OS_ARCH),SunOS)
|
||||
DEFINES += -DHAVE_ALLOCA_H
|
||||
endif
|
||||
|
||||
EXPORTS_NAMESPACES = opus
|
||||
|
||||
|
@ -104,6 +104,14 @@ struct ParamTraits<nsHttpHeaderArray::nsEntry>
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::net::InfallableCopyCString>
|
||||
: public ParamTraits<nsCString>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct ParamTraits<nsHttpHeaderArray>
|
||||
{
|
||||
|
@ -89,17 +89,17 @@ nsHttpHeaderArray::ClearHeader(nsHttpAtom header)
|
||||
}
|
||||
|
||||
const char *
|
||||
nsHttpHeaderArray::PeekHeader(nsHttpAtom header)
|
||||
nsHttpHeaderArray::PeekHeader(nsHttpAtom header) const
|
||||
{
|
||||
nsEntry *entry = nsnull;
|
||||
const nsEntry *entry = nsnull;
|
||||
LookupEntry(header, &entry);
|
||||
return entry ? entry->value.get() : nsnull;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result)
|
||||
nsHttpHeaderArray::GetHeader(nsHttpAtom header, nsACString &result) const
|
||||
{
|
||||
nsEntry *entry = nsnull;
|
||||
const nsEntry *entry = nsnull;
|
||||
LookupEntry(header, &entry);
|
||||
if (!entry)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
@ -196,7 +196,7 @@ nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders)
|
||||
}
|
||||
|
||||
const char *
|
||||
nsHttpHeaderArray::PeekHeaderAt(PRUint32 index, nsHttpAtom &header)
|
||||
nsHttpHeaderArray::PeekHeaderAt(PRUint32 index, nsHttpAtom &header) const
|
||||
{
|
||||
const nsEntry &entry = mHeaders[index];
|
||||
|
||||
|
@ -14,13 +14,40 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
// A nsCString that aborts if it fails to successfully copy its input during
|
||||
// copy construction and/or assignment. This is useful for building classes
|
||||
// that are safely copy-constructable and safely assignable using the compiler-
|
||||
// generated copy constructor and assignment operator.
|
||||
class InfallableCopyCString : public nsCString
|
||||
{
|
||||
public:
|
||||
InfallableCopyCString() { }
|
||||
InfallableCopyCString(const nsACString & other)
|
||||
: nsCString(other)
|
||||
{
|
||||
if (Length() != other.Length())
|
||||
NS_RUNTIMEABORT("malloc");
|
||||
}
|
||||
|
||||
InfallableCopyCString & operator=(const nsACString & other)
|
||||
{
|
||||
nsCString::operator=(other);
|
||||
|
||||
if (Length() != other.Length())
|
||||
NS_RUNTIMEABORT("malloc");
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} } // namespace mozilla::net
|
||||
|
||||
class nsHttpHeaderArray
|
||||
{
|
||||
public:
|
||||
nsHttpHeaderArray() {}
|
||||
~nsHttpHeaderArray() { Clear(); }
|
||||
|
||||
const char *PeekHeader(nsHttpAtom header);
|
||||
const char *PeekHeader(nsHttpAtom header) const;
|
||||
|
||||
// Used by internal setters: to set header from network use SetHeaderFromNet
|
||||
nsresult SetHeader(nsHttpAtom header, const nsACString &value,
|
||||
@ -30,17 +57,19 @@ public:
|
||||
// needs to be thrown or 1st value kept.
|
||||
nsresult SetHeaderFromNet(nsHttpAtom header, const nsACString &value);
|
||||
|
||||
nsresult GetHeader(nsHttpAtom header, nsACString &value);
|
||||
nsresult GetHeader(nsHttpAtom header, nsACString &value) const;
|
||||
void ClearHeader(nsHttpAtom h);
|
||||
|
||||
// Find the location of the given header value, or null if none exists.
|
||||
const char *FindHeaderValue(nsHttpAtom header, const char *value) {
|
||||
const char *FindHeaderValue(nsHttpAtom header, const char *value) const
|
||||
{
|
||||
return nsHttp::FindToken(PeekHeader(header), value,
|
||||
HTTP_HEADER_VALUE_SEPS);
|
||||
}
|
||||
|
||||
// Determine if the given header value exists.
|
||||
bool HasHeaderValue(nsHttpAtom header, const char *value) {
|
||||
bool HasHeaderValue(nsHttpAtom header, const char *value) const
|
||||
{
|
||||
return FindHeaderValue(header, value) != nsnull;
|
||||
}
|
||||
|
||||
@ -54,18 +83,17 @@ public:
|
||||
|
||||
void Flatten(nsACString &, bool pruneProxyHeaders=false);
|
||||
|
||||
PRUint32 Count() { return mHeaders.Length(); }
|
||||
PRUint32 Count() const { return mHeaders.Length(); }
|
||||
|
||||
const char *PeekHeaderAt(PRUint32 i, nsHttpAtom &header);
|
||||
const char *PeekHeaderAt(PRUint32 i, nsHttpAtom &header) const;
|
||||
|
||||
void Clear();
|
||||
|
||||
// Must be copy-constructable and assignable
|
||||
struct nsEntry
|
||||
{
|
||||
nsEntry() {}
|
||||
|
||||
nsHttpAtom header;
|
||||
nsCString value;
|
||||
mozilla::net::InfallableCopyCString value;
|
||||
|
||||
struct MatchHeader {
|
||||
bool Equals(const nsEntry &entry, const nsHttpAtom &header) const {
|
||||
@ -75,6 +103,7 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
PRInt32 LookupEntry(nsHttpAtom header, const nsEntry **) const;
|
||||
PRInt32 LookupEntry(nsHttpAtom header, nsEntry **);
|
||||
void MergeHeader(nsHttpAtom header, nsEntry *entry, const nsACString &value);
|
||||
|
||||
@ -89,6 +118,7 @@ private:
|
||||
// injection)
|
||||
bool IsSuspectDuplicateHeader(nsHttpAtom header);
|
||||
|
||||
// All members must be copy-constructable and assignable
|
||||
nsTArray<nsEntry> mHeaders;
|
||||
|
||||
friend struct IPC::ParamTraits<nsHttpHeaderArray>;
|
||||
@ -99,6 +129,15 @@ private:
|
||||
// nsHttpHeaderArray <private>: inline functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline PRInt32
|
||||
nsHttpHeaderArray::LookupEntry(nsHttpAtom header, const nsEntry **entry) const
|
||||
{
|
||||
PRUint32 index = mHeaders.IndexOf(header, 0, nsEntry::MatchHeader());
|
||||
if (index != PR_UINT32_MAX)
|
||||
*entry = &mHeaders[index];
|
||||
return index;
|
||||
}
|
||||
|
||||
inline PRInt32
|
||||
nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry)
|
||||
{
|
||||
|
@ -20,33 +20,46 @@ class nsHttpRequestHead
|
||||
{
|
||||
public:
|
||||
nsHttpRequestHead() : mMethod(nsHttp::Get), mVersion(NS_HTTP_VERSION_1_1) {}
|
||||
~nsHttpRequestHead() {}
|
||||
|
||||
void SetMethod(nsHttpAtom method) { mMethod = method; }
|
||||
void SetVersion(nsHttpVersion version) { mVersion = version; }
|
||||
void SetRequestURI(const nsCSubstring &s) { mRequestURI = s; }
|
||||
|
||||
nsHttpHeaderArray &Headers() { return mHeaders; }
|
||||
nsHttpAtom Method() { return mMethod; }
|
||||
nsHttpVersion Version() { return mVersion; }
|
||||
const nsCSubstring &RequestURI() { return mRequestURI; }
|
||||
const nsHttpHeaderArray &Headers() const { return mHeaders; }
|
||||
nsHttpHeaderArray & Headers() { return mHeaders; }
|
||||
nsHttpAtom Method() const { return mMethod; }
|
||||
nsHttpVersion Version() const { return mVersion; }
|
||||
const nsCSubstring &RequestURI() const { return mRequestURI; }
|
||||
|
||||
const char *PeekHeader(nsHttpAtom h) { return mHeaders.PeekHeader(h); }
|
||||
const char *PeekHeader(nsHttpAtom h) const
|
||||
{
|
||||
return mHeaders.PeekHeader(h);
|
||||
}
|
||||
nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m=false) { return mHeaders.SetHeader(h, v, m); }
|
||||
nsresult GetHeader(nsHttpAtom h, nsACString &v) { return mHeaders.GetHeader(h, v); }
|
||||
nsresult GetHeader(nsHttpAtom h, nsACString &v) const
|
||||
{
|
||||
return mHeaders.GetHeader(h, v);
|
||||
}
|
||||
void ClearHeader(nsHttpAtom h) { mHeaders.ClearHeader(h); }
|
||||
void ClearHeaders() { mHeaders.Clear(); }
|
||||
|
||||
const char *FindHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.FindHeaderValue(h, v); }
|
||||
bool HasHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.HasHeaderValue(h, v); }
|
||||
const char *FindHeaderValue(nsHttpAtom h, const char *v) const
|
||||
{
|
||||
return mHeaders.FindHeaderValue(h, v);
|
||||
}
|
||||
bool HasHeaderValue(nsHttpAtom h, const char *v) const
|
||||
{
|
||||
return mHeaders.HasHeaderValue(h, v);
|
||||
}
|
||||
|
||||
void Flatten(nsACString &, bool pruneProxyHeaders = false);
|
||||
|
||||
private:
|
||||
// All members must be copy-constructable and assignable
|
||||
nsHttpHeaderArray mHeaders;
|
||||
nsHttpAtom mMethod;
|
||||
nsHttpVersion mVersion;
|
||||
nsCString mRequestURI;
|
||||
mozilla::net::InfallableCopyCString mRequestURI;
|
||||
};
|
||||
|
||||
#endif // nsHttpRequestHead_h__
|
||||
|
@ -155,7 +155,7 @@ nsHttpResponseHead::ParseStatusLine(const char *line)
|
||||
mStatusText.AssignLiteral("OK");
|
||||
}
|
||||
else
|
||||
mStatusText = ++line;
|
||||
mStatusText = nsDependentCString(++line);
|
||||
}
|
||||
|
||||
LOG(("Have status line [version=%u status=%u statusText=%s]\n",
|
||||
@ -214,7 +214,7 @@ nsHttpResponseHead::ParseHeaderLine(const char *line)
|
||||
nsresult
|
||||
nsHttpResponseHead::ComputeCurrentAge(PRUint32 now,
|
||||
PRUint32 requestTime,
|
||||
PRUint32 *result)
|
||||
PRUint32 *result) const
|
||||
{
|
||||
PRUint32 dateValue;
|
||||
PRUint32 ageValue;
|
||||
@ -256,7 +256,7 @@ nsHttpResponseHead::ComputeCurrentAge(PRUint32 now,
|
||||
// freshnessLifetime = 0
|
||||
//
|
||||
nsresult
|
||||
nsHttpResponseHead::ComputeFreshnessLifetime(PRUint32 *result)
|
||||
nsHttpResponseHead::ComputeFreshnessLifetime(PRUint32 *result) const
|
||||
{
|
||||
*result = 0;
|
||||
|
||||
@ -303,7 +303,7 @@ nsHttpResponseHead::ComputeFreshnessLifetime(PRUint32 *result)
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpResponseHead::MustValidate()
|
||||
nsHttpResponseHead::MustValidate() const
|
||||
{
|
||||
LOG(("nsHttpResponseHead::MustValidate ??\n"));
|
||||
|
||||
@ -364,7 +364,7 @@ nsHttpResponseHead::MustValidate()
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpResponseHead::MustValidateIfExpired()
|
||||
nsHttpResponseHead::MustValidateIfExpired() const
|
||||
{
|
||||
// according to RFC2616, section 14.9.4:
|
||||
//
|
||||
@ -376,7 +376,7 @@ nsHttpResponseHead::MustValidateIfExpired()
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpResponseHead::IsResumable()
|
||||
nsHttpResponseHead::IsResumable() const
|
||||
{
|
||||
// even though some HTTP/1.0 servers may support byte range requests, we're not
|
||||
// going to bother with them, since those servers wouldn't understand If-Range.
|
||||
@ -387,7 +387,7 @@ nsHttpResponseHead::IsResumable()
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpResponseHead::ExpiresInPast()
|
||||
nsHttpResponseHead::ExpiresInPast() const
|
||||
{
|
||||
PRUint32 maxAgeVal, expiresVal, dateVal;
|
||||
|
||||
@ -402,7 +402,7 @@ nsHttpResponseHead::ExpiresInPast()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpResponseHead::UpdateHeaders(nsHttpHeaderArray &headers)
|
||||
nsHttpResponseHead::UpdateHeaders(const nsHttpHeaderArray &headers)
|
||||
{
|
||||
LOG(("nsHttpResponseHead::UpdateHeaders [this=%x]\n", this));
|
||||
|
||||
@ -469,7 +469,7 @@ nsHttpResponseHead::Reset()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, PRUint32 *result)
|
||||
nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, PRUint32 *result) const
|
||||
{
|
||||
const char *val = PeekHeader(header);
|
||||
if (!val)
|
||||
@ -485,7 +485,7 @@ nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, PRUint32 *result)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpResponseHead::GetAgeValue(PRUint32 *result)
|
||||
nsHttpResponseHead::GetAgeValue(PRUint32 *result) const
|
||||
{
|
||||
const char *val = PeekHeader(nsHttp::Age);
|
||||
if (!val)
|
||||
@ -498,7 +498,7 @@ nsHttpResponseHead::GetAgeValue(PRUint32 *result)
|
||||
// Return the value of the (HTTP 1.1) max-age directive, which itself is a
|
||||
// component of the Cache-Control response header
|
||||
nsresult
|
||||
nsHttpResponseHead::GetMaxAgeValue(PRUint32 *result)
|
||||
nsHttpResponseHead::GetMaxAgeValue(PRUint32 *result) const
|
||||
{
|
||||
const char *val = PeekHeader(nsHttp::Cache_Control);
|
||||
if (!val)
|
||||
@ -516,7 +516,7 @@ nsHttpResponseHead::GetMaxAgeValue(PRUint32 *result)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpResponseHead::GetExpiresValue(PRUint32 *result)
|
||||
nsHttpResponseHead::GetExpiresValue(PRUint32 *result) const
|
||||
{
|
||||
const char *val = PeekHeader(nsHttp::Expires);
|
||||
if (!val)
|
||||
@ -539,7 +539,7 @@ nsHttpResponseHead::GetExpiresValue(PRUint32 *result)
|
||||
}
|
||||
|
||||
PRInt64
|
||||
nsHttpResponseHead::TotalEntitySize()
|
||||
nsHttpResponseHead::TotalEntitySize() const
|
||||
{
|
||||
const char* contentRange = PeekHeader(nsHttp::Content_Range);
|
||||
if (!contentRange)
|
||||
|
@ -24,35 +24,38 @@ public:
|
||||
, mCacheControlNoStore(false)
|
||||
, mCacheControlNoCache(false)
|
||||
, mPragmaNoCache(false) {}
|
||||
~nsHttpResponseHead()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
nsHttpHeaderArray &Headers() { return mHeaders; }
|
||||
nsHttpVersion Version() { return mVersion; }
|
||||
PRUint16 Status() { return mStatus; }
|
||||
const nsAFlatCString &StatusText() { return mStatusText; }
|
||||
PRInt64 ContentLength() { return mContentLength; }
|
||||
const nsAFlatCString &ContentType() { return mContentType; }
|
||||
const nsAFlatCString &ContentCharset() { return mContentCharset; }
|
||||
bool NoStore() { return mCacheControlNoStore; }
|
||||
bool NoCache() { return (mCacheControlNoCache || mPragmaNoCache); }
|
||||
const nsHttpHeaderArray & Headers() const { return mHeaders; }
|
||||
nsHttpHeaderArray &Headers() { return mHeaders; }
|
||||
nsHttpVersion Version() const { return mVersion; }
|
||||
PRUint16 Status() const { return mStatus; }
|
||||
const nsAFlatCString &StatusText() const { return mStatusText; }
|
||||
PRInt64 ContentLength() const { return mContentLength; }
|
||||
const nsAFlatCString &ContentType() const { return mContentType; }
|
||||
const nsAFlatCString &ContentCharset() const { return mContentCharset; }
|
||||
bool NoStore() const { return mCacheControlNoStore; }
|
||||
bool NoCache() const { return (mCacheControlNoCache || mPragmaNoCache); }
|
||||
/**
|
||||
* Full length of the entity. For byte-range requests, this may be larger
|
||||
* than ContentLength(), which will only represent the requested part of the
|
||||
* entity.
|
||||
*/
|
||||
PRInt64 TotalEntitySize();
|
||||
PRInt64 TotalEntitySize() const;
|
||||
|
||||
const char *PeekHeader(nsHttpAtom h) { return mHeaders.PeekHeader(h); }
|
||||
const char *PeekHeader(nsHttpAtom h) const { return mHeaders.PeekHeader(h); }
|
||||
nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m=false);
|
||||
nsresult GetHeader(nsHttpAtom h, nsACString &v) { return mHeaders.GetHeader(h, v); }
|
||||
nsresult GetHeader(nsHttpAtom h, nsACString &v) const { return mHeaders.GetHeader(h, v); }
|
||||
void ClearHeader(nsHttpAtom h) { mHeaders.ClearHeader(h); }
|
||||
void ClearHeaders() { mHeaders.Clear(); }
|
||||
|
||||
const char *FindHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.FindHeaderValue(h, v); }
|
||||
bool HasHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.HasHeaderValue(h, v); }
|
||||
const char *FindHeaderValue(nsHttpAtom h, const char *v) const
|
||||
{
|
||||
return mHeaders.FindHeaderValue(h, v);
|
||||
}
|
||||
bool HasHeaderValue(nsHttpAtom h, const char *v) const
|
||||
{
|
||||
return mHeaders.HasHeaderValue(h, v);
|
||||
}
|
||||
|
||||
void SetContentType(const nsACString &s) { mContentType = s; }
|
||||
void SetContentCharset(const nsACString &s) { mContentCharset = s; }
|
||||
@ -74,31 +77,37 @@ public:
|
||||
nsresult ParseHeaderLine(const char *line);
|
||||
|
||||
// cache validation support methods
|
||||
nsresult ComputeFreshnessLifetime(PRUint32 *);
|
||||
nsresult ComputeCurrentAge(PRUint32 now, PRUint32 requestTime, PRUint32 *result);
|
||||
bool MustValidate();
|
||||
bool MustValidateIfExpired();
|
||||
nsresult ComputeFreshnessLifetime(PRUint32 *) const;
|
||||
nsresult ComputeCurrentAge(PRUint32 now, PRUint32 requestTime, PRUint32 *result) const;
|
||||
bool MustValidate() const;
|
||||
bool MustValidateIfExpired() const;
|
||||
|
||||
// returns true if the server appears to support byte range requests.
|
||||
bool IsResumable();
|
||||
bool IsResumable() const;
|
||||
|
||||
// returns true if the Expires header has a value in the past relative to the
|
||||
// value of the Date header.
|
||||
bool ExpiresInPast();
|
||||
bool ExpiresInPast() const;
|
||||
|
||||
// update headers...
|
||||
nsresult UpdateHeaders(nsHttpHeaderArray &headers);
|
||||
nsresult UpdateHeaders(const nsHttpHeaderArray &headers);
|
||||
|
||||
// reset the response head to it's initial state
|
||||
void Reset();
|
||||
|
||||
// these return failure if the header does not exist.
|
||||
nsresult ParseDateHeader(nsHttpAtom header, PRUint32 *result);
|
||||
nsresult GetAgeValue(PRUint32 *result);
|
||||
nsresult GetMaxAgeValue(PRUint32 *result);
|
||||
nsresult GetDateValue(PRUint32 *result) { return ParseDateHeader(nsHttp::Date, result); }
|
||||
nsresult GetExpiresValue(PRUint32 *result);
|
||||
nsresult GetLastModifiedValue(PRUint32 *result) { return ParseDateHeader(nsHttp::Last_Modified, result); }
|
||||
nsresult ParseDateHeader(nsHttpAtom header, PRUint32 *result) const;
|
||||
nsresult GetAgeValue(PRUint32 *result) const;
|
||||
nsresult GetMaxAgeValue(PRUint32 *result) const;
|
||||
nsresult GetDateValue(PRUint32 *result) const
|
||||
{
|
||||
return ParseDateHeader(nsHttp::Date, result);
|
||||
}
|
||||
nsresult GetExpiresValue(PRUint32 *result) const ;
|
||||
nsresult GetLastModifiedValue(PRUint32 *result) const
|
||||
{
|
||||
return ParseDateHeader(nsHttp::Last_Modified, result);
|
||||
}
|
||||
|
||||
private:
|
||||
void ParseVersion(const char *);
|
||||
@ -106,13 +115,14 @@ private:
|
||||
void ParsePragma(const char *);
|
||||
|
||||
private:
|
||||
// All members must be copy-constructable and assignable
|
||||
nsHttpHeaderArray mHeaders;
|
||||
nsHttpVersion mVersion;
|
||||
PRUint16 mStatus;
|
||||
nsCString mStatusText;
|
||||
mozilla::net::InfallableCopyCString mStatusText;
|
||||
PRInt64 mContentLength;
|
||||
nsCString mContentType;
|
||||
nsCString mContentCharset;
|
||||
mozilla::net::InfallableCopyCString mContentType;
|
||||
mozilla::net::InfallableCopyCString mContentCharset;
|
||||
bool mCacheControlNoStore;
|
||||
bool mCacheControlNoCache;
|
||||
bool mPragmaNoCache;
|
||||
|
@ -1113,6 +1113,12 @@ AuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
|
||||
NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
|
||||
|
||||
nsNSSSocketInfo *socketInfo = static_cast<nsNSSSocketInfo*>(arg);
|
||||
|
||||
if (socketInfo) {
|
||||
// This is the first callback during full handshakes.
|
||||
socketInfo->SetFirstServerHelloReceived();
|
||||
}
|
||||
|
||||
CERTCertificate *serverCert = SSL_PeerCertificate(fd);
|
||||
CERTCertificateCleaner serverCertCleaner(serverCert);
|
||||
|
||||
|
@ -831,6 +831,11 @@ void PR_CALLBACK HandshakeCallback(PRFileDesc* fd, void* client_data) {
|
||||
|
||||
nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
|
||||
|
||||
if (infoObject) {
|
||||
// This is the first callback on resumption handshakes
|
||||
infoObject->SetFirstServerHelloReceived();
|
||||
}
|
||||
|
||||
// If the handshake completed, then we know the site is TLS tolerant (if this
|
||||
// was a TLS connection).
|
||||
nsSSLIOLayerHelpers::rememberTolerantSite(infoObject);
|
||||
|
@ -98,8 +98,6 @@ extern PRLogModuleInfo* gPIPNSSLog;
|
||||
nsNSSSocketInfo::nsNSSSocketInfo()
|
||||
: mFd(nsnull),
|
||||
mCertVerificationState(before_cert_verification),
|
||||
mCertVerificationStarted(0),
|
||||
mCertVerificationEnded(0),
|
||||
mForSTARTTLS(false),
|
||||
mSSL3Enabled(false),
|
||||
mTLSEnabled(false),
|
||||
@ -109,6 +107,7 @@ nsNSSSocketInfo::nsNSSSocketInfo()
|
||||
mAllowTLSIntoleranceTimeout(true),
|
||||
mRememberClientAuthCertificate(false),
|
||||
mHandshakeStartTime(0),
|
||||
mFirstServerHelloReceived(false),
|
||||
mNPNCompleted(false),
|
||||
mHandshakeCompleted(false),
|
||||
mJoined(false),
|
||||
@ -423,7 +422,6 @@ nsNSSSocketInfo::SetCertVerificationWaiting()
|
||||
NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification,
|
||||
"Invalid state transition to waiting_for_cert_verification");
|
||||
mCertVerificationState = waiting_for_cert_verification;
|
||||
mCertVerificationStarted = PR_IntervalNow();
|
||||
}
|
||||
|
||||
// Be careful that SetCertVerificationResult does NOT get called while we are
|
||||
@ -437,8 +435,6 @@ nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode,
|
||||
NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification,
|
||||
"Invalid state transition to cert_verification_finished");
|
||||
|
||||
mCertVerificationEnded = PR_IntervalNow();
|
||||
|
||||
if (mFd) {
|
||||
SECStatus rv = SSL_AuthCertificateComplete(mFd, errorCode);
|
||||
// Only replace errorCode if there was originally no error
|
||||
@ -474,40 +470,30 @@ void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(bool aAllow)
|
||||
mAllowTLSIntoleranceTimeout = aAllow;
|
||||
}
|
||||
|
||||
#define HANDSHAKE_TIMEOUT_SECONDS 25
|
||||
|
||||
bool nsNSSSocketInfo::HandshakeTimeout()
|
||||
{
|
||||
if (mCertVerificationState == waiting_for_cert_verification) {
|
||||
// Do not do a TLS interlerance timeout during cert verification because:
|
||||
//
|
||||
// * If we would have timed out, but cert verification is still ongoing,
|
||||
// then the handshake probably already completed, and it is probably the
|
||||
// certificate validation (OCSP responder or similar) that is timing
|
||||
// out.
|
||||
// * If certificate validation AND the handshake is slow, then that is a
|
||||
// good indication that the network is bad, and so the problem probably
|
||||
// isn't the server being TLS intolerant.
|
||||
// * When we timeout, we return non-zero flags from PR_Poll, which will
|
||||
// cause the application to try to read from and/or write to the socket,
|
||||
// possibly in a loop. But, it is likely that the socket is blocked on
|
||||
// cert authentication, so those read and/or write calls would result in
|
||||
// PR_WOULD_BLOCK_ERROR, causing the application to spin.
|
||||
if (!mAllowTLSIntoleranceTimeout)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout)
|
||||
if (!mHandshakeInProgress)
|
||||
return false; // have not even sent client hello yet
|
||||
|
||||
if (mFirstServerHelloReceived)
|
||||
return false;
|
||||
|
||||
// Now we know we are in the first handshake, and haven't received the
|
||||
// ServerHello+Certificate sequence or the
|
||||
// ServerHello+ChangeCipherSpec+Finished sequence.
|
||||
//
|
||||
// XXX: Bug 754356 - waiting to receive the Certificate or Finished messages
|
||||
// may cause us to time out in cases where we shouldn't.
|
||||
|
||||
static const PRIntervalTime handshakeTimeoutInterval
|
||||
= PR_SecondsToInterval(25);
|
||||
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
PRIntervalTime certVerificationTime =
|
||||
mCertVerificationEnded - mCertVerificationStarted;
|
||||
PRIntervalTime totalTime = now - mHandshakeStartTime;
|
||||
PRIntervalTime totalTimeExceptCertVerificationTime =
|
||||
totalTime - certVerificationTime;
|
||||
|
||||
return totalTimeExceptCertVerificationTime >
|
||||
PR_SecondsToInterval(HANDSHAKE_TIMEOUT_SECONDS);
|
||||
bool result = (now - mHandshakeStartTime) > handshakeTimeoutInterval;
|
||||
return result;
|
||||
}
|
||||
|
||||
void nsSSLIOLayerHelpers::Cleanup()
|
||||
|
@ -76,6 +76,7 @@ public:
|
||||
|
||||
void SetHandshakeInProgress(bool aIsIn);
|
||||
bool GetHandshakeInProgress() { return mHandshakeInProgress; }
|
||||
void SetFirstServerHelloReceived() { mFirstServerHelloReceived = true; }
|
||||
bool HandshakeTimeout();
|
||||
|
||||
void SetAllowTLSIntoleranceTimeout(bool aAllow);
|
||||
@ -115,8 +116,6 @@ private:
|
||||
PRFileDesc* mFd;
|
||||
|
||||
CertVerificationState mCertVerificationState;
|
||||
PRIntervalTime mCertVerificationStarted;
|
||||
PRIntervalTime mCertVerificationEnded;
|
||||
|
||||
bool mForSTARTTLS;
|
||||
bool mSSL3Enabled;
|
||||
@ -127,6 +126,7 @@ private:
|
||||
bool mAllowTLSIntoleranceTimeout;
|
||||
bool mRememberClientAuthCertificate;
|
||||
PRIntervalTime mHandshakeStartTime;
|
||||
bool mFirstServerHelloReceived;
|
||||
|
||||
nsresult ActivateSSL();
|
||||
|
||||
|
@ -23,7 +23,7 @@ h2 {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.treeLine {
|
||||
.treeline {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,10 @@ String.prototype.startsWith =
|
||||
// undone are prefixed with "unsafe"; the rest are prefixed with "safe".
|
||||
function flipBackslashes(aUnsafeStr)
|
||||
{
|
||||
return aUnsafeStr.replace(/\\/g, '/');
|
||||
// Save memory by only doing the replacement if it's necessary.
|
||||
return (aUnsafeStr.indexOf('\\') === -1)
|
||||
? aUnsafeStr
|
||||
: aUnsafeStr.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
const gAssertionFailureMsgPrefix = "aboutMemory.js assertion failed: ";
|
||||
@ -408,16 +411,23 @@ function updateAboutMemory()
|
||||
let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
|
||||
let treesByProcess = {}, othersByProcess = {}, heapTotalByProcess = {};
|
||||
getTreesAndOthersByProcess(mgr, treesByProcess, othersByProcess,
|
||||
heapTotalByProcess);
|
||||
|
||||
// Generate output for one process at a time. Always start with the
|
||||
// Main process.
|
||||
let reportsByProcess = getReportsByProcess(mgr);
|
||||
let hasMozMallocUsableSize = mgr.hasMozMallocUsableSize;
|
||||
appendProcessReportsElements(body, "Main", reportsByProcess["Main"],
|
||||
hasMozMallocUsableSize);
|
||||
for (let process in reportsByProcess) {
|
||||
if (process !== "Main") {
|
||||
appendProcessReportsElements(body, process, reportsByProcess[process],
|
||||
appendProcessAboutMemoryElements(body, "Main", treesByProcess["Main"],
|
||||
othersByProcess["Main"],
|
||||
heapTotalByProcess["Main"],
|
||||
hasMozMallocUsableSize);
|
||||
for (let process in treesByProcess) {
|
||||
if (process !== "Main") {
|
||||
appendProcessAboutMemoryElements(body, process, treesByProcess[process],
|
||||
othersByProcess[process],
|
||||
heapTotalByProcess[process],
|
||||
hasMozMallocUsableSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,28 +485,23 @@ function updateAboutMemory()
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
function Report(aUnsafePath, aKind, aUnits, aAmount, aDescription)
|
||||
{
|
||||
this._unsafePath = aUnsafePath;
|
||||
this._kind = aKind;
|
||||
this._units = aUnits;
|
||||
this._amount = aAmount;
|
||||
this._description = aDescription;
|
||||
// this._nMerged is only defined if > 1
|
||||
// this._done is defined and set to true when the Report's amount is read
|
||||
}
|
||||
|
||||
Report.prototype = {
|
||||
// Sum the values and mark |this| as a dup. We mark dups because it's useful
|
||||
// to know when a report is duplicated; it might be worth investigating and
|
||||
// splitting up to have non-duplicated names.
|
||||
merge: function(r) {
|
||||
this._amount += r._amount;
|
||||
this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
|
||||
},
|
||||
};
|
||||
|
||||
function getReportsByProcess(aMgr)
|
||||
/**
|
||||
* This function reads all the memory reports, and puts that data in structures
|
||||
* that will be used to generate the page.
|
||||
*
|
||||
* @param aMgr
|
||||
* The memory reporter manager.
|
||||
* @param aTreesByProcess
|
||||
* Table of trees, indexed by process, which this function appends to.
|
||||
* @param aOthersByProcess
|
||||
* Table of other lists, indexed by process, which this function appends
|
||||
* to.
|
||||
* @param aHeapTotalByProcess
|
||||
* Table of heap total counts, indexed by process, which this function
|
||||
* appends to.
|
||||
*/
|
||||
function getTreesAndOthersByProcess(aMgr, aTreesByProcess, aOthersByProcess,
|
||||
aHeapTotalByProcess)
|
||||
{
|
||||
// Ignore the "smaps" multi-reporter in non-verbose mode, and the
|
||||
// "compartments" and "ghost-windows" multi-reporters all the time. (Note
|
||||
@ -517,50 +522,91 @@ function getReportsByProcess(aMgr)
|
||||
aMRName === "ghost-windows";
|
||||
}
|
||||
|
||||
let reportsByProcess = {};
|
||||
|
||||
function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
|
||||
aDescription)
|
||||
{
|
||||
let process = aProcess === "" ? "Main" : aProcess;
|
||||
let r = new Report(aUnsafePath, aKind, aUnits, aAmount, aDescription);
|
||||
if (!reportsByProcess[process]) {
|
||||
reportsByProcess[process] = {};
|
||||
}
|
||||
let reports = reportsByProcess[process];
|
||||
let rOld = reports[r._unsafePath];
|
||||
if (rOld) {
|
||||
// Already an entry; must be a duplicated report. This can happen
|
||||
// legitimately. Merge them.
|
||||
rOld.merge(r);
|
||||
|
||||
if (aUnsafePath.indexOf('/') !== -1) {
|
||||
// Tree report. Get the tree for the process, creating it if necessary.
|
||||
// All the trees for each process ("explicit", "vsize", etc) are stored
|
||||
// in a "tree-of-trees". This makes things simple later.
|
||||
if (!aTreesByProcess[process]) {
|
||||
aTreesByProcess[process] = new TreeNode("tree-of-trees");
|
||||
}
|
||||
let t = aTreesByProcess[process];
|
||||
|
||||
// Add any missing nodes in the tree implied by aUnsafePath, and fill in
|
||||
// the properties that we can with a top-down traversal.
|
||||
let unsafeNames = aUnsafePath.split('/');
|
||||
let u = t;
|
||||
for (let i = 0; i < unsafeNames.length; i++) {
|
||||
let unsafeName = unsafeNames[i];
|
||||
let uMatch = u.findKid(unsafeName);
|
||||
if (uMatch) {
|
||||
u = uMatch;
|
||||
} else {
|
||||
let v = new TreeNode(unsafeName);
|
||||
if (!u._kids) {
|
||||
u._kids = [];
|
||||
}
|
||||
u._kids.push(v);
|
||||
u = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (u._amount) {
|
||||
// Duplicate! Sum the values and mark it as a dup.
|
||||
u._amount += aAmount;
|
||||
u._nMerged = u._nMerged ? u._nMerged + 1 : 2;
|
||||
} else {
|
||||
// New leaf node. Fill in extra details node from the report.
|
||||
u._amount = aAmount;
|
||||
u._description = aDescription;
|
||||
}
|
||||
|
||||
if (unsafeNames[0] === "explicit" && aKind == KIND_HEAP) {
|
||||
if (!aHeapTotalByProcess[process]) {
|
||||
aHeapTotalByProcess[process] = 0;
|
||||
}
|
||||
aHeapTotalByProcess[process] += aAmount;
|
||||
}
|
||||
|
||||
} else {
|
||||
reports[r._unsafePath] = r;
|
||||
// "Other" (non-tree) report. Get the "others" for the process, creating
|
||||
// it if necessary.
|
||||
if (!aOthersByProcess[process]) {
|
||||
aOthersByProcess[process] = {};
|
||||
}
|
||||
let others = aOthersByProcess[process];
|
||||
|
||||
// Record the report.
|
||||
assert(!others[aUnsafePath], "dup'd OTHER report");
|
||||
others[aUnsafePath] =
|
||||
new OtherReport(aUnsafePath, aUnits, aAmount, aDescription);
|
||||
}
|
||||
}
|
||||
|
||||
processMemoryReporters(aMgr, ignoreSingle, ignoreMulti, handleReport);
|
||||
|
||||
return reportsByProcess;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// There are two kinds of TreeNode.
|
||||
// - Leaf TreeNodes correspond to Reports and have more properties.
|
||||
// - Leaf TreeNodes correspond to reports.
|
||||
// - Non-leaf TreeNodes are just scaffolding nodes for the tree; their values
|
||||
// are derived from their children.
|
||||
function TreeNode(aUnsafeName)
|
||||
{
|
||||
// Nb: _units is not needed, it's always UNITS_BYTES.
|
||||
this._unsafeName = aUnsafeName;
|
||||
this._kids = [];
|
||||
// Leaf TreeNodes have these properties added immediately after construction:
|
||||
// - _amount
|
||||
// - _description
|
||||
// - _kind
|
||||
// - _nMerged (only defined if > 1)
|
||||
//
|
||||
// Non-leaf TreeNodes have these properties added later:
|
||||
// - _kids
|
||||
// - _amount
|
||||
// - _description
|
||||
// - _hideKids (only defined if true)
|
||||
@ -568,9 +614,11 @@ function TreeNode(aUnsafeName)
|
||||
|
||||
TreeNode.prototype = {
|
||||
findKid: function(aUnsafeName) {
|
||||
for (let i = 0; i < this._kids.length; i++) {
|
||||
if (this._kids[i]._unsafeName === aUnsafeName) {
|
||||
return this._kids[i];
|
||||
if (this._kids) {
|
||||
for (let i = 0; i < this._kids.length; i++) {
|
||||
if (this._kids[i]._unsafeName === aUnsafeName) {
|
||||
return this._kids[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@ -586,100 +634,61 @@ TreeNode.compare = function(a, b) {
|
||||
};
|
||||
|
||||
/**
|
||||
* From a table of Reports, builds a tree that mirrors the tree structure that
|
||||
* will be shown as output.
|
||||
* Fill in the remaining properties for the specified tree in a bottom-up
|
||||
* fashion.
|
||||
*
|
||||
* @param aReports
|
||||
* The table of Reports, indexed by _unsafePath.
|
||||
* @param aTreeOfTrees
|
||||
* The tree-of-trees.
|
||||
* @param aTreePrefix
|
||||
* The prefix (name) of the tree being built. Must have '/' on the end.
|
||||
* @return The built tree.
|
||||
*/
|
||||
function buildTree(aReports, aTreePrefix)
|
||||
function fillInTree(aTreeOfTrees, aTreePrefix)
|
||||
{
|
||||
assert(aTreePrefix.indexOf('/') == aTreePrefix.length - 1,
|
||||
"aTreePrefix doesn't end in '/'");
|
||||
|
||||
// We want to process all reports that begin with |aTreePrefix|. First we
|
||||
// build the tree but only fill the properties that we can with a top-down
|
||||
// traversal.
|
||||
|
||||
let foundReport = false;
|
||||
let t = new TreeNode("falseRoot");
|
||||
for (let unsafePath in aReports) {
|
||||
// Add any missing nodes in the tree implied by the unsafePath.
|
||||
if (unsafePath.startsWith(aTreePrefix)) {
|
||||
foundReport = true;
|
||||
let r = aReports[unsafePath];
|
||||
let unsafeNames = r._unsafePath.split('/');
|
||||
let u = t;
|
||||
for (let i = 0; i < unsafeNames.length; i++) {
|
||||
let unsafeName = unsafeNames[i];
|
||||
let uMatch = u.findKid(unsafeName);
|
||||
if (uMatch) {
|
||||
u = uMatch;
|
||||
} else {
|
||||
let v = new TreeNode(unsafeName);
|
||||
u._kids.push(v);
|
||||
u = v;
|
||||
}
|
||||
}
|
||||
// Fill in extra details in the leaf node from the Report object.
|
||||
u._amount = r._amount;
|
||||
u._description = r._description;
|
||||
u._kind = r._kind;
|
||||
if (r._nMerged) {
|
||||
u._nMerged = r._nMerged;
|
||||
}
|
||||
r._done = true;
|
||||
}
|
||||
}
|
||||
|
||||
// There should always be at least one matching Report object when
|
||||
// |aTreePrefix| is "explicit/". But there may be zero for smaps trees; if
|
||||
// that happens, bail.
|
||||
if (!foundReport) {
|
||||
assert(aTreePrefix !== 'explicit/', "aTreePrefix !== 'explicit/'");
|
||||
// There should always be an "explicit/" tree. But smaps trees might not be
|
||||
// present; if that happens, return early.
|
||||
let t = aTreeOfTrees.findKid(aTreePrefix.replace(/\//g, ''));
|
||||
if (!t) {
|
||||
assert(aTreePrefix !== 'explicit/', "missing explicit tree");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Using falseRoot makes the above code simpler. Now discard it, leaving
|
||||
// aTreePrefix at the root.
|
||||
t = t._kids[0];
|
||||
|
||||
// Next, fill in the remaining properties bottom-up.
|
||||
function fillInNonLeafNodes(aT, aCannotMerge)
|
||||
{
|
||||
if (aT._kids.length === 0) {
|
||||
if (!aT._kids) {
|
||||
// Leaf node. Has already been filled in.
|
||||
assert(aT._kind !== undefined, "aT._kind is undefined for leaf node");
|
||||
|
||||
} else if (aT._kids.length === 1 && !aCannotMerge) {
|
||||
// Non-leaf node with one child. Merge the child with the node to avoid
|
||||
// redundant entries.
|
||||
assert(aT._kind === undefined, "aT._kind is defined for non-leaf node");
|
||||
let kid = aT._kids[0];
|
||||
let kidBytes = fillInNonLeafNodes(kid);
|
||||
aT._unsafeName += '/' + kid._unsafeName;
|
||||
aT._kids = kid._kids;
|
||||
if (kid._kids) {
|
||||
aT._kids = kid._kids;
|
||||
} else {
|
||||
delete aT._kids;
|
||||
}
|
||||
aT._amount = kid._amount;
|
||||
aT._description = kid._description;
|
||||
aT._kind = kid._kind;
|
||||
if (kid._nMerged) {
|
||||
if (kid._nMerged !== undefined) {
|
||||
aT._nMerged = kid._nMerged
|
||||
}
|
||||
assert(!aT._hideKids && !kid._hideKids, "_hideKids set when merging");
|
||||
|
||||
} else {
|
||||
// Non-leaf node with multiple children. Derive its _amount and
|
||||
// _description entirely from its children.
|
||||
assert(aT._kind === undefined, "aT._kind is defined for non-leaf node");
|
||||
let kidsBytes = 0;
|
||||
for (let i = 0; i < aT._kids.length; i++) {
|
||||
kidsBytes += fillInNonLeafNodes(aT._kids[i]);
|
||||
}
|
||||
aT._amount = kidsBytes;
|
||||
aT._description = "The sum of all entries below '" +
|
||||
flipBackslashes(aT._unsafeName) + "'.";
|
||||
aT._description = "The sum of all entries below this one.";
|
||||
}
|
||||
return aT._amount;
|
||||
}
|
||||
@ -694,63 +703,27 @@ function buildTree(aReports, aTreePrefix)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore all the memory reports that belong to a smaps tree; this involves
|
||||
* explicitly marking them as done.
|
||||
*
|
||||
* @param aReports
|
||||
* The table of Reports, indexed by _unsafePath.
|
||||
*/
|
||||
function ignoreSmapsTrees(aReports)
|
||||
{
|
||||
for (let unsafePath in aReports) {
|
||||
let r = aReports[unsafePath];
|
||||
if (isSmapsPath(r._unsafePath)) {
|
||||
r._done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do some work which only makes sense for the 'explicit' tree.
|
||||
* Compute the "heap-unclassified" value and insert it into the "explicit"
|
||||
* tree.
|
||||
*
|
||||
* @param aT
|
||||
* The tree.
|
||||
* @param aReports
|
||||
* Table of Reports for this process, indexed by _unsafePath.
|
||||
* The "explicit" tree.
|
||||
* @param aOthers
|
||||
* "Other measurements" for this process, indexed by _unsafePath.
|
||||
* @param aHeapTotal
|
||||
* The sum of all explicit HEAP reporters for this process.
|
||||
* @return A boolean indicating if "heap-allocated" is known for the process.
|
||||
*/
|
||||
function fixUpExplicitTree(aT, aReports)
|
||||
function addHeapUnclassifiedNode(aT, aOthers, aHeapTotal)
|
||||
{
|
||||
// Determine how many bytes are in heap reports.
|
||||
function getKnownHeapUsedBytes(aT)
|
||||
{
|
||||
let n = 0;
|
||||
if (aT._kids.length === 0) {
|
||||
// Leaf node.
|
||||
assert(aT._kind !== undefined, "aT._kind is undefined for leaf node");
|
||||
n = aT._kind === KIND_HEAP ? aT._amount : 0;
|
||||
} else {
|
||||
for (let i = 0; i < aT._kids.length; i++) {
|
||||
n += getKnownHeapUsedBytes(aT._kids[i]);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// A special case: compute the derived "heap-unclassified" value. Don't
|
||||
// mark "heap-allocated" when we get its size because we want it to appear
|
||||
// in the "Other Measurements" list.
|
||||
let heapAllocatedReport = aReports["heap-allocated"];
|
||||
let heapAllocatedReport = aOthers["heap-allocated"];
|
||||
if (heapAllocatedReport === undefined)
|
||||
return false;
|
||||
|
||||
let heapAllocatedBytes = heapAllocatedReport._amount;
|
||||
let heapUnclassifiedT = new TreeNode("heap-unclassified");
|
||||
heapUnclassifiedT._amount = heapAllocatedBytes - getKnownHeapUsedBytes(aT);
|
||||
// This kindToString() ensures the "(Heap)" prefix is set without having to
|
||||
// set the _kind property, which would mean that there is a corresponding
|
||||
// Report object for this TreeNode object (which isn't true)
|
||||
heapUnclassifiedT._description = kindToString(KIND_HEAP) +
|
||||
heapUnclassifiedT._amount = heapAllocatedBytes - aHeapTotal;
|
||||
heapUnclassifiedT._description =
|
||||
"Memory not classified by a more specific reporter. This includes " +
|
||||
"slop bytes due to internal fragmentation in the heap allocator " +
|
||||
"(caused when the allocator rounds up request sizes).";
|
||||
@ -778,7 +751,7 @@ function sortTreeAndInsertAggregateNodes(aTotalBytes, aT)
|
||||
(100 * aT._amount / aTotalBytes) < kSignificanceThresholdPerc;
|
||||
}
|
||||
|
||||
if (aT._kids.length === 0) {
|
||||
if (!aT._kids) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -805,6 +778,7 @@ function sortTreeAndInsertAggregateNodes(aTotalBytes, aT)
|
||||
let nAgg = aT._kids.length - i0;
|
||||
// Create an aggregate node.
|
||||
let aggT = new TreeNode("(" + nAgg + " tiny)");
|
||||
aggT._kids = [];
|
||||
let aggBytes = 0;
|
||||
for ( ; i < aT._kids.length; i++) {
|
||||
aggBytes += aT._kids[i]._amount;
|
||||
@ -886,37 +860,40 @@ function appendWarningElements(aP, aHasKnownHeapAllocated,
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the elements for a single process's Reports.
|
||||
* Appends the about:memory elements for a single process.
|
||||
*
|
||||
* @param aP
|
||||
* The parent DOM node.
|
||||
* @param aProcess
|
||||
* The name of the process.
|
||||
* @param aReports
|
||||
* Table of Reports for this process, indexed by _unsafePath.
|
||||
* @param aTreeOfTrees
|
||||
* The tree-of-trees for this process.
|
||||
* @param aOthers
|
||||
* The "other measurements" for this process.
|
||||
* @param aHasMozMallocUsableSize
|
||||
* Boolean indicating if moz_malloc_usable_size works.
|
||||
* @return The generated text.
|
||||
*/
|
||||
function appendProcessReportsElements(aP, aProcess, aReports,
|
||||
aHasMozMallocUsableSize)
|
||||
function appendProcessAboutMemoryElements(aP, aProcess, aTreeOfTrees, aOthers,
|
||||
aHeapTotal, aHasMozMallocUsableSize)
|
||||
{
|
||||
appendElementWithText(aP, "h1", "", aProcess + " Process\n\n");
|
||||
|
||||
// We'll fill this in later.
|
||||
let warningsDiv = appendElement(aP, "div", "accuracyWarning");
|
||||
|
||||
let explicitTree = buildTree(aReports, 'explicit/');
|
||||
let hasKnownHeapAllocated = fixUpExplicitTree(explicitTree, aReports);
|
||||
let explicitTree = fillInTree(aTreeOfTrees, "explicit/");
|
||||
let hasKnownHeapAllocated =
|
||||
addHeapUnclassifiedNode(explicitTree, aOthers, aHeapTotal);
|
||||
sortTreeAndInsertAggregateNodes(explicitTree._amount, explicitTree);
|
||||
appendTreeElements(aP, explicitTree, aProcess);
|
||||
|
||||
// We only show these breakdown trees in verbose mode.
|
||||
if (gVerbose) {
|
||||
kSmapsTreePrefixes.forEach(function(aTreePrefix) {
|
||||
let t = buildTree(aReports, aTreePrefix);
|
||||
let t = fillInTree(aTreeOfTrees, aTreePrefix);
|
||||
|
||||
// |t| will be null if we don't have any reports for the given
|
||||
// |t| will be undefined if we don't have any reports for the given
|
||||
// unsafePath.
|
||||
if (t) {
|
||||
sortTreeAndInsertAggregateNodes(t._amount, t);
|
||||
@ -924,16 +901,11 @@ function appendProcessReportsElements(aP, aProcess, aReports,
|
||||
appendTreeElements(aP, t, aProcess);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Although we skip the "smaps" multi-reporter in getReportsByProcess(),
|
||||
// we might get some smaps reports from a child process, and they must be
|
||||
// explicitly ignored.
|
||||
ignoreSmapsTrees(aReports);
|
||||
}
|
||||
|
||||
// We have to call appendOtherElements after we process all the trees,
|
||||
// because it looks at all the reports which aren't part of a tree.
|
||||
appendOtherElements(aP, aReports);
|
||||
appendOtherElements(aP, aOthers);
|
||||
|
||||
// Add any warnings about inaccuracies due to platform limitations.
|
||||
// These must be computed after generating all the text. The newlines give
|
||||
@ -1060,18 +1032,21 @@ function pad(aS, aN, aC)
|
||||
return padding + aS;
|
||||
}
|
||||
|
||||
// There's a subset of the Unicode "light" box-drawing chars that are widely
|
||||
// There's a subset of the Unicode "light" box-drawing chars that is widely
|
||||
// implemented in terminals, and this code sticks to that subset to maximize
|
||||
// the chance that cutting and pasting about:memory output to a terminal will
|
||||
// work correctly:
|
||||
const kHorizontal = "\u2500",
|
||||
kVertical = "\u2502",
|
||||
kUpAndRight = "\u2514",
|
||||
kVerticalAndRight = "\u251c",
|
||||
kNoKidsSep = " \u2500\u2500 ",
|
||||
kHideKidsSep = " ++ ",
|
||||
kShowKidsSep = " -- ";
|
||||
// work correctly.
|
||||
const kHorizontal = "\u2500",
|
||||
kVertical = "\u2502",
|
||||
kUpAndRight = "\u2514",
|
||||
kUpAndRight_Right_Right = "\u2514\u2500\u2500",
|
||||
kVerticalAndRight = "\u251c",
|
||||
kVerticalAndRight_Right_Right = "\u251c\u2500\u2500",
|
||||
kVertical_Space_Space = "\u2502 ";
|
||||
|
||||
const kNoKidsSep = " \u2500\u2500 ",
|
||||
kHideKidsSep = " ++ ",
|
||||
kShowKidsSep = " -- ";
|
||||
|
||||
function appendMrValueSpan(aP, aValue, aIsInvalid)
|
||||
{
|
||||
@ -1079,35 +1054,29 @@ function appendMrValueSpan(aP, aValue, aIsInvalid)
|
||||
aValue);
|
||||
}
|
||||
|
||||
function kindToString(aKind)
|
||||
function appendMrNameSpan(aP, aDescription, aUnsafeName, aIsInvalid, aNMerged)
|
||||
{
|
||||
switch (aKind) {
|
||||
case KIND_NONHEAP: return "(Non-heap) ";
|
||||
case KIND_HEAP: return "(Heap) ";
|
||||
case KIND_OTHER:
|
||||
case undefined: return "";
|
||||
default: assert(false, "bad kind in kindToString");
|
||||
let safeName = flipBackslashes(aUnsafeName);
|
||||
if (!aIsInvalid && !aNMerged) {
|
||||
safeName += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
function appendMrNameSpan(aP, aKind, aSep, aDescription, aUnsafeName,
|
||||
aIsInvalid, aNMerged)
|
||||
{
|
||||
appendElementWithText(aP, "span", "mrSep", aSep);
|
||||
|
||||
let nameSpan = appendElementWithText(aP, "span", "mrName",
|
||||
flipBackslashes(aUnsafeName));
|
||||
nameSpan.title = kindToString(aKind) + aDescription;
|
||||
let nameSpan = appendElementWithText(aP, "span", "mrName", safeName);
|
||||
nameSpan.title = aDescription;
|
||||
|
||||
if (aIsInvalid) {
|
||||
let noteSpan = appendElementWithText(aP, "span", "mrNote", " [?!]");
|
||||
let noteText = " [?!]";
|
||||
if (!aNMerged) {
|
||||
noteText += "\n";
|
||||
}
|
||||
let noteSpan = appendElementWithText(aP, "span", "mrNote", noteText);
|
||||
noteSpan.title =
|
||||
"Warning: this value is invalid and indicates a bug in one or more " +
|
||||
"memory reporters. ";
|
||||
}
|
||||
|
||||
if (aNMerged) {
|
||||
let noteSpan = appendElementWithText(aP, "span", "mrNote",
|
||||
" [" + aNMerged + "]");
|
||||
" [" + aNMerged + "]\n");
|
||||
noteSpan.title =
|
||||
"This value is the sum of " + aNMerged +
|
||||
" memory reporters that all have the same path.";
|
||||
@ -1136,7 +1105,7 @@ function toggle(aEvent)
|
||||
// right nodes. And the span containing the children of this line must
|
||||
// immediately follow. Assertions check this.
|
||||
|
||||
// |aEvent.target| will be one of the five spans. Get the outer span.
|
||||
// |aEvent.target| will be one of the spans. Get the outer span.
|
||||
let outerSpan = aEvent.target.parentNode;
|
||||
assertClassListContains(outerSpan, "hasKids");
|
||||
|
||||
@ -1196,7 +1165,6 @@ function expandPathToThisElement(aElement)
|
||||
* The tree.
|
||||
* @param aProcess
|
||||
* The process the tree corresponds to.
|
||||
* @return The generated text.
|
||||
*/
|
||||
function appendTreeElements(aPOuter, aT, aProcess)
|
||||
{
|
||||
@ -1209,78 +1177,74 @@ function appendTreeElements(aPOuter, aT, aProcess)
|
||||
*
|
||||
* @param aP
|
||||
* The parent DOM node.
|
||||
* @param aUnsafePrePath
|
||||
* The partial unsafePath leading up to this node.
|
||||
* @param aUnsafeNames
|
||||
* An array of the names forming the path to aT.
|
||||
* @param aT
|
||||
* The tree.
|
||||
* @param aBaseIndentText
|
||||
* The base text of the indent, which may be augmented within the
|
||||
* function.
|
||||
* @param aIndentGuide
|
||||
* Records what indentation is required for this tree. It has one
|
||||
* entry per level of indentation. For each entry, ._isLastKid
|
||||
* records whether the node in question is the last child, and
|
||||
* ._depth records how many chars of indentation are required.
|
||||
* @param aTreelineText1
|
||||
* The first part of the treeline for this entry and this entry's
|
||||
* children.
|
||||
* @param aTreelineText2a
|
||||
* The second part of the treeline for this entry.
|
||||
* @param aTreelineText2b
|
||||
* The second part of the treeline for this entry's children.
|
||||
* @param aParentStringLength
|
||||
* The length of the formatted byte count of the top node in the tree.
|
||||
* @return The generated text.
|
||||
*/
|
||||
function appendTreeElements2(aP, aUnsafePrePath, aT, aIndentGuide,
|
||||
aBaseIndentText, aParentStringLength)
|
||||
function appendTreeElements2(aP, aUnsafeNames, aT, aTreelineText1,
|
||||
aTreelineText2a, aTreelineText2b,
|
||||
aParentStringLength)
|
||||
{
|
||||
function repeatStr(aA, aC, aN)
|
||||
function appendN(aS, aC, aN)
|
||||
{
|
||||
for (let i = 0; i < aN; i++) {
|
||||
aA.push(aC);
|
||||
aS += aC;
|
||||
}
|
||||
return aS;
|
||||
}
|
||||
|
||||
let unsafePath = aUnsafePrePath + aT._unsafeName;
|
||||
|
||||
// Indent more if this entry is narrower than its parent, and update
|
||||
// aIndentGuide accordingly.
|
||||
let tString = aT.toString();
|
||||
let extraIndentLength = Math.max(aParentStringLength - tString.length, 0);
|
||||
let indentText;
|
||||
if (extraIndentLength > 0) {
|
||||
let extraIndentArray = [];
|
||||
repeatStr(extraIndentArray, kHorizontal, extraIndentLength);
|
||||
aIndentGuide[aIndentGuide.length - 1]._depth += extraIndentLength;
|
||||
indentText = aBaseIndentText + extraIndentArray.join("");
|
||||
// Indent more if this entry is narrower than its parent.
|
||||
let valueText = aT.toString();
|
||||
let extraTreelineLength =
|
||||
Math.max(aParentStringLength - valueText.length, 0);
|
||||
if (extraTreelineLength > 0) {
|
||||
aTreelineText2a =
|
||||
appendN(aTreelineText2a, kHorizontal, extraTreelineLength);
|
||||
aTreelineText2b =
|
||||
appendN(aTreelineText2b, " ", extraTreelineLength);
|
||||
}
|
||||
else {
|
||||
indentText = aBaseIndentText;
|
||||
}
|
||||
appendElementWithText(aP, "span", "treeLine", indentText);
|
||||
let treelineText = aTreelineText1 + aTreelineText2a;
|
||||
appendElementWithText(aP, "span", "treeline", treelineText);
|
||||
|
||||
// Generate the percentage; detect and record invalid values at the same
|
||||
// time.
|
||||
let percText = "";
|
||||
let percText;
|
||||
let tIsInvalid = false;
|
||||
if (aT._amount === treeBytes) {
|
||||
percText = "100.0";
|
||||
percText = " (100.0%)";
|
||||
} else {
|
||||
if (!(0 <= aT._amount && aT._amount <= treeBytes)) {
|
||||
tIsInvalid = true;
|
||||
let unsafePath = aUnsafeNames.join("/");
|
||||
gUnsafePathsWithInvalidValuesForThisProcess.push(unsafePath);
|
||||
reportAssertionFailure("Invalid value for " +
|
||||
flipBackslashes(unsafePath));
|
||||
}
|
||||
percText = (100 * aT._amount / treeBytes).toFixed(2);
|
||||
percText = pad(percText, 5, '0');
|
||||
let num = 100 * aT._amount / treeBytes;
|
||||
let numText = num.toFixed(2);
|
||||
percText = (0 <= num && num < 10 ? " (0" : " (") + numText + "%)";
|
||||
}
|
||||
percText = " (" + percText + "%)";
|
||||
|
||||
// For non-leaf nodes, the entire sub-tree is put within a span so it can
|
||||
// be collapsed if the node is clicked on.
|
||||
let d;
|
||||
let hasKids = aT._kids.length > 0;
|
||||
let sep;
|
||||
let showSubtrees;
|
||||
if (hasKids) {
|
||||
if (aT._kids) {
|
||||
// Determine if we should show the sub-tree below this entry; this
|
||||
// involves reinstating any previous toggling of the sub-tree.
|
||||
let safeTreeId = flipBackslashes(aProcess + ":" + unsafePath);
|
||||
let unsafePath = aUnsafeNames.join("/");
|
||||
let safeTreeId = aProcess + ":" + flipBackslashes(unsafePath);
|
||||
showSubtrees = !aT._hideKids;
|
||||
if (gShowSubtreesBySafeTreeId[safeTreeId] !== undefined) {
|
||||
showSubtrees = gShowSubtreesBySafeTreeId[safeTreeId];
|
||||
@ -1295,15 +1259,12 @@ function appendTreeElements(aPOuter, aT, aProcess)
|
||||
d = aP;
|
||||
}
|
||||
|
||||
appendMrValueSpan(d, tString, tIsInvalid);
|
||||
appendMrValueSpan(d, valueText, tIsInvalid);
|
||||
appendElementWithText(d, "span", "mrPerc", percText);
|
||||
appendElementWithText(d, "span", "mrSep", sep);
|
||||
|
||||
// We don't want to show '(nonheap)' on a tree like 'vsize/', since
|
||||
// the whole tree is non-heap.
|
||||
let kind = isExplicitTree ? aT._kind : undefined;
|
||||
appendMrNameSpan(d, kind, sep, aT._description, aT._unsafeName,
|
||||
appendMrNameSpan(d, aT._description, aT._unsafeName,
|
||||
tIsInvalid, aT._nMerged);
|
||||
appendTextNode(d, "\n");
|
||||
|
||||
// In non-verbose mode, invalid nodes can be hidden in collapsed sub-trees.
|
||||
// But it's good to always see them, so force this.
|
||||
@ -1311,31 +1272,25 @@ function appendTreeElements(aPOuter, aT, aProcess)
|
||||
expandPathToThisElement(d);
|
||||
}
|
||||
|
||||
if (hasKids) {
|
||||
if (aT._kids) {
|
||||
// The 'kids' class is just used for sanity checking in toggle().
|
||||
d = appendElement(aP, "span", showSubtrees ? "kids" : "kids hidden");
|
||||
|
||||
let kidTreelineText1 = aTreelineText1 + aTreelineText2b;
|
||||
for (let i = 0; i < aT._kids.length; i++) {
|
||||
// 3 is the standard depth, the callee adjusts it if necessary.
|
||||
aIndentGuide.push({ _isLastKid: (i === aT._kids.length - 1), _depth: 3 });
|
||||
|
||||
// Generate the base indent.
|
||||
let baseIndentArray = [];
|
||||
if (aIndentGuide.length > 0) {
|
||||
let j;
|
||||
for (j = 0; j < aIndentGuide.length - 1; j++) {
|
||||
baseIndentArray.push(aIndentGuide[j]._isLastKid ? " " : kVertical);
|
||||
repeatStr(baseIndentArray, " ", aIndentGuide[j]._depth - 1);
|
||||
}
|
||||
baseIndentArray.push(aIndentGuide[j]._isLastKid ?
|
||||
kUpAndRight : kVerticalAndRight);
|
||||
repeatStr(baseIndentArray, kHorizontal, aIndentGuide[j]._depth - 1);
|
||||
let kidTreelineText2a, kidTreelineText2b;
|
||||
if (i < aT._kids.length - 1) {
|
||||
kidTreelineText2a = kVerticalAndRight_Right_Right;
|
||||
kidTreelineText2b = kVertical_Space_Space;
|
||||
} else {
|
||||
kidTreelineText2a = kUpAndRight_Right_Right;
|
||||
kidTreelineText2b = " ";
|
||||
}
|
||||
|
||||
let baseIndentText = baseIndentArray.join("");
|
||||
appendTreeElements2(d, unsafePath + "/", aT._kids[i], aIndentGuide,
|
||||
baseIndentText, tString.length);
|
||||
aIndentGuide.pop();
|
||||
aUnsafeNames.push(aT._kids[i]._unsafeName);
|
||||
appendTreeElements2(d, aUnsafeNames, aT._kids[i], kidTreelineText1,
|
||||
kidTreelineText2a, kidTreelineText2b,
|
||||
valueText.length);
|
||||
aUnsafeNames.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1343,7 +1298,7 @@ function appendTreeElements(aPOuter, aT, aProcess)
|
||||
appendSectionHeader(aPOuter, kSectionNames[aT._unsafeName]);
|
||||
|
||||
let pre = appendElement(aPOuter, "pre", "entries");
|
||||
appendTreeElements2(pre, /* prePath = */"", aT, [], "", rootStringLength);
|
||||
appendTreeElements2(pre, [aT._unsafeName], aT, "", "", "", rootStringLength);
|
||||
appendTextNode(aPOuter, "\n"); // gives nice spacing when we cut and paste
|
||||
}
|
||||
|
||||
@ -1351,7 +1306,6 @@ function appendTreeElements(aPOuter, aT, aProcess)
|
||||
|
||||
function OtherReport(aUnsafePath, aUnits, aAmount, aDescription, aNMerged)
|
||||
{
|
||||
// Nb: _kind is not needed, it's always KIND_OTHER.
|
||||
this._unsafePath = aUnsafePath;
|
||||
this._units = aUnits;
|
||||
this._amount = aAmount;
|
||||
@ -1395,33 +1349,24 @@ OtherReport.compare = function(a, b) {
|
||||
*
|
||||
* @param aP
|
||||
* The parent DOM node.
|
||||
* @param aReportsByProcess
|
||||
* Table of Reports for this process, indexed by _unsafePath.
|
||||
* @param aProcess
|
||||
* The process these Reports correspond to.
|
||||
* @return The generated text.
|
||||
* @param aOthers
|
||||
* The "other measurements" for this process.
|
||||
*/
|
||||
function appendOtherElements(aP, aReportsByProcess)
|
||||
function appendOtherElements(aP, aOthers)
|
||||
{
|
||||
appendSectionHeader(aP, kSectionNames['other']);
|
||||
|
||||
let pre = appendElement(aP, "pre", "entries");
|
||||
|
||||
// Generate an array of Report-like elements, stripping out all the
|
||||
// Reports that have already been handled. Also find the width of the
|
||||
// Convert the table of OtherReports to an array. Also find the width of the
|
||||
// widest element, so we can format things nicely.
|
||||
let maxStringLength = 0;
|
||||
let otherReports = [];
|
||||
for (let unsafePath in aReportsByProcess) {
|
||||
let r = aReportsByProcess[unsafePath];
|
||||
if (!r._done) {
|
||||
assert(r._nMerged === undefined, "dup'd OTHER report");
|
||||
let o = new OtherReport(r._unsafePath, r._units, r._amount,
|
||||
r._description);
|
||||
otherReports.push(o);
|
||||
if (o._asString.length > maxStringLength) {
|
||||
maxStringLength = o._asString.length;
|
||||
}
|
||||
for (let unsafePath in aOthers) {
|
||||
let o = aOthers[unsafePath];
|
||||
otherReports.push(o);
|
||||
if (o._asString.length > maxStringLength) {
|
||||
maxStringLength = o._asString.length;
|
||||
}
|
||||
}
|
||||
otherReports.sort(OtherReport.compare);
|
||||
@ -1437,9 +1382,8 @@ function appendOtherElements(aP, aReportsByProcess)
|
||||
flipBackslashes(o._unsafePath));
|
||||
}
|
||||
appendMrValueSpan(pre, pad(o._asString, maxStringLength, ' '), oIsInvalid);
|
||||
appendMrNameSpan(pre, KIND_OTHER, kNoKidsSep, o._description, o._unsafePath,
|
||||
oIsInvalid);
|
||||
appendTextNode(pre, "\n");
|
||||
appendElementWithText(pre, "span", "mrSep", kNoKidsSep);
|
||||
appendMrNameSpan(pre, o._description, o._unsafePath, oIsInvalid);
|
||||
}
|
||||
|
||||
appendTextNode(aP, "\n"); // gives nice spacing when we cut and paste
|
||||
@ -1688,8 +1632,6 @@ function appendProcessAboutCompartmentsElementsHelper(aP, aEntries, aKindString)
|
||||
* Table of Compartments for this process, indexed by _unsafeName.
|
||||
* @param aGhostWindows
|
||||
* Array of window URLs of ghost windows.
|
||||
*
|
||||
* @return The generated text.
|
||||
*/
|
||||
function appendProcessAboutCompartmentsElements(aP, aProcess, aCompartments, aGhostWindows)
|
||||
{
|
||||
|
@ -188,6 +188,7 @@
|
||||
// Check that we can handle "heap-allocated" not being present.
|
||||
f("3rd", "explicit/a/b", HEAP, 333 * MB),
|
||||
f("3rd", "explicit/a/c", HEAP, 444 * MB),
|
||||
f2("3rd", "other1", OTHER, BYTES, 1 * MB),
|
||||
|
||||
// Invalid values (negative, too-big) should be identified.
|
||||
f("4th", "heap-allocated", OTHER, 100 * MB),
|
||||
@ -290,6 +291,7 @@ Explicit Allocations\n\
|
||||
└──333.00 MB (42.86%) ── b\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
1.00 MB ── other1\n\
|
||||
\n\
|
||||
4th Process\n\
|
||||
\n\
|
||||
@ -432,6 +434,7 @@ Explicit Allocations\n\
|
||||
└──349,175,808 B (42.86%) ── b\n\
|
||||
\n\
|
||||
Other Measurements\n\
|
||||
1,048,576 B ── other1\n\
|
||||
\n\
|
||||
4th Process\n\
|
||||
\n\
|
||||
|
@ -678,9 +678,9 @@ ensure_tree_header_cell_widget()
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn);
|
||||
|
||||
/* Use the middle column's header for our button */
|
||||
/* TODO */
|
||||
gTreeHeaderCellWidget = NULL;
|
||||
gTreeHeaderSortArrowWidget = NULL;
|
||||
/* TODO, but they can't be NULL */
|
||||
gTreeHeaderCellWidget = gtk_button_new_with_label("M");
|
||||
gTreeHeaderSortArrowWidget = gtk_button_new();
|
||||
}
|
||||
return MOZ_GTK_SUCCESS;
|
||||
}
|
||||
@ -2259,9 +2259,9 @@ moz_gtk_tabpanels_paint(cairo_t *cr, GdkRectangle* rect,
|
||||
*/
|
||||
/* left side */
|
||||
cairo_save(cr);
|
||||
cairo_rectangle(rect->x, rect->y,
|
||||
cairo_rectangle(cr, rect->x, rect->y,
|
||||
rect->x + rect->width / 2,
|
||||
rect->y + rect->height)
|
||||
rect->y + rect->height);
|
||||
cairo_clip(cr);
|
||||
gtk_render_frame_gap(style, cr,
|
||||
rect->x, rect->y,
|
||||
@ -2271,9 +2271,9 @@ moz_gtk_tabpanels_paint(cairo_t *cr, GdkRectangle* rect,
|
||||
|
||||
/* right side */
|
||||
cairo_save(cr);
|
||||
cairo_rectangle(rect->x + rect->width / 2, rect->y,
|
||||
cairo_rectangle(cr, rect->x + rect->width / 2, rect->y,
|
||||
rect->x + rect->width,
|
||||
rect->y + rect->height)
|
||||
rect->y + rect->height);
|
||||
cairo_clip(cr);
|
||||
gtk_render_frame_gap(style, cr,
|
||||
rect->x, rect->y,
|
||||
@ -2429,17 +2429,19 @@ moz_gtk_menu_item_paint(cairo_t *cr, GdkRectangle* rect,
|
||||
GtkWidget* item_widget;
|
||||
|
||||
if (state->inHover && !state->disabled) {
|
||||
gtk_style_context_save(style);
|
||||
style = gtk_widget_get_style_context(item_widget);
|
||||
|
||||
if (flags & MOZ_TOPLEVEL_MENU_ITEM) {
|
||||
ensure_menu_bar_item_widget();
|
||||
item_widget = gMenuBarItemWidget;
|
||||
gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR);
|
||||
} else {
|
||||
ensure_menu_item_widget();
|
||||
item_widget = gMenuItemWidget;
|
||||
}
|
||||
style = gtk_widget_get_style_context(item_widget);
|
||||
gtk_style_context_save(style);
|
||||
|
||||
if (flags & MOZ_TOPLEVEL_MENU_ITEM) {
|
||||
gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR);
|
||||
}
|
||||
|
||||
gtk_widget_set_direction(item_widget, direction);
|
||||
gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUITEM);
|
||||
@ -3247,6 +3249,12 @@ moz_gtk_shutdown()
|
||||
if (gProtoWindow)
|
||||
gtk_widget_destroy(gProtoWindow);
|
||||
|
||||
/* TODO - replace it with appropriate widgets */
|
||||
if (gTreeHeaderCellWidget)
|
||||
gtk_widget_destroy(gTreeHeaderCellWidget);
|
||||
if (gTreeHeaderSortArrowWidget)
|
||||
gtk_widget_destroy(gTreeHeaderSortArrowWidget);
|
||||
|
||||
gProtoWindow = NULL;
|
||||
gProtoLayout = NULL;
|
||||
gButtonWidget = NULL;
|
||||
|
@ -1502,9 +1502,10 @@ nsWindow::GetClientOffset()
|
||||
int format_returned;
|
||||
int length_returned;
|
||||
long *frame_extents;
|
||||
GdkWindow* window;
|
||||
|
||||
if (!mShell || !mShell->window ||
|
||||
!gdk_property_get(mShell->window,
|
||||
if (!mShell || !(window = gtk_widget_get_window(mShell)) ||
|
||||
!gdk_property_get(window,
|
||||
gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
|
||||
cardinal_atom,
|
||||
0, // offset
|
||||
|
@ -131,7 +131,7 @@ GfxInfo::GetData()
|
||||
|
||||
// only useful for Linux kernel version check for FGLRX driver.
|
||||
// assumes X client == X server, which is sad.
|
||||
utsname unameobj;
|
||||
struct utsname unameobj;
|
||||
if (!uname(&unameobj))
|
||||
{
|
||||
mOS.Assign(unameobj.sysname);
|
||||
|
Loading…
Reference in New Issue
Block a user