mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Merge last green PGO from inbound to central
This commit is contained in:
commit
2b24ab86ca
@ -162,12 +162,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void PresShellDestroyed(nsIPresShell *aPresShell) = 0;
|
virtual void PresShellDestroyed(nsIPresShell *aPresShell) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Recreate an accessible for the given content node in the presshell.
|
|
||||||
*/
|
|
||||||
virtual void RecreateAccessible(nsIPresShell* aPresShell,
|
|
||||||
nsIContent* aContent) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fire accessible event of the given type for the given target.
|
* Fire accessible event of the given type for the given target.
|
||||||
*
|
*
|
||||||
|
@ -277,8 +277,10 @@ XULDescriptionIterator::Next()
|
|||||||
// IDRefsIterator
|
// IDRefsIterator
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
IDRefsIterator::IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr) :
|
IDRefsIterator::
|
||||||
mCurrIdx(0), mContent(aContent)
|
IDRefsIterator(nsDocAccessible* aDoc, nsIContent* aContent,
|
||||||
|
nsIAtom* aIDRefsAttr) :
|
||||||
|
mCurrIdx(0), mContent(aContent), mDoc(aDoc)
|
||||||
{
|
{
|
||||||
if (mContent->IsInDoc())
|
if (mContent->IsInDoc())
|
||||||
mContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs);
|
mContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs);
|
||||||
@ -368,7 +370,7 @@ nsAccessible*
|
|||||||
IDRefsIterator::Next()
|
IDRefsIterator::Next()
|
||||||
{
|
{
|
||||||
nsIContent* nextElm = NextElem();
|
nsIContent* nextElm = NextElem();
|
||||||
return nextElm ? GetAccService()->GetAccessible(nextElm, nsnull) : nsnull;
|
return nextElm ? mDoc->GetAccessible(nextElm) : nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAccessible*
|
nsAccessible*
|
||||||
|
@ -264,7 +264,8 @@ private:
|
|||||||
class IDRefsIterator : public AccIterable
|
class IDRefsIterator : public AccIterable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr);
|
IDRefsIterator(nsDocAccessible* aDoc, nsIContent* aContent,
|
||||||
|
nsIAtom* aIDRefsAttr);
|
||||||
virtual ~IDRefsIterator() { }
|
virtual ~IDRefsIterator() { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,6 +293,7 @@ private:
|
|||||||
|
|
||||||
nsString mIDs;
|
nsString mIDs;
|
||||||
nsIContent* mContent;
|
nsIContent* mContent;
|
||||||
|
nsDocAccessible* mDoc;
|
||||||
nsAString::index_type mCurrIdx;
|
nsAString::index_type mCurrIdx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -335,6 +335,15 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
|
|||||||
kNoReqStates,
|
kNoReqStates,
|
||||||
eARIACheckableBool
|
eARIACheckableBool
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"note",
|
||||||
|
roles::NOTE,
|
||||||
|
kUseMapRole,
|
||||||
|
eNoValue,
|
||||||
|
eNoAction,
|
||||||
|
eNoLiveAttr,
|
||||||
|
kNoReqStates
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"option",
|
"option",
|
||||||
roles::OPTION,
|
roles::OPTION,
|
||||||
|
@ -676,10 +676,8 @@ nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell,
|
|||||||
nsIContent* aContent)
|
nsIContent* aContent)
|
||||||
{
|
{
|
||||||
nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument());
|
nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument());
|
||||||
if (document) {
|
if (document)
|
||||||
document->HandleNotification<nsDocAccessible, nsIContent>
|
document->RecreateAccessible(aContent);
|
||||||
(document, &nsDocAccessible::RecreateAccessible, aContent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -173,8 +173,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void PresShellActivated(nsIPresShell* aPresShell);
|
virtual void PresShellActivated(nsIPresShell* aPresShell);
|
||||||
|
|
||||||
virtual void RecreateAccessible(nsIPresShell* aPresShell,
|
/**
|
||||||
nsIContent* aContent);
|
* Recreate an accessible for the given content node in the presshell.
|
||||||
|
*/
|
||||||
|
void RecreateAccessible(nsIPresShell* aPresShell, nsIContent* aContent);
|
||||||
|
|
||||||
virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget);
|
virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget);
|
||||||
|
|
||||||
|
@ -2015,14 +2015,14 @@ nsAccessible::RelationByType(PRUint32 aType)
|
|||||||
Relation rel(new RelatedAccIterator(Document(), mContent,
|
Relation rel(new RelatedAccIterator(Document(), mContent,
|
||||||
nsGkAtoms::aria_labelledby));
|
nsGkAtoms::aria_labelledby));
|
||||||
if (mContent->Tag() == nsGkAtoms::label)
|
if (mContent->Tag() == nsGkAtoms::label)
|
||||||
rel.AppendIter(new IDRefsIterator(mContent, mContent->IsHTML() ?
|
rel.AppendIter(new IDRefsIterator(mDoc, mContent, mContent->IsHTML() ?
|
||||||
nsGkAtoms::_for :
|
nsGkAtoms::_for :
|
||||||
nsGkAtoms::control));
|
nsGkAtoms::control));
|
||||||
|
|
||||||
return rel;
|
return rel;
|
||||||
}
|
}
|
||||||
case nsIAccessibleRelation::RELATION_LABELLED_BY: {
|
case nsIAccessibleRelation::RELATION_LABELLED_BY: {
|
||||||
Relation rel(new IDRefsIterator(mContent,
|
Relation rel(new IDRefsIterator(mDoc, mContent,
|
||||||
nsGkAtoms::aria_labelledby));
|
nsGkAtoms::aria_labelledby));
|
||||||
if (mContent->IsHTML()) {
|
if (mContent->IsHTML()) {
|
||||||
rel.AppendIter(new HTMLLabelIterator(Document(), this));
|
rel.AppendIter(new HTMLLabelIterator(Document(), this));
|
||||||
@ -2033,8 +2033,8 @@ nsAccessible::RelationByType(PRUint32 aType)
|
|||||||
return rel;
|
return rel;
|
||||||
}
|
}
|
||||||
case nsIAccessibleRelation::RELATION_DESCRIBED_BY: {
|
case nsIAccessibleRelation::RELATION_DESCRIBED_BY: {
|
||||||
Relation rel(new IDRefsIterator(mContent,
|
Relation rel(new IDRefsIterator(mDoc, mContent,
|
||||||
nsGkAtoms::aria_describedby));
|
nsGkAtoms::aria_describedby));
|
||||||
if (mContent->IsXUL())
|
if (mContent->IsXUL())
|
||||||
rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
|
rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
|
||||||
|
|
||||||
@ -2049,7 +2049,7 @@ nsAccessible::RelationByType(PRUint32 aType)
|
|||||||
// tied to a control.
|
// tied to a control.
|
||||||
if (mContent->Tag() == nsGkAtoms::description &&
|
if (mContent->Tag() == nsGkAtoms::description &&
|
||||||
mContent->IsXUL())
|
mContent->IsXUL())
|
||||||
rel.AppendIter(new IDRefsIterator(mContent,
|
rel.AppendIter(new IDRefsIterator(mDoc, mContent,
|
||||||
nsGkAtoms::control));
|
nsGkAtoms::control));
|
||||||
|
|
||||||
return rel;
|
return rel;
|
||||||
@ -2091,13 +2091,13 @@ nsAccessible::RelationByType(PRUint32 aType)
|
|||||||
return Relation(new RelatedAccIterator(Document(), mContent,
|
return Relation(new RelatedAccIterator(Document(), mContent,
|
||||||
nsGkAtoms::aria_controls));
|
nsGkAtoms::aria_controls));
|
||||||
case nsIAccessibleRelation::RELATION_CONTROLLER_FOR: {
|
case nsIAccessibleRelation::RELATION_CONTROLLER_FOR: {
|
||||||
Relation rel(new IDRefsIterator(mContent,
|
Relation rel(new IDRefsIterator(mDoc, mContent,
|
||||||
nsGkAtoms::aria_controls));
|
nsGkAtoms::aria_controls));
|
||||||
rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
|
rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
|
||||||
return rel;
|
return rel;
|
||||||
}
|
}
|
||||||
case nsIAccessibleRelation::RELATION_FLOWS_TO:
|
case nsIAccessibleRelation::RELATION_FLOWS_TO:
|
||||||
return Relation(new IDRefsIterator(mContent,
|
return Relation(new IDRefsIterator(mDoc, mContent,
|
||||||
nsGkAtoms::aria_flowto));
|
nsGkAtoms::aria_flowto));
|
||||||
case nsIAccessibleRelation::RELATION_FLOWS_FROM:
|
case nsIAccessibleRelation::RELATION_FLOWS_FROM:
|
||||||
return Relation(new RelatedAccIterator(Document(), mContent,
|
return Relation(new RelatedAccIterator(Document(), mContent,
|
||||||
|
@ -1467,14 +1467,8 @@ nsDocAccessible::RecreateAccessible(nsIContent* aContent)
|
|||||||
// coalescence with normal hide and show events. Note, in this case they
|
// coalescence with normal hide and show events. Note, in this case they
|
||||||
// should be coalesced with normal show/hide events.
|
// should be coalesced with normal show/hide events.
|
||||||
|
|
||||||
// Check if the node is in accessible document.
|
ContentRemoved(aContent->GetParent(), aContent);
|
||||||
nsAccessible* container = GetContainerAccessible(aContent);
|
ContentInserted(aContent->GetParent(), aContent, aContent->GetNextSibling());
|
||||||
if (container) {
|
|
||||||
// Remove and reinsert.
|
|
||||||
UpdateTree(container, aContent, false);
|
|
||||||
container->UpdateChildren();
|
|
||||||
UpdateTree(container, aContent, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1623,7 +1617,7 @@ nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
|
IDRefsIterator iter(this, aRelProvider->GetContent(), relAttr);
|
||||||
while (true) {
|
while (true) {
|
||||||
const nsDependentSubstring id = iter.NextID();
|
const nsDependentSubstring id = iter.NextID();
|
||||||
if (id.IsEmpty())
|
if (id.IsEmpty())
|
||||||
@ -1674,7 +1668,7 @@ nsDocAccessible::RemoveDependentIDsFor(nsAccessible* aRelProvider,
|
|||||||
if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
|
if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
|
IDRefsIterator iter(this, aRelProvider->GetContent(), relAttr);
|
||||||
while (true) {
|
while (true) {
|
||||||
const nsDependentSubstring id = iter.NextID();
|
const nsDependentSubstring id = iter.NextID();
|
||||||
if (id.IsEmpty())
|
if (id.IsEmpty())
|
||||||
@ -1717,8 +1711,7 @@ nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
|||||||
// Recreate the accessible when role is changed because we might require a
|
// Recreate the accessible when role is changed because we might require a
|
||||||
// different accessible class for the new role or the accessible may expose
|
// different accessible class for the new role or the accessible may expose
|
||||||
// a different sets of interfaces (COM restriction).
|
// a different sets of interfaces (COM restriction).
|
||||||
HandleNotification<nsDocAccessible, nsIContent>
|
RecreateAccessible(aElement);
|
||||||
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1729,11 +1722,9 @@ nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
|||||||
// kill use to recreate the accessible even if the attribute was used in
|
// kill use to recreate the accessible even if the attribute was used in
|
||||||
// the wrong namespace or an element that doesn't support it.
|
// the wrong namespace or an element that doesn't support it.
|
||||||
|
|
||||||
// Recreate accessible asynchronously to allow the content to handle
|
// Make sure the accessible is recreated asynchronously to allow the content
|
||||||
// the attribute change.
|
// to handle the attribute change.
|
||||||
mNotificationController->ScheduleNotification<nsDocAccessible, nsIContent>
|
RecreateAccessible(aElement);
|
||||||
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1742,8 +1733,7 @@ nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
|||||||
// This affects whether the accessible supports SelectAccessible.
|
// This affects whether the accessible supports SelectAccessible.
|
||||||
// COM says we cannot change what interfaces are supported on-the-fly,
|
// COM says we cannot change what interfaces are supported on-the-fly,
|
||||||
// so invalidate this object. A new one will be created on demand.
|
// so invalidate this object. A new one will be created on demand.
|
||||||
HandleNotification<nsDocAccessible, nsIContent>
|
RecreateAccessible(aElement);
|
||||||
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ nsTextEquivUtils::GetTextEquivFromIDRefs(nsAccessible *aAccessible,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
nsIContent* refContent = nsnull;
|
nsIContent* refContent = nsnull;
|
||||||
IDRefsIterator iter(content, aIDRefsAttr);
|
IDRefsIterator iter(aAccessible->Document(), content, aIDRefsAttr);
|
||||||
while ((refContent = iter.NextElem())) {
|
while ((refContent = iter.NextElem())) {
|
||||||
if (!aTextEquiv.IsEmpty())
|
if (!aTextEquiv.IsEmpty())
|
||||||
aTextEquiv += ' ';
|
aTextEquiv += ' ';
|
||||||
|
@ -334,7 +334,7 @@ nsHTMLTableCellAccessible::GetHeaderCells(PRInt32 aRowOrColumnHeaderCell,
|
|||||||
nsIArray **aHeaderCells)
|
nsIArray **aHeaderCells)
|
||||||
{
|
{
|
||||||
// Get header cells from @header attribute.
|
// Get header cells from @header attribute.
|
||||||
IDRefsIterator iter(mContent, nsGkAtoms::headers);
|
IDRefsIterator iter(mDoc, mContent, nsGkAtoms::headers);
|
||||||
nsIContent* headerCellElm = iter.NextElem();
|
nsIContent* headerCellElm = iter.NextElem();
|
||||||
if (headerCellElm) {
|
if (headerCellElm) {
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
@ -204,7 +204,7 @@ nsHTMLOutputAccessible::RelationByType(PRUint32 aType)
|
|||||||
{
|
{
|
||||||
Relation rel = nsAccessibleWrap::RelationByType(aType);
|
Relation rel = nsAccessibleWrap::RelationByType(aType);
|
||||||
if (aType == nsIAccessibleRelation::RELATION_CONTROLLED_BY)
|
if (aType == nsIAccessibleRelation::RELATION_CONTROLLED_BY)
|
||||||
rel.AppendIter(new IDRefsIterator(mContent, nsGkAtoms::_for));
|
rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::_for));
|
||||||
|
|
||||||
return rel;
|
return rel;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=529289
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// misc roles
|
// misc roles
|
||||||
|
testRole("note", ROLE_NOTE);
|
||||||
testRole("scrollbar", ROLE_SCROLLBAR);
|
testRole("scrollbar", ROLE_SCROLLBAR);
|
||||||
testRole("dir", ROLE_LIST);
|
testRole("dir", ROLE_LIST);
|
||||||
|
|
||||||
@ -154,6 +155,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=529289
|
|||||||
<div role="sectionhead" id="sectionhead">sectionhead</div>
|
<div role="sectionhead" id="sectionhead">sectionhead</div>
|
||||||
|
|
||||||
<!-- misc roles -->
|
<!-- misc roles -->
|
||||||
|
<div role="note" id="note">note</div>
|
||||||
<div role="scrollbar" id="scrollbar">scrollbar</div>
|
<div role="scrollbar" id="scrollbar">scrollbar</div>
|
||||||
|
|
||||||
<div id="dir" role="directory">
|
<div id="dir" role="directory">
|
||||||
|
@ -345,6 +345,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hideImageMap(aContainerID, aImageID)
|
||||||
|
{
|
||||||
|
this.container = getAccessible(aContainerID);
|
||||||
|
this.imageMap = null;
|
||||||
|
this.imageMapNode = getNode(aImageID);
|
||||||
|
|
||||||
|
function getImageMap(aThisObj)
|
||||||
|
{
|
||||||
|
return aThisObj.imageMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventSeq = [
|
||||||
|
new invokerChecker(EVENT_HIDE, getImageMap, this),
|
||||||
|
new invokerChecker(EVENT_REORDER, aContainerID)
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function hideImageMap_invoke()
|
||||||
|
{
|
||||||
|
this.imageMap = getAccessible(this.imageMapNode);
|
||||||
|
this.imageMapNode.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.finalCheck = function hideImageMap_finalCheck()
|
||||||
|
{
|
||||||
|
var accTree =
|
||||||
|
{ SECTION: [ ] };
|
||||||
|
testAccessibleTree(this.container, accTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function hideImageMap_getID()
|
||||||
|
{
|
||||||
|
return "display:none image";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//gA11yEventDumpToConsole = true;
|
//gA11yEventDumpToConsole = true;
|
||||||
|
|
||||||
var gQueue = null;
|
var gQueue = null;
|
||||||
@ -359,6 +394,7 @@
|
|||||||
gQueue.push(new restoreNameOnMap("container", "imgmap", "map"));
|
gQueue.push(new restoreNameOnMap("container", "imgmap", "map"));
|
||||||
gQueue.push(new removeMap("container", "imgmap", "map"));
|
gQueue.push(new removeMap("container", "imgmap", "map"));
|
||||||
gQueue.push(new insertMap("container", "imgmap"));
|
gQueue.push(new insertMap("container", "imgmap"));
|
||||||
|
gQueue.push(new hideImageMap("container", "imgmap"));
|
||||||
|
|
||||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
@ -2450,7 +2450,6 @@ ia64*-hpux*)
|
|||||||
ZIP=zip
|
ZIP=zip
|
||||||
UNZIP=unzip
|
UNZIP=unzip
|
||||||
DOXYGEN=:
|
DOXYGEN=:
|
||||||
GARBAGE='$(OBJDIR)/vc20.pdb $(OBJDIR)/vc40.pdb'
|
|
||||||
ASM_SUFFIX=asm
|
ASM_SUFFIX=asm
|
||||||
OBJ_SUFFIX=obj
|
OBJ_SUFFIX=obj
|
||||||
LIB_SUFFIX=lib
|
LIB_SUFFIX=lib
|
||||||
|
@ -111,6 +111,7 @@ class imgIRequest;
|
|||||||
class nsISHEntry;
|
class nsISHEntry;
|
||||||
class nsDOMNavigationTiming;
|
class nsDOMNavigationTiming;
|
||||||
class nsWindowSizes;
|
class nsWindowSizes;
|
||||||
|
class nsIObjectLoadingContent;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace css {
|
namespace css {
|
||||||
@ -1566,6 +1567,10 @@ public:
|
|||||||
// state is unlocked/false.
|
// state is unlocked/false.
|
||||||
virtual nsresult SetImageLockingState(bool aLocked) = 0;
|
virtual nsresult SetImageLockingState(bool aLocked) = 0;
|
||||||
|
|
||||||
|
virtual nsresult AddPlugin(nsIObjectLoadingContent* aPlugin) = 0;
|
||||||
|
virtual void RemovePlugin(nsIObjectLoadingContent* aPlugin) = 0;
|
||||||
|
virtual void GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) = 0;
|
||||||
|
|
||||||
virtual nsresult GetStateObject(nsIVariant** aResult) = 0;
|
virtual nsresult GetStateObject(nsIVariant** aResult) = 0;
|
||||||
|
|
||||||
virtual nsDOMNavigationTiming* GetNavigationTiming() const = 0;
|
virtual nsDOMNavigationTiming* GetNavigationTiming() const = 0;
|
||||||
|
@ -52,7 +52,7 @@ interface nsIURI;
|
|||||||
/**
|
/**
|
||||||
* This interface represents a content node that loads objects.
|
* This interface represents a content node that loads objects.
|
||||||
*/
|
*/
|
||||||
[scriptable, uuid(3FF07AB3-5BAC-4D98-9549-5BD15CCEBCD3)]
|
[scriptable, uuid(fd56fda8-d3c3-4368-8cf3-67dbc992aec9)]
|
||||||
interface nsIObjectLoadingContent : nsISupports
|
interface nsIObjectLoadingContent : nsISupports
|
||||||
{
|
{
|
||||||
const unsigned long TYPE_LOADING = 0;
|
const unsigned long TYPE_LOADING = 0;
|
||||||
@ -125,6 +125,12 @@ interface nsIObjectLoadingContent : nsISupports
|
|||||||
*/
|
*/
|
||||||
void playPlugin();
|
void playPlugin();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This attribute will return true if the plugin has been activated
|
||||||
|
* and false if the plugin is still in the click-to-play state.
|
||||||
|
*/
|
||||||
|
readonly attribute boolean activated;
|
||||||
|
|
||||||
[noscript] void stopPluginInstance();
|
[noscript] void stopPluginInstance();
|
||||||
|
|
||||||
[noscript] void syncStartPluginInstance();
|
[noscript] void syncStartPluginInstance();
|
||||||
|
@ -1673,6 +1673,8 @@ nsDocument::~nsDocument()
|
|||||||
// unlocked state, and then clear the table.
|
// unlocked state, and then clear the table.
|
||||||
SetImageLockingState(false);
|
SetImageLockingState(false);
|
||||||
mImageTracker.Clear();
|
mImageTracker.Clear();
|
||||||
|
|
||||||
|
mPlugins.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
|
||||||
@ -2023,7 +2025,8 @@ nsDocument::Init()
|
|||||||
mScriptLoader = new nsScriptLoader(this);
|
mScriptLoader = new nsScriptLoader(this);
|
||||||
NS_ENSURE_TRUE(mScriptLoader, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(mScriptLoader, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
if (!mImageTracker.Init()) {
|
if (!mImageTracker.Init() ||
|
||||||
|
!mPlugins.Init()) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8354,6 +8357,51 @@ nsDocument::RemoveImage(imgIRequest* aImage)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aPlugin);
|
||||||
|
if (!mPlugins.PutEntry(aPlugin)) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aPlugin);
|
||||||
|
mPlugins.RemoveEntry(aPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg)
|
||||||
|
{
|
||||||
|
nsTArray<nsIObjectLoadingContent*>* plugins =
|
||||||
|
reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
|
||||||
|
MOZ_ASSERT(plugins);
|
||||||
|
aDocument->GetPlugins(*plugins);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLDHashOperator
|
||||||
|
AllPluginEnum(nsPtrHashKey<nsIObjectLoadingContent>* aPlugin, void* userArg)
|
||||||
|
{
|
||||||
|
nsTArray<nsIObjectLoadingContent*>* allPlugins =
|
||||||
|
reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
|
||||||
|
MOZ_ASSERT(allPlugins);
|
||||||
|
allPlugins->AppendElement(aPlugin->GetKey());
|
||||||
|
return PL_DHASH_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
|
||||||
|
{
|
||||||
|
aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
|
||||||
|
mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins);
|
||||||
|
EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
PLDHashOperator LockEnumerator(imgIRequest* aKey,
|
PLDHashOperator LockEnumerator(imgIRequest* aKey,
|
||||||
PRUint32 aData,
|
PRUint32 aData,
|
||||||
void* userArg)
|
void* userArg)
|
||||||
|
@ -934,6 +934,16 @@ public:
|
|||||||
virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage);
|
virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage);
|
||||||
virtual NS_HIDDEN_(nsresult) SetImageLockingState(bool aLocked);
|
virtual NS_HIDDEN_(nsresult) SetImageLockingState(bool aLocked);
|
||||||
|
|
||||||
|
// AddPlugin adds a plugin-related element to mPlugins when the element is
|
||||||
|
// added to the tree.
|
||||||
|
virtual nsresult AddPlugin(nsIObjectLoadingContent* aPlugin);
|
||||||
|
// RemovePlugin removes a plugin-related element to mPlugins when the
|
||||||
|
// element is removed from the tree.
|
||||||
|
virtual void RemovePlugin(nsIObjectLoadingContent* aPlugin);
|
||||||
|
// GetPlugins returns the plugin-related elements from
|
||||||
|
// the frame and any subframes.
|
||||||
|
virtual void GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins);
|
||||||
|
|
||||||
virtual nsresult GetStateObject(nsIVariant** aResult);
|
virtual nsresult GetStateObject(nsIVariant** aResult);
|
||||||
|
|
||||||
virtual nsDOMNavigationTiming* GetNavigationTiming() const;
|
virtual nsDOMNavigationTiming* GetNavigationTiming() const;
|
||||||
@ -1298,6 +1308,9 @@ private:
|
|||||||
// Tracking for images in the document.
|
// Tracking for images in the document.
|
||||||
nsDataHashtable< nsPtrHashKey<imgIRequest>, PRUint32> mImageTracker;
|
nsDataHashtable< nsPtrHashKey<imgIRequest>, PRUint32> mImageTracker;
|
||||||
|
|
||||||
|
// Tracking for plugins in the document.
|
||||||
|
nsTHashtable< nsPtrHashKey<nsIObjectLoadingContent> > mPlugins;
|
||||||
|
|
||||||
VisibilityState mVisibilityState;
|
VisibilityState mVisibilityState;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -115,6 +115,18 @@ static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
|
|||||||
|
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
|
|
||||||
|
static bool gClickToPlayPlugins = false;
|
||||||
|
|
||||||
|
static void
|
||||||
|
InitPrefCache()
|
||||||
|
{
|
||||||
|
static bool initializedPrefCache = false;
|
||||||
|
if (!initializedPrefCache) {
|
||||||
|
mozilla::Preferences::AddBoolVarCache(&gClickToPlayPlugins, "plugins.click_to_play");
|
||||||
|
}
|
||||||
|
initializedPrefCache = true;
|
||||||
|
}
|
||||||
|
|
||||||
class nsAsyncInstantiateEvent : public nsRunnable {
|
class nsAsyncInstantiateEvent : public nsRunnable {
|
||||||
public:
|
public:
|
||||||
nsObjectLoadingContent *mContent;
|
nsObjectLoadingContent *mContent;
|
||||||
@ -546,6 +558,26 @@ bool nsObjectLoadingContent::IsPluginEnabledByExtension(nsIURI* uri, nsCString&
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* /*aParent*/,
|
||||||
|
nsIContent* /*aBindingParent*/,
|
||||||
|
bool /*aCompileEventHandlers*/)
|
||||||
|
{
|
||||||
|
if (aDocument) {
|
||||||
|
return aDocument->AddPlugin(this);
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsObjectLoadingContent::UnbindFromTree(bool /*aDeep*/, bool /*aNullParent*/)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
|
||||||
|
MOZ_ASSERT(thisContent);
|
||||||
|
nsIDocument* ownerDoc = thisContent->OwnerDoc();
|
||||||
|
ownerDoc->RemovePlugin(this);
|
||||||
|
}
|
||||||
|
|
||||||
nsObjectLoadingContent::nsObjectLoadingContent()
|
nsObjectLoadingContent::nsObjectLoadingContent()
|
||||||
: mPendingInstantiateEvent(nsnull)
|
: mPendingInstantiateEvent(nsnull)
|
||||||
, mChannel(nsnull)
|
, mChannel(nsnull)
|
||||||
@ -554,11 +586,14 @@ nsObjectLoadingContent::nsObjectLoadingContent()
|
|||||||
, mUserDisabled(false)
|
, mUserDisabled(false)
|
||||||
, mSuppressed(false)
|
, mSuppressed(false)
|
||||||
, mNetworkCreated(true)
|
, mNetworkCreated(true)
|
||||||
// If plugins.click_to_play is false, plugins should always play
|
|
||||||
, mShouldPlay(!mozilla::Preferences::GetBool("plugins.click_to_play", false))
|
|
||||||
, mSrcStreamLoading(false)
|
, mSrcStreamLoading(false)
|
||||||
, mFallbackReason(ePluginOtherState)
|
, mFallbackReason(ePluginOtherState)
|
||||||
{
|
{
|
||||||
|
InitPrefCache();
|
||||||
|
// If plugins.click_to_play is false, plugins should always play
|
||||||
|
mShouldPlay = !gClickToPlayPlugins;
|
||||||
|
// If plugins.click_to_play is true, track the activated state of plugins.
|
||||||
|
mActivated = !gClickToPlayPlugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsObjectLoadingContent::~nsObjectLoadingContent()
|
nsObjectLoadingContent::~nsObjectLoadingContent()
|
||||||
@ -2205,5 +2240,13 @@ nsObjectLoadingContent::PlayPlugin()
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
mShouldPlay = true;
|
mShouldPlay = true;
|
||||||
|
mActivated = true;
|
||||||
return LoadObject(mURI, true, mContentType, true);
|
return LoadObject(mURI, true, mContentType, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsObjectLoadingContent::GetActivated(bool* aActivated)
|
||||||
|
{
|
||||||
|
*aActivated = mActivated;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
@ -244,6 +244,12 @@ class nsObjectLoadingContent : public nsImageLoadingContent
|
|||||||
|
|
||||||
static void DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner, bool aDelayedStop);
|
static void DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner, bool aDelayedStop);
|
||||||
|
|
||||||
|
nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||||
|
nsIContent* aBindingParent,
|
||||||
|
bool aCompileEventHandler);
|
||||||
|
void UnbindFromTree(bool aDeep = true,
|
||||||
|
bool aNullParent = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void NotifyContentObjectWrapper();
|
void NotifyContentObjectWrapper();
|
||||||
@ -399,6 +405,10 @@ class nsObjectLoadingContent : public nsImageLoadingContent
|
|||||||
// This is used for click-to-play plugins.
|
// This is used for click-to-play plugins.
|
||||||
bool mShouldPlay : 1;
|
bool mShouldPlay : 1;
|
||||||
|
|
||||||
|
// Used to keep track of whether or not a plugin has been played.
|
||||||
|
// This is used for click-to-play plugins.
|
||||||
|
bool mActivated : 1;
|
||||||
|
|
||||||
// Used to track when we might try to instantiate a plugin instance based on
|
// Used to track when we might try to instantiate a plugin instance based on
|
||||||
// a src data stream being delivered to this object. When this is true we don't
|
// a src data stream being delivered to this object. When this is true we don't
|
||||||
// want plugin instance instantiation code to attempt to load src data again or
|
// want plugin instance instantiation code to attempt to load src data again or
|
||||||
|
@ -2779,6 +2779,10 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blocking gets are common enough out of XHR that we should mark
|
||||||
|
// the channel slow by default for pipeline purposes
|
||||||
|
AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
|
||||||
|
|
||||||
if (!IsSystemXHR()) {
|
if (!IsSystemXHR()) {
|
||||||
// Always create a nsCORSListenerProxy here even if it's
|
// Always create a nsCORSListenerProxy here even if it's
|
||||||
// a same-origin request right now, since it could be redirected.
|
// a same-origin request right now, since it could be redirected.
|
||||||
|
@ -57,6 +57,8 @@
|
|||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsIThreadInternal.h"
|
#include "nsIThreadInternal.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
|
#include "nsIRequest.h"
|
||||||
|
|
||||||
#include "nsFrameManager.h"
|
#include "nsFrameManager.h"
|
||||||
#include "nsIScriptSecurityManager.h"
|
#include "nsIScriptSecurityManager.h"
|
||||||
#include "nsIXPConnect.h"
|
#include "nsIXPConnect.h"
|
||||||
@ -2850,6 +2852,13 @@ void nsHTMLMediaElement::SetRequestHeaders(nsIHttpChannel* aChannel)
|
|||||||
// Send Accept header for video and audio types only (Bug 489071)
|
// Send Accept header for video and audio types only (Bug 489071)
|
||||||
SetAcceptHeader(aChannel);
|
SetAcceptHeader(aChannel);
|
||||||
|
|
||||||
|
// Media elements are likely candidates for HTTP Pipeline head of line
|
||||||
|
// blocking problems, so disable pipelines.
|
||||||
|
nsLoadFlags loadflags;
|
||||||
|
aChannel->GetLoadFlags(&loadflags);
|
||||||
|
loadflags |= nsIRequest::INHIBIT_PIPELINE;
|
||||||
|
aChannel->SetLoadFlags(loadflags);
|
||||||
|
|
||||||
// Apache doesn't send Content-Length when gzip transfer encoding is used,
|
// Apache doesn't send Content-Length when gzip transfer encoding is used,
|
||||||
// which prevents us from estimating the video length (if explicit Content-Duration
|
// which prevents us from estimating the video length (if explicit Content-Duration
|
||||||
// and a length spec in the container are not present either) and from seeking.
|
// and a length spec in the container are not present either) and from seeking.
|
||||||
|
@ -265,6 +265,11 @@ nsHTMLObjectElement::BindToTree(nsIDocument *aDocument,
|
|||||||
aCompileEventHandlers);
|
aCompileEventHandlers);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = nsObjectLoadingContent::BindToTree(aDocument, aParent,
|
||||||
|
aBindingParent,
|
||||||
|
aCompileEventHandlers);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// If we already have all the children, start the load.
|
// If we already have all the children, start the load.
|
||||||
if (mIsDoneAddingChildren) {
|
if (mIsDoneAddingChildren) {
|
||||||
void (nsHTMLObjectElement::*start)() = &nsHTMLObjectElement::StartObjectLoad;
|
void (nsHTMLObjectElement::*start)() = &nsHTMLObjectElement::StartObjectLoad;
|
||||||
@ -279,6 +284,7 @@ nsHTMLObjectElement::UnbindFromTree(bool aDeep,
|
|||||||
bool aNullParent)
|
bool aNullParent)
|
||||||
{
|
{
|
||||||
RemovedFromDocument();
|
RemovedFromDocument();
|
||||||
|
nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
|
||||||
nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
|
nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +283,11 @@ nsHTMLSharedObjectElement::BindToTree(nsIDocument *aDocument,
|
|||||||
aCompileEventHandlers);
|
aCompileEventHandlers);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = nsObjectLoadingContent::BindToTree(aDocument, aParent,
|
||||||
|
aBindingParent,
|
||||||
|
aCompileEventHandlers);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// If we already have all the children, start the load.
|
// If we already have all the children, start the load.
|
||||||
if (mIsDoneAddingChildren) {
|
if (mIsDoneAddingChildren) {
|
||||||
void (nsHTMLSharedObjectElement::*start)() =
|
void (nsHTMLSharedObjectElement::*start)() =
|
||||||
@ -298,6 +303,7 @@ nsHTMLSharedObjectElement::UnbindFromTree(bool aDeep,
|
|||||||
bool aNullParent)
|
bool aNullParent)
|
||||||
{
|
{
|
||||||
RemovedFromDocument();
|
RemovedFromDocument();
|
||||||
|
nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
|
||||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,8 +423,13 @@ nsXBLContentSink::OnOpenContainer(const PRUnichar **aAtts,
|
|||||||
bool ret = true;
|
bool ret = true;
|
||||||
if (aTagName == nsGkAtoms::bindings) {
|
if (aTagName == nsGkAtoms::bindings) {
|
||||||
ENSURE_XBL_STATE(mState == eXBL_InDocument);
|
ENSURE_XBL_STATE(mState == eXBL_InDocument);
|
||||||
|
|
||||||
mDocInfo = NS_NewXBLDocumentInfo(mDocument);
|
NS_ASSERTION(mDocument, "Must have a document!");
|
||||||
|
nsRefPtr<nsXBLDocumentInfo> info = new nsXBLDocumentInfo(mDocument);
|
||||||
|
|
||||||
|
// We keep a weak ref. We're creating a cycle between doc/binding manager/doc info.
|
||||||
|
mDocInfo = info;
|
||||||
|
|
||||||
if (!mDocInfo) {
|
if (!mDocInfo) {
|
||||||
mState = eXBL_Error;
|
mState = eXBL_Error;
|
||||||
return true;
|
return true;
|
||||||
@ -433,16 +438,14 @@ nsXBLContentSink::OnOpenContainer(const PRUnichar **aAtts,
|
|||||||
mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo);
|
mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo);
|
||||||
|
|
||||||
nsIURI *uri = mDocument->GetDocumentURI();
|
nsIURI *uri = mDocument->GetDocumentURI();
|
||||||
|
|
||||||
bool isChrome = false;
|
bool isChrome = false;
|
||||||
bool isRes = false;
|
bool isRes = false;
|
||||||
|
|
||||||
uri->SchemeIs("chrome", &isChrome);
|
uri->SchemeIs("chrome", &isChrome);
|
||||||
uri->SchemeIs("resource", &isRes);
|
uri->SchemeIs("resource", &isRes);
|
||||||
mIsChromeOrResource = isChrome || isRes;
|
mIsChromeOrResource = isChrome || isRes;
|
||||||
|
|
||||||
nsXBLDocumentInfo* info = mDocInfo;
|
|
||||||
NS_RELEASE(info); // We keep a weak ref. We've created a cycle between doc/binding manager/doc info.
|
|
||||||
mState = eXBL_InBindings;
|
mState = eXBL_InBindings;
|
||||||
}
|
}
|
||||||
else if (aTagName == nsGkAtoms::binding) {
|
else if (aTagName == nsGkAtoms::binding) {
|
||||||
|
@ -682,7 +682,8 @@ nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocI
|
|||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
|
||||||
nsRefPtr<nsXBLDocumentInfo> docInfo = NS_NewXBLDocumentInfo(doc);
|
NS_ASSERTION(doc, "Must have a document!");
|
||||||
|
nsRefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(doc);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
PRUint8 flags;
|
PRUint8 flags;
|
||||||
@ -783,14 +784,3 @@ nsXBLDocumentInfo::GetScriptGlobalObject()
|
|||||||
|
|
||||||
return mGlobalObject;
|
return mGlobalObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsXBLDocumentInfo* NS_NewXBLDocumentInfo(nsIDocument* aDocument)
|
|
||||||
{
|
|
||||||
NS_PRECONDITION(aDocument, "Must have a document!");
|
|
||||||
|
|
||||||
nsXBLDocumentInfo* result;
|
|
||||||
|
|
||||||
result = new nsXBLDocumentInfo(aDocument);
|
|
||||||
NS_ADDREF(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
@ -101,6 +101,4 @@ private:
|
|||||||
nsRefPtr<nsXBLDocGlobalObject> mGlobalObject;
|
nsRefPtr<nsXBLDocGlobalObject> mGlobalObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
nsXBLDocumentInfo* NS_NewXBLDocumentInfo(nsIDocument* aDocument);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1106,11 +1106,7 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!info)
|
info.forget(aResult);
|
||||||
return NS_OK;
|
|
||||||
|
|
||||||
*aResult = info;
|
|
||||||
NS_IF_ADDREF(*aResult);
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
#include "nsRefreshDriver.h"
|
#include "nsRefreshDriver.h"
|
||||||
#include "nsDOMTouchEvent.h"
|
#include "nsDOMTouchEvent.h"
|
||||||
#include "nsIDOMTouchEvent.h"
|
#include "nsIDOMTouchEvent.h"
|
||||||
|
#include "nsObjectLoadingContent.h"
|
||||||
|
|
||||||
#include "nsIScrollableFrame.h"
|
#include "nsIScrollableFrame.h"
|
||||||
|
|
||||||
@ -76,6 +77,7 @@
|
|||||||
#include "nsCSSProps.h"
|
#include "nsCSSProps.h"
|
||||||
#include "nsDOMFile.h"
|
#include "nsDOMFile.h"
|
||||||
#include "BasicLayers.h"
|
#include "BasicLayers.h"
|
||||||
|
#include "nsTArrayHelpers.h"
|
||||||
|
|
||||||
#if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2)
|
#if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2)
|
||||||
#include <gdk/gdk.h>
|
#include <gdk/gdk.h>
|
||||||
@ -2230,3 +2232,26 @@ nsDOMWindowUtils::GetPaintingSuppressed(bool *aPaintingSuppressed)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsDOMWindowUtils::GetPlugins(JSContext* cx, jsval* aPlugins)
|
||||||
|
{
|
||||||
|
if (!IsUniversalXPConnectCapable()) {
|
||||||
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIDOMDocument* ddoc = mWindow->GetExtantDocument();
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(ddoc, &rv);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsTArray<nsIObjectLoadingContent*> plugins;
|
||||||
|
doc->GetPlugins(plugins);
|
||||||
|
|
||||||
|
JSObject* jsPlugins = nsnull;
|
||||||
|
rv = nsTArrayToJSArray(cx, plugins, &jsPlugins);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
*aPlugins = OBJECT_TO_JSVAL(jsPlugins);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
@ -70,7 +70,7 @@ interface nsIDOMFile;
|
|||||||
interface nsIFile;
|
interface nsIFile;
|
||||||
interface nsIDOMTouch;
|
interface nsIDOMTouch;
|
||||||
|
|
||||||
[scriptable, uuid(43feb172-30e1-4ff1-b021-004f973da516)]
|
[scriptable, uuid(c7f303a1-4f7b-4d38-a192-c3f0e25dadb1)]
|
||||||
interface nsIDOMWindowUtils : nsISupports {
|
interface nsIDOMWindowUtils : nsISupports {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1099,4 +1099,15 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
readonly attribute boolean paintingSuppressed;
|
readonly attribute boolean paintingSuppressed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of plugins on the page for opt-in activation.
|
||||||
|
*
|
||||||
|
* Cannot be accessed from unprivileged context (not content-accessible).
|
||||||
|
* Will throw a DOM security error if called without UniversalXPConnect
|
||||||
|
* privileges.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
[implicit_jscontext]
|
||||||
|
readonly attribute jsval plugins;
|
||||||
};
|
};
|
||||||
|
@ -116,3 +116,8 @@ QTMLocationProvider::Shutdown()
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
QTMLocationProvider::SetHighAccuracy(bool)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "SystemWorkerManager.h"
|
#include "SystemWorkerManager.h"
|
||||||
|
#include "nsTArrayHelpers.h"
|
||||||
|
|
||||||
#include "CallEvent.h"
|
#include "CallEvent.h"
|
||||||
#include "TelephonyCall.h"
|
#include "TelephonyCall.h"
|
||||||
@ -69,53 +70,6 @@ typedef nsAutoTArray<Telephony*, 2> TelephonyList;
|
|||||||
|
|
||||||
TelephonyList* gTelephonyList;
|
TelephonyList* gTelephonyList;
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline nsresult
|
|
||||||
nsTArrayToJSArray(JSContext* aCx, JSObject* aGlobal,
|
|
||||||
const nsTArray<nsRefPtr<T> >& aSourceArray,
|
|
||||||
JSObject** aResultArray)
|
|
||||||
{
|
|
||||||
NS_ASSERTION(aCx, "Null context!");
|
|
||||||
NS_ASSERTION(aGlobal, "Null global!");
|
|
||||||
|
|
||||||
JSAutoRequest ar(aCx);
|
|
||||||
JSAutoEnterCompartment ac;
|
|
||||||
if (!ac.enter(aCx, aGlobal)) {
|
|
||||||
NS_WARNING("Failed to enter compartment!");
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSObject* arrayObj;
|
|
||||||
|
|
||||||
if (aSourceArray.IsEmpty()) {
|
|
||||||
arrayObj = JS_NewArrayObject(aCx, 0, nsnull);
|
|
||||||
} else {
|
|
||||||
nsTArray<jsval> valArray;
|
|
||||||
valArray.SetLength(aSourceArray.Length());
|
|
||||||
|
|
||||||
for (PRUint32 index = 0; index < valArray.Length(); index++) {
|
|
||||||
nsISupports* obj = aSourceArray[index]->ToISupports();
|
|
||||||
nsresult rv =
|
|
||||||
nsContentUtils::WrapNative(aCx, aGlobal, obj, &valArray[index]);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
arrayObj = JS_NewArrayObject(aCx, valArray.Length(), valArray.Elements());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!arrayObj) {
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX This is not what Jonas wants. He wants it to be live.
|
|
||||||
if (!JS_FreezeObject(aCx, arrayObj)) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*aResultArray = arrayObj;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
Telephony::Telephony()
|
Telephony::Telephony()
|
||||||
@ -352,8 +306,7 @@ Telephony::GetCalls(jsval* aCalls)
|
|||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
if (sc) {
|
if (sc) {
|
||||||
rv =
|
rv =
|
||||||
nsTArrayToJSArray(sc->GetNativeContext(),
|
nsTArrayToJSArray(mScriptContext->GetNativeContext(), mCalls, &calls);
|
||||||
sc->GetNativeGlobal(), mCalls, &calls);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (!mRooted) {
|
if (!mRooted) {
|
||||||
|
@ -1910,7 +1910,7 @@ GLContextProviderEGL::CreateOffscreen(const gfxIntSize& aSize,
|
|||||||
return nsnull;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ANDROID) || defined(XP_WIN)
|
#if !defined(MOZ_X11)
|
||||||
bool usePBuffers = false; // Generally, prefer FBOs to PBuffers
|
bool usePBuffers = false; // Generally, prefer FBOs to PBuffers
|
||||||
|
|
||||||
if (sEGLLibrary.IsANGLE())
|
if (sEGLLibrary.IsANGLE())
|
||||||
|
@ -458,23 +458,11 @@ ImageLayerOGL::RenderLayer(int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
InitTexture(GLContext* aGL, GLuint aTexture, GLenum aFormat, const gfxIntSize& aSize)
|
SetClamping(GLContext* aGL, GLuint aTexture)
|
||||||
{
|
{
|
||||||
aGL->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
|
aGL->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
|
||||||
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
|
|
||||||
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
|
|
||||||
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
|
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
|
||||||
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
|
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
aGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
aFormat,
|
|
||||||
aSize.width,
|
|
||||||
aSize.height,
|
|
||||||
0,
|
|
||||||
aFormat,
|
|
||||||
LOCAL_GL_UNSIGNED_BYTE,
|
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -529,13 +517,13 @@ ImageLayerOGL::AllocateTexturesYCbCr(PlanarYCbCrImage *aImage)
|
|||||||
gl()->MakeCurrent();
|
gl()->MakeCurrent();
|
||||||
|
|
||||||
mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_Y, data.mYSize, gl(), &backendData->mTextures[0]);
|
mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_Y, data.mYSize, gl(), &backendData->mTextures[0]);
|
||||||
InitTexture(gl(), backendData->mTextures[0].GetTextureID(), LOCAL_GL_LUMINANCE, data.mYSize);
|
SetClamping(gl(), backendData->mTextures[0].GetTextureID());
|
||||||
|
|
||||||
mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_C, data.mCbCrSize, gl(), &backendData->mTextures[1]);
|
mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_C, data.mCbCrSize, gl(), &backendData->mTextures[1]);
|
||||||
InitTexture(gl(), backendData->mTextures[1].GetTextureID(), LOCAL_GL_LUMINANCE, data.mCbCrSize);
|
SetClamping(gl(), backendData->mTextures[1].GetTextureID());
|
||||||
|
|
||||||
mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_C, data.mCbCrSize, gl(), &backendData->mTextures[2]);
|
mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_C, data.mCbCrSize, gl(), &backendData->mTextures[2]);
|
||||||
InitTexture(gl(), backendData->mTextures[2].GetTextureID(), LOCAL_GL_LUMINANCE, data.mCbCrSize);
|
SetClamping(gl(), backendData->mTextures[2].GetTextureID());
|
||||||
|
|
||||||
UploadYUVToTexture(gl(), aImage->mData,
|
UploadYUVToTexture(gl(), aImage->mData,
|
||||||
&backendData->mTextures[0],
|
&backendData->mTextures[0],
|
||||||
@ -569,6 +557,8 @@ ImageLayerOGL::AllocateTexturesCairo(CairoImage *aImage)
|
|||||||
GLuint tex = texture.GetTextureID();
|
GLuint tex = texture.GetTextureID();
|
||||||
gl->fActiveTexture(LOCAL_GL_TEXTURE0);
|
gl->fActiveTexture(LOCAL_GL_TEXTURE0);
|
||||||
|
|
||||||
|
SetClamping(gl, tex);
|
||||||
|
|
||||||
#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
|
#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
|
||||||
if (sGLXLibrary.SupportsTextureFromPixmap(aImage->mSurface)) {
|
if (sGLXLibrary.SupportsTextureFromPixmap(aImage->mSurface)) {
|
||||||
if (aImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
|
if (aImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
|
||||||
@ -636,9 +626,9 @@ ShadowImageLayerOGL::Init(const SharedImage& aFront)
|
|||||||
"Texture allocation failed!");
|
"Texture allocation failed!");
|
||||||
|
|
||||||
gl()->MakeCurrent();
|
gl()->MakeCurrent();
|
||||||
InitTexture(gl(), mYUVTexture[0].GetTextureID(), LOCAL_GL_LUMINANCE, mSize);
|
SetClamping(gl(), mYUVTexture[0].GetTextureID());
|
||||||
InitTexture(gl(), mYUVTexture[1].GetTextureID(), LOCAL_GL_LUMINANCE, mCbCrSize);
|
SetClamping(gl(), mYUVTexture[1].GetTextureID());
|
||||||
InitTexture(gl(), mYUVTexture[2].GetTextureID(), LOCAL_GL_LUMINANCE, mCbCrSize);
|
SetClamping(gl(), mYUVTexture[2].GetTextureID());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -333,6 +333,22 @@ CPPSRCS += \
|
|||||||
OS_CXXFLAGS += $(MOZ_PANGO_CFLAGS)
|
OS_CXXFLAGS += $(MOZ_PANGO_CFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq (qt,$(MOZ_WIDGET_TOOLKIT))
|
||||||
|
CPPSRCS += \
|
||||||
|
SkFontHost_FreeType.cpp \
|
||||||
|
SkFontHost_gamma_none.cpp \
|
||||||
|
SkMMapStream.cpp \
|
||||||
|
SkOSFile.cpp \
|
||||||
|
$(NULL)
|
||||||
|
ifeq (Linux,$(OS_TARGET))
|
||||||
|
CPPSRCS += \
|
||||||
|
SkFontHost_linux.cpp \
|
||||||
|
SkTime_Unix.cpp \
|
||||||
|
$(NULL)
|
||||||
|
endif
|
||||||
|
OS_CXXFLAGS += $(MOZ_PANGO_CFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
|
ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
|
||||||
EXPORTS_skia += \
|
EXPORTS_skia += \
|
||||||
include/config/sk_stdint.h \
|
include/config/sk_stdint.h \
|
||||||
|
@ -46,6 +46,8 @@
|
|||||||
|
|
||||||
#include "gfxFontconfigUtils.h"
|
#include "gfxFontconfigUtils.h"
|
||||||
|
|
||||||
|
#include "mozilla/gfx/2D.h"
|
||||||
|
|
||||||
#include "cairo.h"
|
#include "cairo.h"
|
||||||
|
|
||||||
#include "gfxImageSurface.h"
|
#include "gfxImageSurface.h"
|
||||||
@ -81,6 +83,7 @@
|
|||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::unicode;
|
using namespace mozilla::unicode;
|
||||||
|
using namespace mozilla::gfx;
|
||||||
|
|
||||||
#define DEFAULT_RENDER_MODE RENDER_DIRECT
|
#define DEFAULT_RENDER_MODE RENDER_DIRECT
|
||||||
|
|
||||||
@ -596,3 +599,11 @@ gfxQtPlatform::GetOffscreenFormat()
|
|||||||
{
|
{
|
||||||
return sOffscreenFormat;
|
return sOffscreenFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
gfxQtPlatform::SupportsAzure(BackendType& aBackend)
|
||||||
|
{
|
||||||
|
aBackend = BACKEND_SKIA;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,8 @@ public:
|
|||||||
already_AddRefed<gfxASurface> CreateOffscreenSurface(const gfxIntSize& size,
|
already_AddRefed<gfxASurface> CreateOffscreenSurface(const gfxIntSize& size,
|
||||||
gfxASurface::gfxContentType contentType);
|
gfxASurface::gfxContentType contentType);
|
||||||
|
|
||||||
|
virtual bool SupportsAzure(mozilla::gfx::BackendType& aBackend);
|
||||||
|
|
||||||
nsresult GetFontList(nsIAtom *aLangGroup,
|
nsresult GetFontList(nsIAtom *aLangGroup,
|
||||||
const nsACString& aGenericFamily,
|
const nsACString& aGenericFamily,
|
||||||
nsTArray<nsString>& aListOfFonts);
|
nsTArray<nsString>& aListOfFonts);
|
||||||
|
@ -685,6 +685,15 @@ NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
|
|||||||
aStatusArg);
|
aStatusArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NS_FAILED(aStatus)) {
|
||||||
|
// Some kind of problem has happened with image decoding.
|
||||||
|
// Report the URI to net:failed-to-decode-uri observers.
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||||
|
if (os)
|
||||||
|
os->NotifyObservers(mURI, "net:failed-to-process-uri", nsnull);
|
||||||
|
}
|
||||||
|
|
||||||
// RasterImage and everything below it is completely correct and
|
// RasterImage and everything below it is completely correct and
|
||||||
// bulletproof about its handling of decoder notifications.
|
// bulletproof about its handling of decoder notifications.
|
||||||
// Unfortunately, here and above we have to make some gross and
|
// Unfortunately, here and above we have to make some gross and
|
||||||
|
@ -118,6 +118,9 @@ _CHROME_FILES = imgutils.js \
|
|||||||
test_undisplayed_iframe.html \
|
test_undisplayed_iframe.html \
|
||||||
iframe.html \
|
iframe.html \
|
||||||
ref-iframe.html \
|
ref-iframe.html \
|
||||||
|
test_net_failedtoprocess.html \
|
||||||
|
invalid.jpg \
|
||||||
|
damon.jpg \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
libs:: $(_TEST_FILES)
|
libs:: $(_TEST_FILES)
|
||||||
|
1
image/test/mochitest/invalid.jpg
Normal file
1
image/test/mochitest/invalid.jpg
Normal file
@ -0,0 +1 @@
|
|||||||
|
notajpg
|
48
image/test/mochitest/test_net_failedtoprocess.html
Normal file
48
image/test/mochitest/test_net_failedtoprocess.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
Test that a image decoding error producs a net:failed-to-process-uri
|
||||||
|
observer event with the nsIURI of the failed image as the subject
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<title>Test for image net:failed-to-process-uri</title>
|
||||||
|
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||||
|
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p id="display"></p>
|
||||||
|
<pre id="test">
|
||||||
|
<script type="application/javascript">
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const Cc = Components.classes;
|
||||||
|
|
||||||
|
var observer = {
|
||||||
|
QueryInterface: function (aIID) {
|
||||||
|
if (aIID.equals(Ci.nsISupports) ||
|
||||||
|
aIID.equals(Ci.nsIObserver))
|
||||||
|
return this;
|
||||||
|
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||||
|
},
|
||||||
|
|
||||||
|
observe: function(subject, topic, data) {
|
||||||
|
ok(topic == "net:failed-to-process-uri", "wrong topic");
|
||||||
|
subject = subject.QueryInterface(Ci.nsIURI);
|
||||||
|
ok(subject.asciiSpec == "chrome://mochitests/content/chrome/image/test/mochitest/invalid.jpg", "wrong subject");
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var obs = Cc["@mozilla.org/observer-service;1"].getService();
|
||||||
|
obs = obs.QueryInterface(Ci.nsIObserverService);
|
||||||
|
obs.addObserver(observer, "net:failed-to-process-uri", false);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
<img src="damon.jpg">
|
||||||
|
<img src="invalid.jpg">
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -2225,7 +2225,6 @@ ia64*-hpux*)
|
|||||||
PKG_SKIP_STRIP=1
|
PKG_SKIP_STRIP=1
|
||||||
XARGS=xargs
|
XARGS=xargs
|
||||||
DOXYGEN=:
|
DOXYGEN=:
|
||||||
GARBAGE='$(OBJDIR)/vc20.pdb $(OBJDIR)/vc40.pdb'
|
|
||||||
ASM_SUFFIX=asm
|
ASM_SUFFIX=asm
|
||||||
OBJ_SUFFIX=obj
|
OBJ_SUFFIX=obj
|
||||||
LIB_SUFFIX=lib
|
LIB_SUFFIX=lib
|
||||||
|
@ -4104,7 +4104,7 @@ JS_SetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp)
|
|||||||
{
|
{
|
||||||
AssertNoGC(cx);
|
AssertNoGC(cx);
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
assertSameCompartment(cx, obj);
|
assertSameCompartment(cx, obj, *vp);
|
||||||
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
|
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
|
||||||
return obj->setElement(cx, index, vp, false);
|
return obj->setElement(cx, index, vp, false);
|
||||||
}
|
}
|
||||||
|
@ -1001,29 +1001,14 @@ JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t
|
|||||||
|
|
||||||
size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote);
|
size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote);
|
||||||
|
|
||||||
uint8_t *data = NULL;
|
/*
|
||||||
#if JS_SCRIPT_INLINE_DATA_LIMIT
|
* We assume that calloc aligns on sizeof(Value) if the size we ask to
|
||||||
if (size <= JS_SCRIPT_INLINE_DATA_LIMIT) {
|
* allocate divides sizeof(Value).
|
||||||
/*
|
*/
|
||||||
* Check that if inlineData is big enough to store const values, we
|
JS_STATIC_ASSERT(sizeof(Value) == sizeof(double));
|
||||||
* can do that without any special alignment requirements given that
|
uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
|
||||||
* the script as a GC thing is always aligned on Cell::CellSize.
|
if (!data)
|
||||||
*/
|
return NULL;
|
||||||
JS_STATIC_ASSERT(Cell::CellSize % sizeof(Value) == 0);
|
|
||||||
JS_STATIC_ASSERT(JS_SCRIPT_INLINE_DATA_LIMIT < sizeof(Value) ||
|
|
||||||
offsetof(JSScript, inlineData) % sizeof(Value) == 0);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We assume that calloc aligns on sizeof(Value) if the size we ask to
|
|
||||||
* allocate divides sizeof(Value).
|
|
||||||
*/
|
|
||||||
JS_STATIC_ASSERT(sizeof(Value) == sizeof(double));
|
|
||||||
data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
|
|
||||||
if (!data)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSScript *script = js_NewGCScript(cx);
|
JSScript *script = js_NewGCScript(cx);
|
||||||
if (!script) {
|
if (!script) {
|
||||||
@ -1034,10 +1019,6 @@ JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t
|
|||||||
PodZero(script);
|
PodZero(script);
|
||||||
#ifdef JS_CRASH_DIAGNOSTICS
|
#ifdef JS_CRASH_DIAGNOSTICS
|
||||||
script->cookie1[0] = script->cookie2[0] = JS_SCRIPT_COOKIE;
|
script->cookie1[0] = script->cookie2[0] = JS_SCRIPT_COOKIE;
|
||||||
#endif
|
|
||||||
#if JS_SCRIPT_INLINE_DATA_LIMIT
|
|
||||||
if (!data)
|
|
||||||
data = script->inlineData;
|
|
||||||
#endif
|
#endif
|
||||||
script->data = data;
|
script->data = data;
|
||||||
script->length = length;
|
script->length = length;
|
||||||
@ -1325,11 +1306,6 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
|||||||
size_t
|
size_t
|
||||||
JSScript::computedSizeOfData()
|
JSScript::computedSizeOfData()
|
||||||
{
|
{
|
||||||
#if JS_SCRIPT_INLINE_DATA_LIMIT
|
|
||||||
if (data == inlineData)
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint8_t *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
|
uint8_t *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
|
||||||
JS_ASSERT(dataEnd >= data);
|
JS_ASSERT(dataEnd >= data);
|
||||||
return dataEnd - data;
|
return dataEnd - data;
|
||||||
@ -1338,11 +1314,6 @@ JSScript::computedSizeOfData()
|
|||||||
size_t
|
size_t
|
||||||
JSScript::sizeOfData(JSMallocSizeOfFun mallocSizeOf)
|
JSScript::sizeOfData(JSMallocSizeOfFun mallocSizeOf)
|
||||||
{
|
{
|
||||||
#if JS_SCRIPT_INLINE_DATA_LIMIT
|
|
||||||
if (data == inlineData)
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return mallocSizeOf(data);
|
return mallocSizeOf(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1436,13 +1407,8 @@ JSScript::finalize(JSContext *cx, bool background)
|
|||||||
cx->free_(debug);
|
cx->free_(debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JS_SCRIPT_INLINE_DATA_LIMIT
|
JS_POISON(data, 0xdb, computedSizeOfData());
|
||||||
if (data != inlineData)
|
cx->free_(data);
|
||||||
#endif
|
|
||||||
{
|
|
||||||
JS_POISON(data, 0xdb, computedSizeOfData());
|
|
||||||
cx->free_(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
@ -452,16 +452,6 @@ struct JSScript : public js::gc::Cell
|
|||||||
uint16_t nClosedArgs; /* number of args which are closed over. */
|
uint16_t nClosedArgs; /* number of args which are closed over. */
|
||||||
uint16_t nClosedVars; /* number of vars which are closed over. */
|
uint16_t nClosedVars; /* number of vars which are closed over. */
|
||||||
|
|
||||||
/*
|
|
||||||
* To ensure sizeof(JSScript) % gc::Cell::CellSize == 0 on we must pad
|
|
||||||
* the script with 4 bytes. We use them to store tiny scripts like empty
|
|
||||||
* scripts.
|
|
||||||
*/
|
|
||||||
#if JS_BITS_PER_WORD == 64
|
|
||||||
#define JS_SCRIPT_INLINE_DATA_LIMIT 4
|
|
||||||
uint8_t inlineData[JS_SCRIPT_INLINE_DATA_LIMIT];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *filename; /* source filename or null */
|
const char *filename; /* source filename or null */
|
||||||
JSAtom **atoms; /* maps immediate index to literal struct */
|
JSAtom **atoms; /* maps immediate index to literal struct */
|
||||||
private:
|
private:
|
||||||
|
@ -49,6 +49,7 @@ EXPORTS = \
|
|||||||
nsAXPCNativeCallContext.h \
|
nsAXPCNativeCallContext.h \
|
||||||
xpc_map_end.h \
|
xpc_map_end.h \
|
||||||
nsAutoJSValHolder.h \
|
nsAutoJSValHolder.h \
|
||||||
|
nsTArrayHelpers.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
49
js/xpconnect/public/nsTArrayHelpers.h
Normal file
49
js/xpconnect/public/nsTArrayHelpers.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef __NSTARRAYHELPERS_H__
|
||||||
|
#define __NSTARRAYHELPERS_H__
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline nsresult
|
||||||
|
nsTArrayToJSArray(JSContext* aCx, const nsTArray<T>& aSourceArray,
|
||||||
|
JSObject** aResultArray)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aCx);
|
||||||
|
JSAutoRequest ar(aCx);
|
||||||
|
|
||||||
|
JSObject* arrayObj = JS_NewArrayObject(aCx, aSourceArray.Length(), nsnull);
|
||||||
|
if (!arrayObj) {
|
||||||
|
NS_WARNING("JS_NewArrayObject failed!");
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject* global = JS_GetGlobalForScopeChain(aCx);
|
||||||
|
MOZ_ASSERT(global);
|
||||||
|
|
||||||
|
for (PRUint32 index = 0; index < aSourceArray.Length(); index++) {
|
||||||
|
nsCOMPtr<nsISupports> obj;
|
||||||
|
nsresult rv = CallQueryInterface(aSourceArray[index], getter_AddRefs(obj));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
jsval wrappedVal;
|
||||||
|
rv = nsContentUtils::WrapNative(aCx, global, obj, &wrappedVal, nsnull, true);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (!JS_SetElement(aCx, arrayObj, index, &wrappedVal)) {
|
||||||
|
NS_WARNING("JS_SetElement failed!");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!JS_FreezeObject(aCx, arrayObj)) {
|
||||||
|
NS_WARNING("JS_FreezeObject failed!");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*aResultArray = arrayObj;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __NSTARRAYHELPERS_H__ */
|
@ -145,7 +145,7 @@ pref("browser.sessionhistory.max_entries", 50);
|
|||||||
|
|
||||||
/* session store */
|
/* session store */
|
||||||
pref("browser.sessionstore.resume_session_once", false);
|
pref("browser.sessionstore.resume_session_once", false);
|
||||||
pref("browser.sessionstore.resume_from_crash", true);
|
pref("browser.sessionstore.resume_from_crash", false);
|
||||||
pref("browser.sessionstore.resume_from_crash_timeout", 60); // minutes
|
pref("browser.sessionstore.resume_from_crash_timeout", 60); // minutes
|
||||||
pref("browser.sessionstore.interval", 10000); // milliseconds
|
pref("browser.sessionstore.interval", 10000); // milliseconds
|
||||||
pref("browser.sessionstore.max_tabs_undo", 1);
|
pref("browser.sessionstore.max_tabs_undo", 1);
|
||||||
|
@ -295,17 +295,7 @@ public class Favicons {
|
|||||||
image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src");
|
image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Trying to read icons from nested jar files will fail
|
Log.e(LOGTAG, "Error downloading favicon", e);
|
||||||
if (mFaviconUrl.startsWith("jar:jar:")) {
|
|
||||||
InputStream stream = GeckoJarReader.getStream(mFaviconUrl);
|
|
||||||
if (stream != null) {
|
|
||||||
image = new BitmapDrawable(stream);
|
|
||||||
} else {
|
|
||||||
Log.d(LOGTAG, "Error getting favicon from jar: " + e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(LOGTAG, "Error downloading favicon: " + e);
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
if (urlConnection != null && urlConnection instanceof HttpURLConnection) {
|
if (urlConnection != null && urlConnection instanceof HttpURLConnection) {
|
||||||
HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
|
HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
|
||||||
|
@ -453,10 +453,10 @@ abstract public class GeckoApp
|
|||||||
|
|
||||||
forward.setEnabled(tab.canDoForward());
|
forward.setEnabled(tab.canDoForward());
|
||||||
|
|
||||||
// Disable share menuitem for about:, chrome: and file: URIs
|
// Disable share menuitem for about:, chrome:, file:, and resource: URIs
|
||||||
String scheme = Uri.parse(tab.getURL()).getScheme();
|
String scheme = Uri.parse(tab.getURL()).getScheme();
|
||||||
share.setEnabled(!(scheme.equals("about") || scheme.equals("chrome") ||
|
share.setEnabled(!(scheme.equals("about") || scheme.equals("chrome") ||
|
||||||
scheme.equals("file")));
|
scheme.equals("file") || scheme.equals("resource")));
|
||||||
|
|
||||||
// Disable save as PDF for about:home and xul pages
|
// Disable save as PDF for about:home and xul pages
|
||||||
saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
|
saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
|
||||||
@ -1677,9 +1677,10 @@ abstract public class GeckoApp
|
|||||||
if (uri != null && uri.length() > 0)
|
if (uri != null && uri.length() > 0)
|
||||||
passedUri = mLastTitle = uri;
|
passedUri = mLastTitle = uri;
|
||||||
|
|
||||||
|
mRestoreSession |= getProfile().shouldRestoreSession();
|
||||||
if (passedUri == null || passedUri.equals("about:home")) {
|
if (passedUri == null || passedUri.equals("about:home")) {
|
||||||
// show about:home if we aren't restoring previous session
|
// show about:home if we aren't restoring previous session
|
||||||
if (! getProfile().hasSession()) {
|
if (!mRestoreSession) {
|
||||||
mBrowserToolbar.updateTabCount(1);
|
mBrowserToolbar.updateTabCount(1);
|
||||||
showAboutHome();
|
showAboutHome();
|
||||||
}
|
}
|
||||||
@ -1742,9 +1743,15 @@ abstract public class GeckoApp
|
|||||||
* run experience, perhaps?
|
* run experience, perhaps?
|
||||||
*/
|
*/
|
||||||
mLayerController = new LayerController(this);
|
mLayerController = new LayerController(this);
|
||||||
mPlaceholderLayerClient = new PlaceholderLayerClient(mLayerController, mLastViewport);
|
View v = mLayerController.getView();
|
||||||
|
|
||||||
mGeckoLayout.addView(mLayerController.getView(), 0);
|
mPlaceholderLayerClient = new PlaceholderLayerClient(mLayerController, mLastViewport);
|
||||||
|
if (!mPlaceholderLayerClient.loadScreenshot()) {
|
||||||
|
// Instead of flickering the checkerboard, show a white screen until Gecko paints
|
||||||
|
v.setBackgroundColor(Color.WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
mGeckoLayout.addView(v, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
|
mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
|
||||||
|
@ -26,11 +26,12 @@ public class GeckoJarReader {
|
|||||||
Stack<String> jarUrls = parseUrl(url);
|
Stack<String> jarUrls = parseUrl(url);
|
||||||
ZipInputStream inputStream = null;
|
ZipInputStream inputStream = null;
|
||||||
|
|
||||||
|
ZipFile zip = null;
|
||||||
try {
|
try {
|
||||||
// Load the initial jar file as a zip
|
// Load the initial jar file as a zip
|
||||||
URL fileUrl = new URL(jarUrls.pop());
|
URL fileUrl = new URL(jarUrls.pop());
|
||||||
File file = new File(fileUrl.getPath());
|
File file = new File(fileUrl.getPath());
|
||||||
ZipFile zip = new ZipFile(file);
|
zip = new ZipFile(file);
|
||||||
ZipEntry entry = null;
|
ZipEntry entry = null;
|
||||||
|
|
||||||
// loop through children jar files until we reach the innermost one
|
// loop through children jar files until we reach the innermost one
|
||||||
@ -63,6 +64,14 @@ public class GeckoJarReader {
|
|||||||
Log.e(LOGTAG, "Exception ", ex);
|
Log.e(LOGTAG, "Exception ", ex);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Log.e(LOGTAG, "Exception ", ex);
|
Log.e(LOGTAG, "Exception ", ex);
|
||||||
|
} finally {
|
||||||
|
if (zip != null) {
|
||||||
|
try {
|
||||||
|
zip.close();
|
||||||
|
} catch(IOException ex) {
|
||||||
|
Log.e(LOGTAG, "Error closing zip", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return inputStream;
|
return inputStream;
|
||||||
|
@ -33,6 +33,9 @@ public final class GeckoProfile {
|
|||||||
private File mMozDir;
|
private File mMozDir;
|
||||||
private File mDir;
|
private File mDir;
|
||||||
|
|
||||||
|
// this short timeout is a temporary fix until bug 735399 is implemented
|
||||||
|
private static final long SESSION_TIMEOUT = 30 * 1000; // 30 seconds
|
||||||
|
|
||||||
public static GeckoProfile get(Context context) {
|
public static GeckoProfile get(Context context) {
|
||||||
return get(context, null);
|
return get(context, null);
|
||||||
}
|
}
|
||||||
@ -80,12 +83,19 @@ public final class GeckoProfile {
|
|||||||
return mDir;
|
return mDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSession() {
|
public boolean shouldRestoreSession() {
|
||||||
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start check sessionstore.js exists");
|
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start check sessionstore.js exists");
|
||||||
File dir = getDir();
|
File dir = getDir();
|
||||||
boolean hasSession = (dir != null && new File(dir, "sessionstore.js").exists());
|
if (dir == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
File sessionFile = new File(dir, "sessionstore.js");
|
||||||
|
if (!sessionFile.exists())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean shouldRestore = (System.currentTimeMillis() - sessionFile.lastModified() < SESSION_TIMEOUT);
|
||||||
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - finish check sessionstore.js exists");
|
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - finish check sessionstore.js exists");
|
||||||
return hasSession;
|
return shouldRestore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String readSessionFile(boolean geckoReady) {
|
public String readSessionFile(boolean geckoReady) {
|
||||||
|
@ -97,6 +97,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
|||||||
Bookmarks.TITLE,
|
Bookmarks.TITLE,
|
||||||
Bookmarks.TYPE,
|
Bookmarks.TYPE,
|
||||||
Bookmarks.PARENT,
|
Bookmarks.PARENT,
|
||||||
|
Bookmarks.KEYWORD,
|
||||||
Bookmarks.FAVICON };
|
Bookmarks.FAVICON };
|
||||||
|
|
||||||
public LocalBrowserDB(String profile) {
|
public LocalBrowserDB(String profile) {
|
||||||
|
@ -367,6 +367,7 @@ public class GeckoLayerClient implements GeckoEventResponder,
|
|||||||
// a full viewport update, which is fine because if browser.js has somehow moved to
|
// a full viewport update, which is fine because if browser.js has somehow moved to
|
||||||
// be out of sync with this first-paint viewport, then we force them back in sync.
|
// be out of sync with this first-paint viewport, then we force them back in sync.
|
||||||
mLayerController.abortPanZoomAnimation();
|
mLayerController.abortPanZoomAnimation();
|
||||||
|
mLayerController.getView().setPaintState(LayerView.PAINT_BEFORE_FIRST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,6 +680,16 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||||||
pixelBuffer.notify();
|
pixelBuffer.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove white screen once we've painted
|
||||||
|
if (mView.getPaintState() == LayerView.PAINT_BEFORE_FIRST) {
|
||||||
|
GeckoAppShell.getMainHandler().postAtFrontOfQueue(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mView.setBackgroundColor(android.graphics.Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mView.setPaintState(LayerView.PAINT_AFTER_FIRST);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
package org.mozilla.gecko.gfx;
|
package org.mozilla.gecko.gfx;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.GeckoAppShell;
|
||||||
import org.mozilla.gecko.GeckoInputConnection;
|
import org.mozilla.gecko.GeckoInputConnection;
|
||||||
import org.mozilla.gecko.gfx.FloatSize;
|
import org.mozilla.gecko.gfx.FloatSize;
|
||||||
import org.mozilla.gecko.gfx.InputConnectionHandler;
|
import org.mozilla.gecko.gfx.InputConnectionHandler;
|
||||||
@ -77,6 +78,14 @@ public class LayerView extends FlexibleGLSurfaceView {
|
|||||||
private static String LOGTAG = "GeckoLayerView";
|
private static String LOGTAG = "GeckoLayerView";
|
||||||
/* List of events to be processed if the page does not prevent them. Should only be touched on the main thread */
|
/* List of events to be processed if the page does not prevent them. Should only be touched on the main thread */
|
||||||
private LinkedList<MotionEvent> mEventQueue = new LinkedList<MotionEvent>();
|
private LinkedList<MotionEvent> mEventQueue = new LinkedList<MotionEvent>();
|
||||||
|
/* Must be a PAINT_xxx constant */
|
||||||
|
private int mPaintState = PAINT_NONE;
|
||||||
|
|
||||||
|
/* Flags used to determine when to show the painted surface. The integer
|
||||||
|
* order must correspond to the order in which these states occur. */
|
||||||
|
public static final int PAINT_NONE = 0;
|
||||||
|
public static final int PAINT_BEFORE_FIRST = 1;
|
||||||
|
public static final int PAINT_AFTER_FIRST = 2;
|
||||||
|
|
||||||
|
|
||||||
public LayerView(Context context, LayerController controller) {
|
public LayerView(Context context, LayerController controller) {
|
||||||
@ -237,5 +246,18 @@ public class LayerView extends FlexibleGLSurfaceView {
|
|||||||
public LayerRenderer getLayerRenderer() {
|
public LayerRenderer getLayerRenderer() {
|
||||||
return mRenderer;
|
return mRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* paintState must be a PAINT_xxx constant. The state will only be changed
|
||||||
|
* if paintState represents a state that occurs after the current state. */
|
||||||
|
public void setPaintState(int paintState) {
|
||||||
|
if (paintState > mPaintState) {
|
||||||
|
Log.d(LOGTAG, "LayerView paint state set to " + paintState);
|
||||||
|
mPaintState = paintState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPaintState() {
|
||||||
|
return mPaintState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,25 +79,6 @@ public class PlaceholderLayerClient {
|
|||||||
} else {
|
} else {
|
||||||
mViewport = new ViewportMetrics();
|
mViewport = new ViewportMetrics();
|
||||||
}
|
}
|
||||||
loadScreenshot();
|
|
||||||
|
|
||||||
|
|
||||||
if (mViewportUnknown)
|
|
||||||
mViewport.setViewport(mLayerController.getViewport());
|
|
||||||
mLayerController.setViewportMetrics(mViewport);
|
|
||||||
|
|
||||||
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);
|
|
||||||
SingleTileLayer tileLayer = new SingleTileLayer(image);
|
|
||||||
|
|
||||||
tileLayer.beginTransaction(); // calling thread irrelevant; nobody else has a ref to tileLayer yet
|
|
||||||
try {
|
|
||||||
Point origin = PointUtils.round(mViewport.getOrigin());
|
|
||||||
tileLayer.setPosition(new Rect(origin.x, origin.y, origin.x + mWidth, origin.y + mHeight));
|
|
||||||
} finally {
|
|
||||||
tileLayer.endTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
mLayerController.setRoot(tileLayer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
@ -107,7 +88,7 @@ public class PlaceholderLayerClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean loadScreenshot() {
|
public boolean loadScreenshot() {
|
||||||
if (GeckoApp.mAppContext.mLastScreen == null)
|
if (GeckoApp.mAppContext.mLastScreen == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -131,6 +112,18 @@ public class PlaceholderLayerClient {
|
|||||||
mLayerController.setPageSize(mViewport.getPageSize());
|
mLayerController.setPageSize(mViewport.getPageSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);
|
||||||
|
SingleTileLayer tileLayer = new SingleTileLayer(image);
|
||||||
|
|
||||||
|
tileLayer.beginTransaction(); // calling thread irrelevant; nobody else has a ref to tileLayer yet
|
||||||
|
try {
|
||||||
|
Point origin = PointUtils.round(mViewport.getOrigin());
|
||||||
|
tileLayer.setPosition(new Rect(origin.x, origin.y, origin.x + mWidth, origin.y + mHeight));
|
||||||
|
} finally {
|
||||||
|
tileLayer.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLayerController.setRoot(tileLayer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +327,7 @@ var BrowserApp = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (this.isAppUpdated())
|
if (this.isAppUpdated())
|
||||||
this.onUpdate();
|
this.onAppUpdated();
|
||||||
},
|
},
|
||||||
|
|
||||||
isAppUpdated: function() {
|
isAppUpdated: function() {
|
||||||
@ -1455,9 +1455,10 @@ function Tab(aURL, aParams) {
|
|||||||
this.create(aURL, aParams);
|
this.create(aURL, aParams);
|
||||||
this._zoom = 1.0;
|
this._zoom = 1.0;
|
||||||
this.userScrollPos = { x: 0, y: 0 };
|
this.userScrollPos = { x: 0, y: 0 };
|
||||||
this._pluginCount = 0;
|
|
||||||
this._pluginOverlayShowing = false;
|
|
||||||
this.contentDocumentIsDisplayed = true;
|
this.contentDocumentIsDisplayed = true;
|
||||||
|
this.clickToPlayPluginDoorhangerShown = false;
|
||||||
|
this.clickToPlayPluginsActivated = false;
|
||||||
|
this.loadEventProcessed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tab.prototype = {
|
Tab.prototype = {
|
||||||
@ -1511,6 +1512,7 @@ Tab.prototype = {
|
|||||||
this.browser.sessionHistory.addSHistoryListener(this);
|
this.browser.sessionHistory.addSHistoryListener(this);
|
||||||
|
|
||||||
this.browser.addEventListener("DOMContentLoaded", this, true);
|
this.browser.addEventListener("DOMContentLoaded", this, true);
|
||||||
|
this.browser.addEventListener("load", this, true);
|
||||||
this.browser.addEventListener("DOMLinkAdded", this, true);
|
this.browser.addEventListener("DOMLinkAdded", this, true);
|
||||||
this.browser.addEventListener("DOMTitleChanged", this, true);
|
this.browser.addEventListener("DOMTitleChanged", this, true);
|
||||||
this.browser.addEventListener("DOMWindowClose", this, true);
|
this.browser.addEventListener("DOMWindowClose", this, true);
|
||||||
@ -1518,8 +1520,6 @@ Tab.prototype = {
|
|||||||
this.browser.addEventListener("scroll", this, true);
|
this.browser.addEventListener("scroll", this, true);
|
||||||
this.browser.addEventListener("MozScrolledAreaChanged", this, true);
|
this.browser.addEventListener("MozScrolledAreaChanged", this, true);
|
||||||
this.browser.addEventListener("PluginClickToPlay", this, true);
|
this.browser.addEventListener("PluginClickToPlay", this, true);
|
||||||
this.browser.addEventListener("pagehide", this, true);
|
|
||||||
this.browser.addEventListener("pageshow", this, true);
|
|
||||||
|
|
||||||
Services.obs.addObserver(this, "before-first-paint", false);
|
Services.obs.addObserver(this, "before-first-paint", false);
|
||||||
|
|
||||||
@ -1558,6 +1558,7 @@ Tab.prototype = {
|
|||||||
|
|
||||||
this.browser.removeProgressListener(this);
|
this.browser.removeProgressListener(this);
|
||||||
this.browser.removeEventListener("DOMContentLoaded", this, true);
|
this.browser.removeEventListener("DOMContentLoaded", this, true);
|
||||||
|
this.browser.removeEventListener("load", this, true);
|
||||||
this.browser.removeEventListener("DOMLinkAdded", this, true);
|
this.browser.removeEventListener("DOMLinkAdded", this, true);
|
||||||
this.browser.removeEventListener("DOMTitleChanged", this, true);
|
this.browser.removeEventListener("DOMTitleChanged", this, true);
|
||||||
this.browser.removeEventListener("DOMWindowClose", this, true);
|
this.browser.removeEventListener("DOMWindowClose", this, true);
|
||||||
@ -1565,8 +1566,6 @@ Tab.prototype = {
|
|||||||
this.browser.removeEventListener("scroll", this, true);
|
this.browser.removeEventListener("scroll", this, true);
|
||||||
this.browser.removeEventListener("PluginClickToPlay", this, true);
|
this.browser.removeEventListener("PluginClickToPlay", this, true);
|
||||||
this.browser.removeEventListener("MozScrolledAreaChanged", this, true);
|
this.browser.removeEventListener("MozScrolledAreaChanged", this, true);
|
||||||
this.browser.removeEventListener("pagehide", this, true);
|
|
||||||
this.browser.removeEventListener("pageshow", this, true);
|
|
||||||
|
|
||||||
Services.obs.removeObserver(this, "before-first-paint");
|
Services.obs.removeObserver(this, "before-first-paint");
|
||||||
|
|
||||||
@ -1763,13 +1762,25 @@ Tab.prototype = {
|
|||||||
this.browser.removeEventListener("pagehide", listener, true);
|
this.browser.removeEventListener("pagehide", listener, true);
|
||||||
}.bind(this), true);
|
}.bind(this), true);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Show a plugin doorhanger if there are plugins on the page but no
|
case "load": {
|
||||||
// clickable overlays showing (this doesn't work on pages loaded after
|
this.loadEventProcessed = true;
|
||||||
// back/forward navigation - see bug 719875)
|
// Show a plugin doorhanger if there are no clickable overlays showing
|
||||||
if (this._pluginCount && !this._pluginOverlayShowing)
|
let contentWindow = this.browser.contentWindow;
|
||||||
|
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils);
|
||||||
|
// XXX not sure if we should enable plugins for the parent documents...
|
||||||
|
let plugins = cwu.plugins;
|
||||||
|
let isAnyPluginVisible = false;
|
||||||
|
for (let plugin of plugins) {
|
||||||
|
let overlay = plugin.ownerDocument.getAnonymousElementByAttribute(plugin, "class", "mainBox");
|
||||||
|
if (overlay && !PluginHelper.isTooSmall(plugin, overlay))
|
||||||
|
isAnyPluginVisible = true;
|
||||||
|
}
|
||||||
|
if (plugins && plugins.length && !isAnyPluginVisible)
|
||||||
PluginHelper.showDoorHanger(this);
|
PluginHelper.showDoorHanger(this);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1877,39 +1888,36 @@ Tab.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "PluginClickToPlay": {
|
case "PluginClickToPlay": {
|
||||||
// Keep track of the number of plugins to know whether or not to show
|
|
||||||
// the hidden plugins doorhanger
|
|
||||||
this._pluginCount++;
|
|
||||||
|
|
||||||
let plugin = aEvent.target;
|
let plugin = aEvent.target;
|
||||||
let overlay = plugin.ownerDocument.getAnonymousElementByAttribute(plugin, "class", "mainBox");
|
|
||||||
if (!overlay)
|
if (this.clickToPlayPluginsActivated) {
|
||||||
|
PluginHelper.playPlugin(plugin);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If the overlay is too small, hide the overlay and act like this
|
// If the overlay is too small, hide the overlay and act like this
|
||||||
// is a hidden plugin object
|
// is a hidden plugin object
|
||||||
if (PluginHelper.isTooSmall(plugin, overlay)) {
|
let overlay = plugin.ownerDocument.getAnonymousElementByAttribute(plugin, "class", "mainBox");
|
||||||
overlay.style.visibility = "hidden";
|
if (!overlay || PluginHelper.isTooSmall(plugin, overlay)) {
|
||||||
|
if (overlay)
|
||||||
|
overlay.style.visibility = "hidden";
|
||||||
|
if (this.loadEventProcessed && !this.clickToPlayPluginDoorhangerShown)
|
||||||
|
PluginHelper.showDoorHanger(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add click to play listener to the overlay
|
// Add click to play listener to the overlay
|
||||||
overlay.addEventListener("click", (function(event) {
|
overlay.addEventListener("click", function(e) {
|
||||||
// Play all the plugin objects when the user clicks on one
|
if (e) {
|
||||||
PluginHelper.playAllPlugins(this, event);
|
if (!e.isTrusted)
|
||||||
}).bind(this), true);
|
return;
|
||||||
|
e.preventDefault();
|
||||||
this._pluginOverlayShowing = true;
|
}
|
||||||
break;
|
let win = e.target.ownerDocument.defaultView.top;
|
||||||
}
|
let tab = BrowserApp.getTabForWindow(win);
|
||||||
|
tab.clickToPlayPluginsActivated = true;
|
||||||
case "pagehide": {
|
PluginHelper.playAllPlugins(win);
|
||||||
// Check to make sure it's top-level pagehide
|
}, true);
|
||||||
if (aEvent.target.defaultView == this.browser.contentWindow) {
|
|
||||||
// Reset plugin state when we leave the page
|
|
||||||
this._pluginCount = 0;
|
|
||||||
this._pluginOverlayShowing = false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1969,6 +1977,11 @@ Tab.prototype = {
|
|||||||
contentType = browser.contentDocument.contentType;
|
contentType = browser.contentDocument.contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset state of click-to-play plugin notifications.
|
||||||
|
this.clickToPlayPluginDoorhangerShown = false;
|
||||||
|
this.clickToPlayPluginsActivated = false;
|
||||||
|
this.loadEventProcessed = false;
|
||||||
|
|
||||||
let message = {
|
let message = {
|
||||||
gecko: {
|
gecko: {
|
||||||
type: "Content:LocationChange",
|
type: "Content:LocationChange",
|
||||||
@ -3854,12 +3867,13 @@ var ClipboardHelper = {
|
|||||||
|
|
||||||
var PluginHelper = {
|
var PluginHelper = {
|
||||||
showDoorHanger: function(aTab) {
|
showDoorHanger: function(aTab) {
|
||||||
|
aTab.clickToPlayPluginDoorhangerShown = true;
|
||||||
let message = Strings.browser.GetStringFromName("clickToPlayPlugins.message");
|
let message = Strings.browser.GetStringFromName("clickToPlayPlugins.message");
|
||||||
let buttons = [
|
let buttons = [
|
||||||
{
|
{
|
||||||
label: Strings.browser.GetStringFromName("clickToPlayPlugins.yes"),
|
label: Strings.browser.GetStringFromName("clickToPlayPlugins.yes"),
|
||||||
callback: function() {
|
callback: function() {
|
||||||
PluginHelper.playAllPlugins(aTab);
|
PluginHelper.playAllPlugins(aTab.browser.contentWindow);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -3872,41 +3886,21 @@ var PluginHelper = {
|
|||||||
NativeWindow.doorhanger.show(message, "ask-to-play-plugins", buttons, aTab.id);
|
NativeWindow.doorhanger.show(message, "ask-to-play-plugins", buttons, aTab.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
playAllPlugins: function(aTab, aEvent) {
|
playAllPlugins: function(aContentWindow) {
|
||||||
if (aEvent) {
|
let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
if (!aEvent.isTrusted)
|
.getInterface(Ci.nsIDOMWindowUtils);
|
||||||
return;
|
// XXX not sure if we should enable plugins for the parent documents...
|
||||||
aEvent.preventDefault();
|
let plugins = cwu.plugins;
|
||||||
}
|
if (!plugins || !plugins.length)
|
||||||
|
return;
|
||||||
|
|
||||||
this._findAndPlayAllPlugins(aTab.browser.contentWindow);
|
plugins.forEach(this.playPlugin);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Helper function that recurses through sub-frames to find all plugin objects
|
playPlugin: function(plugin) {
|
||||||
_findAndPlayAllPlugins: function _findAndPlayAllPlugins(aWindow) {
|
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||||
let embeds = aWindow.document.getElementsByTagName("embed");
|
if (!objLoadingContent.activated)
|
||||||
for (let i = 0; i < embeds.length; i++) {
|
objLoadingContent.playPlugin();
|
||||||
if (!embeds[i].hasAttribute("played"))
|
|
||||||
this._playPlugin(embeds[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let objects = aWindow.document.getElementsByTagName("object");
|
|
||||||
for (let i = 0; i < objects.length; i++) {
|
|
||||||
if (!objects[i].hasAttribute("played"))
|
|
||||||
this._playPlugin(objects[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < aWindow.frames.length; i++) {
|
|
||||||
this._findAndPlayAllPlugins(aWindow.frames[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_playPlugin: function _playPlugin(aPlugin) {
|
|
||||||
let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
|
||||||
objLoadingContent.playPlugin();
|
|
||||||
|
|
||||||
// Set an attribute on the plugin object to avoid re-loading it
|
|
||||||
aPlugin.setAttribute("played", true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getPluginPreference: function getPluginPreference() {
|
getPluginPreference: function getPluginPreference() {
|
||||||
|
@ -205,6 +205,10 @@ setting > vbox {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preferences-description {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.preferences-description:empty {
|
.preferences-description:empty {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -217,7 +221,7 @@ setting[type="string"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preferences-alignment > textbox {
|
.preferences-alignment > textbox {
|
||||||
margin-top: 12px;
|
margin: 12px 0 0 0;
|
||||||
font-size: 22px !important;
|
font-size: 22px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,16 +229,16 @@ checkbox {
|
|||||||
-moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox-with-spacing") !important;
|
-moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox-with-spacing") !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkbox[label=""] > .checkbox-label-box,
|
||||||
|
checkbox:not([label]) > .checkbox-label-box {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox-check {
|
.checkbox-check {
|
||||||
border: 2px transparent;
|
background: url("chrome://browser/skin/images/checkbox_unchecked.png") no-repeat 50% 50%;
|
||||||
-moz-border-top-colors: -moz-initial;
|
border: 0 transparent;
|
||||||
-moz-border-right-colors: -moz-initial;
|
|
||||||
-moz-border-bottom-colors: -moz-initial;
|
|
||||||
-moz-border-left-colors: -moz-initial;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
background: url("chrome://browser/skin/images/checkbox_unchecked.png") no-repeat 50% 50%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setting:active checkbox > .checkbox-spacer-box > .checkbox-check {
|
setting:active checkbox > .checkbox-spacer-box > .checkbox-check {
|
||||||
@ -257,6 +261,62 @@ checkbox[checked="true"][disabled="true"] > .checkbox-spacer-box > .checkbox-che
|
|||||||
background-image: url("chrome://browser/skin/images/checkbox_checked_disabled.png");
|
background-image: url("chrome://browser/skin/images/checkbox_checked_disabled.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Textbox */
|
||||||
|
|
||||||
|
textbox[type="number"] > spinbuttons {
|
||||||
|
visibility: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
textbox {
|
||||||
|
background: white -moz-linear-gradient(top, rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 3px, rgba(255,255,255,0.2) 16px);
|
||||||
|
border-radius: 3px;
|
||||||
|
border-color: rgb(94,128,153);
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textbox-input-box {
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Menulist */
|
||||||
|
|
||||||
|
menulist {
|
||||||
|
-moz-appearance: none !important;
|
||||||
|
-moz-user-focus: ignore;
|
||||||
|
/* min-width: 200px !important; */
|
||||||
|
color: #000 !important;
|
||||||
|
border-radius: 5px;
|
||||||
|
border-color: rgb(94,128,153);
|
||||||
|
border-style: solid;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #cacdd5;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgb(94,128,153);
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menulist-label {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
menulist > menupopup > menuitem > label{
|
||||||
|
-moz-padding-start:3px !important;
|
||||||
|
-moz-padding-end:7px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
menulist > dropmarker {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
margin-left: @margin_snormal@;
|
||||||
|
background-color: transparent; /* for windows */
|
||||||
|
border: none; /* for windows */
|
||||||
|
-moz-box-align: center;
|
||||||
|
-moz-box-pack: center;
|
||||||
|
list-style-image: url("chrome://browser/skin/images/dropmarker.svg") !important;
|
||||||
|
-moz-image-region: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
/* XBL bindings */
|
/* XBL bindings */
|
||||||
|
|
||||||
|
@ -809,11 +809,23 @@ pref("network.http.pipelining.ssl" , false); // disable pipelining over SSL
|
|||||||
pref("network.http.proxy.pipelining", false);
|
pref("network.http.proxy.pipelining", false);
|
||||||
|
|
||||||
// Max number of requests in the pipeline
|
// Max number of requests in the pipeline
|
||||||
pref("network.http.pipelining.maxrequests" , 4);
|
pref("network.http.pipelining.maxrequests" , 32);
|
||||||
|
|
||||||
|
// An optimistic request is one pipelined when policy might allow a new
|
||||||
|
// connection instead
|
||||||
|
pref("network.http.pipelining.max-optimistic-requests" , 4);
|
||||||
|
|
||||||
|
pref("network.http.pipelining.aggressive", false);
|
||||||
|
pref("network.http.pipelining.maxsize" , 300000);
|
||||||
|
pref("network.http.pipelining.read-timeout", 10000);
|
||||||
|
|
||||||
// Prompt for 307 redirects
|
// Prompt for 307 redirects
|
||||||
pref("network.http.prompt-temp-redirect", true);
|
pref("network.http.prompt-temp-redirect", true);
|
||||||
|
|
||||||
|
// If true generate CORRUPTED_CONTENT errors for entities that
|
||||||
|
// contain an invalid Assoc-Req response header
|
||||||
|
pref("network.http.assoc-req.enforce", false);
|
||||||
|
|
||||||
// On networks deploying QoS, it is recommended that these be lockpref()'d,
|
// On networks deploying QoS, it is recommended that these be lockpref()'d,
|
||||||
// since inappropriate marking can easily overwhelm bandwidth reservations
|
// since inappropriate marking can easily overwhelm bandwidth reservations
|
||||||
// for certain services (i.e. EF for VoIP, AF4x for interactive video,
|
// for certain services (i.e. EF for VoIP, AF4x for interactive video,
|
||||||
|
@ -159,6 +159,13 @@ interface nsIRequest : nsISupports
|
|||||||
* The following flags control the flow of data into the cache.
|
* The following flags control the flow of data into the cache.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This flag prevents loading of the request with an HTTP pipeline.
|
||||||
|
* Generally this is because the resource is expected to take a
|
||||||
|
* while to load and may cause head of line blocking problems.
|
||||||
|
*/
|
||||||
|
const unsigned long INHIBIT_PIPELINE = 1 << 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This flag prevents caching of any kind. It does not, however, prevent
|
* This flag prevents caching of any kind. It does not, however, prevent
|
||||||
* cached content from being used to satisfy this request.
|
* cached content from being used to satisfy this request.
|
||||||
|
117
netwerk/cache/nsCacheService.cpp
vendored
117
netwerk/cache/nsCacheService.cpp
vendored
@ -1020,7 +1020,8 @@ public:
|
|||||||
nsnull);
|
nsnull);
|
||||||
|
|
||||||
// Don't delete the request if it was queued
|
// Don't delete the request if it was queued
|
||||||
if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION)
|
if (!(mRequest->IsBlocking() &&
|
||||||
|
rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
|
||||||
delete mRequest;
|
delete mRequest;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -1033,6 +1034,91 @@ private:
|
|||||||
nsCacheRequest *mRequest;
|
nsCacheRequest *mRequest;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* nsNotifyDoomListener
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
class nsNotifyDoomListener : public nsRunnable {
|
||||||
|
public:
|
||||||
|
nsNotifyDoomListener(nsICacheListener *listener,
|
||||||
|
nsresult status)
|
||||||
|
: mListener(listener) // transfers reference
|
||||||
|
, mStatus(status)
|
||||||
|
{}
|
||||||
|
|
||||||
|
NS_IMETHOD Run()
|
||||||
|
{
|
||||||
|
mListener->OnCacheEntryDoomed(mStatus);
|
||||||
|
NS_RELEASE(mListener);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsICacheListener *mListener;
|
||||||
|
nsresult mStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* nsDoomEvent
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
class nsDoomEvent : public nsRunnable {
|
||||||
|
public:
|
||||||
|
nsDoomEvent(nsCacheSession *session,
|
||||||
|
const nsACString &key,
|
||||||
|
nsICacheListener *listener)
|
||||||
|
{
|
||||||
|
mKey = *session->ClientID();
|
||||||
|
mKey.Append(':');
|
||||||
|
mKey.Append(key);
|
||||||
|
mStoragePolicy = session->StoragePolicy();
|
||||||
|
mListener = listener;
|
||||||
|
mThread = do_GetCurrentThread();
|
||||||
|
// We addref the listener here and release it in nsNotifyDoomListener
|
||||||
|
// on the callers thread. If posting of nsNotifyDoomListener event fails
|
||||||
|
// we leak the listener which is better than releasing it on a wrong
|
||||||
|
// thread.
|
||||||
|
NS_IF_ADDREF(mListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD Run()
|
||||||
|
{
|
||||||
|
nsCacheServiceAutoLock lock;
|
||||||
|
|
||||||
|
bool foundActive = true;
|
||||||
|
nsresult status = NS_ERROR_NOT_AVAILABLE;
|
||||||
|
nsCacheEntry *entry;
|
||||||
|
entry = nsCacheService::gService->mActiveEntries.GetEntry(&mKey);
|
||||||
|
if (!entry) {
|
||||||
|
bool collision = false;
|
||||||
|
foundActive = false;
|
||||||
|
entry = nsCacheService::gService->SearchCacheDevices(&mKey,
|
||||||
|
mStoragePolicy,
|
||||||
|
&collision);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
status = NS_OK;
|
||||||
|
nsCacheService::gService->DoomEntry_Internal(entry, foundActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mListener) {
|
||||||
|
mThread->Dispatch(new nsNotifyDoomListener(mListener, status),
|
||||||
|
NS_DISPATCH_NORMAL);
|
||||||
|
// posted event will release the reference on the correct thread
|
||||||
|
mListener = nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsCString mKey;
|
||||||
|
nsCacheStoragePolicy mStoragePolicy;
|
||||||
|
nsICacheListener *mListener;
|
||||||
|
nsCOMPtr<nsIThread> mThread;
|
||||||
|
};
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* nsCacheService
|
* nsCacheService
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@ -1349,6 +1435,22 @@ nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsCacheService::DoomEntry(nsCacheSession *session,
|
||||||
|
const nsACString &key,
|
||||||
|
nsICacheListener *listener)
|
||||||
|
{
|
||||||
|
CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
|
||||||
|
session, PromiseFlatCString(key).get()));
|
||||||
|
NS_ASSERTION(gService, "nsCacheService::gService is null.");
|
||||||
|
|
||||||
|
if (!gService->mInitialized)
|
||||||
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
|
|
||||||
|
return DispatchToCacheIOThread(new nsDoomEvent(session, key, listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy)
|
nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy)
|
||||||
{
|
{
|
||||||
@ -1662,11 +1764,13 @@ nsCacheService::ProcessRequest(nsCacheRequest * request,
|
|||||||
// entry->RequestAccess queues request on entry
|
// entry->RequestAccess queues request on entry
|
||||||
rv = entry->RequestAccess(request, &accessGranted);
|
rv = entry->RequestAccess(request, &accessGranted);
|
||||||
if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
|
if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
|
||||||
|
|
||||||
if (request->mListener) // async exits - validate, doom, or close will resume
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
if (request->IsBlocking()) {
|
if (request->IsBlocking()) {
|
||||||
|
if (request->mListener) {
|
||||||
|
// async exits - validate, doom, or close will resume
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
// XXX this is probably wrong...
|
// XXX this is probably wrong...
|
||||||
Unlock();
|
Unlock();
|
||||||
rv = request->WaitForValidation();
|
rv = request->WaitForValidation();
|
||||||
@ -1773,7 +1877,8 @@ nsCacheService::OpenCacheEntry(nsCacheSession * session,
|
|||||||
rv = gService->ProcessRequest(request, true, result);
|
rv = gService->ProcessRequest(request, true, result);
|
||||||
|
|
||||||
// delete requests that have completed
|
// delete requests that have completed
|
||||||
if (!(listener && (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
|
if (!(listener && blockingMode &&
|
||||||
|
(rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
|
||||||
delete request;
|
delete request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
netwerk/cache/nsCacheService.h
vendored
5
netwerk/cache/nsCacheService.h
vendored
@ -99,6 +99,10 @@ public:
|
|||||||
static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
|
static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
|
||||||
bool * result);
|
bool * result);
|
||||||
|
|
||||||
|
static nsresult DoomEntry(nsCacheSession *session,
|
||||||
|
const nsACString &key,
|
||||||
|
nsICacheListener *listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Methods called by nsCacheEntryDescriptor
|
* Methods called by nsCacheEntryDescriptor
|
||||||
*/
|
*/
|
||||||
@ -198,6 +202,7 @@ private:
|
|||||||
friend class nsSetSmartSizeEvent;
|
friend class nsSetSmartSizeEvent;
|
||||||
friend class nsBlockOnCacheThreadEvent;
|
friend class nsBlockOnCacheThreadEvent;
|
||||||
friend class nsSetDiskSmartSizeCallback;
|
friend class nsSetDiskSmartSizeCallback;
|
||||||
|
friend class nsDoomEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal Methods
|
* Internal Methods
|
||||||
|
11
netwerk/cache/nsCacheSession.cpp
vendored
11
netwerk/cache/nsCacheSession.cpp
vendored
@ -102,13 +102,14 @@ nsCacheSession::OpenCacheEntry(const nsACString & key,
|
|||||||
|
|
||||||
NS_IMETHODIMP nsCacheSession::AsyncOpenCacheEntry(const nsACString & key,
|
NS_IMETHODIMP nsCacheSession::AsyncOpenCacheEntry(const nsACString & key,
|
||||||
nsCacheAccessMode accessRequested,
|
nsCacheAccessMode accessRequested,
|
||||||
nsICacheListener *listener)
|
nsICacheListener *listener,
|
||||||
|
bool noWait)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
rv = nsCacheService::OpenCacheEntry(this,
|
rv = nsCacheService::OpenCacheEntry(this,
|
||||||
key,
|
key,
|
||||||
accessRequested,
|
accessRequested,
|
||||||
nsICache::BLOCKING,
|
!noWait,
|
||||||
listener,
|
listener,
|
||||||
nsnull); // no result
|
nsnull); // no result
|
||||||
|
|
||||||
@ -127,3 +128,9 @@ NS_IMETHODIMP nsCacheSession::IsStorageEnabled(bool *result)
|
|||||||
|
|
||||||
return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result);
|
return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP nsCacheSession::DoomEntry(const nsACString &key,
|
||||||
|
nsICacheListener *listener)
|
||||||
|
{
|
||||||
|
return nsCacheService::DoomEntry(this, key, listener);
|
||||||
|
}
|
||||||
|
9
netwerk/cache/nsICacheListener.idl
vendored
9
netwerk/cache/nsICacheListener.idl
vendored
@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
interface nsICacheEntryDescriptor;
|
interface nsICacheEntryDescriptor;
|
||||||
|
|
||||||
[scriptable, uuid(638c3848-778b-4851-8ff3-9400f65b8773)]
|
[scriptable, uuid(8eadf2ed-8cac-4961-8025-6da6d5827e74)]
|
||||||
interface nsICacheListener : nsISupports
|
interface nsICacheListener : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -58,4 +58,11 @@ interface nsICacheListener : nsISupports
|
|||||||
void onCacheEntryAvailable(in nsICacheEntryDescriptor descriptor,
|
void onCacheEntryAvailable(in nsICacheEntryDescriptor descriptor,
|
||||||
in nsCacheAccessMode accessGranted,
|
in nsCacheAccessMode accessGranted,
|
||||||
in nsresult status);
|
in nsresult status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when nsCacheSession::DoomEntry() is completed. The status
|
||||||
|
* parameter is NS_OK when the entry was doomed, or NS_ERROR_NOT_AVAILABLE
|
||||||
|
* when there is no such entry.
|
||||||
|
*/
|
||||||
|
void onCacheEntryDoomed(in nsresult status);
|
||||||
};
|
};
|
||||||
|
25
netwerk/cache/nsICacheSession.idl
vendored
25
netwerk/cache/nsICacheSession.idl
vendored
@ -46,7 +46,7 @@
|
|||||||
interface nsICacheEntryDescriptor;
|
interface nsICacheEntryDescriptor;
|
||||||
interface nsICacheListener;
|
interface nsICacheListener;
|
||||||
|
|
||||||
[scriptable, uuid(ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e)]
|
[scriptable, uuid(1dd7708c-de48-4ffe-b5aa-cd218c762887)]
|
||||||
interface nsICacheSession : nsISupports
|
interface nsICacheSession : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -76,13 +76,17 @@ interface nsICacheSession : nsISupports
|
|||||||
in boolean blockingMode);
|
in boolean blockingMode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronous cache access. Does not block the calling thread.
|
* Asynchronous cache access. Does not block the calling thread. Instead,
|
||||||
* Instead, the listener will be notified when the descriptor is
|
* the listener will be notified when the descriptor is available. If
|
||||||
* available.
|
* 'noWait' is set to true, the listener will be notified immediately with
|
||||||
|
* status NS_ERROR_CACHE_WAIT_FOR_VALIDATION rather than queuing the request
|
||||||
|
* when another descriptor has been given WRITE access but hasn't validated
|
||||||
|
* the entry yet.
|
||||||
*/
|
*/
|
||||||
void asyncOpenCacheEntry(in ACString key,
|
void asyncOpenCacheEntry(in ACString key,
|
||||||
in nsCacheAccessMode accessRequested,
|
in nsCacheAccessMode accessRequested,
|
||||||
in nsICacheListener listener);
|
in nsICacheListener listener,
|
||||||
|
[optional] in boolean noWait);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evict all entries for this session's clientID according to its storagePolicy.
|
* Evict all entries for this session's clientID according to its storagePolicy.
|
||||||
@ -94,4 +98,11 @@ interface nsICacheSession : nsISupports
|
|||||||
* are currently enabled for instantiation if they don't already exist.
|
* are currently enabled for instantiation if they don't already exist.
|
||||||
*/
|
*/
|
||||||
boolean isStorageEnabled();
|
boolean isStorageEnabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously doom an entry specified by the key. Listener will be
|
||||||
|
* notified about the status of the operation. Null may be passed if caller
|
||||||
|
* doesn't care about the result.
|
||||||
|
*/
|
||||||
|
void doomEntry(in ACString key, in nsICacheListener listener);
|
||||||
};
|
};
|
||||||
|
@ -2095,6 +2095,14 @@ nsFtpState::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsFtpState::OnCacheEntryDoomed(nsresult status)
|
||||||
|
{
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsFtpState::OnStartRequest(nsIRequest *request, nsISupports *context)
|
nsFtpState::OnStartRequest(nsIRequest *request, nsISupports *context)
|
||||||
{
|
{
|
||||||
@ -2288,7 +2296,7 @@ nsFtpState::CheckCache()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
|
if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
|
||||||
rv = session->AsyncOpenCacheEntry(key, accessReq, this);
|
rv = session->AsyncOpenCacheEntry(key, accessReq, this, false);
|
||||||
return NS_SUCCEEDED(rv);
|
return NS_SUCCEEDED(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2034,6 +2034,30 @@ SpdySession::Transport()
|
|||||||
return mConnection->Transport();
|
return mConnection->Transport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRUint32
|
||||||
|
SpdySession::CancelPipeline(nsresult reason)
|
||||||
|
{
|
||||||
|
// we don't pipeline inside spdy, so this isn't an issue
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAHttpTransaction::Classifier
|
||||||
|
SpdySession::Classification()
|
||||||
|
{
|
||||||
|
if (!mConnection)
|
||||||
|
return nsAHttpTransaction::CLASS_GENERAL;
|
||||||
|
return mConnection->Classification();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SpdySession::Classify(nsAHttpTransaction::Classifier newclass)
|
||||||
|
{
|
||||||
|
if (!mConnection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mConnection->Classify(newclass);
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// unused methods of nsAHttpTransaction
|
// unused methods of nsAHttpTransaction
|
||||||
// We can be sure of this because SpdySession is only constructed in
|
// We can be sure of this because SpdySession is only constructed in
|
||||||
@ -2064,8 +2088,7 @@ SpdySession::SetSSLConnectFailed()
|
|||||||
bool
|
bool
|
||||||
SpdySession::IsDone()
|
SpdySession::IsDone()
|
||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(false, "SpdySession::IsDone()");
|
return !mStreamTransactionHash.Count();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -2075,6 +2098,13 @@ SpdySession::Status()
|
|||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRUint8
|
||||||
|
SpdySession::Caps()
|
||||||
|
{
|
||||||
|
NS_ABORT_IF_FALSE(false, "SpdySession::Caps()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
PRUint32
|
PRUint32
|
||||||
SpdySession::Available()
|
SpdySession::Available()
|
||||||
{
|
{
|
||||||
@ -2132,6 +2162,42 @@ SpdySession::TakeSubTransactions(
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
SpdySession::AddTransaction(nsAHttpTransaction *)
|
||||||
|
{
|
||||||
|
// This API is meant for pipelining, SpdySession's should be
|
||||||
|
// extended with AddStream()
|
||||||
|
|
||||||
|
NS_ABORT_IF_FALSE(false,
|
||||||
|
"SpdySession::AddTransaction() should not be called");
|
||||||
|
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRUint32
|
||||||
|
SpdySession::PipelineDepth()
|
||||||
|
{
|
||||||
|
return IsDone() ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
SpdySession::SetPipelinePosition(PRInt32 position)
|
||||||
|
{
|
||||||
|
// This API is meant for pipelining, SpdySession's should be
|
||||||
|
// extended with AddStream()
|
||||||
|
|
||||||
|
NS_ABORT_IF_FALSE(false,
|
||||||
|
"SpdySession::SetPipelinePosition() should not be called");
|
||||||
|
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt32
|
||||||
|
SpdySession::PipelinePosition()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Pass through methods of nsAHttpConnection
|
// Pass through methods of nsAHttpConnection
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -2179,6 +2245,13 @@ SpdySession::PushBack(const char *buf, PRUint32 len)
|
|||||||
return mConnection->PushBack(buf, len);
|
return mConnection->PushBack(buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SpdySession::IsProxyConnectInProgress()
|
||||||
|
{
|
||||||
|
NS_ABORT_IF_FALSE(mConnection, "no connection");
|
||||||
|
return mConnection->IsProxyConnectInProgress();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SpdySession::LastTransactionExpectedNoContent()
|
SpdySession::LastTransactionExpectedNoContent()
|
||||||
{
|
{
|
||||||
|
@ -75,10 +75,9 @@ public:
|
|||||||
|
|
||||||
bool AddStream(nsAHttpTransaction *, PRInt32);
|
bool AddStream(nsAHttpTransaction *, PRInt32);
|
||||||
bool CanReuse() { return !mShouldGoAway && !mClosed; }
|
bool CanReuse() { return !mShouldGoAway && !mClosed; }
|
||||||
void DontReuse();
|
|
||||||
bool RoomForMoreStreams();
|
bool RoomForMoreStreams();
|
||||||
|
|
||||||
// When the connection is active this is called every 15 seconds
|
// When the connection is active this is called every 1 second
|
||||||
void ReadTimeoutTick(PRIntervalTime now);
|
void ReadTimeoutTick(PRIntervalTime now);
|
||||||
|
|
||||||
// Idle time represents time since "goodput".. e.g. a data or header frame
|
// Idle time represents time since "goodput".. e.g. a data or header frame
|
||||||
|
@ -39,8 +39,8 @@
|
|||||||
#define nsAHttpConnection_h__
|
#define nsAHttpConnection_h__
|
||||||
|
|
||||||
#include "nsISupports.h"
|
#include "nsISupports.h"
|
||||||
|
#include "nsAHttpTransaction.h"
|
||||||
|
|
||||||
class nsAHttpTransaction;
|
|
||||||
class nsHttpRequestHead;
|
class nsHttpRequestHead;
|
||||||
class nsHttpResponseHead;
|
class nsHttpResponseHead;
|
||||||
class nsHttpConnectionInfo;
|
class nsHttpConnectionInfo;
|
||||||
@ -120,13 +120,19 @@ public:
|
|||||||
// persistent... important in determining the end of a response.
|
// persistent... important in determining the end of a response.
|
||||||
virtual bool IsPersistent() = 0;
|
virtual bool IsPersistent() = 0;
|
||||||
|
|
||||||
// called to determine if a connection has been reused.
|
// called to determine or set if a connection has been reused.
|
||||||
virtual bool IsReused() = 0;
|
virtual bool IsReused() = 0;
|
||||||
|
virtual void DontReuse() = 0;
|
||||||
|
|
||||||
// called by a transaction when the transaction reads more from the socket
|
// called by a transaction when the transaction reads more from the socket
|
||||||
// than it should have (eg. containing part of the next pipelined response).
|
// than it should have (eg. containing part of the next pipelined response).
|
||||||
virtual nsresult PushBack(const char *data, PRUint32 length) = 0;
|
virtual nsresult PushBack(const char *data, PRUint32 length) = 0;
|
||||||
|
|
||||||
|
// Used to determine if the connection wants read events even though
|
||||||
|
// it has not written out a transaction. Used when a connection has issued
|
||||||
|
// a preamble such as a proxy ssl CONNECT sequence.
|
||||||
|
virtual bool IsProxyConnectInProgress() = 0;
|
||||||
|
|
||||||
// Used by a transaction to manage the state of previous response bodies on
|
// Used by a transaction to manage the state of previous response bodies on
|
||||||
// the same connection and work around buggy servers.
|
// the same connection and work around buggy servers.
|
||||||
virtual bool LastTransactionExpectedNoContent() = 0;
|
virtual bool LastTransactionExpectedNoContent() = 0;
|
||||||
@ -139,6 +145,14 @@ public:
|
|||||||
// Get the nsISocketTransport used by the connection without changing
|
// Get the nsISocketTransport used by the connection without changing
|
||||||
// references or ownership.
|
// references or ownership.
|
||||||
virtual nsISocketTransport *Transport() = 0;
|
virtual nsISocketTransport *Transport() = 0;
|
||||||
|
|
||||||
|
// Cancel and reschedule transactions deeper than the current response.
|
||||||
|
// Returns the number of canceled transactions.
|
||||||
|
virtual PRUint32 CancelPipeline(nsresult originalReason) = 0;
|
||||||
|
|
||||||
|
// Read and write class of transaction that is carried on this connection
|
||||||
|
virtual nsAHttpTransaction::Classifier Classification() = 0;
|
||||||
|
virtual void Classify(nsAHttpTransaction::Classifier newclass) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NS_DECL_NSAHTTPCONNECTION \
|
#define NS_DECL_NSAHTTPCONNECTION \
|
||||||
@ -153,10 +167,15 @@ public:
|
|||||||
void GetSecurityInfo(nsISupports **); \
|
void GetSecurityInfo(nsISupports **); \
|
||||||
bool IsPersistent(); \
|
bool IsPersistent(); \
|
||||||
bool IsReused(); \
|
bool IsReused(); \
|
||||||
|
void DontReuse(); \
|
||||||
nsresult PushBack(const char *, PRUint32); \
|
nsresult PushBack(const char *, PRUint32); \
|
||||||
|
bool IsProxyConnectInProgress(); \
|
||||||
bool LastTransactionExpectedNoContent(); \
|
bool LastTransactionExpectedNoContent(); \
|
||||||
void SetLastTransactionExpectedNoContent(bool); \
|
void SetLastTransactionExpectedNoContent(bool); \
|
||||||
nsHttpConnection *TakeHttpConnection(); \
|
nsHttpConnection *TakeHttpConnection(); \
|
||||||
nsISocketTransport *Transport();
|
nsISocketTransport *Transport(); \
|
||||||
|
PRUint32 CancelPipeline(nsresult originalReason); \
|
||||||
|
nsAHttpTransaction::Classifier Classification(); \
|
||||||
|
void Classify(nsAHttpTransaction::Classifier);
|
||||||
|
|
||||||
#endif // nsAHttpConnection_h__
|
#endif // nsAHttpConnection_h__
|
||||||
|
@ -48,6 +48,7 @@ class nsIInterfaceRequestor;
|
|||||||
class nsIEventTarget;
|
class nsIEventTarget;
|
||||||
class nsITransport;
|
class nsITransport;
|
||||||
class nsHttpRequestHead;
|
class nsHttpRequestHead;
|
||||||
|
class nsHttpPipeline;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// Abstract base class for a HTTP transaction:
|
// Abstract base class for a HTTP transaction:
|
||||||
@ -63,6 +64,8 @@ class nsAHttpTransaction : public nsISupports
|
|||||||
public:
|
public:
|
||||||
// called by the connection when it takes ownership of the transaction.
|
// called by the connection when it takes ownership of the transaction.
|
||||||
virtual void SetConnection(nsAHttpConnection *) = 0;
|
virtual void SetConnection(nsAHttpConnection *) = 0;
|
||||||
|
|
||||||
|
// used to obtain the connection associated with this transaction
|
||||||
virtual nsAHttpConnection *Connection() = 0;
|
virtual nsAHttpConnection *Connection() = 0;
|
||||||
|
|
||||||
// called by the connection to get security callbacks to set on the
|
// called by the connection to get security callbacks to set on the
|
||||||
@ -77,6 +80,7 @@ public:
|
|||||||
// called to check the transaction status.
|
// called to check the transaction status.
|
||||||
virtual bool IsDone() = 0;
|
virtual bool IsDone() = 0;
|
||||||
virtual nsresult Status() = 0;
|
virtual nsresult Status() = 0;
|
||||||
|
virtual PRUint8 Caps() = 0;
|
||||||
|
|
||||||
// called to find out how much request data is available for writing.
|
// called to find out how much request data is available for writing.
|
||||||
virtual PRUint32 Available() = 0;
|
virtual PRUint32 Available() = 0;
|
||||||
@ -114,6 +118,49 @@ public:
|
|||||||
//
|
//
|
||||||
virtual nsresult TakeSubTransactions(
|
virtual nsresult TakeSubTransactions(
|
||||||
nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions) = 0;
|
nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions) = 0;
|
||||||
|
|
||||||
|
// called to add a sub-transaction in the case of pipelined transactions
|
||||||
|
// classes that do not implement sub transactions
|
||||||
|
// return NS_ERROR_NOT_IMPLEMENTED
|
||||||
|
virtual nsresult AddTransaction(nsAHttpTransaction *transaction) = 0;
|
||||||
|
|
||||||
|
// The total length of the outstanding pipeline comprised of transacations
|
||||||
|
// and sub-transactions.
|
||||||
|
virtual PRUint32 PipelineDepth() = 0;
|
||||||
|
|
||||||
|
// Used to inform the connection that it is being used in a pipelined
|
||||||
|
// context. That may influence the handling of some errors.
|
||||||
|
// The value is the pipeline position (> 1).
|
||||||
|
virtual nsresult SetPipelinePosition(PRInt32) = 0;
|
||||||
|
virtual PRInt32 PipelinePosition() = 0;
|
||||||
|
|
||||||
|
// If we used rtti this would be the result of doing
|
||||||
|
// dynamic_cast<nsHttpPipeline *>(this).. i.e. it can be nsnull for
|
||||||
|
// non pipeline implementations of nsAHttpTransaction
|
||||||
|
virtual nsHttpPipeline *QueryPipeline() { return nsnull; }
|
||||||
|
|
||||||
|
// Every transaction is classified into one of the types below. When using
|
||||||
|
// HTTP pipelines, only transactions with the same type appear on the same
|
||||||
|
// pipeline.
|
||||||
|
enum Classifier {
|
||||||
|
// Transactions that expect a short 304 (no-content) response
|
||||||
|
CLASS_REVALIDATION,
|
||||||
|
|
||||||
|
// Transactions for content expected to be CSS or JS
|
||||||
|
CLASS_SCRIPT,
|
||||||
|
|
||||||
|
// Transactions for content expected to be an image
|
||||||
|
CLASS_IMAGE,
|
||||||
|
|
||||||
|
// Transactions that cannot involve a pipeline
|
||||||
|
CLASS_SOLO,
|
||||||
|
|
||||||
|
// Transactions that do not fit any of the other categories. HTML
|
||||||
|
// is normally GENERAL.
|
||||||
|
CLASS_GENERAL,
|
||||||
|
|
||||||
|
CLASS_MAX
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NS_DECL_NSAHTTPTRANSACTION \
|
#define NS_DECL_NSAHTTPTRANSACTION \
|
||||||
@ -125,6 +172,7 @@ public:
|
|||||||
nsresult status, PRUint64 progress); \
|
nsresult status, PRUint64 progress); \
|
||||||
bool IsDone(); \
|
bool IsDone(); \
|
||||||
nsresult Status(); \
|
nsresult Status(); \
|
||||||
|
PRUint8 Caps(); \
|
||||||
PRUint32 Available(); \
|
PRUint32 Available(); \
|
||||||
nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *); \
|
nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *); \
|
||||||
nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *); \
|
nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *); \
|
||||||
@ -132,7 +180,11 @@ public:
|
|||||||
void SetSSLConnectFailed(); \
|
void SetSSLConnectFailed(); \
|
||||||
nsHttpRequestHead *RequestHead(); \
|
nsHttpRequestHead *RequestHead(); \
|
||||||
PRUint32 Http1xTransactionCount(); \
|
PRUint32 Http1xTransactionCount(); \
|
||||||
nsresult TakeSubTransactions(nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions);
|
nsresult TakeSubTransactions(nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions); \
|
||||||
|
nsresult AddTransaction(nsAHttpTransaction *); \
|
||||||
|
PRUint32 PipelineDepth(); \
|
||||||
|
nsresult SetPipelinePosition(PRInt32); \
|
||||||
|
PRInt32 PipelinePosition();
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsAHttpSegmentReader
|
// nsAHttpSegmentReader
|
||||||
|
@ -187,6 +187,12 @@ nsHttp::DestroyAtomTable()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mutex *
|
||||||
|
nsHttp::GetLock()
|
||||||
|
{
|
||||||
|
return sLock;
|
||||||
|
}
|
||||||
|
|
||||||
// this function may be called from multiple threads
|
// this function may be called from multiple threads
|
||||||
nsHttpAtom
|
nsHttpAtom
|
||||||
nsHttp::ResolveAtom(const char *str)
|
nsHttp::ResolveAtom(const char *str)
|
||||||
|
@ -140,9 +140,6 @@ typedef PRUint8 nsHttpVersion;
|
|||||||
// some default values
|
// some default values
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// hard upper limit on the number of requests that can be pipelined
|
|
||||||
#define NS_HTTP_MAX_PIPELINED_REQUESTS 8
|
|
||||||
|
|
||||||
#define NS_HTTP_DEFAULT_PORT 80
|
#define NS_HTTP_DEFAULT_PORT 80
|
||||||
#define NS_HTTPS_DEFAULT_PORT 443
|
#define NS_HTTPS_DEFAULT_PORT 443
|
||||||
|
|
||||||
@ -169,6 +166,11 @@ struct nsHttp
|
|||||||
static nsresult CreateAtomTable();
|
static nsresult CreateAtomTable();
|
||||||
static void DestroyAtomTable();
|
static void DestroyAtomTable();
|
||||||
|
|
||||||
|
// The mutex is valid any time the Atom Table is valid
|
||||||
|
// This mutex is used in the unusual case that the network thread and
|
||||||
|
// main thread might access the same data
|
||||||
|
static mozilla::Mutex *GetLock();
|
||||||
|
|
||||||
// will dynamically add atoms to the table if they don't already exist
|
// will dynamically add atoms to the table if they don't already exist
|
||||||
static nsHttpAtom ResolveAtom(const char *);
|
static nsHttpAtom ResolveAtom(const char *);
|
||||||
static nsHttpAtom ResolveAtom(const nsACString &s)
|
static nsHttpAtom ResolveAtom(const nsACString &s)
|
||||||
|
@ -57,6 +57,7 @@ HTTP_ATOM(Accept_Ranges, "Accept-Ranges")
|
|||||||
HTTP_ATOM(Age, "Age")
|
HTTP_ATOM(Age, "Age")
|
||||||
HTTP_ATOM(Allow, "Allow")
|
HTTP_ATOM(Allow, "Allow")
|
||||||
HTTP_ATOM(Alternate_Protocol, "Alternate-Protocol")
|
HTTP_ATOM(Alternate_Protocol, "Alternate-Protocol")
|
||||||
|
HTTP_ATOM(Assoc_Req, "Assoc-Req")
|
||||||
HTTP_ATOM(Authentication, "Authentication")
|
HTTP_ATOM(Authentication, "Authentication")
|
||||||
HTTP_ATOM(Authorization, "Authorization")
|
HTTP_ATOM(Authorization, "Authorization")
|
||||||
HTTP_ATOM(Cache_Control, "Cache-Control")
|
HTTP_ATOM(Cache_Control, "Cache-Control")
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
|
|
||||||
#include "nsHttpChannel.h"
|
#include "nsHttpChannel.h"
|
||||||
#include "nsHttpHandler.h"
|
#include "nsHttpHandler.h"
|
||||||
|
#include "nsStandardURL.h"
|
||||||
#include "nsIApplicationCacheService.h"
|
#include "nsIApplicationCacheService.h"
|
||||||
#include "nsIApplicationCacheContainer.h"
|
#include "nsIApplicationCacheContainer.h"
|
||||||
#include "nsIAuthInformation.h"
|
#include "nsIAuthInformation.h"
|
||||||
@ -70,6 +71,7 @@
|
|||||||
#include "nsDOMError.h"
|
#include "nsDOMError.h"
|
||||||
#include "nsAlgorithm.h"
|
#include "nsAlgorithm.h"
|
||||||
#include "sampler.h"
|
#include "sampler.h"
|
||||||
|
#include "nsIConsoleService.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
@ -125,7 +127,6 @@ nsHttpChannel::nsHttpChannel()
|
|||||||
, mPostID(0)
|
, mPostID(0)
|
||||||
, mRequestTime(0)
|
, mRequestTime(0)
|
||||||
, mOnCacheEntryAvailableCallback(nsnull)
|
, mOnCacheEntryAvailableCallback(nsnull)
|
||||||
, mAsyncCacheOpen(false)
|
|
||||||
, mCachedContentIsValid(false)
|
, mCachedContentIsValid(false)
|
||||||
, mCachedContentIsPartial(false)
|
, mCachedContentIsPartial(false)
|
||||||
, mTransactionReplaced(false)
|
, mTransactionReplaced(false)
|
||||||
@ -246,6 +247,12 @@ nsHttpChannel::Connect(bool firstTime)
|
|||||||
// open a cache entry for this channel...
|
// open a cache entry for this channel...
|
||||||
rv = OpenCacheEntry();
|
rv = OpenCacheEntry();
|
||||||
|
|
||||||
|
// do not continue if asyncOpenCacheEntry is in progress
|
||||||
|
if (mOnCacheEntryAvailableCallback) {
|
||||||
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
LOG(("OpenCacheEntry failed [rv=%x]\n", rv));
|
LOG(("OpenCacheEntry failed [rv=%x]\n", rv));
|
||||||
// if this channel is only allowed to pull from the cache, then
|
// if this channel is only allowed to pull from the cache, then
|
||||||
@ -266,10 +273,10 @@ nsHttpChannel::Connect(bool firstTime)
|
|||||||
if (mCacheForOfflineUse) {
|
if (mCacheForOfflineUse) {
|
||||||
rv = OpenOfflineCacheEntryForWriting();
|
rv = OpenOfflineCacheEntryForWriting();
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
}
|
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv) && mAsyncCacheOpen)
|
if (mOnCacheEntryAvailableCallback)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we may or may not have a cache entry at this point
|
// we may or may not have a cache entry at this point
|
||||||
@ -500,16 +507,17 @@ nsHttpChannel::SetupTransaction()
|
|||||||
if (mCaps & NS_HTTP_ALLOW_PIPELINING) {
|
if (mCaps & NS_HTTP_ALLOW_PIPELINING) {
|
||||||
//
|
//
|
||||||
// disable pipelining if:
|
// disable pipelining if:
|
||||||
// (1) pipelining has been explicitly disabled
|
// (1) pipelining has been disabled by config
|
||||||
// (2) request corresponds to a top-level document load (link click)
|
// (2) pipelining has been disabled by connection mgr info
|
||||||
// (3) request method is non-idempotent
|
// (3) request corresponds to a top-level document load (link click)
|
||||||
|
// (4) request method is non-idempotent
|
||||||
|
// (5) request is marked slow (e.g XHR)
|
||||||
//
|
//
|
||||||
// XXX does the toplevel document check really belong here? or, should
|
if (!mAllowPipelining ||
|
||||||
// we push it out entirely to necko consumers?
|
(mLoadFlags & (LOAD_INITIAL_DOCUMENT_URI | INHIBIT_PIPELINE)) ||
|
||||||
//
|
|
||||||
if (!mAllowPipelining || (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) ||
|
|
||||||
!(mRequestHead.Method() == nsHttp::Get ||
|
!(mRequestHead.Method() == nsHttp::Get ||
|
||||||
mRequestHead.Method() == nsHttp::Head ||
|
mRequestHead.Method() == nsHttp::Head ||
|
||||||
|
mRequestHead.Method() == nsHttp::Options ||
|
||||||
mRequestHead.Method() == nsHttp::Propfind ||
|
mRequestHead.Method() == nsHttp::Propfind ||
|
||||||
mRequestHead.Method() == nsHttp::Proppatch)) {
|
mRequestHead.Method() == nsHttp::Proppatch)) {
|
||||||
LOG((" pipelining disallowed\n"));
|
LOG((" pipelining disallowed\n"));
|
||||||
@ -768,6 +776,10 @@ nsHttpChannel::CallOnStartRequest()
|
|||||||
rv = ApplyContentConversions();
|
rv = ApplyContentConversions();
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
|
rv = EnsureAssocReq();
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
|
||||||
// if this channel is for a download, close off access to the cache.
|
// if this channel is for a download, close off access to the cache.
|
||||||
if (mCacheEntry && mChannelIsForDownload) {
|
if (mCacheEntry && mChannelIsForDownload) {
|
||||||
mCacheEntry->Doom();
|
mCacheEntry->Doom();
|
||||||
@ -1713,6 +1725,117 @@ nsHttpChannel::Hash(const char *buf, nsACString &hash)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsHttpChannel::EnsureAssocReq()
|
||||||
|
{
|
||||||
|
// Confirm Assoc-Req response header on pipelined transactions
|
||||||
|
// per draft-nottingham-http-pipeline-01.txt
|
||||||
|
// of the form: GET http://blah.com/foo/bar?qv
|
||||||
|
// return NS_OK as long as we don't find a violation
|
||||||
|
// (i.e. no header is ok, as are malformed headers, as are
|
||||||
|
// transactions that have not been pipelined (unless those have been
|
||||||
|
// opted in via pragma))
|
||||||
|
|
||||||
|
if (!mResponseHead)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
const char *assoc_val = mResponseHead->PeekHeader(nsHttp::Assoc_Req);
|
||||||
|
if (!assoc_val)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
if (!mTransaction || !mURI)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
if (!mTransaction->PipelinePosition()) {
|
||||||
|
// "Pragma: X-Verify-Assoc-Req" can be used to verify even non pipelined
|
||||||
|
// transactions. It is used by test harness.
|
||||||
|
|
||||||
|
const char *pragma_val = mResponseHead->PeekHeader(nsHttp::Pragma);
|
||||||
|
if (!pragma_val ||
|
||||||
|
!nsHttp::FindToken(pragma_val, "X-Verify-Assoc-Req",
|
||||||
|
HTTP_HEADER_VALUE_SEPS))
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *method = net_FindCharNotInSet(assoc_val, HTTP_LWS);
|
||||||
|
if (!method)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
bool equals;
|
||||||
|
char *endofmethod;
|
||||||
|
|
||||||
|
assoc_val = nsnull;
|
||||||
|
endofmethod = net_FindCharInSet(method, HTTP_LWS);
|
||||||
|
if (endofmethod)
|
||||||
|
assoc_val = net_FindCharNotInSet(endofmethod, HTTP_LWS);
|
||||||
|
if (!assoc_val)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
// check the method
|
||||||
|
PRInt32 methodlen = PL_strlen(mRequestHead.Method().get());
|
||||||
|
if ((methodlen != (endofmethod - method)) ||
|
||||||
|
PL_strncmp(method,
|
||||||
|
mRequestHead.Method().get(),
|
||||||
|
endofmethod - method)) {
|
||||||
|
LOG((" Assoc-Req failure Method %s", method));
|
||||||
|
if (mConnectionInfo)
|
||||||
|
gHttpHandler->ConnMgr()->
|
||||||
|
PipelineFeedbackInfo(mConnectionInfo,
|
||||||
|
nsHttpConnectionMgr::RedCorruptedContent,
|
||||||
|
nsnull, 0);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIConsoleService> consoleService =
|
||||||
|
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||||
|
if (consoleService) {
|
||||||
|
nsAutoString message
|
||||||
|
(NS_LITERAL_STRING("Failed Assoc-Req. Received "));
|
||||||
|
AppendASCIItoUTF16(
|
||||||
|
mResponseHead->PeekHeader(nsHttp::Assoc_Req),
|
||||||
|
message);
|
||||||
|
message += NS_LITERAL_STRING(" expected method ");
|
||||||
|
AppendASCIItoUTF16(mRequestHead.Method().get(), message);
|
||||||
|
consoleService->LogStringMessage(message.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gHttpHandler->EnforceAssocReq())
|
||||||
|
return NS_ERROR_CORRUPTED_CONTENT;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the URL
|
||||||
|
nsCOMPtr<nsIURI> assoc_url;
|
||||||
|
if (NS_FAILED(NS_NewURI(getter_AddRefs(assoc_url), assoc_val)) ||
|
||||||
|
!assoc_url)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
mURI->Equals(assoc_url, &equals);
|
||||||
|
if (!equals) {
|
||||||
|
LOG((" Assoc-Req failure URL %s", assoc_val));
|
||||||
|
if (mConnectionInfo)
|
||||||
|
gHttpHandler->ConnMgr()->
|
||||||
|
PipelineFeedbackInfo(mConnectionInfo,
|
||||||
|
nsHttpConnectionMgr::RedCorruptedContent,
|
||||||
|
nsnull, 0);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIConsoleService> consoleService =
|
||||||
|
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||||
|
if (consoleService) {
|
||||||
|
nsAutoString message
|
||||||
|
(NS_LITERAL_STRING("Failed Assoc-Req. Received "));
|
||||||
|
AppendASCIItoUTF16(
|
||||||
|
mResponseHead->PeekHeader(nsHttp::Assoc_Req),
|
||||||
|
message);
|
||||||
|
message += NS_LITERAL_STRING(" expected URL ");
|
||||||
|
AppendASCIItoUTF16(mSpec.get(), message);
|
||||||
|
consoleService->LogStringMessage(message.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gHttpHandler->EnforceAssocReq())
|
||||||
|
return NS_ERROR_CORRUPTED_CONTENT;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsHttpChannel <byte-range>
|
// nsHttpChannel <byte-range>
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -1856,6 +1979,33 @@ nsHttpChannel::ProcessNotModified()
|
|||||||
NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
|
||||||
NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
|
// If the 304 response contains a Last-Modified different than the
|
||||||
|
// one in our cache that is pretty suspicious and is, in at least the
|
||||||
|
// case of bug 716840, a sign of the server having previously corrupted
|
||||||
|
// our cache with a bad response. Take the minor step here of just dooming
|
||||||
|
// that cache entry so there is a fighting chance of getting things on the
|
||||||
|
// right track as well as disabling pipelining for that host.
|
||||||
|
|
||||||
|
nsCAutoString lastModified;
|
||||||
|
nsCAutoString lastModified304;
|
||||||
|
|
||||||
|
rv = mCachedResponseHead->GetHeader(nsHttp::Last_Modified,
|
||||||
|
lastModified);
|
||||||
|
if (NS_SUCCEEDED(rv))
|
||||||
|
rv = mResponseHead->GetHeader(nsHttp::Last_Modified,
|
||||||
|
lastModified304);
|
||||||
|
if (NS_SUCCEEDED(rv) && !lastModified304.Equals(lastModified)) {
|
||||||
|
LOG(("Cache Entry and 304 Last-Modified Headers Do Not Match "
|
||||||
|
"%s and %s\n", lastModified.get(), lastModified304.get()));
|
||||||
|
|
||||||
|
mCacheEntry->Doom();
|
||||||
|
if (mConnectionInfo)
|
||||||
|
gHttpHandler->ConnMgr()->
|
||||||
|
PipelineFeedbackInfo(mConnectionInfo,
|
||||||
|
nsHttpConnectionMgr::RedCorruptedContent,
|
||||||
|
nsnull, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// merge any new headers with the cached response headers
|
// merge any new headers with the cached response headers
|
||||||
rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
|
rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
@ -2034,7 +2184,7 @@ nsHttpChannel::OpenCacheEntry()
|
|||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
mAsyncCacheOpen = false;
|
NS_ASSERTION(!mOnCacheEntryAvailableCallback, "Unexpected state");
|
||||||
mLoadedFromApplicationCache = false;
|
mLoadedFromApplicationCache = false;
|
||||||
|
|
||||||
LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
|
LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
|
||||||
@ -2117,52 +2267,33 @@ nsHttpChannel::OpenCacheEntry()
|
|||||||
getter_AddRefs(session));
|
getter_AddRefs(session));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
|
mOnCacheEntryAvailableCallback =
|
||||||
// must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
|
&nsHttpChannel::OnOfflineCacheEntryAvailable;
|
||||||
rv = session->OpenCacheEntry(cacheKey,
|
// We open with ACCESS_READ only, because we don't want to overwrite
|
||||||
nsICache::ACCESS_READ, false,
|
// the offline cache entry non-atomically. ACCESS_READ will prevent us
|
||||||
getter_AddRefs(mCacheEntry));
|
// from writing to the offline cache as a normal cache entry.
|
||||||
if (NS_SUCCEEDED(rv)) {
|
rv = session->AsyncOpenCacheEntry(
|
||||||
mCacheEntry->GetAccessGranted(&mCacheAccess);
|
cacheKey,
|
||||||
LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]",
|
nsICache::ACCESS_READ,
|
||||||
this, mCacheAccess));
|
this,
|
||||||
mLoadedFromApplicationCache = true;
|
mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
|
||||||
return NS_OK;
|
|
||||||
} else if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
|
|
||||||
LOG(("bypassing local cache since it is busy\n"));
|
|
||||||
// Don't try to load normal cache entry
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mOnCacheEntryAvailableCallback =
|
|
||||||
&nsHttpChannel::OnOfflineCacheEntryAvailable;
|
|
||||||
// We open with ACCESS_READ only, because we don't want to
|
|
||||||
// overwrite the offline cache entry non-atomically.
|
|
||||||
// ACCESS_READ will prevent us from writing to the offline
|
|
||||||
// cache as a normal cache entry.
|
|
||||||
rv = session->AsyncOpenCacheEntry(cacheKey,
|
|
||||||
nsICache::ACCESS_READ,
|
|
||||||
this);
|
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv))
|
||||||
mAsyncCacheOpen = true;
|
return NS_OK;
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync or async opening failed
|
mOnCacheEntryAvailableCallback = nsnull;
|
||||||
return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE,
|
|
||||||
rv, true);
|
// opening cache entry failed
|
||||||
|
return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OpenNormalCacheEntry(true);
|
return OpenNormalCacheEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
||||||
nsCacheAccessMode aAccess,
|
nsCacheAccessMode aAccess,
|
||||||
nsresult aEntryStatus,
|
nsresult aEntryStatus)
|
||||||
bool aIsSync)
|
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
@ -2174,14 +2305,19 @@ nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
|||||||
mCacheAccess = aAccess;
|
mCacheAccess = aAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
|
||||||
|
LOG(("bypassing local cache since it is busy\n"));
|
||||||
|
// Don't try to load normal cache entry
|
||||||
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
if (mCanceled && NS_FAILED(mStatus)) {
|
if (mCanceled && NS_FAILED(mStatus)) {
|
||||||
LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
|
LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
|
||||||
return mStatus;
|
return mStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_SUCCEEDED(aEntryStatus))
|
if (NS_SUCCEEDED(aEntryStatus))
|
||||||
// Called from OnCacheEntryAvailable, advance to the next state
|
return NS_OK;
|
||||||
return Connect(false);
|
|
||||||
|
|
||||||
if (!mCacheForOfflineUse && !mFallbackChannel) {
|
if (!mCacheForOfflineUse && !mFallbackChannel) {
|
||||||
nsCAutoString cacheKey;
|
nsCAutoString cacheKey;
|
||||||
@ -2191,8 +2327,6 @@ nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
|||||||
nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
|
nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
|
||||||
rv = mApplicationCache->GetMatchingNamespace
|
rv = mApplicationCache->GetMatchingNamespace
|
||||||
(cacheKey, getter_AddRefs(namespaceEntry));
|
(cacheKey, getter_AddRefs(namespaceEntry));
|
||||||
if (NS_FAILED(rv) && !aIsSync)
|
|
||||||
return Connect(false);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
PRUint32 namespaceType = 0;
|
PRUint32 namespaceType = 0;
|
||||||
@ -2210,14 +2344,12 @@ nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
|||||||
|
|
||||||
// ... and if there were an application cache entry,
|
// ... and if there were an application cache entry,
|
||||||
// we would have found it earlier.
|
// we would have found it earlier.
|
||||||
return aIsSync ? NS_ERROR_CACHE_KEY_NOT_FOUND : Connect(false);
|
return NS_ERROR_CACHE_KEY_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (namespaceType &
|
if (namespaceType &
|
||||||
nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
|
nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
|
||||||
rv = namespaceEntry->GetData(mFallbackKey);
|
rv = namespaceEntry->GetData(mFallbackKey);
|
||||||
if (NS_FAILED(rv) && !aIsSync)
|
|
||||||
return Connect(false);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2235,12 +2367,12 @@ nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return OpenNormalCacheEntry(aIsSync);
|
return OpenNormalCacheEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpChannel::OpenNormalCacheEntry(bool aIsSync)
|
nsHttpChannel::OpenNormalCacheEntry()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
|
NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
|
||||||
|
|
||||||
@ -2260,43 +2392,18 @@ nsHttpChannel::OpenNormalCacheEntry(bool aIsSync)
|
|||||||
rv = DetermineCacheAccess(&accessRequested);
|
rv = DetermineCacheAccess(&accessRequested);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
|
mOnCacheEntryAvailableCallback =
|
||||||
if (!aIsSync) {
|
&nsHttpChannel::OnNormalCacheEntryAvailable;
|
||||||
// Unexpected state: we were called from OnCacheEntryAvailable(),
|
rv = session->AsyncOpenCacheEntry(
|
||||||
// so LOAD_BYPASS_LOCAL_CACHE_IF_BUSY shouldn't be set. Unless
|
cacheKey,
|
||||||
// somebody altered mLoadFlags between OpenCacheEntry() and
|
accessRequested,
|
||||||
// OnCacheEntryAvailable()...
|
this,
|
||||||
NS_WARNING(
|
mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
|
||||||
"OpenNormalCacheEntry() called from OnCacheEntryAvailable() "
|
|
||||||
"when LOAD_BYPASS_LOCAL_CACHE_IF_BUSY was specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
// must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
|
if (NS_SUCCEEDED(rv))
|
||||||
rv = session->OpenCacheEntry(cacheKey, accessRequested, false,
|
return NS_OK;
|
||||||
getter_AddRefs(mCacheEntry));
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
mCacheEntry->GetAccessGranted(&mCacheAccess);
|
|
||||||
LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]",
|
|
||||||
this, mCacheAccess));
|
|
||||||
}
|
|
||||||
else if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
|
|
||||||
LOG(("bypassing local cache since it is busy\n"));
|
|
||||||
rv = NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mOnCacheEntryAvailableCallback =
|
|
||||||
&nsHttpChannel::OnNormalCacheEntryAvailable;
|
|
||||||
rv = session->AsyncOpenCacheEntry(cacheKey, accessRequested, this);
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
mAsyncCacheOpen = true;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aIsSync)
|
mOnCacheEntryAvailableCallback = nsnull;
|
||||||
// Called from OnCacheEntryAvailable, advance to the next state
|
|
||||||
rv = Connect(false);
|
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@ -2304,16 +2411,17 @@ nsHttpChannel::OpenNormalCacheEntry(bool aIsSync)
|
|||||||
nsresult
|
nsresult
|
||||||
nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
||||||
nsCacheAccessMode aAccess,
|
nsCacheAccessMode aAccess,
|
||||||
nsresult aEntryStatus,
|
nsresult aEntryStatus)
|
||||||
bool aIsSync)
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!aIsSync, "aIsSync should be false");
|
|
||||||
|
|
||||||
if (NS_SUCCEEDED(aEntryStatus)) {
|
if (NS_SUCCEEDED(aEntryStatus)) {
|
||||||
mCacheEntry = aEntry;
|
mCacheEntry = aEntry;
|
||||||
mCacheAccess = aAccess;
|
mCacheAccess = aAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
|
||||||
|
LOG(("bypassing local cache since it is busy\n"));
|
||||||
|
}
|
||||||
|
|
||||||
if (mCanceled && NS_FAILED(mStatus)) {
|
if (mCanceled && NS_FAILED(mStatus)) {
|
||||||
LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
|
LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
|
||||||
return mStatus;
|
return mStatus;
|
||||||
@ -2325,7 +2433,7 @@ nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
|||||||
return NS_ERROR_DOCUMENT_NOT_CACHED;
|
return NS_ERROR_DOCUMENT_NOT_CACHED;
|
||||||
|
|
||||||
// advance to the next state...
|
// advance to the next state...
|
||||||
return Connect(false);
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2372,22 +2480,43 @@ nsHttpChannel::OpenOfflineCacheEntryForWriting()
|
|||||||
getter_AddRefs(session));
|
getter_AddRefs(session));
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
|
mOnCacheEntryAvailableCallback =
|
||||||
false, getter_AddRefs(mOfflineCacheEntry));
|
&nsHttpChannel::OnOfflineCacheEntryForWritingAvailable;
|
||||||
|
rv = session->AsyncOpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
|
||||||
|
this, true);
|
||||||
|
if (NS_SUCCEEDED(rv))
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
|
mOnCacheEntryAvailableCallback = nsnull;
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(
|
||||||
|
nsICacheEntryDescriptor *aEntry,
|
||||||
|
nsCacheAccessMode aAccess,
|
||||||
|
nsresult aEntryStatus)
|
||||||
|
{
|
||||||
|
if (NS_SUCCEEDED(aEntryStatus)) {
|
||||||
|
mOfflineCacheEntry = aEntry;
|
||||||
|
mOfflineCacheAccess = aAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
|
||||||
// access to the cache entry has been denied (because the cache entry
|
// access to the cache entry has been denied (because the cache entry
|
||||||
// is probably in use by another channel). Either the cache is being
|
// is probably in use by another channel). Either the cache is being
|
||||||
// read from (we're offline) or it's being updated elsewhere.
|
// read from (we're offline) or it's being updated elsewhere.
|
||||||
return NS_OK;
|
aEntryStatus = NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (mCanceled && NS_FAILED(mStatus)) {
|
||||||
mOfflineCacheEntry->GetAccessGranted(&mOfflineCacheAccess);
|
LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
|
||||||
LOG(("got offline cache entry [access=%x]\n", mOfflineCacheAccess));
|
return mStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
// advance to the next state...
|
||||||
|
return aEntryStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates the proper cache-key for this instance of nsHttpChannel
|
// Generates the proper cache-key for this instance of nsHttpChannel
|
||||||
@ -4843,24 +4972,8 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
|
|||||||
if (!mIsPending)
|
if (!mIsPending)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
nsOnCacheEntryAvailableCallback callback = mOnCacheEntryAvailableCallback;
|
rv = OnCacheEntryAvailableInternal(entry, access, status);
|
||||||
mOnCacheEntryAvailableCallback = nsnull;
|
|
||||||
|
|
||||||
NS_ASSERTION(callback,
|
|
||||||
"nsHttpChannel::OnCacheEntryAvailable called without callback");
|
|
||||||
rv = ((*this).*callback)(entry, access, status, false);
|
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
LOG(("AsyncOpenCacheEntry failed [rv=%x]\n", rv));
|
|
||||||
if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
|
|
||||||
// If we have a fallback URI (and we're not already
|
|
||||||
// falling back), process the fallback asynchronously.
|
|
||||||
if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
|
|
||||||
rv = AsyncCall(&nsHttpChannel::HandleAsyncFallback);
|
|
||||||
if (NS_SUCCEEDED(rv))
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CloseCacheEntry(true);
|
CloseCacheEntry(true);
|
||||||
AsyncAbort(rv);
|
AsyncAbort(rv);
|
||||||
}
|
}
|
||||||
@ -4868,6 +4981,67 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntryDescriptor *entry,
|
||||||
|
nsCacheAccessMode access,
|
||||||
|
nsresult status)
|
||||||
|
{
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
nsOnCacheEntryAvailableCallback callback = mOnCacheEntryAvailableCallback;
|
||||||
|
mOnCacheEntryAvailableCallback = nsnull;
|
||||||
|
|
||||||
|
NS_ASSERTION(callback,
|
||||||
|
"nsHttpChannel::OnCacheEntryAvailable called without callback");
|
||||||
|
rv = ((*this).*callback)(entry, access, status);
|
||||||
|
|
||||||
|
if (mOnCacheEntryAvailableCallback) {
|
||||||
|
// callback fired another async open
|
||||||
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback != &nsHttpChannel::OnOfflineCacheEntryForWritingAvailable) {
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
LOG(("AsyncOpenCacheEntry failed [rv=%x]\n", rv));
|
||||||
|
if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
|
||||||
|
// If we have a fallback URI (and we're not already
|
||||||
|
// falling back), process the fallback asynchronously.
|
||||||
|
if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
|
||||||
|
return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
|
||||||
|
}
|
||||||
|
return NS_ERROR_DOCUMENT_NOT_CACHED;
|
||||||
|
}
|
||||||
|
// proceed without using the cache
|
||||||
|
}
|
||||||
|
|
||||||
|
// if cacheForOfflineUse has been set, open up an offline cache entry
|
||||||
|
// to update
|
||||||
|
if (mCacheForOfflineUse) {
|
||||||
|
rv = OpenOfflineCacheEntryForWriting();
|
||||||
|
if (mOnCacheEntryAvailableCallback) {
|
||||||
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// check result of OnOfflineCacheEntryForWritingAvailable()
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Connect(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsHttpChannel::OnCacheEntryDoomed(nsresult status)
|
||||||
|
{
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
|
nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
|
||||||
{
|
{
|
||||||
@ -5186,19 +5360,7 @@ nsHttpChannel::DoInvalidateCacheEntry(nsACString &key)
|
|||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Now, find the actual cache-entry
|
session->DoomEntry(key, nsnull);
|
||||||
nsCOMPtr<nsICacheEntryDescriptor> tmpCacheEntry;
|
|
||||||
rv = session->OpenCacheEntry(key, nsICache::ACCESS_READ,
|
|
||||||
false,
|
|
||||||
getter_AddRefs(tmpCacheEntry));
|
|
||||||
|
|
||||||
// If entry was found, set its expiration-time = 0
|
|
||||||
if(NS_SUCCEEDED(rv)) {
|
|
||||||
tmpCacheEntry->SetExpirationTime(0);
|
|
||||||
LOG((" cache-entry invalidated [key=%s]\n", key.Data()));
|
|
||||||
} else {
|
|
||||||
LOG((" cache-entry not found [key=%s]\n", key.Data()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCacheStoragePolicy
|
nsCacheStoragePolicy
|
||||||
|
@ -182,6 +182,7 @@ private:
|
|||||||
nsresult ContinueProcessFallback(nsresult);
|
nsresult ContinueProcessFallback(nsresult);
|
||||||
bool ResponseWouldVary();
|
bool ResponseWouldVary();
|
||||||
void HandleAsyncAbort();
|
void HandleAsyncAbort();
|
||||||
|
nsresult EnsureAssocReq();
|
||||||
|
|
||||||
nsresult ContinueOnStartRequest1(nsresult);
|
nsresult ContinueOnStartRequest1(nsresult);
|
||||||
nsresult ContinueOnStartRequest2(nsresult);
|
nsresult ContinueOnStartRequest2(nsresult);
|
||||||
@ -210,14 +211,19 @@ private:
|
|||||||
nsresult OpenCacheEntry();
|
nsresult OpenCacheEntry();
|
||||||
nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
||||||
nsCacheAccessMode aAccess,
|
nsCacheAccessMode aAccess,
|
||||||
nsresult aResult,
|
nsresult aResult);
|
||||||
bool aSync);
|
nsresult OpenNormalCacheEntry();
|
||||||
nsresult OpenNormalCacheEntry(bool aSync);
|
|
||||||
nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
|
||||||
nsCacheAccessMode aAccess,
|
nsCacheAccessMode aAccess,
|
||||||
nsresult aResult,
|
nsresult aResult);
|
||||||
bool aSync);
|
|
||||||
nsresult OpenOfflineCacheEntryForWriting();
|
nsresult OpenOfflineCacheEntryForWriting();
|
||||||
|
nsresult OnOfflineCacheEntryForWritingAvailable(
|
||||||
|
nsICacheEntryDescriptor *aEntry,
|
||||||
|
nsCacheAccessMode aAccess,
|
||||||
|
nsresult aResult);
|
||||||
|
nsresult OnCacheEntryAvailableInternal(nsICacheEntryDescriptor *entry,
|
||||||
|
nsCacheAccessMode access,
|
||||||
|
nsresult status);
|
||||||
nsresult GenerateCacheKey(PRUint32 postID, nsACString &key);
|
nsresult GenerateCacheKey(PRUint32 postID, nsACString &key);
|
||||||
nsresult UpdateExpirationTime();
|
nsresult UpdateExpirationTime();
|
||||||
nsresult CheckCache();
|
nsresult CheckCache();
|
||||||
@ -299,9 +305,8 @@ private:
|
|||||||
PRUint32 mRequestTime;
|
PRUint32 mRequestTime;
|
||||||
|
|
||||||
typedef nsresult (nsHttpChannel:: *nsOnCacheEntryAvailableCallback)(
|
typedef nsresult (nsHttpChannel:: *nsOnCacheEntryAvailableCallback)(
|
||||||
nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult, bool);
|
nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult);
|
||||||
nsOnCacheEntryAvailableCallback mOnCacheEntryAvailableCallback;
|
nsOnCacheEntryAvailableCallback mOnCacheEntryAvailableCallback;
|
||||||
bool mAsyncCacheOpen;
|
|
||||||
|
|
||||||
nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
|
nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
|
||||||
nsCacheAccessMode mOfflineCacheAccess;
|
nsCacheAccessMode mOfflineCacheAccess;
|
||||||
|
@ -72,8 +72,6 @@ using namespace mozilla::net;
|
|||||||
|
|
||||||
nsHttpConnection::nsHttpConnection()
|
nsHttpConnection::nsHttpConnection()
|
||||||
: mTransaction(nsnull)
|
: mTransaction(nsnull)
|
||||||
, mLastReadTime(0)
|
|
||||||
, mIdleTimeout(0)
|
|
||||||
, mConsiderReusedAfterInterval(0)
|
, mConsiderReusedAfterInterval(0)
|
||||||
, mConsiderReusedAfterEpoch(0)
|
, mConsiderReusedAfterEpoch(0)
|
||||||
, mCurrentBytesRead(0)
|
, mCurrentBytesRead(0)
|
||||||
@ -86,7 +84,10 @@ nsHttpConnection::nsHttpConnection()
|
|||||||
, mCompletedProxyConnect(false)
|
, mCompletedProxyConnect(false)
|
||||||
, mLastTransactionExpectedNoContent(false)
|
, mLastTransactionExpectedNoContent(false)
|
||||||
, mIdleMonitoring(false)
|
, mIdleMonitoring(false)
|
||||||
|
, mProxyConnectInProgress(false)
|
||||||
, mHttp1xTransactionCount(0)
|
, mHttp1xTransactionCount(0)
|
||||||
|
, mRemainingConnectionUses(0xffffffff)
|
||||||
|
, mClassification(nsAHttpTransaction::CLASS_GENERAL)
|
||||||
, mNPNComplete(false)
|
, mNPNComplete(false)
|
||||||
, mSetupNPNCalled(false)
|
, mSetupNPNCalled(false)
|
||||||
, mUsingSpdy(false)
|
, mUsingSpdy(false)
|
||||||
@ -141,20 +142,25 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info,
|
|||||||
nsIAsyncInputStream *instream,
|
nsIAsyncInputStream *instream,
|
||||||
nsIAsyncOutputStream *outstream,
|
nsIAsyncOutputStream *outstream,
|
||||||
nsIInterfaceRequestor *callbacks,
|
nsIInterfaceRequestor *callbacks,
|
||||||
nsIEventTarget *callbackTarget)
|
nsIEventTarget *callbackTarget,
|
||||||
|
PRIntervalTime rtt)
|
||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(transport && instream && outstream,
|
NS_ABORT_IF_FALSE(transport && instream && outstream,
|
||||||
"invalid socket information");
|
"invalid socket information");
|
||||||
LOG(("nsHttpConnection::Init [this=%p "
|
LOG(("nsHttpConnection::Init [this=%p "
|
||||||
"transport=%p instream=%p outstream=%p]\n",
|
"transport=%p instream=%p outstream=%p rtt=%d]\n",
|
||||||
this, transport, instream, outstream));
|
this, transport, instream, outstream,
|
||||||
|
PR_IntervalToMilliseconds(rtt)));
|
||||||
|
|
||||||
NS_ENSURE_ARG_POINTER(info);
|
NS_ENSURE_ARG_POINTER(info);
|
||||||
NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
|
NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
|
||||||
|
|
||||||
mConnInfo = info;
|
mConnInfo = info;
|
||||||
mMaxHangTime = PR_SecondsToInterval(maxHangTime);
|
|
||||||
mLastReadTime = PR_IntervalNow();
|
mLastReadTime = PR_IntervalNow();
|
||||||
|
mSupportsPipelining =
|
||||||
|
gHttpHandler->ConnMgr()->SupportsPipelining(mConnInfo);
|
||||||
|
mRtt = rtt;
|
||||||
|
mMaxHangTime = PR_SecondsToInterval(maxHangTime);
|
||||||
|
|
||||||
mSocketTransport = transport;
|
mSocketTransport = transport;
|
||||||
mSocketIn = instream;
|
mSocketIn = instream;
|
||||||
@ -227,13 +233,16 @@ nsHttpConnection::StartSpdy()
|
|||||||
"into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
|
"into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
NS_ABORT_IF_FALSE(!list.IsEmpty(), "sub transaction list empty");
|
|
||||||
|
|
||||||
PRInt32 count = list.Length();
|
PRInt32 count = list.Length();
|
||||||
|
|
||||||
LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d "
|
LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d "
|
||||||
"into SpdySession %p\n", count, mSpdySession.get()));
|
"into SpdySession %p\n", count, mSpdySession.get()));
|
||||||
|
|
||||||
|
if (!count) {
|
||||||
|
mTransaction->Close(NS_ERROR_ABORT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (PRInt32 index = 0; index < count; ++index) {
|
for (PRInt32 index = 0; index < count; ++index) {
|
||||||
if (!mSpdySession) {
|
if (!mSpdySession) {
|
||||||
mSpdySession = new SpdySession(list[index],
|
mSpdySession = new SpdySession(list[index],
|
||||||
@ -338,6 +347,9 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps, PRInt32 pri)
|
|||||||
NS_ENSURE_ARG_POINTER(trans);
|
NS_ENSURE_ARG_POINTER(trans);
|
||||||
NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
|
NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
|
||||||
|
|
||||||
|
// reset the read timers to wash away any idle time
|
||||||
|
mLastReadTime = PR_IntervalNow();
|
||||||
|
|
||||||
// Update security callbacks
|
// Update security callbacks
|
||||||
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
||||||
nsCOMPtr<nsIEventTarget> callbackTarget;
|
nsCOMPtr<nsIEventTarget> callbackTarget;
|
||||||
@ -369,6 +381,7 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps, PRInt32 pri)
|
|||||||
rv = SetupProxyConnect();
|
rv = SetupProxyConnect();
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
goto failed_activation;
|
goto failed_activation;
|
||||||
|
mProxyConnectInProgress = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the per activation counter
|
// Clear the per activation counter
|
||||||
@ -525,16 +538,35 @@ nsHttpConnection::DontReuse()
|
|||||||
mSpdySession->DontReuse();
|
mSpdySession->DontReuse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checked by the Connection Manager before scheduling a pipelined transaction
|
||||||
|
bool
|
||||||
|
nsHttpConnection::SupportsPipelining()
|
||||||
|
{
|
||||||
|
if (mTransaction &&
|
||||||
|
mTransaction->PipelineDepth() >= mRemainingConnectionUses) {
|
||||||
|
LOG(("nsHttpConnection::SupportsPipelining this=%p deny pipeline "
|
||||||
|
"because current depth %d exceeds max remaining uses %d\n",
|
||||||
|
this, mTransaction->PipelineDepth(), mRemainingConnectionUses));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return mSupportsPipelining && IsKeepAlive();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsHttpConnection::CanReuse()
|
nsHttpConnection::CanReuse()
|
||||||
{
|
{
|
||||||
|
if ((mTransaction ? mTransaction->PipelineDepth() : 0) >=
|
||||||
|
mRemainingConnectionUses) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool canReuse;
|
bool canReuse;
|
||||||
|
|
||||||
if (mUsingSpdy)
|
if (mSpdySession)
|
||||||
canReuse = mSpdySession->CanReuse();
|
canReuse = mSpdySession->CanReuse();
|
||||||
else
|
else
|
||||||
canReuse = IsKeepAlive();
|
canReuse = IsKeepAlive();
|
||||||
|
|
||||||
canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive();
|
canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive();
|
||||||
|
|
||||||
// An idle persistent connection should not have data waiting to be read
|
// An idle persistent connection should not have data waiting to be read
|
||||||
@ -560,7 +592,8 @@ nsHttpConnection::CanDirectlyActivate()
|
|||||||
// time through Activate(). In practice this means this is a healthy SPDY
|
// time through Activate(). In practice this means this is a healthy SPDY
|
||||||
// connection with room for more concurrent streams.
|
// connection with room for more concurrent streams.
|
||||||
|
|
||||||
return UsingSpdy() && CanReuse() && mSpdySession->RoomForMoreStreams();
|
return UsingSpdy() && CanReuse() &&
|
||||||
|
mSpdySession && mSpdySession->RoomForMoreStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
PRIntervalTime
|
PRIntervalTime
|
||||||
@ -619,21 +652,19 @@ nsHttpConnection::SupportsPipelining(nsHttpResponseHead *responseHead)
|
|||||||
if (mUsingSpdy)
|
if (mUsingSpdy)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// XXX there should be a strict mode available that disables this
|
|
||||||
// blacklisting.
|
|
||||||
|
|
||||||
// assuming connection is HTTP/1.1 with keep-alive enabled
|
// assuming connection is HTTP/1.1 with keep-alive enabled
|
||||||
if (mConnInfo->UsingHttpProxy() && !mConnInfo->UsingSSL()) {
|
if (mConnInfo->UsingHttpProxy() && !mConnInfo->UsingSSL()) {
|
||||||
// XXX check for bad proxy servers...
|
// XXX check for bad proxy servers...
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX what about checking for a Via header? (transparent proxies)
|
|
||||||
|
|
||||||
// check for bad origin servers
|
// check for bad origin servers
|
||||||
const char *val = responseHead->PeekHeader(nsHttp::Server);
|
const char *val = responseHead->PeekHeader(nsHttp::Server);
|
||||||
|
|
||||||
|
// If there is no server header we will assume it should not be banned
|
||||||
|
// as facebook and some other prominent sites do this
|
||||||
if (!val)
|
if (!val)
|
||||||
return false; // no header, no love
|
return true;
|
||||||
|
|
||||||
// The blacklist is indexed by the first character. All of these servers are
|
// The blacklist is indexed by the first character. All of these servers are
|
||||||
// known to return their identifier as the first thing in the server string,
|
// known to return their identifier as the first thing in the server string,
|
||||||
@ -660,6 +691,8 @@ nsHttpConnection::SupportsPipelining(nsHttpResponseHead *responseHead)
|
|||||||
for (int i = 0; bad_servers[index][i] != nsnull; i++) {
|
for (int i = 0; bad_servers[index][i] != nsnull; i++) {
|
||||||
if (!PL_strncmp (val, bad_servers[index][i], strlen (bad_servers[index][i]))) {
|
if (!PL_strncmp (val, bad_servers[index][i], strlen (bad_servers[index][i]))) {
|
||||||
LOG(("looks like this server does not support pipelining"));
|
LOG(("looks like this server does not support pipelining"));
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::RedBannedServer, this , 0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -715,11 +748,23 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|||||||
mKeepAlive = true;
|
mKeepAlive = true;
|
||||||
else
|
else
|
||||||
mKeepAlive = false;
|
mKeepAlive = false;
|
||||||
|
|
||||||
|
// We need at least version 1.1 to use pipelines
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::RedVersionTooLow, this, 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// HTTP/1.1 connections are by default persistent
|
// HTTP/1.1 connections are by default persistent
|
||||||
if (val && !PL_strcasecmp(val, "close"))
|
if (val && !PL_strcasecmp(val, "close")) {
|
||||||
mKeepAlive = false;
|
mKeepAlive = false;
|
||||||
|
|
||||||
|
// persistent connections are required for pipelining to work - if
|
||||||
|
// this close was not pre-announced then generate the negative
|
||||||
|
// BadExplicitClose feedback
|
||||||
|
if (mRemainingConnectionUses > 1)
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::BadExplicitClose, this, 0);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
mKeepAlive = true;
|
mKeepAlive = true;
|
||||||
|
|
||||||
@ -734,6 +779,31 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|||||||
}
|
}
|
||||||
mKeepAliveMask = mKeepAlive;
|
mKeepAliveMask = mKeepAlive;
|
||||||
|
|
||||||
|
// Update the pipelining status in the connection info object
|
||||||
|
// and also read it back. It is possible the ci status is
|
||||||
|
// locked to false if pipelining has been banned on this ci due to
|
||||||
|
// some kind of observed flaky behavior
|
||||||
|
if (mSupportsPipelining) {
|
||||||
|
// report the pipelining-compatible header to the connection manager
|
||||||
|
// as positive feedback. This will undo 1 penalty point the host
|
||||||
|
// may have accumulated in the past.
|
||||||
|
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::NeutralExpectedOK, this, 0);
|
||||||
|
|
||||||
|
mSupportsPipelining =
|
||||||
|
gHttpHandler->ConnMgr()->SupportsPipelining(mConnInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this connection is reserved for revalidations and we are
|
||||||
|
// receiving a document that failed revalidation then switch the
|
||||||
|
// classification to general to avoid pipelining more revalidations behind
|
||||||
|
// it.
|
||||||
|
if (mClassification == nsAHttpTransaction::CLASS_REVALIDATION &&
|
||||||
|
responseHead->Status() != 304) {
|
||||||
|
mClassification = nsAHttpTransaction::CLASS_GENERAL;
|
||||||
|
}
|
||||||
|
|
||||||
// if this connection is persistent, then the server may send a "Keep-Alive"
|
// if this connection is persistent, then the server may send a "Keep-Alive"
|
||||||
// header specifying the maximum number of times the connection can be
|
// header specifying the maximum number of times the connection can be
|
||||||
// reused as well as the maximum amount of time the connection can be idle
|
// reused as well as the maximum amount of time the connection can be idle
|
||||||
@ -741,6 +811,7 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|||||||
// a "keep-alive" connection is by definition capable of being reused, and
|
// a "keep-alive" connection is by definition capable of being reused, and
|
||||||
// we only care about being able to reuse it once. if a timeout is not
|
// we only care about being able to reuse it once. if a timeout is not
|
||||||
// specified then we use our advertized timeout value.
|
// specified then we use our advertized timeout value.
|
||||||
|
bool foundKeepAliveMax = false;
|
||||||
if (mKeepAlive) {
|
if (mKeepAlive) {
|
||||||
val = responseHead->PeekHeader(nsHttp::Keep_Alive);
|
val = responseHead->PeekHeader(nsHttp::Keep_Alive);
|
||||||
|
|
||||||
@ -749,16 +820,28 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|||||||
if (cp)
|
if (cp)
|
||||||
mIdleTimeout = PR_SecondsToInterval((PRUint32) atoi(cp + 8));
|
mIdleTimeout = PR_SecondsToInterval((PRUint32) atoi(cp + 8));
|
||||||
else
|
else
|
||||||
mIdleTimeout = gHttpHandler->IdleTimeout();
|
mIdleTimeout = gHttpHandler->SpdyTimeout();
|
||||||
|
|
||||||
|
cp = PL_strcasestr(val, "max=");
|
||||||
|
if (cp) {
|
||||||
|
int val = atoi(cp + 4);
|
||||||
|
if (val > 0) {
|
||||||
|
foundKeepAliveMax = true;
|
||||||
|
mRemainingConnectionUses = static_cast<PRUint32>(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mIdleTimeout = gHttpHandler->SpdyTimeout();
|
mIdleTimeout = gHttpHandler->SpdyTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(("Connection can be reused [this=%x idle-timeout=%usec]\n",
|
LOG(("Connection can be reused [this=%p idle-timeout=%usec]\n",
|
||||||
this, PR_IntervalToSeconds(mIdleTimeout)));
|
this, PR_IntervalToSeconds(mIdleTimeout)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!foundKeepAliveMax && mRemainingConnectionUses && !mUsingSpdy)
|
||||||
|
--mRemainingConnectionUses;
|
||||||
|
|
||||||
if (!mProxyConnectStream)
|
if (!mProxyConnectStream)
|
||||||
HandleAlternateProtocol(responseHead);
|
HandleAlternateProtocol(responseHead);
|
||||||
|
|
||||||
@ -880,8 +963,56 @@ nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pending patches places pipeline rescheduling code will go here
|
PRIntervalTime delta = PR_IntervalNow() - mLastReadTime;
|
||||||
|
|
||||||
|
// we replicate some of the checks both here and in OnSocketReadable() as
|
||||||
|
// they will be discovered under different conditions. The ones here
|
||||||
|
// will generally be discovered if we are totally hung and OSR does
|
||||||
|
// not get called at all, however OSR discovers them with lower latency
|
||||||
|
// if the issue is just very slow (but not stalled) reading.
|
||||||
|
//
|
||||||
|
// Right now we only take action if pipelining is involved, but this would
|
||||||
|
// be the place to add general read timeout handling if it is desired.
|
||||||
|
|
||||||
|
const PRIntervalTime k1000ms = PR_MillisecondsToInterval(1000);
|
||||||
|
|
||||||
|
if (delta < k1000ms)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PRUint32 pipelineDepth = mTransaction->PipelineDepth();
|
||||||
|
|
||||||
|
// this just reschedules blocked transactions. no transaction
|
||||||
|
// is aborted completely.
|
||||||
|
LOG(("cancelling pipeline due to a %ums stall - depth %d\n",
|
||||||
|
PR_IntervalToMilliseconds(delta), pipelineDepth));
|
||||||
|
|
||||||
|
if (pipelineDepth > 1) {
|
||||||
|
nsHttpPipeline *pipeline = mTransaction->QueryPipeline();
|
||||||
|
NS_ABORT_IF_FALSE(pipeline, "pipelinedepth > 1 without pipeline");
|
||||||
|
// code this defensively for the moment and check for null in opt build
|
||||||
|
if (pipeline)
|
||||||
|
pipeline->CancelPipeline(NS_ERROR_NET_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta < gHttpHandler->GetPipelineTimeout())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pipelineDepth <= 1 && !mTransaction->PipelinePosition())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// nothing has transpired on this pipelined socket for many
|
||||||
|
// seconds. Call that a total stall and close the transaction.
|
||||||
|
// There is a chance the transaction will be restarted again
|
||||||
|
// depending on its state.. that will come back araound
|
||||||
|
// without pipelining on, so this won't loop.
|
||||||
|
|
||||||
|
LOG(("canceling transaction stalled for %ums on a pipeline"
|
||||||
|
"of depth %d and scheduled originally at pos %d\n",
|
||||||
|
PR_IntervalToMilliseconds(delta),
|
||||||
|
pipelineDepth, mTransaction->PipelinePosition()));
|
||||||
|
|
||||||
|
// This will also close the connection
|
||||||
|
CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -930,6 +1061,14 @@ nsHttpConnection::ResumeRecv()
|
|||||||
|
|
||||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||||
|
|
||||||
|
// the mLastReadTime timestamp is used for finding slowish readers
|
||||||
|
// and can be pretty sensitive. For that reason we actually reset it
|
||||||
|
// when we ask to read (resume recv()) so that when we get called back
|
||||||
|
// with actual read data in OnSocketReadable() we are only measuring
|
||||||
|
// the latency between those two acts and not all the processing that
|
||||||
|
// may get done before the ResumeRecv() call
|
||||||
|
mLastReadTime = PR_IntervalNow();
|
||||||
|
|
||||||
if (mSocketIn)
|
if (mSocketIn)
|
||||||
return mSocketIn->AsyncWait(this, 0, 0, nsnull);
|
return mSocketIn->AsyncWait(this, 0, 0, nsnull);
|
||||||
|
|
||||||
@ -1052,7 +1191,8 @@ nsHttpConnection::OnReadSegment(const char *buf,
|
|||||||
nsresult
|
nsresult
|
||||||
nsHttpConnection::OnSocketWritable()
|
nsHttpConnection::OnSocketWritable()
|
||||||
{
|
{
|
||||||
LOG(("nsHttpConnection::OnSocketWritable [this=%x]\n", this));
|
LOG(("nsHttpConnection::OnSocketWritable [this=%p] host=%s\n",
|
||||||
|
this, mConnInfo->Host()));
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
PRUint32 n;
|
PRUint32 n;
|
||||||
@ -1094,6 +1234,7 @@ nsHttpConnection::OnSocketWritable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOG((" writing transaction request stream\n"));
|
LOG((" writing transaction request stream\n"));
|
||||||
|
mProxyConnectInProgress = false;
|
||||||
rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize, &n);
|
rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize, &n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1131,7 +1272,7 @@ nsHttpConnection::OnSocketWritable()
|
|||||||
nsISocketTransport::STATUS_WAITING_FOR,
|
nsISocketTransport::STATUS_WAITING_FOR,
|
||||||
LL_ZERO);
|
LL_ZERO);
|
||||||
|
|
||||||
rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading
|
rv = ResumeRecv(); // start reading
|
||||||
again = false;
|
again = false;
|
||||||
}
|
}
|
||||||
// write more to the socket until error or end-of-request...
|
// write more to the socket until error or end-of-request...
|
||||||
@ -1170,14 +1311,53 @@ nsHttpConnection::OnSocketReadable()
|
|||||||
LOG(("nsHttpConnection::OnSocketReadable [this=%x]\n", this));
|
LOG(("nsHttpConnection::OnSocketReadable [this=%x]\n", this));
|
||||||
|
|
||||||
PRIntervalTime now = PR_IntervalNow();
|
PRIntervalTime now = PR_IntervalNow();
|
||||||
|
PRIntervalTime delta = now - mLastReadTime;
|
||||||
|
|
||||||
if (mKeepAliveMask && ((now - mLastReadTime) >= mMaxHangTime)) {
|
if (mKeepAliveMask && (delta >= mMaxHangTime)) {
|
||||||
LOG(("max hang time exceeded!\n"));
|
LOG(("max hang time exceeded!\n"));
|
||||||
// give the handler a chance to create a new persistent connection to
|
// give the handler a chance to create a new persistent connection to
|
||||||
// this host if we've been busy for too long.
|
// this host if we've been busy for too long.
|
||||||
mKeepAliveMask = false;
|
mKeepAliveMask = false;
|
||||||
gHttpHandler->ProcessPendingQ(mConnInfo);
|
gHttpHandler->ProcessPendingQ(mConnInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Look for data being sent in bursts with large pauses. If the pauses
|
||||||
|
// are caused by server bottlenecks such as think-time, disk i/o, or
|
||||||
|
// cpu exhaustion (as opposed to network latency) then we generate negative
|
||||||
|
// pipelining feedback to prevent head of line problems
|
||||||
|
|
||||||
|
// Reduce the estimate of the time since last read by up to 1 RTT to
|
||||||
|
// accommodate exhausted sender TCP congestion windows or minor I/O delays.
|
||||||
|
|
||||||
|
if (delta > mRtt)
|
||||||
|
delta -= mRtt;
|
||||||
|
else
|
||||||
|
delta = 0;
|
||||||
|
|
||||||
|
const PRIntervalTime k400ms = PR_MillisecondsToInterval(400);
|
||||||
|
const PRIntervalTime k1200ms = PR_MillisecondsToInterval(1200);
|
||||||
|
|
||||||
|
if (delta > k1200ms) {
|
||||||
|
LOG(("Read delta ms of %u causing slow read major "
|
||||||
|
"event and pipeline cancellation",
|
||||||
|
PR_IntervalToMilliseconds(delta)));
|
||||||
|
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::BadSlowReadMajor, this, 0);
|
||||||
|
|
||||||
|
if (mTransaction->PipelineDepth() > 1) {
|
||||||
|
nsHttpPipeline *pipeline = mTransaction->QueryPipeline();
|
||||||
|
NS_ABORT_IF_FALSE(pipeline, "pipelinedepth > 1 without pipeline");
|
||||||
|
// code this defensively for the moment and check for null
|
||||||
|
if (pipeline)
|
||||||
|
pipeline->CancelPipeline(NS_ERROR_NET_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (delta > k400ms) {
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::BadSlowReadMinor, this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
mLastReadTime = now;
|
mLastReadTime = now;
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
@ -1199,7 +1379,7 @@ nsHttpConnection::OnSocketReadable()
|
|||||||
if (NS_FAILED(mSocketInCondition)) {
|
if (NS_FAILED(mSocketInCondition)) {
|
||||||
// continue waiting for the socket if necessary...
|
// continue waiting for the socket if necessary...
|
||||||
if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK)
|
if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK)
|
||||||
rv = mSocketIn->AsyncWait(this, 0, 0, nsnull);
|
rv = ResumeRecv();
|
||||||
else
|
else
|
||||||
rv = mSocketInCondition;
|
rv = mSocketInCondition;
|
||||||
again = false;
|
again = false;
|
||||||
|
@ -41,13 +41,14 @@
|
|||||||
|
|
||||||
#include "nsHttp.h"
|
#include "nsHttp.h"
|
||||||
#include "nsHttpConnectionInfo.h"
|
#include "nsHttpConnectionInfo.h"
|
||||||
#include "nsAHttpConnection.h"
|
|
||||||
#include "nsAHttpTransaction.h"
|
#include "nsAHttpTransaction.h"
|
||||||
|
#include "nsHttpPipeline.h"
|
||||||
#include "nsXPIDLString.h"
|
#include "nsXPIDLString.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
#include "prinrval.h"
|
#include "prinrval.h"
|
||||||
#include "SpdySession.h"
|
#include "SpdySession.h"
|
||||||
|
#include "mozilla/TimeStamp.h"
|
||||||
|
|
||||||
#include "nsIStreamListener.h"
|
#include "nsIStreamListener.h"
|
||||||
#include "nsISocketTransport.h"
|
#include "nsISocketTransport.h"
|
||||||
@ -56,6 +57,9 @@
|
|||||||
#include "nsIInterfaceRequestor.h"
|
#include "nsIInterfaceRequestor.h"
|
||||||
#include "nsIEventTarget.h"
|
#include "nsIEventTarget.h"
|
||||||
|
|
||||||
|
class nsHttpRequestHead;
|
||||||
|
class nsHttpResponseHead;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsHttpConnection - represents a connection to a HTTP server (or proxy)
|
// nsHttpConnection - represents a connection to a HTTP server (or proxy)
|
||||||
//
|
//
|
||||||
@ -90,7 +94,7 @@ public:
|
|||||||
nsresult Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime,
|
nsresult Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime,
|
||||||
nsISocketTransport *, nsIAsyncInputStream *,
|
nsISocketTransport *, nsIAsyncInputStream *,
|
||||||
nsIAsyncOutputStream *, nsIInterfaceRequestor *,
|
nsIAsyncOutputStream *, nsIInterfaceRequestor *,
|
||||||
nsIEventTarget *);
|
nsIEventTarget *, PRIntervalTime);
|
||||||
|
|
||||||
// Activate causes the given transaction to be processed on this
|
// Activate causes the given transaction to be processed on this
|
||||||
// connection. It fails if there is already an existing transaction unless
|
// connection. It fails if there is already an existing transaction unless
|
||||||
@ -103,7 +107,7 @@ public:
|
|||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
// XXX document when these are ok to call
|
// XXX document when these are ok to call
|
||||||
|
|
||||||
bool SupportsPipelining() { return mSupportsPipelining; }
|
bool SupportsPipelining();
|
||||||
bool IsKeepAlive() { return mUsingSpdy ||
|
bool IsKeepAlive() { return mUsingSpdy ||
|
||||||
(mKeepAliveMask && mKeepAlive); }
|
(mKeepAliveMask && mKeepAlive); }
|
||||||
bool CanReuse(); // can this connection be reused?
|
bool CanReuse(); // can this connection be reused?
|
||||||
@ -115,6 +119,11 @@ public:
|
|||||||
void DontReuse();
|
void DontReuse();
|
||||||
void DropTransport() { DontReuse(); mSocketTransport = 0; }
|
void DropTransport() { DontReuse(); mSocketTransport = 0; }
|
||||||
|
|
||||||
|
bool IsProxyConnectInProgress()
|
||||||
|
{
|
||||||
|
return mProxyConnectInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
bool LastTransactionExpectedNoContent()
|
bool LastTransactionExpectedNoContent()
|
||||||
{
|
{
|
||||||
return mLastTransactionExpectedNoContent;
|
return mLastTransactionExpectedNoContent;
|
||||||
@ -158,9 +167,18 @@ public:
|
|||||||
|
|
||||||
bool UsingSpdy() { return mUsingSpdy; }
|
bool UsingSpdy() { return mUsingSpdy; }
|
||||||
|
|
||||||
// When the connection is active this is called every 15 seconds
|
// When the connection is active this is called every 1 second
|
||||||
void ReadTimeoutTick(PRIntervalTime now);
|
void ReadTimeoutTick(PRIntervalTime now);
|
||||||
|
|
||||||
|
nsAHttpTransaction::Classifier Classification() { return mClassification; }
|
||||||
|
void Classify(nsAHttpTransaction::Classifier newclass)
|
||||||
|
{
|
||||||
|
mClassification = newclass;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the connection is active this is called every second
|
||||||
|
void ReadTimeoutTick();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// called to cause the underlying socket to start speaking SSL
|
// called to cause the underlying socket to start speaking SSL
|
||||||
nsresult ProxyStartSSL();
|
nsresult ProxyStartSSL();
|
||||||
@ -210,7 +228,7 @@ private:
|
|||||||
|
|
||||||
nsRefPtr<nsHttpConnectionInfo> mConnInfo;
|
nsRefPtr<nsHttpConnectionInfo> mConnInfo;
|
||||||
|
|
||||||
PRUint32 mLastReadTime;
|
PRIntervalTime mLastReadTime;
|
||||||
PRIntervalTime mMaxHangTime; // max download time before dropping keep-alive status
|
PRIntervalTime mMaxHangTime; // max download time before dropping keep-alive status
|
||||||
PRIntervalTime mIdleTimeout; // value of keep-alive: timeout=
|
PRIntervalTime mIdleTimeout; // value of keep-alive: timeout=
|
||||||
PRIntervalTime mConsiderReusedAfterInterval;
|
PRIntervalTime mConsiderReusedAfterInterval;
|
||||||
@ -221,6 +239,8 @@ private:
|
|||||||
|
|
||||||
nsRefPtr<nsIAsyncInputStream> mInputOverflow;
|
nsRefPtr<nsIAsyncInputStream> mInputOverflow;
|
||||||
|
|
||||||
|
PRIntervalTime mRtt;
|
||||||
|
|
||||||
bool mKeepAlive;
|
bool mKeepAlive;
|
||||||
bool mKeepAliveMask;
|
bool mKeepAliveMask;
|
||||||
bool mSupportsPipelining;
|
bool mSupportsPipelining;
|
||||||
@ -228,11 +248,19 @@ private:
|
|||||||
bool mCompletedProxyConnect;
|
bool mCompletedProxyConnect;
|
||||||
bool mLastTransactionExpectedNoContent;
|
bool mLastTransactionExpectedNoContent;
|
||||||
bool mIdleMonitoring;
|
bool mIdleMonitoring;
|
||||||
|
bool mProxyConnectInProgress;
|
||||||
|
|
||||||
// The number of <= HTTP/1.1 transactions performed on this connection. This
|
// The number of <= HTTP/1.1 transactions performed on this connection. This
|
||||||
// excludes spdy transactions.
|
// excludes spdy transactions.
|
||||||
PRUint32 mHttp1xTransactionCount;
|
PRUint32 mHttp1xTransactionCount;
|
||||||
|
|
||||||
|
// Keep-Alive: max="mRemainingConnectionUses" provides the number of future
|
||||||
|
// transactions (including the current one) that the server expects to allow
|
||||||
|
// on this persistent connection.
|
||||||
|
PRUint32 mRemainingConnectionUses;
|
||||||
|
|
||||||
|
nsAHttpTransaction::Classifier mClassification;
|
||||||
|
|
||||||
// SPDY related
|
// SPDY related
|
||||||
bool mNPNComplete;
|
bool mNPNComplete;
|
||||||
bool mSetupNPNCalled;
|
bool mSetupNPNCalled;
|
||||||
|
@ -125,6 +125,7 @@ public:
|
|||||||
void SetAnonymous(bool anon)
|
void SetAnonymous(bool anon)
|
||||||
{ mHashKey.SetCharAt(anon ? 'A' : '.', 2); }
|
{ mHashKey.SetCharAt(anon ? 'A' : '.', 2); }
|
||||||
bool GetAnonymous() { return mHashKey.CharAt(2) == 'A'; }
|
bool GetAnonymous() { return mHashKey.CharAt(2) == 'A'; }
|
||||||
|
|
||||||
bool ShouldForceConnectMethod();
|
bool ShouldForceConnectMethod();
|
||||||
const nsCString &GetHost() { return mHost; }
|
const nsCString &GetHost() { return mHost; }
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -49,12 +49,13 @@
|
|||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
#include "mozilla/ReentrantMonitor.h"
|
#include "mozilla/ReentrantMonitor.h"
|
||||||
#include "nsISocketTransportService.h"
|
#include "nsISocketTransportService.h"
|
||||||
|
#include "mozilla/TimeStamp.h"
|
||||||
|
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "nsITimer.h"
|
#include "nsITimer.h"
|
||||||
#include "nsIX509Cert3.h"
|
#include "nsIX509Cert3.h"
|
||||||
|
|
||||||
class nsHttpPipeline;
|
#include "nsHttpPipeline.h"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -72,7 +73,8 @@ public:
|
|||||||
MAX_PERSISTENT_CONNECTIONS_PER_HOST,
|
MAX_PERSISTENT_CONNECTIONS_PER_HOST,
|
||||||
MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
|
MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
|
||||||
MAX_REQUEST_DELAY,
|
MAX_REQUEST_DELAY,
|
||||||
MAX_PIPELINED_REQUESTS
|
MAX_PIPELINED_REQUESTS,
|
||||||
|
MAX_OPTIMISTIC_PIPELINED_REQUESTS
|
||||||
};
|
};
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
@ -87,7 +89,8 @@ public:
|
|||||||
PRUint16 maxPersistentConnectionsPerHost,
|
PRUint16 maxPersistentConnectionsPerHost,
|
||||||
PRUint16 maxPersistentConnectionsPerProxy,
|
PRUint16 maxPersistentConnectionsPerProxy,
|
||||||
PRUint16 maxRequestDelay,
|
PRUint16 maxRequestDelay,
|
||||||
PRUint16 maxPipelinedRequests);
|
PRUint16 maxPipelinedRequests,
|
||||||
|
PRUint16 maxOptimisticPipelinedRequests);
|
||||||
nsresult Shutdown();
|
nsresult Shutdown();
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
@ -138,17 +141,80 @@ public:
|
|||||||
void ReportSpdyAlternateProtocol(nsHttpConnection *);
|
void ReportSpdyAlternateProtocol(nsHttpConnection *);
|
||||||
void RemoveSpdyAlternateProtocol(nsACString &key);
|
void RemoveSpdyAlternateProtocol(nsACString &key);
|
||||||
|
|
||||||
|
// Pipielining Interfaces and Datatypes
|
||||||
|
|
||||||
|
const static PRUint32 kPipelineInfoTypeMask = 0xffff0000;
|
||||||
|
const static PRUint32 kPipelineInfoIDMask = ~kPipelineInfoTypeMask;
|
||||||
|
|
||||||
|
const static PRUint32 kPipelineInfoTypeRed = 0x00010000;
|
||||||
|
const static PRUint32 kPipelineInfoTypeBad = 0x00020000;
|
||||||
|
const static PRUint32 kPipelineInfoTypeNeutral = 0x00040000;
|
||||||
|
const static PRUint32 kPipelineInfoTypeGood = 0x00080000;
|
||||||
|
|
||||||
|
enum PipelineFeedbackInfoType
|
||||||
|
{
|
||||||
|
// Used when an HTTP response less than 1.1 is received
|
||||||
|
RedVersionTooLow = kPipelineInfoTypeRed | kPipelineInfoTypeBad | 0x0001,
|
||||||
|
|
||||||
|
// Used when a HTTP Server response header that is on the banned from
|
||||||
|
// pipelining list is received
|
||||||
|
RedBannedServer = kPipelineInfoTypeRed | kPipelineInfoTypeBad | 0x0002,
|
||||||
|
|
||||||
|
// Used when a response is terminated early, when it fails an
|
||||||
|
// integrity check such as assoc-req or when a 304 contained a Last-Modified
|
||||||
|
// differnet than the entry being validated.
|
||||||
|
RedCorruptedContent = kPipelineInfoTypeRed | kPipelineInfoTypeBad | 0x0004,
|
||||||
|
|
||||||
|
// Used when a pipeline is only partly satisfied - for instance if the
|
||||||
|
// server closed the connection after responding to the first
|
||||||
|
// request but left some requests unprocessed.
|
||||||
|
RedCanceledPipeline = kPipelineInfoTypeRed | kPipelineInfoTypeBad | 0x0005,
|
||||||
|
|
||||||
|
// Used when a connection that we expected to stay persistently open
|
||||||
|
// was closed by the server. Not used when simply timed out.
|
||||||
|
BadExplicitClose = kPipelineInfoTypeBad | 0x0003,
|
||||||
|
|
||||||
|
// Used when there is a gap of around 400 - 1200ms in between data being
|
||||||
|
// read from the server
|
||||||
|
BadSlowReadMinor = kPipelineInfoTypeBad | 0x0006,
|
||||||
|
|
||||||
|
// Used when there is a gap of > 1200ms in between data being
|
||||||
|
// read from the server
|
||||||
|
BadSlowReadMajor = kPipelineInfoTypeBad | 0x0007,
|
||||||
|
|
||||||
|
// Used when a response is received that is not framed with either chunked
|
||||||
|
// encoding or a complete content length.
|
||||||
|
BadInsufficientFraming = kPipelineInfoTypeBad | 0x0008,
|
||||||
|
|
||||||
|
// Used when a very large response is recevied in a potential pipelining
|
||||||
|
// context. Large responses cause head of line blocking.
|
||||||
|
BadUnexpectedLarge = kPipelineInfoTypeBad | 0x000B,
|
||||||
|
|
||||||
|
// Used when a response is received that has headers that appear to support
|
||||||
|
// pipelining.
|
||||||
|
NeutralExpectedOK = kPipelineInfoTypeNeutral | 0x0009,
|
||||||
|
|
||||||
|
// Used when a response is received successfully to a pipelined request.
|
||||||
|
GoodCompletedOK = kPipelineInfoTypeGood | 0x000A
|
||||||
|
};
|
||||||
|
|
||||||
|
// called to provide information relevant to the pipelining manager
|
||||||
|
// may be called from any thread
|
||||||
|
void PipelineFeedbackInfo(nsHttpConnectionInfo *,
|
||||||
|
PipelineFeedbackInfoType info,
|
||||||
|
nsHttpConnection *,
|
||||||
|
PRUint32);
|
||||||
|
|
||||||
|
void ReportFailedToProcess(nsIURI *uri);
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
// NOTE: functions below may be called only on the socket thread.
|
// NOTE: functions below may be called only on the socket thread.
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
// removes the next transaction for the specified connection from the
|
|
||||||
// pending transaction queue.
|
|
||||||
void AddTransactionToPipeline(nsHttpPipeline *);
|
|
||||||
|
|
||||||
// called to force the transaction queue to be processed once more, giving
|
// called to force the transaction queue to be processed once more, giving
|
||||||
// preference to the specified connection.
|
// preference to the specified connection.
|
||||||
nsresult ProcessPendingQ(nsHttpConnectionInfo *);
|
nsresult ProcessPendingQ(nsHttpConnectionInfo *);
|
||||||
|
bool ProcessPendingQForEntry(nsHttpConnectionInfo *);
|
||||||
|
|
||||||
// This is used to force an idle connection to be closed and removed from
|
// This is used to force an idle connection to be closed and removed from
|
||||||
// the idle connection list. It is called when the idle connection detects
|
// the idle connection list. It is called when the idle connection detects
|
||||||
@ -160,26 +226,41 @@ public:
|
|||||||
// bit different.
|
// bit different.
|
||||||
void ReportSpdyConnection(nsHttpConnection *, bool usingSpdy);
|
void ReportSpdyConnection(nsHttpConnection *, bool usingSpdy);
|
||||||
|
|
||||||
|
|
||||||
|
bool SupportsPipelining(nsHttpConnectionInfo *);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~nsHttpConnectionMgr();
|
virtual ~nsHttpConnectionMgr();
|
||||||
class nsHalfOpenSocket;
|
|
||||||
|
enum PipeliningState {
|
||||||
|
// Host has proven itself pipeline capable through past experience and
|
||||||
|
// large pipeline depths are allowed on multiple connections.
|
||||||
|
PS_GREEN,
|
||||||
|
|
||||||
|
// Not enough information is available yet with this host to be certain
|
||||||
|
// of pipeline capability. Small pipelines on a single connection are
|
||||||
|
// allowed in order to decide whether or not to proceed to green.
|
||||||
|
PS_YELLOW,
|
||||||
|
|
||||||
|
// One or more bad events has happened that indicate that pipelining
|
||||||
|
// to this host (or a particular type of transaction with this host)
|
||||||
|
// is a bad idea. Pipelining is not currently allowed, but time and
|
||||||
|
// other positive experiences will eventually allow it to try again.
|
||||||
|
PS_RED
|
||||||
|
};
|
||||||
|
|
||||||
|
class nsHalfOpenSocket;
|
||||||
|
|
||||||
// nsConnectionEntry
|
// nsConnectionEntry
|
||||||
//
|
//
|
||||||
// mCT maps connection info hash key to nsConnectionEntry object, which
|
// mCT maps connection info hash key to nsConnectionEntry object, which
|
||||||
// contains list of active and idle connections as well as the list of
|
// contains list of active and idle connections as well as the list of
|
||||||
// pending transactions.
|
// pending transactions.
|
||||||
//
|
//
|
||||||
struct nsConnectionEntry
|
class nsConnectionEntry
|
||||||
{
|
{
|
||||||
nsConnectionEntry(nsHttpConnectionInfo *ci)
|
public:
|
||||||
: mConnInfo(ci),
|
nsConnectionEntry(nsHttpConnectionInfo *ci);
|
||||||
mUsingSpdy(false),
|
|
||||||
mTestedSpdy(false),
|
|
||||||
mSpdyPreferred(false)
|
|
||||||
{
|
|
||||||
NS_ADDREF(mConnInfo);
|
|
||||||
}
|
|
||||||
~nsConnectionEntry();
|
~nsConnectionEntry();
|
||||||
|
|
||||||
nsHttpConnectionInfo *mConnInfo;
|
nsHttpConnectionInfo *mConnInfo;
|
||||||
@ -188,6 +269,54 @@ private:
|
|||||||
nsTArray<nsHttpConnection*> mIdleConns; // idle persistent connections
|
nsTArray<nsHttpConnection*> mIdleConns; // idle persistent connections
|
||||||
nsTArray<nsHalfOpenSocket*> mHalfOpens;
|
nsTArray<nsHalfOpenSocket*> mHalfOpens;
|
||||||
|
|
||||||
|
// Pipeline depths for various states
|
||||||
|
const static PRUint32 kPipelineUnlimited = 1024; // fully open - extended green
|
||||||
|
const static PRUint32 kPipelineOpen = 6; // 6 on each conn - normal green
|
||||||
|
const static PRUint32 kPipelineRestricted = 2; // 2 on just 1 conn in yellow
|
||||||
|
|
||||||
|
nsHttpConnectionMgr::PipeliningState PipelineState();
|
||||||
|
void OnPipelineFeedbackInfo(
|
||||||
|
nsHttpConnectionMgr::PipelineFeedbackInfoType info,
|
||||||
|
nsHttpConnection *, PRUint32);
|
||||||
|
bool SupportsPipelining();
|
||||||
|
PRUint32 MaxPipelineDepth(nsAHttpTransaction::Classifier classification);
|
||||||
|
void CreditPenalty();
|
||||||
|
|
||||||
|
nsHttpConnectionMgr::PipeliningState mPipelineState;
|
||||||
|
|
||||||
|
void SetYellowConnection(nsHttpConnection *);
|
||||||
|
void OnYellowComplete();
|
||||||
|
PRUint32 mYellowGoodEvents;
|
||||||
|
PRUint32 mYellowBadEvents;
|
||||||
|
nsHttpConnection *mYellowConnection;
|
||||||
|
|
||||||
|
// initialGreenDepth is the max depth of a pipeline when you first
|
||||||
|
// transition to green. Normally this is kPipelineOpen, but it can
|
||||||
|
// be kPipelineUnlimited in aggressive mode.
|
||||||
|
PRUint32 mInitialGreenDepth;
|
||||||
|
|
||||||
|
// greenDepth is the current max allowed depth of a pipeline when
|
||||||
|
// in the green state. Normally this starts as kPipelineOpen and
|
||||||
|
// grows to kPipelineUnlimited after a pipeline of depth 3 has been
|
||||||
|
// successfully transacted.
|
||||||
|
PRUint32 mGreenDepth;
|
||||||
|
|
||||||
|
// pipeliningPenalty is the current amount of penalty points this host
|
||||||
|
// entry has earned for participating in events that are not conducive
|
||||||
|
// to good pipelines - such as head of line blocking, canceled pipelines,
|
||||||
|
// etc.. penalties are paid back either through elapsed time or simply
|
||||||
|
// healthy transactions. Having penalty points means that this host is
|
||||||
|
// not currently eligible for pipelines.
|
||||||
|
PRInt16 mPipeliningPenalty;
|
||||||
|
|
||||||
|
// some penalty points only apply to particular classifications of
|
||||||
|
// transactions - this allows a server that perhaps has head of line
|
||||||
|
// blocking problems on CGI queries to still serve JS pipelined.
|
||||||
|
PRInt16 mPipeliningClassPenalty[nsAHttpTransaction::CLASS_MAX];
|
||||||
|
|
||||||
|
// for calculating penalty repair credits
|
||||||
|
mozilla::TimeStamp mLastCreditTime;
|
||||||
|
|
||||||
// Spdy sometimes resolves the address in the socket manager in order
|
// Spdy sometimes resolves the address in the socket manager in order
|
||||||
// to re-coalesce sharded HTTP hosts. The dotted decimal address is
|
// to re-coalesce sharded HTTP hosts. The dotted decimal address is
|
||||||
// combined with the Anonymous flag from the connection information
|
// combined with the Anonymous flag from the connection information
|
||||||
@ -272,6 +401,9 @@ private:
|
|||||||
nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
|
nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
|
||||||
nsCOMPtr<nsIAsyncInputStream> mStreamIn;
|
nsCOMPtr<nsIAsyncInputStream> mStreamIn;
|
||||||
|
|
||||||
|
mozilla::TimeStamp mPrimarySynStarted;
|
||||||
|
mozilla::TimeStamp mBackupSynStarted;
|
||||||
|
|
||||||
// for syn retry
|
// for syn retry
|
||||||
nsCOMPtr<nsITimer> mSynTimer;
|
nsCOMPtr<nsITimer> mSynTimer;
|
||||||
nsCOMPtr<nsISocketTransport> mBackupTransport;
|
nsCOMPtr<nsISocketTransport> mBackupTransport;
|
||||||
@ -296,7 +428,7 @@ private:
|
|||||||
PRUint16 mMaxPersistConnsPerProxy;
|
PRUint16 mMaxPersistConnsPerProxy;
|
||||||
PRUint16 mMaxRequestDelay; // in seconds
|
PRUint16 mMaxRequestDelay; // in seconds
|
||||||
PRUint16 mMaxPipelinedRequests;
|
PRUint16 mMaxPipelinedRequests;
|
||||||
|
PRUint16 mMaxOptimisticPipelinedRequests;
|
||||||
bool mIsShuttingDown;
|
bool mIsShuttingDown;
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
@ -310,12 +442,18 @@ private:
|
|||||||
static PLDHashOperator PurgeExcessIdleConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
|
static PLDHashOperator PurgeExcessIdleConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
|
||||||
static PLDHashOperator ClosePersistentConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
|
static PLDHashOperator ClosePersistentConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
|
||||||
bool ProcessPendingQForEntry(nsConnectionEntry *);
|
bool ProcessPendingQForEntry(nsConnectionEntry *);
|
||||||
|
bool IsUnderPressure(nsConnectionEntry *ent,
|
||||||
|
nsHttpTransaction::Classifier classification);
|
||||||
bool AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
|
bool AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
|
||||||
void GetConnection(nsConnectionEntry *, nsHttpTransaction *,
|
nsresult TryDispatchTransaction(nsConnectionEntry *ent,
|
||||||
bool, nsHttpConnection **);
|
bool onlyReusedConnection,
|
||||||
nsresult DispatchTransaction(nsConnectionEntry *, nsHttpTransaction *,
|
nsHttpTransaction *trans);
|
||||||
PRUint8 caps, nsHttpConnection *);
|
nsresult DispatchTransaction(nsConnectionEntry *,
|
||||||
bool BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
|
nsHttpTransaction *,
|
||||||
|
nsHttpConnection *);
|
||||||
|
nsresult BuildPipeline(nsConnectionEntry *,
|
||||||
|
nsAHttpTransaction *,
|
||||||
|
nsHttpPipeline **);
|
||||||
nsresult ProcessNewTransaction(nsHttpTransaction *);
|
nsresult ProcessNewTransaction(nsHttpTransaction *);
|
||||||
nsresult EnsureSocketThreadTargetIfOnline();
|
nsresult EnsureSocketThreadTargetIfOnline();
|
||||||
void ClosePersistentConnections(nsConnectionEntry *ent);
|
void ClosePersistentConnections(nsConnectionEntry *ent);
|
||||||
@ -324,6 +462,13 @@ private:
|
|||||||
void StartedConnect();
|
void StartedConnect();
|
||||||
void RecvdConnect();
|
void RecvdConnect();
|
||||||
|
|
||||||
|
bool MakeNewConnection(nsConnectionEntry *ent,
|
||||||
|
nsHttpTransaction *trans);
|
||||||
|
bool AddToShortestPipeline(nsConnectionEntry *ent,
|
||||||
|
nsHttpTransaction *trans,
|
||||||
|
nsHttpTransaction::Classifier classification,
|
||||||
|
PRUint16 depthLimit);
|
||||||
|
|
||||||
// Manage the preferred spdy connection entry for this address
|
// Manage the preferred spdy connection entry for this address
|
||||||
nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
|
nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
|
||||||
void RemoveSpdyPreferredEnt(nsACString &aDottedDecimal);
|
void RemoveSpdyPreferredEnt(nsACString &aDottedDecimal);
|
||||||
@ -396,6 +541,7 @@ private:
|
|||||||
void OnMsgReclaimConnection (PRInt32, void *);
|
void OnMsgReclaimConnection (PRInt32, void *);
|
||||||
void OnMsgUpdateParam (PRInt32, void *);
|
void OnMsgUpdateParam (PRInt32, void *);
|
||||||
void OnMsgClosePersistentConnections (PRInt32, void *);
|
void OnMsgClosePersistentConnections (PRInt32, void *);
|
||||||
|
void OnMsgProcessFeedback (PRInt32, void *);
|
||||||
|
|
||||||
// Total number of active connections in all of the ConnectionEntry objects
|
// Total number of active connections in all of the ConnectionEntry objects
|
||||||
// that are accessed from mCT connection table.
|
// that are accessed from mCT connection table.
|
||||||
|
@ -185,11 +185,16 @@ nsHttpHandler::nsHttpHandler()
|
|||||||
, mMaxConnectionsPerServer(8)
|
, mMaxConnectionsPerServer(8)
|
||||||
, mMaxPersistentConnectionsPerServer(2)
|
, mMaxPersistentConnectionsPerServer(2)
|
||||||
, mMaxPersistentConnectionsPerProxy(4)
|
, mMaxPersistentConnectionsPerProxy(4)
|
||||||
, mMaxPipelinedRequests(2)
|
, mMaxPipelinedRequests(32)
|
||||||
|
, mMaxOptimisticPipelinedRequests(4)
|
||||||
|
, mPipelineAggressive(false)
|
||||||
|
, mMaxPipelineObjectSize(300000)
|
||||||
|
, mPipelineReadTimeout(PR_MillisecondsToInterval(10000))
|
||||||
, mRedirectionLimit(10)
|
, mRedirectionLimit(10)
|
||||||
, mPhishyUserPassLength(1)
|
, mPhishyUserPassLength(1)
|
||||||
, mQoSBits(0x00)
|
, mQoSBits(0x00)
|
||||||
, mPipeliningOverSSL(false)
|
, mPipeliningOverSSL(false)
|
||||||
|
, mEnforceAssocReq(false)
|
||||||
, mInPrivateBrowsingMode(PRIVATE_BROWSING_UNKNOWN)
|
, mInPrivateBrowsingMode(PRIVATE_BROWSING_UNKNOWN)
|
||||||
, mLastUniqueID(NowInSeconds())
|
, mLastUniqueID(NowInSeconds())
|
||||||
, mSessionStartTime(0)
|
, mSessionStartTime(0)
|
||||||
@ -338,6 +343,7 @@ nsHttpHandler::Init()
|
|||||||
mObserverService->AddObserver(this, "net:clear-active-logins", true);
|
mObserverService->AddObserver(this, "net:clear-active-logins", true);
|
||||||
mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
|
mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
|
||||||
mObserverService->AddObserver(this, "net:prune-dead-connections", true);
|
mObserverService->AddObserver(this, "net:prune-dead-connections", true);
|
||||||
|
mObserverService->AddObserver(this, "net:failed-to-process-uri", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -363,7 +369,8 @@ nsHttpHandler::InitConnectionMgr()
|
|||||||
mMaxPersistentConnectionsPerServer,
|
mMaxPersistentConnectionsPerServer,
|
||||||
mMaxPersistentConnectionsPerProxy,
|
mMaxPersistentConnectionsPerProxy,
|
||||||
mMaxRequestDelay,
|
mMaxRequestDelay,
|
||||||
mMaxPipelinedRequests);
|
mMaxPipelinedRequests,
|
||||||
|
mMaxOptimisticPipelinedRequests);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1015,13 +1022,47 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
|||||||
if (PREF_CHANGED(HTTP_PREF("pipelining.maxrequests"))) {
|
if (PREF_CHANGED(HTTP_PREF("pipelining.maxrequests"))) {
|
||||||
rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxrequests"), &val);
|
rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxrequests"), &val);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
mMaxPipelinedRequests = clamped(val, 1, NS_HTTP_MAX_PIPELINED_REQUESTS);
|
mMaxPipelinedRequests = clamped(val, 1, 0xffff);
|
||||||
if (mConnMgr)
|
if (mConnMgr)
|
||||||
mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PIPELINED_REQUESTS,
|
mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PIPELINED_REQUESTS,
|
||||||
mMaxPipelinedRequests);
|
mMaxPipelinedRequests);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PREF_CHANGED(HTTP_PREF("pipelining.max-optimistic-requests"))) {
|
||||||
|
rv = prefs->
|
||||||
|
GetIntPref(HTTP_PREF("pipelining.max-optimistic-requests"), &val);
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
mMaxOptimisticPipelinedRequests = clamped(val, 1, 0xffff);
|
||||||
|
if (mConnMgr)
|
||||||
|
mConnMgr->UpdateParam
|
||||||
|
(nsHttpConnectionMgr::MAX_OPTIMISTIC_PIPELINED_REQUESTS,
|
||||||
|
mMaxOptimisticPipelinedRequests);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PREF_CHANGED(HTTP_PREF("pipelining.aggressive"))) {
|
||||||
|
rv = prefs->GetBoolPref(HTTP_PREF("pipelining.aggressive"), &cVar);
|
||||||
|
if (NS_SUCCEEDED(rv))
|
||||||
|
mPipelineAggressive = cVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PREF_CHANGED(HTTP_PREF("pipelining.maxsize"))) {
|
||||||
|
rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxsize"), &val);
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
mMaxPipelineObjectSize =
|
||||||
|
static_cast<PRInt64>(clamped(val, 1000, 100000000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PREF_CHANGED(HTTP_PREF("pipelining.read-timeout"))) {
|
||||||
|
rv = prefs->GetIntPref(HTTP_PREF("pipelining.read-timeout"), &val);
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
mPipelineReadTimeout =
|
||||||
|
PR_MillisecondsToInterval(clamped(val, 500, 0xffff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (PREF_CHANGED(HTTP_PREF("pipelining.ssl"))) {
|
if (PREF_CHANGED(HTTP_PREF("pipelining.ssl"))) {
|
||||||
rv = prefs->GetBoolPref(HTTP_PREF("pipelining.ssl"), &cVar);
|
rv = prefs->GetBoolPref(HTTP_PREF("pipelining.ssl"), &cVar);
|
||||||
if (NS_SUCCEEDED(rv))
|
if (NS_SUCCEEDED(rv))
|
||||||
@ -1103,6 +1144,13 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PREF_CHANGED(HTTP_PREF("assoc-req.enforce"))) {
|
||||||
|
cVar = false;
|
||||||
|
rv = prefs->GetBoolPref(HTTP_PREF("assoc-req.enforce"), &cVar);
|
||||||
|
if (NS_SUCCEEDED(rv))
|
||||||
|
mEnforceAssocReq = cVar;
|
||||||
|
}
|
||||||
|
|
||||||
// enable Persistent caching for HTTPS - bug#205921
|
// enable Persistent caching for HTTPS - bug#205921
|
||||||
if (PREF_CHANGED(BROWSER_PREF("disk_cache_ssl"))) {
|
if (PREF_CHANGED(BROWSER_PREF("disk_cache_ssl"))) {
|
||||||
cVar = false;
|
cVar = false;
|
||||||
@ -1587,6 +1635,11 @@ nsHttpHandler::Observe(nsISupports *subject,
|
|||||||
mConnMgr->PruneDeadConnections();
|
mConnMgr->PruneDeadConnections();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (strcmp(topic, "net:failed-to-process-uri") == 0) {
|
||||||
|
nsCOMPtr<nsIURI> uri = do_QueryInterface(subject);
|
||||||
|
if (uri && mConnMgr)
|
||||||
|
mConnMgr->ReportFailedToProcess(uri);
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,7 @@ public:
|
|||||||
PRUint16 GetIdleSynTimeout() { return mIdleSynTimeout; }
|
PRUint16 GetIdleSynTimeout() { return mIdleSynTimeout; }
|
||||||
bool FastFallbackToIPv4() { return mFastFallbackToIPv4; }
|
bool FastFallbackToIPv4() { return mFastFallbackToIPv4; }
|
||||||
PRUint32 MaxSocketCount();
|
PRUint32 MaxSocketCount();
|
||||||
|
bool EnforceAssocReq() { return mEnforceAssocReq; }
|
||||||
|
|
||||||
bool IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; }
|
bool IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; }
|
||||||
bool IsTelemetryEnabled() { return mTelemetryEnabled; }
|
bool IsTelemetryEnabled() { return mTelemetryEnabled; }
|
||||||
@ -231,6 +232,13 @@ public:
|
|||||||
static nsresult GenerateHostPort(const nsCString& host, PRInt32 port,
|
static nsresult GenerateHostPort(const nsCString& host, PRInt32 port,
|
||||||
nsCString& hostLine);
|
nsCString& hostLine);
|
||||||
|
|
||||||
|
bool GetPipelineAggressive() { return mPipelineAggressive; }
|
||||||
|
void GetMaxPipelineObjectSize(PRInt64 &outVal)
|
||||||
|
{
|
||||||
|
outVal = mMaxPipelineObjectSize;
|
||||||
|
}
|
||||||
|
PRIntervalTime GetPipelineTimeout() { return mPipelineReadTimeout; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -287,7 +295,12 @@ private:
|
|||||||
PRUint8 mMaxConnectionsPerServer;
|
PRUint8 mMaxConnectionsPerServer;
|
||||||
PRUint8 mMaxPersistentConnectionsPerServer;
|
PRUint8 mMaxPersistentConnectionsPerServer;
|
||||||
PRUint8 mMaxPersistentConnectionsPerProxy;
|
PRUint8 mMaxPersistentConnectionsPerProxy;
|
||||||
PRUint8 mMaxPipelinedRequests;
|
PRUint16 mMaxPipelinedRequests;
|
||||||
|
PRUint16 mMaxOptimisticPipelinedRequests;
|
||||||
|
bool mPipelineAggressive;
|
||||||
|
PRInt64 mMaxPipelineObjectSize;
|
||||||
|
|
||||||
|
PRIntervalTime mPipelineReadTimeout;
|
||||||
|
|
||||||
PRUint8 mRedirectionLimit;
|
PRUint8 mRedirectionLimit;
|
||||||
|
|
||||||
@ -300,6 +313,7 @@ private:
|
|||||||
PRUint8 mQoSBits;
|
PRUint8 mQoSBits;
|
||||||
|
|
||||||
bool mPipeliningOverSSL;
|
bool mPipeliningOverSSL;
|
||||||
|
bool mEnforceAssocReq;
|
||||||
|
|
||||||
// cached value of whether or not the browser is in private browsing mode.
|
// cached value of whether or not the browser is in private browsing mode.
|
||||||
enum {
|
enum {
|
||||||
|
@ -98,6 +98,7 @@ nsHttpPipeline::nsHttpPipeline()
|
|||||||
, mRequestIsPartial(false)
|
, mRequestIsPartial(false)
|
||||||
, mResponseIsPartial(false)
|
, mResponseIsPartial(false)
|
||||||
, mClosed(false)
|
, mClosed(false)
|
||||||
|
, mUtilizedPipeline(false)
|
||||||
, mPushBackBuf(nsnull)
|
, mPushBackBuf(nsnull)
|
||||||
, mPushBackLen(0)
|
, mPushBackLen(0)
|
||||||
, mPushBackMax(0)
|
, mPushBackMax(0)
|
||||||
@ -124,19 +125,62 @@ nsHttpPipeline::AddTransaction(nsAHttpTransaction *trans)
|
|||||||
{
|
{
|
||||||
LOG(("nsHttpPipeline::AddTransaction [this=%x trans=%x]\n", this, trans));
|
LOG(("nsHttpPipeline::AddTransaction [this=%x trans=%x]\n", this, trans));
|
||||||
|
|
||||||
|
if (mRequestQ.Length() || mResponseQ.Length())
|
||||||
|
mUtilizedPipeline = true;
|
||||||
|
|
||||||
NS_ADDREF(trans);
|
NS_ADDREF(trans);
|
||||||
mRequestQ.AppendElement(trans);
|
mRequestQ.AppendElement(trans);
|
||||||
|
PRUint32 qlen = PipelineDepth();
|
||||||
if (mConnection && !mClosed) {
|
|
||||||
trans->SetConnection(this);
|
if (qlen != 1) {
|
||||||
|
trans->SetPipelinePosition(qlen);
|
||||||
if (mRequestQ.Length() == 1)
|
}
|
||||||
mConnection->ResumeSend();
|
else {
|
||||||
|
// do it for this case in case an idempotent cancellation
|
||||||
|
// is being repeated and an old value needs to be cleared
|
||||||
|
trans->SetPipelinePosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trans->SetConnection() needs to be updated to point back at
|
||||||
|
// the pipeline object.
|
||||||
|
trans->SetConnection(this);
|
||||||
|
|
||||||
|
if (mConnection && !mClosed && mRequestQ.Length() == 1)
|
||||||
|
mConnection->ResumeSend();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRUint32
|
||||||
|
nsHttpPipeline::PipelineDepth()
|
||||||
|
{
|
||||||
|
return mRequestQ.Length() + mResponseQ.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsHttpPipeline::SetPipelinePosition(PRInt32 position)
|
||||||
|
{
|
||||||
|
nsAHttpTransaction *trans = Response(0);
|
||||||
|
if (trans)
|
||||||
|
return trans->SetPipelinePosition(position);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt32
|
||||||
|
nsHttpPipeline::PipelinePosition()
|
||||||
|
{
|
||||||
|
nsAHttpTransaction *trans = Response(0);
|
||||||
|
if (trans)
|
||||||
|
return trans->PipelinePosition();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsHttpPipeline *
|
||||||
|
nsHttpPipeline::QueryPipeline()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsHttpPipeline::nsISupports
|
// nsHttpPipeline::nsISupports
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -164,9 +208,26 @@ nsHttpPipeline::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|||||||
|
|
||||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||||
NS_ASSERTION(mConnection, "no connection");
|
NS_ASSERTION(mConnection, "no connection");
|
||||||
|
|
||||||
|
nsRefPtr<nsHttpConnectionInfo> ci;
|
||||||
|
GetConnectionInfo(getter_AddRefs(ci));
|
||||||
|
|
||||||
|
NS_ABORT_IF_FALSE(ci, "no connection info");
|
||||||
|
|
||||||
|
bool pipeliningBefore = gHttpHandler->ConnMgr()->SupportsPipelining(ci);
|
||||||
|
|
||||||
// trans has now received its response headers; forward to the real connection
|
// trans has now received its response headers; forward to the real connection
|
||||||
return mConnection->OnHeadersAvailable(trans, requestHead, responseHead, reset);
|
nsresult rv = mConnection->OnHeadersAvailable(trans,
|
||||||
|
requestHead,
|
||||||
|
responseHead,
|
||||||
|
reset);
|
||||||
|
|
||||||
|
if (!pipeliningBefore && gHttpHandler->ConnMgr()->SupportsPipelining(ci))
|
||||||
|
// The received headers have expanded the eligible
|
||||||
|
// pipeline depth for this connection
|
||||||
|
gHttpHandler->ConnMgr()->ProcessPendingQForEntry(ci);
|
||||||
|
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -191,7 +252,7 @@ nsHttpPipeline::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
|
|||||||
LOG(("nsHttpPipeline::CloseTransaction [this=%x trans=%x reason=%x]\n",
|
LOG(("nsHttpPipeline::CloseTransaction [this=%x trans=%x reason=%x]\n",
|
||||||
this, trans, reason));
|
this, trans, reason));
|
||||||
|
|
||||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||||
NS_ASSERTION(NS_FAILED(reason), "expecting failure code");
|
NS_ASSERTION(NS_FAILED(reason), "expecting failure code");
|
||||||
|
|
||||||
// the specified transaction is to be closed with the given "reason"
|
// the specified transaction is to be closed with the given "reason"
|
||||||
@ -219,21 +280,27 @@ nsHttpPipeline::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
|
|||||||
killPipeline = true;
|
killPipeline = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marking this connection as non-reusable prevents other items from being
|
||||||
|
// added to it and causes it to be torn down soon. Don't tear it down yet
|
||||||
|
// as that would prevent Response(0) from being processed.
|
||||||
|
DontReuse();
|
||||||
|
|
||||||
trans->Close(reason);
|
trans->Close(reason);
|
||||||
NS_RELEASE(trans);
|
NS_RELEASE(trans);
|
||||||
|
|
||||||
if (killPipeline) {
|
if (killPipeline)
|
||||||
if (mConnection)
|
// reschedule anything from this pipeline onto a different connection
|
||||||
mConnection->CloseTransaction(this, reason);
|
CancelPipeline(reason);
|
||||||
else
|
|
||||||
Close(reason);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpPipeline::GetConnectionInfo(nsHttpConnectionInfo **result)
|
nsHttpPipeline::GetConnectionInfo(nsHttpConnectionInfo **result)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mConnection, "no connection");
|
if (!mConnection) {
|
||||||
|
*result = nsnull;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mConnection->GetConnectionInfo(result);
|
mConnection->GetConnectionInfo(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +328,16 @@ nsHttpPipeline::IsPersistent()
|
|||||||
bool
|
bool
|
||||||
nsHttpPipeline::IsReused()
|
nsHttpPipeline::IsReused()
|
||||||
{
|
{
|
||||||
return true; // pipelining requires this
|
if (!mUtilizedPipeline && mConnection)
|
||||||
|
return mConnection->IsReused();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsHttpPipeline::DontReuse()
|
||||||
|
{
|
||||||
|
if (mConnection)
|
||||||
|
mConnection->DontReuse();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -269,7 +345,7 @@ nsHttpPipeline::PushBack(const char *data, PRUint32 length)
|
|||||||
{
|
{
|
||||||
LOG(("nsHttpPipeline::PushBack [this=%x len=%u]\n", this, length));
|
LOG(("nsHttpPipeline::PushBack [this=%x len=%u]\n", this, length));
|
||||||
|
|
||||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||||
NS_ASSERTION(mPushBackLen == 0, "push back buffer already has data!");
|
NS_ASSERTION(mPushBackLen == 0, "push back buffer already has data!");
|
||||||
|
|
||||||
// If we have no chance for a pipeline (e.g. due to an Upgrade)
|
// If we have no chance for a pipeline (e.g. due to an Upgrade)
|
||||||
@ -309,6 +385,13 @@ nsHttpPipeline::PushBack(const char *data, PRUint32 length)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsHttpPipeline::IsProxyConnectInProgress()
|
||||||
|
{
|
||||||
|
NS_ABORT_IF_FALSE(mConnection, "no connection");
|
||||||
|
return mConnection->IsProxyConnectInProgress();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsHttpPipeline::LastTransactionExpectedNoContent()
|
nsHttpPipeline::LastTransactionExpectedNoContent()
|
||||||
{
|
{
|
||||||
@ -339,6 +422,24 @@ nsHttpPipeline::Transport()
|
|||||||
return mConnection->Transport();
|
return mConnection->Transport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsAHttpTransaction::Classifier
|
||||||
|
nsHttpPipeline::Classification()
|
||||||
|
{
|
||||||
|
if (mConnection)
|
||||||
|
return mConnection->Classification();
|
||||||
|
|
||||||
|
LOG(("nsHttpPipeline::Classification this=%p "
|
||||||
|
"has null mConnection using CLASS_SOLO default", this));
|
||||||
|
return nsAHttpTransaction::CLASS_SOLO;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsHttpPipeline::Classify(nsAHttpTransaction::Classifier newclass)
|
||||||
|
{
|
||||||
|
if (mConnection)
|
||||||
|
mConnection->Classify(newclass);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpPipeline::SetSSLConnectFailed()
|
nsHttpPipeline::SetSSLConnectFailed()
|
||||||
{
|
{
|
||||||
@ -373,12 +474,6 @@ nsHttpPipeline::TakeSubTransactions(
|
|||||||
if (mResponseQ.Length() || mRequestIsPartial)
|
if (mResponseQ.Length() || mRequestIsPartial)
|
||||||
return NS_ERROR_ALREADY_OPENED;
|
return NS_ERROR_ALREADY_OPENED;
|
||||||
|
|
||||||
// request queue could be empty if it was already canceled, in which
|
|
||||||
// case it is safe to treat this as a case without any
|
|
||||||
// sub-transactions.
|
|
||||||
if (!mRequestQ.Length())
|
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
|
|
||||||
PRInt32 i, count = mRequestQ.Length();
|
PRInt32 i, count = mRequestQ.Length();
|
||||||
for (i = 0; i < count; ++i) {
|
for (i = 0; i < count; ++i) {
|
||||||
nsAHttpTransaction *trans = Request(i);
|
nsAHttpTransaction *trans = Request(i);
|
||||||
@ -392,7 +487,7 @@ nsHttpPipeline::TakeSubTransactions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsHttpPipeline::nsAHttpConnection
|
// nsHttpPipeline::nsAHttpTransaction
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -404,10 +499,6 @@ nsHttpPipeline::SetConnection(nsAHttpConnection *conn)
|
|||||||
NS_ASSERTION(!mConnection, "already have a connection");
|
NS_ASSERTION(!mConnection, "already have a connection");
|
||||||
|
|
||||||
NS_IF_ADDREF(mConnection = conn);
|
NS_IF_ADDREF(mConnection = conn);
|
||||||
|
|
||||||
PRInt32 i, count = mRequestQ.Length();
|
|
||||||
for (i=0; i<count; ++i)
|
|
||||||
Request(i)->SetConnection(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAHttpConnection *
|
nsAHttpConnection *
|
||||||
@ -425,8 +516,13 @@ nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result,
|
|||||||
{
|
{
|
||||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||||
|
|
||||||
// return security callbacks from first request
|
// depending on timing this could be either the request or the response
|
||||||
|
// that is needed - but they both go to the same host. A request for these
|
||||||
|
// callbacks directly in nsHttpTransaction would not make a distinction
|
||||||
|
// over whether the the request had been transmitted yet.
|
||||||
nsAHttpTransaction *trans = Request(0);
|
nsAHttpTransaction *trans = Request(0);
|
||||||
|
if (!trans)
|
||||||
|
trans = Response(0);
|
||||||
if (trans)
|
if (trans)
|
||||||
trans->GetSecurityCallbacks(result, target);
|
trans->GetSecurityCallbacks(result, target);
|
||||||
else {
|
else {
|
||||||
@ -546,6 +642,16 @@ nsHttpPipeline::Status()
|
|||||||
return mStatus;
|
return mStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRUint8
|
||||||
|
nsHttpPipeline::Caps()
|
||||||
|
{
|
||||||
|
nsAHttpTransaction *trans = Request(0);
|
||||||
|
if (!trans)
|
||||||
|
trans = Response(0);
|
||||||
|
|
||||||
|
return trans ? trans->Caps() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
PRUint32
|
PRUint32
|
||||||
nsHttpPipeline::Available()
|
nsHttpPipeline::Available()
|
||||||
{
|
{
|
||||||
@ -632,6 +738,17 @@ nsHttpPipeline::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
trans = Response(0);
|
trans = Response(0);
|
||||||
|
// This code deals with the establishment of a CONNECT tunnel through
|
||||||
|
// an HTTP proxy. It allows the connection to do the CONNECT/200
|
||||||
|
// HTTP transaction to establish an SSL tunnel as a precursor to the
|
||||||
|
// actual pipeline of regular HTTP transactions.
|
||||||
|
if (!trans && mRequestQ.Length() &&
|
||||||
|
mConnection->IsProxyConnectInProgress()) {
|
||||||
|
LOG(("nsHttpPipeline::WriteSegments [this=%p] Forced Delegation\n",
|
||||||
|
this));
|
||||||
|
trans = Request(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (!trans) {
|
if (!trans) {
|
||||||
if (mRequestQ.Length() > 0)
|
if (mRequestQ.Length() > 0)
|
||||||
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
||||||
@ -654,7 +771,10 @@ nsHttpPipeline::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||||||
|
|
||||||
// ask the connection manager to add additional transactions
|
// ask the connection manager to add additional transactions
|
||||||
// to our pipeline.
|
// to our pipeline.
|
||||||
gHttpHandler->ConnMgr()->AddTransactionToPipeline(this);
|
nsRefPtr<nsHttpConnectionInfo> ci;
|
||||||
|
GetConnectionInfo(getter_AddRefs(ci));
|
||||||
|
if (ci)
|
||||||
|
gHttpHandler->ConnMgr()->ProcessPendingQForEntry(ci);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mResponseIsPartial = true;
|
mResponseIsPartial = true;
|
||||||
@ -683,6 +803,52 @@ nsHttpPipeline::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRUint32
|
||||||
|
nsHttpPipeline::CancelPipeline(nsresult originalReason)
|
||||||
|
{
|
||||||
|
PRUint32 i, reqLen, respLen, total;
|
||||||
|
nsAHttpTransaction *trans;
|
||||||
|
|
||||||
|
reqLen = mRequestQ.Length();
|
||||||
|
respLen = mResponseQ.Length();
|
||||||
|
total = reqLen + respLen;
|
||||||
|
|
||||||
|
// don't count the first response, if presnet
|
||||||
|
if (respLen)
|
||||||
|
total--;
|
||||||
|
|
||||||
|
if (!total)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// any pending requests can ignore this error and be restarted
|
||||||
|
// unless it is during a CONNECT tunnel request
|
||||||
|
for (i = 0; i < reqLen; ++i) {
|
||||||
|
trans = Request(i);
|
||||||
|
if (mConnection && mConnection->IsProxyConnectInProgress())
|
||||||
|
trans->Close(originalReason);
|
||||||
|
else
|
||||||
|
trans->Close(NS_ERROR_NET_RESET);
|
||||||
|
NS_RELEASE(trans);
|
||||||
|
}
|
||||||
|
mRequestQ.Clear();
|
||||||
|
|
||||||
|
// any pending responses can be restarted except for the first one,
|
||||||
|
// that we might want to finish on this pipeline or cancel individually
|
||||||
|
for (i = 1; i < respLen; ++i) {
|
||||||
|
trans = Response(i);
|
||||||
|
trans->Close(NS_ERROR_NET_RESET);
|
||||||
|
NS_RELEASE(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (respLen > 1)
|
||||||
|
mResponseQ.TruncateLength(1);
|
||||||
|
|
||||||
|
DontReuse();
|
||||||
|
Classify(nsAHttpTransaction::CLASS_SOLO);
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHttpPipeline::Close(nsresult reason)
|
nsHttpPipeline::Close(nsresult reason)
|
||||||
{
|
{
|
||||||
@ -697,38 +863,37 @@ nsHttpPipeline::Close(nsresult reason)
|
|||||||
mStatus = reason;
|
mStatus = reason;
|
||||||
mClosed = true;
|
mClosed = true;
|
||||||
|
|
||||||
PRUint32 i, count;
|
nsRefPtr<nsHttpConnectionInfo> ci;
|
||||||
nsAHttpTransaction *trans;
|
GetConnectionInfo(getter_AddRefs(ci));
|
||||||
|
PRUint32 numRescheduled = CancelPipeline(reason);
|
||||||
|
|
||||||
// any pending requests can ignore this error and be restarted
|
// numRescheduled can be 0 if there is just a single response in the
|
||||||
count = mRequestQ.Length();
|
// pipeline object. That isn't really a meaningful pipeline that
|
||||||
for (i=0; i<count; ++i) {
|
// has been forced to be rescheduled so it does not need to generate
|
||||||
trans = Request(i);
|
// negative feedback.
|
||||||
|
if (ci && numRescheduled)
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
ci, nsHttpConnectionMgr::RedCanceledPipeline, nsnull, 0);
|
||||||
|
|
||||||
|
nsAHttpTransaction *trans = Response(0);
|
||||||
|
if (!trans)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The current transaction can be restarted via reset
|
||||||
|
// if the response has not started to arrive and the reason
|
||||||
|
// for failure is innocuous (e.g. not an SSL error)
|
||||||
|
if (!mResponseIsPartial &&
|
||||||
|
(reason == NS_ERROR_NET_RESET ||
|
||||||
|
reason == NS_OK ||
|
||||||
|
reason == NS_BASE_STREAM_CLOSED)) {
|
||||||
trans->Close(NS_ERROR_NET_RESET);
|
trans->Close(NS_ERROR_NET_RESET);
|
||||||
NS_RELEASE(trans);
|
|
||||||
}
|
}
|
||||||
mRequestQ.Clear();
|
else {
|
||||||
|
trans->Close(reason);
|
||||||
trans = Response(0);
|
|
||||||
if (trans) {
|
|
||||||
// if the current response is partially complete, then it cannot be
|
|
||||||
// restarted and will have to fail with the status of the connection.
|
|
||||||
if (mResponseIsPartial)
|
|
||||||
trans->Close(reason);
|
|
||||||
else
|
|
||||||
trans->Close(NS_ERROR_NET_RESET);
|
|
||||||
NS_RELEASE(trans);
|
|
||||||
|
|
||||||
// any remaining pending responses can be restarted
|
|
||||||
count = mResponseQ.Length();
|
|
||||||
for (i=1; i<count; ++i) {
|
|
||||||
trans = Response(i);
|
|
||||||
trans->Close(NS_ERROR_NET_RESET);
|
|
||||||
NS_RELEASE(trans);
|
|
||||||
}
|
|
||||||
mResponseQ.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_RELEASE(trans);
|
||||||
|
mResponseQ.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -764,6 +929,13 @@ nsHttpPipeline::FillSendBuf()
|
|||||||
while ((trans = Request(0)) != nsnull) {
|
while ((trans = Request(0)) != nsnull) {
|
||||||
avail = trans->Available();
|
avail = trans->Available();
|
||||||
if (avail) {
|
if (avail) {
|
||||||
|
// if there is already a response in the responseq then this
|
||||||
|
// new data comprises a pipeline. Update the transaction in the
|
||||||
|
// response queue to reflect that if necessary. We are now sending
|
||||||
|
// out a request while we haven't received all responses.
|
||||||
|
nsAHttpTransaction *response = Response(0);
|
||||||
|
if (response && !response->PipelinePosition())
|
||||||
|
response->SetPipelinePosition(1);
|
||||||
rv = trans->ReadSegments(this, avail, &n);
|
rv = trans->ReadSegments(this, avail, &n);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
@ -794,6 +966,10 @@ nsHttpPipeline::FillSendBuf()
|
|||||||
NS_NET_STATUS_WAITING_FOR,
|
NS_NET_STATUS_WAITING_FOR,
|
||||||
mSendingToProgress);
|
mSendingToProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It would be good to re-enable data read handlers via ResumeRecv()
|
||||||
|
// except the read handler code can be synchronously dispatched on
|
||||||
|
// the stack.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mRequestIsPartial = true;
|
mRequestIsPartial = true;
|
||||||
|
@ -60,8 +60,6 @@ public:
|
|||||||
nsHttpPipeline();
|
nsHttpPipeline();
|
||||||
virtual ~nsHttpPipeline();
|
virtual ~nsHttpPipeline();
|
||||||
|
|
||||||
nsresult AddTransaction(nsAHttpTransaction *);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsresult FillSendBuf();
|
nsresult FillSendBuf();
|
||||||
|
|
||||||
@ -84,6 +82,9 @@ private:
|
|||||||
return mResponseQ[i];
|
return mResponseQ[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overload of nsAHttpTransaction::QueryPipeline()
|
||||||
|
nsHttpPipeline *QueryPipeline();
|
||||||
|
|
||||||
nsAHttpConnection *mConnection;
|
nsAHttpConnection *mConnection;
|
||||||
nsTArray<nsAHttpTransaction*> mRequestQ; // array of transactions
|
nsTArray<nsAHttpTransaction*> mRequestQ; // array of transactions
|
||||||
nsTArray<nsAHttpTransaction*> mResponseQ; // array of transactions
|
nsTArray<nsAHttpTransaction*> mResponseQ; // array of transactions
|
||||||
@ -99,6 +100,10 @@ private:
|
|||||||
// indicates whether or not the pipeline has been explicitly closed.
|
// indicates whether or not the pipeline has been explicitly closed.
|
||||||
bool mClosed;
|
bool mClosed;
|
||||||
|
|
||||||
|
// indicates whether or not a true pipeline (more than 1 request without
|
||||||
|
// a synchronous response) has been formed.
|
||||||
|
bool mUtilizedPipeline;
|
||||||
|
|
||||||
// used when calling ReadSegments/WriteSegments on a transaction.
|
// used when calling ReadSegments/WriteSegments on a transaction.
|
||||||
nsAHttpSegmentReader *mReader;
|
nsAHttpSegmentReader *mReader;
|
||||||
nsAHttpSegmentWriter *mWriter;
|
nsAHttpSegmentWriter *mWriter;
|
||||||
|
@ -64,6 +64,8 @@
|
|||||||
|
|
||||||
#include "mozilla/FunctionTimer.h"
|
#include "mozilla/FunctionTimer.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -119,6 +121,8 @@ nsHttpTransaction::nsHttpTransaction()
|
|||||||
, mPriority(0)
|
, mPriority(0)
|
||||||
, mRestartCount(0)
|
, mRestartCount(0)
|
||||||
, mCaps(0)
|
, mCaps(0)
|
||||||
|
, mClassification(CLASS_GENERAL)
|
||||||
|
, mPipelinePosition(0)
|
||||||
, mClosed(false)
|
, mClosed(false)
|
||||||
, mConnected(false)
|
, mConnected(false)
|
||||||
, mHaveStatusLine(false)
|
, mHaveStatusLine(false)
|
||||||
@ -134,8 +138,14 @@ nsHttpTransaction::nsHttpTransaction()
|
|||||||
, mSSLConnectFailed(false)
|
, mSSLConnectFailed(false)
|
||||||
, mHttpResponseMatched(false)
|
, mHttpResponseMatched(false)
|
||||||
, mPreserveStream(false)
|
, mPreserveStream(false)
|
||||||
|
, mToReadBeforeRestart(0)
|
||||||
|
, mReportedStart(false)
|
||||||
|
, mReportedResponseHeader(false)
|
||||||
|
, mForTakeResponseHead(nsnull)
|
||||||
|
, mTakenResponseHeader(false)
|
||||||
{
|
{
|
||||||
LOG(("Creating nsHttpTransaction @%x\n", this));
|
LOG(("Creating nsHttpTransaction @%x\n", this));
|
||||||
|
gHttpHandler->GetMaxPipelineObjectSize(mMaxPipelineObjectSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsHttpTransaction::~nsHttpTransaction()
|
nsHttpTransaction::~nsHttpTransaction()
|
||||||
@ -146,9 +156,44 @@ nsHttpTransaction::~nsHttpTransaction()
|
|||||||
NS_IF_RELEASE(mConnInfo);
|
NS_IF_RELEASE(mConnInfo);
|
||||||
|
|
||||||
delete mResponseHead;
|
delete mResponseHead;
|
||||||
|
delete mForTakeResponseHead;
|
||||||
delete mChunkedDecoder;
|
delete mChunkedDecoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsHttpTransaction::Classifier
|
||||||
|
nsHttpTransaction::Classify()
|
||||||
|
{
|
||||||
|
if (!(mCaps & NS_HTTP_ALLOW_PIPELINING))
|
||||||
|
return (mClassification = CLASS_SOLO);
|
||||||
|
|
||||||
|
if (mRequestHead->PeekHeader(nsHttp::If_Modified_Since) ||
|
||||||
|
mRequestHead->PeekHeader(nsHttp::If_None_Match))
|
||||||
|
return (mClassification = CLASS_REVALIDATION);
|
||||||
|
|
||||||
|
const char *accept = mRequestHead->PeekHeader(nsHttp::Accept);
|
||||||
|
if (accept && !PL_strncmp(accept, "image/", 6))
|
||||||
|
return (mClassification = CLASS_IMAGE);
|
||||||
|
|
||||||
|
if (accept && !PL_strncmp(accept, "text/css", 8))
|
||||||
|
return (mClassification = CLASS_SCRIPT);
|
||||||
|
|
||||||
|
mClassification = CLASS_GENERAL;
|
||||||
|
|
||||||
|
PRInt32 queryPos = mRequestHead->RequestURI().FindChar('?');
|
||||||
|
if (queryPos == kNotFound) {
|
||||||
|
if (StringEndsWith(mRequestHead->RequestURI(),
|
||||||
|
NS_LITERAL_CSTRING(".js")))
|
||||||
|
mClassification = CLASS_SCRIPT;
|
||||||
|
}
|
||||||
|
else if (queryPos >= 3 &&
|
||||||
|
Substring(mRequestHead->RequestURI(), queryPos - 3, 3).
|
||||||
|
EqualsLiteral(".js")) {
|
||||||
|
mClassification = CLASS_SCRIPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mClassification;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpTransaction::Init(PRUint8 caps,
|
nsHttpTransaction::Init(PRUint8 caps,
|
||||||
nsHttpConnectionInfo *cinfo,
|
nsHttpConnectionInfo *cinfo,
|
||||||
@ -300,6 +345,8 @@ nsHttpTransaction::Init(PRUint8 caps,
|
|||||||
nsIOService::gDefaultSegmentCount);
|
nsIOService::gDefaultSegmentCount);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
|
Classify();
|
||||||
|
|
||||||
NS_ADDREF(*responseBody = mPipeIn);
|
NS_ADDREF(*responseBody = mPipeIn);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -313,13 +360,30 @@ nsHttpTransaction::Connection()
|
|||||||
nsHttpResponseHead *
|
nsHttpResponseHead *
|
||||||
nsHttpTransaction::TakeResponseHead()
|
nsHttpTransaction::TakeResponseHead()
|
||||||
{
|
{
|
||||||
|
NS_ABORT_IF_FALSE(!mTakenResponseHeader, "TakeResponseHead called 2x");
|
||||||
|
|
||||||
|
// Lock RestartInProgress() and TakeResponseHead() against main thread
|
||||||
|
MutexAutoLock lock(*nsHttp::GetLock());
|
||||||
|
|
||||||
|
mTakenResponseHeader = true;
|
||||||
|
|
||||||
|
// Even in OnStartRequest() the headers won't be available if we were
|
||||||
|
// canceled
|
||||||
if (!mHaveAllHeaders) {
|
if (!mHaveAllHeaders) {
|
||||||
NS_WARNING("response headers not available or incomplete");
|
NS_WARNING("response headers not available or incomplete");
|
||||||
return nsnull;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsHttpResponseHead *head = mResponseHead;
|
// Prefer mForTakeResponseHead over mResponseHead
|
||||||
mResponseHead = nsnull;
|
nsHttpResponseHead *head;
|
||||||
|
if (mForTakeResponseHead) {
|
||||||
|
head = mForTakeResponseHead;
|
||||||
|
mForTakeResponseHead = nsnull;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
head = mResponseHead;
|
||||||
|
mResponseHead = nsnull;
|
||||||
|
}
|
||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,13 +469,14 @@ nsHttpTransaction::OnTransportStatus(nsITransport* transport,
|
|||||||
PR_Now(), LL_ZERO, EmptyCString());
|
PR_Now(), LL_ZERO, EmptyCString());
|
||||||
|
|
||||||
// report the status and progress
|
// report the status and progress
|
||||||
mActivityDistributor->ObserveActivity(
|
if (!mRestartInProgressVerifier.Active())
|
||||||
mChannel,
|
mActivityDistributor->ObserveActivity(
|
||||||
NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
|
mChannel,
|
||||||
static_cast<PRUint32>(status),
|
NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
|
||||||
PR_Now(),
|
static_cast<PRUint32>(status),
|
||||||
progress,
|
PR_Now(),
|
||||||
EmptyCString());
|
progress,
|
||||||
|
EmptyCString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// nsHttpChannel synthesizes progress events in OnDataAvailable
|
// nsHttpChannel synthesizes progress events in OnDataAvailable
|
||||||
@ -456,6 +521,12 @@ nsHttpTransaction::Status()
|
|||||||
return mStatus;
|
return mStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRUint8
|
||||||
|
nsHttpTransaction::Caps()
|
||||||
|
{
|
||||||
|
return mCaps;
|
||||||
|
}
|
||||||
|
|
||||||
PRUint32
|
PRUint32
|
||||||
nsHttpTransaction::Available()
|
nsHttpTransaction::Available()
|
||||||
{
|
{
|
||||||
@ -654,16 +725,47 @@ nsHttpTransaction::Close(nsresult reason)
|
|||||||
// mReceivedData == FALSE. (see bug 203057 for more info.)
|
// mReceivedData == FALSE. (see bug 203057 for more info.)
|
||||||
//
|
//
|
||||||
if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
|
if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
|
||||||
if (!mReceivedData && (!mSentData || connReused)) {
|
if (!mReceivedData && (!mSentData || connReused || mPipelinePosition)) {
|
||||||
// if restarting fails, then we must proceed to close the pipe,
|
// if restarting fails, then we must proceed to close the pipe,
|
||||||
// which will notify the channel that the transaction failed.
|
// which will notify the channel that the transaction failed.
|
||||||
|
|
||||||
|
if (mPipelinePosition) {
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::RedCanceledPipeline,
|
||||||
|
nsnull, 0);
|
||||||
|
}
|
||||||
if (NS_SUCCEEDED(Restart()))
|
if (NS_SUCCEEDED(Restart()))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (!mResponseIsComplete && mPipelinePosition &&
|
||||||
|
reason == NS_ERROR_NET_RESET) {
|
||||||
|
// due to unhandled rst on a pipeline - safe to
|
||||||
|
// restart as only idempotent is found there
|
||||||
|
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::RedCorruptedContent, nsnull, 0);
|
||||||
|
if (NS_SUCCEEDED(RestartInProgress()))
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool relConn = true;
|
bool relConn = true;
|
||||||
if (NS_SUCCEEDED(reason)) {
|
if (NS_SUCCEEDED(reason)) {
|
||||||
|
if (!mResponseIsComplete) {
|
||||||
|
// The response has not been delimited with a high-confidence
|
||||||
|
// algorithm like Content-Length or Chunked Encoding. We
|
||||||
|
// need to use a strong framing mechanism to pipeline.
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
|
||||||
|
nsnull, mClassification);
|
||||||
|
}
|
||||||
|
else if (mPipelinePosition) {
|
||||||
|
// report this success as feedback
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::GoodCompletedOK,
|
||||||
|
nsnull, mPipelinePosition);
|
||||||
|
}
|
||||||
|
|
||||||
// the server has not sent the final \r\n terminating the header
|
// the server has not sent the final \r\n terminating the header
|
||||||
// section, and there may still be a header line unparsed. let's make
|
// section, and there may still be a header line unparsed. let's make
|
||||||
// sure we parse the remaining header line, and then hopefully, the
|
// sure we parse the remaining header line, and then hopefully, the
|
||||||
@ -704,10 +806,86 @@ nsHttpTransaction::Close(nsresult reason)
|
|||||||
mPipeOut->CloseWithStatus(reason);
|
mPipeOut->CloseWithStatus(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsHttpTransaction::AddTransaction(nsAHttpTransaction *trans)
|
||||||
|
{
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRUint32
|
||||||
|
nsHttpTransaction::PipelineDepth()
|
||||||
|
{
|
||||||
|
return IsDone() ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsHttpTransaction::SetPipelinePosition(PRInt32 position)
|
||||||
|
{
|
||||||
|
mPipelinePosition = position;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt32
|
||||||
|
nsHttpTransaction::PipelinePosition()
|
||||||
|
{
|
||||||
|
return mPipelinePosition;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsHttpTransaction <private>
|
// nsHttpTransaction <private>
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsHttpTransaction::RestartInProgress()
|
||||||
|
{
|
||||||
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||||
|
|
||||||
|
// Lock RestartInProgress() and TakeResponseHead() against main thread
|
||||||
|
MutexAutoLock lock(*nsHttp::GetLock());
|
||||||
|
|
||||||
|
// don't try and restart 0.9
|
||||||
|
if (mHaveAllHeaders && !mRestartInProgressVerifier.IsSetup())
|
||||||
|
return NS_ERROR_NET_RESET;
|
||||||
|
|
||||||
|
LOG(("Will restart transaction %p and skip first %lld bytes, "
|
||||||
|
"old Content-Length %lld",
|
||||||
|
this, mContentRead, mContentLength));
|
||||||
|
|
||||||
|
if (mHaveAllHeaders) {
|
||||||
|
mRestartInProgressVerifier.SetAlreadyProcessed(
|
||||||
|
PR_MAX(mRestartInProgressVerifier.AlreadyProcessed(), mContentRead));
|
||||||
|
mToReadBeforeRestart = mRestartInProgressVerifier.AlreadyProcessed();
|
||||||
|
mRestartInProgressVerifier.SetActive(true);
|
||||||
|
|
||||||
|
if (!mTakenResponseHeader && !mForTakeResponseHead) {
|
||||||
|
// TakeResponseHeader() has not been called yet and this
|
||||||
|
// is the first restart. Store the resp headers exclusively
|
||||||
|
// for TakeResponseHeader()
|
||||||
|
mForTakeResponseHead = mResponseHead;
|
||||||
|
mResponseHead = nsnull;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mResponseHead) {
|
||||||
|
mResponseHead->Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
mContentRead = 0;
|
||||||
|
mContentLength = -1;
|
||||||
|
delete mChunkedDecoder;
|
||||||
|
mChunkedDecoder = nsnull;
|
||||||
|
mHaveStatusLine = false;
|
||||||
|
mHaveAllHeaders = false;
|
||||||
|
mHttpResponseMatched = false;
|
||||||
|
mResponseIsComplete = false;
|
||||||
|
mDidContentStart = false;
|
||||||
|
mNoContent = false;
|
||||||
|
mSentData = false;
|
||||||
|
mReceivedData = false;
|
||||||
|
|
||||||
|
return Restart();
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpTransaction::Restart()
|
nsHttpTransaction::Restart()
|
||||||
{
|
{
|
||||||
@ -839,6 +1017,9 @@ nsHttpTransaction::ParseLineSegment(char *segment, PRUint32 len)
|
|||||||
nsresult rv = ParseLine(mLineBuf.BeginWriting());
|
nsresult rv = ParseLine(mLineBuf.BeginWriting());
|
||||||
mLineBuf.Truncate();
|
mLineBuf.Truncate();
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::RedCorruptedContent,
|
||||||
|
nsnull, 0);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -887,12 +1068,14 @@ nsHttpTransaction::ParseHead(char *buf,
|
|||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
// report that we have a least some of the response
|
// report that we have a least some of the response
|
||||||
if (mActivityDistributor)
|
if (mActivityDistributor && !mReportedStart) {
|
||||||
|
mReportedStart = true;
|
||||||
mActivityDistributor->ObserveActivity(
|
mActivityDistributor->ObserveActivity(
|
||||||
mChannel,
|
mChannel,
|
||||||
NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
|
NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
|
||||||
NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
|
NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
|
||||||
PR_Now(), LL_ZERO, EmptyCString());
|
PR_Now(), LL_ZERO, EmptyCString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mHttpResponseMatched) {
|
if (!mHttpResponseMatched) {
|
||||||
@ -1006,7 +1189,8 @@ nsHttpTransaction::HandleContentStart()
|
|||||||
#endif
|
#endif
|
||||||
// notify the connection, give it a chance to cause a reset.
|
// notify the connection, give it a chance to cause a reset.
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);
|
if (!mRestartInProgressVerifier.IsSetup())
|
||||||
|
mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);
|
||||||
|
|
||||||
// looks like we should ignore this response, resetting...
|
// looks like we should ignore this response, resetting...
|
||||||
if (reset) {
|
if (reset) {
|
||||||
@ -1033,6 +1217,10 @@ nsHttpTransaction::HandleContentStart()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mConnection->SetLastTransactionExpectedNoContent(mNoContent);
|
mConnection->SetLastTransactionExpectedNoContent(mNoContent);
|
||||||
|
if (mInvalidResponseBytesRead)
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
|
||||||
|
nsnull, mClassification);
|
||||||
|
|
||||||
if (mNoContent)
|
if (mNoContent)
|
||||||
mContentLength = 0;
|
mContentLength = 0;
|
||||||
@ -1040,6 +1228,10 @@ nsHttpTransaction::HandleContentStart()
|
|||||||
// grab the content-length from the response headers
|
// grab the content-length from the response headers
|
||||||
mContentLength = mResponseHead->ContentLength();
|
mContentLength = mResponseHead->ContentLength();
|
||||||
|
|
||||||
|
if ((mClassification != CLASS_SOLO) &&
|
||||||
|
(mContentLength > mMaxPipelineObjectSize))
|
||||||
|
CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
|
||||||
|
|
||||||
// handle chunked encoding here, so we'll know immediately when
|
// handle chunked encoding here, so we'll know immediately when
|
||||||
// we're done with the socket. please note that _all_ other
|
// we're done with the socket. please note that _all_ other
|
||||||
// decoding is done when the channel receives the content data
|
// decoding is done when the channel receives the content data
|
||||||
@ -1060,9 +1252,15 @@ nsHttpTransaction::HandleContentStart()
|
|||||||
LOG(("waiting for the server to close the connection.\n"));
|
LOG(("waiting for the server to close the connection.\n"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
if (mRestartInProgressVerifier.Active() &&
|
||||||
|
!mRestartInProgressVerifier.Verify(mContentLength, mResponseHead)) {
|
||||||
|
LOG(("Restart in progress subsequent transaction failed to match"));
|
||||||
|
return NS_ERROR_ABORT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mDidContentStart = true;
|
mDidContentStart = true;
|
||||||
|
mRestartInProgressVerifier.Set(mContentLength, mResponseHead);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1121,6 +1319,19 @@ nsHttpTransaction::HandleContent(char *buf,
|
|||||||
// (no explicit content-length given)
|
// (no explicit content-length given)
|
||||||
*contentRead = count;
|
*contentRead = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mRestartInProgressVerifier.Active() &&
|
||||||
|
mToReadBeforeRestart && *contentRead) {
|
||||||
|
PRUint32 ignore = PR_MIN(*contentRead, PRUint32(mToReadBeforeRestart));
|
||||||
|
LOG(("Due To Restart ignoring %d of remaining %ld",
|
||||||
|
ignore, mToReadBeforeRestart));
|
||||||
|
*contentRead -= ignore;
|
||||||
|
mContentRead += ignore;
|
||||||
|
mToReadBeforeRestart -= ignore;
|
||||||
|
memmove(buf, buf + ignore, *contentRead + *contentRemaining);
|
||||||
|
if (!mToReadBeforeRestart)
|
||||||
|
mRestartInProgressVerifier.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (*contentRead) {
|
if (*contentRead) {
|
||||||
// update count of content bytes read and report progress...
|
// update count of content bytes read and report progress...
|
||||||
@ -1134,6 +1345,13 @@ nsHttpTransaction::HandleContent(char *buf,
|
|||||||
LOG(("nsHttpTransaction::HandleContent [this=%x count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
|
LOG(("nsHttpTransaction::HandleContent [this=%x count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
|
||||||
this, count, *contentRead, mContentRead, mContentLength));
|
this, count, *contentRead, mContentRead, mContentLength));
|
||||||
|
|
||||||
|
// Check the size of chunked responses. If we exceed the max pipeline size
|
||||||
|
// for this response reschedule the pipeline
|
||||||
|
if ((mClassification != CLASS_SOLO) &&
|
||||||
|
mChunkedDecoder &&
|
||||||
|
(mContentRead > mMaxPipelineObjectSize))
|
||||||
|
CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
|
||||||
|
|
||||||
// check for end-of-file
|
// check for end-of-file
|
||||||
if ((mContentRead == mContentLength) ||
|
if ((mContentRead == mContentLength) ||
|
||||||
(mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
|
(mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
|
||||||
@ -1189,7 +1407,9 @@ nsHttpTransaction::ProcessData(char *buf, PRUint32 count, PRUint32 *countRead)
|
|||||||
memmove(buf, buf + bytesConsumed, count);
|
memmove(buf, buf + bytesConsumed, count);
|
||||||
|
|
||||||
// report the completed response header
|
// report the completed response header
|
||||||
if (mActivityDistributor && mResponseHead && mHaveAllHeaders) {
|
if (mActivityDistributor && mResponseHead && mHaveAllHeaders &&
|
||||||
|
!mReportedResponseHeader) {
|
||||||
|
mReportedResponseHeader = true;
|
||||||
nsCAutoString completeResponseHeaders;
|
nsCAutoString completeResponseHeaders;
|
||||||
mResponseHead->Flatten(completeResponseHeaders, false);
|
mResponseHead->Flatten(completeResponseHeaders, false);
|
||||||
completeResponseHeaders.AppendLiteral("\r\n");
|
completeResponseHeaders.AppendLiteral("\r\n");
|
||||||
@ -1233,6 +1453,23 @@ nsHttpTransaction::ProcessData(char *buf, PRUint32 count, PRUint32 *countRead)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsHttpTransaction::CancelPipeline(PRUint32 reason)
|
||||||
|
{
|
||||||
|
// reason is casted through a uint to avoid compiler header deps
|
||||||
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
||||||
|
mConnInfo,
|
||||||
|
static_cast<nsHttpConnectionMgr::PipelineFeedbackInfoType>(reason),
|
||||||
|
nsnull, mClassification);
|
||||||
|
|
||||||
|
mConnection->CancelPipeline(NS_ERROR_CORRUPTED_CONTENT);
|
||||||
|
|
||||||
|
// Avoid pipelining this transaction on restart by classifying it as solo.
|
||||||
|
// This also prevents BadUnexpectedLarge from being reported more
|
||||||
|
// than one time per transaction.
|
||||||
|
mClassification = CLASS_SOLO;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsHttpTransaction deletion event
|
// nsHttpTransaction deletion event
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -1328,3 +1565,76 @@ nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
|||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nsHttpTransaction::RestartVerifier
|
||||||
|
|
||||||
|
static bool
|
||||||
|
matchOld(nsHttpResponseHead *newHead, nsCString &old,
|
||||||
|
nsHttpAtom headerAtom)
|
||||||
|
{
|
||||||
|
const char *val;
|
||||||
|
|
||||||
|
val = newHead->PeekHeader(headerAtom);
|
||||||
|
if (val && old.IsEmpty())
|
||||||
|
return false;
|
||||||
|
if (!val && !old.IsEmpty())
|
||||||
|
return false;
|
||||||
|
if (val && !old.Equals(val))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsHttpTransaction::RestartVerifier::Verify(PRInt64 contentLength,
|
||||||
|
nsHttpResponseHead *newHead)
|
||||||
|
{
|
||||||
|
if (mContentLength != contentLength)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!matchOld(newHead, mContentRange, nsHttp::Content_Range))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!matchOld(newHead, mLastModified, nsHttp::Last_Modified))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!matchOld(newHead, mETag, nsHttp::ETag))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!matchOld(newHead, mContentEncoding, nsHttp::Content_Encoding))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!matchOld(newHead, mTransferEncoding, nsHttp::Transfer_Encoding))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsHttpTransaction::RestartVerifier::Set(PRInt64 contentLength,
|
||||||
|
nsHttpResponseHead *head)
|
||||||
|
{
|
||||||
|
if (mSetup)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mContentLength = contentLength;
|
||||||
|
|
||||||
|
if (head) {
|
||||||
|
const char *val;
|
||||||
|
val = head->PeekHeader(nsHttp::ETag);
|
||||||
|
if (val)
|
||||||
|
mETag.Assign(val);
|
||||||
|
val = head->PeekHeader(nsHttp::Last_Modified);
|
||||||
|
if (val)
|
||||||
|
mLastModified.Assign(val);
|
||||||
|
val = head->PeekHeader(nsHttp::Content_Range);
|
||||||
|
if (val)
|
||||||
|
mContentRange.Assign(val);
|
||||||
|
val = head->PeekHeader(nsHttp::Content_Encoding);
|
||||||
|
if (val)
|
||||||
|
mContentEncoding.Assign(val);
|
||||||
|
val = head->PeekHeader(nsHttp::Transfer_Encoding);
|
||||||
|
if (val)
|
||||||
|
mTransferEncoding.Assign(val);
|
||||||
|
mSetup = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -113,7 +113,6 @@ public:
|
|||||||
nsIAsyncInputStream **responseBody);
|
nsIAsyncInputStream **responseBody);
|
||||||
|
|
||||||
// attributes
|
// attributes
|
||||||
PRUint8 Caps() { return mCaps; }
|
|
||||||
nsHttpConnectionInfo *ConnectionInfo() { return mConnInfo; }
|
nsHttpConnectionInfo *ConnectionInfo() { return mConnInfo; }
|
||||||
nsHttpResponseHead *ResponseHead() { return mHaveAllHeaders ? mResponseHead : nsnull; }
|
nsHttpResponseHead *ResponseHead() { return mHaveAllHeaders ? mResponseHead : nsnull; }
|
||||||
nsISupports *SecurityInfo() { return mSecurityInfo; }
|
nsISupports *SecurityInfo() { return mSecurityInfo; }
|
||||||
@ -135,9 +134,11 @@ public:
|
|||||||
PRInt32 Priority() { return mPriority; }
|
PRInt32 Priority() { return mPriority; }
|
||||||
|
|
||||||
const TimingStruct& Timings() const { return mTimings; }
|
const TimingStruct& Timings() const { return mTimings; }
|
||||||
|
enum Classifier Classification() { return mClassification; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsresult Restart();
|
nsresult Restart();
|
||||||
|
nsresult RestartInProgress();
|
||||||
char *LocateHttpStart(char *buf, PRUint32 len,
|
char *LocateHttpStart(char *buf, PRUint32 len,
|
||||||
bool aAllowPartialMatch);
|
bool aAllowPartialMatch);
|
||||||
nsresult ParseLine(char *line);
|
nsresult ParseLine(char *line);
|
||||||
@ -148,6 +149,9 @@ private:
|
|||||||
nsresult ProcessData(char *, PRUint32, PRUint32 *);
|
nsresult ProcessData(char *, PRUint32, PRUint32 *);
|
||||||
void DeleteSelfOnConsumerThread();
|
void DeleteSelfOnConsumerThread();
|
||||||
|
|
||||||
|
Classifier Classify();
|
||||||
|
void CancelPipeline(PRUint32 reason);
|
||||||
|
|
||||||
static NS_METHOD ReadRequestSegment(nsIInputStream *, void *, const char *,
|
static NS_METHOD ReadRequestSegment(nsIInputStream *, void *, const char *,
|
||||||
PRUint32, PRUint32, PRUint32 *);
|
PRUint32, PRUint32, PRUint32 *);
|
||||||
static NS_METHOD WritePipeSegment(nsIOutputStream *, void *, char *,
|
static NS_METHOD WritePipeSegment(nsIOutputStream *, void *, char *,
|
||||||
@ -200,6 +204,9 @@ private:
|
|||||||
|
|
||||||
PRUint16 mRestartCount; // the number of times this transaction has been restarted
|
PRUint16 mRestartCount; // the number of times this transaction has been restarted
|
||||||
PRUint8 mCaps;
|
PRUint8 mCaps;
|
||||||
|
enum Classifier mClassification;
|
||||||
|
PRInt32 mPipelinePosition;
|
||||||
|
PRInt64 mMaxPipelineObjectSize;
|
||||||
|
|
||||||
// state flags, all logically boolean, but not packed together into a
|
// state flags, all logically boolean, but not packed together into a
|
||||||
// bitfield so as to avoid bitfield-induced races. See bug 560579.
|
// bitfield so as to avoid bitfield-induced races. See bug 560579.
|
||||||
@ -222,6 +229,66 @@ private:
|
|||||||
// mClosed := transaction has been explicitly closed
|
// mClosed := transaction has been explicitly closed
|
||||||
// mTransactionDone := transaction ran to completion or was interrupted
|
// mTransactionDone := transaction ran to completion or was interrupted
|
||||||
// mResponseComplete := transaction ran to completion
|
// mResponseComplete := transaction ran to completion
|
||||||
|
|
||||||
|
// For Restart-In-Progress Functionality
|
||||||
|
PRInt64 mToReadBeforeRestart;
|
||||||
|
bool mReportedStart;
|
||||||
|
bool mReportedResponseHeader;
|
||||||
|
|
||||||
|
// protected by nsHttp::GetLock()
|
||||||
|
nsHttpResponseHead *mForTakeResponseHead;
|
||||||
|
bool mTakenResponseHeader;
|
||||||
|
|
||||||
|
class RestartVerifier
|
||||||
|
{
|
||||||
|
|
||||||
|
// When a idemptotent transaction has received part of its response body
|
||||||
|
// and incurs an error it can be restarted. To do this we mark the place
|
||||||
|
// where we stopped feeding the body to the consumer and start the
|
||||||
|
// network call over again. If everything we track (headers, length, etc..)
|
||||||
|
// matches up to the place where we left off then the consumer starts being
|
||||||
|
// fed data again with the new information. This can be done N times up
|
||||||
|
// to the normal restart (i.e. with no response info) limit.
|
||||||
|
|
||||||
|
public:
|
||||||
|
RestartVerifier()
|
||||||
|
: mContentLength(-1)
|
||||||
|
, mAlreadyProcessed(0)
|
||||||
|
, mActive(false)
|
||||||
|
, mSetup(false)
|
||||||
|
{}
|
||||||
|
~RestartVerifier() {}
|
||||||
|
|
||||||
|
void Set(PRInt64 contentLength, nsHttpResponseHead *head);
|
||||||
|
bool Verify(PRInt64 contentLength, nsHttpResponseHead *head);
|
||||||
|
bool Active() { return mActive; }
|
||||||
|
void SetActive(bool val) { mActive = val; }
|
||||||
|
bool IsSetup() { return mSetup; }
|
||||||
|
PRInt64 AlreadyProcessed() { return mAlreadyProcessed; }
|
||||||
|
void SetAlreadyProcessed(PRInt64 val) { mAlreadyProcessed = val; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is the data from the first complete response header
|
||||||
|
// used to make sure that all subsequent response headers match
|
||||||
|
|
||||||
|
PRInt64 mContentLength;
|
||||||
|
nsCString mETag;
|
||||||
|
nsCString mLastModified;
|
||||||
|
nsCString mContentRange;
|
||||||
|
nsCString mContentEncoding;
|
||||||
|
nsCString mTransferEncoding;
|
||||||
|
|
||||||
|
// This is the amount of data that has been passed to the channel
|
||||||
|
// from previous iterations of the transaction and must therefore
|
||||||
|
// be skipped in the new one.
|
||||||
|
PRInt64 mAlreadyProcessed;
|
||||||
|
|
||||||
|
// true when iteration > 0 has started
|
||||||
|
bool mActive;
|
||||||
|
|
||||||
|
// true when ::Set has been called with a response header
|
||||||
|
bool mSetup;
|
||||||
|
} mRestartInProgressVerifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // nsHttpTransaction_h__
|
#endif // nsHttpTransaction_h__
|
||||||
|
@ -612,6 +612,12 @@ nsWyciwygChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor * aCacheEntry, n
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsWyciwygChannel::OnCacheEntryDoomed(nsresult status)
|
||||||
|
{
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsWyciwygChannel::nsIStreamListener
|
// nsWyciwygChannel::nsIStreamListener
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -709,7 +715,7 @@ nsWyciwygChannel::OpenCacheEntry(const nsACString & aCacheKey,
|
|||||||
rv = cacheSession->OpenCacheEntry(aCacheKey, aAccessMode, false,
|
rv = cacheSession->OpenCacheEntry(aCacheKey, aAccessMode, false,
|
||||||
getter_AddRefs(mCacheEntry));
|
getter_AddRefs(mCacheEntry));
|
||||||
else
|
else
|
||||||
rv = cacheSession->AsyncOpenCacheEntry(aCacheKey, aAccessMode, this);
|
rv = cacheSession->AsyncOpenCacheEntry(aCacheKey, aAccessMode, this, false);
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ const CL_EXPECT_GZIP = 0x2;
|
|||||||
const CL_EXPECT_3S_DELAY = 0x4;
|
const CL_EXPECT_3S_DELAY = 0x4;
|
||||||
const CL_SUSPEND = 0x8;
|
const CL_SUSPEND = 0x8;
|
||||||
const CL_ALLOW_UNKNOWN_CL = 0x10;
|
const CL_ALLOW_UNKNOWN_CL = 0x10;
|
||||||
|
const CL_EXPECT_LATE_FAILURE = 0x20;
|
||||||
|
|
||||||
const SUSPEND_DELAY = 3000;
|
const SUSPEND_DELAY = 3000;
|
||||||
|
|
||||||
@ -38,7 +39,8 @@ const SUSPEND_DELAY = 3000;
|
|||||||
*
|
*
|
||||||
* This listener makes sure that various parts of the channel API are
|
* This listener makes sure that various parts of the channel API are
|
||||||
* implemented correctly and that the channel's status is a success code
|
* implemented correctly and that the channel's status is a success code
|
||||||
* (you can pass CL_EXPECT_FAILURE as flags to allow a failure code)
|
* (you can pass CL_EXPECT_FAILURE or CL_EXPECT_LATE_FAILURE as flags
|
||||||
|
* to allow a failure code)
|
||||||
*
|
*
|
||||||
* Note that it also requires a valid content length on the channel and
|
* Note that it also requires a valid content length on the channel and
|
||||||
* is thus not fully generic.
|
* is thus not fully generic.
|
||||||
@ -131,15 +133,15 @@ ChannelListener.prototype = {
|
|||||||
if (this._got_onstoprequest)
|
if (this._got_onstoprequest)
|
||||||
do_throw("Got second onStopRequest event!");
|
do_throw("Got second onStopRequest event!");
|
||||||
this._got_onstoprequest = true;
|
this._got_onstoprequest = true;
|
||||||
if ((this._flags & CL_EXPECT_FAILURE) && success)
|
if ((this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && success)
|
||||||
do_throw("Should have failed to load URL (status is " + status.toString(16) + ")");
|
do_throw("Should have failed to load URL (status is " + status.toString(16) + ")");
|
||||||
else if (!(this._flags & CL_EXPECT_FAILURE) && !success)
|
else if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && !success)
|
||||||
do_throw("Failed to load URL: " + status.toString(16));
|
do_throw("Failed to load URL: " + status.toString(16));
|
||||||
if (status != request.status)
|
if (status != request.status)
|
||||||
do_throw("request.status does not match status arg to onStopRequest!");
|
do_throw("request.status does not match status arg to onStopRequest!");
|
||||||
if (request.isPending())
|
if (request.isPending())
|
||||||
do_throw("request reports itself as pending from onStopRequest!");
|
do_throw("request reports itself as pending from onStopRequest!");
|
||||||
if (!(this._flags & CL_EXPECT_FAILURE) &&
|
if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) &&
|
||||||
!(this._flags & CL_EXPECT_GZIP) &&
|
!(this._flags & CL_EXPECT_GZIP) &&
|
||||||
this._contentLen != -1)
|
this._contentLen != -1)
|
||||||
do_check_eq(this._buffer.length, this._contentLen)
|
do_check_eq(this._buffer.length, this._contentLen)
|
||||||
|
90
netwerk/test/unit/test_assoc.js
Normal file
90
netwerk/test/unit/test_assoc.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
do_load_httpd_js();
|
||||||
|
|
||||||
|
var httpserver = new nsHttpServer();
|
||||||
|
var currentTestIndex = 0;
|
||||||
|
var tests = [
|
||||||
|
// this is valid
|
||||||
|
{url: "/assoc/assoctest?valid",
|
||||||
|
responseheader: [ "Assoc-Req: GET http://localhost:4444/assoc/assoctest?valid",
|
||||||
|
"Pragma: X-Verify-Assoc-Req" ],
|
||||||
|
flags : 0},
|
||||||
|
|
||||||
|
// this is invalid because the method is wrong
|
||||||
|
{url: "/assoc/assoctest?invalid",
|
||||||
|
responseheader: [ "Assoc-Req: POST http://localhost:4444/assoc/assoctest?invalid",
|
||||||
|
"Pragma: X-Verify-Assoc-Req" ],
|
||||||
|
flags : CL_EXPECT_LATE_FAILURE},
|
||||||
|
|
||||||
|
// this is invalid because the url is wrong
|
||||||
|
{url: "/assoc/assoctest?notvalid",
|
||||||
|
responseheader: [ "Assoc-Req: GET http://localhost:4444/wrongpath/assoc/assoctest?notvalid",
|
||||||
|
"Pragma: X-Verify-Assoc-Req" ],
|
||||||
|
flags : CL_EXPECT_LATE_FAILURE},
|
||||||
|
|
||||||
|
// this is invalid because the space between method and URL is missing
|
||||||
|
{url: "/assoc/assoctest?invalid2",
|
||||||
|
responseheader: [ "Assoc-Req: GEThttp://localhost:4444/assoc/assoctest?invalid2",
|
||||||
|
"Pragma: X-Verify-Assoc-Req" ],
|
||||||
|
flags : CL_EXPECT_LATE_FAILURE},
|
||||||
|
];
|
||||||
|
|
||||||
|
var oldPrefVal;
|
||||||
|
var domBranch;
|
||||||
|
|
||||||
|
function setupChannel(url)
|
||||||
|
{
|
||||||
|
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||||
|
getService(Ci.nsIIOService);
|
||||||
|
var chan = ios.newChannel("http://localhost:4444" + url, "", null);
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startIter()
|
||||||
|
{
|
||||||
|
var channel = setupChannel(tests[currentTestIndex].url);
|
||||||
|
channel.asyncOpen(new ChannelListener(completeIter,
|
||||||
|
channel, tests[currentTestIndex].flags), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeIter(request, data, ctx)
|
||||||
|
{
|
||||||
|
if (++currentTestIndex < tests.length ) {
|
||||||
|
startIter();
|
||||||
|
} else {
|
||||||
|
domBranch.setBoolPref("enforce", oldPrefVal);
|
||||||
|
httpserver.stop(do_test_finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_test()
|
||||||
|
{
|
||||||
|
var prefService =
|
||||||
|
Components.classes["@mozilla.org/preferences-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIPrefService);
|
||||||
|
domBranch = prefService.getBranch("network.http.assoc-req.");
|
||||||
|
oldPrefVal = domBranch.getBoolPref("enforce");
|
||||||
|
domBranch.setBoolPref("enforce", true);
|
||||||
|
|
||||||
|
httpserver.registerPathHandler("/assoc/assoctest", handler);
|
||||||
|
httpserver.start(4444);
|
||||||
|
|
||||||
|
startIter();
|
||||||
|
do_test_pending();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handler(metadata, response)
|
||||||
|
{
|
||||||
|
var body = "thequickbrownfox";
|
||||||
|
response.setHeader("Content-Type", "text/plain", false);
|
||||||
|
|
||||||
|
var header = tests[currentTestIndex].responseheader;
|
||||||
|
if (header != undefined) {
|
||||||
|
for (var i = 0; i < header.length; i++) {
|
||||||
|
var splitHdr = header[i].split(": ");
|
||||||
|
response.setHeader(splitHdr[0], splitHdr[1], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||||
|
response.bodyOutputStream.write(body, body.length);
|
||||||
|
}
|
170
netwerk/test/unit/test_doomentry.js
Normal file
170
netwerk/test/unit/test_doomentry.js
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/**
|
||||||
|
* Test for nsICacheSession.doomEntry().
|
||||||
|
* It tests dooming
|
||||||
|
* - an existent inactive entry
|
||||||
|
* - a non-existent inactive entry
|
||||||
|
* - an existent active entry
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const Cr = Components.results;
|
||||||
|
|
||||||
|
var _CSvc;
|
||||||
|
function get_cache_service() {
|
||||||
|
if (_CSvc)
|
||||||
|
return _CSvc;
|
||||||
|
|
||||||
|
return _CSvc = Cc["@mozilla.org/network/cache-service;1"].
|
||||||
|
getService(Ci.nsICacheService);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetOutputStreamForEntry(key, asFile, append, callback)
|
||||||
|
{
|
||||||
|
this._key = key;
|
||||||
|
this._asFile = asFile;
|
||||||
|
this._append = append;
|
||||||
|
this._callback = callback;
|
||||||
|
this.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
GetOutputStreamForEntry.prototype = {
|
||||||
|
_key: "",
|
||||||
|
_asFile: false,
|
||||||
|
_append: false,
|
||||||
|
_callback: null,
|
||||||
|
|
||||||
|
QueryInterface: function(iid) {
|
||||||
|
if (iid.equals(Ci.nsICacheListener) ||
|
||||||
|
iid.equals(Ci.nsISupports))
|
||||||
|
return this;
|
||||||
|
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||||
|
},
|
||||||
|
|
||||||
|
onCacheEntryAvailable: function (entry, access, status) {
|
||||||
|
if (!entry)
|
||||||
|
do_throw("entry not available");
|
||||||
|
|
||||||
|
var ostream = entry.openOutputStream(this._append ? entry.dataSize : 0);
|
||||||
|
this._callback(entry, ostream);
|
||||||
|
},
|
||||||
|
|
||||||
|
run: function() {
|
||||||
|
var cache = get_cache_service();
|
||||||
|
var session = cache.createSession(
|
||||||
|
"HTTP",
|
||||||
|
this._asFile ? Ci.nsICache.STORE_ON_DISK_AS_FILE
|
||||||
|
: Ci.nsICache.STORE_ON_DISK,
|
||||||
|
Ci.nsICache.STREAM_BASED);
|
||||||
|
var cacheEntry = session.asyncOpenCacheEntry(
|
||||||
|
this._key,
|
||||||
|
this._append ? Ci.nsICache.ACCESS_READ_WRITE
|
||||||
|
: Ci.nsICache.ACCESS_WRITE,
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function DoomEntry(key, callback) {
|
||||||
|
this._key = key;
|
||||||
|
this._callback = callback;
|
||||||
|
this.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
DoomEntry.prototype = {
|
||||||
|
_key: "",
|
||||||
|
_callback: null,
|
||||||
|
|
||||||
|
QueryInterface: function(iid) {
|
||||||
|
if (iid.equals(Ci.nsICacheListener) ||
|
||||||
|
iid.equals(Ci.nsISupports))
|
||||||
|
return this;
|
||||||
|
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||||
|
},
|
||||||
|
|
||||||
|
onCacheEntryDoomed: function (status) {
|
||||||
|
this._callback(status);
|
||||||
|
},
|
||||||
|
|
||||||
|
run: function() {
|
||||||
|
get_cache_service()
|
||||||
|
.createSession("HTTP",
|
||||||
|
Ci.nsICache.STORE_ANYWHERE,
|
||||||
|
Ci.nsICache.STREAM_BASED)
|
||||||
|
.doomEntry(this._key, this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function write_and_check(str, data, len)
|
||||||
|
{
|
||||||
|
var written = str.write(data, len);
|
||||||
|
if (written != len) {
|
||||||
|
do_throw("str.write has not written all data!\n" +
|
||||||
|
" Expected: " + len + "\n" +
|
||||||
|
" Actual: " + written + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_entry()
|
||||||
|
{
|
||||||
|
new GetOutputStreamForEntry("testentry", true, false, write_entry_cont);
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_entry_cont(entry, ostream)
|
||||||
|
{
|
||||||
|
var data = "testdata";
|
||||||
|
write_and_check(ostream, data, data.length);
|
||||||
|
ostream.close();
|
||||||
|
entry.close();
|
||||||
|
new DoomEntry("testentry", check_doom1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_doom1(status)
|
||||||
|
{
|
||||||
|
do_check_eq(status, Cr.NS_OK);
|
||||||
|
new DoomEntry("nonexistententry", check_doom2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_doom2(status)
|
||||||
|
{
|
||||||
|
do_check_eq(status, Cr.NS_ERROR_NOT_AVAILABLE);
|
||||||
|
new GetOutputStreamForEntry("testentry", true, false, write_entry2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var gEntry;
|
||||||
|
var gOstream;
|
||||||
|
function write_entry2(entry, ostream)
|
||||||
|
{
|
||||||
|
// write some data and doom the entry while it is active
|
||||||
|
var data = "testdata";
|
||||||
|
write_and_check(ostream, data, data.length);
|
||||||
|
gEntry = entry;
|
||||||
|
gOstream = ostream;
|
||||||
|
new DoomEntry("testentry", check_doom3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_doom3(status)
|
||||||
|
{
|
||||||
|
do_check_eq(status, Cr.NS_OK);
|
||||||
|
// entry was doomed but writing should still succeed
|
||||||
|
var data = "testdata";
|
||||||
|
write_and_check(gOstream, data, data.length);
|
||||||
|
gEntry.close();
|
||||||
|
gOstream.close();
|
||||||
|
// dooming the same entry again should fail
|
||||||
|
new DoomEntry("testentry", check_doom4);
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_doom4(status)
|
||||||
|
{
|
||||||
|
do_check_eq(status, Cr.NS_ERROR_NOT_AVAILABLE);
|
||||||
|
do_test_finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_get_profile();
|
||||||
|
|
||||||
|
// clear the cache
|
||||||
|
get_cache_service().evictEntries(Ci.nsICache.STORE_ANYWHERE);
|
||||||
|
write_entry();
|
||||||
|
do_test_pending();
|
||||||
|
}
|
126
netwerk/test/unit/test_mismatch_lm.js
Normal file
126
netwerk/test/unit/test_mismatch_lm.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
do_load_httpd_js();
|
||||||
|
var httpserver = new nsHttpServer();
|
||||||
|
var cacheService;
|
||||||
|
var ios;
|
||||||
|
|
||||||
|
// Test the handling of a cache revalidation with mismatching last-modified
|
||||||
|
// headers. If we get such a revalidation the cache entry should be purged.
|
||||||
|
// see bug 717350
|
||||||
|
|
||||||
|
// In this test the wrong data is from 11-16-1994 with a value of 'A',
|
||||||
|
// and the right data is from 11-15-1994 with a value of 'B'.
|
||||||
|
|
||||||
|
// the same URL is requested 3 times. the first time the wrong data comes
|
||||||
|
// back, the second time that wrong data is revalidated with a 304 but
|
||||||
|
// a L-M header of the right data (this triggers a cache purge), and
|
||||||
|
// the third time the right data is returned.
|
||||||
|
|
||||||
|
var listener_3 = {
|
||||||
|
// this listener is used to process the the request made after
|
||||||
|
// the cache invalidation. it expects to see the 'right data'
|
||||||
|
|
||||||
|
onStartRequest: function test_onStartR(request, ctx) {},
|
||||||
|
|
||||||
|
onDataAvailable: function test_ODA(request, cx, inputStream,
|
||||||
|
offset, count) {
|
||||||
|
var data = new BinaryInputStream(inputStream).readByteArray(count);
|
||||||
|
|
||||||
|
// This is 'B'
|
||||||
|
do_check_eq(data, 66);
|
||||||
|
},
|
||||||
|
|
||||||
|
onStopRequest: function test_onStopR(request, ctx, status) {
|
||||||
|
httpserver.stop(do_test_finished);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var listener_2 = {
|
||||||
|
// this listener is used to process the revalidation of the
|
||||||
|
// corrupted cache entry. its revalidation prompts it to be cleaned
|
||||||
|
|
||||||
|
onStartRequest: function test_onStartR(request, ctx) {},
|
||||||
|
|
||||||
|
onDataAvailable: function test_ODA(request, cx, inputStream,
|
||||||
|
offset, count) {
|
||||||
|
var data = new BinaryInputStream(inputStream).readByteArray(count);
|
||||||
|
|
||||||
|
// This is 'A' from a cache revalidation, but that reval will clean the cache
|
||||||
|
// because of mismatched last-modified response headers
|
||||||
|
|
||||||
|
do_check_eq(data, 65);
|
||||||
|
},
|
||||||
|
|
||||||
|
onStopRequest: function test_onStopR(request, ctx, status) {
|
||||||
|
var channel = request.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
|
||||||
|
var chan = ios.newChannel("http://localhost:4444/test1", "", null);
|
||||||
|
var httpChan = chan.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
httpChan.requestMethod = "GET";
|
||||||
|
httpChan.asyncOpen(listener_3, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var listener_1 = {
|
||||||
|
// this listener processes the initial request from a empty cache.
|
||||||
|
// the server responds with the wrong data ('A')
|
||||||
|
|
||||||
|
onStartRequest: function test_onStartR(request, ctx) {},
|
||||||
|
|
||||||
|
onDataAvailable: function test_ODA(request, cx, inputStream,
|
||||||
|
offset, count) {
|
||||||
|
var data = new BinaryInputStream(inputStream).readByteArray(count);
|
||||||
|
do_check_eq(data, 65);
|
||||||
|
},
|
||||||
|
|
||||||
|
onStopRequest: function test_onStopR(request, ctx, status) {
|
||||||
|
var channel = request.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
|
||||||
|
var chan = ios.newChannel("http://localhost:4444/test1", "", null);
|
||||||
|
var httpChan = chan.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
httpChan.requestMethod = "GET";
|
||||||
|
httpChan.asyncOpen(listener_2, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_get_profile();
|
||||||
|
cacheService = Cc["@mozilla.org/network/cache-service;1"].
|
||||||
|
getService(Ci.nsICacheService);
|
||||||
|
ios = Cc["@mozilla.org/network/io-service;1"]
|
||||||
|
.getService(Ci.nsIIOService);
|
||||||
|
|
||||||
|
cacheService.evictEntries(Ci.nsICache.STORE_ANYWHERE);
|
||||||
|
|
||||||
|
httpserver.registerPathHandler("/test1", handler);
|
||||||
|
httpserver.start(4444);
|
||||||
|
|
||||||
|
var chan = ios.newChannel("http://localhost:4444/test1", "", null);
|
||||||
|
var httpChan = chan.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
httpChan.requestMethod = "GET";
|
||||||
|
httpChan.asyncOpen(listener_1, null);
|
||||||
|
|
||||||
|
do_test_pending();
|
||||||
|
}
|
||||||
|
|
||||||
|
var iter=0;
|
||||||
|
function handler(metadata, response) {
|
||||||
|
iter++;
|
||||||
|
if (metadata.hasHeader("If-Modified-Since")) {
|
||||||
|
response.setStatusLine(metadata.httpVersion, 304, "Not Modified");
|
||||||
|
response.setHeader("Last-Modified", "Tue, 15 Nov 1994 12:45:26 GMT", false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||||
|
response.setHeader("Cache-Control", "max-age=0", false)
|
||||||
|
if (iter == 1) {
|
||||||
|
// simulated wrong response
|
||||||
|
response.setHeader("Last-Modified", "Wed, 16 Nov 1994 00:00:00 GMT", false);
|
||||||
|
response.bodyOutputStream.write("A", 1);
|
||||||
|
}
|
||||||
|
if (iter == 3) {
|
||||||
|
// 'correct' response
|
||||||
|
response.setHeader("Last-Modified", "Tue, 15 Nov 1994 12:45:26 GMT", false);
|
||||||
|
response.bodyOutputStream.write("B", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ tail =
|
|||||||
[test_NetUtil.js]
|
[test_NetUtil.js]
|
||||||
[test_URIs.js]
|
[test_URIs.js]
|
||||||
[test_aboutblank.js]
|
[test_aboutblank.js]
|
||||||
|
[test_assoc.js]
|
||||||
[test_auth_proxy.js]
|
[test_auth_proxy.js]
|
||||||
[test_authentication.js]
|
[test_authentication.js]
|
||||||
# Bug 675039: test hangs consistently on Android
|
# Bug 675039: test hangs consistently on Android
|
||||||
@ -83,6 +84,7 @@ fail-if = os == "android"
|
|||||||
[test_bug667907.js]
|
[test_bug667907.js]
|
||||||
[test_bug667818.js]
|
[test_bug667818.js]
|
||||||
[test_bug669001.js]
|
[test_bug669001.js]
|
||||||
|
[test_doomentry.js]
|
||||||
[test_cacheflags.js]
|
[test_cacheflags.js]
|
||||||
[test_channel_close.js]
|
[test_channel_close.js]
|
||||||
[test_compareURIs.js]
|
[test_compareURIs.js]
|
||||||
@ -131,6 +133,7 @@ skip-if = os == "android"
|
|||||||
[test_httpsuspend.js]
|
[test_httpsuspend.js]
|
||||||
[test_idnservice.js]
|
[test_idnservice.js]
|
||||||
[test_localstreams.js]
|
[test_localstreams.js]
|
||||||
|
[test_mismatch_lm.js]
|
||||||
[test_MIME_params.js]
|
[test_MIME_params.js]
|
||||||
[test_multipart_streamconv.js]
|
[test_multipart_streamconv.js]
|
||||||
[test_multipart_streamconv_missing_lead_boundary.js]
|
[test_multipart_streamconv_missing_lead_boundary.js]
|
||||||
|
@ -72,7 +72,7 @@ nsAndroidHistory::RegisterVisitedCallback(nsIURI *aURI, Link *aContent)
|
|||||||
nsCAutoString uri;
|
nsCAutoString uri;
|
||||||
nsresult rv = aURI->GetSpec(uri);
|
nsresult rv = aURI->GetSpec(uri);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
nsString uriString = NS_ConvertUTF8toUTF16(uri);
|
NS_ConvertUTF8toUTF16 uriString(uri);
|
||||||
|
|
||||||
nsTArray<Link*>* list = mListeners.Get(uriString);
|
nsTArray<Link*>* list = mListeners.Get(uriString);
|
||||||
if (! list) {
|
if (! list) {
|
||||||
@ -98,7 +98,7 @@ nsAndroidHistory::UnregisterVisitedCallback(nsIURI *aURI, Link *aContent)
|
|||||||
nsCAutoString uri;
|
nsCAutoString uri;
|
||||||
nsresult rv = aURI->GetSpec(uri);
|
nsresult rv = aURI->GetSpec(uri);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
nsString uriString = NS_ConvertUTF8toUTF16(uri);
|
NS_ConvertUTF8toUTF16 uriString(uri);
|
||||||
|
|
||||||
nsTArray<Link*>* list = mListeners.Get(uriString);
|
nsTArray<Link*>* list = mListeners.Get(uriString);
|
||||||
if (! list)
|
if (! list)
|
||||||
@ -126,7 +126,7 @@ nsAndroidHistory::VisitURI(nsIURI *aURI, nsIURI *aLastVisitedURI, PRUint32 aFlag
|
|||||||
nsCAutoString uri;
|
nsCAutoString uri;
|
||||||
nsresult rv = aURI->GetSpec(uri);
|
nsresult rv = aURI->GetSpec(uri);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
nsString uriString = NS_ConvertUTF8toUTF16(uri);
|
NS_ConvertUTF8toUTF16 uriString(uri);
|
||||||
bridge->MarkURIVisited(uriString);
|
bridge->MarkURIVisited(uriString);
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -791,7 +791,7 @@ nsNavHistory::TokensToQueries(const nsTArray<QueryKeyValuePair>& aTokens,
|
|||||||
} else if (kvp.key.EqualsLiteral(QUERYKEY_TAG)) {
|
} else if (kvp.key.EqualsLiteral(QUERYKEY_TAG)) {
|
||||||
nsCAutoString unescaped(kvp.value);
|
nsCAutoString unescaped(kvp.value);
|
||||||
NS_UnescapeURL(unescaped); // modifies input
|
NS_UnescapeURL(unescaped); // modifies input
|
||||||
nsString tag = NS_ConvertUTF8toUTF16(unescaped);
|
NS_ConvertUTF8toUTF16 tag(unescaped);
|
||||||
if (!tags.Contains(tag)) {
|
if (!tags.Contains(tag)) {
|
||||||
NS_ENSURE_TRUE(tags.AppendElement(tag), NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(tags.AppendElement(tag), NS_ERROR_OUT_OF_MEMORY);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</binding>
|
</binding>
|
||||||
|
|
||||||
<binding id="scrollbar" extends="chrome://global/content/bindings/scrollbar.xml#scrollbar-base">
|
<binding id="scrollbar" extends="chrome://global/content/bindings/scrollbar.xml#scrollbar-base">
|
||||||
<content allowclickthrough="always">
|
<content clickthrough="always">
|
||||||
<xul:scrollbarbutton sbattr="scrollbar-up-top" type="decrement" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
|
<xul:scrollbarbutton sbattr="scrollbar-up-top" type="decrement" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
|
||||||
<xul:scrollbarbutton sbattr="scrollbar-down-top" type="increment" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
|
<xul:scrollbarbutton sbattr="scrollbar-down-top" type="increment" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
|
||||||
<xul:slider flex="1" xbl:inherits="disabled,curpos,maxpos,pageincrement,increment,orient,sborient=orient">
|
<xul:slider flex="1" xbl:inherits="disabled,curpos,maxpos,pageincrement,increment,orient,sborient=orient">
|
||||||
|
@ -792,7 +792,6 @@ if [ "$ENABLE_TESTS" ]; then
|
|||||||
dom/tests/mochitest/dom-level2-html/files/Makefile
|
dom/tests/mochitest/dom-level2-html/files/Makefile
|
||||||
dom/tests/mochitest/general/Makefile
|
dom/tests/mochitest/general/Makefile
|
||||||
dom/tests/mochitest/geolocation/Makefile
|
dom/tests/mochitest/geolocation/Makefile
|
||||||
dom/tests/mochitest/globalstorage/Makefile
|
|
||||||
dom/tests/mochitest/localstorage/Makefile
|
dom/tests/mochitest/localstorage/Makefile
|
||||||
dom/tests/mochitest/orientation/Makefile
|
dom/tests/mochitest/orientation/Makefile
|
||||||
dom/tests/mochitest/sessionstorage/Makefile
|
dom/tests/mochitest/sessionstorage/Makefile
|
||||||
|
@ -69,6 +69,8 @@ SHARED_LIBRARY_LIBS += \
|
|||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
export::
|
export::
|
||||||
|
# Avoid building libunwind documentation
|
||||||
|
$(topsrcdir)/tools/profiler/libunwind/dont_build_docs.sh $(DEPTH)/tools/profiler/libunwind/src/doc/Makefile
|
||||||
$(call SUBMAKE,,libunwind/src)
|
$(call SUBMAKE,,libunwind/src)
|
||||||
|
|
||||||
distclean::
|
distclean::
|
||||||
|
@ -523,12 +523,16 @@ void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
|
|||||||
mozilla::ArrayLength(pc_array),
|
mozilla::ArrayLength(pc_array),
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Start with the current function.
|
||||||
|
StackWalkCallback(aSample->pc, &array);
|
||||||
|
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
pthread_t pt = GetProfiledThread(platform_data());
|
pthread_t pt = GetProfiledThread(platform_data());
|
||||||
void *stackEnd = reinterpret_cast<void*>(-1);
|
void *stackEnd = reinterpret_cast<void*>(-1);
|
||||||
if (pt)
|
if (pt)
|
||||||
stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
|
stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
|
||||||
nsresult rv = FramePointerStackWalk(StackWalkCallback, 1, &array, reinterpret_cast<void**>(aSample->fp), stackEnd);
|
nsresult rv = FramePointerStackWalk(StackWalkCallback, 0, &array, reinterpret_cast<void**>(aSample->fp), stackEnd);
|
||||||
#else
|
#else
|
||||||
nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
|
nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
|
||||||
#endif
|
#endif
|
||||||
|
4
tools/profiler/libunwind/dont_build_docs.sh
Executable file
4
tools/profiler/libunwind/dont_build_docs.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Edit the doc Makefile to just echo the commands
|
||||||
|
sed -e 's/latex2man/echo/' -e 's/pdflatex/echo/' -i "$1"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user