Merge last green PGO from inbound to central
authorMarco Bonardo <mbonardo@mozilla.com>
Fri, 23 Mar 2012 12:49:57 +0100
changeset 93430 c20ec27eb0e8ec666a987a6f71a3902257f8128d
parent 93376 ab2ff3b5611fe63c94b3de9beae3757076214ef5 (current diff)
parent 93429 ea55576a4a1e963f43f87ba5f416752ddaaee785 (diff)
child 93431 8042c37b8100337e398ef704a3d58140b110a29d
child 93458 b3e429486693a64a17ca096b30fd47d038623e1f
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge last green PGO from inbound to central
configure.in
--- a/accessible/public/nsIAccessibilityService.h
+++ b/accessible/public/nsIAccessibilityService.h
@@ -158,22 +158,16 @@ public:
 
   /**
    * Notify the accessibility service that the given presshell is
    * being destroyed.
    */
   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.
    *
    * @param aEvent   [in] accessible event type
    * @param aTarget  [in] target of accessible event
    */
   virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget) = 0;
 };
 
--- a/accessible/src/base/AccIterator.cpp
+++ b/accessible/src/base/AccIterator.cpp
@@ -272,18 +272,20 @@ XULDescriptionIterator::Next()
 
   return nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // IDRefsIterator
 ////////////////////////////////////////////////////////////////////////////////
 
-IDRefsIterator::IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr) :
-  mCurrIdx(0), mContent(aContent)
+IDRefsIterator::
+  IDRefsIterator(nsDocAccessible* aDoc, nsIContent* aContent,
+                 nsIAtom* aIDRefsAttr) :
+  mCurrIdx(0), mContent(aContent), mDoc(aDoc)
 {
   if (mContent->IsInDoc())
     mContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs);
 }
 
 const nsDependentSubstring
 IDRefsIterator::NextID()
 {
@@ -363,17 +365,17 @@ IDRefsIterator::GetElem(const nsDependen
 
   return nsnull;
 }
 
 nsAccessible*
 IDRefsIterator::Next()
 {
   nsIContent* nextElm = NextElem();
-  return nextElm ? GetAccService()->GetAccessible(nextElm, nsnull) : nsnull;
+  return nextElm ? mDoc->GetAccessible(nextElm) : nsnull;
 }
 
 nsAccessible*
 SingleAccIterator::Next()
 {
   nsRefPtr<nsAccessible> nextAcc;
   mAcc.swap(nextAcc);
   return (nextAcc && !nextAcc->IsDefunct()) ? nextAcc : nsnull;
--- a/accessible/src/base/AccIterator.h
+++ b/accessible/src/base/AccIterator.h
@@ -259,17 +259,18 @@ private:
 /**
  * Used to iterate through IDs, elements or accessibles pointed by IDRefs
  * attribute. Note, any method used to iterate through IDs, elements, or
  * accessibles moves iterator to next position.
  */
 class IDRefsIterator : public AccIterable
 {
 public:
-  IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr);
+  IDRefsIterator(nsDocAccessible* aDoc, nsIContent* aContent,
+                 nsIAtom* aIDRefsAttr);
   virtual ~IDRefsIterator() { }
 
   /**
    * Return next ID.
    */
   const nsDependentSubstring NextID();
 
   /**
@@ -287,16 +288,17 @@ public:
 
 private:
   IDRefsIterator();
   IDRefsIterator(const IDRefsIterator&);
   IDRefsIterator operator = (const IDRefsIterator&);
 
   nsString mIDs;
   nsIContent* mContent;
+  nsDocAccessible* mDoc;
   nsAString::index_type mCurrIdx;
 };
 
 /**
  * Iterator that points to a single accessible returning it on the first call
  * to Next().
  */
 class SingleAccIterator : public AccIterable
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -331,16 +331,25 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] 
     kUseMapRole,
     eNoValue,
     eClickAction,
     eNoLiveAttr,
     kNoReqStates,
     eARIACheckableBool
   },
   {
+    "note",
+    roles::NOTE,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kNoReqStates
+  },
+  {
     "option",
     roles::OPTION,
     kUseMapRole,
     eNoValue,
     eSelectAction,
     eNoLiveAttr,
     kNoReqStates,
     eARIASelectable,
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -671,20 +671,18 @@ nsAccessibilityService::PresShellActivat
   }
 }
 
 void
 nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell,
                                            nsIContent* aContent)
 {
   nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument());
-  if (document) {
-    document->HandleNotification<nsDocAccessible, nsIContent>
-      (document, &nsDocAccessible::RecreateAccessible, aContent);
-  }
+  if (document)
+    document->RecreateAccessible(aContent);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibleRetrieval
 
 NS_IMETHODIMP
 nsAccessibilityService::GetApplicationAccessible(nsIAccessible **aAccessibleApplication)
 {
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -168,18 +168,20 @@ public:
 
   virtual void PresShellDestroyed(nsIPresShell* aPresShell);
 
   /**
    * Notify that presshell is activated.
    */
   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);
 
   // nsAccessibiltiyService
 
   /**
    * Return true if accessibility service has been shutdown.
    */
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -2010,51 +2010,51 @@ nsAccessible::RelationByType(PRUint32 aT
 {
   // Relationships are defined on the same content node that the role would be
   // defined on.
   switch (aType) {
     case nsIAccessibleRelation::RELATION_LABEL_FOR: {
       Relation rel(new RelatedAccIterator(Document(), mContent,
                                           nsGkAtoms::aria_labelledby));
       if (mContent->Tag() == nsGkAtoms::label)
-        rel.AppendIter(new IDRefsIterator(mContent, mContent->IsHTML() ?
+        rel.AppendIter(new IDRefsIterator(mDoc, mContent, mContent->IsHTML() ?
                                           nsGkAtoms::_for :
                                           nsGkAtoms::control));
 
       return rel;
     }
     case nsIAccessibleRelation::RELATION_LABELLED_BY: {
-      Relation rel(new IDRefsIterator(mContent,
+      Relation rel(new IDRefsIterator(mDoc, mContent,
                                       nsGkAtoms::aria_labelledby));
       if (mContent->IsHTML()) {
         rel.AppendIter(new HTMLLabelIterator(Document(), this));
       } else if (mContent->IsXUL()) {
         rel.AppendIter(new XULLabelIterator(Document(), mContent));
       }
 
       return rel;
     }
     case nsIAccessibleRelation::RELATION_DESCRIBED_BY: {
-      Relation rel(new IDRefsIterator(mContent,
-                                        nsGkAtoms::aria_describedby));
+      Relation rel(new IDRefsIterator(mDoc, mContent,
+                                      nsGkAtoms::aria_describedby));
       if (mContent->IsXUL())
         rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
 
       return rel;
     }
     case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR: {
       Relation rel(new RelatedAccIterator(Document(), mContent,
                                           nsGkAtoms::aria_describedby));
 
       // This affectively adds an optional control attribute to xul:description,
       // which only affects accessibility, by allowing the description to be
       // tied to a control.
       if (mContent->Tag() == nsGkAtoms::description &&
           mContent->IsXUL())
-        rel.AppendIter(new IDRefsIterator(mContent,
+        rel.AppendIter(new IDRefsIterator(mDoc, mContent,
                                           nsGkAtoms::control));
 
       return rel;
     }
     case nsIAccessibleRelation::RELATION_NODE_CHILD_OF: {
       Relation rel(new RelatedAccIterator(Document(), mContent,
                                           nsGkAtoms::aria_owns));
       
@@ -2086,23 +2086,23 @@ nsAccessible::RelationByType(PRUint32 aT
       }
 
       return rel;
     }
     case nsIAccessibleRelation::RELATION_CONTROLLED_BY:
       return Relation(new RelatedAccIterator(Document(), mContent,
                                              nsGkAtoms::aria_controls));
     case nsIAccessibleRelation::RELATION_CONTROLLER_FOR: {
-      Relation rel(new IDRefsIterator(mContent,
+      Relation rel(new IDRefsIterator(mDoc, mContent,
                                       nsGkAtoms::aria_controls));
       rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
       return rel;
     }
     case nsIAccessibleRelation::RELATION_FLOWS_TO:
-      return Relation(new IDRefsIterator(mContent,
+      return Relation(new IDRefsIterator(mDoc, mContent,
                                          nsGkAtoms::aria_flowto));
     case nsIAccessibleRelation::RELATION_FLOWS_FROM:
       return Relation(new RelatedAccIterator(Document(), mContent,
                                              nsGkAtoms::aria_flowto));
     case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON: {
       if (mContent->IsHTML()) {
         // HTML form controls implements nsIFormControl interface.
         nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1462,24 +1462,18 @@ nsDocAccessible::ContentRemoved(nsIConte
 void
 nsDocAccessible::RecreateAccessible(nsIContent* aContent)
 {
   // XXX: we shouldn't recreate whole accessible subtree, instead we should
   // subclass hide and show events to handle them separately and implement their
   // coalescence with normal hide and show events. Note, in this case they
   // should be coalesced with normal show/hide events.
 
-  // Check if the node is in accessible document.
-  nsAccessible* container = GetContainerAccessible(aContent);
-  if (container) {
-    // Remove and reinsert.
-    UpdateTree(container, aContent, false);
-    container->UpdateChildren();
-    UpdateTree(container, aContent, true);
-  }
+  ContentRemoved(aContent->GetParent(), aContent);
+  ContentInserted(aContent->GetParent(), aContent, aContent->GetNextSibling());
 }
 
 void
 nsDocAccessible::ProcessInvalidationList()
 {
   // Invalidate children of container accessible for each element in
   // invalidation list. Allow invalidation list insertions while container
   // children are recached.
@@ -1618,17 +1612,17 @@ nsDocAccessible::AddDependentIDsFor(nsAc
 
     } else if (relAttr == nsGkAtoms::control) {
       if (!aRelProvider->GetContent()->IsXUL() ||
           (aRelProvider->GetContent()->Tag() != nsGkAtoms::label &&
            aRelProvider->GetContent()->Tag() != nsGkAtoms::description))
         continue;
     }
 
-    IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
+    IDRefsIterator iter(this, aRelProvider->GetContent(), relAttr);
     while (true) {
       const nsDependentSubstring id = iter.NextID();
       if (id.IsEmpty())
         break;
 
       AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
       if (!providers) {
         providers = new AttrRelProviderArray();
@@ -1669,17 +1663,17 @@ void
 nsDocAccessible::RemoveDependentIDsFor(nsAccessible* aRelProvider,
                                        nsIAtom* aRelAttr)
 {
   for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
     nsIAtom* relAttr = *kRelationAttrs[idx];
     if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
       continue;
 
-    IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
+    IDRefsIterator iter(this, aRelProvider->GetContent(), relAttr);
     while (true) {
       const nsDependentSubstring id = iter.NextID();
       if (id.IsEmpty())
         break;
 
       AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
       if (providers) {
         for (PRUint32 jdx = 0; jdx < providers->Length(); ) {
@@ -1712,43 +1706,39 @@ nsDocAccessible::UpdateAccessibleOnAttrC
     if (mContent == aElement) {
       SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aElement));
       return true;
     }
 
     // Recreate the accessible when role is changed because we might require a
     // different accessible class for the new role or the accessible may expose
     // a different sets of interfaces (COM restriction).
-    HandleNotification<nsDocAccessible, nsIContent>
-      (this, &nsDocAccessible::RecreateAccessible, aElement);
+    RecreateAccessible(aElement);
 
     return true;
   }
 
   if (aAttribute == nsGkAtoms::href ||
       aAttribute == nsGkAtoms::onclick) {
     // Not worth the expense to ensure which namespace these are in. It doesn't
     // kill use to recreate the accessible even if the attribute was used in
     // the wrong namespace or an element that doesn't support it.
 
-    // Recreate accessible asynchronously to allow the content to handle
-    // the attribute change.
-    mNotificationController->ScheduleNotification<nsDocAccessible, nsIContent>
-      (this, &nsDocAccessible::RecreateAccessible, aElement);
-
+    // Make sure the accessible is recreated asynchronously to allow the content
+    // to handle the attribute change.
+    RecreateAccessible(aElement);
     return true;
   }
 
   if (aAttribute == nsGkAtoms::aria_multiselectable &&
       aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
     // This affects whether the accessible supports SelectAccessible.
     // COM says we cannot change what interfaces are supported on-the-fly,
     // so invalidate this object. A new one will be created on demand.
-    HandleNotification<nsDocAccessible, nsIContent>
-      (this, &nsDocAccessible::RecreateAccessible, aElement);
+    RecreateAccessible(aElement);
 
     return true;
   }
 
   return false;
 }
 
 // nsDocAccessible public member
--- a/accessible/src/base/nsTextEquivUtils.cpp
+++ b/accessible/src/base/nsTextEquivUtils.cpp
@@ -89,17 +89,17 @@ nsTextEquivUtils::GetTextEquivFromIDRefs
 {
   aTextEquiv.Truncate();
 
   nsIContent* content = aAccessible->GetContent();
   if (!content)
     return NS_OK;
 
   nsIContent* refContent = nsnull;
-  IDRefsIterator iter(content, aIDRefsAttr);
+  IDRefsIterator iter(aAccessible->Document(), content, aIDRefsAttr);
   while ((refContent = iter.NextElem())) {
     if (!aTextEquiv.IsEmpty())
       aTextEquiv += ' ';
 
     nsresult rv = AppendTextEquivFromContent(aAccessible, refContent,
                                              &aTextEquiv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -329,17 +329,17 @@ nsHTMLTableCellAccessible::GetCellIndexe
   return cellLayout->GetCellIndexes(aRowIndex, aColIndex);
 }
 
 nsresult
 nsHTMLTableCellAccessible::GetHeaderCells(PRInt32 aRowOrColumnHeaderCell,
                                           nsIArray **aHeaderCells)
 {
   // Get header cells from @header attribute.
-  IDRefsIterator iter(mContent, nsGkAtoms::headers);
+  IDRefsIterator iter(mDoc, mContent, nsGkAtoms::headers);
   nsIContent* headerCellElm = iter.NextElem();
   if (headerCellElm) {
     nsresult rv = NS_OK;
     nsCOMPtr<nsIMutableArray> headerCells =
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     roles::Role desiredRole = static_cast<roles::Role>(-1) ;
     if (aRowOrColumnHeaderCell == nsAccUtils::eRowHeaderCells)
--- a/accessible/src/html/nsHTMLTextAccessible.cpp
+++ b/accessible/src/html/nsHTMLTextAccessible.cpp
@@ -199,17 +199,17 @@ nsHTMLOutputAccessible::
 
 NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLOutputAccessible, nsHyperTextAccessible)
 
 Relation
 nsHTMLOutputAccessible::RelationByType(PRUint32 aType)
 {
   Relation rel = nsAccessibleWrap::RelationByType(aType);
   if (aType == nsIAccessibleRelation::RELATION_CONTROLLED_BY)
-    rel.AppendIter(new IDRefsIterator(mContent, nsGkAtoms::_for));
+    rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::_for));
 
   return rel;
 }
 
 role
 nsHTMLOutputAccessible::NativeRole()
 {
   return roles::SECTION;
--- a/accessible/tests/mochitest/test_aria_roles.html
+++ b/accessible/tests/mochitest/test_aria_roles.html
@@ -63,16 +63,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       var abstract_roles = ["composite", "landmark", "structure", "widget",
                             "window", "input", "range", "select", "section",
                             "sectionhead"];
       for (a in abstract_roles)
         testRole(abstract_roles[a], ROLE_SECTION);
 
       //////////////////////////////////////////////////////////////////////////
       // misc roles
+      testRole("note", ROLE_NOTE);
       testRole("scrollbar", ROLE_SCROLLBAR);
       testRole("dir", ROLE_LIST);
 
       //////////////////////////////////////////////////////////////////////////
       // test document role map update
       var testDoc = getAccessible(document, [nsIAccessibleDocument]);
       testRole(testDoc, ROLE_DOCUMENT);
       document.body.setAttribute("role", "application");
@@ -149,16 +150,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <div role="input" id="input">input</div>
   <div role="range" id="range">range</div>
   <div role="select" id="select">select</div>
   <!-- test abstract structure roles -->
   <div role="section" id="section">section</div>
   <div role="sectionhead" id="sectionhead">sectionhead</div>
 
   <!-- misc roles -->
+  <div role="note" id="note">note</div>
   <div role="scrollbar" id="scrollbar">scrollbar</div>
 
   <div id="dir" role="directory">
     <div role="listitem">A</div>
     <div role="listitem">B</div>
     <div role="listitem">C</div>
   </div>
 </body>
--- a/accessible/tests/mochitest/treeupdate/test_imagemap.html
+++ b/accessible/tests/mochitest/treeupdate/test_imagemap.html
@@ -340,30 +340,66 @@
       }
 
       this.getID = function insertMap_getID()
       {
         return "insert map element";
       }
     }
 
+    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;
 
     var gQueue = null;
     function doTest()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new insertArea("imgmap", "map"));
       gQueue.push(new appendArea("imgmap", "map"));
       gQueue.push(new removeArea("imgmap", "map"));
       gQueue.push(new removeNameOnMap("container", "imgmap", "map"));
       gQueue.push(new restoreNameOnMap("container", "imgmap", "map"));
       gQueue.push(new removeMap("container", "imgmap", "map"));
       gQueue.push(new insertMap("container", "imgmap"));
+      gQueue.push(new hideImageMap("container", "imgmap"));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
--- a/configure.in
+++ b/configure.in
@@ -2445,17 +2445,16 @@ ia64*-hpux*)
         AR_EXTRACT=
         RANLIB='echo not_ranlib'
         STRIP='echo not_strip'
         PKG_SKIP_STRIP=1
         XARGS=xargs
         ZIP=zip
         UNZIP=unzip
         DOXYGEN=:
-        GARBAGE='$(OBJDIR)/vc20.pdb $(OBJDIR)/vc40.pdb'
         ASM_SUFFIX=asm
         OBJ_SUFFIX=obj
         LIB_SUFFIX=lib
         DLL_PREFIX=
         LIB_PREFIX=
         IMPORT_LIB_SUFFIX=lib
         MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -106,16 +106,17 @@ class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
 class nsFrameLoader;
 class nsIBoxObject;
 class imgIRequest;
 class nsISHEntry;
 class nsDOMNavigationTiming;
 class nsWindowSizes;
+class nsIObjectLoadingContent;
 
 namespace mozilla {
 namespace css {
 class Loader;
 } // namespace css
 
 namespace dom {
 class Link;
@@ -1561,16 +1562,20 @@ public:
   // Add/Remove images from the document image tracker
   virtual nsresult AddImage(imgIRequest* aImage) = 0;
   virtual nsresult RemoveImage(imgIRequest* aImage) = 0;
 
   // Makes the images on this document locked/unlocked. By default, the locking
   // state is unlocked/false.
   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 nsDOMNavigationTiming* GetNavigationTiming() const = 0;
 
   virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming) = 0;
 
   virtual Element* FindImageMap(const nsAString& aNormalizedMapName) = 0;
 
--- a/content/base/public/nsIObjectLoadingContent.idl
+++ b/content/base/public/nsIObjectLoadingContent.idl
@@ -47,17 +47,17 @@ interface nsIURI;
 %{C++
 #include "nsNPAPIPluginInstance.h"
 %}
 [ptr] native nsNPAPIPluginInstancePtr(nsNPAPIPluginInstance);
 
 /**
  * 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
 {
   const unsigned long TYPE_LOADING  = 0;
   const unsigned long TYPE_IMAGE    = 1;
   const unsigned long TYPE_PLUGIN   = 2;
   const unsigned long TYPE_DOCUMENT = 3;
   const unsigned long TYPE_NULL     = 4;
 
@@ -120,16 +120,22 @@ interface nsIObjectLoadingContent : nsIS
                                 in boolean submittedCrashReport);
 
   /**
    * This method will play a plugin that has been stopped by the
    * click-to-play plugins feature.
    */
   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 syncStartPluginInstance();
   [noscript] void asyncStartPluginInstance();
 
   /**
    * The URL of the data/src loaded in the object. This may be null (i.e.
    * an <embed> with no src).
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1668,16 +1668,18 @@ nsDocument::~nsDocument()
   for (PRUint32 i = 0; i < mFileDataUris.Length(); ++i) {
     nsBlobProtocolHandler::RemoveFileDataEntry(mFileDataUris[i]);
   }
 
   // We don't want to leave residual locks on images. Make sure we're in an
   // unlocked state, and then clear the table.
   SetImageLockingState(false);
   mImageTracker.Clear();
+
+  mPlugins.Clear();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
 
 NS_INTERFACE_TABLE_HEAD(nsDocument)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_DOCUMENT_INTERFACE_TABLE_BEGIN(nsDocument)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
@@ -2018,17 +2020,18 @@ nsDocument::Init()
   NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE,
                     "Bad NodeType in aNodeInfo");
 
   NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
 
   mScriptLoader = new nsScriptLoader(this);
   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_OK;
 }
 
 void 
 nsIDocument::DeleteAllProperties()
@@ -8349,16 +8352,61 @@ nsDocument::RemoveImage(imgIRequest* aIm
   // Request that the image be discarded if nobody else holds a lock on it.
   // Do this even if !mLockingImages, because even if we didn't just unlock
   // this image, it might still be a candidate for discarding.
   aImage->RequestDiscard();
 
   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,
                                PRUint32 aData,
                                void*    userArg)
 {
   aKey->LockImage();
   aKey->RequestDecode();
   return PL_DHASH_NEXT;
 }
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -929,16 +929,26 @@ public:
   virtual const nsSmallVoidArray* GetAllElementsForId(const nsAString& aElementId) const;
 
   virtual Element *LookupImageElement(const nsAString& aElementId);
 
   virtual NS_HIDDEN_(nsresult) AddImage(imgIRequest* aImage);
   virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage);
   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 nsDOMNavigationTiming* GetNavigationTiming() const;
   virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming);
 
   virtual Element* FindImageMap(const nsAString& aNormalizedMapName);
 
   virtual void NotifyAudioAvailableListener();
@@ -1293,16 +1303,19 @@ private:
 
   nsCString mScrollToRef;
   PRUint8 mScrolledToRefAlready : 1;
   PRUint8 mChangeScrollPosWhenScrollingToRef : 1;
 
   // Tracking for images in the document.
   nsDataHashtable< nsPtrHashKey<imgIRequest>, PRUint32> mImageTracker;
 
+  // Tracking for plugins in the document.
+  nsTHashtable< nsPtrHashKey<nsIObjectLoadingContent> > mPlugins;
+
   VisibilityState mVisibilityState;
 
 #ifdef DEBUG
 protected:
   bool mWillReparent;
 #endif
 };
 
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -110,16 +110,28 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
 #endif
 
 #define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG)
 
 #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 {
 public:
   nsObjectLoadingContent *mContent;
   nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent)
   : mContent(aContent)
   {
     static_cast<nsIObjectLoadingContent *>(mContent)->AddRef();
   }
@@ -541,29 +553,52 @@ bool nsObjectLoadingContent::IsPluginEna
   const char* typeFromExt;
   if (NS_SUCCEEDED(pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt))) {
     mimeType = typeFromExt;
     return true;
   }
   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()
   : mPendingInstantiateEvent(nsnull)
   , mChannel(nsnull)
   , mType(eType_Loading)
   , mInstantiating(false)
   , mUserDisabled(false)
   , mSuppressed(false)
   , mNetworkCreated(true)
-  // If plugins.click_to_play is false, plugins should always play
-  , mShouldPlay(!mozilla::Preferences::GetBool("plugins.click_to_play", false))
   , mSrcStreamLoading(false)
   , 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()
 {
   DestroyImageLoadingContent();
   if (mFrameLoader) {
     mFrameLoader->Destroy();
   }
@@ -2200,10 +2235,18 @@ nsObjectLoadingContent::NotifyContentObj
 
 NS_IMETHODIMP
 nsObjectLoadingContent::PlayPlugin()
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_OK;
 
   mShouldPlay = true;
+  mActivated = true;
   return LoadObject(mURI, true, mContentType, true);
 }
+
+NS_IMETHODIMP
+nsObjectLoadingContent::GetActivated(bool* aActivated)
+{
+  *aActivated = mActivated;
+  return NS_OK;
+}
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -239,16 +239,22 @@ class nsObjectLoadingContent : public ns
 
     static void Traverse(nsObjectLoadingContent *tmp,
                          nsCycleCollectionTraversalCallback &cb);
 
     void CreateStaticClone(nsObjectLoadingContent* aDest) const;
 
     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:
 
     void NotifyContentObjectWrapper();
 
     /**
      * Check whether the given request represents a successful load.
      */
     static bool IsSuccessfulRequest(nsIRequest* aRequest);
@@ -394,16 +400,20 @@ class nsObjectLoadingContent : public ns
     // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
     // it may lose the flag.
     bool                        mNetworkCreated : 1;
 
     // Used to keep track of whether or not a plugin should be played.
     // This is used for click-to-play plugins.
     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
     // 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
     // we'll deliver duplicate streams. Should be cleared when we are not loading
     // src data.
     bool mSrcStreamLoading;
 
     // A specific state that caused us to fallback
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2774,16 +2774,20 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
   nsCOMPtr<nsIStreamListener> listener = this;
   if (mState & XML_HTTP_REQUEST_MULTIPART) {
     listener = new nsMultipartProxyListener(listener);
     if (!listener) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
+  // 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()) {
     // Always create a nsCORSListenerProxy here even if it's
     // a same-origin request right now, since it could be redirected.
     listener = new nsCORSListenerProxy(listener, mPrincipal, mChannel,
                                        withCredentials, true, &rv);
     NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
     NS_ENSURE_SUCCESS(rv, rv);
   }
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -52,16 +52,18 @@
 #include "nsIDOMDocument.h"
 #include "nsDOMError.h"
 #include "nsNodeInfoManager.h"
 #include "nsNetUtil.h"
 #include "nsXPCOMStrings.h"
 #include "nsThreadUtils.h"
 #include "nsIThreadInternal.h"
 #include "nsContentUtils.h"
+#include "nsIRequest.h"
+
 #include "nsFrameManager.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 
 #include "nsITimer.h"
 
 #include "nsEventDispatcher.h"
@@ -2845,16 +2847,23 @@ nsresult nsHTMLMediaElement::GetBuffered
   return NS_OK;
 }
 
 void nsHTMLMediaElement::SetRequestHeaders(nsIHttpChannel* aChannel)
 {
   // Send Accept header for video and audio types only (Bug 489071)
   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,
   // 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.
   // So, disable the standard "Accept-Encoding: gzip,deflate" that we usually send.
   // See bug 614760.
   aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept-Encoding"),
                              EmptyCString(), false);
 
--- a/content/html/content/src/nsHTMLObjectElement.cpp
+++ b/content/html/content/src/nsHTMLObjectElement.cpp
@@ -260,30 +260,36 @@ nsHTMLObjectElement::BindToTree(nsIDocum
                                 nsIContent *aBindingParent,
                                 bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLFormElement::BindToTree(aDocument, aParent,
                                                      aBindingParent,
                                                      aCompileEventHandlers);
   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 (mIsDoneAddingChildren) {
     void (nsHTMLObjectElement::*start)() = &nsHTMLObjectElement::StartObjectLoad;
     nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start));
   }
 
   return NS_OK;
 }
 
 void
 nsHTMLObjectElement::UnbindFromTree(bool aDeep,
                                     bool aNullParent)
 {
   RemovedFromDocument();
+  nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
   nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 
 
 nsresult
 nsHTMLObjectElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom *aName,
                              nsIAtom *aPrefix, const nsAString &aValue,
--- a/content/html/content/src/nsHTMLSharedObjectElement.cpp
+++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp
@@ -278,31 +278,37 @@ nsHTMLSharedObjectElement::BindToTree(ns
                                       nsIContent *aBindingParent,
                                       bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   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 (mIsDoneAddingChildren) {
     void (nsHTMLSharedObjectElement::*start)() =
       &nsHTMLSharedObjectElement::StartObjectLoad;
     nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start));
   }
 
   return NS_OK;
 }
 
 void
 nsHTMLSharedObjectElement::UnbindFromTree(bool aDeep,
                                           bool aNullParent)
 {
   RemovedFromDocument();
+  nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 
 
 nsresult
 nsHTMLSharedObjectElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom *aName,
                                    nsIAtom *aPrefix, const nsAString &aValue,
--- a/content/xbl/src/nsXBLContentSink.cpp
+++ b/content/xbl/src/nsXBLContentSink.cpp
@@ -418,36 +418,39 @@ nsXBLContentSink::OnOpenContainer(const 
   if (aNameSpaceID != kNameSpaceID_XBL) {
     // Construct non-XBL nodes
     return true;
   }
 
   bool ret = true;
   if (aTagName == nsGkAtoms::bindings) {
     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) {
       mState = eXBL_Error;
       return true;
     }
 
     mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo);
 
     nsIURI *uri = mDocument->GetDocumentURI();
-      
+
     bool isChrome = false;
     bool isRes = false;
 
     uri->SchemeIs("chrome", &isChrome);
     uri->SchemeIs("resource", &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;
   }
   else if (aTagName == nsGkAtoms::binding) {
     ENSURE_XBL_STATE(mState == eXBL_InBindings);
     mState = eXBL_InBinding;
   }
   else if (aTagName == nsGkAtoms::handlers) {
     ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -677,17 +677,18 @@ nsXBLDocumentInfo::ReadPrototypeBindings
   nsContentUtils::GetSecurityManager()->
     GetSystemPrincipal(getter_AddRefs(principal));
 
   nsCOMPtr<nsIDOMDocument> domdoc;
   rv = NS_NewXBLDocument(getter_AddRefs(domdoc), aURI, nsnull, principal);
   NS_ENSURE_SUCCESS(rv, rv);
 
   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) {
     PRUint8 flags;
     nsresult rv = stream->Read8(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
     if (flags == XBLBinding_Serialize_NoMoreBindings)
       break;
 
@@ -778,19 +779,8 @@ nsXBLDocumentInfo::GetScriptGlobalObject
     if (!global)
       return nsnull;
 
     mGlobalObject = global;
   }
 
   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;
-}
--- a/content/xbl/src/nsXBLDocumentInfo.h
+++ b/content/xbl/src/nsXBLDocumentInfo.h
@@ -96,11 +96,9 @@ private:
   // the binding table owns each nsXBLPrototypeBinding
   nsObjectHashtable* mBindingTable;
   // non-owning pointer to the first binding in the table
   nsXBLPrototypeBinding* mFirstBinding;
 
   nsRefPtr<nsXBLDocGlobalObject> mGlobalObject;
 };
 
-nsXBLDocumentInfo* NS_NewXBLDocumentInfo(nsIDocument* aDocument);
-
 #endif
--- a/content/xbl/src/nsXBLService.cpp
+++ b/content/xbl/src/nsXBLService.cpp
@@ -1101,21 +1101,17 @@ nsXBLService::LoadBindingDocumentInfo(ns
         if (bindingManager) {
           // Also put it in our binding manager's document table.
           bindingManager->PutXBLDocumentInfo(info);
         }
       }
     }
   }
 
-  if (!info)
-    return NS_OK;
- 
-  *aResult = info;
-  NS_IF_ADDREF(*aResult);
+  info.forget(aResult);
 
   return NS_OK;
 }
 
 nsresult
 nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
                                    nsIURI* aDocumentURI, nsIURI* aBindingURI, 
                                    bool aForceSyncLoad, nsIDocument** aResult)
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -46,16 +46,17 @@
 #include "nsGlobalWindow.h"
 #include "nsIDocument.h"
 #include "nsFocusManager.h"
 #include "nsEventStateManager.h"
 #include "nsFrameManager.h"
 #include "nsRefreshDriver.h"
 #include "nsDOMTouchEvent.h"
 #include "nsIDOMTouchEvent.h"
+#include "nsObjectLoadingContent.h"
 
 #include "nsIScrollableFrame.h"
 
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 
 #include "nsIFrame.h"
 #include "nsIWidget.h"
@@ -71,16 +72,17 @@
 #include "gfxImageSurface.h"
 #include "nsLayoutUtils.h"
 #include "nsComputedDOMStyle.h"
 #include "nsIPresShell.h"
 #include "nsStyleAnimation.h"
 #include "nsCSSProps.h"
 #include "nsDOMFile.h"
 #include "BasicLayers.h"
+#include "nsTArrayHelpers.h"
 
 #if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2)
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #endif
 
 #include "Layers.h"
 #include "nsIIOService.h"
@@ -2225,8 +2227,31 @@ nsDOMWindowUtils::GetPaintingSuppressed(
   nsCOMPtr<nsIPresShell> presShell;
   docShell->GetPresShell(getter_AddRefs(presShell));
   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
 
   *aPaintingSuppressed = presShell->IsPaintingSuppressed();
   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;
+}
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -65,17 +65,17 @@ interface nsIDOMEvent;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 
-[scriptable, uuid(43feb172-30e1-4ff1-b021-004f973da516)]
+[scriptable, uuid(c7f303a1-4f7b-4d38-a192-c3f0e25dadb1)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -1094,9 +1094,20 @@ interface nsIDOMWindowUtils : nsISupport
   [implicit_jscontext]
   AString getPCCountScriptContents(in long script);
 
   /**
    * Returns true if painting is suppressed for this window and false
    * otherwise.
    */
   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;
 };
--- a/dom/system/unix/QTMLocationProvider.cpp
+++ b/dom/system/unix/QTMLocationProvider.cpp
@@ -111,8 +111,13 @@ QTMLocationProvider::Shutdown()
         return NS_ERROR_NOT_IMPLEMENTED;
 
     mLocation->stopUpdates();
     mCallback = nsnull;
 
     return NS_OK;
 }
 
+NS_IMETHODIMP
+QTMLocationProvider::SetHighAccuracy(bool)
+{
+  return NS_OK;
+}
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -48,79 +48,33 @@
 #include "mozilla/Preferences.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "SystemWorkerManager.h"
+#include "nsTArrayHelpers.h"
 
 #include "CallEvent.h"
 #include "TelephonyCall.h"
 
 USING_TELEPHONY_NAMESPACE
 using namespace mozilla::dom::gonk;
 using mozilla::Preferences;
 
 #define DOM_TELEPHONY_APP_PHONE_URL_PREF "dom.telephony.app.phone.url"
 
 namespace {
 
 typedef nsAutoTArray<Telephony*, 2> TelephonyList;
 
 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
 
 Telephony::Telephony()
 : mActiveCall(nsnull), mCallsArray(nsnull), mRooted(false)
 {
   if (!gTelephonyList) {
     gTelephonyList = new TelephonyList();
   }
@@ -347,18 +301,17 @@ Telephony::GetCalls(jsval* aCalls)
 {
   JSObject* calls = mCallsArray;
   if (!calls) {
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS(rv, rv);
     if (sc) {
       rv =
-        nsTArrayToJSArray(sc->GetNativeContext(),
-                          sc->GetNativeGlobal(), mCalls, &calls);
+        nsTArrayToJSArray(mScriptContext->GetNativeContext(), mCalls, &calls);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (!mRooted) {
         NS_HOLD_JS_OBJECTS(this, Telephony);
         mRooted = true;
       }
 
       mCallsArray = calls;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -1905,17 +1905,17 @@ already_AddRefed<GLContext>
 GLContextProviderEGL::CreateOffscreen(const gfxIntSize& aSize,
                                       const ContextFormat& aFormat,
                                       const ContextFlags aFlags)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
         return nsnull;
     }
 
-#if defined(ANDROID) || defined(XP_WIN)
+#if !defined(MOZ_X11)
     bool usePBuffers = false; // Generally, prefer FBOs to PBuffers
 
     if (sEGLLibrary.IsANGLE())
       usePBuffers = true; // For d3d share handle, we need an EGL surface
 
     gfxIntSize pbufferSize = usePBuffers ? aSize : gfxIntSize(16, 16);
     nsRefPtr<GLContextEGL> glContext =
         GLContextEGL::CreateEGLPBufferOffscreenContext(pbufferSize, aFormat, !usePBuffers);
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -453,33 +453,21 @@ ImageLayerOGL::RenderLayer(int,
      mOGLManager->BindAndDrawQuad(program);
      gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
 #endif
   }
   GetContainer()->NotifyPaintedImage(image);
 }
 
 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->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_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
 UploadYUVToTexture(GLContext* gl, const PlanarYCbCrImage::Data& aData, 
                    GLTexture* aYTexture,
                    GLTexture* aUTexture,
                    GLTexture* aVTexture)
 {
@@ -524,23 +512,23 @@ ImageLayerOGL::AllocateTexturesYCbCr(Pla
   nsAutoPtr<PlanarYCbCrOGLBackendData> backendData(
     new PlanarYCbCrOGLBackendData);
 
   PlanarYCbCrImage::Data &data = aImage->mData;
 
   gl()->MakeCurrent();
  
   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]);
-  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]);
-  InitTexture(gl(), backendData->mTextures[2].GetTextureID(), LOCAL_GL_LUMINANCE, data.mCbCrSize);
+  SetClamping(gl(), backendData->mTextures[2].GetTextureID());
 
   UploadYUVToTexture(gl(), aImage->mData,
                      &backendData->mTextures[0],
                      &backendData->mTextures[1],
                      &backendData->mTextures[2]);
 
   backendData->mYSize = aImage->mData.mYSize;
   backendData->mCbCrSize = aImage->mData.mCbCrSize;
@@ -564,16 +552,18 @@ ImageLayerOGL::AllocateTexturesCairo(Cai
   }
 
   mozilla::gl::GLContext *gl = texture.GetGLContext();
   gl->MakeCurrent();
 
   GLuint tex = texture.GetTextureID();
   gl->fActiveTexture(LOCAL_GL_TEXTURE0);
 
+  SetClamping(gl, tex);
+
 #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
   if (sGLXLibrary.SupportsTextureFromPixmap(aImage->mSurface)) {
     if (aImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
       backendData->mLayerProgram = gl::RGBALayerProgramType;
     } else {
       backendData->mLayerProgram = gl::RGBXLayerProgramType;
     }
 
@@ -631,19 +621,19 @@ ShadowImageLayerOGL::Init(const SharedIm
     }
 
     NS_ASSERTION(mYUVTexture[0].IsAllocated() &&
                  mYUVTexture[1].IsAllocated() &&
                  mYUVTexture[2].IsAllocated(),
                  "Texture allocation failed!");
 
     gl()->MakeCurrent();
-    InitTexture(gl(), mYUVTexture[0].GetTextureID(), LOCAL_GL_LUMINANCE, mSize);
-    InitTexture(gl(), mYUVTexture[1].GetTextureID(), LOCAL_GL_LUMINANCE, mCbCrSize);
-    InitTexture(gl(), mYUVTexture[2].GetTextureID(), LOCAL_GL_LUMINANCE, mCbCrSize);
+    SetClamping(gl(), mYUVTexture[0].GetTextureID());
+    SetClamping(gl(), mYUVTexture[1].GetTextureID());
+    SetClamping(gl(), mYUVTexture[2].GetTextureID());
     return true;
   }
   return false;
 }
 
 void
 ShadowImageLayerOGL::Swap(const SharedImage& aNewFront,
                           SharedImage* aNewBack)
--- a/gfx/skia/Makefile.in
+++ b/gfx/skia/Makefile.in
@@ -328,16 +328,32 @@ CPPSRCS += \
 	SkTime_Unix.cpp \
 	SkMMapStream.cpp \
 	SkOSFile.cpp \
 	$(NULL)
 
 OS_CXXFLAGS += $(MOZ_PANGO_CFLAGS)
 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))
 EXPORTS_skia += \
 	include/config/sk_stdint.h \
 	include/ports/SkTypeface_win.h \
 	$(NULL)
 CPPSRCS += \
 	SkFontHost_win.cpp \
 	SkFontHost_sandbox_none.cpp \
--- a/gfx/thebes/gfxQtPlatform.cpp
+++ b/gfx/thebes/gfxQtPlatform.cpp
@@ -41,16 +41,18 @@
 #include <QApplication>
 #include <QDesktopWidget>
 #include <QPaintEngine>
 
 #include "gfxQtPlatform.h"
 
 #include "gfxFontconfigUtils.h"
 
+#include "mozilla/gfx/2D.h"
+
 #include "cairo.h"
 
 #include "gfxImageSurface.h"
 #include "gfxQPainterSurface.h"
 #include "nsUnicodeProperties.h"
 
 #ifdef MOZ_PANGO
 #include "gfxPangoFonts.h"
@@ -76,16 +78,17 @@
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #endif
 
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::unicode;
+using namespace mozilla::gfx;
 
 #define DEFAULT_RENDER_MODE RENDER_DIRECT
 
 static QPaintEngine::Type sDefaultQtPaintEngineType = QPaintEngine::Raster;
 gfxFontconfigUtils *gfxQtPlatform::sFontconfigUtils = nsnull;
 static cairo_user_data_key_t cairo_qt_pixmap_key;
 static void do_qt_pixmap_unref (void *data)
 {
@@ -591,8 +594,16 @@ gfxQtPlatform::GetDPI()
     return dpi <= 0 ? 96 : dpi;
 }
 
 gfxImageFormat
 gfxQtPlatform::GetOffscreenFormat()
 {
     return sOffscreenFormat;
 }
+
+bool
+gfxQtPlatform::SupportsAzure(BackendType& aBackend)
+{
+  aBackend = BACKEND_SKIA;
+  return true;
+}
+
--- a/gfx/thebes/gfxQtPlatform.h
+++ b/gfx/thebes/gfxQtPlatform.h
@@ -71,16 +71,18 @@ public:
 
     static gfxQtPlatform *GetPlatform() {
         return (gfxQtPlatform*) gfxPlatform::GetPlatform();
     }
 
     already_AddRefed<gfxASurface> CreateOffscreenSurface(const gfxIntSize& size,
                                                          gfxASurface::gfxContentType contentType);
 
+    virtual bool SupportsAzure(mozilla::gfx::BackendType& aBackend);
+
     nsresult GetFontList(nsIAtom *aLangGroup,
                          const nsACString& aGenericFamily,
                          nsTArray<nsString>& aListOfFonts);
 
     nsresult UpdateFontList();
 
     nsresult ResolveFontName(const nsAString& aFontName,
                              FontResolverCallback aCallback,
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -680,16 +680,25 @@ NS_IMETHODIMP imgRequest::OnStopDecode(i
   mImage->GetStatusTracker().RecordStopDecode(aStatus, aStatusArg);
 
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
     mImage->GetStatusTracker().SendStopDecode(iter.GetNext(), aStatus,
                                               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
   // bulletproof about its handling of decoder notifications.
   // Unfortunately, here and above we have to make some gross and
   // inappropriate use of things to get things to work without
   // completely overhauling the decoder observer interface (this will,
   // thankfully, happen in bug 505385). From imgRequest and above (for
   // the time being), OnStopDecode is just a companion to OnStopRequest
   // that signals success or failure of the _load_ (not the _decode_).
--- a/image/test/mochitest/Makefile.in
+++ b/image/test/mochitest/Makefile.in
@@ -113,15 +113,18 @@ include $(topsrcdir)/config/rules.mk
                 filter-final.svg \
                 test_svg_filter_animation.html \
                 test_xultree_animation.xhtml \
                 test_changeOfSource.html \
                 test_changeOfSource2.html \
                 test_undisplayed_iframe.html \
                 iframe.html \
                 ref-iframe.html \
+                test_net_failedtoprocess.html \
+                invalid.jpg \
+                damon.jpg \
                 $(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/invalid.jpg
@@ -0,0 +1,1 @@
+notajpg
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_net_failedtoprocess.html
@@ -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>
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2220,17 +2220,16 @@ ia64*-hpux*)
         AR='lib'
         AR_FLAGS='-NOLOGO -OUT:"$@"'
         AR_EXTRACT=
         RANLIB='echo not_ranlib'
         STRIP='echo not_strip'
         PKG_SKIP_STRIP=1
         XARGS=xargs
         DOXYGEN=:
-        GARBAGE='$(OBJDIR)/vc20.pdb $(OBJDIR)/vc40.pdb'
         ASM_SUFFIX=asm
         OBJ_SUFFIX=obj
         LIB_SUFFIX=lib
         DLL_PREFIX=
         LIB_PREFIX=
         IMPORT_LIB_SUFFIX=lib
         MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4099,17 +4099,17 @@ JS_SetPropertyById(JSContext *cx, JSObje
     return obj->setGeneric(cx, id, vp, false);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
+    assertSameCompartment(cx, obj, *vp);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
     return obj->setElement(cx, index, vp, false);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp)
 {
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -996,54 +996,35 @@ JSScript::NewScript(JSContext *cx, uint3
     JS_STATIC_ASSERT(sizeof(JSTryNoteArray) % sizeof(jsval) == 0);
     JS_STATIC_ASSERT(sizeof(GlobalSlotArray) % sizeof(jsval) == 0);
     JS_STATIC_ASSERT(sizeof(JSConstArray) % sizeof(jsval) == 0);
     if (nconsts != 0)
         size += sizeof(JSConstArray) + nconsts * sizeof(Value);
 
     size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote);
 
-    uint8_t *data = NULL;
-#if JS_SCRIPT_INLINE_DATA_LIMIT
-    if (size <= JS_SCRIPT_INLINE_DATA_LIMIT) {
-        /*
-         * Check that if inlineData is big enough to store const values, we
-         * can do that without any special alignment requirements given that
-         * the script as a GC thing is always aligned on Cell::CellSize.
-         */
-        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;
-    }
+    /*
+     * 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));
+    uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
+    if (!data)
+        return NULL;
 
     JSScript *script = js_NewGCScript(cx);
     if (!script) {
         Foreground::free_(data);
         return NULL;
     }
 
     PodZero(script);
 #ifdef JS_CRASH_DIAGNOSTICS
     script->cookie1[0] = script->cookie2[0] = JS_SCRIPT_COOKIE;
 #endif
-#if JS_SCRIPT_INLINE_DATA_LIMIT
-    if (!data)
-        data = script->inlineData;
-#endif
     script->data  = data;
     script->length = length;
     script->version = version;
     new (&script->bindings) Bindings(cx);
 
     uint8_t *cursor = data;
     if (nobjects != 0) {
         script->objectsOffset = uint8_t(cursor - data);
@@ -1320,34 +1301,24 @@ JSScript::NewScriptFromEmitter(JSContext
         (void) script->initCounts(cx);
 
     return script;
 }
 
 size_t
 JSScript::computedSizeOfData()
 {
-#if JS_SCRIPT_INLINE_DATA_LIMIT
-    if (data == inlineData)
-        return 0;
-#endif
-
     uint8_t *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
     JS_ASSERT(dataEnd >= data);
     return dataEnd - data;
 }
 
 size_t
 JSScript::sizeOfData(JSMallocSizeOfFun mallocSizeOf)
 {
-#if JS_SCRIPT_INLINE_DATA_LIMIT
-    if (data == inlineData)
-        return 0;
-#endif
-
     return mallocSizeOf(data);
 }
 
 /*
  * Nb: srcnotes are variable-length.  This function computes the number of
  * srcnote *slots*, which may be greater than the number of srcnotes.
  */
 uint32_t
@@ -1431,23 +1402,18 @@ JSScript::finalize(JSContext *cx, bool b
                 JS_ASSERT(site->firstBreakpoint() == NULL);
                 site->clearTrap(cx, NULL, NULL);
                 JS_ASSERT(getBreakpointSite(pc) == NULL);
             }
         }
         cx->free_(debug);
     }
 
-#if JS_SCRIPT_INLINE_DATA_LIMIT
-    if (data != inlineData)
-#endif
-    {
-        JS_POISON(data, 0xdb, computedSizeOfData());
-        cx->free_(data);
-    }
+    JS_POISON(data, 0xdb, computedSizeOfData());
+    cx->free_(data);
 }
 
 namespace js {
 
 static const uint32_t GSN_CACHE_THRESHOLD = 100;
 static const uint32_t GSN_CACHE_MAP_INIT_SIZE = 20;
 
 void
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -447,26 +447,16 @@ struct JSScript : public js::gc::Cell
 
     uint32_t        natoms;     /* length of atoms array */
     uint16_t        nslots;     /* vars plus maximum stack depth */
     uint16_t        staticLevel;/* static level for display maintenance */
 
     uint16_t        nClosedArgs; /* number of args 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 */
     JSAtom          **atoms;    /* maps immediate index to literal struct */
   private:
     size_t          useCount;  /* Number of times the script has been called
                                  * or has had backedges taken. Reset if the
                                  * script's JIT code is forcibly discarded. */
   public:
     js::Bindings    bindings;   /* names of top-level variables in this script
--- a/js/xpconnect/public/Makefile.in
+++ b/js/xpconnect/public/Makefile.in
@@ -44,11 +44,12 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= xpconnect
 
 EXPORTS		= \
 		nsAXPCNativeCallContext.h \
 		xpc_map_end.h \
 		nsAutoJSValHolder.h \
+		nsTArrayHelpers.h \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/public/nsTArrayHelpers.h
@@ -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__ */
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -140,17 +140,17 @@ pref("browser.display.history.maxresults
 pref("browser.display.remotetabs.timeout", 10);
 
 /* session history */
 pref("browser.sessionhistory.max_total_viewers", 1);
 pref("browser.sessionhistory.max_entries", 50);
 
 /* session store */
 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.interval", 10000); // milliseconds
 pref("browser.sessionstore.max_tabs_undo", 1);
 pref("browser.sessionstore.max_resumed_crashes", 1);
 pref("browser.sessionstore.recent_crashes", 0);
 
 /* these should help performance */
 pref("mozilla.widget.force-24bpp", true);
--- a/mobile/android/base/Favicons.java
+++ b/mobile/android/base/Favicons.java
@@ -290,27 +290,17 @@ public class Favicons {
                 int offset = 0;
                 while ((pos = contentStream.read(bytes, offset, length - offset)) > 0)
                     offset += pos;
                 if (length == offset) {
                     byteStream = new ByteArrayInputStream(bytes);
                     image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src");
                 }
             } catch (Exception e) {
-                // Trying to read icons from nested jar files will fail
-                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);
-                }
+                Log.e(LOGTAG, "Error downloading favicon", e);
             } finally {
                 if (urlConnection != null && urlConnection instanceof HttpURLConnection) {
                     HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
                     httpConnection.disconnect();
                 }
 
                 try {
                     if (contentStream != null)
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -448,20 +448,20 @@ abstract public class GeckoApp
             bookmark.setIcon(R.drawable.ic_menu_bookmark_remove);
         } else {
             bookmark.setChecked(false);
             bookmark.setIcon(R.drawable.ic_menu_bookmark_add);
         }
 
         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();
         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
         saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
                                tab.getContentType().equals("application/vnd.mozilla.xul+xml")));
 
         charEncoding.setVisible(GeckoPreferences.getCharEncodingState());
 
         return true;
@@ -1672,19 +1672,20 @@ abstract public class GeckoApp
 
         mBrowserToolbar.setTitle(mLastTitle);
 
         String passedUri = null;
         String uri = getURIFromIntent(intent);
         if (uri != null && uri.length() > 0)
             passedUri = mLastTitle = uri;
 
+        mRestoreSession |= getProfile().shouldRestoreSession();
         if (passedUri == null || passedUri.equals("about:home")) {
             // show about:home if we aren't restoring previous session
-            if (! getProfile().hasSession()) {
+            if (!mRestoreSession) {
                 mBrowserToolbar.updateTabCount(1);
                 showAboutHome();
             }
         } else {
             mBrowserToolbar.updateTabCount(1);
         }
 
         Uri data = intent.getData();
@@ -1737,19 +1738,25 @@ abstract public class GeckoApp
              * and zoom a cached screenshot of the previous page. This call will return null if
              * there is no cached screenshot; in that case, we have no choice but to display a
              * checkerboard.
              *
              * TODO: Fall back to a built-in screenshot of the Fennec Start page for a nice first-
              * run experience, perhaps?
              */
             mLayerController = new LayerController(this);
+            View v = mLayerController.getView();
+
             mPlaceholderLayerClient = new PlaceholderLayerClient(mLayerController, mLastViewport);
-
-            mGeckoLayout.addView(mLayerController.getView(), 0);
+            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);
 
         mDoorHangerPopup = new DoorHangerPopup(this);
         mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
 
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - UI almost up");
--- a/mobile/android/base/GeckoJarReader.java
+++ b/mobile/android/base/GeckoJarReader.java
@@ -21,21 +21,22 @@ import android.util.Log;
  */
 public class GeckoJarReader {
     static private String LOGTAG = "GeckoJarReader";
 
     static public InputStream getStream(String url) {
         Stack<String> jarUrls = parseUrl(url);
         ZipInputStream inputStream = null;
 
+        ZipFile zip = null;
         try {
             // Load the initial jar file as a zip
             URL fileUrl = new URL(jarUrls.pop());
             File file = new File(fileUrl.getPath());
-            ZipFile zip = new ZipFile(file);
+            zip = new ZipFile(file);
             ZipEntry entry = null;
 
             // loop through children jar files until we reach the innermost one
             while (jarUrls.peek() != null) {
                 String fileName = jarUrls.pop();
 
                 if (inputStream != null) {
                     entry = getEntryFromStream(inputStream, fileName);
@@ -58,16 +59,24 @@ public class GeckoJarReader {
                 }
             }
         } catch (EmptyStackException ex) {
             Log.d(LOGTAG, "Reached Jar reader reached end of stack");
         } catch (IOException ex) {
             Log.e(LOGTAG, "Exception ", ex);
         } catch (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;
     }
 
     /* Searches through a ZipInputStream for an entry with a given name */
     static private ZipEntry getEntryFromStream(ZipInputStream zipStream, String entryName) {
         ZipEntry entry = null;
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -28,16 +28,19 @@ public final class GeckoProfile {
 
     private static HashMap<String, GeckoProfile> sProfileCache = new HashMap<String, GeckoProfile>();
 
     private final Context mContext;
     private final String mName;
     private File mMozDir;
     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) {
         return get(context, null);
     }
 
     public static GeckoProfile get(Context context, String profileName) {
         if (context == null) {
             throw new IllegalArgumentException("context must be non-null");
         }
@@ -75,22 +78,29 @@ public final class GeckoProfile {
                 Log.d(LOGTAG, "Found profile dir: " + mDir.getAbsolutePath());
             }
         } catch (IOException ioe) {
             Log.e(LOGTAG, "Error getting profile dir", ioe);
         }
         return mDir;
     }
 
-    public boolean hasSession() {
+    public boolean shouldRestoreSession() {
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start check sessionstore.js exists");
         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");
-        return hasSession;
+        return shouldRestore;
     }
 
     public String readSessionFile(boolean geckoReady) {
         File dir = getDir();
         if (dir == null) {
             return null;
         }
 
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -92,16 +92,17 @@ public class LocalBrowserDB implements B
 
     private static final String[] DEFAULT_BOOKMARK_COLUMNS =
             new String[] { Bookmarks._ID,
                            Bookmarks.GUID,
                            Bookmarks.URL,
                            Bookmarks.TITLE,
                            Bookmarks.TYPE,
                            Bookmarks.PARENT,
+                           Bookmarks.KEYWORD,
                            Bookmarks.FAVICON }; 
 
     public LocalBrowserDB(String profile) {
         mProfile = profile;
         mMobileFolderId = -1;
         mDesktopBookmarksExist = null;
 
         mBookmarksUriWithProfile = appendProfile(Bookmarks.CONTENT_URI);
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -362,16 +362,17 @@ public class GeckoLayerClient implements
             // At this point, we have just switched to displaying a different document than we
             // we previously displaying. This means we need to abort any panning/zooming animations
             // that are in progress and send an updated display port request to browser.js as soon
             // as possible. We accomplish this by passing true to abortPanZoomAnimation, which
             // sends the request after aborting the animation. The display port request is actually
             // 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.
             mLayerController.abortPanZoomAnimation();
+            mLayerController.getView().setPaintState(LayerView.PAINT_BEFORE_FIRST);
         }
     }
 
     /** This function is invoked by Gecko via JNI; be careful when modifying signature.
       * The compositor invokes this function whenever it determines that the page size
       * has changed (based on the information it gets from layout). If setFirstPaintViewport
       * is invoked on a frame, then this function will not be. For any given frame, this
       * function will be invoked before syncViewportInfo.
--- a/mobile/android/base/gfx/LayerRenderer.java
+++ b/mobile/android/base/gfx/LayerRenderer.java
@@ -675,11 +675,21 @@ public class LayerRenderer implements GL
                 synchronized (pixelBuffer) {
                     pixelBuffer.position(0);
                     GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(),
                                         (int)mScreenContext.viewport.height(), GLES20.GL_RGBA,
                                         GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
                     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);
+            }
         }
     }
 }
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -33,16 +33,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko.gfx;
 
+import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoInputConnection;
 import org.mozilla.gecko.gfx.FloatSize;
 import org.mozilla.gecko.gfx.InputConnectionHandler;
 import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
 import android.content.Context;
 import android.opengl.GLSurfaceView;
 import android.view.View;
@@ -72,16 +73,24 @@ public class LayerView extends FlexibleG
     private LayerRenderer mRenderer;
     private GestureDetector mGestureDetector;
     private SimpleScaleGestureDetector mScaleGestureDetector;
     private long mRenderTime;
     private boolean mRenderTimeReset;
     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 */
     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) {
         super(context);
 
         mContext = context;
         mController = controller;
         mRenderer = new LayerRenderer(this);
@@ -232,10 +241,23 @@ public class LayerView extends FlexibleG
     public void setLayerRenderer(LayerRenderer renderer) {
         mRenderer = renderer;
         setRenderer(mRenderer);
     }
 
     public LayerRenderer getLayerRenderer() {
         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;
+    }
 }
 
--- a/mobile/android/base/gfx/PlaceholderLayerClient.java
+++ b/mobile/android/base/gfx/PlaceholderLayerClient.java
@@ -74,45 +74,26 @@ public class PlaceholderLayerClient {
                 mViewportUnknown = false;
             } catch (JSONException e) {
                 Log.e(LOGTAG, "Error parsing saved viewport!");
                 mViewport = new ViewportMetrics();
             }
         } else {
             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() {
         if (mBuffer != null) {
             GeckoAppShell.freeDirectBuffer(mBuffer);
             mBuffer = null;
         }
     }
 
-    boolean loadScreenshot() {
+    public boolean loadScreenshot() {
         if (GeckoApp.mAppContext.mLastScreen == null)
             return false;
 
         Bitmap bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(GeckoApp.mAppContext.mLastScreen));
         if (bitmap == null)
             return false;
 
         Bitmap.Config config = bitmap.getConfig();
@@ -126,11 +107,23 @@ public class PlaceholderLayerClient {
 
         bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
 
         if (mViewportUnknown) {
             mViewport.setPageSize(new FloatSize(mWidth, mHeight));
             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;
     }
 }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -322,17 +322,17 @@ var BrowserApp = {
     sendMessageToJava({
       gecko: {
         "type": "Checkerboard:Toggle",
         "value": Services.prefs.getBoolPref("gfx.show_checkerboard_pattern")
       }
     });
 
     if (this.isAppUpdated())
-      this.onUpdate();
+      this.onAppUpdated();
   },
 
   isAppUpdated: function() {
     let savedmstone = null;
     try {
       savedmstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone");
     } catch (e) {
     }
@@ -1450,19 +1450,20 @@ let gScreenHeight = 1;
 
 function Tab(aURL, aParams) {
   this.browser = null;
   this.id = 0;
   this.showProgress = true;
   this.create(aURL, aParams);
   this._zoom = 1.0;
   this.userScrollPos = { x: 0, y: 0 };
-  this._pluginCount = 0;
-  this._pluginOverlayShowing = false;
   this.contentDocumentIsDisplayed = true;
+  this.clickToPlayPluginDoorhangerShown = false;
+  this.clickToPlayPluginsActivated = false;
+  this.loadEventProcessed = false;
 }
 
 Tab.prototype = {
   create: function(aURL, aParams) {
     if (this.browser)
       return;
 
     aParams = aParams || {};
@@ -1506,25 +1507,24 @@ Tab.prototype = {
 
     let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
                 Ci.nsIWebProgress.NOTIFY_LOCATION |
                 Ci.nsIWebProgress.NOTIFY_SECURITY;
     this.browser.addProgressListener(this, flags);
     this.browser.sessionHistory.addSHistoryListener(this);
 
     this.browser.addEventListener("DOMContentLoaded", this, true);
+    this.browser.addEventListener("load", this, true);
     this.browser.addEventListener("DOMLinkAdded", this, true);
     this.browser.addEventListener("DOMTitleChanged", this, true);
     this.browser.addEventListener("DOMWindowClose", this, true);
     this.browser.addEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.addEventListener("scroll", this, true);
     this.browser.addEventListener("MozScrolledAreaChanged", 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);
 
     if (!aParams.delayLoad) {
       let flags = "flags" in aParams ? aParams.flags : Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
       let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null;
       let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
       let charset = "charset" in aParams ? aParams.charset : null;
@@ -1553,25 +1553,24 @@ Tab.prototype = {
     if (!this.browser)
       return;
 
     this.browser.controllers.contentWindow
       .removeController(this.overscrollController);
 
     this.browser.removeProgressListener(this);
     this.browser.removeEventListener("DOMContentLoaded", this, true);
+    this.browser.removeEventListener("load", this, true);
     this.browser.removeEventListener("DOMLinkAdded", this, true);
     this.browser.removeEventListener("DOMTitleChanged", this, true);
     this.browser.removeEventListener("DOMWindowClose", this, true);
     this.browser.removeEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.removeEventListener("scroll", this, true);
     this.browser.removeEventListener("PluginClickToPlay", 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");
 
     // Make sure the previously selected panel remains selected. The selected panel of a deck is
     // not stable when panels are removed.
     let selectedPanel = BrowserApp.deck.selectedPanel;
     BrowserApp.deck.removeChild(this.browser);
     BrowserApp.deck.selectedPanel = selectedPanel;
@@ -1758,23 +1757,35 @@ Tab.prototype = {
         // pages have any privilege themselves.
         if (/^about:/.test(target.documentURI)) {
           this.browser.addEventListener("click", ErrorPageEventHandler, false);
           this.browser.addEventListener("pagehide", function listener() {
             this.browser.removeEventListener("click", ErrorPageEventHandler, false);
             this.browser.removeEventListener("pagehide", listener, true);
           }.bind(this), true);
         }
-
-        // Show a plugin doorhanger if there are plugins on the page but no
-        // clickable overlays showing (this doesn't work on pages loaded after
-        // back/forward navigation - see bug 719875)
-        if (this._pluginCount && !this._pluginOverlayShowing)
+        break;
+      }
+
+      case "load": {
+        this.loadEventProcessed = true;
+        // Show a plugin doorhanger if there are no clickable overlays showing
+        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);
-
         break;
       }
 
       case "DOMLinkAdded": {
         let target = aEvent.originalTarget;
         if (!target.href || target.disabled)
           return;
 
@@ -1872,49 +1883,46 @@ Tab.prototype = {
         if (aEvent.originalTarget != this.browser.contentDocument)
           return;
 
         this.sendViewportUpdate(true);
         break;
       }
 
       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 overlay = plugin.ownerDocument.getAnonymousElementByAttribute(plugin, "class", "mainBox");
-        if (!overlay)
+
+        if (this.clickToPlayPluginsActivated) {
+          PluginHelper.playPlugin(plugin);
           return;
+        }
 
         // If the overlay is too small, hide the overlay and act like this
         // is a hidden plugin object
-        if (PluginHelper.isTooSmall(plugin, overlay)) {
-          overlay.style.visibility = "hidden";
+        let overlay = plugin.ownerDocument.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+        if (!overlay || PluginHelper.isTooSmall(plugin, overlay)) {
+          if (overlay)
+            overlay.style.visibility = "hidden";
+          if (this.loadEventProcessed && !this.clickToPlayPluginDoorhangerShown)
+            PluginHelper.showDoorHanger(this);
           return;
         }
 
         // Add click to play listener to the overlay
-        overlay.addEventListener("click", (function(event) {
-          // Play all the plugin objects when the user clicks on one
-          PluginHelper.playAllPlugins(this, event);
-        }).bind(this), true);
-
-        this._pluginOverlayShowing = true;
-        break;
-      }
-
-      case "pagehide": {
-        // Check to make sure it's top-level pagehide
-        if (aEvent.target.defaultView == this.browser.contentWindow) {
-          // Reset plugin state when we leave the page
-          this._pluginCount = 0;
-          this._pluginOverlayShowing = false;
-        }
+        overlay.addEventListener("click", function(e) {
+          if (e) {
+            if (!e.isTrusted)
+              return;
+            e.preventDefault();
+          }
+          let win = e.target.ownerDocument.defaultView.top;
+          let tab = BrowserApp.getTabForWindow(win);
+          tab.clickToPlayPluginsActivated = true;
+          PluginHelper.playAllPlugins(win);
+        }, true);
         break;
       }
     }
   },
 
   onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
     let contentWin = aWebProgress.DOMWindow;
     if (contentWin != contentWin.top)
@@ -1964,16 +1972,21 @@ Tab.prototype = {
     let uri = browser.currentURI.spec;
     let documentURI = "";
     let contentType = "";
     if (browser.contentDocument) {
       documentURI = browser.contentDocument.documentURIObject.spec;
       contentType = browser.contentDocument.contentType;
     }
 
+    // Reset state of click-to-play plugin notifications.
+    this.clickToPlayPluginDoorhangerShown = false;
+    this.clickToPlayPluginsActivated = false;
+    this.loadEventProcessed = false;
+
     let message = {
       gecko: {
         type: "Content:LocationChange",
         tabID: this.id,
         uri: uri,
         documentURI: documentURI,
         contentType: contentType
       }
@@ -3849,69 +3862,50 @@ var ClipboardHelper = {
       }
       return false;
     }
   }
 };
 
 var PluginHelper = {
   showDoorHanger: function(aTab) {
+    aTab.clickToPlayPluginDoorhangerShown = true;
     let message = Strings.browser.GetStringFromName("clickToPlayPlugins.message");
     let buttons = [
       {
         label: Strings.browser.GetStringFromName("clickToPlayPlugins.yes"),
         callback: function() {
-          PluginHelper.playAllPlugins(aTab);
+          PluginHelper.playAllPlugins(aTab.browser.contentWindow);
         }
       },
       {
         label: Strings.browser.GetStringFromName("clickToPlayPlugins.no"),
         callback: function() {
           // Do nothing
         }
       }
     ]
     NativeWindow.doorhanger.show(message, "ask-to-play-plugins", buttons, aTab.id);
   },
 
-  playAllPlugins: function(aTab, aEvent) {
-    if (aEvent) {
-      if (!aEvent.isTrusted)
-        return;
-      aEvent.preventDefault();
-    }
-
-    this._findAndPlayAllPlugins(aTab.browser.contentWindow);
+  playAllPlugins: function(aContentWindow) {
+    let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+    // XXX not sure if we should enable plugins for the parent documents...
+    let plugins = cwu.plugins;
+    if (!plugins || !plugins.length)
+      return;
+
+    plugins.forEach(this.playPlugin);
   },
 
-  // Helper function that recurses through sub-frames to find all plugin objects
-  _findAndPlayAllPlugins: function _findAndPlayAllPlugins(aWindow) {
-    let embeds = aWindow.document.getElementsByTagName("embed");
-    for (let i = 0; i < embeds.length; i++) {
-      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);
+  playPlugin: function(plugin) {
+    let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+    if (!objLoadingContent.activated)
+      objLoadingContent.playPlugin();
   },
 
   getPluginPreference: function getPluginPreference() {
     let pluginDisable = Services.prefs.getBoolPref("plugin.disable");
     if (pluginDisable)
       return "0";
 
     let clickToPlay = Services.prefs.getBoolPref("plugins.click_to_play");
--- a/mobile/android/themes/core/aboutAddons.css
+++ b/mobile/android/themes/core/aboutAddons.css
@@ -200,46 +200,50 @@ setting {
 setting > vbox {
   -moz-box-flex: 1;
 }
 
 .preferences-title {
   font-weight: bold;
 }
 
+.preferences-description {
+  margin-top: 4px;
+}
+
 .preferences-description:empty {
   display: none;
 }
 
 /* Put setting textboxes on a separate row */
 setting[type="integer"],
 setting[type="string"] {
   -moz-box-align: stretch;
   -moz-box-orient: vertical;
 }
 
 .preferences-alignment > textbox {
-  margin-top: 12px;
+  margin: 12px 0 0 0;
   font-size: 22px !important;
 }
 
 checkbox {
   -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 {
-  border: 2px transparent;
-  -moz-border-top-colors: -moz-initial;
-  -moz-border-right-colors: -moz-initial;
-  -moz-border-bottom-colors: -moz-initial;
-  -moz-border-left-colors: -moz-initial;
-  -moz-box-sizing: border-box;
+  background: url("chrome://browser/skin/images/checkbox_unchecked.png") no-repeat 50% 50%;
+  border: 0 transparent;
   width: 48px;
   height: 48px;
-  background: url("chrome://browser/skin/images/checkbox_unchecked.png") no-repeat 50% 50%;
 }
 
 setting:active checkbox > .checkbox-spacer-box > .checkbox-check {
   background-image: url("chrome://browser/skin/images/checkbox_unchecked_pressed.png");
 }
 
 checkbox[disabled="true"] > .checkbox-spacer-box > .checkbox-check {
   background-image: url("chrome://browser/skin/images/checkbox_unchecked_disabled.png");
@@ -252,16 +256,72 @@ checkbox[checked="true"] > .checkbox-spa
 setting:active checkbox[checked="true"] > .checkbox-spacer-box > .checkbox-check {
   background-image: url("chrome://browser/skin/images/checkbox_checked_pressed.png");
 }
 
 checkbox[checked="true"][disabled="true"] > .checkbox-spacer-box > .checkbox-check {
   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 */
 
 settings {
   -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#settings");
 }
 
 setting {
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -804,21 +804,33 @@ pref("network.http.redirection-limit", 2
 // NOTE: separate values with comma+space (", "): see bug 576033
 pref("network.http.accept-encoding", "gzip, deflate");
 
 pref("network.http.pipelining"      , false);
 pref("network.http.pipelining.ssl"  , false); // disable pipelining over SSL
 pref("network.http.proxy.pipelining", false);
 
 // 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
 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,
 // since inappropriate marking can easily overwhelm bandwidth reservations
 // for certain services (i.e. EF for VoIP, AF4x for interactive video,
 // AF3x for broadcast/streaming video, etc)
 
 // default value for HTTP
 // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
 // Section 4.8 "High-Throughput Data Service Class"
--- a/netwerk/base/public/nsIRequest.idl
+++ b/netwerk/base/public/nsIRequest.idl
@@ -155,16 +155,23 @@ interface nsIRequest : nsISupports
      */
     const unsigned long LOAD_BACKGROUND = 1 << 0; 
 
     /**************************************************************************
      * 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
      * cached content from being used to satisfy this request.
      */
     const unsigned long INHIBIT_CACHING = 1 << 7;
 
     /**
      * This flag prevents caching on disk (or other persistent media), which
      * may be needed to preserve privacy.  For HTTPS, this flag is set auto-
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -1015,30 +1015,116 @@ public:
                      "Sync OpenCacheEntry() posted to background thread!");
 
         nsCacheServiceAutoLock lock;
         rv = nsCacheService::gService->ProcessRequest(mRequest,
                                                       false,
                                                       nsnull);
 
         // 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;
 
         return NS_OK;
     }
 
 protected:
     virtual ~nsProcessRequestEvent() {}
 
 private:
     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 *   nsCacheService::gService = nsnull;
 
 static nsCOMPtr<nsIMemoryReporter> MemoryCacheReporter = nsnull;
 
 NS_THREADSAFE_MEMORY_REPORTER_IMPLEMENT(NetworkMemoryCache,
     "explicit/network-memory-cache",
@@ -1344,16 +1430,32 @@ nsCacheService::IsStorageEnabledForPolic
     if (gService == nsnull) return NS_ERROR_NOT_AVAILABLE;
     nsCacheServiceAutoLock lock;
 
     *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy);
     return NS_OK;
 }
 
 
+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          
 nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy  storagePolicy)
 {
     if (gService->mEnableMemoryDevice &&
         (storagePolicy == nsICache::STORE_ANYWHERE ||
          storagePolicy == nsICache::STORE_IN_MEMORY)) {
         return true;
     }
@@ -1657,21 +1759,23 @@ nsCacheService::ProcessRequest(nsCacheRe
         rv = ActivateEntry(request, &entry, &doomedEntry);  // get the entry for this request
         if (NS_FAILED(rv))  break;
 
         while(1) { // Request Access loop
             NS_ASSERTION(entry, "no entry in Request Access loop!");
             // entry->RequestAccess queues request on entry
             rv = entry->RequestAccess(request, &accessGranted);
             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->mListener) {
+                    // async exits - validate, doom, or close will resume
+                    return rv;
+                }
+
                 // XXX this is probably wrong...
                 Unlock();
                 rv = request->WaitForValidation();
                 Lock();
             }
 
             PR_REMOVE_AND_INIT_LINK(request);
             if (NS_FAILED(rv)) break;   // non-blocking mode returns WAIT_FOR_VALIDATION error
@@ -1768,17 +1872,18 @@ nsCacheService::OpenCacheEntry(nsCacheSe
             delete request;
     }
     else {
 
         nsCacheServiceAutoLock lock;
         rv = gService->ProcessRequest(request, true, result);
 
         // 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;
     }
 
     return rv;
 }
 
 
 nsresult
--- a/netwerk/cache/nsCacheService.h
+++ b/netwerk/cache/nsCacheService.h
@@ -94,16 +94,20 @@ public:
                                     nsICacheListener *         listener,
                                     nsICacheEntryDescriptor ** result);
 
     static nsresult  EvictEntriesForSession(nsCacheSession *   session);
 
     static nsresult  IsStorageEnabledForPolicy(nsCacheStoragePolicy  storagePolicy,
                                                bool *              result);
 
+    static nsresult  DoomEntry(nsCacheSession   *session,
+                               const nsACString &key,
+                               nsICacheListener *listener);
+
     /**
      * Methods called by nsCacheEntryDescriptor
      */
 
     static void      CloseDescriptor(nsCacheEntryDescriptor * descriptor);
 
     static nsresult  GetFileForEntry(nsCacheEntry *         entry,
                                      nsIFile **             result);
@@ -193,16 +197,17 @@ public:
 
 private:
     friend class nsCacheServiceAutoLock;
     friend class nsOfflineCacheDevice;
     friend class nsProcessRequestEvent;
     friend class nsSetSmartSizeEvent;
     friend class nsBlockOnCacheThreadEvent;
     friend class nsSetDiskSmartSizeCallback;
+    friend class nsDoomEvent;
 
     /**
      * Internal Methods
      */
 
     static void      Lock();
     static void      Unlock();
 
--- a/netwerk/cache/nsCacheSession.cpp
+++ b/netwerk/cache/nsCacheSession.cpp
@@ -97,23 +97,24 @@ nsCacheSession::OpenCacheEntry(const nsA
                                          nsnull, // no listener
                                          result);
     return rv;
 }
 
 
 NS_IMETHODIMP nsCacheSession::AsyncOpenCacheEntry(const nsACString & key,
                                                   nsCacheAccessMode accessRequested,
-                                                  nsICacheListener *listener)
+                                                  nsICacheListener *listener,
+                                                  bool              noWait)
 {
     nsresult rv;
     rv = nsCacheService::OpenCacheEntry(this,
                                         key,
                                         accessRequested,
-                                        nsICache::BLOCKING,
+                                        !noWait,
                                         listener,
                                         nsnull); // no result
 
     if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) rv = NS_OK;
     return rv;
 }
 
 NS_IMETHODIMP nsCacheSession::EvictEntries()
@@ -122,8 +123,14 @@ NS_IMETHODIMP nsCacheSession::EvictEntri
 }
 
 
 NS_IMETHODIMP nsCacheSession::IsStorageEnabled(bool *result)
 {
 
     return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result);
 }
+
+NS_IMETHODIMP nsCacheSession::DoomEntry(const nsACString &key,
+                                        nsICacheListener *listener)
+{
+    return nsCacheService::DoomEntry(this, key, listener);
+}
--- a/netwerk/cache/nsICacheListener.idl
+++ b/netwerk/cache/nsICacheListener.idl
@@ -42,20 +42,27 @@
 
 
 #include "nsISupports.idl"
 #include "nsICache.idl"
 
 
 interface nsICacheEntryDescriptor;
 
-[scriptable, uuid(638c3848-778b-4851-8ff3-9400f65b8773)]
+[scriptable, uuid(8eadf2ed-8cac-4961-8025-6da6d5827e74)]
 interface nsICacheListener : nsISupports
 {
     /**
      * Called when the requested access (or appropriate subset) is
      * acquired.  The status parameter equals NS_OK on success.
      * See nsICacheService.idl for accessGranted values.
      */
     void onCacheEntryAvailable(in nsICacheEntryDescriptor descriptor,
                                in nsCacheAccessMode       accessGranted,
                                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);
 };
--- a/netwerk/cache/nsICacheSession.idl
+++ b/netwerk/cache/nsICacheSession.idl
@@ -41,17 +41,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsICache.idl"
 
 interface nsICacheEntryDescriptor;
 interface nsICacheListener;
 
-[scriptable, uuid(ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e)]
+[scriptable, uuid(1dd7708c-de48-4ffe-b5aa-cd218c762887)]
 interface nsICacheSession : nsISupports
 {
     /**
      * Expired entries will be doomed or evicted if this attribute is set to
      * true.  If false, expired entries will be returned (useful for offline-
      * mode and clients, such as HTTP, that can update the valid lifetime of
      * cached content).  This attribute defaults to true.
      */
@@ -71,27 +71,38 @@ interface nsICacheSession : nsISupports
      * return NS_ERROR_CACHE_WAIT_FOR_VALIDATION rather than block when another
      * descriptor has been given WRITE access but hasn't validated the entry yet.
      */
     nsICacheEntryDescriptor openCacheEntry(in ACString          key,
                                            in nsCacheAccessMode accessRequested,
                                            in boolean           blockingMode);
 
     /**
-     * Asynchronous cache access. Does not block the calling thread.
-     * Instead, the listener will be notified when the descriptor is
-     * available.
+     * Asynchronous cache access. Does not block the calling thread. Instead,
+     * the listener will be notified when the descriptor is available. If
+     * '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,
-                             in nsCacheAccessMode accessRequested,
-                             in nsICacheListener  listener);
+    void asyncOpenCacheEntry(in ACString           key,
+                             in nsCacheAccessMode  accessRequested,
+                             in nsICacheListener   listener,
+                             [optional] in boolean noWait);
 
     /**
      * Evict all entries for this session's clientID according to its storagePolicy.
      */
     void evictEntries();
     
     /**
      * Return whether any of the cache devices implied by the session storage policy
      * are currently enabled for instantiation if they don't already exist.
      */
     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);
 };
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -2091,16 +2091,24 @@ nsFtpState::OnCacheEntryAvailable(nsICac
 
     Connect();
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
+nsFtpState::OnCacheEntryDoomed(nsresult status)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
 nsFtpState::OnStartRequest(nsIRequest *request, nsISupports *context)
 {
     mStorReplyReceived = false;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFtpState::OnStopRequest(nsIRequest *request, nsISupports *context,
@@ -2283,17 +2291,17 @@ nsFtpState::CheckCache()
     nsresult rv = session->OpenCacheEntry(key, accessReq, false,
                                           getter_AddRefs(mCacheEntry));
     if (NS_SUCCEEDED(rv) && mCacheEntry) {
         mDoomCache = true;
         return false;  // great, we're ready to proceed!
     }
 
     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 false;
 }
 
 nsresult
 nsFtpState::ConvertUTF8PathToCharset(const nsACString &aCharset)
--- a/netwerk/protocol/http/SpdySession.cpp
+++ b/netwerk/protocol/http/SpdySession.cpp
@@ -2029,16 +2029,40 @@ SpdySession::TakeHttpConnection()
 nsISocketTransport *
 SpdySession::Transport()
 {
   if (!mConnection)
     return nsnull;
   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
 // We can be sure of this because SpdySession is only constructed in
 // nsHttpConnection and is never passed out of that object
 //-----------------------------------------------------------------------------
 
 void
 SpdySession::SetConnection(nsAHttpConnection *)
@@ -2059,27 +2083,33 @@ void
 SpdySession::SetSSLConnectFailed()
 {
   NS_ABORT_IF_FALSE(false, "SpdySession::SetSSLConnectFailed()");
 }
 
 bool
 SpdySession::IsDone()
 {
-  NS_ABORT_IF_FALSE(false, "SpdySession::IsDone()");
-  return false;
+  return !mStreamTransactionHash.Count();
 }
 
 nsresult
 SpdySession::Status()
 {
   NS_ABORT_IF_FALSE(false, "SpdySession::Status()");
   return NS_ERROR_UNEXPECTED;
 }
 
+PRUint8
+SpdySession::Caps()
+{
+  NS_ABORT_IF_FALSE(false, "SpdySession::Caps()");
+  return 0;
+}
+
 PRUint32
 SpdySession::Available()
 {
   NS_ABORT_IF_FALSE(false, "SpdySession::Available()");
   return 0;
 }
 
 nsHttpRequestHead *
@@ -2127,16 +2157,52 @@ SpdySession::TakeSubTransactions(
     return NS_ERROR_ALREADY_OPENED;
 
   LOG3(("   taking %d\n", mStreamTransactionHash.Count()));
 
   mStreamTransactionHash.Enumerate(TakeStream, &outTransactions);
   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
 //-----------------------------------------------------------------------------
 
 nsAHttpConnection *
 SpdySession::Connection()
 {
   NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
@@ -2175,16 +2241,23 @@ SpdySession::IsReused()
 
 nsresult
 SpdySession::PushBack(const char *buf, PRUint32 len)
 {
   return mConnection->PushBack(buf, len);
 }
 
 bool
+SpdySession::IsProxyConnectInProgress()
+{
+    NS_ABORT_IF_FALSE(mConnection, "no connection");
+    return mConnection->IsProxyConnectInProgress();
+}
+
+bool
 SpdySession::LastTransactionExpectedNoContent()
 {
   return mConnection->LastTransactionExpectedNoContent();
 }
 
 void
 SpdySession::SetLastTransactionExpectedNoContent(bool val)
 {
--- a/netwerk/protocol/http/SpdySession.h
+++ b/netwerk/protocol/http/SpdySession.h
@@ -70,20 +70,19 @@ public:
   NS_DECL_NSAHTTPSEGMENTREADER
   NS_DECL_NSAHTTPSEGMENTWRITER
 
   SpdySession(nsAHttpTransaction *, nsISocketTransport *, PRInt32);
   ~SpdySession();
 
   bool AddStream(nsAHttpTransaction *, PRInt32);
   bool CanReuse() { return !mShouldGoAway && !mClosed; }
-  void DontReuse();
   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);
   
   // Idle time represents time since "goodput".. e.g. a data or header frame
   PRIntervalTime IdleTime();
 
   PRUint32 RegisterStreamID(SpdyStream *);
 
   const static PRUint8 kFlag_Control   = 0x80;
--- a/netwerk/protocol/http/nsAHttpConnection.h
+++ b/netwerk/protocol/http/nsAHttpConnection.h
@@ -34,18 +34,18 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsAHttpConnection_h__
 #define nsAHttpConnection_h__
 
 #include "nsISupports.h"
+#include "nsAHttpTransaction.h"
 
-class nsAHttpTransaction;
 class nsHttpRequestHead;
 class nsHttpResponseHead;
 class nsHttpConnectionInfo;
 class nsHttpConnection;
 class nsISocketTransport;
 class nsIAsyncInputStream;
 class nsIAsyncOutputStream;
 
@@ -115,48 +115,67 @@ public:
 
     // called by a transaction to get the security info from the socket.
     virtual void GetSecurityInfo(nsISupports **) = 0;
 
     // called by a transaction to determine whether or not the connection is
     // persistent... important in determining the end of a response.
     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 void   DontReuse() = 0;
+
     // called by a transaction when the transaction reads more from the socket
     // than it should have (eg. containing part of the next pipelined response).
     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
     // the same connection and work around buggy servers.
     virtual bool LastTransactionExpectedNoContent() = 0;
     virtual void   SetLastTransactionExpectedNoContent(bool) = 0;
 
     // Transfer the base http connection object along with a
     // reference to it to the caller.
     virtual nsHttpConnection *TakeHttpConnection() = 0;
 
     // Get the nsISocketTransport used by the connection without changing
     //  references or ownership.
     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 \
     nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, bool *reset); \
     nsresult ResumeSend(); \
     nsresult ResumeRecv(); \
     void CloseTransaction(nsAHttpTransaction *, nsresult); \
     void GetConnectionInfo(nsHttpConnectionInfo **); \
     nsresult TakeTransport(nsISocketTransport **,    \
                            nsIAsyncInputStream **,   \
                            nsIAsyncOutputStream **); \
     void GetSecurityInfo(nsISupports **); \
     bool IsPersistent(); \
     bool IsReused(); \
+    void DontReuse();  \
     nsresult PushBack(const char *, PRUint32); \
+    bool IsProxyConnectInProgress(); \
     bool LastTransactionExpectedNoContent(); \
-    void   SetLastTransactionExpectedNoContent(bool); \
+    void SetLastTransactionExpectedNoContent(bool); \
     nsHttpConnection *TakeHttpConnection(); \
-    nsISocketTransport *Transport();
+    nsISocketTransport *Transport();        \
+    PRUint32 CancelPipeline(nsresult originalReason);   \
+    nsAHttpTransaction::Classifier Classification();    \
+    void Classify(nsAHttpTransaction::Classifier);
 
 #endif // nsAHttpConnection_h__
--- a/netwerk/protocol/http/nsAHttpTransaction.h
+++ b/netwerk/protocol/http/nsAHttpTransaction.h
@@ -43,45 +43,49 @@
 
 class nsAHttpConnection;
 class nsAHttpSegmentReader;
 class nsAHttpSegmentWriter;
 class nsIInterfaceRequestor;
 class nsIEventTarget;
 class nsITransport;
 class nsHttpRequestHead;
+class nsHttpPipeline;
 
 //----------------------------------------------------------------------------
 // Abstract base class for a HTTP transaction:
 //
 // A transaction is a "sink" for the response data.  The connection pushes
 // data to the transaction by writing to it.  The transaction supports
 // WriteSegments and may refuse to accept data if its buffers are full (its
 // write function returns NS_BASE_STREAM_WOULD_BLOCK in this case).
 //----------------------------------------------------------------------------
 
 class nsAHttpTransaction : public nsISupports
 {
 public:
     // called by the connection when it takes ownership of the transaction.
     virtual void SetConnection(nsAHttpConnection *) = 0;
+
+    // used to obtain the connection associated with this transaction
     virtual nsAHttpConnection *Connection() = 0;
 
     // called by the connection to get security callbacks to set on the
     // socket transport.
     virtual void GetSecurityCallbacks(nsIInterfaceRequestor **,
                                       nsIEventTarget **) = 0;
 
     // called to report socket status (see nsITransportEventSink)
     virtual void OnTransportStatus(nsITransport* transport,
                                    nsresult status, PRUint64 progress) = 0;
 
     // called to check the transaction status.
     virtual bool     IsDone() = 0;
     virtual nsresult Status() = 0;
+    virtual PRUint8  Caps() = 0;
 
     // called to find out how much request data is available for writing.
     virtual PRUint32 Available() = 0;
 
     // called to read request data from the transaction.
     virtual nsresult ReadSegments(nsAHttpSegmentReader *reader,
                                   PRUint32 count, PRUint32 *countRead) = 0;
 
@@ -109,35 +113,83 @@ public:
     // Returns NS_ERROR_NOT_IMPLEMENTED if the object does not implement
     // sub-transactions.
     //
     // Returns NS_ERROR_ALREADY_OPENED if the subtransactions have been
     // at least partially written and cannot be moved.
     //
     virtual nsresult TakeSubTransactions(
         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 \
     void SetConnection(nsAHttpConnection *); \
     nsAHttpConnection *Connection(); \
     void GetSecurityCallbacks(nsIInterfaceRequestor **, \
                               nsIEventTarget **);       \
     void OnTransportStatus(nsITransport* transport, \
                            nsresult status, PRUint64 progress); \
     bool     IsDone(); \
     nsresult Status(); \
+    PRUint8  Caps();   \
     PRUint32 Available(); \
     nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *); \
     nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *); \
     void     Close(nsresult reason);                                    \
     void     SetSSLConnectFailed();                                     \
     nsHttpRequestHead *RequestHead();                                   \
     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
 //-----------------------------------------------------------------------------
 
 class nsAHttpSegmentReader
 {
 public:
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -182,16 +182,22 @@ nsHttp::DestroyAtomTable()
     }
 
     if (sLock) {
         delete sLock;
         sLock = nsnull;
     }
 }
 
+Mutex *
+nsHttp::GetLock()
+{
+    return sLock;
+}
+
 // this function may be called from multiple threads
 nsHttpAtom
 nsHttp::ResolveAtom(const char *str)
 {
     nsHttpAtom atom = { nsnull };
 
     if (!str || !sAtomTable.ops)
         return atom;
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -135,19 +135,16 @@ typedef PRUint8 nsHttpVersion;
 // such as HTTP upgrade which are nonsensical for SPDY, it is not the
 // SPDY configuration variable.
 #define NS_HTTP_DISALLOW_SPDY        (1<<7)
 
 //-----------------------------------------------------------------------------
 // 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_HTTPS_DEFAULT_PORT 443
 
 #define NS_HTTP_HEADER_SEPS ", \t"
 
 //-----------------------------------------------------------------------------
 // http atoms...
 //-----------------------------------------------------------------------------
@@ -164,16 +161,21 @@ struct nsHttpAtom
     const char *_val;
 };
 
 struct nsHttp
 {
     static nsresult CreateAtomTable();
     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
     static nsHttpAtom ResolveAtom(const char *);
     static nsHttpAtom ResolveAtom(const nsACString &s)
     {
         return ResolveAtom(PromiseFlatCString(s).get());
     }
 
     // returns true if the specified token [start,end) is valid per RFC 2616
--- a/netwerk/protocol/http/nsHttpAtomList.h
+++ b/netwerk/protocol/http/nsHttpAtomList.h
@@ -52,16 +52,17 @@
 
 HTTP_ATOM(Accept,                    "Accept")
 HTTP_ATOM(Accept_Encoding,           "Accept-Encoding")
 HTTP_ATOM(Accept_Language,           "Accept-Language")
 HTTP_ATOM(Accept_Ranges,             "Accept-Ranges")
 HTTP_ATOM(Age,                       "Age")
 HTTP_ATOM(Allow,                     "Allow")
 HTTP_ATOM(Alternate_Protocol,        "Alternate-Protocol")
+HTTP_ATOM(Assoc_Req,                 "Assoc-Req")
 HTTP_ATOM(Authentication,            "Authentication")
 HTTP_ATOM(Authorization,             "Authorization")
 HTTP_ATOM(Cache_Control,             "Cache-Control")
 HTTP_ATOM(Connection,                "Connection")
 HTTP_ATOM(Content_Disposition,       "Content-Disposition")
 HTTP_ATOM(Content_Encoding,          "Content-Encoding")
 HTTP_ATOM(Content_Language,          "Content-Language")
 HTTP_ATOM(Content_Length,            "Content-Length")
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -42,16 +42,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
+#include "nsStandardURL.h"
 #include "nsIApplicationCacheService.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIAuthInformation.h"
 #include "nsIStringBundle.h"
 #include "nsIIDNService.h"
 #include "nsIStreamListenerTee.h"
 #include "nsISeekableStream.h"
 #include "nsMimeTypes.h"
@@ -65,16 +66,17 @@
 #include "nsDNSPrefetch.h"
 #include "nsChannelClassifier.h"
 #include "nsIRedirectResultListener.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "nsDOMError.h"
 #include "nsAlgorithm.h"
 #include "sampler.h"
+#include "nsIConsoleService.h"
 
 using namespace mozilla;
 
 // Device IDs for various cache types
 const char kDiskDeviceID[] = "disk";
 const char kMemoryDeviceID[] = "memory";
 const char kOfflineDeviceID[] = "offline";
 
@@ -120,17 +122,16 @@ AutoRedirectVetoNotifier::ReportRedirect
 
 nsHttpChannel::nsHttpChannel()
     : HttpAsyncAborter<nsHttpChannel>(this)
     , mLogicalOffset(0)
     , mCacheAccess(0)
     , mPostID(0)
     , mRequestTime(0)
     , mOnCacheEntryAvailableCallback(nsnull)
-    , mAsyncCacheOpen(false)
     , mCachedContentIsValid(false)
     , mCachedContentIsPartial(false)
     , mTransactionReplaced(false)
     , mAuthRetryPending(false)
     , mResuming(false)
     , mInitedCacheEntry(false)
     , mCacheForOfflineUse(false)
     , mCachingOpportunistically(false)
@@ -241,16 +242,22 @@ nsHttpChannel::Connect(bool firstTime)
         if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
             LOG(("Resuming from cache is not supported yet"));
             return NS_ERROR_DOCUMENT_NOT_CACHED;
         }
 
         // open a cache entry for this channel...
         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)) {
             LOG(("OpenCacheEntry failed [rv=%x]\n", rv));
             // if this channel is only allowed to pull from the cache, then
             // we must fail if we were unable to open a cache entry.
             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()) {
@@ -261,20 +268,20 @@ nsHttpChannel::Connect(bool firstTime)
             // otherwise, let's just proceed without using the cache.
         }
 
         // if cacheForOfflineUse has been set, open up an offline cache
         // entry to update
         if (mCacheForOfflineUse) {
             rv = OpenOfflineCacheEntryForWriting();
             if (NS_FAILED(rv)) return rv;
+
+            if (mOnCacheEntryAvailableCallback)
+                return NS_OK;
         }
-
-        if (NS_SUCCEEDED(rv) && mAsyncCacheOpen)
-            return NS_OK;
     }
 
     // we may or may not have a cache entry at this point
     if (mCacheEntry) {
         // inspect the cache entry to determine whether or not we need to go
         // out to net to validate it.  this call sets mCachedContentIsValid
         // and may set request headers as required for cache validation.
         rv = CheckCache();
@@ -495,26 +502,27 @@ nsHttpChannel::SetupTransaction()
 
     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
 
     nsresult rv;
 
     if (mCaps & NS_HTTP_ALLOW_PIPELINING) {
         //
         // disable pipelining if:
-        //   (1) pipelining has been explicitly disabled
-        //   (2) request corresponds to a top-level document load (link click)
-        //   (3) request method is non-idempotent
+        //   (1) pipelining has been disabled by config
+        //   (2) pipelining has been disabled by connection mgr info
+        //   (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
-        //     we push it out entirely to necko consumers?
-        //
-        if (!mAllowPipelining || (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) ||
+        if (!mAllowPipelining ||
+           (mLoadFlags & (LOAD_INITIAL_DOCUMENT_URI | INHIBIT_PIPELINE)) ||
             !(mRequestHead.Method() == nsHttp::Get ||
               mRequestHead.Method() == nsHttp::Head ||
+              mRequestHead.Method() == nsHttp::Options ||
               mRequestHead.Method() == nsHttp::Propfind ||
               mRequestHead.Method() == nsHttp::Proppatch)) {
             LOG(("  pipelining disallowed\n"));
             mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
         }
     }
 
     if (!mAllowSpdy)
@@ -763,16 +771,20 @@ nsHttpChannel::CallOnStartRequest()
     LOG(("  calling mListener->OnStartRequest\n"));
     nsresult rv = mListener->OnStartRequest(this, mListenerContext);
     if (NS_FAILED(rv)) return rv;
 
     // install stream converter if required
     rv = ApplyContentConversions();
     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 (mCacheEntry && mChannelIsForDownload) {
         mCacheEntry->Doom();
         CloseCacheEntry(false);
     }
 
     if (!mCanceled) {
         // create offline cache entry if offline caching was requested
@@ -1708,16 +1720,127 @@ nsHttpChannel::Hash(const char *buf, nsA
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mHasher->Finish(true, hash);
     NS_ENSURE_SUCCESS(rv, rv);
 
     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>
 //-----------------------------------------------------------------------------
 
 nsresult
 nsHttpChannel::SetupByteRangeRequest(PRUint32 partialLen)
 {
     // cached content has been found to be partial, add necessary request
@@ -1851,16 +1974,43 @@ nsHttpChannel::ProcessNotModified()
     if (mCustomConditionalRequest) {
         LOG(("Bypassing ProcessNotModified due to custom conditional headers")); 
         return NS_ERROR_FAILURE;
     }
 
     NS_ENSURE_TRUE(mCachedResponseHead, 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
     rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
     if (NS_FAILED(rv)) return rv;
 
     // update the cached response head
     nsCAutoString head;
     mCachedResponseHead->Flatten(head, true);
     rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
@@ -2029,17 +2179,17 @@ IsSubRangeRequest(nsHttpRequestHead &aRe
     return !byteRange.EqualsLiteral("bytes=0-");
 }
 
 nsresult
 nsHttpChannel::OpenCacheEntry()
 {
     nsresult rv;
 
-    mAsyncCacheOpen = false;
+    NS_ASSERTION(!mOnCacheEntryAvailableCallback, "Unexpected state");
     mLoadedFromApplicationCache = false;
 
     LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
 
     // make sure we're not abusing this function
     NS_PRECONDITION(!mCacheEntry, "cache entry already open");
 
     nsCAutoString cacheKey;
@@ -2112,92 +2262,76 @@ nsHttpChannel::OpenCacheEntry()
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = serv->CreateSession(appCacheClientID.get(),
                                  nsICache::STORE_OFFLINE,
                                  nsICache::STREAM_BASED,
                                  getter_AddRefs(session));
         NS_ENSURE_SUCCESS(rv, rv);
 
-        if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
-            // must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
-            rv = session->OpenCacheEntry(cacheKey,
-                                         nsICache::ACCESS_READ, false,
-                                         getter_AddRefs(mCacheEntry));
-            if (NS_SUCCEEDED(rv)) {
-                mCacheEntry->GetAccessGranted(&mCacheAccess);
-                LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]",
-                    this, mCacheAccess));
-                mLoadedFromApplicationCache = true;
-                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)) {
-                mAsyncCacheOpen = true;
-                return NS_OK;
-            }
-        }
-
-        // sync or async opening failed
-        return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE,
-                                            rv, true);
+        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,
+            mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
+
+        if (NS_SUCCEEDED(rv))
+            return NS_OK;
+
+        mOnCacheEntryAvailableCallback = nsnull;
+
+        // opening cache entry failed
+        return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, rv);
     }
 
-    return OpenNormalCacheEntry(true);
+    return OpenNormalCacheEntry();
 }
 
 nsresult
 nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                             nsCacheAccessMode aAccess,
-                                            nsresult aEntryStatus,
-                                            bool aIsSync)
+                                            nsresult aEntryStatus)
 {
     nsresult rv;
 
     if (NS_SUCCEEDED(aEntryStatus)) {
         // We successfully opened an offline cache session and the entry,
         // so indicate we will load from the offline cache.
         mLoadedFromApplicationCache = true;
         mCacheEntry = aEntry;
         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)) {
         LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
         return mStatus;
     }
 
     if (NS_SUCCEEDED(aEntryStatus))
-        // Called from OnCacheEntryAvailable, advance to the next state
-        return Connect(false);
+        return NS_OK;
 
     if (!mCacheForOfflineUse && !mFallbackChannel) {
         nsCAutoString cacheKey;
         GenerateCacheKey(mPostID, cacheKey);
 
         // Check for namespace match.
         nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
         rv = mApplicationCache->GetMatchingNamespace
             (cacheKey, getter_AddRefs(namespaceEntry));
-        if (NS_FAILED(rv) && !aIsSync)
-            return Connect(false);
         NS_ENSURE_SUCCESS(rv, rv);
 
         PRUint32 namespaceType = 0;
         if (!namespaceEntry ||
             NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) ||
             (namespaceType &
              (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK |
               nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC |
@@ -2205,24 +2339,22 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
             // When loading from an application cache, only items
             // on the whitelist or matching a
             // fallback/opportunistic namespace should hit the
             // network...
             mLoadFlags |= LOAD_ONLY_FROM_CACHE;
 
             // ... and if there were an application cache entry,
             // 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 &
             nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
             rv = namespaceEntry->GetData(mFallbackKey);
-            if (NS_FAILED(rv) && !aIsSync)
-                return Connect(false);
             NS_ENSURE_SUCCESS(rv, rv);
         }
 
         if ((namespaceType &
              nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC) &&
             mLoadFlags & LOAD_DOCUMENT_URI) {
             // Document loads for items in an opportunistic namespace
             // should be placed in the offline cache.
@@ -2230,22 +2362,22 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
             mApplicationCache->GetClientID(clientID);
 
             mCacheForOfflineUse = !clientID.IsEmpty();
             SetOfflineCacheClientID(clientID);
             mCachingOpportunistically = true;
         }
     }
 
-    return OpenNormalCacheEntry(aIsSync);
+    return OpenNormalCacheEntry();
 }
 
 
 nsresult
-nsHttpChannel::OpenNormalCacheEntry(bool aIsSync)
+nsHttpChannel::OpenNormalCacheEntry()
 {
     NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
 
     nsresult rv;
 
     nsCAutoString cacheKey;
     GenerateCacheKey(mPostID, cacheKey);
 
@@ -2255,82 +2387,58 @@ nsHttpChannel::OpenNormalCacheEntry(bool
     rv = gHttpHandler->GetCacheSession(storagePolicy,
                                        getter_AddRefs(session));
     if (NS_FAILED(rv)) return rv;
 
     nsCacheAccessMode accessRequested;
     rv = DetermineCacheAccess(&accessRequested);
     if (NS_FAILED(rv)) return rv;
 
-    if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
-        if (!aIsSync) {
-            // Unexpected state: we were called from OnCacheEntryAvailable(),
-            // so LOAD_BYPASS_LOCAL_CACHE_IF_BUSY shouldn't be set. Unless
-            // somebody altered mLoadFlags between OpenCacheEntry() and
-            // OnCacheEntryAvailable()...
-            NS_WARNING(
-                "OpenNormalCacheEntry() called from OnCacheEntryAvailable() "
-                "when LOAD_BYPASS_LOCAL_CACHE_IF_BUSY was specified");
-        }
-
-        // must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
-        rv = session->OpenCacheEntry(cacheKey, accessRequested, false,
-                                     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)
-        // Called from OnCacheEntryAvailable, advance to the next state
-        rv = Connect(false);
+    mOnCacheEntryAvailableCallback =
+        &nsHttpChannel::OnNormalCacheEntryAvailable;
+    rv = session->AsyncOpenCacheEntry(
+        cacheKey,
+        accessRequested,
+        this,
+        mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
+
+    if (NS_SUCCEEDED(rv))
+        return NS_OK;
+
+    mOnCacheEntryAvailableCallback = nsnull;
 
     return rv;
 }
 
 nsresult
 nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                            nsCacheAccessMode aAccess,
-                                           nsresult aEntryStatus,
-                                           bool aIsSync)
+                                           nsresult aEntryStatus)
 {
-    NS_ASSERTION(!aIsSync, "aIsSync should be false");
-
     if (NS_SUCCEEDED(aEntryStatus)) {
         mCacheEntry = aEntry;
         mCacheAccess = aAccess;
     }
 
+    if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
+        LOG(("bypassing local cache since it is busy\n"));
+    }
+
     if (mCanceled && NS_FAILED(mStatus)) {
         LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
         return mStatus;
     }
 
     if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(aEntryStatus))
         // if this channel is only allowed to pull from the cache, then
         // we must fail if we were unable to open a cache entry.
         return NS_ERROR_DOCUMENT_NOT_CACHED;
 
     // advance to the next state...
-    return Connect(false);
+    return NS_OK;
 }
 
 
 nsresult
 nsHttpChannel::OpenOfflineCacheEntryForWriting()
 {
     nsresult rv;
 
@@ -2367,32 +2475,53 @@ nsHttpChannel::OpenOfflineCacheEntryForW
     if (NS_FAILED(rv)) return rv;
 
     rv = serv->CreateSession(mOfflineCacheClientID.get(),
                              nsICache::STORE_OFFLINE,
                              nsICache::STREAM_BASED,
                              getter_AddRefs(session));
     if (NS_FAILED(rv)) return rv;
 
-    rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
-                                 false, getter_AddRefs(mOfflineCacheEntry));
-
-    if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
+    mOnCacheEntryAvailableCallback =
+        &nsHttpChannel::OnOfflineCacheEntryForWritingAvailable;
+    rv = session->AsyncOpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
+                                      this, true);
+    if (NS_SUCCEEDED(rv))
+        return NS_OK;
+
+    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
         // is probably in use by another channel).  Either the cache is being
         // read from (we're offline) or it's being updated elsewhere.
-        return NS_OK;
+        aEntryStatus = NS_OK;
     }
 
-    if (NS_SUCCEEDED(rv)) {
-        mOfflineCacheEntry->GetAccessGranted(&mOfflineCacheAccess);
-        LOG(("got offline cache entry [access=%x]\n", mOfflineCacheAccess));
+    if (mCanceled && NS_FAILED(mStatus)) {
+        LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
+        return mStatus;
     }
 
-    return rv;
+    // advance to the next state...
+    return aEntryStatus;
 }
 
 // Generates the proper cache-key for this instance of nsHttpChannel
 nsresult
 nsHttpChannel::GenerateCacheKey(PRUint32 postID, nsACString &cacheKey)
 {
     AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(),
                      postID, cacheKey);
@@ -4838,39 +4967,84 @@ nsHttpChannel::OnCacheEntryAvailable(nsI
     LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
          "access=%x status=%x]\n", this, entry, access, status));
 
     // if the channel's already fired onStopRequest, then we should ignore
     // this event.
     if (!mIsPending)
         return NS_OK;
 
+    rv = OnCacheEntryAvailableInternal(entry, access, status);
+    if (NS_FAILED(rv)) {
+        CloseCacheEntry(true);
+        AsyncAbort(rv);
+    }
+
+    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, false);
-
-    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;
+    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
         }
-        CloseCacheEntry(true);
-        AsyncAbort(rv);
+
+        // 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 NS_OK;
+    return Connect(false);
+}
+
+NS_IMETHODIMP
+nsHttpChannel::OnCacheEntryDoomed(nsresult status)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
 {
     LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n", this));
 
     NS_ASSERTION(!mTransaction, "should not have a transaction");
@@ -5181,29 +5355,17 @@ nsHttpChannel::DoInvalidateCacheEntry(ns
     nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy();
 
     nsresult rv = gHttpHandler->GetCacheSession(storagePolicy,
                                                 getter_AddRefs(session));
 
     if (NS_FAILED(rv))
         return;
 
-    // Now, find the actual cache-entry
-    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()));
-    }
+    session->DoomEntry(key, nsnull);
 }
 
 nsCacheStoragePolicy
 nsHttpChannel::DetermineStoragePolicy()
 {
     nsCacheStoragePolicy policy = nsICache::STORE_ANYWHERE;
     if (mLoadFlags & INHIBIT_PERSISTENT_CACHING)
         policy = nsICache::STORE_IN_MEMORY;
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -177,16 +177,17 @@ private:
     nsresult ContinueProcessRedirection(nsresult);
     nsresult ContinueProcessRedirectionAfterFallback(nsresult);
     bool     ShouldSSLProxyResponseContinue(PRUint32 httpStatus);
     nsresult ProcessFailedSSLConnect(PRUint32 httpStatus);
     nsresult ProcessFallback(bool *waitingForRedirectCallback);
     nsresult ContinueProcessFallback(nsresult);
     bool     ResponseWouldVary();
     void     HandleAsyncAbort();
+    nsresult EnsureAssocReq();
 
     nsresult ContinueOnStartRequest1(nsresult);
     nsresult ContinueOnStartRequest2(nsresult);
     nsresult ContinueOnStartRequest3(nsresult);
 
     // redirection specific methods
     void     HandleAsyncRedirect();
     nsresult ContinueHandleAsyncRedirect(nsresult);
@@ -205,24 +206,29 @@ private:
     void HandleAsyncReplaceWithProxy();
     nsresult ContinueHandleAsyncReplaceWithProxy(nsresult);
     nsresult ResolveProxy();
 
     // cache specific methods
     nsresult OpenCacheEntry();
     nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                           nsCacheAccessMode aAccess,
-                                          nsresult aResult,
-                                          bool aSync);
-    nsresult OpenNormalCacheEntry(bool aSync);
+                                          nsresult aResult);
+    nsresult OpenNormalCacheEntry();
     nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                          nsCacheAccessMode aAccess,
-                                         nsresult aResult,
-                                         bool aSync);
+                                         nsresult aResult);
     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 UpdateExpirationTime();
     nsresult CheckCache();
     nsresult ShouldUpdateOfflineCacheEntry(bool *shouldCacheForOfflineUse);
     nsresult ReadFromCache();
     void     CloseCacheEntry(bool doomOnFailure);
     void     CloseOfflineCacheEntry();
     nsresult InitCacheEntry();
@@ -294,19 +300,18 @@ private:
     nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
     nsRefPtr<nsInputStreamPump>       mCachePump;
     nsAutoPtr<nsHttpResponseHead>     mCachedResponseHead;
     nsCacheAccessMode                 mCacheAccess;
     PRUint32                          mPostID;
     PRUint32                          mRequestTime;
 
     typedef nsresult (nsHttpChannel:: *nsOnCacheEntryAvailableCallback)(
-        nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult, bool);
+        nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult);
     nsOnCacheEntryAvailableCallback   mOnCacheEntryAvailableCallback;
-    bool                              mAsyncCacheOpen;
 
     nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
     nsCacheAccessMode                 mOfflineCacheAccess;
     nsCString                         mOfflineCacheClientID;
 
     // auth specific data
     nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;
 
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -67,31 +67,32 @@ static NS_DEFINE_CID(kSocketTransportSer
 using namespace mozilla::net;
 
 //-----------------------------------------------------------------------------
 // nsHttpConnection <public>
 //-----------------------------------------------------------------------------
 
 nsHttpConnection::nsHttpConnection()
     : mTransaction(nsnull)
-    , mLastReadTime(0)
-    , mIdleTimeout(0)
     , mConsiderReusedAfterInterval(0)
     , mConsiderReusedAfterEpoch(0)
     , mCurrentBytesRead(0)
     , mMaxBytesRead(0)
     , mTotalBytesRead(0)
     , mKeepAlive(true) // assume to keep-alive by default
     , mKeepAliveMask(true)
     , mSupportsPipelining(false) // assume low-grade server
     , mIsReused(false)
     , mCompletedProxyConnect(false)
     , mLastTransactionExpectedNoContent(false)
     , mIdleMonitoring(false)
+    , mProxyConnectInProgress(false)
     , mHttp1xTransactionCount(0)
+    , mRemainingConnectionUses(0xffffffff)
+    , mClassification(nsAHttpTransaction::CLASS_GENERAL)
     , mNPNComplete(false)
     , mSetupNPNCalled(false)
     , mUsingSpdy(false)
     , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
     , mReportedSpdy(false)
     , mEverUsedSpdy(false)
 {
     LOG(("Creating nsHttpConnection @%x\n", this));
@@ -136,30 +137,35 @@ nsHttpConnection::~nsHttpConnection()
 
 nsresult
 nsHttpConnection::Init(nsHttpConnectionInfo *info,
                        PRUint16 maxHangTime,
                        nsISocketTransport *transport,
                        nsIAsyncInputStream *instream,
                        nsIAsyncOutputStream *outstream,
                        nsIInterfaceRequestor *callbacks,
-                       nsIEventTarget *callbackTarget)
+                       nsIEventTarget *callbackTarget,
+                       PRIntervalTime rtt)
 {
     NS_ABORT_IF_FALSE(transport && instream && outstream,
                       "invalid socket information");
     LOG(("nsHttpConnection::Init [this=%p "
-         "transport=%p instream=%p outstream=%p]\n",
-         this, transport, instream, outstream));
+         "transport=%p instream=%p outstream=%p rtt=%d]\n",
+         this, transport, instream, outstream,
+         PR_IntervalToMilliseconds(rtt)));
 
     NS_ENSURE_ARG_POINTER(info);
     NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
 
     mConnInfo = info;
+    mLastReadTime = PR_IntervalNow();
+    mSupportsPipelining =
+        gHttpHandler->ConnMgr()->SupportsPipelining(mConnInfo);
+    mRtt = rtt;
     mMaxHangTime = PR_SecondsToInterval(maxHangTime);
-    mLastReadTime = PR_IntervalNow();
 
     mSocketTransport = transport;
     mSocketIn = instream;
     mSocketOut = outstream;
     nsresult rv = mSocketTransport->SetEventSink(this, nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mCallbacks = callbacks;
@@ -222,23 +228,26 @@ nsHttpConnection::StartSpdy()
         // as the first stream.
         mSpdySession = new SpdySession(mTransaction,
                                        mSocketTransport,
                                        mPriority);
         LOG(("nsHttpConnection::StartSpdy moves single transaction %p "
              "into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
     }
     else {
-        NS_ABORT_IF_FALSE(!list.IsEmpty(), "sub transaction list empty");
-        
         PRInt32 count = list.Length();
 
         LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d "
              "into SpdySession %p\n", count, mSpdySession.get()));
 
+        if (!count) {
+            mTransaction->Close(NS_ERROR_ABORT);
+            return;
+        }
+
         for (PRInt32 index = 0; index < count; ++index) {
             if (!mSpdySession) {
                 mSpdySession = new SpdySession(list[index],
                                                mSocketTransport,
                                                mPriority);
             }
             else {
                 // AddStream() cannot fail
@@ -333,16 +342,19 @@ nsHttpConnection::Activate(nsAHttpTransa
 
     mPriority = pri;
     if (mTransaction && mUsingSpdy)
         return AddTransaction(trans, pri);
 
     NS_ENSURE_ARG_POINTER(trans);
     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
 
+    // reset the read timers to wash away any idle time
+    mLastReadTime = PR_IntervalNow();
+
     // Update security callbacks
     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     nsCOMPtr<nsIEventTarget>        callbackTarget;
     trans->GetSecurityCallbacks(getter_AddRefs(callbacks),
                                 getter_AddRefs(callbackTarget));
     if (callbacks != mCallbacks) {
         mCallbacks.swap(callbacks);
         if (callbacks)
@@ -364,16 +376,17 @@ nsHttpConnection::Activate(nsAHttpTransa
 
     // need to handle HTTP CONNECT tunnels if this is the first time if
     // we are tunneling through a proxy
     if (((mConnInfo->UsingSSL() && mConnInfo->UsingHttpProxy()) ||
          mConnInfo->ShouldForceConnectMethod()) && !mCompletedProxyConnect) {
         rv = SetupProxyConnect();
         if (NS_FAILED(rv))
             goto failed_activation;
+        mProxyConnectInProgress = true;
     }
 
     // Clear the per activation counter
     mCurrentBytesRead = 0;
 
     // The overflow state is not needed between activations
     mInputOverflow = nsnull;
 
@@ -520,26 +533,45 @@ nsHttpConnection::DontReuse()
 {
     mKeepAliveMask = false;
     mKeepAlive = false;
     mIdleTimeout = 0;
     if (mSpdySession)
         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
 nsHttpConnection::CanReuse()
 {
+    if ((mTransaction ? mTransaction->PipelineDepth() : 0) >=
+        mRemainingConnectionUses) {
+        return false;
+    }
+
     bool canReuse;
     
-    if (mUsingSpdy)
+    if (mSpdySession)
         canReuse = mSpdySession->CanReuse();
     else
         canReuse = IsKeepAlive();
-    
+
     canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive();
 
     // An idle persistent connection should not have data waiting to be read
     // before a request is sent. Data here is likely a 408 timeout response
     // which we would deal with later on through the restart logic, but that
     // path is more expensive than just closing the socket now.
 
     PRUint32 dataSize;
@@ -555,17 +587,18 @@ nsHttpConnection::CanReuse()
 
 bool
 nsHttpConnection::CanDirectlyActivate()
 {
     // return true if a new transaction can be addded to ths connection at any
     // time through Activate(). In practice this means this is a healthy SPDY
     // connection with room for more concurrent streams.
     
-    return UsingSpdy() && CanReuse() && mSpdySession->RoomForMoreStreams();
+    return UsingSpdy() && CanReuse() &&
+        mSpdySession && mSpdySession->RoomForMoreStreams();
 }
 
 PRIntervalTime
 nsHttpConnection::IdleTime()
 {
     return mSpdySession ?
         mSpdySession->IdleTime() : (PR_IntervalNow() - mLastReadTime);
 }
@@ -614,31 +647,29 @@ nsHttpConnection::IsAlive()
 
 bool
 nsHttpConnection::SupportsPipelining(nsHttpResponseHead *responseHead)
 {
     // SPDY supports infinite parallelism, so no need to pipeline.
     if (mUsingSpdy)
         return false;
 
-    // XXX there should be a strict mode available that disables this
-    // blacklisting.
-
     // assuming connection is HTTP/1.1 with keep-alive enabled
     if (mConnInfo->UsingHttpProxy() && !mConnInfo->UsingSSL()) {
         // XXX check for bad proxy servers...
         return true;
     }
 
-    // XXX what about checking for a Via header? (transparent proxies)
-
     // check for bad origin servers
     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)
-        return false; // no header, no love
+        return true;
 
     // 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,
     // so we can do a leading match. 
 
     static const char *bad_servers[26][6] = {
         { nsnull }, { nsnull }, { nsnull }, { nsnull },                 // a - d
         { "EFAServer/", nsnull },                                       // e
@@ -655,16 +686,18 @@ nsHttpConnection::SupportsPipelining(nsH
     };  
 
     int index = val[0] - 'A'; // the whole table begins with capital letters
     if ((index >= 0) && (index <= 25))
     {
         for (int i = 0; bad_servers[index][i] != nsnull; i++) {
             if (!PL_strncmp (val, bad_servers[index][i], strlen (bad_servers[index][i]))) {
                 LOG(("looks like this server does not support pipelining"));
+                gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
+                    mConnInfo, nsHttpConnectionMgr::RedBannedServer, this , 0);
                 return false;
             }
         }
     }
 
     // ok, let's allow pipelining to this server
     return true;
 }
@@ -710,60 +743,110 @@ nsHttpConnection::OnHeadersAvailable(nsA
 
     if ((responseHead->Version() < NS_HTTP_VERSION_1_1) ||
         (requestHead->Version() < NS_HTTP_VERSION_1_1)) {
         // HTTP/1.0 connections are by default NOT persistent
         if (val && !PL_strcasecmp(val, "keep-alive"))
             mKeepAlive = true;
         else
             mKeepAlive = false;
+        
+        // We need at least version 1.1 to use pipelines
+        gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
+            mConnInfo, nsHttpConnectionMgr::RedVersionTooLow, this, 0);
     }
     else {
         // HTTP/1.1 connections are by default persistent
-        if (val && !PL_strcasecmp(val, "close")) 
+        if (val && !PL_strcasecmp(val, "close")) {
             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 {
             mKeepAlive = true;
 
             // Do not support pipelining when we are establishing
             // an SSL tunnel though an HTTP proxy. Pipelining support
             // determination must be based on comunication with the
             // target server in this case. See bug 422016 for futher
             // details.
             if (!mProxyConnectStream)
               mSupportsPipelining = SupportsPipelining(responseHead);
         }
     }
     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"
     // 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
     // before the server will close it.  we ignore the max reuse count, because
     // 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 
     // specified then we use our advertized timeout value.
+    bool foundKeepAliveMax = false;
     if (mKeepAlive) {
         val = responseHead->PeekHeader(nsHttp::Keep_Alive);
 
         if (!mUsingSpdy) {
             const char *cp = PL_strcasestr(val, "timeout=");
             if (cp)
                 mIdleTimeout = PR_SecondsToInterval((PRUint32) atoi(cp + 8));
             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 {
             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)));
     }
 
+    if (!foundKeepAliveMax && mRemainingConnectionUses && !mUsingSpdy)
+        --mRemainingConnectionUses;
+
     if (!mProxyConnectStream)
         HandleAlternateProtocol(responseHead);
 
     // if we're doing an SSL proxy connect, then we need to check whether or not
     // the connect was successful.  if so, then we have to reset the transaction
     // and step-up the socket connection to SSL. finally, we have to wake up the
     // socket write request.
     if (mProxyConnectStream) {
@@ -875,18 +958,66 @@ nsHttpConnection::ReadTimeoutTick(PRInte
 
     // Spdy in the future actually should implement some timeout handling
     // using the SPDY ping frame.
     if (mSpdySession) {
         mSpdySession->ReadTimeoutTick(now);
         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
 nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
 {
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mSocketTransport) {
@@ -925,16 +1056,24 @@ nsHttpConnection::ResumeSend()
 
 nsresult
 nsHttpConnection::ResumeRecv()
 {
     LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this));
 
     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)
         return mSocketIn->AsyncWait(this, 0, 0, nsnull);
 
     NS_NOTREACHED("no socket input stream");
     return NS_ERROR_UNEXPECTED;
 }
 
 void
@@ -1047,17 +1186,18 @@ nsHttpConnection::OnReadSegment(const ch
         mSocketOutCondition = NS_OK; // reset condition
 
     return mSocketOutCondition;
 }
 
 nsresult
 nsHttpConnection::OnSocketWritable()
 {
-    LOG(("nsHttpConnection::OnSocketWritable [this=%x]\n", this));
+    LOG(("nsHttpConnection::OnSocketWritable [this=%p] host=%s\n",
+         this, mConnInfo->Host()));
 
     nsresult rv;
     PRUint32 n;
     bool again = true;
 
     do {
         mSocketOutCondition = NS_OK;
 
@@ -1089,16 +1229,17 @@ nsHttpConnection::OnSocketWritable()
         }
         else {
             if (!mReportedSpdy) {
                 mReportedSpdy = true;
                 gHttpHandler->ConnMgr()->ReportSpdyConnection(this, mUsingSpdy);
             }
 
             LOG(("  writing transaction request stream\n"));
+            mProxyConnectInProgress = false;
             rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize, &n);
         }
 
         LOG(("  ReadSegments returned [rv=%x read=%u sock-cond=%x]\n",
             rv, n, mSocketOutCondition));
 
         // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
         if (rv == NS_BASE_STREAM_CLOSED) {
@@ -1126,17 +1267,17 @@ nsHttpConnection::OnSocketWritable()
             // must wait for the server's response.  we manufacture a status message
             // here to reflect the fact that we are waiting.  this message will be
             // trumped (overwritten) if the server responds quickly.
             //
             mTransaction->OnTransportStatus(mSocketTransport,
                                             nsISocketTransport::STATUS_WAITING_FOR,
                                             LL_ZERO);
 
-            rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading
+            rv = ResumeRecv(); // start reading
             again = false;
         }
         // write more to the socket until error or end-of-request...
     } while (again);
 
     return rv;
 }
 
@@ -1165,24 +1306,63 @@ nsHttpConnection::OnWriteSegment(char *b
 }
 
 nsresult
 nsHttpConnection::OnSocketReadable()
 {
     LOG(("nsHttpConnection::OnSocketReadable [this=%x]\n", this));
 
     PRIntervalTime now = PR_IntervalNow();
+    PRIntervalTime delta = now - mLastReadTime;
 
-    if (mKeepAliveMask && ((now - mLastReadTime) >= mMaxHangTime)) {
+    if (mKeepAliveMask && (delta >= mMaxHangTime)) {
         LOG(("max hang time exceeded!\n"));
         // give the handler a chance to create a new persistent connection to
         // this host if we've been busy for too long.
         mKeepAliveMask = false;
         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;
 
     nsresult rv;
     PRUint32 n;
     bool again = true;
 
     do {
         rv = mTransaction->WriteSegments(this, nsIOService::gDefaultSegmentSize, &n);
@@ -1194,17 +1374,17 @@ nsHttpConnection::OnSocketReadable()
             again = false;
         }
         else {
             mCurrentBytesRead += n;
             mTotalBytesRead += n;
             if (NS_FAILED(mSocketInCondition)) {
                 // continue waiting for the socket if necessary...
                 if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK)
-                    rv = mSocketIn->AsyncWait(this, 0, 0, nsnull);
+                    rv = ResumeRecv();
                 else
                     rv = mSocketInCondition;
                 again = false;
             }
         }
         // read more from the socket until error...
     } while (again);
 
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -36,31 +36,35 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsHttpConnection_h__
 #define nsHttpConnection_h__
 
 #include "nsHttp.h"
 #include "nsHttpConnectionInfo.h"
-#include "nsAHttpConnection.h"
 #include "nsAHttpTransaction.h"
+#include "nsHttpPipeline.h"
 #include "nsXPIDLString.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "prinrval.h"
 #include "SpdySession.h"
+#include "mozilla/TimeStamp.h"
 
 #include "nsIStreamListener.h"
 #include "nsISocketTransport.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIEventTarget.h"
 
+class nsHttpRequestHead;
+class nsHttpResponseHead;
+
 //-----------------------------------------------------------------------------
 // nsHttpConnection - represents a connection to a HTTP server (or proxy)
 //
 // NOTE: this objects lives on the socket thread only.  it should not be
 // accessed from any other thread.
 //-----------------------------------------------------------------------------
 
 class nsHttpConnection : public nsAHttpSegmentReader
@@ -85,41 +89,46 @@ public:
     // Initialize the connection:
     //  info        - specifies the connection parameters.
     //  maxHangTime - limits the amount of time this connection can spend on a
     //                single transaction before it should no longer be kept 
     //                alive.  a value of 0xffff indicates no limit.
     nsresult Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime,
                   nsISocketTransport *, nsIAsyncInputStream *,
                   nsIAsyncOutputStream *, nsIInterfaceRequestor *,
-                  nsIEventTarget *);
+                  nsIEventTarget *, PRIntervalTime);
 
     // Activate causes the given transaction to be processed on this
     // connection.  It fails if there is already an existing transaction unless
     // a multiplexing protocol such as SPDY is being used
     nsresult Activate(nsAHttpTransaction *, PRUint8 caps, PRInt32 pri);
 
     // Close the underlying socket transport.
     void Close(nsresult reason);
 
     //-------------------------------------------------------------------------
     // XXX document when these are ok to call
 
-    bool     SupportsPipelining() { return mSupportsPipelining; }
+    bool     SupportsPipelining();
     bool     IsKeepAlive() { return mUsingSpdy ||
                                     (mKeepAliveMask && mKeepAlive); }
     bool     CanReuse();   // can this connection be reused?
     bool     CanDirectlyActivate();
 
     // Returns time in seconds for how long connection can be reused.
     PRUint32 TimeToLive();
 
     void     DontReuse();
     void     DropTransport() { DontReuse(); mSocketTransport = 0; }
 
+    bool     IsProxyConnectInProgress()
+    {
+        return mProxyConnectInProgress;
+    }
+
     bool     LastTransactionExpectedNoContent()
     {
         return mLastTransactionExpectedNoContent;
     }
 
     void     SetLastTransactionExpectedNoContent(bool val)
     {
         mLastTransactionExpectedNoContent = val;
@@ -153,19 +162,28 @@ public:
     // connection pool, the nsHttpConnection still reads errors and hangups
     // on the socket so that it can be proactively released if the server
     // initiates a termination. Only call on socket thread.
     void BeginIdleMonitoring();
     void EndIdleMonitoring();
 
     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);
 
+    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:
     // called to cause the underlying socket to start speaking SSL
     nsresult ProxyStartSSL();
 
     nsresult OnTransactionDone(nsresult reason);
     nsresult OnSocketWritable();
     nsresult OnSocketReadable();
 
@@ -205,39 +223,49 @@ private:
     // transaction is open, otherwise it is null.
     nsRefPtr<nsAHttpTransaction>    mTransaction;
 
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
     nsCOMPtr<nsIEventTarget>        mCallbackTarget;
 
     nsRefPtr<nsHttpConnectionInfo> mConnInfo;
 
-    PRUint32                        mLastReadTime;
+    PRIntervalTime                  mLastReadTime;
     PRIntervalTime                  mMaxHangTime;    // max download time before dropping keep-alive status
     PRIntervalTime                  mIdleTimeout;    // value of keep-alive: timeout=
     PRIntervalTime                  mConsiderReusedAfterInterval;
     PRIntervalTime                  mConsiderReusedAfterEpoch;
     PRInt64                         mCurrentBytesRead;   // data read per activation
     PRInt64                         mMaxBytesRead;       // max read in 1 activation
     PRInt64                         mTotalBytesRead;     // total data read
 
     nsRefPtr<nsIAsyncInputStream>   mInputOverflow;
 
+    PRIntervalTime                  mRtt;
+
     bool                            mKeepAlive;
     bool                            mKeepAliveMask;
     bool                            mSupportsPipelining;
     bool                            mIsReused;
     bool                            mCompletedProxyConnect;
     bool                            mLastTransactionExpectedNoContent;
     bool                            mIdleMonitoring;
+    bool                            mProxyConnectInProgress;
 
     // The number of <= HTTP/1.1 transactions performed on this connection. This
     // excludes spdy transactions.
     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
     bool                            mNPNComplete;
     bool                            mSetupNPNCalled;
     bool                            mUsingSpdy;
     nsRefPtr<mozilla::net::SpdySession> mSpdySession;
     PRInt32                         mPriority;
     bool                            mReportedSpdy;
 
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -120,16 +120,17 @@ public:
     PRInt32       Port() const           { return mPort; }
     nsProxyInfo  *ProxyInfo()            { return mProxyInfo; }
     bool          UsingHttpProxy() const { return mUsingHttpProxy; }
     bool          UsingSSL() const       { return mUsingSSL; }
     PRInt32       DefaultPort() const    { return mUsingSSL ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT; }
     void          SetAnonymous(bool anon)         
                                          { mHashKey.SetCharAt(anon ? 'A' : '.', 2); }
     bool          GetAnonymous()         { return mHashKey.CharAt(2) == 'A'; }
+
     bool          ShouldForceConnectMethod();
     const nsCString &GetHost() { return mHost; }
 
 private:
     nsrefcnt               mRef;
     nsCString              mHashKey;
     nsCString              mHost;
     PRInt32                mPort;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -138,30 +138,32 @@ nsHttpConnectionMgr::EnsureSocketThreadT
 
 nsresult
 nsHttpConnectionMgr::Init(PRUint16 maxConns,
                           PRUint16 maxConnsPerHost,
                           PRUint16 maxConnsPerProxy,
                           PRUint16 maxPersistConnsPerHost,
                           PRUint16 maxPersistConnsPerProxy,
                           PRUint16 maxRequestDelay,
-                          PRUint16 maxPipelinedRequests)
+                          PRUint16 maxPipelinedRequests,
+                          PRUint16 maxOptimisticPipelinedRequests)
 {
     LOG(("nsHttpConnectionMgr::Init\n"));
 
     {
         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
         mMaxConns = maxConns;
         mMaxConnsPerHost = maxConnsPerHost;
         mMaxConnsPerProxy = maxConnsPerProxy;
         mMaxPersistConnsPerHost = maxPersistConnsPerHost;
         mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
         mMaxRequestDelay = maxRequestDelay;
         mMaxPipelinedRequests = maxPipelinedRequests;
+        mMaxOptimisticPipelinedRequests = maxOptimisticPipelinedRequests;
 
         mIsShuttingDown = false;
     }
 
     return EnsureSocketThreadTargetIfOnline();
 }
 
 nsresult
@@ -206,20 +208,17 @@ nsHttpConnectionMgr::PostEvent(nsConnEve
 
     nsresult rv;
     if (!mSocketThreadTarget) {
         NS_WARNING("cannot post event if not initialized");
         rv = NS_ERROR_NOT_INITIALIZED;
     }
     else {
         nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
-        if (!event)
-            rv = NS_ERROR_OUT_OF_MEMORY;
-        else
-            rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
+        rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
     }
     return rv;
 }
 
 void
 nsHttpConnectionMgr::PruneDeadConnectionsAfter(PRUint32 timeInSeconds)
 {
     LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
@@ -344,45 +343,16 @@ nsHttpConnectionMgr::GetSocketThreadTarg
     // care of initializing the socket thread target if that's the case.
     EnsureSocketThreadTargetIfOnline();
 
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     NS_IF_ADDREF(*target = mSocketThreadTarget);
     return NS_OK;
 }
 
-void
-nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
-{
-    LOG(("nsHttpConnectionMgr::AddTransactionToPipeline [pipeline=%x]\n", pipeline));
-
-    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
-
-    nsRefPtr<nsHttpConnectionInfo> ci;
-    pipeline->GetConnectionInfo(getter_AddRefs(ci));
-    if (ci) {
-        nsConnectionEntry *ent = mCT.Get(ci->HashKey());
-        if (ent) {
-            // search for another request to pipeline...
-            PRInt32 i, count = ent->mPendingQ.Length();
-            for (i=0; i<count; ++i) {
-                nsHttpTransaction *trans = ent->mPendingQ[i];
-                if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
-                    pipeline->AddTransaction(trans);
-
-                    // remove transaction from pending queue
-                    ent->mPendingQ.RemoveElementAt(i);
-                    NS_RELEASE(trans);
-                    break;
-                }
-            }
-        }
-    }
-}
-
 nsresult
 nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
 {
     LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
 
     NS_ADDREF(conn);
     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
     if (NS_FAILED(rv))
@@ -805,39 +775,35 @@ nsHttpConnectionMgr::PruneDeadConnection
         // mTimeOfNextWakeUp, we need to schedule the pruning to happen
         // after timeToNextExpire.
         if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
             self->PruneDeadConnectionsAfter(timeToNextExpire);
         }
     } else {
         self->ConditionallyStopPruneDeadConnectionsTimer();
     }
-#ifdef DEBUG
-    count = ent->mActiveConns.Length();
-    if (count > 0) {
-        for (PRInt32 i=count-1; i>=0; --i) {
-            nsHttpConnection *conn = ent->mActiveConns[i];
-            LOG(("    active conn [%x] with trans [%x]\n", conn, conn->Transaction()));
-        }
-    }
-#endif
-
-    // if this entry is empty, then we can remove it.
-    if (ent->mIdleConns.Length()   == 0 &&
+
+    // if this entry is empty, we have too many entries,
+    // and this doesn't represent some painfully determined
+    // red condition, then we can clean it up and restart from
+    // yellow
+    if (ent->PipelineState()       != PS_RED &&
+        self->mCT.Count()          >  125 &&
+        ent->mIdleConns.Length()   == 0 &&
         ent->mActiveConns.Length() == 0 &&
         ent->mHalfOpens.Length()   == 0 &&
         ent->mPendingQ.Length()    == 0 &&
         ((!ent->mTestedSpdy && !ent->mUsingSpdy) ||
          !gHttpHandler->IsSpdyEnabled() ||
          self->mCT.Count() > 300)) {
         LOG(("    removing empty connection entry\n"));
         return PL_DHASH_REMOVE;
     }
 
-    // else, use this opportunity to compact our arrays...
+    // otherwise use this opportunity to compact our arrays...
     ent->mIdleConns.Compact();
     ent->mActiveConns.Compact();
     ent->mPendingQ.Compact();
 
     return PL_DHASH_NEXT;
 }
 
 PLDHashOperator
@@ -893,74 +859,165 @@ nsHttpConnectionMgr::ShutdownPassCB(cons
 }
 
 //-----------------------------------------------------------------------------
 
 bool
 nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
 {
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
-        ent->mConnInfo->HashKey().get()));
+         ent->mConnInfo->HashKey().get()));
 
     ProcessSpdyPendingQ(ent);
 
-    PRUint32 i, count = ent->mPendingQ.Length();
-    if (count > 0) {
-        LOG(("  pending-count=%u\n", count));
-        nsHttpTransaction *trans = nsnull;
-        nsHttpConnection *conn = nsnull;
-        for (i = 0; i < count; ++i) {
-            trans = ent->mPendingQ[i];
-
-            // When this transaction has already established a half-open
-            // connection, we want to prevent any duplicate half-open
-            // connections from being established and bound to this
-            // transaction. Allow only use of an idle persistent connection
-            // (if found) for transactions referred by a half-open connection.
-            bool alreadyHalfOpen = false;
-            for (PRInt32 j = 0; j < ((PRInt32) ent->mHalfOpens.Length()); j++) {
-                if (ent->mHalfOpens[j]->Transaction() == trans) {
-                    alreadyHalfOpen = true;
-                    break;
-                }
-            }
-
-            GetConnection(ent, trans, alreadyHalfOpen, &conn);
-            if (conn)
+    PRUint32 count = ent->mPendingQ.Length();
+    nsHttpTransaction *trans;
+    nsresult rv;
+    bool dispatchedSuccessfully = false;
+
+    // iterate the pending list until one is dispatched successfully. Keep
+    // iterating afterwards only until a transaction fails to dispatch.
+    for (PRUint32 i = 0; i < count; ++i) {
+        trans = ent->mPendingQ[i];
+
+        // When this transaction has already established a half-open
+        // connection, we want to prevent any duplicate half-open
+        // connections from being established and bound to this
+        // transaction. Allow only use of an idle persistent connection
+        // (if found) for transactions referred by a half-open connection.
+        bool alreadyHalfOpen = false;
+        for (PRInt32 j = 0; j < ((PRInt32) ent->mHalfOpens.Length()); ++j) {
+            if (ent->mHalfOpens[j]->Transaction() == trans) {
+                alreadyHalfOpen = true;
                 break;
-
-            NS_ABORT_IF_FALSE(count == ent->mPendingQ.Length(),
-                              "something mutated pending queue from "
-                              "GetConnection()");
+            }
         }
-        if (conn) {
+
+        rv = TryDispatchTransaction(ent, alreadyHalfOpen, trans);
+        if (NS_SUCCEEDED(rv)) {
             LOG(("  dispatching pending transaction...\n"));
-
-            // remove pending transaction
             ent->mPendingQ.RemoveElementAt(i);
-
-            nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
-            if (NS_SUCCEEDED(rv))
-                NS_RELEASE(trans);
-            else {
-                LOG(("  DispatchTransaction failed [rv=%x]\n", rv));
-                // on failure, just put the transaction back
-                ent->mPendingQ.InsertElementAt(i, trans);
-                // might be something wrong with the connection... close it.
-                conn->Close(rv);
-            }
-
-            NS_RELEASE(conn);
+            NS_RELEASE(trans);
+
+            // reset index and array length after RemoveElelmentAt()
+            dispatchedSuccessfully = true;
+            count = ent->mPendingQ.Length();
+            --i;
+            continue;
+        }
+
+        if (dispatchedSuccessfully)
             return true;
-        }
+
+        NS_ABORT_IF_FALSE(count == ((PRInt32) ent->mPendingQ.Length()),
+                          "something mutated pending queue from "
+                          "GetConnection()");
     }
     return false;
 }
 
+bool
+nsHttpConnectionMgr::ProcessPendingQForEntry(nsHttpConnectionInfo *ci)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    if (ent)
+        return ProcessPendingQForEntry(ent);
+    return false;
+}
+
+bool
+nsHttpConnectionMgr::SupportsPipelining(nsHttpConnectionInfo *ci)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    if (ent)
+        return ent->SupportsPipelining();
+    return false;
+}
+
+// nsHttpPipelineFeedback used to hold references across events
+
+class nsHttpPipelineFeedback
+{
+public:
+    nsHttpPipelineFeedback(nsHttpConnectionInfo *ci,
+                           nsHttpConnectionMgr::PipelineFeedbackInfoType info,
+                           nsHttpConnection *conn, PRUint32 data)
+        : mConnInfo(ci)
+        , mConn(conn)
+        , mInfo(info)
+        , mData(data)
+        {
+        }
+    
+    ~nsHttpPipelineFeedback()
+    {
+    }
+    
+    nsRefPtr<nsHttpConnectionInfo> mConnInfo;
+    nsRefPtr<nsHttpConnection> mConn;
+    nsHttpConnectionMgr::PipelineFeedbackInfoType mInfo;
+    PRUint32 mData;
+};
+
+void
+nsHttpConnectionMgr::PipelineFeedbackInfo(nsHttpConnectionInfo *ci,
+                                          PipelineFeedbackInfoType info,
+                                          nsHttpConnection *conn,
+                                          PRUint32 data)
+{
+    if (!ci)
+        return;
+
+    // Post this to the socket thread if we are not running there already
+    if (PR_GetCurrentThread() != gSocketThread) {
+        nsHttpPipelineFeedback *fb = new nsHttpPipelineFeedback(ci, info,
+                                                                conn, data);
+
+        nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessFeedback,
+                                0, fb);
+        if (NS_FAILED(rv))
+            delete fb;
+        return;
+    }
+
+    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+
+    if (ent)
+        ent->OnPipelineFeedbackInfo(info, conn, data);
+}
+
+void
+nsHttpConnectionMgr::ReportFailedToProcess(nsIURI *uri)
+{
+    NS_ABORT_IF_FALSE(uri, "precondition");
+
+    nsCAutoString host;
+    PRInt32 port = -1;
+    bool usingSSL = false;
+
+    nsresult rv = uri->SchemeIs("https", &usingSSL);
+    if (NS_SUCCEEDED(rv))
+        rv = uri->GetAsciiHost(host);
+    if (NS_SUCCEEDED(rv))
+        rv = uri->GetPort(&port);
+    if (NS_FAILED(rv) || host.IsEmpty())
+        return;
+
+    nsRefPtr<nsHttpConnectionInfo> ci =
+        new nsHttpConnectionInfo(host, port, nsnull, usingSSL);
+    
+    PipelineFeedbackInfo(ci, RedCorruptedContent, nsnull, 0);
+}
+
 // we're at the active connection limit if any one of the following conditions is true:
 //  (1) at max-connections
 //  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
 //  (3) keep-alive disabled and at max-connections-per-server
 bool
 nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
 {
     nsHttpConnectionInfo *ci = ent->mConnInfo;
@@ -1044,132 +1101,468 @@ nsHttpConnectionMgr::ClosePersistentConn
                                                   nsAutoPtr<nsConnectionEntry> &ent,
                                                   void *closure)
 {
     nsHttpConnectionMgr *self = static_cast<nsHttpConnectionMgr *>(closure);
     self->ClosePersistentConnections(ent);
     return PL_DHASH_NEXT;
 }
 
-void
-nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
-                                   nsHttpTransaction *trans,
-                                   bool onlyReusedConnection,
-                                   nsHttpConnection **result)
+bool
+nsHttpConnectionMgr::MakeNewConnection(nsConnectionEntry *ent,
+                                       nsHttpTransaction *trans)
+{
+    LOG(("nsHttpConnectionMgr::MakeNewConnection %p ent=%p trans=%p",
+         this, ent, trans));
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+        
+    // If this host is trying to negotiate a SPDY session right now,
+    // don't create any new connections until the result of the
+    // negotiation is known.
+
+    if (gHttpHandler->IsSpdyEnabled() &&
+        ent->mConnInfo->UsingSSL() &&
+        !ent->mConnInfo->UsingHttpProxy() &&
+        !(trans->Caps() & NS_HTTP_DISALLOW_SPDY) &&
+        (!ent->mTestedSpdy || ent->mUsingSpdy) &&
+        (ent->mHalfOpens.Length() || ent->mActiveConns.Length())) {
+        return false;
+    }
+
+    // We need to make a new connection. If that is going to exceed the
+    // global connection limit then try and free up some room by closing
+    // an idle connection to another host. We know it won't select "ent"
+    // beacuse we have already determined there are no idle connections
+    // to our destination
+
+    if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) && mNumIdleConns)
+        mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
+
+    if (AtActiveConnectionLimit(ent, trans->Caps()))
+        return false;
+
+    nsresult rv = CreateTransport(ent, trans);
+    if (NS_FAILED(rv))                            /* hard failure */
+        trans->Close(rv);
+
+    return true;
+}
+
+bool
+nsHttpConnectionMgr::AddToShortestPipeline(nsConnectionEntry *ent,
+                                           nsHttpTransaction *trans,
+                                           nsHttpTransaction::Classifier classification,
+                                           PRUint16 depthLimit)
 {
-    LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
-        ent->mConnInfo->HashKey().get(), PRUint32(trans->Caps())));
-
-    // First, see if an existing connection can be used - either an idle
-    // persistent connection or an active spdy session may be reused instead of
-    // establishing a new socket. We do not need to check the connection limits
-    // yet as they govern the maximum number of open connections and reusing
-    // an old connection never increases that.
-
-    *result = nsnull;
-
-    nsHttpConnection *conn = nsnull;
-    bool addConnToActiveList = true;
-
-    if (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) {
-
-        // if willing to use spdy look for an active spdy connections
-        // before considering idle http ones.
-        if (gHttpHandler->IsSpdyEnabled()) {
-            conn = GetSpdyPreferredConn(ent);
-            if (conn)
-                addConnToActiveList = false;
+    if (classification == nsAHttpTransaction::CLASS_SOLO)
+        return false;
+
+    PRUint32 maxdepth = ent->MaxPipelineDepth(classification);
+    if (maxdepth == 0) {
+        ent->CreditPenalty();
+        maxdepth = ent->MaxPipelineDepth(classification);
+    }
+    
+    if (ent->PipelineState() == PS_RED)
+        return false;
+
+    if (ent->PipelineState() == PS_YELLOW && ent->mYellowConnection)
+        return false;
+
+    // The maximum depth of a pipeline in yellow is 1 pipeline of
+    // depth 2 for entire CI. When that transaction completes successfully
+    // we transition to green and that expands the allowed depth
+    // to any number of pipelines of up to depth 4.  When a transaction
+    // queued at position 3 or deeper succeeds we open it all the way
+    // up to depths limited only by configuration. The staggered start
+    // in green is simply because a successful yellow test of depth=2
+    // might really just be a race condition (i.e. depth=1 from the
+    // server's point of view), while depth=3 is a stronger indicator -
+    // keeping the pipelines to a modest depth during that period limits
+    // the damage if something is going to go wrong.
+
+    maxdepth = PR_MIN(maxdepth, depthLimit);
+
+    if (maxdepth < 2)
+        return false;
+
+    nsAHttpTransaction *activeTrans;
+
+    nsHttpConnection *bestConn = nsnull;
+    PRUint32 activeCount = ent->mActiveConns.Length();
+    PRUint32 bestConnLength = 0;
+    PRUint32 connLength;
+
+    for (PRUint32 i = 0; i < activeCount; ++i) {
+        nsHttpConnection *conn = ent->mActiveConns[i];
+        if (!conn->SupportsPipelining())
+            continue;
+
+        if (conn->Classification() != classification)
+            continue;
+
+        activeTrans = conn->Transaction();
+        if (!activeTrans ||
+            activeTrans->IsDone() ||
+            NS_FAILED(activeTrans->Status()))
+            continue;
+
+        connLength = activeTrans->PipelineDepth();
+
+        if (maxdepth <= connLength)
+            continue;
+
+        if (!bestConn || (connLength < bestConnLength)) {
+            bestConn = conn;
+            bestConnLength = connLength;
         }
-        
-        // search the idle connection list. Each element in the list
-        // has a reference, so if we remove it from the list into a local
-        // ptr, that ptr now owns the reference
+    }
+
+    if (!bestConn)
+        return false;
+
+    activeTrans = bestConn->Transaction();
+    nsresult rv = activeTrans->AddTransaction(trans);
+    if (NS_FAILED(rv))
+        return false;
+
+    LOG(("   scheduling trans %p on pipeline at position %d\n",
+         trans, trans->PipelinePosition()));
+
+    if ((ent->PipelineState() == PS_YELLOW) && (trans->PipelinePosition() > 1))
+        ent->SetYellowConnection(bestConn);
+    return true;
+}
+
+bool
+nsHttpConnectionMgr::IsUnderPressure(nsConnectionEntry *ent,
+                                   nsHttpTransaction::Classifier classification)
+{
+    // A connection entry is declared to be "under pressure" if most of the 
+    // allowed parallel connections are already used up. In that case we want to
+    // favor existing pipelines over more parallelism so as to reserve any
+    // unused parallel connections for types that don't have existing pipelines.
+    //
+    // The defintion of connection pressure is a pretty liberal one here - that
+    // is why we are using the more restrictive maxPersist* counters.
+    //
+    // Pipelines are also favored when the requested classification is already
+    // using 3 or more of the connections. Failure to do this could result in
+    // one class (e.g. images) establishing self replenishing queues on all the
+    // connections that would starve the other transaction types.
+    
+    PRInt32 currentConns = ent->mActiveConns.Length();
+    PRInt32 maxConns =
+        (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingSSL()) ?
+        mMaxPersistConnsPerProxy : mMaxPersistConnsPerHost;
+
+    // Leave room for at least 3 distinct types to operate concurrently,
+    // this satisfies the typical {html, js/css, img} page.
+    if (currentConns >= (maxConns - 2))
+        return true;                           /* prefer pipeline */
+
+    PRInt32 sameClass = 0;
+    for (PRInt32 i = 0; i < currentConns; ++i)
+        if (classification == ent->mActiveConns[i]->Classification())
+            if (++sameClass == 3)
+                return true;                   /* prefer pipeline */
+    
+    return false;                              /* normal behavior */
+}
+
+// returns OK if a connection is found for the transaction
+// and the transaction is started.
+// returns ERROR_NOT_AVAILABLE if no connection can be found and it
+// should be queued
+nsresult
+nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
+                                            bool onlyReusedConnection,
+                                            nsHttpTransaction *trans)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    LOG(("nsHttpConnectionMgr::TryDispatchTransaction without conn "
+         "[ci=%s caps=%x]\n",
+         ent->mConnInfo->HashKey().get(), PRUint32(trans->Caps())));
+
+    nsHttpTransaction::Classifier classification = trans->Classification();
+    PRUint8 caps = trans->Caps();
+
+    // no keep-alive means no pipelines either
+    if (!(caps & NS_HTTP_ALLOW_KEEPALIVE))
+        caps = caps & ~NS_HTTP_ALLOW_PIPELINING;
+
+    // 0 - If this should use spdy then dispatch it post haste.
+    // 1 - If there is connection pressure then see if we can pipeline this on
+    //     a connection of a matching type instead of using a new conn
+    // 2 - If there is an idle connection, use it!
+    // 3 - if class == reval or script and there is an open conn of that type
+    //     then pipeline onto shortest pipeline of that class if limits allow
+    // 4 - If we aren't up against our connection limit,
+    //     then open a new one
+    // 5 - Try a pipeline if we haven't already - this will be unusual because
+    //     it implies a low connection pressure situation where
+    //     MakeNewConnection() failed.. that is possible, but unlikely, due to
+    //     global limits
+    // 6 - no connection is available - queue it
+
+    bool attemptedOptimisticPipeline = !(caps & NS_HTTP_ALLOW_PIPELINING);
+
+    // step 0
+    // look for existing spdy connection - that's always best because it is
+    // essentially pipelining without head of line blocking
+
+    if (!(caps & NS_HTTP_DISALLOW_SPDY) && gHttpHandler->IsSpdyEnabled()) {
+        nsRefPtr<nsHttpConnection> conn = GetSpdyPreferredConn(ent);
+        if (conn) {
+            LOG(("   dispatch to spdy: [conn=%x]\n", conn.get()));
+            DispatchTransaction(ent, trans, conn);
+            return NS_OK;
+        }
+    }
+
+    // step 1
+    // If connection pressure, then we want to favor pipelining of any kind
+    if (IsUnderPressure(ent, classification) && !attemptedOptimisticPipeline) {
+        attemptedOptimisticPipeline = true;
+        if (AddToShortestPipeline(ent, trans,
+                                  classification,
+                                  mMaxOptimisticPipelinedRequests)) {
+            return NS_OK;
+        }
+    }
+
+    // step 2
+    // consider an idle persistent connection
+    if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
+        nsRefPtr<nsHttpConnection> conn;
         while (!conn && (ent->mIdleConns.Length() > 0)) {
             conn = ent->mIdleConns[0];
+            ent->mIdleConns.RemoveElementAt(0);
+            mNumIdleConns--;
+            nsHttpConnection *temp = conn;
+            NS_RELEASE(temp);
+            
             // we check if the connection can be reused before even checking if
             // it is a "matching" connection.
             if (!conn->CanReuse()) {
-                LOG(("   dropping stale connection: [conn=%x]\n", conn));
+                LOG(("   dropping stale connection: [conn=%x]\n", conn.get()));
                 conn->Close(NS_ERROR_ABORT);
-                NS_RELEASE(conn);
+                conn = nsnull;
             }
             else {
-                LOG(("   reusing connection [conn=%x]\n", conn));
+                LOG(("   reusing connection [conn=%x]\n", conn.get()));
                 conn->EndIdleMonitoring();
             }
 
-            ent->mIdleConns.RemoveElementAt(0);
-            mNumIdleConns--;
-
             // If there are no idle connections left at all, we need to make
             // sure that we are not pruning dead connections anymore.
             ConditionallyStopPruneDeadConnectionsTimer();
         }
-    }
-
-    if (!conn) {
-
-        // If the onlyReusedConnection parameter is TRUE, then GetConnection()
-        // does not create new transports under any circumstances.
-        if (onlyReusedConnection)
-            return;
-        
-        if (gHttpHandler->IsSpdyEnabled() &&
-            ent->mConnInfo->UsingSSL() &&
-            !ent->mConnInfo->UsingHttpProxy())
-        {
-            // If this host is trying to negotiate a SPDY session right now,
-            // don't create any new connections until the result of the
-            // negotiation is known.
-    
-            if ((!ent->mTestedSpdy || ent->mUsingSpdy) &&
-                (ent->mHalfOpens.Length() || ent->mActiveConns.Length()))
-                return;
+        if (conn) {
+            // This will update the class of the connection to be the class of
+            // the transaction dispatched on it.
+            AddActiveConn(conn, ent);
+            DispatchTransaction(ent, trans, conn);
+            return NS_OK;
         }
-        
-        // Check if we need to purge an idle connection. Note that we may have
-        // removed one above; if so, this will be a no-op. We do this before
-        // checking the active connection limit to catch the case where we do
-        // have an idle connection, but the purge timer hasn't fired yet.
-        // XXX this just purges a random idle connection.  we should instead
-        // enumerate the entire hash table to find the eldest idle connection.
-        if (mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 >= mMaxConns)
-            mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
-
-        // Need to make a new TCP connection. First, we check if we've hit
-        // either the maximum connection limit globally or for this particular
-        // host or proxy. If we have, we're done.
-        if (AtActiveConnectionLimit(ent, trans->Caps())) {
-            LOG(("nsHttpConnectionMgr::GetConnection [ci = %s]"
-                 "at active connection limit - will queue\n",
-                 ent->mConnInfo->HashKey().get()));
-            return;
+    }
+
+    // step 3
+    // consider pipelining scripts and revalidations
+    if (!attemptedOptimisticPipeline &&
+        (classification == nsHttpTransaction::CLASS_REVALIDATION ||
+         classification == nsHttpTransaction::CLASS_SCRIPT)) {
+        attemptedOptimisticPipeline = true;
+        if (AddToShortestPipeline(ent, trans,
+                                  classification,
+                                  mMaxOptimisticPipelinedRequests)) {
+            return NS_OK;
         }
-
-        LOG(("nsHttpConnectionMgr::GetConnection Open Connection "
-             "%s %s ent=%p spdy=%d",
-             ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
-             ent, ent->mUsingSpdy));
-        
-        nsresult rv = CreateTransport(ent, trans);
-        if (NS_FAILED(rv))
-            trans->Close(rv);
-        return;
+    }
+
+    // step 4
+    if (!onlyReusedConnection && MakeNewConnection(ent, trans)) {
+        return NS_ERROR_IN_PROGRESS;
     }
-
-    if (addConnToActiveList) {
-        // hold an owning ref to this connection
-        ent->mActiveConns.AppendElement(conn);
-        mNumActiveConns++;
+    
+    // step 5
+    if (caps & NS_HTTP_ALLOW_PIPELINING) {
+        if (AddToShortestPipeline(ent, trans,
+                                  classification,
+                                  mMaxPipelinedRequests)) {
+            return NS_OK;
+        }
     }
     
-    NS_ADDREF(conn);
-    *result = conn;
+    // step 6
+    return NS_ERROR_NOT_AVAILABLE;                /* queue it */
+}
+
+nsresult
+nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
+                                         nsHttpTransaction *trans,
+                                         nsHttpConnection *conn)
+{
+    PRUint8 caps = trans->Caps();
+    PRInt32 priority = trans->Priority();
+
+    LOG(("nsHttpConnectionMgr::DispatchTransaction "
+         "[ci=%s trans=%x caps=%x conn=%x priority=%d]\n",
+         ent->mConnInfo->HashKey().get(), trans, caps, conn, priority));
+
+    if (conn->UsingSpdy()) {
+        LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
+             "Connection host = %s\n",
+             trans->ConnectionInfo()->Host(),
+             conn->ConnectionInfo()->Host()));
+        nsresult rv = conn->Activate(trans, caps, priority);
+        NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
+        return rv;
+    }
+
+    NS_ABORT_IF_FALSE(conn && !conn->Transaction(),
+                      "DispatchTranaction() on non spdy active connection");
+
+    /* Use pipeline datastructure even if connection does not currently qualify
+       to pipeline this transaction because a different pipeline-eligible
+        transaction might be placed on the active connection */
+
+    nsRefPtr<nsHttpPipeline> pipeline;
+    nsresult rv = BuildPipeline(ent, trans, getter_AddRefs(pipeline));
+    if (!NS_SUCCEEDED(rv))
+        return rv;
+
+    nsRefPtr<nsConnectionHandle> handle = new nsConnectionHandle(conn);
+
+    // give the transaction the indirect reference to the connection.
+    pipeline->SetConnection(handle);
+
+    if (!(caps & NS_HTTP_ALLOW_PIPELINING))
+        conn->Classify(nsAHttpTransaction::CLASS_SOLO);
+    else
+        conn->Classify(trans->Classification());
+
+    rv = conn->Activate(pipeline, caps, priority);
+    if (NS_FAILED(rv)) {
+        LOG(("  conn->Activate failed [rv=%x]\n", rv));
+        ent->mActiveConns.RemoveElement(conn);
+        if (conn == ent->mYellowConnection)
+            ent->OnYellowComplete();
+        mNumActiveConns--;
+        // sever back references to connection, and do so without triggering
+        // a call to ReclaimConnection ;-)
+        pipeline->SetConnection(nsnull);
+        NS_RELEASE(handle->mConn);
+        // destroy the connection
+        NS_RELEASE(conn);
+    }
+
+    // As pipeline goes out of scope it will drop the last refernece to the
+    // pipeline if activation failed, in which case this will destroy
+    // the pipeline, which will cause each the transactions owned by the 
+    // pipeline to be restarted.
+
+    return rv;
 }
 
+nsresult
+nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
+                                   nsAHttpTransaction *firstTrans,
+                                   nsHttpPipeline **result)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+    /* form a pipeline here even if nothing is pending so that we
+       can stream-feed it as new transactions arrive */
+
+    /* the first transaction can go in unconditionally - 1 transaction
+       on a nsHttpPipeline object is not a real HTTP pipeline */
+   
+    nsRefPtr<nsHttpPipeline> pipeline = new nsHttpPipeline();
+    pipeline->AddTransaction(firstTrans);
+    NS_ADDREF(*result = pipeline);
+    return NS_OK;
+}
+
+nsresult
+nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+    // since "adds" and "cancels" are processed asynchronously and because
+    // various events might trigger an "add" directly on the socket thread,
+    // we must take care to avoid dispatching a transaction that has already
+    // been canceled (see bug 190001).
+    if (NS_FAILED(trans->Status())) {
+        LOG(("  transaction was canceled... dropping event!\n"));
+        return NS_OK;
+    }
+
+    nsresult rv = NS_OK;
+    nsHttpConnectionInfo *ci = trans->ConnectionInfo();
+    NS_ASSERTION(ci, "no connection info");
+
+    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    if (!ent) {
+        nsHttpConnectionInfo *clone = ci->Clone();
+        if (!clone)
+            return NS_ERROR_OUT_OF_MEMORY;
+        ent = new nsConnectionEntry(clone);
+        mCT.Put(ci->HashKey(), ent);
+    }
+
+    // SPDY coalescing of hostnames means we might redirect from this
+    // connection entry onto the preferred one.
+    nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
+    if (preferredEntry && (preferredEntry != ent)) {
+        LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
+             "redirected via coalescing from %s to %s\n", trans,
+             ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
+
+        ent = preferredEntry;
+    }
+
+    // If we are doing a force reload then close out any existing conns
+    // to this host so that changes in DNS, LBs, etc.. are reflected
+    if (trans->Caps() & NS_HTTP_CLEAR_KEEPALIVES)
+        ClosePersistentConnections(ent);
+
+    // Check if the transaction already has a sticky reference to a connection.
+    // If so, then we can just use it directly by transferring its reference
+    // to the new connection variable instead of searching for a new one
+
+    nsAHttpConnection *wrappedConnection = trans->Connection();
+    nsRefPtr<nsHttpConnection> conn;
+    if (wrappedConnection)
+        conn = dont_AddRef(wrappedConnection->TakeHttpConnection());
+
+    if (conn) {
+        NS_ASSERTION(trans->Caps() & NS_HTTP_STICKY_CONNECTION,
+                     "unexpected caps");
+        NS_ABORT_IF_FALSE(((PRInt32)ent->mActiveConns.IndexOf(conn)) != -1,
+                          "Sticky Connection Not In Active List");
+        trans->SetConnection(nsnull);
+        rv = DispatchTransaction(ent, trans, conn);
+    }
+    else
+        rv = TryDispatchTransaction(ent, false, trans);
+
+    if (NS_FAILED(rv)) {
+        LOG(("  adding transaction to pending queue "
+             "[trans=%p pending-count=%u]\n",
+             trans, ent->mPendingQ.Length()+1));
+        // put this transaction on the pending queue...
+        InsertTransactionSorted(ent->mPendingQ, trans);
+        NS_ADDREF(trans);
+    }
+
+    return NS_OK;
+}
+
+
 void
 nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
                                    nsConnectionEntry *ent)
 {
     NS_ADDREF(conn);
     ent->mActiveConns.AppendElement(conn);
     mNumActiveConns++;
     ActivateTimeoutTick();
@@ -1196,200 +1589,16 @@ nsHttpConnectionMgr::CreateTransport(nsC
     nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans);
     nsresult rv = sock->SetupPrimaryStreams();
     NS_ENSURE_SUCCESS(rv, rv);
 
     ent->mHalfOpens.AppendElement(sock);
     return NS_OK;
 }
 
-nsresult
-nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
-                                         nsHttpTransaction *aTrans,
-                                         PRUint8 caps,
-                                         nsHttpConnection *conn)
-{
-    LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
-        ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
-    nsresult rv;
-    
-    PRInt32 priority = aTrans->Priority();
-
-    if (conn->UsingSpdy()) {
-        LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
-             "Connection host = %s\n",
-             aTrans->ConnectionInfo()->Host(),
-             conn->ConnectionInfo()->Host()));
-        rv = conn->Activate(aTrans, caps, priority);
-        NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
-        return rv;
-    }
-
-    nsConnectionHandle *handle = new nsConnectionHandle(conn);
-    if (!handle)
-        return NS_ERROR_OUT_OF_MEMORY;
-    NS_ADDREF(handle);
-
-    nsHttpPipeline *pipeline = nsnull;
-    nsAHttpTransaction *trans = aTrans;
-
-    if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
-        LOG(("  looking to build pipeline...\n"));
-        if (BuildPipeline(ent, trans, &pipeline))
-            trans = pipeline;
-    }
-
-    // give the transaction the indirect reference to the connection.
-    trans->SetConnection(handle);
-
-    rv = conn->Activate(trans, caps, priority);
-
-    if (NS_FAILED(rv)) {
-        LOG(("  conn->Activate failed [rv=%x]\n", rv));
-        ent->mActiveConns.RemoveElement(conn);
-        mNumActiveConns--;
-        // sever back references to connection, and do so without triggering
-        // a call to ReclaimConnection ;-)
-        trans->SetConnection(nsnull);
-        NS_RELEASE(handle->mConn);
-        // destroy the connection
-        NS_RELEASE(conn);
-    }
-
-    // if we were unable to activate the pipeline, then this will destroy
-    // the pipeline, which will cause each the transactions owned by the 
-    // pipeline to be restarted.
-    NS_IF_RELEASE(pipeline);
-
-    NS_RELEASE(handle);
-    return rv;
-}
-
-bool
-nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
-                                   nsAHttpTransaction *firstTrans,
-                                   nsHttpPipeline **result)
-{
-    if (mMaxPipelinedRequests < 2)
-        return false;
-
-    nsHttpPipeline *pipeline = nsnull;
-    nsHttpTransaction *trans;
-
-    PRUint32 i = 0, numAdded = 0;
-    while (i < ent->mPendingQ.Length()) {
-        trans = ent->mPendingQ[i];
-        if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
-            if (numAdded == 0) {
-                pipeline = new nsHttpPipeline;
-                if (!pipeline)
-                    return false;
-                pipeline->AddTransaction(firstTrans);
-                numAdded = 1;
-            }
-            pipeline->AddTransaction(trans);
-
-            // remove transaction from pending queue
-            ent->mPendingQ.RemoveElementAt(i);
-            NS_RELEASE(trans);
-
-            if (++numAdded == mMaxPipelinedRequests)
-                break;
-        }
-        else
-            ++i; // skip to next pending transaction
-    }
-
-    if (numAdded == 0)
-        return false;
-
-    LOG(("  pipelined %u transactions\n", numAdded));
-    NS_ADDREF(*result = pipeline);
-    return true;
-}
-
-nsresult
-nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
-{
-    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
-
-    // since "adds" and "cancels" are processed asynchronously and because
-    // various events might trigger an "add" directly on the socket thread,
-    // we must take care to avoid dispatching a transaction that has already
-    // been canceled (see bug 190001).
-    if (NS_FAILED(trans->Status())) {
-        LOG(("  transaction was canceled... dropping event!\n"));
-        return NS_OK;
-    }
-
-    PRUint8 caps = trans->Caps();
-    nsHttpConnectionInfo *ci = trans->ConnectionInfo();
-    NS_ASSERTION(ci, "no connection info");
-
-    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
-    if (!ent) {
-        nsHttpConnectionInfo *clone = ci->Clone();
-        if (!clone)
-            return NS_ERROR_OUT_OF_MEMORY;
-        ent = new nsConnectionEntry(clone);
-        if (!ent)
-            return NS_ERROR_OUT_OF_MEMORY;
-        mCT.Put(ci->HashKey(), ent);
-    }
-
-    // SPDY coalescing of hostnames means we might redirect from this
-    // connection entry onto the preferred one.
-    nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
-    if (preferredEntry && (preferredEntry != ent)) {
-        LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
-             "redirected via coalescing from %s to %s\n", trans,
-             ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
-
-        ent = preferredEntry;
-    }
-
-    // If we are doing a force reload then close out any existing conns
-    // to this host so that changes in DNS, LBs, etc.. are reflected
-    if (caps & NS_HTTP_CLEAR_KEEPALIVES)
-        ClosePersistentConnections(ent);
-
-    // Check if the transaction already has a sticky reference to a connection.
-    // If so, then we can just use it directly by transferring its reference
-    // to the new connection var instead of calling GetConnection() to search
-    // for an available one.
-
-    nsAHttpConnection *wrappedConnection = trans->Connection();
-    nsHttpConnection  *conn;
-    conn = wrappedConnection ? wrappedConnection->TakeHttpConnection() : nsnull;
-
-    if (conn) {
-        NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
-
-        trans->SetConnection(nsnull);
-    }
-    else
-        GetConnection(ent, trans, false, &conn);
-
-    nsresult rv;
-    if (!conn) {
-        LOG(("  adding transaction to pending queue [trans=%x pending-count=%u]\n",
-            trans, ent->mPendingQ.Length()+1));
-        // put this transaction on the pending queue...
-        InsertTransactionSorted(ent->mPendingQ, trans);
-        NS_ADDREF(trans);
-        rv = NS_OK;
-    }
-    else {
-        rv = DispatchTransaction(ent, trans, caps, conn);
-        NS_RELEASE(conn);
-    }
-
-    return rv;
-}
-
 // This function tries to dispatch the pending spdy transactions on
 // the connection entry sent in as an argument. It will do so on the
 // active spdy connection either in that same entry or in the
 // redirected 'preferred' entry for the same coalescing hash key if
 // coalescing is enabled.
 
 void
 nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
@@ -1404,17 +1613,17 @@ nsHttpConnectionMgr::ProcessSpdyPendingQ
         nsHttpTransaction *trans = ent->mPendingQ[index];
 
         if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
             trans->Caps() & NS_HTTP_DISALLOW_SPDY)
             continue;
  
         ent->mPendingQ.RemoveElementAt(index);
 
-        nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
+        nsresult rv = DispatchTransaction(ent, trans, conn);
         if (NS_FAILED(rv)) {
             // this cannot happen, but if due to some bug it does then
             // close the transaction
             NS_ABORT_IF_FALSE(false, "Dispatch SPDY Transaction");
             LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
                     trans));
             trans->Close(rv);
         }
@@ -1469,16 +1678,17 @@ nsHttpConnectionMgr::GetSpdyPreferredCon
     return conn;
 }
 
 //-----------------------------------------------------------------------------
 
 void
 nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
 {
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
 
     mCT.Enumerate(ShutdownPassCB, this);
 
     if (mReadTimeoutTick) {
         mReadTimeoutTick->Cancel();
         mReadTimeoutTick = nsnull;
         mReadTimeoutTickArmed = false;
@@ -1500,17 +1710,18 @@ nsHttpConnectionMgr::OnMsgNewTransaction
     if (NS_FAILED(rv))
         trans->Close(rv); // for whatever its worth
     NS_RELEASE(trans);
 }
 
 void
 nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
 {
-    LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
 
     nsHttpTransaction *trans = (nsHttpTransaction *) param;
     trans->SetPriority(priority);
 
     nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
                                                    nsnull, trans);
 
     if (ent) {
@@ -1522,16 +1733,17 @@ nsHttpConnectionMgr::OnMsgReschedTransac
     }
 
     NS_RELEASE(trans);
 }
 
 void
 nsHttpConnectionMgr::OnMsgCancelTransaction(PRInt32 reason, void *param)
 {
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
 
     nsHttpTransaction *trans = (nsHttpTransaction *) param;
     //
     // if the transaction owns a connection and the transaction is not done,
     // then ask the connection to close the transaction.  otherwise, close the
     // transaction directly (removing it from the pending queue first).
     //
@@ -1553,16 +1765,17 @@ nsHttpConnectionMgr::OnMsgCancelTransact
         trans->Close(reason);
     }
     NS_RELEASE(trans);
 }
 
 void
 nsHttpConnectionMgr::OnMsgProcessPendingQ(PRInt32, void *param)
 {
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
 
     LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
 
     // start by processing the queue identified by the given connection info.
     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
     if (!(ent && ProcessPendingQForEntry(ent))) {
         // if we reach here, it means that we couldn't dispatch a transaction
@@ -1571,16 +1784,17 @@ nsHttpConnectionMgr::OnMsgProcessPending
     }
 
     NS_RELEASE(ci);
 }
 
 void
 nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
 {
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
 
     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
     mTimeOfNextWakeUp = LL_MAXUINT;
 
     // check canreuse() for all idle connections plus any active connections on
     // connection entries that are using spdy.
     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
@@ -1593,16 +1807,17 @@ nsHttpConnectionMgr::OnMsgClosePersisten
     LOG(("nsHttpConnectionMgr::OnMsgClosePersistentConnections\n"));
 
     mCT.Enumerate(ClosePersistentConnectionsCB, this);
 }
 
 void
 nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
 {
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
 
     nsHttpConnection *conn = (nsHttpConnection *) param;
 
     // 
     // 1) remove the connection from the active list
     // 2) if keep-alive, add connection to idle list
     // 3) post event to process the pending transaction queue
@@ -1632,16 +1847,18 @@ nsHttpConnectionMgr::OnMsgReclaimConnect
             // conns. Even when they have 0 transactions on them they are
             // considered active connections. So when one is reclaimed it
             // is really complete and is meant to be shut down and not
             // reused.
             conn->DontReuse();
         }
         
         if (ent->mActiveConns.RemoveElement(conn)) {
+            if (conn == ent->mYellowConnection)
+                ent->OnYellowComplete();
             nsHttpConnection *temp = conn;
             NS_RELEASE(temp);
             mNumActiveConns--;
         }
 
         if (conn->CanReuse()) {
             LOG(("  adding connection to idle list\n"));
             // Keep The idle connection list sorted with the connections that
@@ -1667,17 +1884,16 @@ nsHttpConnectionMgr::OnMsgReclaimConnect
             // time to live among the watched connections, pruning dead
             // connections needs to be done when it can't be reused anymore.
             PRUint32 timeToLive = conn->TimeToLive();
             if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
                 PruneDeadConnectionsAfter(timeToLive);
         }
         else {
             LOG(("  connection cannot be reused; closing connection\n"));
-            // make sure the connection is closed and release our reference.
             conn->Close(NS_ERROR_ABORT);
         }
     }
  
     OnMsgProcessPendingQ(NS_OK, ci); // releases |ci|
     NS_RELEASE(conn);
 }
 
@@ -1704,45 +1920,52 @@ nsHttpConnectionMgr::OnMsgUpdateParam(PR
         mMaxPersistConnsPerProxy = value;
         break;
     case MAX_REQUEST_DELAY:
         mMaxRequestDelay = value;
         break;
     case MAX_PIPELINED_REQUESTS:
         mMaxPipelinedRequests = value;
         break;
+    case MAX_OPTIMISTIC_PIPELINED_REQUESTS:
+        mMaxOptimisticPipelinedRequests = value;
+        break;
     default:
         NS_NOTREACHED("unexpected parameter name");
     }
 }
 
 // nsHttpConnectionMgr::nsConnectionEntry
 nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
 {
     if (mSpdyPreferred)
         gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
 
     NS_RELEASE(mConnInfo);
 }
 
+void
+nsHttpConnectionMgr::OnMsgProcessFeedback(PRInt32, void *param)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    nsHttpPipelineFeedback *fb = (nsHttpPipelineFeedback *)param;
+    
+    PipelineFeedbackInfo(fb->mConnInfo, fb->mInfo, fb->mConn, fb->mData);
+    delete fb;
+}
+
 // Read Timeout Tick handlers
 
 void
 nsHttpConnectionMgr::ActivateTimeoutTick()
 {
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
          "this=%p mReadTimeoutTick=%p\n"));
 
-    // right now the spdy timeout code is the only thing hooked to the timeout
-    // tick, so disable it if spdy is not being used. However pipelining code
-    // will also want this functionality soon.
-    if (!gHttpHandler->IsSpdyEnabled())
-        return;
-
     // The timer tick should be enabled if it is not already pending.
     // Upon running the tick will rearm itself if there are active
     // connections available.
 
     if (mReadTimeoutTick && mReadTimeoutTickArmed)
         return;
 
     if (!mReadTimeoutTick) {
@@ -1750,18 +1973,17 @@ nsHttpConnectionMgr::ActivateTimeoutTick
         if (!mReadTimeoutTick) {
             NS_WARNING("failed to create timer for http timeout management");
             return;
         }
     }
 
     NS_ABORT_IF_FALSE(!mReadTimeoutTickArmed, "timer tick armed");
     mReadTimeoutTickArmed = true;
-    // pipeline will expect a 1000ms granuality
-    mReadTimeoutTick->Init(this, 15000, nsITimer::TYPE_REPEATING_SLACK);
+    mReadTimeoutTick->Init(this, 1000, nsITimer::TYPE_REPEATING_SLACK);
 }
 
 void
 nsHttpConnectionMgr::ReadTimeoutTick()
 {
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ABORT_IF_FALSE(mReadTimeoutTick, "no readtimeout tick");
 
@@ -1862,16 +2084,22 @@ nsHttpConnectionMgr::nsConnectionHandle:
 }
 
 bool
 nsHttpConnectionMgr::nsConnectionHandle::IsReused()
 {
     return mConn->IsReused();
 }
 
+void
+nsHttpConnectionMgr::nsConnectionHandle::DontReuse()
+{
+    mConn->DontReuse();
+}
+
 nsresult
 nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
 {
     return mConn->PushBack(buf, bufLen);
 }
 
 
 //////////////////////// nsHalfOpenSocket
@@ -1986,16 +2214,17 @@ nsHalfOpenSocket::SetupStreams(nsISocket
 
 nsresult
 nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
 {
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     nsresult rv;
 
+    mPrimarySynStarted = mozilla::TimeStamp::Now();
     rv = SetupStreams(getter_AddRefs(mSocketTransport),
                       getter_AddRefs(mStreamIn),
                       getter_AddRefs(mStreamOut),
                       false);
     LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
          this, mEnt->mConnInfo->Host(), rv));
     if (NS_FAILED(rv)) {
         if (mStreamOut)
@@ -2005,16 +2234,17 @@ nsHttpConnectionMgr::nsHalfOpenSocket::S
         mSocketTransport = nsnull;
     }
     return rv;
 }
 
 nsresult
 nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
 {
+    mBackupSynStarted = mozilla::TimeStamp::Now();
     nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
                                getter_AddRefs(mBackupStreamIn),
                                getter_AddRefs(mBackupStreamOut),
                                true);
     LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
          this, mEnt->mConnInfo->Host(), rv));
     if (NS_FAILED(rv)) {
         if (mBackupStreamOut)
@@ -2126,31 +2356,38 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
     LOG(("nsHalfOpenSocket::OnOutputStreamReady "
          "Created new nshttpconnection %p\n", conn.get()));
 
     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     nsCOMPtr<nsIEventTarget>        callbackTarget;
     mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks),
                                        getter_AddRefs(callbackTarget));
     if (out == mStreamOut) {
+        mozilla::TimeDuration rtt = 
+            mozilla::TimeStamp::Now() - mPrimarySynStarted;
         rv = conn->Init(mEnt->mConnInfo,
                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
                         mSocketTransport, mStreamIn, mStreamOut,
-                        callbacks, callbackTarget);
+                        callbacks, callbackTarget,
+                        PR_MillisecondsToInterval(rtt.ToMilliseconds()));
 
         // The nsHttpConnection object now owns these streams and sockets
         mStreamOut = nsnull;
         mStreamIn = nsnull;
         mSocketTransport = nsnull;
     }
     else {
+        mozilla::TimeDuration rtt = 
+            mozilla::TimeStamp::Now() - mBackupSynStarted;
+        
         rv = conn->Init(mEnt->mConnInfo,
                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
                         mBackupTransport, mBackupStreamIn, mBackupStreamOut,
-                        callbacks, callbackTarget);
+                        callbacks, callbackTarget,
+                        PR_MillisecondsToInterval(rtt.ToMilliseconds()));
 
         // The nsHttpConnection object now owns these streams and sockets
         mBackupStreamOut = nsnull;
         mBackupStreamIn = nsnull;
         mBackupTransport = nsnull;
     }
 
     if (NS_FAILED(rv)) {
@@ -2162,17 +2399,16 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
     // if this is still in the pending list, remove it and dispatch it
     index = mEnt->mPendingQ.IndexOf(mTransaction);
     if (index != -1) {
         mEnt->mPendingQ.RemoveElementAt(index);
         nsHttpTransaction *temp = mTransaction;
         NS_RELEASE(temp);
         gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
         rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, mTransaction,
-                                                          mTransaction->Caps(),
                                                           conn);
     }
     else {
         // this transaction was dispatched off the pending q before all the
         // sockets established themselves.
 
         // We need to establish a small non-zero idle timeout so the connection
         // mgr perceives this socket as suitable for persistent connection reuse
@@ -2293,16 +2529,301 @@ nsHttpConnectionMgr::nsConnectionHandle:
 
     NS_ASSERTION(mConn, "no connection");
     nsHttpConnection *conn = mConn;
     mConn = nsnull;
     return conn;
 }
 
 bool
+nsHttpConnectionMgr::nsConnectionHandle::IsProxyConnectInProgress()
+{
+    return mConn->IsProxyConnectInProgress();
+}
+
+PRUint32
+nsHttpConnectionMgr::nsConnectionHandle::CancelPipeline(nsresult reason)
+{
+    // no pipeline to cancel
+    return 0;
+}
+
+nsAHttpTransaction::Classifier
+nsHttpConnectionMgr::nsConnectionHandle::Classification()
+{
+    if (mConn)
+        return mConn->Classification();
+
+    LOG(("nsConnectionHandle::Classification this=%p "
+         "has null mConn using CLASS_SOLO default", this));
+    return nsAHttpTransaction::CLASS_SOLO;
+}
+
+void
+nsHttpConnectionMgr::
+nsConnectionHandle::Classify(nsAHttpTransaction::Classifier newclass)
+{
+    if (mConn)
+        mConn->Classify(newclass);
+}
+
+// nsConnectionEntry
+
+nsHttpConnectionMgr::
+nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
+    : mConnInfo(ci)
+    , mPipelineState(PS_YELLOW)
+    , mYellowGoodEvents(0)
+    , mYellowBadEvents(0)
+    , mYellowConnection(nsnull)
+    , mGreenDepth(kPipelineOpen)
+    , mPipeliningPenalty(0)
+    , mUsingSpdy(false)
+    , mTestedSpdy(false)
+    , mSpdyPreferred(false)
+{
+    NS_ADDREF(mConnInfo);
+    if (gHttpHandler->GetPipelineAggressive()) {
+        mGreenDepth = kPipelineUnlimited;
+        mPipelineState = PS_GREEN;
+    }
+    mInitialGreenDepth = mGreenDepth;
+    memset(mPipeliningClassPenalty, 0, sizeof(PRInt16) * nsAHttpTransaction::CLASS_MAX);
+}
+
+bool
+nsHttpConnectionMgr::nsConnectionEntry::SupportsPipelining()
+{
+    return mPipelineState != nsHttpConnectionMgr::PS_RED;
+}
+
+nsHttpConnectionMgr::PipeliningState
+nsHttpConnectionMgr::nsConnectionEntry::PipelineState()
+{
+    return mPipelineState;
+}
+    
+void
+nsHttpConnectionMgr::
+nsConnectionEntry::OnPipelineFeedbackInfo(
+    nsHttpConnectionMgr::PipelineFeedbackInfoType info,
+    nsHttpConnection *conn,
+    PRUint32 data)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    
+    if (mPipelineState == PS_YELLOW) {
+        if (info & kPipelineInfoTypeBad)
+            mYellowBadEvents++;
+        else if (info & (kPipelineInfoTypeNeutral | kPipelineInfoTypeGood))
+            mYellowGoodEvents++;
+    }
+    
+    if (mPipelineState == PS_GREEN && info == GoodCompletedOK) {
+        PRInt32 depth = data;
+        LOG(("Transaction completed at pipeline depty of %d. Host = %s\n",
+             depth, mConnInfo->Host()));
+
+        if (depth >= 3)
+            mGreenDepth = kPipelineUnlimited;
+    }
+
+    nsAHttpTransaction::Classifier classification;
+    if (conn)
+        classification = conn->Classification();
+    else if (info == BadInsufficientFraming ||
+             info == BadUnexpectedLarge)
+        classification = (nsAHttpTransaction::Classifier) data;
+    else
+        classification = nsAHttpTransaction::CLASS_SOLO;
+
+    if (gHttpHandler->GetPipelineAggressive() &&
+        info & kPipelineInfoTypeBad &&
+        info != BadExplicitClose &&
+        info != RedVersionTooLow &&
+        info != RedBannedServer &&
+        info != RedCorruptedContent &&
+        info != BadInsufficientFraming) {
+        LOG(("minor negative feedback ignored "
+             "because of pipeline aggressive mode"));
+    }
+    else if (info & kPipelineInfoTypeBad) {
+        if ((info & kPipelineInfoTypeRed) && (mPipelineState != PS_RED)) {
+            LOG(("transition to red from %d. Host = %s.\n",
+                 mPipelineState, mConnInfo->Host()));
+            mPipelineState = PS_RED;
+            mPipeliningPenalty = 0;
+        }
+
+        if (mLastCreditTime.IsNull())
+            mLastCreditTime = mozilla::TimeStamp::Now();
+
+        // Red* events impact the host globally via mPipeliningPenalty, while
+        // Bad* events impact the per class penalty.
+        
+        // The individual penalties should be < 16bit-signed-maxint - 25000
+        // (approx 7500). Penalties are paid-off either when something promising
+        // happens (a successful transaction, or promising headers) or when
+        // time goes by at a rate of 1 penalty point every 16 seconds.
+
+        switch (info) {
+        case RedVersionTooLow:
+            mPipeliningPenalty += 1000;
+            break;
+        case RedBannedServer:
+            mPipeliningPenalty += 7000;
+            break;
+        case RedCorruptedContent:
+            mPipeliningPenalty += 7000;
+            break;
+        case RedCanceledPipeline:
+            mPipeliningPenalty += 60;
+            break;
+        case BadExplicitClose:
+            mPipeliningClassPenalty[classification] += 250;
+            break;
+        case BadSlowReadMinor:
+            mPipeliningClassPenalty[classification] += 5;
+            break;
+        case BadSlowReadMajor:
+            mPipeliningClassPenalty[classification] += 25;
+            break;
+        case BadInsufficientFraming:
+            mPipeliningClassPenalty[classification] += 7000;
+            break;
+        case BadUnexpectedLarge:
+            mPipeliningClassPenalty[classification] += 120;
+            break;
+
+        default:
+            NS_ABORT_IF_FALSE(0, "Unknown Bad/Red Pipeline Feedback Event");
+        }
+        
+        mPipeliningPenalty = PR_MIN(mPipeliningPenalty, 25000);
+        mPipeliningClassPenalty[classification] = PR_MIN(mPipeliningClassPenalty[classification], 25000);
+            
+        LOG(("Assessing red penalty to %s class %d for event %d. "
+             "Penalty now %d, throttle[%d] = %d\n", mConnInfo->Host(),
+             classification, info, mPipeliningPenalty, classification,
+             mPipeliningClassPenalty[classification]));
+    }
+    else {
+        // hand out credits for neutral and good events such as
+        // "headers look ok" events
+
+        mPipeliningPenalty = PR_MAX(mPipeliningPenalty - 1, 0);
+        mPipeliningClassPenalty[classification] = PR_MAX(mPipeliningClassPenalty[classification] - 1, 0);
+    }
+
+    if (mPipelineState == PS_RED && !mPipeliningPenalty)
+    {
+        LOG(("transition %s to yellow\n", mConnInfo->Host()));
+        mPipelineState = PS_YELLOW;
+        mYellowConnection = nsnull;
+    }
+}
+
+void
+nsHttpConnectionMgr::
+nsConnectionEntry::SetYellowConnection(nsHttpConnection *conn)
+{
+    NS_ABORT_IF_FALSE(!mYellowConnection && mPipelineState == PS_YELLOW,
+                      "yellow connection already set or state is not yellow");
+    mYellowConnection = conn;
+    mYellowGoodEvents = mYellowBadEvents = 0;
+}
+
+void
+nsHttpConnectionMgr::nsConnectionEntry::OnYellowComplete()
+{
+    if (mPipelineState == PS_YELLOW) {
+        if (mYellowGoodEvents && !mYellowBadEvents) {
+            LOG(("transition %s to green\n", mConnInfo->Host()));
+            mPipelineState = PS_GREEN;
+            mGreenDepth = mInitialGreenDepth;
+        }
+        else {
+            // The purpose of the yellow state is to witness at least
+            // one successful pipelined transaction without seeing any
+            // kind of negative feedback before opening the flood gates.
+            // If we haven't confirmed that, then transfer back to red.
+            LOG(("transition %s to red from yellow return\n",
+                 mConnInfo->Host()));
+            mPipelineState = PS_RED;
+        }
+    }
+
+    mYellowConnection = nsnull;
+}
+
+void
+nsHttpConnectionMgr::nsConnectionEntry::CreditPenalty()
+{
+    if (mLastCreditTime.IsNull())
+        return;
+    
+    // Decrease penalty values by 1 for every 16 seconds
+    // (i.e 3.7 per minute, or 1000 every 4h20m)
+
+    mozilla::TimeStamp now = mozilla::TimeStamp::Now();
+    mozilla::TimeDuration elapsedTime = now - mLastCreditTime;
+    PRUint32 creditsEarned =
+        static_cast<PRUint32>(elapsedTime.ToSeconds()) >> 4;
+    
+    bool failed = false;
+    if (creditsEarned > 0) {
+        mPipeliningPenalty = 
+            PR_MAX(PRInt32(mPipeliningPenalty - creditsEarned), 0);
+        if (mPipeliningPenalty > 0)
+            failed = true;
+        
+        for (PRInt32 i = 0; i < nsAHttpTransaction::CLASS_MAX; ++i) {
+            mPipeliningClassPenalty[i]  =
+                PR_MAX(PRInt32(mPipeliningClassPenalty[i] - creditsEarned), 0);
+            failed = failed || (mPipeliningClassPenalty[i] > 0);
+        }
+
+        // update last credit mark to reflect elapsed time
+        mLastCreditTime +=
+            mozilla::TimeDuration::FromSeconds(creditsEarned << 4);
+    }
+    else {
+        failed = true;                         /* just assume this */
+    }
+
+    // If we are no longer red then clear the credit counter - you only
+    // get credits for time spent in the red state
+    if (!failed)
+        mLastCreditTime = mozilla::TimeStamp();    /* reset to null timestamp */
+
+    if (mPipelineState == PS_RED && !mPipeliningPenalty)
+    {
+        LOG(("transition %s to yellow based on time credit\n",
+             mConnInfo->Host()));
+        mPipelineState = PS_YELLOW;
+        mYellowConnection = nsnull;
+    }    
+}
+
+PRUint32
+nsHttpConnectionMgr::
+nsConnectionEntry::MaxPipelineDepth(nsAHttpTransaction::Classifier aClass)
+{
+    // Still subject to configuration limit no matter return value
+    
+    if ((mPipelineState == PS_RED) || (mPipeliningClassPenalty[aClass] > 0))
+        return 0;
+
+    if (mPipelineState == PS_YELLOW)
+        return kPipelineRestricted;
+
+    return mGreenDepth;
+}
+
+bool
 nsHttpConnectionMgr::nsConnectionHandle::LastTransactionExpectedNoContent()
 {
     return mConn->LastTransactionExpectedNoContent();
 }
 
 void
 nsHttpConnectionMgr::
 nsConnectionHandle::SetLastTransactionExpectedNoContent(bool val)
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -44,22 +44,23 @@
 #include "nsHttpTransaction.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsISocketTransportService.h"
+#include "mozilla/TimeStamp.h"
 
 #include "nsIObserver.h"
 #include "nsITimer.h"
 #include "nsIX509Cert3.h"
 
-class nsHttpPipeline;
+#include "nsHttpPipeline.h"
 
 //-----------------------------------------------------------------------------
 
 class nsHttpConnectionMgr : public nsIObserver
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
@@ -67,32 +68,34 @@ public:
     // parameter names
     enum nsParamName {
         MAX_CONNECTIONS,
         MAX_CONNECTIONS_PER_HOST,
         MAX_CONNECTIONS_PER_PROXY,
         MAX_PERSISTENT_CONNECTIONS_PER_HOST,
         MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
         MAX_REQUEST_DELAY,
-        MAX_PIPELINED_REQUESTS
+        MAX_PIPELINED_REQUESTS,
+        MAX_OPTIMISTIC_PIPELINED_REQUESTS
     };
 
     //-------------------------------------------------------------------------
     // NOTE: functions below may only be called on the main thread.
     //-------------------------------------------------------------------------
 
     nsHttpConnectionMgr();
 
     nsresult Init(PRUint16 maxConnections,
                   PRUint16 maxConnectionsPerHost,
                   PRUint16 maxConnectionsPerProxy,
                   PRUint16 maxPersistentConnectionsPerHost,
                   PRUint16 maxPersistentConnectionsPerProxy,
                   PRUint16 maxRequestDelay,
-                  PRUint16 maxPipelinedRequests);
+                  PRUint16 maxPipelinedRequests,
+                  PRUint16 maxOptimisticPipelinedRequests);
     nsresult Shutdown();
 
     //-------------------------------------------------------------------------
     // NOTE: functions below may be called on any thread.
     //-------------------------------------------------------------------------
 
     // Schedules next pruning of dead connection to happen after
     // given time.
@@ -133,66 +136,192 @@ public:
     // been initialized.
     nsresult UpdateParam(nsParamName name, PRUint16 value);
 
     // Lookup/Cancel HTTP->SPDY redirections
     bool GetSpdyAlternateProtocol(nsACString &key);
     void ReportSpdyAlternateProtocol(nsHttpConnection *);
     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.
     //-------------------------------------------------------------------------
 
-    // 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
     // preference to the specified connection.
     nsresult ProcessPendingQ(nsHttpConnectionInfo *);
+    bool     ProcessPendingQForEntry(nsHttpConnectionInfo *);
 
     // 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
     // that the network peer has closed the transport.
     nsresult CloseIdleConnection(nsHttpConnection *);
 
     // The connection manager needs to know when a normal HTTP connection has been
     // upgraded to SPDY because the dispatch and idle semantics are a little
     // bit different.
     void ReportSpdyConnection(nsHttpConnection *, bool usingSpdy);
 
+    
+    bool     SupportsPipelining(nsHttpConnectionInfo *);
+
 private:
     virtual ~nsHttpConnectionMgr();
+
+    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
     //
     // mCT maps connection info hash key to nsConnectionEntry object, which
     // contains list of active and idle connections as well as the list of
     // pending transactions.
     //
-    struct nsConnectionEntry
+    class nsConnectionEntry
     {
-        nsConnectionEntry(nsHttpConnectionInfo *ci)
-          : mConnInfo(ci),
-            mUsingSpdy(false),
-            mTestedSpdy(false),
-            mSpdyPreferred(false)
-        {
-            NS_ADDREF(mConnInfo);
-        }
+    public:
+        nsConnectionEntry(nsHttpConnectionInfo *ci);
         ~nsConnectionEntry();
 
         nsHttpConnectionInfo        *mConnInfo;
         nsTArray<nsHttpTransaction*> mPendingQ;    // pending transaction queue
         nsTArray<nsHttpConnection*>  mActiveConns; // active connections
         nsTArray<nsHttpConnection*>  mIdleConns;   // idle persistent connections
         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
         // to re-coalesce sharded HTTP hosts. The dotted decimal address is
         // combined with the Anonymous flag from the connection information
         // to build the hash key for hosts in the same ip pool.
         //
         // When a set of hosts are coalesced together one of them is marked
         // mSpdyPreferred. The mapping is maintained in the connection mananger
         // mSpdyPreferred hash.
@@ -267,16 +396,19 @@ private:
 
     private:
         nsConnectionEntry              *mEnt;
         nsRefPtr<nsHttpTransaction>    mTransaction;
         nsCOMPtr<nsISocketTransport>   mSocketTransport;
         nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
         nsCOMPtr<nsIAsyncInputStream>  mStreamIn;
 
+        mozilla::TimeStamp             mPrimarySynStarted;
+        mozilla::TimeStamp             mBackupSynStarted;
+
         // for syn retry
         nsCOMPtr<nsITimer>             mSynTimer;
         nsCOMPtr<nsISocketTransport>   mBackupTransport;
         nsCOMPtr<nsIAsyncOutputStream> mBackupStreamOut;
         nsCOMPtr<nsIAsyncInputStream>  mBackupStreamIn;
     };
     friend class nsHalfOpenSocket;
 
@@ -291,44 +423,57 @@ private:
     // connection limits
     PRUint16 mMaxConns;
     PRUint16 mMaxConnsPerHost;
     PRUint16 mMaxConnsPerProxy;
     PRUint16 mMaxPersistConnsPerHost;
     PRUint16 mMaxPersistConnsPerProxy;
     PRUint16 mMaxRequestDelay; // in seconds
     PRUint16 mMaxPipelinedRequests;
-
+    PRUint16 mMaxOptimisticPipelinedRequests;
     bool mIsShuttingDown;
 
     //-------------------------------------------------------------------------
     // NOTE: these members are only accessed on the socket transport thread
     //-------------------------------------------------------------------------
 
     static PLDHashOperator ProcessOneTransactionCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
 
     static PLDHashOperator PruneDeadConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
     static PLDHashOperator ShutdownPassCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
     static PLDHashOperator PurgeExcessIdleConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
     static PLDHashOperator ClosePersistentConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
     bool     ProcessPendingQForEntry(nsConnectionEntry *);
+    bool     IsUnderPressure(nsConnectionEntry *ent,
+                             nsHttpTransaction::Classifier classification);
     bool     AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
-    void     GetConnection(nsConnectionEntry *, nsHttpTransaction *,
-                           bool, nsHttpConnection **);
-    nsresult DispatchTransaction(nsConnectionEntry *, nsHttpTransaction *,
-                                 PRUint8 caps, nsHttpConnection *);
-    bool     BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
+    nsresult TryDispatchTransaction(nsConnectionEntry *ent,
+                                    bool onlyReusedConnection,
+                                    nsHttpTransaction *trans);
+    nsresult DispatchTransaction(nsConnectionEntry *,
+                                 nsHttpTransaction *,
+                                 nsHttpConnection *);
+    nsresult BuildPipeline(nsConnectionEntry *,
+                           nsAHttpTransaction *,
+                           nsHttpPipeline **);
     nsresult ProcessNewTransaction(nsHttpTransaction *);
     nsresult EnsureSocketThreadTargetIfOnline();
     void     ClosePersistentConnections(nsConnectionEntry *ent);
     nsresult CreateTransport(nsConnectionEntry *, nsHttpTransaction *);
     void     AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
     void     StartedConnect();
     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
     nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
     void               RemoveSpdyPreferredEnt(nsACString &aDottedDecimal);
     nsHttpConnection  *GetSpdyPreferredConn(nsConnectionEntry *ent);
     nsDataHashtable<nsCStringHashKey, nsConnectionEntry *>   mSpdyPreferredHash;
     nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci,
                                              nsHttpConnection *conn,
                                              nsHttpTransaction *trans);
@@ -391,16 +536,17 @@ private:
     void OnMsgNewTransaction       (PRInt32, void *);
     void OnMsgReschedTransaction   (PRInt32, void *);
     void OnMsgCancelTransaction    (PRInt32, void *);
     void OnMsgProcessPendingQ      (PRInt32, void *);
     void OnMsgPruneDeadConnections (PRInt32, void *);
     void OnMsgReclaimConnection    (PRInt32, void *);
     void OnMsgUpdateParam          (PRInt32, void *);
     void OnMsgClosePersistentConnections (PRInt32, void *);
+    void OnMsgProcessFeedback      (PRInt32, void *);
 
     // Total number of active connections in all of the ConnectionEntry objects
     // that are accessed from mCT connection table.
     PRUint16 mNumActiveConns;
     // Total number of idle connections in all of the ConnectionEntry objects
     // that are accessed from mCT connection table.
     PRUint16 mNumIdleConns;
 
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -180,21 +180,26 @@ nsHttpHandler::nsHttpHandler()
     , mSpdyTimeout(PR_SecondsToInterval(180))
     , mMaxRequestAttempts(10)
     , mMaxRequestDelay(10)
     , mIdleSynTimeout(250)
     , mMaxConnections(24)
     , mMaxConnectionsPerServer(8)
     , mMaxPersistentConnectionsPerServer(2)
     , mMaxPersistentConnectionsPerProxy(4)
-    , mMaxPipelinedRequests(2)
+    , mMaxPipelinedRequests(32)
+    , mMaxOptimisticPipelinedRequests(4)
+    , mPipelineAggressive(false)
+    , mMaxPipelineObjectSize(300000)
+    , mPipelineReadTimeout(PR_MillisecondsToInterval(10000))
     , mRedirectionLimit(10)
     , mPhishyUserPassLength(1)
     , mQoSBits(0x00)
     , mPipeliningOverSSL(false)
+    , mEnforceAssocReq(false)
     , mInPrivateBrowsingMode(PRIVATE_BROWSING_UNKNOWN)
     , mLastUniqueID(NowInSeconds())
     , mSessionStartTime(0)
     , mLegacyAppName("Mozilla")
     , mLegacyAppVersion("5.0")
     , mProduct("Gecko")
     , mUserAgentIsDirty(true)
     , mUseCache(true)
@@ -333,16 +338,17 @@ nsHttpHandler::Init()
     mObserverService = mozilla::services::GetObserverService();
     if (mObserverService) {
         mObserverService->AddObserver(this, "profile-change-net-teardown", true);
         mObserverService->AddObserver(this, "profile-change-net-restore", true);
         mObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
         mObserverService->AddObserver(this, "net:clear-active-logins", true);
         mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
         mObserverService->AddObserver(this, "net:prune-dead-connections", true);
+        mObserverService->AddObserver(this, "net:failed-to-process-uri", true);
     }
  
     return NS_OK;
 }
 
 nsresult
 nsHttpHandler::InitConnectionMgr()
 {
@@ -358,17 +364,18 @@ nsHttpHandler::InitConnectionMgr()
     }
 
     rv = mConnMgr->Init(mMaxConnections,
                         mMaxConnectionsPerServer,
                         mMaxConnectionsPerServer,
                         mMaxPersistentConnectionsPerServer,
                         mMaxPersistentConnectionsPerProxy,
                         mMaxRequestDelay,
-                        mMaxPipelinedRequests);
+                        mMaxPipelinedRequests,
+                        mMaxOptimisticPipelinedRequests);
     return rv;
 }
 
 nsresult
 nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request,
                                          PRUint8 caps,
                                          bool useProxy)
 {
@@ -1010,23 +1017,57 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
             else
                 mCapabilities &= ~NS_HTTP_ALLOW_PIPELINING;
         }
     }
 
     if (PREF_CHANGED(HTTP_PREF("pipelining.maxrequests"))) {
         rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxrequests"), &val);
         if (NS_SUCCEEDED(rv)) {
-            mMaxPipelinedRequests = clamped(val, 1, NS_HTTP_MAX_PIPELINED_REQUESTS);
+            mMaxPipelinedRequests = clamped(val, 1, 0xffff);
             if (mConnMgr)
                 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PIPELINED_REQUESTS,
                                       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"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("pipelining.ssl"), &cVar);
         if (NS_SUCCEEDED(rv))
             mPipeliningOverSSL = cVar;
     }
 
     if (PREF_CHANGED(HTTP_PREF("proxy.pipelining"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("proxy.pipelining"), &cVar);
@@ -1098,16 +1139,23 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
 
     if (PREF_CHANGED(HTTP_PREF("prompt-temp-redirect"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("prompt-temp-redirect"), &cVar);
         if (NS_SUCCEEDED(rv)) {
             mPromptTempRedirect = cVar;
         }
     }
 
+    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    
     if (PREF_CHANGED(BROWSER_PREF("disk_cache_ssl"))) {
         cVar = false;
         rv = prefs->GetBoolPref(BROWSER_PREF("disk_cache_ssl"), &cVar);
         if (NS_SUCCEEDED(rv))
             mEnablePersistentHttpsCaching = cVar;
     }
 
@@ -1582,16 +1630,21 @@ nsHttpHandler::Observe(nsISupports *subj
         if (mConnMgr)
             mConnMgr->ClosePersistentConnections();
     }
     else if (strcmp(topic, "net:prune-dead-connections") == 0) {
         if (mConnMgr) {
             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;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpsHandler implementation
 //-----------------------------------------------------------------------------
 
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -105,16 +105,17 @@ public:
     PRUint16       MaxRequestAttempts()      { return mMaxRequestAttempts; }
     const char    *DefaultSocketType()       { return mDefaultSocketType.get(); /* ok to return null */ }
     nsIIDNService *IDNConverter()            { return mIDNConverter; }
     PRUint32       PhishyUserPassLength()    { return mPhishyUserPassLength; }
     PRUint8        GetQoSBits()              { return mQoSBits; }
     PRUint16       GetIdleSynTimeout()       { return mIdleSynTimeout; }
     bool           FastFallbackToIPv4()      { return mFastFallbackToIPv4; }
     PRUint32       MaxSocketCount();
+    bool           EnforceAssocReq()         { return mEnforceAssocReq; }
 
     bool           IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; }
     bool           IsTelemetryEnabled() { return mTelemetryEnabled; }
     bool           AllowExperiments() { return mTelemetryEnabled && mAllowExperiments; }
 
     bool           IsSpdyEnabled() { return mEnableSpdy; }
     bool           CoalesceSpdy() { return mCoalesceSpdy; }
     bool           UseAlternateProtocol() { return mUseAlternateProtocol; }
@@ -226,16 +227,23 @@ public:
         NotifyObservers(chan, NS_HTTP_ON_EXAMINE_CACHED_RESPONSE_TOPIC);
     }
 
     // Generates the host:port string for use in the Host: header as well as the
     // CONNECT line for proxies. This handles IPv6 literals correctly.
     static nsresult GenerateHostPort(const nsCString& host, PRInt32 port,
                                      nsCString& hostLine);
 
+    bool GetPipelineAggressive()     { return mPipelineAggressive; }
+    void GetMaxPipelineObjectSize(PRInt64 &outVal)
+    {
+        outVal = mMaxPipelineObjectSize;
+    }
+    PRIntervalTime GetPipelineTimeout()   { return mPipelineReadTimeout; }
+
 private:
 
     //
     // Useragent/prefs helper methods
     //
     void     BuildUserAgent();
     void     InitUserAgentComponents();
     void     PrefsChanged(nsIPrefBranch *prefs, const char *pref);
@@ -282,29 +290,35 @@ private:
     PRUint16 mMaxRequestAttempts;
     PRUint16 mMaxRequestDelay;
     PRUint16 mIdleSynTimeout;
 
     PRUint16 mMaxConnections;
     PRUint8  mMaxConnectionsPerServer;
     PRUint8  mMaxPersistentConnectionsPerServer;
     PRUint8  mMaxPersistentConnectionsPerProxy;
-    PRUint8  mMaxPipelinedRequests;
+    PRUint16 mMaxPipelinedRequests;
+    PRUint16 mMaxOptimisticPipelinedRequests;
+    bool     mPipelineAggressive;
+    PRInt64  mMaxPipelineObjectSize;
+
+    PRIntervalTime mPipelineReadTimeout;
 
     PRUint8  mRedirectionLimit;
 
     // we'll warn the user if we load an URL containing a userpass field
     // unless its length is less than this threshold.  this warning is
     // intended to protect the user against spoofing attempts that use
     // the userpass field of the URL to obscure the actual origin server.
     PRUint8  mPhishyUserPassLength;
 
     PRUint8  mQoSBits;
 
     bool mPipeliningOverSSL;
+    bool mEnforceAssocReq;
 
     // cached value of whether or not the browser is in private browsing mode.
     enum {
         PRIVATE_BROWSING_OFF = false,
         PRIVATE_BROWSING_ON = true,
         PRIVATE_BROWSING_UNKNOWN = 2
     } mInPrivateBrowsingMode;
 
--- a/netwerk/protocol/http/nsHttpPipeline.cpp
+++ b/netwerk/protocol/http/nsHttpPipeline.cpp
@@ -93,16 +93,17 @@ private:
 //-----------------------------------------------------------------------------
 
 nsHttpPipeline::nsHttpPipeline()
     : mConnection(nsnull)
     , mStatus(NS_OK)
     , mRequestIsPartial(false)
     , mResponseIsPartial(false)
     , mClosed(false)
+    , mUtilizedPipeline(false)
     , mPushBackBuf(nsnull)
     , mPushBackLen(0)
     , mPushBackMax(0)
     , mHttp1xTransactionCount(0)
     , mReceivingFromProgress(0)
     , mSendingToProgress(0)
     , mSuppressSendEvents(true)
 {
@@ -119,29 +120,72 @@ nsHttpPipeline::~nsHttpPipeline()
         free(mPushBackBuf);
 }
 
 nsresult
 nsHttpPipeline::AddTransaction(nsAHttpTransaction *trans)
 {
     LOG(("nsHttpPipeline::AddTransaction [this=%x trans=%x]\n", this, trans));
 
+    if (mRequestQ.Length() || mResponseQ.Length())
+        mUtilizedPipeline = true;
+
     NS_ADDREF(trans);
     mRequestQ.AppendElement(trans);
-
-    if (mConnection && !mClosed) {
-        trans->SetConnection(this);
-
-        if (mRequestQ.Length() == 1)
-            mConnection->ResumeSend();
+    PRUint32 qlen = PipelineDepth();
+    
+    if (qlen != 1) {
+        trans->SetPipelinePosition(qlen);
+    }
+    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;
 }
 
+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
 //-----------------------------------------------------------------------------
 
 NS_IMPL_THREADSAFE_ADDREF(nsHttpPipeline)
 NS_IMPL_THREADSAFE_RELEASE(nsHttpPipeline)
 
 // multiple inheritance fun :-)
@@ -159,19 +203,36 @@ nsHttpPipeline::OnHeadersAvailable(nsAHt
                                    nsHttpRequestHead *requestHead,
                                    nsHttpResponseHead *responseHead,
                                    bool *reset)
 {
     LOG(("nsHttpPipeline::OnHeadersAvailable [this=%x]\n", this));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     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
-    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
 nsHttpPipeline::ResumeSend()
 {
     if (mConnection)
         return mConnection->ResumeSend();
     return NS_ERROR_UNEXPECTED;
@@ -186,17 +247,17 @@ nsHttpPipeline::ResumeRecv()
 }
 
 void
 nsHttpPipeline::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
 {
     LOG(("nsHttpPipeline::CloseTransaction [this=%x trans=%x reason=%x]\n",
         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");
 
     // the specified transaction is to be closed with the given "reason"
     
     PRInt32 index;
     bool killPipeline = false;
 
     index = mRequestQ.IndexOf(trans);
@@ -214,31 +275,37 @@ nsHttpPipeline::CloseTransaction(nsAHttp
             mResponseQ.RemoveElementAt(index);
         // while we could avoid killing the pipeline if this transaction is the
         // last transaction in the pipeline, there doesn't seem to be that much
         // value in doing so.  most likely if this transaction is going away,
         // the others will be shortly as well.
         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);
     NS_RELEASE(trans);
 
-    if (killPipeline) {
-        if (mConnection)
-            mConnection->CloseTransaction(this, reason);
-        else
-            Close(reason);
-    }
+    if (killPipeline)
+        // reschedule anything from this pipeline onto a different connection
+        CancelPipeline(reason);
 }
 
 void
 nsHttpPipeline::GetConnectionInfo(nsHttpConnectionInfo **result)
 {
-    NS_ASSERTION(mConnection, "no connection");
+    if (!mConnection) {
+        *result = nsnull;
+        return;
+    }
+
     mConnection->GetConnectionInfo(result);
 }
 
 nsresult
 nsHttpPipeline::TakeTransport(nsISocketTransport  **aTransport,
                               nsIAsyncInputStream **aInputStream,
                               nsIAsyncOutputStream **aOutputStream)
 {
@@ -256,25 +323,34 @@ bool
 nsHttpPipeline::IsPersistent()
 {
     return true; // pipelining requires this
 }
 
 bool
 nsHttpPipeline::IsReused()
 {
-    return true; // pipelining requires this
+    if (!mUtilizedPipeline && mConnection)
+        return mConnection->IsReused();
+    return true;
+}
+
+void
+nsHttpPipeline::DontReuse()
+{
+    if (mConnection)
+        mConnection->DontReuse();
 }
 
 nsresult
 nsHttpPipeline::PushBack(const char *data, PRUint32 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!");
 
     // If we have no chance for a pipeline (e.g. due to an Upgrade)
     // then push this data down to original connection
     if (!mConnection->IsPersistent())
         return mConnection->PushBack(data, length);
 
     // PushBack is called recursively from WriteSegments
@@ -305,16 +381,23 @@ nsHttpPipeline::PushBack(const char *dat
  
     memcpy(mPushBackBuf, data, length);
     mPushBackLen = length;
 
     return NS_OK;
 }
 
 bool
+nsHttpPipeline::IsProxyConnectInProgress()
+{
+    NS_ABORT_IF_FALSE(mConnection, "no connection");
+    return mConnection->IsProxyConnectInProgress();
+}
+
+bool
 nsHttpPipeline::LastTransactionExpectedNoContent()
 {
     NS_ABORT_IF_FALSE(mConnection, "no connection");
     return mConnection->LastTransactionExpectedNoContent();
 }
 
 void
 nsHttpPipeline::SetLastTransactionExpectedNoContent(bool val)
@@ -334,16 +417,34 @@ nsHttpPipeline::TakeHttpConnection()
 nsISocketTransport *
 nsHttpPipeline::Transport()
 {
     if (!mConnection)
         return nsnull;
     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
 nsHttpPipeline::SetSSLConnectFailed()
 {
     nsAHttpTransaction *trans = Request(0);
 
     if (trans)
         trans->SetSSLConnectFailed();
 }
@@ -368,51 +469,41 @@ nsresult
 nsHttpPipeline::TakeSubTransactions(
     nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
 {
     LOG(("nsHttpPipeline::TakeSubTransactions [this=%p]\n", this));
 
     if (mResponseQ.Length() || mRequestIsPartial)
         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();
     for (i = 0; i < count; ++i) {
         nsAHttpTransaction *trans = Request(i);
         outTransactions.AppendElement(trans);
         NS_RELEASE(trans);
     }
     mRequestQ.Clear();
 
     LOG(("   took %d\n", count));
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// nsHttpPipeline::nsAHttpConnection
+// nsHttpPipeline::nsAHttpTransaction
 //-----------------------------------------------------------------------------
 
 void
 nsHttpPipeline::SetConnection(nsAHttpConnection *conn)
 {
     LOG(("nsHttpPipeline::SetConnection [this=%x conn=%x]\n", this, conn));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ASSERTION(!mConnection, "already have a connection");
 
     NS_IF_ADDREF(mConnection = conn);
-
-    PRInt32 i, count = mRequestQ.Length();
-    for (i=0; i<count; ++i)
-        Request(i)->SetConnection(this);
 }
 
 nsAHttpConnection *
 nsHttpPipeline::Connection()
 {
     LOG(("nsHttpPipeline::Connection [this=%x conn=%x]\n", this, mConnection));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
@@ -420,18 +511,23 @@ nsHttpPipeline::Connection()
 }
 
 void
 nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result,
                                      nsIEventTarget        **target)
 {
     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);
+    if (!trans)
+        trans = Response(0);
     if (trans)
         trans->GetSecurityCallbacks(result, target);
     else {
         *result = nsnull;
         if (target)
             *target = nsnull;
     }
 }
@@ -541,16 +637,26 @@ nsHttpPipeline::IsDone()
 }
 
 nsresult
 nsHttpPipeline::Status()
 {
     return mStatus;
 }
 
+PRUint8
+nsHttpPipeline::Caps()
+{
+    nsAHttpTransaction *trans = Request(0);
+    if (!trans)
+        trans = Response(0);
+
+    return trans ? trans->Caps() : 0;
+}
+
 PRUint32
 nsHttpPipeline::Available()
 {
     PRUint32 result = 0;
 
     PRInt32 i, count = mRequestQ.Length();
     for (i=0; i<count; ++i)
         result += Request(i)->Available();
@@ -627,16 +733,27 @@ nsHttpPipeline::WriteSegments(nsAHttpSeg
 
     if (mClosed)
         return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
 
     nsAHttpTransaction *trans; 
     nsresult rv;
 
     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 (mRequestQ.Length() > 0)
             rv = NS_BASE_STREAM_WOULD_BLOCK;
         else
             rv = NS_BASE_STREAM_CLOSED;
     }
     else {
         // 
@@ -649,17 +766,20 @@ nsHttpPipeline::WriteSegments(nsAHttpSeg
             trans->Close(NS_OK);
             NS_RELEASE(trans);
             mResponseQ.RemoveElementAt(0);
             mResponseIsPartial = false;
             ++mHttp1xTransactionCount;
 
             // ask the connection manager to add additional transactions
             // to our pipeline.
-            gHttpHandler->ConnMgr()->AddTransactionToPipeline(this);
+            nsRefPtr<nsHttpConnectionInfo> ci;
+            GetConnectionInfo(getter_AddRefs(ci));
+            if (ci)
+                gHttpHandler->ConnMgr()->ProcessPendingQForEntry(ci);
         }
         else
             mResponseIsPartial = true;
     }
 
     if (mPushBackLen) {
         nsHttpPushBackWriter writer(mPushBackBuf, mPushBackLen);
         PRUint32 len = mPushBackLen, n;
@@ -678,62 +798,107 @@ nsHttpPipeline::WriteSegments(nsAHttpSeg
         // so we are guaranteed that the next response will eat the entire
         // push back buffer (even though it might again call PushBack).
         rv = WriteSegments(&writer, len, &n);
     }
 
     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
 nsHttpPipeline::Close(nsresult reason)
 {
     LOG(("nsHttpPipeline::Close [this=%x reason=%x]\n", this, reason));
 
     if (mClosed) {
         LOG(("  already closed\n"));
         return;
     }
 
     // the connection is going away!
     mStatus = reason;
     mClosed = true;
 
-    PRUint32 i, count;
-    nsAHttpTransaction *trans;
+    nsRefPtr<nsHttpConnectionInfo> ci;
+    GetConnectionInfo(getter_AddRefs(ci));
+    PRUint32 numRescheduled = CancelPipeline(reason);
 
-    // any pending requests can ignore this error and be restarted
-    count = mRequestQ.Length();
-    for (i=0; i<count; ++i) {
-        trans = Request(i);
-        trans->Close(NS_ERROR_NET_RESET);
-        NS_RELEASE(trans);
-    }
-    mRequestQ.Clear();
+    // numRescheduled can be 0 if there is just a single response in the
+    // pipeline object. That isn't really a meaningful pipeline that
+    // has been forced to be rescheduled so it does not need to generate
+    // negative feedback.
+    if (ci && numRescheduled)
+        gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
+            ci, nsHttpConnectionMgr::RedCanceledPipeline, nsnull, 0);
 
-    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();
+    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);
+    }
+    else {
+        trans->Close(reason);
     }
 
+    NS_RELEASE(trans);
+    mResponseQ.Clear();
 }
 
 nsresult
 nsHttpPipeline::OnReadSegment(const char *segment,
                               PRUint32 count,
                               PRUint32 *countRead)
 {
     return mSendBufOut->Write(segment, count, countRead);
@@ -759,16 +924,23 @@ nsHttpPipeline::FillSendBuf()
 
     PRUint32 n, avail;
     nsAHttpTransaction *trans;
     nsITransport *transport = Transport();
 
     while ((trans = Request(0)) != nsnull) {
         avail = trans->Available();
         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);
             if (NS_FAILED(rv)) return rv;
             
             if (n == 0) {
                 LOG(("send pipe is full"));
                 break;
             }
 
@@ -789,14 +961,18 @@ nsHttpPipeline::FillSendBuf()
             mRequestIsPartial = false;
 
             if (!mSuppressSendEvents && transport) {
                 // Simulate a WAITING_FOR event
                 trans->OnTransportStatus(transport,
                                          NS_NET_STATUS_WAITING_FOR,
                                          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
             mRequestIsPartial = true;
     }
     return NS_OK;
 }
--- a/netwerk/protocol/http/nsHttpPipeline.h
+++ b/netwerk/protocol/http/nsHttpPipeline.h
@@ -55,18 +55,16 @@ public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSAHTTPCONNECTION
     NS_DECL_NSAHTTPTRANSACTION
     NS_DECL_NSAHTTPSEGMENTREADER
 
     nsHttpPipeline();
     virtual ~nsHttpPipeline();
 
-    nsresult AddTransaction(nsAHttpTransaction *);
-
 private:
     nsresult FillSendBuf();
     
     static NS_METHOD ReadFromPipe(nsIInputStream *, void *, const char *,
                                   PRUint32, PRUint32, PRUint32 *);
 
     // convenience functions
     nsAHttpTransaction *Request(PRInt32 i)
@@ -79,31 +77,38 @@ private:
     nsAHttpTransaction *Response(PRInt32 i)
     {
         if (mResponseQ.Length() == 0)
             return nsnull;
 
         return mResponseQ[i];
     }
 
+    // overload of nsAHttpTransaction::QueryPipeline()
+    nsHttpPipeline *QueryPipeline();
+
     nsAHttpConnection            *mConnection;
     nsTArray<nsAHttpTransaction*> mRequestQ;  // array of transactions
     nsTArray<nsAHttpTransaction*> mResponseQ; // array of transactions
     nsresult                      mStatus;
 
     // these flags indicate whether or not the first request or response
     // is partial.  a partial request means that Request(0) has been 
     // partially written out to the socket.  a partial response means
     // that Response(0) has been partially read in from the socket.
     bool mRequestIsPartial;
     bool mResponseIsPartial;
 
     // indicates whether or not the pipeline has been explicitly closed.
     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.
     nsAHttpSegmentReader *mReader;
     nsAHttpSegmentWriter *mWriter;
 
     // send buffer
     nsCOMPtr<nsIInputStream>  mSendBufIn;
     nsCOMPtr<nsIOutputStream> mSendBufOut;
 
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -59,16 +59,18 @@
 #include "nsStringStream.h"
 
 #include "nsComponentManagerUtils.h" // do_CreateInstance
 #include "nsServiceManagerUtils.h"   // do_GetService
 #include "nsIHttpActivityObserver.h"
 
 #include "mozilla/FunctionTimer.h"
 
+using namespace mozilla;
+
 //-----------------------------------------------------------------------------
 
 #ifdef DEBUG
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
 #endif
 
 //-----------------------------------------------------------------------------
@@ -114,46 +116,89 @@ nsHttpTransaction::nsHttpTransaction()
     , mContentLength(-1)
     , mContentRead(0)
     , mInvalidResponseBytesRead(0)
     , mChunkedDecoder(nsnull)
     , mStatus(NS_OK)
     , mPriority(0)
     , mRestartCount(0)
     , mCaps(0)
+    , mClassification(CLASS_GENERAL)
+    , mPipelinePosition(0)
     , mClosed(false)
     , mConnected(false)
     , mHaveStatusLine(false)
     , mHaveAllHeaders(false)
     , mTransactionDone(false)
     , mResponseIsComplete(false)
     , mDidContentStart(false)
     , mNoContent(false)
     , mSentData(false)
     , mReceivedData(false)
     , mStatusEventPending(false)
     , mHasRequestBody(false)
     , mSSLConnectFailed(false)
     , mHttpResponseMatched(false)
     , mPreserveStream(false)
+    , mToReadBeforeRestart(0)
+    , mReportedStart(false)
+    , mReportedResponseHeader(false)
+    , mForTakeResponseHead(nsnull)
+    , mTakenResponseHeader(false)
 {
     LOG(("Creating nsHttpTransaction @%x\n", this));
+    gHttpHandler->GetMaxPipelineObjectSize(mMaxPipelineObjectSize);
 }
 
 nsHttpTransaction::~nsHttpTransaction()
 {
     LOG(("Destroying nsHttpTransaction @%x\n", this));
 
     NS_IF_RELEASE(mConnection);
     NS_IF_RELEASE(mConnInfo);
 
     delete mResponseHead;
+    delete mForTakeResponseHead;
     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
 nsHttpTransaction::Init(PRUint8 caps,
                         nsHttpConnectionInfo *cinfo,
                         nsHttpRequestHead *requestHead,
                         nsIInputStream *requestBody,
                         bool requestBodyHasHeaders,
                         nsIEventTarget *target,
                         nsIInterfaceRequestor *callbacks,
@@ -295,36 +340,55 @@ nsHttpTransaction::Init(PRUint8 caps,
     // create pipe for response stream
     rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
                      getter_AddRefs(mPipeOut),
                      true, true,
                      nsIOService::gDefaultSegmentSize,
                      nsIOService::gDefaultSegmentCount);
     if (NS_FAILED(rv)) return rv;
 
+    Classify();
+
     NS_ADDREF(*responseBody = mPipeIn);
     return NS_OK;
 }
 
 nsAHttpConnection *
 nsHttpTransaction::Connection()
 {
     return mConnection;
 }
 
 nsHttpResponseHead *
 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) {
         NS_WARNING("response headers not available or incomplete");
         return nsnull;
     }
 
-    nsHttpResponseHead *head = mResponseHead;
-    mResponseHead = nsnull;
+    // Prefer mForTakeResponseHead over mResponseHead
+    nsHttpResponseHead *head;
+    if (mForTakeResponseHead) {
+        head = mForTakeResponseHead;
+        mForTakeResponseHead = nsnull;
+    }
+    else {
+        head = mResponseHead;
+        mResponseHead = nsnull;
+    }
     return head;
 }
 
 void
 nsHttpTransaction::SetSSLConnectFailed()
 {
     mSSLConnectFailed = true;
 }
@@ -400,23 +464,24 @@ nsHttpTransaction::OnTransportStatus(nsI
             (status == nsISocketTransport::STATUS_WAITING_FOR))
             mActivityDistributor->ObserveActivity(
                 mChannel,
                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
                 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
                 PR_Now(), LL_ZERO, EmptyCString());
 
         // report the status and progress
-        mActivityDistributor->ObserveActivity(
-            mChannel,
-            NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
-            static_cast<PRUint32>(status),
-            PR_Now(),
-            progress,
-            EmptyCString());
+        if (!mRestartInProgressVerifier.Active())
+            mActivityDistributor->ObserveActivity(
+                mChannel,
+                NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
+                static_cast<PRUint32>(status),
+                PR_Now(),
+                progress,
+                EmptyCString());
     }
 
     // nsHttpChannel synthesizes progress events in OnDataAvailable
     if (status == nsISocketTransport::STATUS_RECEIVING_FROM)
         return;
 
     PRUint64 progressMax;
 
@@ -451,16 +516,22 @@ nsHttpTransaction::IsDone()
 }
 
 nsresult
 nsHttpTransaction::Status()
 {
     return mStatus;
 }
 
+PRUint8
+nsHttpTransaction::Caps()
+{ 
+    return mCaps;
+}
+
 PRUint32
 nsHttpTransaction::Available()
 {
     PRUint32 size;
     if (NS_FAILED(mRequestStream->Available(&size)))
         size = 0;
     return size;
 }
@@ -649,26 +720,57 @@ nsHttpTransaction::Close(nsresult reason
     // have dire consequences including repeated purchases, etc.
     //
     // NOTE: because of the way SSL proxy CONNECT is implemented, it is
     // possible that the transaction may have received data without having
     // sent any data.  for this reason, mSendData == FALSE does not imply
     // mReceivedData == FALSE.  (see bug 203057 for more info.)
     //
     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,
             // which will notify the channel that the transaction failed.
+            
+            if (mPipelinePosition) {
+                gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
+                    mConnInfo, nsHttpConnectionMgr::RedCanceledPipeline,
+                    nsnull, 0);
+            }
             if (NS_SUCCEEDED(Restart()))
                 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;
     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
         // section, and there may still be a header line unparsed.  let's make
         // sure we parse the remaining header line, and then hopefully, the
         // response will be usable (see bug 88792).
         if (!mHaveAllHeaders) {
             char data = '\n';
             PRUint32 unused;
             ParseHead(&data, 1, &unused);
@@ -699,21 +801,97 @@ nsHttpTransaction::Close(nsresult reason
         delete mChunkedDecoder;
         mChunkedDecoder = nsnull;
     }
 
     // closing this pipe triggers the channel's OnStopRequest method.
     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>
 //-----------------------------------------------------------------------------
 
 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
 nsHttpTransaction::Restart()
 {
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     // limit the number of restart attempts - bug 92224
     if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
         LOG(("reached max request attempts, failing transaction @%x\n", this));
         return NS_ERROR_NET_RESET;
@@ -834,16 +1012,19 @@ nsHttpTransaction::ParseLineSegment(char
         // not a continuation of the previous or if we haven't
         // parsed the status line yet, then parse the contents
         // of mLineBuf.
         mLineBuf.Truncate(mLineBuf.Length() - 1);
         if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
             nsresult rv = ParseLine(mLineBuf.BeginWriting());
             mLineBuf.Truncate();
             if (NS_FAILED(rv)) {
+                gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
+                    mConnInfo, nsHttpConnectionMgr::RedCorruptedContent,
+                    nsnull, 0);
                 return rv;
             }
         }
     }
 
     // append segment to mLineBuf...
     mLineBuf.Append(segment, len);
     
@@ -882,22 +1063,24 @@ nsHttpTransaction::ParseHead(char *buf,
         
     // allocate the response head object if necessary
     if (!mResponseHead) {
         mResponseHead = new nsHttpResponseHead();
         if (!mResponseHead)
             return NS_ERROR_OUT_OF_MEMORY;
 
         // report that we have a least some of the response
-        if (mActivityDistributor)
+        if (mActivityDistributor && !mReportedStart) {
+            mReportedStart = true;
             mActivityDistributor->ObserveActivity(
                 mChannel,
                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
                 PR_Now(), LL_ZERO, EmptyCString());
+        }
     }
 
     if (!mHttpResponseMatched) {
         // Normally we insist on seeing HTTP/1.x in the first few bytes,
         // but if we are on a persistent connection and the previous transaction
         // was not supposed to have any content then we need to be prepared
         // to skip over a response body that the server may have sent even
         // though it wasn't allowed.
@@ -1001,17 +1184,18 @@ nsHttpTransaction::HandleContentStart()
             nsCAutoString headers;
             mResponseHead->Flatten(headers, false);
             LogHeaders(headers.get());
             LOG3(("]\n"));
         }
 #endif
         // notify the connection, give it a chance to cause a reset.
         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...
         if (reset) {
             LOG(("resetting transaction's response head\n"));
             mHaveAllHeaders = false;
             mHaveStatusLine = false;
             mReceivedData = false;
             mSentData = false;
@@ -1028,23 +1212,31 @@ nsHttpTransaction::HandleContentStart()
         case 204:
         case 205:
         case 304:
             mNoContent = true;
             LOG(("this response should not contain a body.\n"));
             break;
         }
         mConnection->SetLastTransactionExpectedNoContent(mNoContent);
+        if (mInvalidResponseBytesRead)
+            gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
+                mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
+                nsnull, mClassification);
 
         if (mNoContent)
             mContentLength = 0;
         else {
             // grab the content-length from the response headers
             mContentLength = mResponseHead->ContentLength();
 
+            if ((mClassification != CLASS_SOLO) &&
+                (mContentLength > mMaxPipelineObjectSize))
+                CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
+            
             // handle chunked encoding here, so we'll know immediately when
             // we're done with the socket.  please note that _all_ other
             // decoding is done when the channel receives the content data
             // so as not to block the socket transport thread too much.
             // ignore chunked responses from HTTP/1.0 servers and proxies.
             if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 &&
                 mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
                 // we only support the "chunked" transfer encoding right now.
@@ -1055,19 +1247,25 @@ nsHttpTransaction::HandleContentStart()
                 // Ignore server specified Content-Length.
                 mContentLength = -1;
             }
 #if defined(PR_LOGGING)
             else if (mContentLength == PRInt64(-1))
                 LOG(("waiting for the server to close the connection.\n"));
 #endif
         }
+        if (mRestartInProgressVerifier.Active() &&
+            !mRestartInProgressVerifier.Verify(mContentLength, mResponseHead)) {
+            LOG(("Restart in progress subsequent transaction failed to match"));
+            return NS_ERROR_ABORT;
+        }
     }
 
     mDidContentStart = true;
+    mRestartInProgressVerifier.Set(mContentLength, mResponseHead);
     return NS_OK;
 }
 
 // called on the socket thread
 nsresult
 nsHttpTransaction::HandleContent(char *buf,
                                  PRUint32 count,
                                  PRUint32 *contentRead,
@@ -1116,29 +1314,49 @@ nsHttpTransaction::HandleContent(char *b
             }
         }
     }
     else {
         // when we are just waiting for the server to close the connection...
         // (no explicit content-length given)
         *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) {
         // update count of content bytes read and report progress...
         mContentRead += *contentRead;
         /* when uncommenting, take care of 64-bit integers w/ NS_MAX...
         if (mProgressSink)
             mProgressSink->OnProgress(nsnull, nsnull, mContentRead, NS_MAX(0, mContentLength));
         */
     }
 
     LOG(("nsHttpTransaction::HandleContent [this=%x count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
         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
     if ((mContentRead == mContentLength) ||
         (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
         // the transaction is done with a complete response.
         mTransactionDone = true;
         mResponseIsComplete = true;
 
         if (TimingEnabled())
@@ -1184,17 +1402,19 @@ nsHttpTransaction::ProcessData(char *buf
         
         count -= bytesConsumed;
 
         // if buf has some content in it, shift bytes to top of buf.
         if (count && bytesConsumed)
             memmove(buf, buf + bytesConsumed, count);
 
         // report the completed response header
-        if (mActivityDistributor && mResponseHead && mHaveAllHeaders) {
+        if (mActivityDistributor && mResponseHead && mHaveAllHeaders &&
+            !mReportedResponseHeader) {
+            mReportedResponseHeader = true;
             nsCAutoString completeResponseHeaders;
             mResponseHead->Flatten(completeResponseHeaders, false);
             completeResponseHeaders.AppendLiteral("\r\n");
             mActivityDistributor->ObserveActivity(
                 mChannel,
                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
                 PR_Now(), LL_ZERO,
@@ -1228,16 +1448,33 @@ nsHttpTransaction::ProcessData(char *buf
             NS_ASSERTION(mConnection, "no connection");
             mConnection->PushBack(buf + *countRead, countRemaining);
         }
     }
 
     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
 //-----------------------------------------------------------------------------
 
 class nsDeleteHttpTransaction : public nsRunnable {
 public:
     nsDeleteHttpTransaction(nsHttpTransaction *trans)
         : mTrans(trans)
@@ -1323,8 +1560,81 @@ nsHttpTransaction::OnOutputStreamReady(n
 {
     if (mConnection) {
         nsresult rv = mConnection->ResumeRecv();
         if (NS_FAILED(rv))
             NS_ERROR("ResumeRecv failed");
     }
     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;
+    }
+}
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -108,17 +108,16 @@ public:
                   nsIInputStream        *reqBody,
                   bool                   reqBodyIncludesHeaders,
                   nsIEventTarget        *consumerTarget,
                   nsIInterfaceRequestor *callbacks,
                   nsITransportEventSink *eventsink,
                   nsIAsyncInputStream  **responseBody);
 
     // attributes
-    PRUint8                Caps()           { return mCaps; }
     nsHttpConnectionInfo  *ConnectionInfo() { return mConnInfo; }
     nsHttpResponseHead    *ResponseHead()   { return mHaveAllHeaders ? mResponseHead : nsnull; }
     nsISupports           *SecurityInfo()   { return mSecurityInfo; }
 
     nsIInterfaceRequestor *Callbacks()      { return mCallbacks; } 
     nsIEventTarget        *ConsumerTarget() { return mConsumerTarget; }
 
     // Called to take ownership of the response headers; the transaction
@@ -130,29 +129,34 @@ public:
 
     bool      SSLConnectFailed() { return mSSLConnectFailed; }
 
     // SetPriority() may only be used by the connection manager.
     void    SetPriority(PRInt32 priority) { mPriority = priority; }
     PRInt32    Priority()                 { return mPriority; }
 
     const TimingStruct& Timings() const { return mTimings; }
+    enum Classifier Classification() { return mClassification; }
 
 private:
     nsresult Restart();
+    nsresult RestartInProgress();
     char    *LocateHttpStart(char *buf, PRUint32 len,
                              bool aAllowPartialMatch);
     nsresult ParseLine(char *line);
     nsresult ParseLineSegment(char *seg, PRUint32 len);
     nsresult ParseHead(char *, PRUint32 count, PRUint32 *countRead);
     nsresult HandleContentStart();
     nsresult HandleContent(char *, PRUint32 count, PRUint32 *contentRead, PRUint32 *contentRemaining);
     nsresult ProcessData(char *, PRUint32, PRUint32 *);
     void     DeleteSelfOnConsumerThread();
 
+    Classifier Classify();
+    void       CancelPipeline(PRUint32 reason);
+
     static NS_METHOD ReadRequestSegment(nsIInputStream *, void *, const char *,
                                         PRUint32, PRUint32, PRUint32 *);
     static NS_METHOD WritePipeSegment(nsIOutputStream *, void *, char *,
                                       PRUint32, PRUint32, PRUint32 *);
 
     bool TimingEnabled() const { return mCaps & NS_HTTP_TIMING_ENABLED; }
 
 private:
@@ -195,16 +199,19 @@ private:
     TimingStruct                    mTimings;
 
     nsresult                        mStatus;
 
     PRInt16                         mPriority;
 
     PRUint16                        mRestartCount;        // the number of times this transaction has been restarted
     PRUint8                         mCaps;
+    enum Classifier                 mClassification;
+    PRInt32                         mPipelinePosition;
+    PRInt64                         mMaxPipelineObjectSize;
 
     // state flags, all logically boolean, but not packed together into a
     // bitfield so as to avoid bitfield-induced races.  See bug 560579.
     bool                            mClosed;
     bool                            mConnected;
     bool                            mHaveStatusLine;
     bool                            mHaveAllHeaders;
     bool                            mTransactionDone;
@@ -217,11 +224,71 @@ private:
     bool                            mHasRequestBody;
     bool                            mSSLConnectFailed;
     bool                            mHttpResponseMatched;
     bool                            mPreserveStream;
 
     // mClosed           := transaction has been explicitly closed
     // mTransactionDone  := transaction ran to completion or was interrupted
     // 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__
--- a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
+++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
@@ -607,16 +607,22 @@ nsWyciwygChannel::OnCacheEntryAvailable(
     CloseCacheEntry(rv);
 
     NotifyListener();
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsWyciwygChannel::OnCacheEntryDoomed(nsresult status)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 //-----------------------------------------------------------------------------
 // nsWyciwygChannel::nsIStreamListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsWyciwygChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctx,
                                   nsIInputStream *input,
                                   PRUint32 offset, PRUint32 count)
@@ -704,17 +710,17 @@ nsWyciwygChannel::OpenCacheEntry(const n
                                    getter_AddRefs(cacheSession));
   if (!cacheSession) 
     return NS_ERROR_FAILURE;
 
   if (aAccessMode == nsICache::ACCESS_WRITE)
     rv = cacheSession->OpenCacheEntry(aCacheKey, aAccessMode, false,
                                       getter_AddRefs(mCacheEntry));
   else
-    rv = cacheSession->AsyncOpenCacheEntry(aCacheKey, aAccessMode, this);
+    rv = cacheSession->AsyncOpenCacheEntry(aCacheKey, aAccessMode, this, false);
 
   return rv;
 }
 
 nsresult
 nsWyciwygChannel::ReadFromCache()
 {
   LOG(("nsWyciwygChannel::ReadFromCache [this=%x] ", this));
--- a/netwerk/test/unit/head_channels.js
+++ b/netwerk/test/unit/head_channels.js
@@ -21,29 +21,31 @@ function read_stream(stream, count) {
   return data.join('');
 }
 
 const CL_EXPECT_FAILURE = 0x1;
 const CL_EXPECT_GZIP = 0x2;
 const CL_EXPECT_3S_DELAY = 0x4;
 const CL_SUSPEND = 0x8;
 const CL_ALLOW_UNKNOWN_CL = 0x10;
+const CL_EXPECT_LATE_FAILURE = 0x20;
 
 const SUSPEND_DELAY = 3000;
 
 /**
  * A stream listener that calls a callback function with a specified
  * context and the received data when the channel is loaded.
  *
  * Signature of the closure:
  *   void closure(in nsIRequest request, in ACString data, in JSObject context);
  *
  * This listener makes sure that various parts of the channel API are
  * 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
  * is thus not fully generic.
  */
 function ChannelListener(closure, ctx, flags) {
   this._closure = closure;
   this._closurectx = ctx;
   this._flags = flags;
@@ -126,25 +128,25 @@ ChannelListener.prototype = {
   onStopRequest: function(request, context, status) {
     try {
       var success = Components.isSuccessCode(status);
       if (!this._got_onstartrequest && success)
         do_throw("onStopRequest without onStartRequest event!");
       if (this._got_onstoprequest)
         do_throw("Got second onStopRequest event!");
       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) + ")");
-      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));
       if (status != request.status)
         do_throw("request.status does not match status arg to onStopRequest!");
       if (request.isPending())
         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._contentLen != -1)
           do_check_eq(this._buffer.length, this._contentLen)
     } catch (ex) {
       do_throw("Error in onStopRequest: " + ex);
     }
     try {
       this._closure(request, this._buffer, this._closurectx);
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_assoc.js
@@ -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);
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_doomentry.js
@@ -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();
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_mismatch_lm.js
@@ -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);
+	}
+    }
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 head = head_channels.js
 tail = 
 
 [test_307_redirect.js]
 [test_NetUtil.js]
 [test_URIs.js]
 [test_aboutblank.js]
+[test_assoc.js]
 [test_auth_proxy.js]
 [test_authentication.js]
 # Bug 675039: test hangs consistently on Android
 skip-if = os == "android"
 [test_authpromptwrapper.js]
 [test_bug203271.js]
 [test_bug248970_cache.js]
 [test_bug248970_cookie.js]
@@ -78,16 +79,17 @@ fail-if = os == "android"
 [test_bug654926_test_seek.js]
 # Bug 675049: test fails consistently on Android
 fail-if = os == "android"  
 [test_bug659569.js]
 [test_bug660066.js]
 [test_bug667907.js]
 [test_bug667818.js]
 [test_bug669001.js]
+[test_doomentry.js]
 [test_cacheflags.js]
 [test_channel_close.js]
 [test_compareURIs.js]
 [test_compressappend.js]
 [test_content_encoding_gzip.js]
 [test_content_sniffer.js]
 [test_cookie_header.js]
 [test_data_protocol.js]
@@ -126,16 +128,17 @@ skip-if = os == "android"
 [test_gzipped_206.js]
 [test_head.js]
 [test_headers.js]
 [test_http_headers.js]
 [test_httpcancel.js]
 [test_httpsuspend.js]
 [test_idnservice.js]
 [test_localstreams.js]
+[test_mismatch_lm.js]
 [test_MIME_params.js]
 [test_multipart_streamconv.js]
 [test_multipart_streamconv_missing_lead_boundary.js]
 [test_nestedabout_serialize.js]
 [test_net_addr.js]
 # Bug 732363: test fails on windows for unknown reasons.
 skip-if = os == "win"
 [test_nojsredir.js]
--- a/toolkit/components/places/nsAndroidHistory.cpp
+++ b/toolkit/components/places/nsAndroidHistory.cpp
@@ -67,17 +67,17 @@ NS_IMETHODIMP
 nsAndroidHistory::RegisterVisitedCallback(nsIURI *aURI, Link *aContent)
 {
   if (!aContent || !aURI)
     return NS_OK;
 
   nsCAutoString uri;
   nsresult rv = aURI->GetSpec(uri);
   if (NS_FAILED(rv)) return rv;
-  nsString uriString = NS_ConvertUTF8toUTF16(uri);
+  NS_ConvertUTF8toUTF16 uriString(uri);
 
   nsTArray<Link*>* list = mListeners.Get(uriString);
   if (! list) {
     list = new nsTArray<Link*>();
     mListeners.Put(uriString, list);
   }
   list->AppendElement(aContent);
 
@@ -93,17 +93,17 @@ NS_IMETHODIMP
 nsAndroidHistory::UnregisterVisitedCallback(nsIURI *aURI, Link *aContent)
 {
   if (!aContent || !aURI)
     return NS_OK;
 
   nsCAutoString uri;
   nsresult rv = aURI->GetSpec(uri);
   if (NS_FAILED(rv)) return rv;
-  nsString uriString = NS_ConvertUTF8toUTF16(uri);
+  NS_ConvertUTF8toUTF16 uriString(uri);
 
   nsTArray<Link*>* list = mListeners.Get(uriString);
   if (! list)
     return NS_OK;
 
   list->RemoveElement(aContent);
   if (list->IsEmpty()) {
     mListeners.Remove(uriString);
@@ -121,17 +121,17 @@ nsAndroidHistory::VisitURI(nsIURI *aURI,
   if (!(aFlags & VisitFlags::TOP_LEVEL))
     return NS_OK;
 
   AndroidBridge *bridge = AndroidBridge::Bridge();
   if (bridge) {
     nsCAutoString uri;
     nsresult rv = aURI->GetSpec(uri);
     if (NS_FAILED(rv)) return rv;
-    nsString uriString = NS_ConvertUTF8toUTF16(uri);
+    NS_ConvertUTF8toUTF16 uriString(uri);
     bridge->MarkURIVisited(uriString);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAndroidHistory::SetURITitle(nsIURI *aURI, const nsAString& aTitle)
 {
--- a/toolkit/components/places/nsNavHistoryQuery.cpp
+++ b/toolkit/components/places/nsNavHistoryQuery.cpp
@@ -786,17 +786,17 @@ nsNavHistory::TokensToQueries(const nsTA
       NS_UnescapeURL(unescaped); // modifies input
       query->SetAnnotationIsNot(false);
       query->SetAnnotation(unescaped);
 
     // tag
     } else if (kvp.key.EqualsLiteral(QUERYKEY_TAG)) {
       nsCAutoString unescaped(kvp.value);
       NS_UnescapeURL(unescaped); // modifies input
-      nsString tag = NS_ConvertUTF8toUTF16(unescaped);
+      NS_ConvertUTF8toUTF16 tag(unescaped);
       if (!tags.Contains(tag)) {
         NS_ENSURE_TRUE(tags.AppendElement(tag), NS_ERROR_OUT_OF_MEMORY);
       }
 
     // not tags
     } else if (kvp.key.EqualsLiteral(QUERYKEY_NOTTAGS)) {
       SetQueryKeyBool(kvp.value, query, &nsINavHistoryQuery::SetTagsAreNot);
 
--- a/toolkit/content/widgets/scrollbar.xml
+++ b/toolkit/content/widgets/scrollbar.xml
@@ -12,17 +12,17 @@
       <handler event="contextmenu" preventdefault="true" action="event.stopPropagation();"/>
       <handler event="click" preventdefault="true" action="event.stopPropagation();"/>
       <handler event="dblclick" action="event.stopPropagation();"/>
       <handler event="command" action="event.stopPropagation();"/>
     </handlers>
   </binding>
 
   <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-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:thumb sbattr="scrollbar-thumb" xbl:inherits="orient,sborient=orient,collapsed=disabled" 
                    align="center" pack="center"/>
       </xul:slider>
       <xul:scrollbarbutton sbattr="scrollbar-up-bottom" type="decrement" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
       <xul:scrollbarbutton sbattr="scrollbar-down-bottom" type="increment" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -787,17 +787,16 @@ if [ "$ENABLE_TESTS" ]; then
     dom/tests/mochitest/dom-level1-core/Makefile
     dom/tests/mochitest/dom-level1-core/files/Makefile
     dom/tests/mochitest/dom-level2-core/Makefile
     dom/tests/mochitest/dom-level2-core/files/Makefile
     dom/tests/mochitest/dom-level2-html/Makefile
     dom/tests/mochitest/dom-level2-html/files/Makefile
     dom/tests/mochitest/general/Makefile
     dom/tests/mochitest/geolocation/Makefile
-    dom/tests/mochitest/globalstorage/Makefile
     dom/tests/mochitest/localstorage/Makefile
     dom/tests/mochitest/orientation/Makefile
     dom/tests/mochitest/sessionstorage/Makefile
     dom/tests/mochitest/storageevent/Makefile
     dom/tests/mochitest/whatwg/Makefile
     dom/workers/test/Makefile
     dom/workers/test/extensions/Makefile
     dom/workers/test/extensions/bootstrap/Makefile
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -64,16 +64,18 @@ LOCAL_INCLUDES += \
   -I$(DEPTH)/tools/profiler/libunwind/src/include \
   $(NULL)
 
 SHARED_LIBRARY_LIBS += \
   $(DEPTH)/tools/profiler/libunwind/src/src/.libs/libunwind-arm.$(LIB_SUFFIX) \
   $(NULL)
 
 export::
+	# Avoid building libunwind documentation
+	$(topsrcdir)/tools/profiler/libunwind/dont_build_docs.sh $(DEPTH)/tools/profiler/libunwind/src/doc/Makefile
 	$(call SUBMAKE,,libunwind/src)
 
 distclean::
 	$(call SUBMAKE,$@,libunwind/src)
 endif
 endif
 
 MODULE          = profiler
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -518,22 +518,26 @@ void TableTicker::doBacktrace(ThreadProf
   MOZ_ASSERT(thread);
 #endif
   void* pc_array[1000];
   PCArray array = {
     pc_array,
     mozilla::ArrayLength(pc_array),
     0
   };
+
+  // Start with the current function.
+  StackWalkCallback(aSample->pc, &array);
+
 #ifdef XP_MACOSX
   pthread_t pt = GetProfiledThread(platform_data());
   void *stackEnd = reinterpret_cast<void*>(-1);
   if (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
   nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
 #endif
   if (NS_SUCCEEDED(rv)) {
     aProfile.addTag(ProfileEntry('s', "(root)", 0));
 
     for (size_t i = array.count; i > 0; --i) {
       aProfile.addTag(ProfileEntry('l', (const char*)array.array[i - 1]));
new file mode 100755
--- /dev/null
+++ b/tools/profiler/libunwind/dont_build_docs.sh
@@ -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"
--- a/xpcom/string/public/nsSubstring.h
+++ b/xpcom/string/public/nsSubstring.h
@@ -31,11 +31,16 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#ifndef nsSubstring_h___
+#define nsSubstring_h___
+
 #ifndef nsAString_h___
 #include "nsAString.h"
 #endif
+
+#endif // !defined(nsSubstring_h___)