Backed out changeset: 1a8fd714a794
authorSerge Gautherie <sgautherie.bz@free.fr>
Fri, 17 Oct 2008 04:10:25 +0200
changeset 20556 8ec9e25596938ce113267ad9c4a3047e8d0291ec
parent 20550 1a8fd714a7944b3af0bac1d916097e39ce4f72ee
child 20557 db54bf3abf0dbc722fea59dc3a9c645b361fc745
push id2963
push usersgautherie.bz@free.fr
push dateFri, 17 Oct 2008 02:12:15 +0000
treeherdermozilla-central@db54bf3abf0d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs459770
milestone1.9.1b2pre
Backed out changeset: 1a8fd714a794 Break out Access-Control code from nsXMLHttpRequest.cpp. r/sr=mrbkap b=459770 which leaks 280 kB.
content/base/src/nsCrossSiteListenerProxy.cpp
content/base/src/nsCrossSiteListenerProxy.h
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
layout/build/nsLayoutStatics.cpp
--- a/content/base/src/nsCrossSiteListenerProxy.cpp
+++ b/content/base/src/nsCrossSiteListenerProxy.cpp
@@ -48,120 +48,18 @@
 #include "nsMimeTypes.h"
 #include "nsIStreamConverterService.h"
 #include "nsStringStream.h"
 #include "nsParserUtils.h"
 #include "nsGkAtoms.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsIChannelEventSink.h"
 #include "nsCommaSeparatedTokenizer.h"
-#include "prclist.h"
-#include "nsAutoPtr.h"
-#include "nsClassHashtable.h"
-#include "nsHashKeys.h"
-#include "prtime.h"
 
-// =========================================================================
-// Support classes
-
-// Class implementing the preflight request cache
-#define ACCESS_CONTROL_CACHE_SIZE 100
-
-class nsACPreflightCache
-{
-public:
-  static void Shutdown()
-  {
-    PR_INIT_CLIST(&mList);
-    delete mTable;
-    mTable = nsnull;
-  }
-
-  struct TokenTime
-  {
-    nsCString token;
-    PRTime expirationTime;
-  };
-
-  class CacheEntry : public PRCList
-  {
-  public:
-    CacheEntry(nsCString& aKey)
-      : mKey(aKey)
-    {
-      MOZ_COUNT_CTOR(nsACPreflightCache::CacheEntry);
-    }
-    
-    ~CacheEntry()
-    {
-      MOZ_COUNT_DTOR(nsACPreflightCache::CacheEntry);
-    }
-
-    void PurgeExpired(PRTime now);
-    PRBool CheckRequest(const nsCString& aMethod,
-                        const nsTArray<nsCString>& aCustomHeaders);
-
-    nsCString mKey;
-    nsTArray<TokenTime> mMethods;
-    nsTArray<TokenTime> mHeaders;
-  };
-
-
-  static CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
-                              PRBool aWithCredentials, PRBool aCreate);
-
-private:
-  PR_STATIC_CALLBACK(PLDHashOperator)
-    RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
-                         void* aUserData);
-
-  static PRBool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
-                            PRBool aWithCredentials, nsACString& _retval);
-
-  static nsClassHashtable<nsCStringHashKey, CacheEntry>* mTable;
-  static PRCList mList;
-};
-
-
-// Class used as streamlistener and notification callback when
-// doing the initial GET request for an access-control check
-
-class nsACPreflightListener : public nsIStreamListener,
-                              public nsIInterfaceRequestor,
-                              public nsIChannelEventSink
-{
-public:
-  nsACPreflightListener(nsIChannel* aOuterChannel,
-                        nsIStreamListener* aOuterListener,
-                        nsISupports* aOuterContext,
-                        nsIPrincipal* aReferrerPrincipal,
-                        PRBool aWithCredentials)
-   : mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
-     mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
-     mWithCredentials(aWithCredentials)
-  { }
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSIREQUESTOBSERVER
-  NS_DECL_NSIINTERFACEREQUESTOR
-  NS_DECL_NSICHANNELEVENTSINK
-
-private:
-  void AddResultToCache(nsIRequest* aRequest);
-
-  nsCOMPtr<nsIChannel> mOuterChannel;
-  nsCOMPtr<nsIStreamListener> mOuterListener;
-  nsCOMPtr<nsISupports> mOuterContext;
-  nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
-  PRBool mWithCredentials;
-};
-
-// =========================================================================
-// nsCrossSiteListenerProxy
+static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 NS_IMPL_ISUPPORTS4(nsCrossSiteListenerProxy, nsIStreamListener,
                    nsIRequestObserver, nsIChannelEventSink,
                    nsIInterfaceRequestor)
 
 nsCrossSiteListenerProxy::nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
                                                    nsIPrincipal* aRequestingPrincipal,
                                                    nsIChannel* aChannel,
@@ -198,138 +96,16 @@ nsCrossSiteListenerProxy::nsCrossSiteLis
     mPreflightHeaders(aPreflightHeaders)
 {
   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
   aChannel->SetNotificationCallbacks(this);
 
   *aResult = UpdateChannel(aChannel);
 }
 
-nsresult
-nsCrossSiteListenerProxy::CheckPreflight(nsIHttpChannel* aRequestChannel,
-                                         nsIStreamListener* aRequestListener,
-                                         nsISupports* aRequestContext,
-                                         nsIPrincipal* aRequestingPrincipal,
-                                         PRBool aForcePreflight,
-                                         nsTArray<nsCString>& aUnsafeHeaders,
-                                         PRBool aWithCredentials,
-                                         PRBool* aPreflighted,
-                                         nsIChannel** aPreflightChannel,
-                                         nsIStreamListener** aPreflightListener)
-{
-  *aPreflighted = PR_FALSE;
-
-  nsCAutoString method;
-  aRequestChannel->GetRequestMethod(method);
-
-  if (!aUnsafeHeaders.IsEmpty() || aForcePreflight) {
-    *aPreflighted = PR_TRUE;
-  }
-  else if (method.LowerCaseEqualsLiteral("post")) {
-    nsCAutoString contentTypeHeader;
-    aRequestChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
-                                      contentTypeHeader);
-
-    nsCAutoString contentType, charset;
-    NS_ParseContentType(contentTypeHeader, contentType, charset);
-    if (!contentType.LowerCaseEqualsLiteral("text/plain")) {
-      *aPreflighted = PR_TRUE;
-    }
-  }
-  else if (!method.LowerCaseEqualsLiteral("get")) {
-    *aPreflighted = PR_TRUE;
-  }
-
-  if (!*aPreflighted) {
-    return NS_OK;
-  }
-
-  // If so, set up the preflight
-
-  // Check to see if this initial OPTIONS request has already been cached
-  // in our special Access Control Cache.
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aRequestChannel->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsACPreflightCache::CacheEntry* entry =
-    nsACPreflightCache::GetEntry(uri, aRequestingPrincipal,
-                                 aWithCredentials, PR_FALSE);
-
-  if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
-    return NS_OK;
-  }
-
-  // Either it wasn't cached or the cached result has expired. Build a
-  // channel for the OPTIONS request.
-  nsCOMPtr<nsILoadGroup> loadGroup;
-  rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsLoadFlags loadFlags;
-  rv = aRequestChannel->GetLoadFlags(&loadFlags);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIChannel> preflightChannel;
-  rv = NS_NewChannel(getter_AddRefs(preflightChannel), uri, nsnull,
-                     loadGroup, nsnull, loadFlags);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIHttpChannel> preflightHttp = do_QueryInterface(preflightChannel);
-  NS_ASSERTION(preflightHttp, "Failed to QI to nsIHttpChannel!");
-
-  rv = preflightHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Add channel headers
-  rv = preflightHttp->
-    SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
-                     method, PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!aUnsafeHeaders.IsEmpty()) {
-    nsCAutoString headers;
-    for (PRUint32 i = 0; i < aUnsafeHeaders.Length(); ++i) {
-      if (i != 0) {
-        headers += ',';
-      }
-      headers += aUnsafeHeaders[i];
-    }
-    rv = preflightHttp->
-      SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
-                       headers, PR_FALSE);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  // Set up listener
-  nsCOMPtr<nsIStreamListener> preflightListener =
-    new nsACPreflightListener(aRequestChannel, aRequestListener,
-                              aRequestContext, aRequestingPrincipal,
-                              aWithCredentials);
-  NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
-
-  preflightListener =
-    new nsCrossSiteListenerProxy(preflightListener, aRequestingPrincipal,
-                                 preflightChannel, aWithCredentials, method,
-                                 aUnsafeHeaders, &rv);
-  NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  preflightChannel.swap(*aPreflightChannel);
-  preflightListener.swap(*aPreflightListener);
-
-  return NS_OK;
-}
-
-void
-nsCrossSiteListenerProxy::ShutdownPreflightCache()
-{
-  nsACPreflightCache::Shutdown();
-}
-
 NS_IMETHODIMP
 nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest,
                                          nsISupports* aContext)
 {
   mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest));
   if (!mRequestApproved) {
     aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
     mOuterListener->OnStartRequest(aRequest, aContext);
@@ -603,405 +379,43 @@ nsCrossSiteListenerProxy::UpdateChannel(
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
   NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);
 
   rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Add preflight headers if this is a preflight request
+  if (mIsPreflight) {
+    rv = http->
+      SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
+                       mPreflightMethod, PR_FALSE);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!mPreflightHeaders.IsEmpty()) {
+      nsCAutoString headers;
+      for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
+        if (i != 0) {
+          headers += ',';
+        }
+        headers += mPreflightHeaders[i];
+      }
+      rv = http->
+        SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
+                         headers, PR_FALSE);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
   // Make cookie-less if needed
   if (mIsPreflight || !mWithCredentials) {
     nsLoadFlags flags;
     rv = http->GetLoadFlags(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     flags |= nsIRequest::LOAD_ANONYMOUS;
     rv = http->SetLoadFlags(flags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
-
-// =========================================================================
-// nsACPreflightListener
-
-NS_IMPL_ISUPPORTS4(nsACPreflightListener, nsIStreamListener, nsIRequestObserver,
-                   nsIInterfaceRequestor, nsIChannelEventSink)
-
-void
-nsACPreflightListener::AddResultToCache(nsIRequest *aRequest)
-{
-  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
-  NS_ASSERTION(http, "Request was not http");
-
-  // The "Access-Control-Max-Age" header should return an age in seconds.
-  nsCAutoString headerVal;
-  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
-                          headerVal);
-  if (headerVal.IsEmpty()) {
-    return;
-  }
-
-  // Sanitize the string. We only allow 'delta-seconds' as specified by
-  // http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
-  // trailing non-whitespace characters).
-  PRUint32 age = 0;
-  nsCSubstring::const_char_iterator iter, end;
-  headerVal.BeginReading(iter);
-  headerVal.EndReading(end);
-  while (iter != end) {
-    if (*iter < '0' || *iter > '9') {
-      return;
-    }
-    age = age * 10 + (*iter - '0');
-    // Cap at 24 hours. This also avoids overflow
-    age = PR_MIN(age, 86400);
-    ++iter;
-  }
-
-  if (!age) {
-    return;
-  }
-
-
-  // String seems fine, go ahead and cache.
-  // Note that we have already checked that these headers follow the correct
-  // syntax.
-
-  nsCOMPtr<nsIURI> uri;
-  http->GetURI(getter_AddRefs(uri));
-
-  // PR_Now gives microseconds
-  PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;
-
-  nsACPreflightCache::CacheEntry* entry =
-    nsACPreflightCache::GetEntry(uri, mReferrerPrincipal, mWithCredentials,
-                                 PR_TRUE);
-  if (!entry) {
-    return;
-  }
-
-  // The "Access-Control-Allow-Methods" header contains a comma separated
-  // list of method names.
-  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
-                          headerVal);
-
-  nsCCommaSeparatedTokenizer methods(headerVal);
-  while(methods.hasMoreTokens()) {
-    const nsDependentCSubstring& method = methods.nextToken();
-    if (method.IsEmpty()) {
-      continue;
-    }
-    PRUint32 i;
-    for (i = 0; i < entry->mMethods.Length(); ++i) {
-      if (entry->mMethods[i].token.Equals(method)) {
-        entry->mMethods[i].expirationTime = expirationTime;
-        break;
-      }
-    }
-    if (i == entry->mMethods.Length()) {
-      nsACPreflightCache::TokenTime* newMethod =
-        entry->mMethods.AppendElement();
-      if (!newMethod) {
-        return;
-      }
-
-      newMethod->token = method;
-      newMethod->expirationTime = expirationTime;
-    }
-  }
-
-  // The "Access-Control-Allow-Headers" header contains a comma separated
-  // list of method names.
-  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
-                          headerVal);
-
-  nsCCommaSeparatedTokenizer headers(headerVal);
-  while(headers.hasMoreTokens()) {
-    const nsDependentCSubstring& header = headers.nextToken();
-    if (header.IsEmpty()) {
-      continue;
-    }
-    PRUint32 i;
-    for (i = 0; i < entry->mHeaders.Length(); ++i) {
-      if (entry->mHeaders[i].token.Equals(header)) {
-        entry->mHeaders[i].expirationTime = expirationTime;
-        break;
-      }
-    }
-    if (i == entry->mHeaders.Length()) {
-      nsACPreflightCache::TokenTime* newHeader =
-        entry->mHeaders.AppendElement();
-      if (!newHeader) {
-        return;
-      }
-
-      newHeader->token = header;
-      newHeader->expirationTime = expirationTime;
-    }
-  }
-}
-
-NS_IMETHODIMP
-nsACPreflightListener::OnStartRequest(nsIRequest *aRequest,
-                                      nsISupports *aContext)
-{
-  nsresult status;
-  nsresult rv = aRequest->GetStatus(&status);
-
-  if (NS_SUCCEEDED(rv)) {
-    rv = status;
-  }
-
-  if (NS_SUCCEEDED(rv)) {
-    // Everything worked, try to cache and then fire off the actual request.
-    AddResultToCache(aRequest);
-
-    rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
-  }
-
-  if (NS_FAILED(rv)) {
-    mOuterChannel->Cancel(rv);
-    mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
-    mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
-    
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsACPreflightListener::OnStopRequest(nsIRequest *aRequest,
-                                     nsISupports *aContext,
-                                     nsresult aStatus)
-{
-  return NS_OK;
-}
-
-/** nsIStreamListener methods **/
-
-NS_IMETHODIMP
-nsACPreflightListener::OnDataAvailable(nsIRequest *aRequest,
-                                       nsISupports *ctxt,
-                                       nsIInputStream *inStr,
-                                       PRUint32 sourceOffset,
-                                       PRUint32 count)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsACPreflightListener::OnChannelRedirect(nsIChannel *aOldChannel,
-                                         nsIChannel *aNewChannel,
-                                         PRUint32 aFlags)
-{
-  // No redirects allowed for now.
-  return NS_ERROR_DOM_BAD_URI;
-}
-
-NS_IMETHODIMP
-nsACPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
-{
-  return QueryInterface(aIID, aResult);
-}
-
-// =========================================================================
-// nsACPreflightCache
-
-nsClassHashtable<nsCStringHashKey, nsACPreflightCache::CacheEntry>*
-  nsACPreflightCache::mTable;
-PRCList nsACPreflightCache::mList;
-
-void
-nsACPreflightCache::CacheEntry::PurgeExpired(PRTime now)
-{
-  PRUint32 i;
-  for (i = 0; i < mMethods.Length(); ++i) {
-    if (now >= mMethods[i].expirationTime) {
-      mMethods.RemoveElementAt(i--);
-    }
-  }
-  for (i = 0; i < mHeaders.Length(); ++i) {
-    if (now >= mHeaders[i].expirationTime) {
-      mHeaders.RemoveElementAt(i--);
-    }
-  }
-}
-
-PRBool
-nsACPreflightCache::CacheEntry::CheckRequest(const nsCString& aMethod,
-                                             const nsTArray<nsCString>& aHeaders)
-{
-  PurgeExpired(PR_Now());
-
-  if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
-    PRUint32 i;
-    for (i = 0; i < mMethods.Length(); ++i) {
-      if (aMethod.Equals(mMethods[i].token))
-        break;
-    }
-    if (i == mMethods.Length()) {
-      return PR_FALSE;
-    }
-  }
-
-  for (PRUint32 i = 0; i < aHeaders.Length(); ++i) {
-    PRUint32 j;
-    for (j = 0; j < mHeaders.Length(); ++j) {
-      if (aHeaders[i].Equals(mHeaders[j].token,
-                             nsCaseInsensitiveCStringComparator())) {
-        break;
-      }
-    }
-    if (j == mHeaders.Length()) {
-      return PR_FALSE;
-    }
-  }
-
-  return PR_TRUE;
-}
-
-nsACPreflightCache::CacheEntry*
-nsACPreflightCache::GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
-                             PRBool aWithCredentials, PRBool aCreate)
-{
-  if (!mTable) {
-    if (!aCreate) {
-      return nsnull;
-    }
-
-    PR_INIT_CLIST(&mList);
-
-    mTable = new nsClassHashtable<nsCStringHashKey, CacheEntry>;
-    NS_ENSURE_TRUE(mTable, nsnull);
-
-    if (!mTable->Init()) {
-      delete mTable;
-      mTable = nsnull;
-      return nsnull;
-    }
-  }
-
-  nsCString key;
-  if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
-    NS_WARNING("Invalid cache key!");
-    return nsnull;
-  }
-
-  CacheEntry* entry;
-
-  if (mTable->Get(key, &entry)) {
-    // Entry already existed so just return it. Also update the LRU list.
-
-    // Move to the head of the list.
-    PR_REMOVE_LINK(entry);
-    PR_INSERT_LINK(entry, &mList);
-
-    return entry;
-  }
-
-  if (!aCreate) {
-    return nsnull;
-  }
-
-  // This is a new entry, allocate and insert into the table now so that any
-  // failures don't cause items to be removed from a full cache.
-  entry = new CacheEntry(key);
-  if (!entry) {
-    NS_WARNING("Failed to allocate new cache entry!");
-    return nsnull;
-  }
-
-  if (!mTable->Put(key, entry)) {
-    // Failed, clean up the new entry.
-    delete entry;
-
-    NS_WARNING("Failed to add entry to the access control cache!");
-    return nsnull;
-  }
-
-  PR_INSERT_LINK(entry, &mList);
-
-  NS_ASSERTION(mTable->Count() <= ACCESS_CONTROL_CACHE_SIZE + 1,
-               "Something is borked, too many entries in the cache!");
-
-  // Now enforce the max count.
-  if (mTable->Count() > ACCESS_CONTROL_CACHE_SIZE) {
-    // Try to kick out all the expired entries.
-    PRTime now = PR_Now();
-    mTable->Enumerate(RemoveExpiredEntries, &now);
-
-    // If that didn't remove anything then kick out the least recently used
-    // entry.
-    if (mTable->Count() > ACCESS_CONTROL_CACHE_SIZE) {
-      CacheEntry* lruEntry = static_cast<CacheEntry*>(PR_LIST_TAIL(&mList));
-      PR_REMOVE_LINK(lruEntry);
-
-      // This will delete 'lruEntry'.
-      mTable->Remove(lruEntry->mKey);
-
-      NS_ASSERTION(mTable->Count() >= ACCESS_CONTROL_CACHE_SIZE,
-                   "Somehow tried to remove an entry that was never added!");
-    }
-  }
-  
-  return entry;
-}
-
-/* static */ PR_CALLBACK PLDHashOperator
-nsACPreflightCache::RemoveExpiredEntries(const nsACString& aKey,
-                                         nsAutoPtr<CacheEntry>& aValue,
-                                         void* aUserData)
-{
-  PRTime* now = static_cast<PRTime*>(aUserData);
-  
-  aValue->PurgeExpired(*now);
-  
-  if (aValue->mHeaders.IsEmpty() &&
-      aValue->mHeaders.IsEmpty()) {
-    // Expired, remove from the list as well as the hash table.
-    PR_REMOVE_LINK(aValue);
-    return PL_DHASH_REMOVE;
-  }
-  
-  return PL_DHASH_NEXT;
-}
-
-/* static */ PRBool
-nsACPreflightCache::GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
-                                PRBool aWithCredentials, nsACString& _retval)
-{
-  NS_ASSERTION(aURI, "Null uri!");
-  NS_ASSERTION(aPrincipal, "Null principal!");
-  
-  NS_NAMED_LITERAL_CSTRING(space, " ");
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, PR_FALSE);
-  
-  nsCAutoString scheme, host, port;
-  if (uri) {
-    uri->GetScheme(scheme);
-    uri->GetHost(host);
-    port.AppendInt(NS_GetRealPort(uri));
-  }
-
-  nsCAutoString cred;
-  if (aWithCredentials) {
-    _retval.AssignLiteral("cred");
-  }
-  else {
-    _retval.AssignLiteral("nocred");
-  }
-
-  nsCAutoString spec;
-  rv = aURI->GetSpec(spec);
-  NS_ENSURE_SUCCESS(rv, PR_FALSE);
-
-  _retval.Assign(cred + space + scheme + space + host + space + port + space +
-                 spec);
-
-  return PR_TRUE;
-}
--- a/content/base/src/nsCrossSiteListenerProxy.h
+++ b/content/base/src/nsCrossSiteListenerProxy.h
@@ -34,23 +34,27 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIStreamListener.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
+#include "nsIURI.h"
 #include "nsTArray.h"
+#include "nsIContentSink.h"
+#include "nsIXMLContentSink.h"
+#include "nsIExpatSink.h"
+#include "nsIInterfaceRequestor.h"
 #include "nsIChannelEventSink.h"
 
 class nsIURI;
 class nsIParser;
 class nsIPrincipal;
-class nsIHttpChannel;
 
 extern PRBool
 IsValidHTTPToken(const nsCSubstring& aToken);
 
 class nsCrossSiteListenerProxy : public nsIStreamListener,
                                  public nsIInterfaceRequestor,
                                  public nsIChannelEventSink
 {
@@ -63,29 +67,16 @@ public:
   nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
                            nsIPrincipal* aRequestingPrincipal,
                            nsIChannel* aChannel,
                            PRBool aWithCredentials,
                            const nsCString& aPreflightMethod,
                            const nsTArray<nsCString>& aPreflightHeaders,
                            nsresult* aResult);
 
-  static nsresult CheckPreflight(nsIHttpChannel* aRequestChannel,
-                                 nsIStreamListener* aRequestListener,
-                                 nsISupports* aRequestContext,
-                                 nsIPrincipal* aRequestingPrincipal,
-                                 PRBool aForcePreflight,
-                                 nsTArray<nsCString>& aUnsafeHeaders,
-                                 PRBool aWithCredentials,
-                                 PRBool* aPreflighted,
-                                 nsIChannel** aPreflightChannel,
-                                 nsIStreamListener** aPreflightListener);
-
-  static void ShutdownPreflightCache();
-
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSICHANNELEVENTSINK
 
 private:
   nsresult UpdateChannel(nsIChannel* aChannel);
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -133,16 +133,18 @@
   (XML_HTTP_REQUEST_UNINITIALIZED |         \
    XML_HTTP_REQUEST_OPENED |                \
    XML_HTTP_REQUEST_LOADED |                \
    XML_HTTP_REQUEST_INTERACTIVE |           \
    XML_HTTP_REQUEST_COMPLETED |             \
    XML_HTTP_REQUEST_SENT |                  \
    XML_HTTP_REQUEST_STOPPED)
 
+#define ACCESS_CONTROL_CACHE_SIZE 100
+
 #define NS_BADCERTHANDLER_CONTRACTID \
   "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventListenerWrapper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMEventListenerWrapper)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
 NS_INTERFACE_MAP_END_AGGREGATED(mListener)
@@ -276,16 +278,230 @@ nsMultipartProxyListener::OnDataAvailabl
                                           nsIInputStream *inStr,
                                           PRUint32 sourceOffset,
                                           PRUint32 count)
 {
   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset,
                                         count);
 }
 
+// Class used as streamlistener and notification callback when
+// doing the initial GET request for an access-control check
+class nsACProxyListener : public nsIStreamListener,
+                          public nsIInterfaceRequestor,
+                          public nsIChannelEventSink
+{
+public:
+  nsACProxyListener(nsIChannel* aOuterChannel,
+                    nsIStreamListener* aOuterListener,
+                    nsISupports* aOuterContext,
+                    nsIPrincipal* aReferrerPrincipal,
+                    const nsACString& aRequestMethod,
+                    PRBool aWithCredentials)
+   : mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
+     mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
+     mRequestMethod(aRequestMethod), mWithCredentials(aWithCredentials)
+  { }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSICHANNELEVENTSINK
+
+private:
+  void AddResultToCache(nsIRequest* aRequest);
+
+  nsCOMPtr<nsIChannel> mOuterChannel;
+  nsCOMPtr<nsIStreamListener> mOuterListener;
+  nsCOMPtr<nsISupports> mOuterContext;
+  nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
+  nsCString mRequestMethod;
+  PRBool mWithCredentials;
+};
+
+NS_IMPL_ISUPPORTS4(nsACProxyListener, nsIStreamListener, nsIRequestObserver,
+                   nsIInterfaceRequestor, nsIChannelEventSink)
+
+void
+nsACProxyListener::AddResultToCache(nsIRequest *aRequest)
+{
+  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
+  NS_ASSERTION(http, "Request was not http");
+
+  // The "Access-Control-Max-Age" header should return an age in seconds.
+  nsCAutoString headerVal;
+  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
+                          headerVal);
+  if (headerVal.IsEmpty()) {
+    return;
+  }
+
+  // Sanitize the string. We only allow 'delta-seconds' as specified by
+  // http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
+  // trailing non-whitespace characters).
+  PRUint32 age = 0;
+  nsCSubstring::const_char_iterator iter, end;
+  headerVal.BeginReading(iter);
+  headerVal.EndReading(end);
+  while (iter != end) {
+    if (*iter < '0' || *iter > '9') {
+      return;
+    }
+    age = age * 10 + (*iter - '0');
+    // Cap at 24 hours. This also avoids overflow
+    age = PR_MIN(age, 86400);
+    ++iter;
+  }
+
+  if (!age || !nsXMLHttpRequest::EnsureACCache()) {
+    return;
+  }
+
+
+  // String seems fine, go ahead and cache.
+  // Note that we have already checked that these headers follow the correct
+  // syntax.
+
+  nsCOMPtr<nsIURI> uri;
+  http->GetURI(getter_AddRefs(uri));
+
+  // PR_Now gives microseconds
+  PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;
+
+  nsAccessControlLRUCache::CacheEntry* entry =
+    nsXMLHttpRequest::sAccessControlCache->
+    GetEntry(uri, mReferrerPrincipal, mWithCredentials, PR_TRUE);
+  if (!entry) {
+    return;
+  }
+
+  // The "Access-Control-Allow-Methods" header contains a comma separated
+  // list of method names.
+  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
+                          headerVal);
+
+  nsCCommaSeparatedTokenizer methods(headerVal);
+  while(methods.hasMoreTokens()) {
+    const nsDependentCSubstring& method = methods.nextToken();
+    if (method.IsEmpty()) {
+      continue;
+    }
+    PRUint32 i;
+    for (i = 0; i < entry->mMethods.Length(); ++i) {
+      if (entry->mMethods[i].token.Equals(method)) {
+        entry->mMethods[i].expirationTime = expirationTime;
+        break;
+      }
+    }
+    if (i == entry->mMethods.Length()) {
+      nsAccessControlLRUCache::TokenTime* newMethod =
+        entry->mMethods.AppendElement();
+      if (!newMethod) {
+        return;
+      }
+
+      newMethod->token = method;
+      newMethod->expirationTime = expirationTime;
+    }
+  }
+
+  // The "Access-Control-Allow-Headers" header contains a comma separated
+  // list of method names.
+  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
+                          headerVal);
+
+  nsCCommaSeparatedTokenizer headers(headerVal);
+  while(headers.hasMoreTokens()) {
+    const nsDependentCSubstring& header = headers.nextToken();
+    if (header.IsEmpty()) {
+      continue;
+    }
+    PRUint32 i;
+    for (i = 0; i < entry->mHeaders.Length(); ++i) {
+      if (entry->mHeaders[i].token.Equals(header)) {
+        entry->mHeaders[i].expirationTime = expirationTime;
+        break;
+      }
+    }
+    if (i == entry->mHeaders.Length()) {
+      nsAccessControlLRUCache::TokenTime* newHeader =
+        entry->mHeaders.AppendElement();
+      if (!newHeader) {
+        return;
+      }
+
+      newHeader->token = header;
+      newHeader->expirationTime = expirationTime;
+    }
+  }
+}
+
+NS_IMETHODIMP
+nsACProxyListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
+{
+  nsresult status;
+  nsresult rv = aRequest->GetStatus(&status);
+
+  if (NS_SUCCEEDED(rv)) {
+    rv = status;
+  }
+
+  if (NS_SUCCEEDED(rv)) {
+    // Everything worked, try to cache and then fire off the actual request.
+    AddResultToCache(aRequest);
+
+    rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
+  }
+
+  if (NS_FAILED(rv)) {
+    mOuterChannel->Cancel(rv);
+    mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
+    mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
+    
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsACProxyListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
+                                 nsresult aStatus)
+{
+  return NS_OK;
+}
+
+/** nsIStreamListener methods **/
+
+NS_IMETHODIMP
+nsACProxyListener::OnDataAvailable(nsIRequest *aRequest,
+                                   nsISupports *ctxt,
+                                   nsIInputStream *inStr,
+                                   PRUint32 sourceOffset,
+                                   PRUint32 count)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsACProxyListener::OnChannelRedirect(nsIChannel *aOldChannel,
+                                     nsIChannel *aNewChannel,
+                                     PRUint32 aFlags)
+{
+  // No redirects allowed for now.
+  return NS_ERROR_DOM_BAD_URI;
+}
+
+NS_IMETHODIMP
+nsACProxyListener::GetInterface(const nsIID & aIID, void **aResult)
+{
+  return QueryInterface(aIID, aResult);
+}
+
 /**
  * Gets the nsIDocument given the script context. Will return nsnull on failure.
  *
  * @param aScriptContext the script context to get the document for; can be null
  *
  * @return the document associated with the script context
  */
 static already_AddRefed<nsIDocument>
@@ -623,22 +839,211 @@ nsXMLHttpRequestUpload::~nsXMLHttpReques
 NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequestUpload)
   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestUpload)
   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XMLHttpRequestUpload)
 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
 
 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
 
+void
+nsAccessControlLRUCache::CacheEntry::PurgeExpired(PRTime now)
+{
+  PRUint32 i;
+  for (i = 0; i < mMethods.Length(); ++i) {
+    if (now >= mMethods[i].expirationTime) {
+      mMethods.RemoveElementAt(i--);
+    }
+  }
+  for (i = 0; i < mHeaders.Length(); ++i) {
+    if (now >= mHeaders[i].expirationTime) {
+      mHeaders.RemoveElementAt(i--);
+    }
+  }
+}
+
+PRBool
+nsAccessControlLRUCache::CacheEntry::CheckRequest(const nsCString& aMethod,
+                                                  const nsTArray<nsCString>& aHeaders)
+{
+  PurgeExpired(PR_Now());
+
+  if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
+    PRUint32 i;
+    for (i = 0; i < mMethods.Length(); ++i) {
+      if (aMethod.Equals(mMethods[i].token))
+        break;
+    }
+    if (i == mMethods.Length()) {
+      return PR_FALSE;
+    }
+  }
+
+  for (PRUint32 i = 0; i < aHeaders.Length(); ++i) {
+    PRUint32 j;
+    for (j = 0; j < mHeaders.Length(); ++j) {
+      if (aHeaders[i].Equals(mHeaders[j].token,
+                             nsCaseInsensitiveCStringComparator())) {
+        break;
+      }
+    }
+    if (j == mHeaders.Length()) {
+      return PR_FALSE;
+    }
+  }
+
+  return PR_TRUE;
+}
+
+nsAccessControlLRUCache::CacheEntry*
+nsAccessControlLRUCache::GetEntry(nsIURI* aURI,
+                                  nsIPrincipal* aPrincipal,
+                                  PRBool aWithCredentials,
+                                  PRBool aCreate)
+{
+  nsCString key;
+  if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
+    NS_WARNING("Invalid cache key!");
+    return nsnull;
+  }
+
+  CacheEntry* entry;
+
+  if (mTable.Get(key, &entry)) {
+    // Entry already existed so just return it. Also update the LRU list.
+
+    // Move to the head of the list.
+    PR_REMOVE_LINK(entry);
+    PR_INSERT_LINK(entry, &mList);
+
+    return entry;
+  }
+
+  if (!aCreate) {
+    return nsnull;
+  }
+
+  // This is a new entry, allocate and insert into the table now so that any
+  // failures don't cause items to be removed from a full cache.
+  entry = new CacheEntry(key);
+  if (!entry) {
+    NS_WARNING("Failed to allocate new cache entry!");
+    return nsnull;
+  }
+
+  if (!mTable.Put(key, entry)) {
+    // Failed, clean up the new entry.
+    delete entry;
+
+    NS_WARNING("Failed to add entry to the access control cache!");
+    return nsnull;
+  }
+
+  PR_INSERT_LINK(entry, &mList);
+
+  NS_ASSERTION(mTable.Count() <= ACCESS_CONTROL_CACHE_SIZE + 1,
+               "Something is borked, too many entries in the cache!");
+
+  // Now enforce the max count.
+  if (mTable.Count() > ACCESS_CONTROL_CACHE_SIZE) {
+    // Try to kick out all the expired entries.
+    PRTime now = PR_Now();
+    mTable.Enumerate(RemoveExpiredEntries, &now);
+
+    // If that didn't remove anything then kick out the least recently used
+    // entry.
+    if (mTable.Count() > ACCESS_CONTROL_CACHE_SIZE) {
+      CacheEntry* lruEntry = static_cast<CacheEntry*>(PR_LIST_TAIL(&mList));
+      PR_REMOVE_LINK(lruEntry);
+
+      // This will delete 'lruEntry'.
+      mTable.Remove(lruEntry->mKey);
+
+      NS_ASSERTION(mTable.Count() >= ACCESS_CONTROL_CACHE_SIZE,
+                   "Somehow tried to remove an entry that was never added!");
+    }
+  }
+  
+  return entry;
+}
+
+void
+nsAccessControlLRUCache::Clear()
+{
+  PR_INIT_CLIST(&mList);
+  mTable.Clear();
+}
+
+/* static */ PR_CALLBACK PLDHashOperator
+nsAccessControlLRUCache::RemoveExpiredEntries(const nsACString& aKey,
+                                              nsAutoPtr<CacheEntry>& aValue,
+                                              void* aUserData)
+{
+  PRTime* now = static_cast<PRTime*>(aUserData);
+  
+  aValue->PurgeExpired(*now);
+  
+  if (aValue->mHeaders.IsEmpty() &&
+      aValue->mHeaders.IsEmpty()) {
+    // Expired, remove from the list as well as the hash table.
+    PR_REMOVE_LINK(aValue);
+    return PL_DHASH_REMOVE;
+  }
+  
+  return PL_DHASH_NEXT;
+}
+
+/* static */ PRBool
+nsAccessControlLRUCache::GetCacheKey(nsIURI* aURI,
+                                     nsIPrincipal* aPrincipal,
+                                     PRBool aWithCredentials,
+                                     nsACString& _retval)
+{
+  NS_ASSERTION(aURI, "Null uri!");
+  NS_ASSERTION(aPrincipal, "Null principal!");
+  
+  NS_NAMED_LITERAL_CSTRING(space, " ");
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+  
+  nsCAutoString scheme, host, port;
+  if (uri) {
+    uri->GetScheme(scheme);
+    uri->GetHost(host);
+    port.AppendInt(NS_GetRealPort(uri));
+  }
+
+  nsCAutoString cred;
+  if (aWithCredentials) {
+    _retval.AssignLiteral("cred");
+  }
+  else {
+    _retval.AssignLiteral("nocred");
+  }
+
+  nsCAutoString spec;
+  rv = aURI->GetSpec(spec);
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+  _retval.Assign(cred + space + scheme + space + host + space + port + space +
+                 spec);
+
+  return PR_TRUE;
+}
 
 /////////////////////////////////////////////
 //
 //
 /////////////////////////////////////////////
 
+// Will be initialized in nsXMLHttpRequest::EnsureACCache.
+nsAccessControlLRUCache* nsXMLHttpRequest::sAccessControlCache = nsnull;
+
 nsXMLHttpRequest::nsXMLHttpRequest()
   : mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNINITIALIZED),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(PR_TRUE),
     mErrorLoad(PR_FALSE), mFirstStartRequestSeen(PR_FALSE)
 {
   nsLayoutStatics::AddRef();
 }
 
@@ -1073,18 +1478,18 @@ NS_IMETHODIMP
 nsXMLHttpRequest::Abort()
 {
   if (mReadRequest) {
     mReadRequest->Cancel(NS_BINDING_ABORTED);
   }
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
   }
-  if (mACPreflightChannel) {
-    mACPreflightChannel->Cancel(NS_BINDING_ABORTED);
+  if (mACGetChannel) {
+    mACGetChannel->Cancel(NS_BINDING_ABORTED);
   }
   mResponseXML = nsnull;
   mResponseBody.Truncate();
   mState |= XML_HTTP_REQUEST_ABORTED;
 
   if (!(mState & (XML_HTTP_REQUEST_UNINITIALIZED |
                   XML_HTTP_REQUEST_OPENED |
                   XML_HTTP_REQUEST_COMPLETED))) {
@@ -1194,16 +1599,34 @@ nsXMLHttpRequest::GetResponseHeader(cons
     _retval.SetIsVoid(PR_TRUE);
     rv = NS_OK;
   }
 
   return rv;
 }
 
 nsresult
+nsXMLHttpRequest::GetLoadGroup(nsILoadGroup **aLoadGroup)
+{
+  NS_ENSURE_ARG_POINTER(aLoadGroup);
+  *aLoadGroup = nsnull;
+
+  if (mState & XML_HTTP_REQUEST_BACKGROUND) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDocument> doc = GetDocumentFromScriptContext(mScriptContext);
+  if (doc) {
+    *aLoadGroup = doc->GetDocumentLoadGroup().get();  // already_AddRefed
+  }
+
+  return NS_OK;
+}
+
+nsresult
 nsXMLHttpRequest::CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent)
 {
   nsresult rv = nsEventDispatcher::CreateEvent(nsnull, nsnull,
                                                NS_LITERAL_STRING("Events"),
                                                aDOMEvent);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -1417,38 +1840,37 @@ nsXMLHttpRequest::OpenRequest(const nsAC
     if (!password.IsEmpty()) {
       userpass.Append(':');
       AppendUTF16toUTF8(password, userpass);
     }
     uri->SetUserPass(userpass);
     authp = PR_TRUE;
   }
 
-  // Find the load group for the page, and add ourselves to it. This way any
-  // pending requests will be automatically aborted if the user leaves the page.
+  // When we are called from JS we can find the load group for the page,
+  // and add ourselves to it. This way any pending requests
+  // will be automatically aborted if the user leaves the page.
   nsCOMPtr<nsILoadGroup> loadGroup;
-  if (doc && !(mState & XML_HTTP_REQUEST_BACKGROUND)) {
-    loadGroup = doc->GetDocumentLoadGroup();
-  }
+  GetLoadGroup(getter_AddRefs(loadGroup));
 
   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
   // in turn keeps STOP button from becoming active.  If the consumer passed in
   // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
   // necko won't generate any progress notifications
   nsLoadFlags loadFlags;
   if (HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR)) ||
       HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) ||
       (mUpload && mUpload->HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR)))) {
     loadFlags = nsIRequest::LOAD_NORMAL;
   } else {
     loadFlags = nsIRequest::LOAD_BACKGROUND;
   }
   rv = NS_NewChannel(getter_AddRefs(mChannel), uri, nsnull, loadGroup, nsnull,
                      loadFlags);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) return rv;
 
   // Check if we're doing a cross-origin request.
   if (IsSystemPrincipal(mPrincipal)) {
     // Chrome callers are always allowed to read from different origins.
     mState |= XML_HTTP_REQUEST_XSITEENABLED;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
@@ -1943,184 +2365,16 @@ nsXMLHttpRequest::SendAsBinary(const nsA
   if (!variant) return NS_ERROR_OUT_OF_MEMORY;
 
   rv = variant->SetAsISupports(stream);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return Send(variant);
 }
 
-nsresult
-nsXMLHttpRequest::SetUploadDataAndType(nsIVariant* aBody)
-{
-  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
-  NS_ASSERTION(httpChannel, "Should be http channel to get here");
-
-  nsXPIDLString serial;
-  nsCOMPtr<nsIInputStream> postDataStream;
-  nsCAutoString charset(NS_LITERAL_CSTRING("UTF-8"));
-
-  PRUint16 dataType;
-  nsresult rv = aBody->GetDataType(&dataType);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  switch (dataType) {
-  case nsIDataType::VTYPE_INTERFACE:
-  case nsIDataType::VTYPE_INTERFACE_IS:
-    {
-      nsCOMPtr<nsISupports> supports;
-      nsID *iid;
-      rv = aBody->GetAsInterface(&iid, getter_AddRefs(supports));
-      if (NS_FAILED(rv))
-        return rv;
-      if (iid)
-        nsMemory::Free(iid);
-
-      // document?
-      nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(supports));
-      if (doc) {
-        nsCOMPtr<nsIDOMSerializer> serializer =
-          do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        nsCOMPtr<nsIDOM3Document> dom3doc(do_QueryInterface(doc));
-        if (dom3doc) {
-          nsAutoString inputEncoding;
-          dom3doc->GetInputEncoding(inputEncoding);
-          if (DOMStringIsNull(inputEncoding)) {
-            charset.AssignLiteral("UTF-8");
-          } else {
-            CopyUTF16toUTF8(inputEncoding, charset);
-          }
-        }
-
-        // Serialize to a stream so that the encoding used will
-        // match the document's.
-        nsCOMPtr<nsIStorageStream> storStream;
-        rv = NS_NewStorageStream(4096, PR_UINT32_MAX,
-                                 getter_AddRefs(storStream));
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        nsCOMPtr<nsIOutputStream> output;
-        rv = storStream->GetOutputStream(0, getter_AddRefs(output));
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        // Empty string for encoding means to use document's current
-        // encoding.
-        rv = serializer->SerializeToStream(doc, output, EmptyCString());
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        output->Close();
-        rv = storStream->NewInputStream(0, getter_AddRefs(postDataStream));
-        NS_ENSURE_SUCCESS(rv, rv);
-      } else {
-        // nsISupportsString?
-        nsCOMPtr<nsISupportsString> wstr(do_QueryInterface(supports));
-        if (wstr) {
-          wstr->GetData(serial);
-        } else {
-          // stream?
-          nsCOMPtr<nsIInputStream> stream(do_QueryInterface(supports));
-          if (stream) {
-            postDataStream = stream;
-            charset.Truncate();
-          }
-        }
-      }
-    }
-    break;
-  case nsIDataType::VTYPE_VOID:
-  case nsIDataType::VTYPE_EMPTY:
-    // Makes us act as if !aBody, don't upload anything
-    break;
-  case nsIDataType::VTYPE_EMPTY_ARRAY:
-  case nsIDataType::VTYPE_ARRAY:
-    // IE6 throws error here, so we do that as well
-    return NS_ERROR_INVALID_ARG;
-  default:
-    // try variant string
-    rv = aBody->GetAsWString(getter_Copies(serial));
-    NS_ENSURE_SUCCESS(rv, rv);
-    break;
-  }
-
-  if (serial) {
-    // Convert to a byte stream
-    nsCOMPtr<nsIScriptableUnicodeConverter> converter =
-      do_CreateInstance("@mozilla.org/intl/scriptableunicodeconverter", &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = converter->SetCharset("UTF-8");
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = converter->ConvertToInputStream(serial,
-                                         getter_AddRefs(postDataStream));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  if (postDataStream) {
-    nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
-    NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
-
-    // If no content type header was set by the client, we set it to
-    // application/xml.
-    nsCAutoString contentType;
-    if (NS_FAILED(httpChannel->
-                  GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
-                                   contentType)) ||
-        contentType.IsEmpty()) {
-      contentType = NS_LITERAL_CSTRING("application/xml");
-    }
-
-    // We don't want to set a charset for streams.
-    if (!charset.IsEmpty()) {
-      nsCAutoString specifiedCharset;
-      PRBool haveCharset;
-      PRInt32 charsetStart, charsetEnd;
-      rv = NS_ExtractCharsetFromContentType(contentType, specifiedCharset,
-                                            &haveCharset, &charsetStart,
-                                            &charsetEnd);
-      if (NS_FAILED(rv)) {
-        contentType.AssignLiteral("application/xml");
-        specifiedCharset.Truncate();
-        charsetStart = charsetEnd = contentType.Length();
-      }
-
-      // If the content-type the page set already has a charset parameter,
-      // and it's the same charset, up to case, as |charset|, just send the
-      // page-set content-type header.  Apparently at least
-      // google-web-toolkit is broken and relies on the exact case of its
-      // charset parameter, which makes things break if we use |charset|
-      // (which is always a fully resolved charset per our charset alias
-      // table, hence might be differently cased).
-      if (!specifiedCharset.Equals(charset,
-                                   nsCaseInsensitiveCStringComparator())) {
-        nsCAutoString newCharset("; charset=");
-        newCharset.Append(charset);
-        contentType.Replace(charsetStart, charsetEnd - charsetStart,
-                            newCharset);
-      }
-    }
-
-    mUploadComplete = PR_FALSE;
-
-    nsCAutoString method;
-    httpChannel->GetRequestMethod(method);
-
-    postDataStream->Available(&mUploadTotal);
-    rv = uploadChannel->SetUploadStream(postDataStream, contentType, -1);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Reset the method to its original value
-    httpChannel->SetRequestMethod(method);
-  }
-
-  return NS_OK;
-}
-
 /* void send (in nsIVariant aBody); */
 NS_IMETHODIMP
 nsXMLHttpRequest::Send(nsIVariant *aBody)
 {
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
   nsresult rv = CheckInnerWindowCorrectness();
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2140,71 +2394,263 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
   //     an asynchronous call.
 
   // Ignore argument if method is GET, there is no point in trying to
   // upload anything
   nsCAutoString method;
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
 
   if (httpChannel) {
-    // If GET, method name will be uppercase
-    httpChannel->GetRequestMethod(method);
+    httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
 
     if (!IsSystemPrincipal(mPrincipal)) {
       nsCOMPtr<nsIURI> codebase;
       mPrincipal->GetURI(getter_AddRefs(codebase));
 
       httpChannel->SetReferrer(codebase);
     }
   }
 
   mUploadTransferred = 0;
   mUploadTotal = 0;
   // By default we don't have any upload, so mark upload complete.
   mUploadComplete = PR_TRUE;
   mErrorLoad = PR_FALSE;
   if (aBody && httpChannel && !method.EqualsLiteral("GET")) {
-    SetUploadDataAndType(aBody);
-  }
-
-  // Bypass the network cache in cases where it makes no sense:
-  // 1) Multipart responses are very large and would likely be doomed by the
-  //    cache once they grow too large, so they are not worth caching.
-  // 2) POST responses are always unique, and we provide no API that would
-  //    allow our consumers to specify a "cache key" to access old POST
-  //    responses, so they are not worth caching.
-  if ((mState & XML_HTTP_REQUEST_MULTIPART) || method.EqualsLiteral("POST")) {
-    AddLoadFlags(mChannel,
-        nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
+    nsXPIDLString serial;
+    nsCOMPtr<nsIInputStream> postDataStream;
+    nsCAutoString charset(NS_LITERAL_CSTRING("UTF-8"));
+
+    PRUint16 dataType;
+    rv = aBody->GetDataType(&dataType);
+    if (NS_FAILED(rv))
+      return rv;
+
+    switch (dataType) {
+    case nsIDataType::VTYPE_INTERFACE:
+    case nsIDataType::VTYPE_INTERFACE_IS:
+      {
+        nsCOMPtr<nsISupports> supports;
+        nsID *iid;
+        rv = aBody->GetAsInterface(&iid, getter_AddRefs(supports));
+        if (NS_FAILED(rv))
+          return rv;
+        if (iid)
+          nsMemory::Free(iid);
+
+        // document?
+        nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(supports));
+        if (doc) {
+          nsCOMPtr<nsIDOMSerializer> serializer(do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv));
+          if (NS_FAILED(rv)) return rv;
+
+          nsCOMPtr<nsIDOM3Document> dom3doc(do_QueryInterface(doc));
+          if (dom3doc) {
+            nsAutoString inputEncoding;
+            dom3doc->GetInputEncoding(inputEncoding);
+            if (DOMStringIsNull(inputEncoding)) {
+              charset.AssignLiteral("UTF-8");
+            } else {
+              CopyUTF16toUTF8(inputEncoding, charset);
+            }
+          }
+
+          // Serialize to a stream so that the encoding used will
+          // match the document's.
+          nsCOMPtr<nsIStorageStream> storStream;
+          rv = NS_NewStorageStream(4096, PR_UINT32_MAX, getter_AddRefs(storStream));
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          nsCOMPtr<nsIOutputStream> output;
+          rv = storStream->GetOutputStream(0, getter_AddRefs(output));
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          // Empty string for encoding means to use document's current
+          // encoding.
+          rv = serializer->SerializeToStream(doc, output, EmptyCString());
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          output->Close();
+          rv = storStream->NewInputStream(0, getter_AddRefs(postDataStream));
+          NS_ENSURE_SUCCESS(rv, rv);
+        } else {
+          // nsISupportsString?
+          nsCOMPtr<nsISupportsString> wstr(do_QueryInterface(supports));
+          if (wstr) {
+            wstr->GetData(serial);
+          } else {
+            // stream?
+            nsCOMPtr<nsIInputStream> stream(do_QueryInterface(supports));
+            if (stream) {
+              postDataStream = stream;
+              charset.Truncate();
+            }
+          }
+        }
+      }
+      break;
+    case nsIDataType::VTYPE_VOID:
+    case nsIDataType::VTYPE_EMPTY:
+      // Makes us act as if !aBody, don't upload anything
+      break;
+    case nsIDataType::VTYPE_EMPTY_ARRAY:
+    case nsIDataType::VTYPE_ARRAY:
+      // IE6 throws error here, so we do that as well
+      return NS_ERROR_INVALID_ARG;
+    default:
+      // try variant string
+      rv = aBody->GetAsWString(getter_Copies(serial));
+      if (NS_FAILED(rv))
+        return rv;
+      break;
+    }
+
+    if (serial) {
+      // Convert to a byte stream
+      nsCOMPtr<nsIScriptableUnicodeConverter> converter =
+        do_CreateInstance("@mozilla.org/intl/scriptableunicodeconverter", &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = converter->SetCharset("UTF-8");
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = converter->ConvertToInputStream(serial,
+                                           getter_AddRefs(postDataStream));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    if (postDataStream) {
+      nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
+      NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
+
+      // If no content type header was set by the client, we set it to
+      // application/xml.
+      nsCAutoString contentType;
+      if (NS_FAILED(httpChannel->
+                      GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
+                                       contentType)) ||
+          contentType.IsEmpty()) {
+        contentType = NS_LITERAL_CSTRING("application/xml");
+      }
+
+      // We don't want to set a charset for streams.
+      if (!charset.IsEmpty()) {
+        nsCAutoString specifiedCharset;
+        PRBool haveCharset;
+        PRInt32 charsetStart, charsetEnd;
+        rv = NS_ExtractCharsetFromContentType(contentType, specifiedCharset,
+                                              &haveCharset, &charsetStart,
+                                              &charsetEnd);
+        if (NS_FAILED(rv)) {
+          contentType.AssignLiteral("application/xml");
+          specifiedCharset.Truncate();
+          charsetStart = charsetEnd = contentType.Length();
+        }
+
+        // If the content-type the page set already has a charset parameter,
+        // and it's the same charset, up to case, as |charset|, just send the
+        // page-set content-type header.  Apparently at least
+        // google-web-toolkit is broken and relies on the exact case of its
+        // charset parameter, which makes things break if we use |charset|
+        // (which is always a fully resolved charset per our charset alias
+        // table, hence might be differently cased).
+        if (!specifiedCharset.Equals(charset,
+                                     nsCaseInsensitiveCStringComparator())) {
+          nsCAutoString newCharset("; charset=");
+          newCharset.Append(charset);
+          contentType.Replace(charsetStart, charsetEnd - charsetStart,
+                              newCharset);
+        }
+      }
+
+      mUploadComplete = PR_FALSE;
+      postDataStream->Available(&mUploadTotal);
+      rv = uploadChannel->SetUploadStream(postDataStream, contentType, -1);
+      // Reset the method to its original value
+      if (httpChannel) {
+        httpChannel->SetRequestMethod(method);
+      }
+    }
   }
-  // When we are sync loading, we need to bypass the local cache when it would
-  // otherwise block us waiting for exclusive access to the cache.  If we don't
-  // do this, then we could dead lock in some cases (see bug 309424).
-  else if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
-    AddLoadFlags(mChannel,
-        nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
-  }
-
-  // Since we expect XML data, set the type hint accordingly
-  // This means that we always try to parse local files as XML
-  // ignoring return value, as this is not critical
-  mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
 
   // Reset responseBody
   mResponseBody.Truncate();
 
   // Reset responseXML
   mResponseXML = nsnull;
 
   rv = CheckChannelForCrossSiteRequest(mChannel);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Start request, or preflight request if one is needed.
   PRBool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
 
+  if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
+    // Check if we need to do a preflight request.
+    NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
+    
+    nsCAutoString method;
+    httpChannel->GetRequestMethod(method);
+    if (!mACUnsafeHeaders.IsEmpty() ||
+        HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) ||
+        (mUpload && mUpload->HasListeners())) {
+      mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
+    }
+    else if (method.LowerCaseEqualsLiteral("post")) {
+      nsCAutoString contentTypeHeader;
+      httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
+                                    contentTypeHeader);
+
+      nsCAutoString contentType, charset;
+      NS_ParseContentType(contentTypeHeader, contentType, charset);
+
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (!contentType.LowerCaseEqualsLiteral("text/plain")) {
+        mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
+      }
+    }
+    else if (!method.LowerCaseEqualsLiteral("get")) {
+      mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
+    }
+
+    // If so, set up the preflight
+    if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
+      // Check to see if this initial OPTIONS request has already been cached
+      // in our special Access Control Cache.
+      nsCOMPtr<nsIURI> uri;
+      rv = mChannel->GetURI(getter_AddRefs(uri));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsAccessControlLRUCache::CacheEntry* entry =
+        sAccessControlCache ?
+        sAccessControlCache->GetEntry(uri, mPrincipal, withCredentials, PR_FALSE) :
+        nsnull;
+
+      if (!entry || !entry->CheckRequest(method, mACUnsafeHeaders)) {
+        // Either it wasn't cached or the cached result has expired. Build a
+        // channel for the OPTIONS request.
+        nsCOMPtr<nsILoadGroup> loadGroup;
+        GetLoadGroup(getter_AddRefs(loadGroup));
+
+        nsLoadFlags loadFlags;
+        rv = mChannel->GetLoadFlags(&loadFlags);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = NS_NewChannel(getter_AddRefs(mACGetChannel), uri, nsnull,
+                           loadGroup, nsnull, loadFlags);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsCOMPtr<nsIHttpChannel> acHttp = do_QueryInterface(mACGetChannel);
+        NS_ASSERTION(acHttp, "Failed to QI to nsIHttpChannel!");
+
+        rv = acHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+  }
+
   // Hook us up to listen to redirects and the like
   mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
   mChannel->SetNotificationCallbacks(this);
 
   // Create our listener
   nsCOMPtr<nsIStreamListener> listener = this;
   if (mState & XML_HTTP_REQUEST_MULTIPART) {
     listener = new nsMultipartProxyListener(listener);
@@ -2217,51 +2663,69 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
     // Always create a nsCrossSiteListenerProxy here even if it's
     // a same-origin request right now, since it could be redirected.
     listener = new nsCrossSiteListenerProxy(listener, mPrincipal, mChannel,
                                             withCredentials, &rv);
     NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsCOMPtr<nsIStreamListener> preflightListener;
-  if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
-    // Check if we need to do a preflight request.
-    NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
-
-    PRBool force = HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) ||
-      (mUpload && mUpload->HasListeners());
-
-    PRBool preflighted;
-    rv = nsCrossSiteListenerProxy::
-      CheckPreflight(httpChannel, listener, nsnull, mPrincipal, force,
-                     mACUnsafeHeaders, withCredentials, &preflighted,
-                     getter_AddRefs(mACPreflightChannel),
-                     getter_AddRefs(preflightListener));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (preflighted) {
-      mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
+  // Bypass the network cache in cases where it makes no sense:
+  // 1) Multipart responses are very large and would likely be doomed by the
+  //    cache once they grow too large, so they are not worth caching.
+  // 2) POST responses are always unique, and we provide no API that would
+  //    allow our consumers to specify a "cache key" to access old POST
+  //    responses, so they are not worth caching.
+  if ((mState & XML_HTTP_REQUEST_MULTIPART) || method.EqualsLiteral("POST")) {
+    AddLoadFlags(mChannel,
+        nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
+  }
+  // When we are sync loading, we need to bypass the local cache when it would
+  // otherwise block us waiting for exclusive access to the cache.  If we don't
+  // do this, then we could dead lock in some cases (see bug 309424).
+  else if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
+    AddLoadFlags(mChannel,
+        nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
+    if (mACGetChannel) {
+      AddLoadFlags(mACGetChannel,
+          nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
     }
   }
 
-  if (mACPreflightChannel) {
-    // Start preflight. Preflight channel will start mChannel
-    // if preflight succeeds.
-    rv = mACPreflightChannel->AsyncOpen(preflightListener, nsnull);
+  // Since we expect XML data, set the type hint accordingly
+  // This means that we always try to parse local files as XML
+  // ignoring return value, as this is not critical
+  mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
+
+  // If we're doing a cross-site non-GET request we need to first do
+  // a GET request to the same URI. Set that up if needed
+  if (mACGetChannel) {
+    nsCOMPtr<nsIStreamListener> acProxyListener =
+      new nsACProxyListener(mChannel, listener, nsnull, mPrincipal, method,
+                            withCredentials);
+    NS_ENSURE_TRUE(acProxyListener, NS_ERROR_OUT_OF_MEMORY);
+
+    acProxyListener =
+      new nsCrossSiteListenerProxy(acProxyListener, mPrincipal, mACGetChannel,
+                                   withCredentials, method, mACUnsafeHeaders,
+                                   &rv);
+    NS_ENSURE_TRUE(acProxyListener, NS_ERROR_OUT_OF_MEMORY);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mACGetChannel->AsyncOpen(acProxyListener, nsnull);
   }
   else {
     // Start reading from the channel
     rv = mChannel->AsyncOpen(listener, nsnull);
   }
 
   if (NS_FAILED(rv)) {
     // Drop our ref to the channel to avoid cycles
     mChannel = nsnull;
-    mACPreflightChannel = nsnull;
+    mACGetChannel = nsnull;
     return rv;
   }
 
   // Now that we've successfully opened the channel, we can change state.  Note
   // that this needs to come after the AsyncOpen() and rv check, because this
   // can run script that would try to restart this request, and that could end
   // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
   ChangeState(XML_HTTP_REQUEST_SENT);
@@ -2301,22 +2765,22 @@ nsXMLHttpRequest::SetRequestHeader(const
 
   // Make sure we don't store an invalid header name in mACUnsafeHeaders
   if (!IsValidHTTPToken(header)) {
     return NS_ERROR_FAILURE;
   }
 
   // Check that we haven't already opened the channel. We can't rely on
   // the channel throwing from mChannel->SetRequestHeader since we might
-  // still be waiting for mACPreflightChannel to actually open mChannel
-  if (mACPreflightChannel) {
+  // still be waiting for mACGetChannel to actually open mChannel
+  if (mACGetChannel) {
     PRBool pending;
-    rv = mACPreflightChannel->IsPending(&pending);
+    rv = mACGetChannel->IsPending(&pending);
     NS_ENSURE_SUCCESS(rv, rv);
-
+    
     if (pending) {
       return NS_ERROR_IN_PROGRESS;
     }
   }
 
   if (!mChannel)             // open() initializes mChannel, and open()
     return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
 
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -59,24 +59,92 @@
 #include "nsIProgressEventSink.h"
 #include "nsCOMArray.h"
 #include "nsJSUtils.h"
 #include "nsTArray.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIJSNativeInitializer.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMLSProgressEvent.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "prclist.h"
 #include "prtime.h"
 #include "nsIEventListenerManager.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsDOMProgressEvent.h"
 
 class nsILoadGroup;
 
+class nsAccessControlLRUCache
+{
+public:
+  struct TokenTime
+  {
+    nsCString token;
+    PRTime expirationTime;
+  };
+
+  struct CacheEntry : public PRCList
+  {
+    CacheEntry(nsCString& aKey)
+      : mKey(aKey)
+    {
+      MOZ_COUNT_CTOR(nsAccessControlLRUCache::CacheEntry);
+    }
+    
+    ~CacheEntry()
+    {
+      MOZ_COUNT_DTOR(nsAccessControlLRUCache::CacheEntry);
+    }
+
+    void PurgeExpired(PRTime now);
+    PRBool CheckRequest(const nsCString& aMethod,
+                        const nsTArray<nsCString>& aCustomHeaders);
+
+    nsCString mKey;
+    nsTArray<TokenTime> mMethods;
+    nsTArray<TokenTime> mHeaders;
+  };
+
+  nsAccessControlLRUCache()
+  {
+    MOZ_COUNT_CTOR(nsAccessControlLRUCache);
+    PR_INIT_CLIST(&mList);
+  }
+
+  ~nsAccessControlLRUCache()
+  {
+    Clear();
+    MOZ_COUNT_DTOR(nsAccessControlLRUCache);
+  }
+
+  PRBool Initialize()
+  {
+    return mTable.Init();
+  }
+
+  CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
+                       PRBool aWithCredentials, PRBool aCreate);
+
+  void Clear();
+
+private:
+  PR_STATIC_CALLBACK(PLDHashOperator)
+    RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
+                         void* aUserData);
+
+  static PRBool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
+                            PRBool aWithCredentials, nsACString& _retval);
+
+  nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
+  PRCList mList;
+};
+
 class nsDOMEventListenerWrapper : public nsIDOMEventListener
 {
 public:
   nsDOMEventListenerWrapper(nsIDOMEventListener* aListener)
   : mListener(aListener) {}
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMEventListenerWrapper)
@@ -260,34 +328,59 @@ public:
   // This is called by the factory constructor.
   nsresult Init();
 
   void SetRequestObserver(nsIRequestObserver* aObserver);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXMLHttpRequest,
                                            nsXHREventTarget)
 
+  static PRBool EnsureACCache()
+  {
+    if (sAccessControlCache)
+      return PR_TRUE;
+
+    nsAutoPtr<nsAccessControlLRUCache> newCache(new nsAccessControlLRUCache());
+    NS_ENSURE_TRUE(newCache, PR_FALSE);
+
+    if (newCache->Initialize()) {
+      sAccessControlCache = newCache.forget();
+      return PR_TRUE;
+    }
+
+    return PR_FALSE;
+  }
+
+  static void ShutdownACCache()
+  {
+    delete sAccessControlCache;
+    sAccessControlCache = nsnull;
+  }
+
   PRBool AllowUploadProgress();
 
+  static nsAccessControlLRUCache* sAccessControlCache;
+
 protected:
   friend class nsMultipartProxyListener;
 
-  nsresult SetUploadDataAndType(nsIVariant* aBody);
   nsresult DetectCharset(nsACString& aCharset);
   nsresult ConvertBodyToText(nsAString& aOutBuffer);
   static NS_METHOD StreamReaderFunc(nsIInputStream* in,
                 void* closure,
                 const char* fromRawSegment,
                 PRUint32 toOffset,
                 PRUint32 count,
                 PRUint32 *writeCount);
   // Change the state of the object with this. The broadcast argument
   // determines if the onreadystatechange listener should be called.
   nsresult ChangeState(PRUint32 aState, PRBool aBroadcast = PR_TRUE);
   nsresult RequestCompleted();
+  nsresult GetLoadGroup(nsILoadGroup **aLoadGroup);
+  nsIURI *GetBaseURI();
 
   nsresult RemoveAddEventListener(const nsAString& aType,
                                   nsRefPtr<nsDOMEventListenerWrapper>& aCurrent,
                                   nsIDOMEventListener* aNew);
 
   nsresult GetInnerEventListener(nsRefPtr<nsDOMEventListenerWrapper>& aWrapper,
                                  nsIDOMEventListener** aListener);
 
@@ -302,17 +395,17 @@ protected:
   nsresult CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
 
   nsCOMPtr<nsISupports> mContext;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIChannel> mChannel;
   // mReadRequest is different from mChannel for multipart requests
   nsCOMPtr<nsIRequest> mReadRequest;
   nsCOMPtr<nsIDOMDocument> mResponseXML;
-  nsCOMPtr<nsIChannel> mACPreflightChannel;
+  nsCOMPtr<nsIChannel> mACGetChannel;
   nsTArray<nsCString> mACUnsafeHeaders;
 
   nsRefPtr<nsDOMEventListenerWrapper> mOnUploadProgressListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnReadystatechangeListener;
 
   nsCOMPtr<nsIStreamListener> mXMLParserStreamListener;
 
   // used to implement getAllResponseHeaders()
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -78,17 +78,16 @@
 #include "nsCellMap.h"
 #include "nsTextFrameTextRunCache.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsTextFragment.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsXMLHttpRequest.h"
 #include "nsIFocusEventSuppressor.h"
 #include "nsDOMThreadService.h"
-#include "nsCrossSiteListenerProxy.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
 #include "nsXULElement.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
 
@@ -342,17 +341,17 @@ nsLayoutStatics::Shutdown()
   nsDOMThreadService::Shutdown();
 
   NS_ShutdownFocusSuppressor();
 
 #ifdef MOZ_OGG
   nsAudioStream::ShutdownLibrary();
 #endif
 
-  nsCrossSiteListenerProxy::ShutdownPreflightCache();
+  nsXMLHttpRequest::ShutdownACCache();
 }
 
 void
 nsLayoutStatics::AddRef()
 {
   NS_ASSERTION(sLayoutStaticRefcnt,
                "nsLayoutStatics already dropped to zero!");