diff --git a/accessible/src/base/AccIterator.cpp b/accessible/src/base/AccIterator.cpp index d230ed8a41b..19165e12f1a 100644 --- a/accessible/src/base/AccIterator.cpp +++ b/accessible/src/base/AccIterator.cpp @@ -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 refDOMElm; - nsCOMPtr xblDocument = - do_QueryInterface(mContent->OwnerDoc()); // Check inside the binding the element is contained in. nsIContent* bindingParent = mContent->GetBindingParent(); if (bindingParent) { - nsCOMPtr bindingParentElm = do_QueryInterface(bindingParent); - xblDocument->GetAnonymousElementByAttribute(bindingParentElm, - NS_LITERAL_STRING("anonid"), - aID, - getter_AddRefs(refDOMElm)); - nsCOMPtr 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 elm = do_QueryInterface(mContent); - xblDocument->GetAnonymousElementByAttribute(elm, - NS_LITERAL_STRING("anonid"), - aID, - getter_AddRefs(refDOMElm)); - nsCOMPtr refElm = do_QueryInterface(refDOMElm); - return refElm; + return mContent->OwnerDoc()-> + GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, aID); } return nsnull; diff --git a/accessible/src/base/AccIterator.h b/accessible/src/base/AccIterator.h index cc7454f874b..60ff066826d 100644 --- a/accessible/src/base/AccIterator.h +++ b/accessible/src/base/AccIterator.h @@ -12,8 +12,6 @@ #include "nscore.h" #include "DocAccessible.h" -#include "nsIDOMDocumentXBL.h" - /** * AccIterable is a basic interface for iterators over accessibles. */ diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index 798a75541ed..a1555ad709f 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -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 - nsCOMPtr xblDoc(do_QueryInterface(xulDoc)); - if (xblDoc) { - nsCOMPtr domDoc = do_QueryInterface(xulDoc); - NS_ASSERTION(domDoc, "No DOM document"); - nsCOMPtr rootEl; - domDoc->GetDocumentElement(getter_AddRefs(rootEl)); - if (rootEl) { - nsCOMPtr 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 relatedContent(do_QueryInterface(buttonEl)); diff --git a/accessible/src/xul/nsXULSliderAccessible.cpp b/accessible/src/xul/nsXULSliderAccessible.cpp index 9332a9e185d..150a7a15d3a 100644 --- a/accessible/src/xul/nsXULSliderAccessible.cpp +++ b/accessible/src/xul/nsXULSliderAccessible.cpp @@ -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 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 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 -nsXULSliderAccessible::GetSliderNode() +nsIContent* +nsXULSliderAccessible::GetSliderElement() { - if (IsDefunct()) - return nsnull; - if (!mSliderNode) { - nsCOMPtr xblDoc(do_QueryInterface(mContent->OwnerDoc())); - if (!xblDoc) - return nsnull; - // XXX: we depend on anonymous content. - nsCOMPtr 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 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 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; } diff --git a/accessible/src/xul/nsXULSliderAccessible.h b/accessible/src/xul/nsXULSliderAccessible.h index 9616a1374be..6c50e724a6b 100644 --- a/accessible/src/xul/nsXULSliderAccessible.h +++ b/accessible/src/xul/nsXULSliderAccessible.h @@ -38,7 +38,10 @@ public: virtual PRUint8 ActionCount(); protected: - already_AddRefed 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 mSliderNode; + nsCOMPtr mSliderNode; }; diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 3506f16327e..c366479997d 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -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; } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 0ffe0513dc6..037f85d37a6 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -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 diff --git a/browser/themes/gnomestripe/browser.css b/browser/themes/gnomestripe/browser.css index 3b60a7978ad..ca32fe06730 100644 --- a/browser/themes/gnomestripe/browser.css +++ b/browser/themes/gnomestripe/browser.css @@ -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; diff --git a/browser/themes/pinstripe/browser.css b/browser/themes/pinstripe/browser.css index 02bf8393713..0a42656418e 100644 --- a/browser/themes/pinstripe/browser.css +++ b/browser/themes/pinstripe/browser.css @@ -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"] { diff --git a/browser/themes/winstripe/browser.css b/browser/themes/winstripe/browser.css index d5263d6a93a..8d97549402d 100644 --- a/browser/themes/winstripe/browser.css +++ b/browser/themes/winstripe/browser.css @@ -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 */ diff --git a/config/solaris_ia32.map b/config/solaris_ia32.map index cdcbf580468..be6ea3736e7 100644 --- a/config/solaris_ia32.map +++ b/config/solaris_ia32.map @@ -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; diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index f35af196b34..9c1f95b3a86 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -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. diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index b2dce607362..ab48ddf1894 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -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 nodeList; - GetAnonymousNodes(aElement, getter_AddRefs(nodeList)); - - if (!nodeList) - return NS_OK; - nsCOMPtr attribute = do_GetAtom(aAttrName); + nsCOMPtr content(do_QueryInterface(aElement)); - PRUint32 length; - nodeList->GetLength(&length); - - bool universalMatch = aAttrValue.EqualsLiteral("*"); - - for (PRUint32 i = 0; i < length; ++i) { - nsCOMPtr current; - nodeList->Item(i, getter_AddRefs(current)); - - nsCOMPtr 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; } diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 0f1d9b25781..1d505fa5728 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -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, diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h index 042b9b70975..021993a9810 100644 --- a/content/html/content/public/nsHTMLMediaElement.h +++ b/content/html/content/public/nsHTMLMediaElement.h @@ -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 diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index f3acf523c85..95cb1c26c22 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -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 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) { diff --git a/content/media/nsBuiltinDecoder.cpp b/content/media/nsBuiltinDecoder.cpp index 889bec66aec..8d182887f0e 100644 --- a/content/media/nsBuiltinDecoder.cpp +++ b/content/media/nsBuiltinDecoder.cpp @@ -647,11 +647,15 @@ 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(); + } } void nsBuiltinDecoder::NotifyBytesDownloaded() diff --git a/gfx/thebes/gfxReusableSurfaceWrapper.h b/gfx/thebes/gfxReusableSurfaceWrapper.h index c0334d67ea3..3aa5740abe1 100644 --- a/gfx/thebes/gfxReusableSurfaceWrapper.h +++ b/gfx/thebes/gfxReusableSurfaceWrapper.h @@ -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. diff --git a/intl/locale/src/unix/Makefile.in b/intl/locale/src/unix/Makefile.in index 26c5909d305..5accdf9a06d 100644 --- a/intl/locale/src/unix/Makefile.in +++ b/intl/locale/src/unix/Makefile.in @@ -22,7 +22,7 @@ CPPSRCS = \ nsPosixLocale.cpp \ $(NULL) -ifeq (android,$(MOZ_WIDGET_TOOLKIT)) +ifeq (Android,$(OS_TARGET)) CPPSRCS += nsAndroidCharset.cpp else CPPSRCS += nsUNIXCharset.cpp diff --git a/js/src/config/solaris_ia32.map b/js/src/config/solaris_ia32.map index cdcbf580468..be6ea3736e7 100644 --- a/js/src/config/solaris_ia32.map +++ b/js/src/config/solaris_ia32.map @@ -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; diff --git a/layout/mathml/nsMathMLmpaddedFrame.cpp b/layout/mathml/nsMathMLmpaddedFrame.cpp index c8924455896..de21b7ff9d2 100644 --- a/layout/mathml/nsMathMLmpaddedFrame.cpp +++ b/layout/mathml/nsMathMLmpaddedFrame.cpp @@ -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 diff --git a/layout/svg/base/src/nsSVGContainerFrame.h b/layout/svg/base/src/nsSVGContainerFrame.h index 3c6fea1a4b6..2ca7e165f80 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.h +++ b/layout/svg/base/src/nsSVGContainerFrame.h @@ -14,6 +14,7 @@ #include "nsISVGChildFrame.h" #include "nsQueryFrame.h" #include "nsRect.h" +#include "nsSVGUtils.h" class nsFrameList; class nsIContent; diff --git a/media/libopus/Makefile.in b/media/libopus/Makefile.in index 3ebcf897e85..afe15c5536c 100644 --- a/media/libopus/Makefile.in +++ b/media/libopus/Makefile.in @@ -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 diff --git a/netwerk/protocol/http/PHttpChannelParams.h b/netwerk/protocol/http/PHttpChannelParams.h index 7da9fb8e4f8..3590592e124 100644 --- a/netwerk/protocol/http/PHttpChannelParams.h +++ b/netwerk/protocol/http/PHttpChannelParams.h @@ -104,6 +104,14 @@ struct ParamTraits } }; + +template<> +struct ParamTraits + : public ParamTraits +{ +}; + + template<> struct ParamTraits { diff --git a/netwerk/protocol/http/nsHttpHeaderArray.cpp b/netwerk/protocol/http/nsHttpHeaderArray.cpp index 76e18157f31..edf9da9787b 100644 --- a/netwerk/protocol/http/nsHttpHeaderArray.cpp +++ b/netwerk/protocol/http/nsHttpHeaderArray.cpp @@ -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]; diff --git a/netwerk/protocol/http/nsHttpHeaderArray.h b/netwerk/protocol/http/nsHttpHeaderArray.h index 878b9d9d30f..dbf291d2b64 100644 --- a/netwerk/protocol/http/nsHttpHeaderArray.h +++ b/netwerk/protocol/http/nsHttpHeaderArray.h @@ -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 mHeaders; friend struct IPC::ParamTraits; @@ -99,6 +129,15 @@ private: // nsHttpHeaderArray : 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) { diff --git a/netwerk/protocol/http/nsHttpRequestHead.h b/netwerk/protocol/http/nsHttpRequestHead.h index d514f0637a4..7a5e1e7a144 100644 --- a/netwerk/protocol/http/nsHttpRequestHead.h +++ b/netwerk/protocol/http/nsHttpRequestHead.h @@ -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__ diff --git a/netwerk/protocol/http/nsHttpResponseHead.cpp b/netwerk/protocol/http/nsHttpResponseHead.cpp index 63babbf901a..8c31e58ecf1 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp @@ -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) diff --git a/netwerk/protocol/http/nsHttpResponseHead.h b/netwerk/protocol/http/nsHttpResponseHead.h index aa25d30a2a9..024fddf5032 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.h +++ b/netwerk/protocol/http/nsHttpResponseHead.h @@ -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; diff --git a/security/manager/ssl/src/SSLServerCertVerification.cpp b/security/manager/ssl/src/SSLServerCertVerification.cpp index 7078ef6bf0b..4ec87495a19 100644 --- a/security/manager/ssl/src/SSLServerCertVerification.cpp +++ b/security/manager/ssl/src/SSLServerCertVerification.cpp @@ -1113,6 +1113,12 @@ AuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true"); nsNSSSocketInfo *socketInfo = static_cast(arg); + + if (socketInfo) { + // This is the first callback during full handshakes. + socketInfo->SetFirstServerHelloReceived(); + } + CERTCertificate *serverCert = SSL_PeerCertificate(fd); CERTCertificateCleaner serverCertCleaner(serverCert); diff --git a/security/manager/ssl/src/nsNSSCallbacks.cpp b/security/manager/ssl/src/nsNSSCallbacks.cpp index 1c8541461dd..c0c5cc189c9 100644 --- a/security/manager/ssl/src/nsNSSCallbacks.cpp +++ b/security/manager/ssl/src/nsNSSCallbacks.cpp @@ -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); diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index cb4b2c33f0e..7c645ad9a36 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -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() diff --git a/security/manager/ssl/src/nsNSSIOLayer.h b/security/manager/ssl/src/nsNSSIOLayer.h index be3c66c0d87..7c42f4ea5d3 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.h +++ b/security/manager/ssl/src/nsNSSIOLayer.h @@ -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(); diff --git a/toolkit/components/aboutmemory/content/aboutMemory.css b/toolkit/components/aboutmemory/content/aboutMemory.css index 77d2fdbd6b5..7b0eed7d9b0 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.css +++ b/toolkit/components/aboutmemory/content/aboutMemory.css @@ -23,7 +23,7 @@ h2 { color: #f00; } -.treeLine { +.treeline { color: #888; } diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 12dc39677ee..f8c275a2097 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -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) { diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul index 07651446333..0715309db61 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -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\ diff --git a/widget/gtk2/gtk3drawing.c b/widget/gtk2/gtk3drawing.c index 7622cbf20fe..63419ea6461 100644 --- a/widget/gtk2/gtk3drawing.c +++ b/widget/gtk2/gtk3drawing.c @@ -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, @@ -2428,19 +2428,21 @@ moz_gtk_menu_item_paint(cairo_t *cr, GdkRectangle* rect, GtkStyleContext* style; GtkWidget* item_widget; - if (state->inHover && !state->disabled) { - gtk_style_context_save(style); - style = gtk_widget_get_style_context(item_widget); - + if (state->inHover && !state->disabled) { 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); gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); @@ -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; diff --git a/widget/gtk2/nsWindow.cpp b/widget/gtk2/nsWindow.cpp index 2b4cb8fe6c8..190256db921 100644 --- a/widget/gtk2/nsWindow.cpp +++ b/widget/gtk2/nsWindow.cpp @@ -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 diff --git a/widget/xpwidgets/GfxInfoX11.cpp b/widget/xpwidgets/GfxInfoX11.cpp index af7558b32e6..57a6e2858dc 100644 --- a/widget/xpwidgets/GfxInfoX11.cpp +++ b/widget/xpwidgets/GfxInfoX11.cpp @@ -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);