Merge last green PGO from inbound to central
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 26 Jan 2012 12:58:39 +0100
changeset 86664 f20f2b7c93cb07a51f739c2242cc4d474a81b9f8
parent 86616 402b394b66238c926edef76a349cdc633120d058 (current diff)
parent 86663 c9b1b20d95931f259528a374410f78a4ee02d7b5 (diff)
child 86667 e758551e3924f8928b624655b968a0029dee9cbd
child 86698 ba57f7b6a2f3ed4202de5a6ce81ebc67dfd48cb2
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.0a1
Merge last green PGO from inbound to central
browser/base/content/test/Makefile.in
mobile/android/base/resources/drawable/favicon.png
--- a/browser/components/migration/src/Makefile.in
+++ b/browser/components/migration/src/Makefile.in
@@ -44,26 +44,25 @@ include $(DEPTH)/config/autoconf.mk
 MODULE		= migration
 LIBRARY_NAME	= migration_s
 FORCE_STATIC_LIB = 1
 ifndef MOZ_MEMORY
 USE_STATIC_LIBS = 1
 endif
 
 
-CPPSRCS  = nsBrowserProfileMigratorUtils.cpp \
-           $(NULL)
-
 ifeq ($(OS_ARCH)_$(GNU_CXX),WINNT_)
 CPPSRCS += nsIEProfileMigrator.cpp \
+           nsBrowserProfileMigratorUtils.cpp \
            $(NULL)
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += nsSafariProfileMigrator.cpp \
+           nsBrowserProfileMigratorUtils.cpp \
            $(NULL)
 endif            
 
 EXTRA_PP_COMPONENTS = \
   ProfileMigrator.js \
   ChromeProfileMigrator.js \
   FirefoxProfileMigrator.js \
   $(NULL)
--- a/build/unix/build-toolchain/build-gcc.py
+++ b/build/unix/build-toolchain/build-gcc.py
@@ -93,17 +93,24 @@ def build_one_stage(env, stage_dir):
 
 def build_tar_package(tar, name, base, directory):
     name = os.path.realpath(name)
     run_in(base, [tar, "-cf", name, "--mtime=2012-01-01", "--owner=root",
                   directory])
 
 ##############################################
 
-source_dir = os.path.realpath('src')
+# The directories end up in the debug info, so the easy way of getting
+# a reproducible build is to run it in a know absolute directory.
+# We use a directory in /builds/slave because the mozilla infrastructure
+# cleans it up automatically.
+base_dir = "/builds/slave/moz-toolschain"
+
+source_dir = base_dir + "/src"
+build_dir  = base_dir + "/build"
 
 def build_source_dir(prefix, version):
     return source_dir + '/' + prefix + version
 
 binutils_version = "2.21.1"
 glibc_version = "2.13" #FIXME: should probably use 2.5.1
 tar_version = "1.26"
 gcc_version = "4.5.2"
@@ -128,44 +135,42 @@ mpc_source_uri = "http://www.multiprecis
 binutils_source_tar = download_uri(binutils_source_uri)
 glibc_source_tar = download_uri(glibc_source_uri)
 tar_source_tar = download_uri(tar_source_uri)
 mpc_source_tar = download_uri(mpc_source_uri)
 mpfr_source_tar = download_uri(mpfr_source_uri)
 gmp_source_tar = download_uri(gmp_source_uri)
 gcc_source_tar = download_uri(gcc_source_uri)
 
-build_dir = os.path.realpath('build')
-
 binutils_source_dir  = build_source_dir('binutils-', binutils_version)
 glibc_source_dir  = build_source_dir('glibc-', glibc_version)
 tar_source_dir  = build_source_dir('tar-', tar_version)
 mpc_source_dir  = build_source_dir('mpc-', mpc_version)
 mpfr_source_dir = build_source_dir('mpfr-', mpfr_version)
 gmp_source_dir  = build_source_dir('gmp-', gmp_version)
 gcc_source_dir  = build_source_dir('gcc-', gcc_version)
 
 if not os.path.exists(source_dir):
-    os.mkdir(source_dir)
+    os.makedirs(source_dir)
     extract(binutils_source_tar, source_dir)
     patch('binutils-deterministic.patch', 1, binutils_source_dir)
     extract(glibc_source_tar, source_dir)
     patch('glibc-deterministic.patch', 1, glibc_source_dir)
     extract(tar_source_tar, source_dir)
     extract(mpc_source_tar, source_dir)
     extract(mpfr_source_tar, source_dir)
     extract(gmp_source_tar, source_dir)
     extract(gcc_source_tar, source_dir)
     patch('plugin_finish_decl.diff', 0, gcc_source_dir)
     patch('pr49911.diff', 1, gcc_source_dir)
     patch('r159628-r163231-r171807.patch', 1, gcc_source_dir)
 
 if os.path.exists(build_dir):
     shutil.rmtree(build_dir)
-os.mkdir(build_dir)
+os.makedirs(build_dir)
 
 tar_inst_dir = build_dir + '/tar_inst'
 build_tar(build_dir, tar_inst_dir)
 
 stage1_dir = build_dir + '/stage1'
 build_one_stage({"CC": "gcc", "CXX" : "g++"}, stage1_dir)
 
 stage1_tool_inst_dir = stage1_dir + '/inst'
--- a/config/system-headers
+++ b/config/system-headers
@@ -263,16 +263,21 @@ frame/req.h
 freetype/freetype.h
 freetype/ftcache.h
 freetype/ftglyph.h
 freetype/ftsynth.h
 freetype/ftoutln.h
 freetype/ttnameid.h
 freetype/tttables.h
 freetype/t1tables.h
+freetype/ftlcdfil.h
+freetype/ftsizes.h
+freetype/ftadvanc.h
+freetype/ftbitmap.h
+freetype/ftxf86.h
 fribidi/fribidi.h
 FSp_fopen.h
 fstream
 fstream.h
 ft2build.h
 fts.h
 gconf/gconf-client.h
 Gdiplus.h
--- a/configure.in
+++ b/configure.in
@@ -8051,17 +8051,17 @@ fi
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 AC_SUBST(GLIB_GMODULE_LIBS)
 
 dnl ========================================================
 dnl Graphics checks.
 dnl ========================================================
 
-if test "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android"; then
+if test "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android" -o "${MOZ_WIDGET_TOOLKIT}" = "gtk2"; then
 MOZ_ENABLE_SKIA=1
 else
 MOZ_ENABLE_SKIA=
 fi
 
 MOZ_ARG_ENABLE_BOOL(skia,
 [  --enable-skia   Enable use of Skia],
 MOZ_ENABLE_SKIA=1,
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -2119,23 +2119,16 @@ public:
 #ifdef IS_BIG_ENDIAN
 #define DOUBLE_NaN {{DOUBLE_HI32_EXPMASK | DOUBLE_HI32_MANTMASK,   \
                         0xffffffff}}
 #else
 #define DOUBLE_NaN {{0xffffffff,                                         \
                         DOUBLE_HI32_EXPMASK | DOUBLE_HI32_MANTMASK}}
 #endif
 
-#if defined(XP_WIN)
-#define DOUBLE_COMPARE(LVAL, OP, RVAL)                                  \
-    (!DOUBLE_IS_NaN(LVAL) && !DOUBLE_IS_NaN(RVAL) && (LVAL) OP (RVAL))
-#else
-#define DOUBLE_COMPARE(LVAL, OP, RVAL) ((LVAL) OP (RVAL))
-#endif
-
 /*
  * In the following helper macros we exploit the fact that the result of a
  * series of additions will not be finite if any one of the operands in the
  * series is not finite.
  */
 #define NS_ENSURE_FINITE(f, rv)                                               \
   if (!NS_finite(f)) {                                                        \
     return (rv);                                                              \
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -55,16 +55,17 @@
 #include "nsContentPolicyUtils.h"
 #include "nsIURI.h"
 #include "nsILoadGroup.h"
 #include "imgIContainer.h"
 #include "imgILoader.h"
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
 #include "nsAsyncDOMEvent.h"
+#include "nsGenericHTMLElement.h"
 
 #include "nsIPresShell.h"
 #include "nsEventStates.h"
 #include "nsGUIEvent.h"
 
 #include "nsIChannel.h"
 #include "nsIStreamListener.h"
 
@@ -769,19 +770,19 @@ nsImageLoadingContent::LoadImage(nsIURI*
   if (!NS_CP_ACCEPTED(cpDecision)) {
     FireEvent(NS_LITERAL_STRING("error"));
     SetBlockedRequest(aNewURI, cpDecision);
     return NS_OK;
   }
 
   nsLoadFlags loadFlags = aLoadFlags;
   PRInt32 corsmode = GetCORSMode();
-  if (corsmode == nsImageLoadingContent::CORS_ANONYMOUS) {
+  if (corsmode == nsGenericHTMLElement::CORS_ANONYMOUS) {
     loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
-  } else if (corsmode == nsImageLoadingContent::CORS_USE_CREDENTIALS) {
+  } else if (corsmode == nsGenericHTMLElement::CORS_USE_CREDENTIALS) {
     loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
   }
 
   // Not blocked. Do the load.
   nsCOMPtr<imgIRequest>& req = PrepareNextRequest();
   nsresult rv;
   rv = nsContentUtils::LoadImage(aNewURI, aDocument,
                                  aDocument->NodePrincipal(),
@@ -1181,13 +1182,13 @@ nsImageLoadingContent::CreateStaticImage
   aDest->mStateChangerDepth = mStateChangerDepth;
   aDest->mIsImageStateForced = mIsImageStateForced;
   aDest->mLoading = mLoading;
   aDest->mBroken = mBroken;
   aDest->mUserDisabled = mUserDisabled;
   aDest->mSuppressed = mSuppressed;
 }
 
-nsImageLoadingContent::CORSMode
+nsGenericHTMLElement::CORSMode
 nsImageLoadingContent::GetCORSMode()
 {
-  return CORS_NONE;
+  return nsGenericHTMLElement::CORS_NONE;
 }
--- a/content/base/src/nsImageLoadingContent.h
+++ b/content/base/src/nsImageLoadingContent.h
@@ -48,16 +48,17 @@
 #include "nsIImageLoadingContent.h"
 #include "nsINode.h"
 #include "imgIRequest.h"
 #include "prtypes.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h" // NS_CONTENT_DELETE_LIST_MEMBER
 #include "nsString.h"
 #include "nsEventStates.h"
+#include "nsGenericHTMLElement.h"
 
 class nsIURI;
 class nsIDocument;
 class imgILoader;
 class nsIIOService;
 
 class nsImageLoadingContent : public nsIImageLoadingContent
 {
@@ -65,35 +66,16 @@ class nsImageLoadingContent : public nsI
 public:
   nsImageLoadingContent();
   virtual ~nsImageLoadingContent();
 
   NS_DECL_IMGICONTAINEROBSERVER
   NS_DECL_IMGIDECODEROBSERVER
   NS_DECL_NSIIMAGELOADINGCONTENT
 
-  enum CORSMode {
-    /**
-     * The default of not using CORS to validate cross-origin loads.
-     */
-    CORS_NONE,
-
-    /**
-     * Validate cross-site loads using CORS, but do not send any credentials
-     * (cookies, HTTP auth logins, etc) along with the request.
-     */
-    CORS_ANONYMOUS,
-
-    /**
-     * Validate cross-site loads using CORS, and send credentials such as cookies
-     * and HTTP auth logins along with the request.
-     */
-    CORS_USE_CREDENTIALS
-  };
-
 protected:
   /**
    * LoadImage is called by subclasses when the appropriate
    * attributes (eg 'src' for <img> tags) change.  The string passed
    * in is the new uri string; this consolidates the code for getting
    * the charset, constructing URI objects, and any other incidentals
    * into this superclass.   
    *
@@ -196,17 +178,17 @@ protected:
   // Sets blocking state only if the desired state is different from the
   // current one. See the comment for mBlockingOnload for more information.
   void SetBlockingOnload(bool aBlocking);
 
   /**
    * Returns the CORS mode that will be used for all future image loads. The
    * default implementation returns CORS_NONE unconditionally.
    */
-  virtual CORSMode GetCORSMode();
+  virtual nsGenericHTMLElement::CORSMode GetCORSMode();
 
 private:
   /**
    * Struct used to manage the image observers.
    */
   struct ImageObserver {
     ImageObserver(imgIDecoderObserver* aObserver) :
       mObserver(aObserver),
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -3945,17 +3945,17 @@ WebGLContext::DOMElementToImageSurface(n
         nsLayoutUtils::SurfaceFromElement(content->AsElement(), flags);
     if (!res.mSurface)
         return NS_ERROR_FAILURE;
     if (res.mSurface->GetType() != gfxASurface::SurfaceTypeImage) {
         // SurfaceFromElement lied!
         return NS_ERROR_FAILURE;
     }
 
-    // We disallow loading cross-domain images that have not been validated
+    // We disallow loading cross-domain images and videos that have not been validated
     // with CORS as WebGL textures. The reason for doing that is that timing
     // attacks on WebGL shaders are able to retrieve approximations of the
     // pixel values in WebGL textures; see bug 655987.
     //
     // To prevent a loophole where a Canvas2D would be used as a proxy to load
     // cross-domain textures, we also disallow loading textures from write-only
     // Canvas2D's.
 
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -990,17 +990,17 @@ NS_NewCanvasRenderingContext2DAzure(nsID
 {
 #ifdef XP_WIN
   if ((gfxWindowsPlatform::GetPlatform()->GetRenderMode() !=
       gfxWindowsPlatform::RENDER_DIRECT2D ||
       !gfxWindowsPlatform::GetPlatform()->DWriteEnabled()) &&
       !Preferences::GetBool("gfx.canvas.azure.prefer-skia", false)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-#elif !defined(XP_MACOSX) && !defined(ANDROID)
+#elif !defined(XP_MACOSX) && !defined(ANDROID) && !defined(XP_LINUX)
   return NS_ERROR_NOT_AVAILABLE;
 #endif
 
   nsRefPtr<nsIDOMCanvasRenderingContext2D> ctx = new nsCanvasRenderingContext2DAzure();
   if (!ctx)
     return NS_ERROR_OUT_OF_MEMORY;
 
   *aResult = ctx.forget().get();
--- a/content/canvas/test/crossorigin/Makefile.in
+++ b/content/canvas/test/crossorigin/Makefile.in
@@ -46,12 +46,14 @@ include $(topsrcdir)/config/rules.mk
 _TEST_FILES = \
 	image-allow-credentials.png \
 	image-allow-credentials.png^headers^ \
 	image-allow-star.png \
 	image-allow-star.png^headers^ \
 	image.png \
 	test_canvas2d_crossorigin.html \
 	test_webgl_crossorigin_textures.html \
+	video.sjs \
+	test_video_crossorigin.html \
 	$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/crossorigin/test_video_crossorigin.html
@@ -0,0 +1,196 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=682299
+-->
+<head>
+  <title>Test for Bug 682299</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/content/media/test/manifest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=682299">Mozilla Bug 682299</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 682299 **/
+
+const MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
+const SECURITY_ERR = 0x805303e8;
+
+function createCanvas(width, height) {
+  var c = document.createElement("canvas");
+  c.width = width;
+  c.height = height;
+  return c;
+}
+
+function testCanvasDrawImage(v) {
+  var c = createCanvas(v.width, v.height);
+  var ctx = c.getContext("2d");
+  ctx.drawImage(v, 0, 0);
+
+  try {
+    var data = ctx.getImageData(0, 0, 1, 1);
+    ok(true, "drawImage '" + v.src + "' then getImageData with crossorigin='" + v.crossorigin + "' worked");
+  } catch(error) {
+    ok(!v.crossorigin && error.result === SECURITY_ERR, "drawImage '" + v.src + "' then getImageData with crossorigin='" + v.crossorigin + "' failed");
+    v.tainted = true;
+  }
+}
+
+function testCanvasCreatePattern(v) {
+  var c = createCanvas(v.width, v.height);
+  var ctx = c.getContext("2d");
+  ctx.fillStyle = ctx.createPattern(v, "");
+  ctx.fillRect(0, 0, c.width, c.height);
+
+  try {
+    var data = ctx.getImageData(0, 0, 1, 1);
+    ok(true, "createPattern '" + v.src + "' then getImageData with crossorigin='" + v.crossorigin + "' worked");
+  } catch(error) {
+    ok(!v.crossorigin && error.result === SECURITY_ERR, "createPattern '" + v.src + "' then getImageData with crossorigin='" + v.crossorigin + "' failed");
+    v.tainted = true;
+  }
+}
+
+function testWebGL(v) {
+  var tex = gl.createTexture();
+  gl.bindTexture(gl.TEXTURE_2D, tex);
+
+  try {
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, v);
+    ok(true, "createTexture from '" + v.src + "' with crossorigin='" + v.crossorigin + "' worked");
+  } catch (error) {
+    ok(!v.crossorigin && error.result === SECURITY_ERR, "createTexture from '" + v.src + "' with crossorigin='" + v.crossorigin + "' failed");
+    v.tainted = true;
+  }
+}
+
+function testTaintedCanvas(v) {
+  var c = createCanvas(v.width, v.height);
+  var ctx = c.getContext("2d");
+  ctx.drawImage(v, 0, 0);
+
+  try {
+    var data = ctx.getImageData(0, 0, 1, 1);
+    ok(false, "changing the CORS mode should not allow reading data from remote videos");
+  } catch (error) {
+    ok(error.result === SECURITY_ERR, "changing the CORS mode, drawImage '" + v.src + "' then getImageData with crossorigin='" + v.crossorigin + "' failed");
+  }
+}
+
+function vidDataSuccess(e) {
+  ok(!e.target.error, "Load '" + e.target.src + "' with crossorigin='" + e.target.crossorigin + "'");
+
+  testCanvasDrawImage(e.target);
+  testCanvasCreatePattern(e.target);
+  if (gl) {
+    testWebGL(e.target);
+  }
+  // If we change the CORS mode after loading the file without CORS it should still throw a security error
+  if (e.target.tainted) {
+    e.target.crossorigin = "anonymous";
+    testTaintedCanvas(e.target);
+  }
+
+  doneTest(e);
+}
+
+function vidLoadFailure(e) {
+  ok(false, "Load '" + e.target.src + "' with crossorigin='" + e.target.crossorigin + "'");
+  doneTest(e);
+}
+
+function vidErrorSuccess(e) {
+  ok(e.target.error.code === MEDIA_ERR_SRC_NOT_SUPPORTED, 
+    "Load '" + e.target.src + "' with crossorigin='" + e.target.crossorigin + "'");
+  doneTest(e);
+}
+
+function startTest(test, token) {
+  var v = document.createElement('video');
+  if (test.cors === "just-crossOrigin-without-value") {
+    var div = document.createElement('div');
+    div.innerHTML="<video crossorigin>";
+    v = div.children[0];
+  } else if (test.cors !== "missing-value-default") {
+    v.crossorigin = test.cors;
+  }
+  v.token = token;
+  manager.started(token);
+  v.autoplay = true;
+  v.preload = "auto";
+  v.style.display = "none";
+  if (test.nameIntent === test.corsIntent || test.corsIntent === "none" ||
+      (test.nameIntent === "use-credentials" && test.corsIntent === "anonymous")) {
+    v.addEventListener("loadeddata", vidDataSuccess, false);
+    v.addEventListener("error", vidLoadFailure, false);
+  } else {
+    v.addEventListener("loadeddata", vidLoadFailure, false);
+    v.addEventListener("error", vidErrorSuccess, false);
+  }
+  v.src = test.name;
+  document.body.appendChild(v);
+}
+
+function doneTest(e) {
+  var v = e.target;
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
+}
+
+var videoFile = getPlayableVideo(gSmallTests);
+if (!videoFile) {
+  SimpleTest.finish();
+}
+videoFile = "?name=tests/content/media/test/" + videoFile.name + "&type=" + videoFile.type;
+
+var gl;
+try {
+  gl = createCanvas(16, 16).getContext("experimental-webgl");
+} catch (ex) {
+  // Mac OS X 10.5 doesn't support WebGL, so we won't run the WebGL tests
+}
+
+var manager = new MediaTestManager;
+var corsTests = [];
+
+const host = "http://example.com/tests/content/canvas/test/crossorigin/video.sjs";
+const serverAttrValues = [
+  [ "&cors=none", "none" ],
+  [ "&cors=anonymous", "anonymous" ],
+  [ "&cors=use-credentials", "use-credentials" ]
+];
+const clientAttrValues = [
+  [ "missing-value-default", "none" ],
+  [ "", "anonymous" ],
+  [ "just-crossOrigin-without-value", "anonymous" ],
+  [ "anonymous", "anonymous" ],
+  [ "use-credentials", "use-credentials" ],
+  [ "foobar", "anonymous" ]
+];
+
+// Build the video file test array
+for (var i = 0; i < serverAttrValues.length; i++) {
+	for (var n = 0; n < clientAttrValues.length; n++) {
+		corsTests.push({
+      name: host + videoFile + serverAttrValues[i][0],
+      nameIntent: serverAttrValues[i][1],
+      cors: clientAttrValues[n][0],
+      corsIntent: clientAttrValues[n][1]
+    });
+	}
+}
+
+manager.runTests(corsTests, startTest);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/crossorigin/video.sjs
@@ -0,0 +1,43 @@
+function parseQuery(request, key) {
+  var params = request.queryString.split('&');
+  for (var j = 0; j < params.length; ++j) {
+    var p = params[j];
+	if (p == key)
+	  return true;
+    if (p.indexOf(key + "=") == 0)
+	  return p.substring(key.length + 1);
+	if (p.indexOf("=") < 0 && key == "")
+	  return p;
+  }
+  return false;
+}
+
+function handleRequest(request, response) {
+  var name = parseQuery(request, "name");
+  var type = parseQuery(request, "type");
+  var cors = parseQuery(request, "cors");
+  var file = Components.classes["@mozilla.org/file/directory_service;1"].
+                        getService(Components.interfaces.nsIProperties).
+                        get("CurWorkD", Components.interfaces.nsILocalFile);
+  var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
+                        createInstance(Components.interfaces.nsIFileInputStream);
+  var bis = Components.classes["@mozilla.org/binaryinputstream;1"].
+                        createInstance(Components.interfaces.nsIBinaryInputStream);
+  var split = name.split("/");
+  for(var i = 0; i < split.length; ++i) {
+    file.append(split[i]);
+  }
+  fis.init(file, -1, -1, false);
+  bis.setInputStream(fis);
+  var bytes = bis.readBytes(bis.available());
+  response.setHeader("Content-Length", ""+bytes.length, false);
+  response.setHeader("Content-Type", type, false);
+  if (cors == "anonymous") {
+    response.setHeader("Access-Control-Allow-Origin", "*", false);
+  } else if (cors == "use-credentials") {
+    response.setHeader("Access-Control-Allow-Origin", "http://mochi.test:8888", false);
+    response.setHeader("Access-Control-Allow-Credentials", "true", false);
+  }
+  response.write(bytes, bytes.length);
+  bis.close();
+}
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -68,16 +68,20 @@ public:
   typedef mozilla::TimeDuration TimeDuration;
 
   enum CanPlayStatus {
     CANPLAY_NO,
     CANPLAY_MAYBE,
     CANPLAY_YES
   };
 
+  CORSMode GetCORSMode() {
+    return mCORSMode;
+  }
+
   nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~nsHTMLMediaElement();
 
   /**
    * This is used when the browser is constructing a video element to play
    * a channel that we've already started loading. The src attribute and
    * <source> children are ignored.
    * @param aChannel the channel to use
@@ -228,18 +232,17 @@ public:
   // Return true if we can activate autoplay assuming enough data has arrived.
   bool CanActivateAutoplay();
 
   // Notify that enough data has arrived to start autoplaying.
   // If the element is 'autoplay' and is ready to play back (not paused,
   // autoplay pref enabled, etc), it should start playing back.
   void NotifyAutoplayDataReady();
 
-  // Gets the pref media.enforce_same_site_origin, which determines
-  // if we should check Access Controls, or allow cross domain loads.
+  // Check if the media element had crossorigin set when loading started
   bool ShouldCheckAllowOrigin();
 
   // Is the media element potentially playing as defined by the HTML 5 specification.
   // http://www.whatwg.org/specs/web-apps/current-work/#potentially-playing
   bool IsPotentiallyPlaying() const;
 
   // Has playback ended as defined by the HTML 5 specification.
   // http://www.whatwg.org/specs/web-apps/current-work/#ended
@@ -756,11 +759,14 @@ protected:
   // True if we've suspended a load in the resource selection algorithm
   // due to loading a preload:none media. When true, the resource we'll
   // load when the user initiates either playback or an explicit load is
   // stored in mPreloadURI.
   bool mLoadIsSuspended;
 
   // True if a same-origin check has been done for the media element and resource.
   bool mMediaSecurityVerified;
+
+  // The CORS mode when loading the media element
+  CORSMode mCORSMode;
 };
 
 #endif
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2879,16 +2879,24 @@ nsGenericHTMLFormElement::PreHandleEvent
         break;
       }
     }
   }
 
   return nsGenericHTMLElement::PreHandleEvent(aVisitor);
 }
 
+const nsAttrValue::EnumTable nsGenericHTMLElement::kCORSAttributeTable[] = {
+  // Order matters here
+  // See ParseAttribute in nsHTMLImageElement or nsHTMLMediaElement
+  { "anonymous",       nsGenericHTMLElement::CORS_ANONYMOUS       },
+  { "use-credentials", nsGenericHTMLElement::CORS_USE_CREDENTIALS },
+  { 0 }
+};
+
 /* virtual */
 bool
 nsGenericHTMLFormElement::IsDisabled() const
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled) ||
          (mFieldSet && mFieldSet->IsDisabled());
 }
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -537,16 +537,42 @@ public:
     return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
   }
 
   bool IsHidden() const
   {
     return HasAttr(kNameSpaceID_None, nsGkAtoms::hidden);
   }
 
+  /**
+   * Shared cross-origin resource sharing attributes so they don't get
+   * duplicated on every CORS-enabled element
+   */
+
+  enum CORSMode {
+    /**
+     * The default of not using CORS to validate cross-origin loads.
+     */
+    CORS_NONE,
+
+    /**
+     * Validate cross-site loads using CORS, but do not send any credentials
+     * (cookies, HTTP auth logins, etc) along with the request.
+     */
+    CORS_ANONYMOUS,
+
+    /**
+     * Validate cross-site loads using CORS, and send credentials such as cookies
+     * and HTTP auth logins along with the request.
+     */
+    CORS_USE_CREDENTIALS
+  };
+
+  const static nsAttrValue::EnumTable kCORSAttributeTable[];
+
 protected:
   /**
    * Add/remove this element to the documents name cache
    */
   void AddToNameTable(nsIAtom* aName) {
     NS_ASSERTION(HasName(), "Node doesn't have name?");
     nsIDocument* doc = GetCurrentDoc();
     if (doc && !IsInAnonymousSubtree()) {
--- a/content/html/content/src/nsHTMLImageElement.cpp
+++ b/content/html/content/src/nsHTMLImageElement.cpp
@@ -121,17 +121,17 @@ public:
   NS_SCRIPTABLE NS_IMETHOD SetInnerHTML(const nsAString& aInnerHTML) {
     return nsGenericHTMLElement::SetInnerHTML(aInnerHTML);
   }
 
   // nsIDOMHTMLImageElement
   NS_DECL_NSIDOMHTMLIMAGEELEMENT
 
   // override from nsImageLoadingContent
-  nsImageLoadingContent::CORSMode GetCORSMode();
+  CORSMode GetCORSMode();
 
   // nsIJSNativeInitializer
   NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
                         JSObject* aObj, PRUint32 argc, jsval* argv);
 
   // nsIContent
   virtual bool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
@@ -240,23 +240,16 @@ NS_IMPL_STRING_ATTR(nsHTMLImageElement, 
 NS_IMPL_INT_ATTR(nsHTMLImageElement, Hspace, hspace)
 NS_IMPL_BOOL_ATTR(nsHTMLImageElement, IsMap, ismap)
 NS_IMPL_URI_ATTR(nsHTMLImageElement, LongDesc, longdesc)
 NS_IMPL_STRING_ATTR(nsHTMLImageElement, Lowsrc, lowsrc)
 NS_IMPL_URI_ATTR(nsHTMLImageElement, Src, src)
 NS_IMPL_STRING_ATTR(nsHTMLImageElement, UseMap, usemap)
 NS_IMPL_INT_ATTR(nsHTMLImageElement, Vspace, vspace)
 
-static const nsAttrValue::EnumTable kCrossOriginTable[] = {
-  // Order matters here; see ParseAttribute
-  { "anonymous",       nsImageLoadingContent::CORS_ANONYMOUS },
-  { "use-credentials", nsImageLoadingContent::CORS_USE_CREDENTIALS },
-  { 0 }
-};
-
 // crossorigin is not "limited to only known values" per spec, so it's
 // just a string attr purposes of the DOM crossOrigin property.
 NS_IMPL_STRING_ATTR(nsHTMLImageElement, CrossOrigin, crossorigin)
 
 NS_IMETHODIMP
 nsHTMLImageElement::GetDraggable(bool* aDraggable)
 {
   // images may be dragged unless the draggable attribute is false
@@ -365,20 +358,20 @@ nsHTMLImageElement::ParseAttribute(PRInt
                                    const nsAString& aValue,
                                    nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::align) {
       return ParseAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::crossorigin) {
-      return aResult.ParseEnumValue(aValue, kCrossOriginTable, false,
+      return aResult.ParseEnumValue(aValue, nsGenericHTMLElement::kCORSAttributeTable, false,
                                     // default value is anonymous if aValue is
                                     // not a value we understand
-                                    &kCrossOriginTable[0]);
+                                    &nsGenericHTMLElement::kCORSAttributeTable[0]);
     }
     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
       return true;
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
@@ -664,22 +657,22 @@ nsresult
 nsHTMLImageElement::CopyInnerTo(nsGenericElement* aDest) const
 {
   if (aDest->OwnerDoc()->IsStaticDocument()) {
     CreateStaticImageClone(static_cast<nsHTMLImageElement*>(aDest));
   }
   return nsGenericHTMLElement::CopyInnerTo(aDest);
 }
 
-nsImageLoadingContent::CORSMode
+nsGenericHTMLElement::CORSMode
 nsHTMLImageElement::GetCORSMode()
 {
-  nsImageLoadingContent::CORSMode ret = nsImageLoadingContent::CORS_NONE;
+  nsGenericHTMLElement::CORSMode ret = nsGenericHTMLElement::CORS_NONE;
 
   const nsAttrValue* value = GetParsedAttr(nsGkAtoms::crossorigin);
   if (value) {
     NS_ASSERTION(value->Type() == nsAttrValue::eEnum,
                  "Why is this not an enum value?");
-    ret = nsImageLoadingContent::CORSMode(value->GetEnumValue());
+    ret = nsGenericHTMLElement::CORSMode(value->GetEnumValue());
   }
 
   return ret;
 }
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -935,16 +935,26 @@ nsresult nsHTMLMediaElement::LoadResourc
     mAudioStream = nsnull;
   }
 
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
     mChannel = nsnull;
   }
 
+  // Set the media element's CORS mode only when loading a resource
+  // By default, it's CORS_NONE
+  mCORSMode = CORS_NONE;
+  const nsAttrValue* value = GetParsedAttr(nsGkAtoms::crossorigin);
+  if (value) {
+    NS_ASSERTION(value->Type() == nsAttrValue::eEnum,
+                 "Why is this not an enum value?");
+    mCORSMode = CORSMode(value->GetEnumValue());
+  }
+
   nsHTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
   if (other) {
     // Clone it.
     nsresult rv = InitializeDecoderAsClone(other->mDecoder);
     if (NS_SUCCEEDED(rv))
       return rv;
   }
 
@@ -997,17 +1007,17 @@ nsresult nsHTMLMediaElement::LoadResourc
   channel->SetNotificationCallbacks(loadListener);
 
   nsCOMPtr<nsIStreamListener> listener;
   if (ShouldCheckAllowOrigin()) {
     listener =
       new nsCORSListenerProxy(loadListener,
                               NodePrincipal(),
                               channel,
-                              false,
+                              GetCORSMode() == CORS_USE_CREDENTIALS,
                               &rv);
   } else {
     rv = nsContentUtils::GetSecurityManager()->
            CheckLoadURIWithPrincipal(NodePrincipal(),
                                      mLoadingSrc,
                                      nsIScriptSecurityManager::STANDARD);
     listener = loadListener;
   }
@@ -1387,21 +1397,20 @@ nsHTMLMediaElement::LookupMediaElementUR
   if (!gElementTable)
     return nsnull;
   MediaElementSetForURI* entry = gElementTable->GetEntry(aURI);
   if (!entry)
     return nsnull;
   for (PRUint32 i = 0; i < entry->mElements.Length(); ++i) {
     nsHTMLMediaElement* elem = entry->mElements[i];
     bool equal;
-    // Look for elements that have the same principal.
-    // XXX when we implement crossorigin for video, we'll also need to check
-    // for the same crossorigin mode here. Ditto for anything else that could
-    // cause us to send different headers.
-    if (NS_SUCCEEDED(elem->NodePrincipal()->Equals(NodePrincipal(), &equal)) && equal) {
+    // Look for elements that have the same principal and CORS mode.
+    // Ditto for anything else that could cause us to send different headers.
+    if (NS_SUCCEEDED(elem->NodePrincipal()->Equals(NodePrincipal(), &equal)) && equal &&
+        elem->mCORSMode == mCORSMode) {
       NS_ASSERTION(elem->mDecoder && elem->mDecoder->GetStream(), "Decoder gone");
       return elem;
     }
   }
   return nsnull;
 }
 
 nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
@@ -1433,17 +1442,18 @@ nsHTMLMediaElement::nsHTMLMediaElement(a
     mDelayingLoadEvent(false),
     mIsRunningSelectResource(false),
     mSuspendedAfterFirstFrame(false),
     mAllowSuspendAfterFirstFrame(true),
     mHasPlayedOrSeeked(false),
     mHasSelfReference(false),
     mShuttingDown(false),
     mLoadIsSuspended(false),
-    mMediaSecurityVerified(false)
+    mMediaSecurityVerified(false),
+    mCORSMode(CORS_NONE)
 {
 #ifdef PR_LOGGING
   if (!gMediaElementLog) {
     gMediaElementLog = PR_NewLogModule("nsMediaElement");
   }
   if (!gMediaElementEventsLog) {
     gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents");
   }
@@ -1554,16 +1564,18 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   // and our preload status.
   AddRemoveSelfReference();
   UpdatePreloadAction();
 
   return NS_OK;
 }
 
+NS_IMPL_STRING_ATTR(nsHTMLMediaElement, Crossorigin, crossorigin)
+
 bool nsHTMLMediaElement::ParseAttribute(PRInt32 aNamespaceID,
                                           nsIAtom* aAttribute,
                                           const nsAString& aValue,
                                           nsAttrValue& aResult)
 {
   // Mappings from 'preload' attribute strings to an enumeration.
   static const nsAttrValue::EnumTable kPreloadTable[] = {
     { "",         nsHTMLMediaElement::PRELOAD_ATTR_EMPTY },
@@ -1572,16 +1584,22 @@ bool nsHTMLMediaElement::ParseAttribute(
     { "auto",     nsHTMLMediaElement::PRELOAD_ATTR_AUTO },
     { 0 }
   };
 
   if (aNamespaceID == kNameSpaceID_None) {
     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
       return true;
     }
+    if (aAttribute == nsGkAtoms::crossorigin) {
+      return aResult.ParseEnumValue(aValue, kCORSAttributeTable, false,
+                                    // default value is anonymous if aValue is
+                                    // not a value we understand
+                                    &kCORSAttributeTable[0]);
+    }
     if (aAttribute == nsGkAtoms::preload) {
       return aResult.ParseEnumValue(aValue, kPreloadTable, false);
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
@@ -2096,18 +2114,18 @@ nsresult nsHTMLMediaElement::FinishDecod
     }
   }
 
   if (OwnerDoc()->HasAudioAvailableListeners()) {
     NotifyAudioAvailableListener();
   }
 
   if (NS_FAILED(rv)) {
-    RemoveMediaElementFromURITable();
-    mDecoder->Shutdown();
+    RemoveMediaElementFromURITable();
+    mDecoder->Shutdown();
     mDecoder = nsnull;
   }
 
   NS_ASSERTION(NS_SUCCEEDED(rv) == (MediaElementTableCount(this, mLoadingSrc) == 1),
     "Media element should have single table entry if decode initialized");
 
   mBegun = true;
   return rv;
@@ -2325,17 +2343,17 @@ void nsHTMLMediaElement::DownloadStalled
 {
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
     DispatchAsyncEvent(NS_LITERAL_STRING("stalled"));
   }
 }
 
 bool nsHTMLMediaElement::ShouldCheckAllowOrigin()
 {
-  return Preferences::GetBool("media.enforce_same_site_origin", true);
+  return mCORSMode != CORS_NONE;
 }
 
 void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
 {
   if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
     // aNextFrame might have a next frame because the decoder can advance
     // on its own thread before ResourceLoaded or MetadataLoaded gets
     // a chance to run.
--- a/content/media/test/file_access_controls.html
+++ b/content/media/test/file_access_controls.html
@@ -71,51 +71,42 @@ var gTests = [
     result: "error",
     description: "Won't load from subdomain",
   }
 ];
 
 var gTestNum = 0;
 var gVideo = null;
 var gTestedRemoved = false;
-var gOldPref;
 
 function eventHandler(event) {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   //dump((gTestNum - 1) + ": " + event.type + "\n");
   var video = event.target;
   opener.is(event.type, video.expectedResult, video.testDescription +
     (gTestedRemoved ? " (element not in document)" : " (element in document)"));
   // Make sure any extra events cause an error
   video.expectedResult = "<none>";
   nextTest();
 }
 
 function createVideo() {
   var v = document.createElement('video');
   v.addEventListener('loadeddata', eventHandler, false);
   v.addEventListener('error', eventHandler, false);
+  v.crossorigin = 'anonymous';
   return v;
 }
 
 function load() {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   opener.is(window.location.href,
             "http://example.org/tests/content/media/test/file_access_controls.html",
             "We must be on a example.org:80");
- 
-  // Ensure access control check pref is on.
-  // media.enforce_same_site_origin
-  var prefService = Components.classes["@mozilla.org/preferences-service;1"]
-                               .getService(Components.interfaces.nsIPrefService);
-  opener.ok(prefService!=null, "Get pref service");
-  var branch = prefService.getBranch("media.");
-  opener.ok(branch!=null, "Get media pref branch");
-  gOldPref = branch.getBoolPref("enforce_same_site_origin");
-  branch.setBoolPref("enforce_same_site_origin", true);
+
   nextTest();
 }
 
 function nextTest() {
   //dump("nextTest() called, gTestNum="+gTestNum+" gTestedRemoved="+gTestedRemoved+"\n");
   if (gTestNum == gTests.length) {
     //dump("gTestNum == gTests.length\n");
     if (!gTestedRemoved) {
@@ -154,21 +145,16 @@ function nextTest() {
   } else {
     gVideo.load();
   }
   gTestNum++;
 }
 
 function done() {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-  // Undo change to access control check pref.
-  var prefService = Components.classes["@mozilla.org/preferences-service;1"]
-                               .getService(Components.interfaces.nsIPrefService);
-  var branch = prefService.getBranch("media.");
-  branch.setBoolPref("enforce_same_site_origin", gOldPref);
   mediaTestCleanup();
   opener.done();
 }
 
 </script>
 </body>
 </html>
 
--- a/content/xslt/src/xpath/txRelationalExpr.cpp
+++ b/content/xslt/src/xpath/txRelationalExpr.cpp
@@ -114,17 +114,17 @@ RelationalExpr::compareResults(txIEvalCo
             result = aLeft->booleanValue() == aRight->booleanValue();
         }
 
         // If either is a number, compare as numbers.
         else if (ltype == txAExprResult::NUMBER ||
                  rtype == txAExprResult::NUMBER) {
             double lval = aLeft->numberValue();
             double rval = aRight->numberValue();
-            result = DOUBLE_COMPARE(lval, ==, rval);
+            result = (lval == rval);
         }
 
         // Otherwise compare as strings. Try to use the stringobject in
         // StringResult if possible since that is a common case.
         else if ((lString = aLeft->stringValuePointer())) {
             if ((rString = aRight->stringValuePointer())) {
                 result = lString->Equals(*rString);
             }
@@ -149,29 +149,29 @@ RelationalExpr::compareResults(txIEvalCo
         return mOp == EQUAL ? result : !result;
     }
 
     double leftDbl = aLeft->numberValue();
     double rightDbl = aRight->numberValue();
     switch (mOp) {
         case LESS_THAN:
         {
-            return DOUBLE_COMPARE(leftDbl, <, rightDbl);
+            return (leftDbl < rightDbl);
         }
         case LESS_OR_EQUAL:
         {
-            return DOUBLE_COMPARE(leftDbl, <=, rightDbl);
+            return (leftDbl <= rightDbl);
         }
         case GREATER_THAN:
         {
-            return DOUBLE_COMPARE(leftDbl, >, rightDbl);
+            return (leftDbl > rightDbl);
         }
         case GREATER_OR_EQUAL:
         {
-            return DOUBLE_COMPARE(leftDbl, >=, rightDbl);
+            return (leftDbl >= rightDbl);
         }
         default:
         {
             NS_NOTREACHED("We should have caught all cases");
         }
     }
 
     return false;
--- a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
@@ -47,17 +47,17 @@
  * <audio> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#audio
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[scriptable, uuid(c74b835f-bb68-4ab3-a02c-08152cbb09fa)]
+[scriptable, uuid(390c059a-0a26-4a44-96b6-3f8817bf92e9)]
 interface nsIDOMHTMLAudioElement : nsIDOMHTMLMediaElement
 {
   // Setup the audio stream for writing
   void mozSetup(in PRUint32 channels, in PRUint32 rate);
 
   // Write audio to the audio stream
   [implicit_jscontext]
   unsigned long mozWriteAudio(in jsval data);
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -52,25 +52,26 @@
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 %{C++
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 %}
 
-[scriptable, uuid(1f312b70-c1e1-40ca-94d4-5f70cac773da)]  
+[scriptable, uuid(6733a409-fab3-45e1-af23-9af8c361bdfd)]
 interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
 {
   // error state
   readonly attribute nsIDOMMediaError error;
 
   // network state
            attribute DOMString src;
   readonly attribute DOMString currentSrc;
+           attribute DOMString crossorigin;
   const unsigned short NETWORK_EMPTY = 0;
   const unsigned short NETWORK_IDLE = 1;
   const unsigned short NETWORK_LOADING = 2;
   const unsigned short NETWORK_NO_SOURCE = 3;
   readonly attribute unsigned short networkState;
            attribute DOMString preload;  
   readonly attribute nsIDOMTimeRanges buffered;
   void load();
--- a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
@@ -43,17 +43,17 @@
  * <video> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#video
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[scriptable, uuid(8e6d81a9-a6e1-44af-95be-cbe86de36ede)]
+[scriptable, uuid(2274055b-8b3a-4a5a-8d72-5d5aea07021a)]
 interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
 {
            attribute long width; 
            attribute long height;
   readonly attribute unsigned long videoWidth;
   readonly attribute unsigned long videoHeight;
            attribute DOMString poster;
            
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -209,16 +209,17 @@ ContainerRender(Container* aContainer,
         aContainer->mSupportsComponentAlphaChildren = true;
       }
     }
 
     aContainer->gl()->PushViewportRect();
     framebufferRect -= childOffset; 
     aManager->CreateFBOWithTexture(framebufferRect,
                                    mode,
+                                   aPreviousFrameBuffer,
                                    &frameBuffer,
                                    &containerSurface);
     childOffset.x = visibleRect.x;
     childOffset.y = visibleRect.y;
   } else {
     frameBuffer = aPreviousFrameBuffer;
     aContainer->mSupportsComponentAlphaChildren = (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE) ||
       (aContainer->GetParent() && aContainer->GetParent()->SupportsComponentAlphaChildren());
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -1127,32 +1127,85 @@ void
 LayerManagerOGL::SetLayerProgramProjectionMatrix(const gfx3DMatrix& aMatrix)
 {
   FOR_EACH_LAYER_PROGRAM(lp) {
     lp->Activate();
     lp->SetProjectionMatrix(aMatrix);
   } FOR_EACH_LAYER_PROGRAM_END
 }
 
+static GLenum
+GetFrameBufferInternalFormat(GLContext* gl,
+                             GLuint aCurrentFrameBuffer,
+                             nsIWidget* aWidget)
+{
+  if (aCurrentFrameBuffer == 0) { // default framebuffer
+    return aWidget->GetGLFrameBufferFormat();
+  }
+  return LOCAL_GL_RGBA;
+}
+
+static bool
+AreFormatsCompatibleForCopyTexImage2D(GLenum aF1, GLenum aF2)
+{
+  // GL requires that the implementation has to handle copies between
+  // different formats, so all are "compatible".  GLES does not
+  // require that.
+#ifdef USE_GLES2
+  return (aF1 == aF2);
+#else
+  return true;
+#endif
+}
+
 void
 LayerManagerOGL::CreateFBOWithTexture(const nsIntRect& aRect, InitMode aInit,
+                                      GLuint aCurrentFrameBuffer,
                                       GLuint *aFBO, GLuint *aTexture)
 {
   GLuint tex, fbo;
 
   mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
   mGLContext->fGenTextures(1, &tex);
   mGLContext->fBindTexture(mFBOTextureTarget, tex);
   if (aInit == InitModeCopy) {
-    mGLContext->fCopyTexImage2D(mFBOTextureTarget,
-                                0,
-                                LOCAL_GL_RGBA,
-                                aRect.x, aRect.y,
-                                aRect.width, aRect.height,
-                                0);
+    // We're going to create an RGBA temporary fbo.  But to
+    // CopyTexImage() from the current framebuffer, the framebuffer's
+    // format has to be compatible with the new texture's.  So we
+    // check the format of the framebuffer here and take a slow path
+    // if it's incompatible.
+    GLenum format =
+      GetFrameBufferInternalFormat(gl(), aCurrentFrameBuffer, mWidget);
+    if (AreFormatsCompatibleForCopyTexImage2D(format, LOCAL_GL_RGBA)) {
+      mGLContext->fCopyTexImage2D(mFBOTextureTarget,
+                                  0,
+                                  LOCAL_GL_RGBA,
+                                  aRect.x, aRect.y,
+                                  aRect.width, aRect.height,
+                                  0);
+    } else {
+      // Curses, incompatible formats.  Take a slow path.
+      //
+      // XXX Technically CopyTexSubImage2D also has the requirement of
+      // matching formats, but it doesn't seem to affect us in the
+      // real world.
+      mGLContext->fTexImage2D(mFBOTextureTarget,
+                              0,
+                              LOCAL_GL_RGBA,
+                              aRect.width, aRect.height,
+                              0,
+                              LOCAL_GL_RGBA,
+                              LOCAL_GL_UNSIGNED_BYTE,
+                              NULL);
+      mGLContext->fCopyTexSubImage2D(mFBOTextureTarget,
+                                     0,    // level
+                                     0, 0, // offset
+                                     aRect.x, aRect.y,
+                                     aRect.width, aRect.height);
+    }
   } else {
     mGLContext->fTexImage2D(mFBOTextureTarget,
                             0,
                             LOCAL_GL_RGBA,
                             aRect.width, aRect.height,
                             0,
                             LOCAL_GL_RGBA,
                             LOCAL_GL_UNSIGNED_BYTE,
--- a/gfx/layers/opengl/LayerManagerOGL.h
+++ b/gfx/layers/opengl/LayerManagerOGL.h
@@ -303,16 +303,17 @@ public:
 
   /* Create a FBO backed by a texture; will leave the FBO
    * bound.  Note that the texture target type will be
    * of the type returned by FBOTextureTarget; different
    * shaders are required to sample from the different
    * texture types.
    */
   void CreateFBOWithTexture(const nsIntRect& aRect, InitMode aInit,
+                            GLuint aCurrentFrameBuffer,
                             GLuint *aFBO, GLuint *aTexture);
 
   GLuint QuadVBO() { return mQuadVBO; }
   GLintptr QuadVBOVertexOffset() { return 0; }
   GLintptr QuadVBOTexCoordOffset() { return sizeof(float)*4*2; }
   GLintptr QuadVBOFlippedTexCoordOffset() { return sizeof(float)*8*2; }
 
   void BindQuadVBO() {
--- a/gfx/skia/Makefile.in
+++ b/gfx/skia/Makefile.in
@@ -63,16 +63,17 @@ LOCAL_INCLUDES += \
 	-I$(srcdir)/include/effects \
 	$(NULL)
 
 VPATH += \
 	$(srcdir)/src/core \
 	$(srcdir)/src/ports \
 	$(srcdir)/src/opts \
 	$(srcdir)/src/effects \
+	$(srcdir)/src/utils \
 	$(NULL)
 
 EXPORTS_skia = \
 	include/core/Sk64.h \
 	include/core/SkAutoKern.h \
 	include/core/SkBitmap.h \
 	include/core/SkBlitRow.h \
 	include/core/SkBlitter.h \
@@ -312,16 +313,29 @@ CPPSRCS += \
 	SkMMapStream.cpp \
 	SkTime_Unix.cpp \
 	$(NULL)
 
 DEFINES += -DSK_BUILD_FOR_ANDROID_NDK
 OS_CXXFLAGS += $(CAIRO_FT_CFLAGS)
 endif
 
+ifeq (gtk2,$(MOZ_WIDGET_TOOLKIT))
+CPPSRCS += \
+	SkFontHost_FreeType.cpp \
+	SkFontHost_linux.cpp \
+	SkFontHost_gamma.cpp \
+	SkTime_Unix.cpp \
+	SkMMapStream.cpp \
+	SkOSFile.cpp \
+	$(NULL)
+
+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/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -51,16 +51,18 @@
 #include "gfxContext.h"
 #include "gfxUserFontSet.h"
 #else
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include "gfxFT2Fonts.h"
 #endif
 
+#include "mozilla/gfx/2D.h"
+
 #include "cairo.h"
 #include <gtk/gtk.h>
 
 #include "gfxImageSurface.h"
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 #include "gfxXlibSurface.h"
 #include "cairo-xlib.h"
@@ -78,16 +80,19 @@
 
 #define GDK_PIXMAP_SIZE_MAX 32767
 
 #ifndef MOZ_PANGO
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #endif
 
+using namespace mozilla;
+using namespace mozilla::gfx;
+
 gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nsnull;
 
 #ifndef MOZ_PANGO
 typedef nsDataHashtable<nsStringHashKey, nsRefPtr<FontFamily> > FontTable;
 typedef nsDataHashtable<nsCStringHashKey, nsTArray<nsRefPtr<gfxFontEntry> > > PrefFontTable;
 static FontTable *gPlatformFonts = NULL;
 static FontTable *gPlatformFontAliases = NULL;
 static PrefFontTable *gPrefFonts = NULL;
@@ -756,8 +761,28 @@ gfxPlatformGtk::GetGdkDrawable(gfxASurfa
     if (result) {
         SetGdkDrawable(target, result);
         return result;
     }
 #endif
 
     return NULL;
 }
+
+RefPtr<ScaledFont>
+gfxPlatformGtk::GetScaledFontForFont(gfxFont *aFont)
+{
+  NativeFont nativeFont;
+  nativeFont.mType = NATIVE_FONT_SKIA_FONT_FACE;
+  nativeFont.mFont = aFont;
+  RefPtr<ScaledFont> scaledFont =
+    Factory::CreateScaledFontForNativeFont(nativeFont, aFont->GetAdjustedSize());
+
+  return scaledFont;
+}
+
+bool
+gfxPlatformGtk::SupportsAzure(BackendType& aBackend)
+{
+  aBackend = BACKEND_SKIA;
+  return true;
+}
+
--- a/gfx/thebes/gfxPlatformGtk.h
+++ b/gfx/thebes/gfxPlatformGtk.h
@@ -61,16 +61,21 @@ public:
 
     static gfxPlatformGtk *GetPlatform() {
         return (gfxPlatformGtk*) gfxPlatform::GetPlatform();
     }
 
     already_AddRefed<gfxASurface> CreateOffscreenSurface(const gfxIntSize& size,
                                                          gfxASurface::gfxContentType contentType);
 
+    mozilla::RefPtr<mozilla::gfx::ScaledFont>
+      GetScaledFontForFont(gfxFont *aFont);
+
+    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/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -479,20 +479,18 @@ struct ParamTraits<mozilla::GraphicsFilt
     case gfxPattern::FILTER_GOOD:
     case gfxPattern::FILTER_BEST:
     case gfxPattern::FILTER_NEAREST:
     case gfxPattern::FILTER_BILINEAR:
     case gfxPattern::FILTER_GAUSSIAN:
       WriteParam(msg, int32(param));
       return;
 
-    default:
-      NS_RUNTIMEABORT("not reached");
-      return;
     }
+    NS_RUNTIMEABORT("not reached");
   }
 
   static bool Read(const Message* msg, void** iter, paramType* result)
   {
     int32 filter;
     if (!ReadParam(msg, iter, &filter))
       return false;
 
--- a/js/src/assembler/assembler/AbstractMacroAssembler.h
+++ b/js/src/assembler/assembler/AbstractMacroAssembler.h
@@ -28,17 +28,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef AbstractMacroAssembler_h
 #define AbstractMacroAssembler_h
 
 #include "assembler/wtf/Platform.h"
 #include "assembler/assembler/MacroAssemblerCodeRef.h"
 #include "assembler/assembler/CodeLocation.h"
-#include "jsstdint.h"
 
 #if ENABLE_ASSEMBLER
 
 namespace JSC {
 
 class LinkBuffer;
 class RepatchBuffer;
 
--- a/js/src/assembler/assembler/AssemblerBuffer.h
+++ b/js/src/assembler/assembler/AssemblerBuffer.h
@@ -32,17 +32,16 @@
 
 #include "assembler/wtf/Platform.h"
 
 #if ENABLE_ASSEMBLER
 
 #include <string.h>
 #include "assembler/jit/ExecutableAllocator.h"
 #include "assembler/wtf/Assertions.h"
-#include "jsstdint.h"
 
 namespace JSC {
 
     class AssemblerBuffer {
         static const int inlineCapacity = 256;
     public:
         AssemblerBuffer()
             : m_buffer(m_inlineBuffer)
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -30,17 +30,16 @@
 #ifndef X86Assembler_h
 #define X86Assembler_h
 
 #include "assembler/wtf/Platform.h"
 
 #if ENABLE_ASSEMBLER && (WTF_CPU_X86 || WTF_CPU_X86_64)
 
 #include "AssemblerBuffer.h"
-#include "jsstdint.h"
 #include "assembler/wtf/Assertions.h"
 #include "js/Vector.h"
 
 #include "methodjit/Logging.h"
 #define IPFX  "        %s"
 #define ISPFX "        "
 #ifdef JS_METHODJIT_SPEW
 # define MAYBE_PAD (isOOLPath ? ">  " : "")
--- a/js/src/config/system-headers
+++ b/js/src/config/system-headers
@@ -263,16 +263,21 @@ frame/req.h
 freetype/freetype.h
 freetype/ftcache.h
 freetype/ftglyph.h
 freetype/ftsynth.h
 freetype/ftoutln.h
 freetype/ttnameid.h
 freetype/tttables.h
 freetype/t1tables.h
+freetype/ftlcdfil.h
+freetype/ftsizes.h
+freetype/ftadvanc.h
+freetype/ftbitmap.h
+freetype/ftxf86.h
 fribidi/fribidi.h
 FSp_fopen.h
 fstream
 fstream.h
 ft2build.h
 fts.h
 gconf/gconf-client.h
 Gdiplus.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -43,17 +43,16 @@
  */
 #ifdef HAVE_MEMORY_H
 #include <memory.h>
 #endif
 #include <new>
 #include <string.h>
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsfun.h"
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -52,17 +52,16 @@
  * This parser attempts no error recovery.
  */
 
 #include "frontend/Parser.h"
 
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsfun.h"
 #include "jsgc.h"
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -48,17 +48,16 @@
 #include <math.h>
 #ifdef HAVE_MEMORY_H
 #include <memory.h>
 #endif
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsexn.h"
 #include "jsnum.h"
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/regress-bug720680.js
@@ -0,0 +1,15 @@
+// |jit-test| error: InternalError
+version(0);
+eval("\
+function TimeFromYear( y ) {}\
+addTestCase( -2208988800000 );\
+function addTestCase( t ) {\
+  var start = TimeFromYear((addTestCase(addTestCase << t, 0)));\
+    new TestCase( \
+                  SECTION,\
+                  '(new Date('+d+')).getUTCDay()',\
+                  WeekDay((d)),\
+                  (new Date(let ({ stop } = 'properties.length' )('/ab[c\\\n]/'))).getUTCDay() \
+                );\
+}\
+");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testTypedArraySetConversion.js
@@ -0,0 +1,15 @@
+var n = 16;
+var a = new Int32Array(n);
+for (var i = 0; i < n; ++i) {
+    a[i] = i;
+}
+var b = new Int32Array(n);
+for (var i = 0; i < n; ++i) {
+    b[i] = i * 2;
+}
+
+a.set(b, 0.99);
+
+for (var i = 0; i < n; ++i) {
+    assertEq(a[i], b[i]);
+}
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -42,17 +42,16 @@
  * JavaScript API.
  */
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -98,17 +98,16 @@
  */
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/RangedPtr.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsversion.h"
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -42,17 +42,16 @@
  */
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/RangedPtr.h"
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsgcmark.h"
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -36,17 +36,16 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * JS boolean implementation.
  */
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsinfer.h"
 #include "jsversion.h"
 #include "jslock.h"
--- a/js/src/jsclone.h
+++ b/js/src/jsclone.h
@@ -36,17 +36,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jsclone_h___
 #define jsclone_h___
 
 #include "jsapi.h"
 #include "jscntxt.h"
-#include "jsstdint.h"
 
 #include "js/HashTable.h"
 #include "js/Vector.h"
 
 namespace js {
 
 bool
 WriteStructuredClone(JSContext *cx, const Value &v, uint64_t **bufp, size_t *nbytesp,
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -49,18 +49,16 @@
 #include <stdlib.h>
 #include <string.h>
 #ifdef ANDROID
 # include <android/log.h>
 # include <fstream>
 # include <string>
 #endif  // ANDROID
 
-#include "jsstdint.h"
-
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jsprf.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -55,17 +55,16 @@
 #include <locale.h>
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsprf.h"
 #include "prmjtime.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsversion.h"
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsinterp.h"
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -41,17 +41,16 @@
 
 /*
  * JS debugging API.
  */
 #include <string.h>
 #include <stdarg.h>
 #include "jsprvtd.h"
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsfun.h"
 #include "jsgc.h"
--- a/js/src/jsdhash.cpp
+++ b/js/src/jsdhash.cpp
@@ -41,17 +41,16 @@
 /*
  * Double hashing implementation.
  *
  * Try to keep this file in sync with xpcom/glue/pldhash.cpp.
  */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "jsstdint.h"
 #include "jsdhash.h"
 #include "jsutil.h"
 
 using namespace js;
 
 #ifdef JS_DHASHMETER
 # if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan
 #  include "nsTraceMalloc.h"
--- a/js/src/jsdtoa.cpp
+++ b/js/src/jsdtoa.cpp
@@ -36,17 +36,16 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * Portable double to alphanumeric string and back converters.
  */
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsdtoa.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsprvtd.h"
 #include "jsnum.h"
 #include "jslibmath.h"
 #include "jscntxt.h"
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -42,17 +42,16 @@
  * JS standard exception implementation.
  */
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -41,17 +41,16 @@
 /*
  * JS function support.
  */
 #include <string.h>
 
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsfun.h"
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -50,17 +50,16 @@
  * mark bit, finalizer type index, etc.
  *
  * XXX swizzle page to freelist for better locality of reference
  */
 #include <math.h>
 #include <string.h>     /* for memset used when DEBUG */
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsclist.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscompartment.h"
 #include "jscrashreport.h"
--- a/js/src/jsgcchunk.cpp
+++ b/js/src/jsgcchunk.cpp
@@ -28,17 +28,16 @@
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <stdlib.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsgcchunk.h"
 
 #ifdef XP_WIN
 # include "jswin.h"
 
 # ifdef _MSC_VER
 #  pragma warning( disable: 4267 4996 4146 )
 # endif
--- a/js/src/jshash.cpp
+++ b/js/src/jshash.cpp
@@ -38,17 +38,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * PR hash table package.
  */
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jshash.h"
 
 using namespace js;
 
 /* Compute the number of buckets in ht */
 #define NBUCKETS(ht)    JS_BIT(JS_HASH_BITS - (ht)->shift)
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -40,17 +40,16 @@
 
 /*
  * JavaScript bytecode interpreter.
  */
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsdate.h"
@@ -819,17 +818,17 @@ js::LooselyEqual(JSContext *cx, const Va
         if (lval.isString()) {
             JSString *l = lval.toString();
             JSString *r = rval.toString();
             return EqualStrings(cx, l, r, result);
         }
 
         if (lval.isDouble()) {
             double l = lval.toDouble(), r = rval.toDouble();
-            *result = JSDOUBLE_COMPARE(l, ==, r, false);
+            *result = (l == r);
             return true;
         }
 
         if (lval.isObject()) {
             JSObject *l = &lval.toObject();
             JSObject *r = &rval.toObject();
 
             if (JSEqualityOp eq = l->getClass()->ext.equality) {
@@ -870,29 +869,29 @@ js::LooselyEqual(JSContext *cx, const Va
         JSString *l = lvalue.toString();
         JSString *r = rvalue.toString();
         return EqualStrings(cx, l, r, result);
     }
 
     double l, r;
     if (!ToNumber(cx, lvalue, &l) || !ToNumber(cx, rvalue, &r))
         return false;
-    *result = JSDOUBLE_COMPARE(l, ==, r, false);
+    *result = (l == r);
     return true;
 }
 
 bool
 js::StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, bool *equal)
 {
     Value lval = lref, rval = rref;
     if (SameType(lval, rval)) {
         if (lval.isString())
             return EqualStrings(cx, lval.toString(), rval.toString(), equal);
         if (lval.isDouble()) {
-            *equal = JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE);
+            *equal = (lval.toDouble() == rval.toDouble());
             return true;
         }
         if (lval.isObject()) {
             *equal = lval.toObject() == rval.toObject();
             return true;
         }
         if (lval.isUndefined()) {
             *equal = true;
@@ -900,23 +899,23 @@ js::StrictlyEqual(JSContext *cx, const V
         }
         *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
         return true;
     }
 
     if (lval.isDouble() && rval.isInt32()) {
         double ld = lval.toDouble();
         double rd = rval.toInt32();
-        *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
+        *equal = (ld == rd);
         return true;
     }
     if (lval.isInt32() && rval.isDouble()) {
         double ld = lval.toInt32();
         double rd = rval.toDouble();
-        *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
+        *equal = (ld == rd);
         return true;
     }
 
     *equal = false;
     return true;
 }
 
 static inline bool
@@ -2320,17 +2319,17 @@ END_CASE(JSOP_CASE)
                 int32_t result;                                               \
                 if (!CompareStrings(cx, l, r, &result))                       \
                     goto error;                                               \
                 cond = result OP 0;                                           \
             } else {                                                          \
                 double l, r;                                                  \
                 if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))       \
                     goto error;                                               \
-                cond = JSDOUBLE_COMPARE(l, OP, r, false);                     \
+                cond = (l OP r);                                              \
             }                                                                 \
         }                                                                     \
         TRY_BRANCH_AFTER_COND(cond, 2);                                       \
         regs.sp[-2].setBoolean(cond);                                         \
         regs.sp--;                                                            \
     JS_END_MACRO
 
 BEGIN_CASE(JSOP_LT)
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -39,17 +39,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * JavaScript iterators.
  */
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsexn.h"
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -37,17 +37,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * JS math package.
  */
 #include <stdlib.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "prmjtime.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jslock.h"
 #include "jsmath.h"
 #include "jsnum.h"
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -50,17 +50,16 @@
 #include <limits.h>
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/RangedPtr.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdtoa.h"
 #include "jsgc.h"
 #include "jsinterp.h"
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -37,17 +37,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jsnum_h___
 #define jsnum_h___
 
 #include <math.h>
 
-#include "jsstdint.h"
 #include "jsobj.h"
 
 /*
  * JS number (IEEE double) interface.
  *
  * JS numbers are optimistically stored in the top 31 bits of 32-bit integers,
  * but floating point literals, results that overflow 31 bits, and division and
  * modulus operands and results require a 64-bit IEEE double.  These are GC'ed
@@ -124,25 +123,16 @@ JSDOUBLE_IS_NEG(jsdouble d)
 static inline uint32_t
 JS_HASH_DOUBLE(jsdouble d)
 {
     jsdpun u;
     u.d = d;
     return u.s.lo ^ u.s.hi;
 }
 
-#if defined(XP_WIN)
-#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN)                               \
-    ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL))                         \
-     ? (IFNAN)                                                                \
-     : (LVAL) OP (RVAL))
-#else
-#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL))
-#endif
-
 extern jsdouble js_NaN;
 extern jsdouble js_PositiveInfinity;
 extern jsdouble js_NegativeInfinity;
 
 namespace js {
 
 extern bool
 InitRuntimeNumberState(JSRuntime *rt);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -42,17 +42,16 @@
  * JS object implementation.
  */
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
@@ -67,17 +66,16 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsonparser.h"
 #include "jsopcode.h"
 #include "jsprobes.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 #include "jsscript.h"
-#include "jsstdint.h"
 #include "jsstr.h"
 #include "jsdbgapi.h"
 #include "json.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "builtin/MapObject.h"
 #include "frontend/BytecodeCompiler.h"
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -50,17 +50,16 @@
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "json.h"
 #include "jsonparser.h"
 #include "jsprf.h"
 #include "jsstr.h"
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsxml.h"
 
 #include "frontend/TokenStream.h"
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jsinferinlines.h"
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -47,17 +47,16 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsfun.h"
@@ -851,18 +850,17 @@ Sprinter::put(const char *s, size_t len)
 
     /* s is within the buffer already */
     if (s >= oldBase && s < oldEnd) {
         /* buffer was realloc'ed */
         if (base != oldBase)
             s = stringAt(s - oldBase);  /* this is where it lives now */
         memmove(bp, s, len);
     } else {
-        JS_ASSERT(s < base || s >= base + size);
-        memcpy(bp, s, len);
+        js_memcpy(bp, s, len);
     }
 
     bp[len] = 0;
     return oldOffset;
 }
 
 ptrdiff_t
 Sprinter::putString(JSString *s)
@@ -2055,17 +2053,17 @@ DecompileDestructuringLHS(SprintStack *s
         /*
          * We may need to auto-parenthesize the left-most value decompiled
          * here, so add back PAREN_SLOP temporarily.  Then decompile until the
          * opcode that would reduce the stack depth to (ss->top-1), which we
          * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
          * the nb parameter.
          */
         ptrdiff_t todo = ss->sprinter.getOffset();
-        ss->sprinter.setOffset(todo + PAREN_SLOP);
+        ss->sprinter.reserve(PAREN_SLOP);
         pc = Decompile(ss, pc, -((intN)ss->top));
         if (!pc)
             return NULL;
         if (pc == endpc)
             return pc;
         LOAD_OP_DATA(pc);
         LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
         xval = PopStr(ss, JSOP_NOP);
--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -41,17 +41,16 @@
 **
 ** Author: Kipp E.B. Hickman
 */
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include "jsprf.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jspubtd.h"
 #include "jsstr.h"
 
 using namespace js;
 
 /*
 ** Note: on some platforms va_list is defined as an array,
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -40,17 +40,16 @@
 
 /*
  * JS symbol tables.
  */
 #include <new>
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsclist.h"
 #include "jsdhash.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jslock.h"
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -37,19 +37,19 @@
  * 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 ***** */
 
 /*
  * JS script operations.
  */
+
 #include <string.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jscrashreport.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -49,17 +49,16 @@
  * allocations in the same native method.
  */
 
 #include "mozilla/Attributes.h"
 
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -37,17 +37,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <string.h>
 
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
@@ -1539,26 +1538,26 @@ class TypedArrayTemplate
             return obj;
         }
 
         /* (obj, byteOffset, length). */
         int32_t byteOffset = -1;
         int32_t length = -1;
 
         if (argc > 1) {
-            if (!NonstandardToInt32(cx, argv[1], &byteOffset))
+            if (!ToInt32(cx, argv[1], &byteOffset))
                 return NULL;
             if (byteOffset < 0) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1");
                 return NULL;
             }
 
             if (argc > 2) {
-                if (!NonstandardToInt32(cx, argv[2], &length))
+                if (!ToInt32(cx, argv[2], &length))
                     return NULL;
                 if (length < 0) {
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                          JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
                     return NULL;
                 }
             }
         }
@@ -1620,17 +1619,17 @@ class TypedArrayTemplate
         JSObject *tarray = getTypedArray(obj);
         if (!tarray)
             return true;
 
         // these are the default values
         int32_t off = 0;
 
         if (args.length() > 1) {
-            if (!NonstandardToInt32(cx, args[1], &off))
+            if (!ToInt32(cx, args[1], &off))
                 return false;
 
             if (off < 0 || uint32_t(off) > getLength(tarray)) {
                 // the given offset is bogus
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_BAD_ARGS);
                 return false;
             }
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -40,17 +40,16 @@
 
 /* Various JS utility functions. */
 
 #include "mozilla/Attributes.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 
 #ifdef WIN32
 #    include "jswin.h"
 #else
 #    include <signal.h>
 #endif
 
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -40,17 +40,16 @@
 #include "mozilla/Util.h"
 
 #include "jsversion.h"
 
 #if JS_HAS_XDR
 
 #include <string.h>
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsnum.h"
 #include "jsobj.h"              /* js_XDRObject */
 #include "jsscript.h"           /* js_XDRScript */
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -43,17 +43,16 @@
 
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsprf.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsfun.h"
@@ -5310,17 +5309,17 @@ js_TestXMLEquality(JSContext *cx, const 
                     ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), &equal);
                     if (ok)
                         *bp = equal;
                 } else {
                     ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d);
                     if (ok) {
                         d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v)
                                              : JSVAL_TO_DOUBLE(v);
-                        *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE);
+                        *bp = (d == d2);
                     }
                 }
             } else {
                 *bp = JS_FALSE;
             }
             js_LeaveLocalRootScope(cx);
         }
     }
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -520,17 +520,17 @@ template void JS_FASTCALL stubs::DefFun<
             int32_t cmp;                                                      \
             if (!CompareStrings(cx, l, r, &cmp))                              \
                 THROWV(JS_FALSE);                                             \
             cond = cmp OP 0;                                                  \
         } else {                                                              \
             double l, r;                                                      \
             if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))           \
                 THROWV(JS_FALSE);                                             \
-            cond = JSDOUBLE_COMPARE(l, OP, r, false);                         \
+            cond = (l OP r);                                                  \
         }                                                                     \
         regs.sp[-2].setBoolean(cond);                                         \
         return cond;                                                          \
     JS_END_MACRO
 
 JSBool JS_FASTCALL
 stubs::LessThan(VMFrame &f)
 {
@@ -563,17 +563,17 @@ stubs::ValueToBoolean(VMFrame &f)
 
 void JS_FASTCALL
 stubs::Not(VMFrame &f)
 {
     JSBool b = !js_ValueToBoolean(f.regs.sp[-1]);
     f.regs.sp[-1].setBoolean(b);
 }
 
-template <bool EQ, bool IFNAN>
+template <bool EQ>
 static inline bool
 StubEqualityOp(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
 
     Value rval = regs.sp[-1];
     Value lval = regs.sp[-2];
@@ -601,19 +601,19 @@ StubEqualityOp(VMFrame &f)
 #endif
 
     if (SameType(lval, rval)) {
         JS_ASSERT(!lval.isString());    /* this case is handled above */
         if (lval.isDouble()) {
             double l = lval.toDouble();
             double r = rval.toDouble();
             if (EQ)
-                cond = JSDOUBLE_COMPARE(l, ==, r, IFNAN);
+                cond = (l == r);
             else
-                cond = JSDOUBLE_COMPARE(l, !=, r, IFNAN);
+                cond = (l != r);
         } else if (lval.isObject()) {
             JSObject *l = &lval.toObject(), *r = &rval.toObject();
             if (JSEqualityOp eq = l->getClass()->ext.equality) {
                 JSBool equal;
                 if (!eq(cx, l, &rval, &equal))
                     return false;
                 cond = !!equal == EQ;
             } else {
@@ -647,39 +647,39 @@ StubEqualityOp(VMFrame &f)
                     return false;
                 cond = equal == EQ;
             } else {
                 double l, r;
                 if (!ToNumber(cx, lval, &l) || !ToNumber(cx, rval, &r))
                     return false;
 
                 if (EQ)
-                    cond = JSDOUBLE_COMPARE(l, ==, r, false);
+                    cond = (l == r);
                 else
-                    cond = JSDOUBLE_COMPARE(l, !=, r, true);
+                    cond = (l != r);
             }
         }
     }
 
     regs.sp[-2].setBoolean(cond);
     return true;
 }
 
 JSBool JS_FASTCALL
 stubs::Equal(VMFrame &f)
 {
-    if (!StubEqualityOp<true, false>(f))
+    if (!StubEqualityOp<true>(f))
         THROWV(JS_FALSE);
     return f.regs.sp[-2].toBoolean();
 }
 
 JSBool JS_FASTCALL
 stubs::NotEqual(VMFrame &f)
 {
-    if (!StubEqualityOp<false, true>(f))
+    if (!StubEqualityOp<false>(f))
         THROWV(JS_FALSE);
     return f.regs.sp[-2].toBoolean();
 }
 
 void JS_FASTCALL
 stubs::Add(VMFrame &f)
 {
     JSContext *cx = f.cx;
--- a/js/src/prmjtime.cpp
+++ b/js/src/prmjtime.cpp
@@ -41,17 +41,16 @@
  * PR time code.
  */
 #ifdef SOLARIS
 #define _REENTRANT 1
 #endif
 #include <string.h>
 #include <time.h>
 
-#include "jsstdint.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "jsprf.h"
 #include "jslock.h"
 #include "prmjtime.h"
 
 #define PRMJ_DO_MILLISECONDS 1
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -47,17 +47,16 @@
 #include <stdlib.h>
 #include <string.h>
 #include <signal.h>
 #include <locale.h>
 
 #include "mozilla/Util.h"
 
 #include "jstypes.h"
-#include "jsstdint.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsdate.h"
--- a/js/src/shell/jsworkers.cpp
+++ b/js/src/shell/jsworkers.cpp
@@ -44,17 +44,16 @@
 
 #include <string.h>
 #include "prthread.h"
 #include "prlock.h"
 #include "prcvar.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsdbgapi.h"
-#include "jsstdint.h"
 #include "jslock.h"
 #include "jsworkers.h"
 
 extern size_t gMaxStackSize;
 
 class AutoLock
 {
   private:
--- a/js/src/yarr/PageBlock.h
+++ b/js/src/yarr/PageBlock.h
@@ -25,18 +25,19 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef PageBlock_h
 #define PageBlock_h
 
+#include "mozilla/StdInt.h"
+
 #include <stdlib.h>
-#include "jsstdint.h"
 #include "assembler/wtf/Platform.h"
 
 namespace WTF {
 
 size_t pageSize();
 inline bool isPageAligned(void* address) { return !(reinterpret_cast<intptr_t>(address) & (pageSize() - 1)); }
 inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); }
 inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -85,16 +85,17 @@
 #include "nsTArray.h"
 #include "nsHTMLCanvasElement.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "gfxPlatform.h"
 #include "nsClientRect.h"
 #ifdef MOZ_MEDIA
 #include "nsHTMLVideoElement.h"
 #endif
+#include "nsGenericHTMLElement.h"
 #include "imgIRequest.h"
 #include "imgIContainer.h"
 #include "nsIImageLoadingContent.h"
 #include "nsCOMPtr.h"
 #include "nsListControlFrame.h"
 #include "ImageLayers.h"
 #include "mozilla/arm.h"
 #include "mozilla/dom/Element.h"
@@ -4146,16 +4147,17 @@ nsLayoutUtils::SurfaceFromElement(dom::E
         new gfxImageSurface(size, gfxASurface::ImageFormatARGB32);
 
       nsRefPtr<gfxContext> ctx = new gfxContext(imgSurf);
       ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
       ctx->DrawSurface(surf, size);
       surf = imgSurf;
     }
 
+    result.mCORSUsed = video->GetCORSMode() != nsGenericHTMLElement::CORS_NONE;
     result.mSurface = surf;
     result.mSize = size;
     result.mPrincipal = principal.forget();
     result.mIsWriteOnly = false;
 
     return result;
   }
 #endif
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -6577,20 +6577,20 @@ nsRuleNode::ComputeColumnData(void* aSta
   }
   else if (SetColor(colorValue, 0, mPresContext, aContext,
                     column->mColumnRuleColor, canStoreInRuleTree)) {
     column->mColumnRuleColorIsForeground = false;
   }
 
   // column-fill: enum
   SetDiscrete(*aRuleData->ValueForColumnFill(),
-                column->mColumnFill, canStoreInRuleTree,
-                SETDSC_ENUMERATED, parent->mColumnFill,
-                NS_STYLE_COLUMN_FILL_BALANCE,
-                0, 0, 0, 0);
+              column->mColumnFill, canStoreInRuleTree,
+              SETDSC_ENUMERATED, parent->mColumnFill,
+              NS_STYLE_COLUMN_FILL_BALANCE,
+              0, 0, 0, 0);
 
   COMPUTE_END_RESET(Column, column)
 }
 
 static void
 SetSVGPaint(const nsCSSValue& aValue, const nsStyleSVGPaint& parentPaint,
             nsPresContext* aPresContext, nsStyleContext *aContext,
             nsStyleSVGPaint& aResult, nsStyleSVGPaintType aInitialPaintType,
--- a/layout/svg/base/src/Makefile.in
+++ b/layout/svg/base/src/Makefile.in
@@ -101,16 +101,17 @@ include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	= \
 		-I$(srcdir)/../../../base \
 		-I$(srcdir)/../../../generic \
 		-I$(srcdir)/../../../style \
 		-I$(srcdir)/../../../xul/base/src \
 		-I$(srcdir)/../../../../content/svg/content/src \
 		-I$(srcdir)/../../../../content/base/src \
+		-I$(srcdir)/../../../../content/html/content/src \
 		$(NULL)
 
 libs::
 	$(INSTALL) $(srcdir)/svg.css $(DIST)/bin/res
 
 install::
 	$(SYSINSTALL) $(IFLAGS1) $(srcdir)/svg.css $(DESTDIR)$(mozappdir)/res
 
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -56,16 +56,20 @@
                   android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation"
                   android:windowSoftInputMode="stateUnspecified|adjustResize"
                   android:launchMode="singleTask"
                   android:theme="@style/Gecko">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="org.mozilla.gecko.UPDATE"/>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
 
             <!-- Default browser intents -->
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <data android:scheme="http" />
                 <data android:scheme="https" />
@@ -107,16 +111,17 @@
         </receiver>
 
         <activity android:name="Restarter"
                   android:process="@ANDROID_PACKAGE_NAME@Restarter"
                   android:theme="@style/Gecko"
                   android:excludeFromRecents="true">
           <intent-filter>
             <action android:name="org.mozilla.gecko.restart"/>
+            <action android:name="org.mozilla.gecko.restart_update"/>
           </intent-filter>
         </activity>
 
 #include ../sync/manifests/SyncAndroidManifest_activities.xml.in
 
 #if MOZ_CRASHREPORTER
   <activity android:name="CrashReporter"
             android:label="@string/crash_reporter_title"
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -100,16 +100,17 @@ abstract public class GeckoApp
     }
 
     public static final String ACTION_ALERT_CLICK   = "org.mozilla.gecko.ACTION_ALERT_CLICK";
     public static final String ACTION_ALERT_CLEAR   = "org.mozilla.gecko.ACTION_ALERT_CLEAR";
     public static final String ACTION_WEBAPP        = "org.mozilla.gecko.WEBAPP";
     public static final String ACTION_DEBUG         = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_BOOKMARK      = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_LOAD          = "org.mozilla.gecko.LOAD";
+    public static final String ACTION_UPDATE        = "org.mozilla.gecko.UPDATE";
     public static final String SAVED_STATE_URI      = "uri";
     public static final String SAVED_STATE_TITLE    = "title";
     public static final String SAVED_STATE_VIEWPORT = "viewport";
     public static final String SAVED_STATE_SCREEN   = "screen";
     public static final String SAVED_STATE_SESSION  = "session";
 
     StartupMode mStartupMode = null;
     private LinearLayout mMainLayout;
@@ -588,21 +589,20 @@ abstract public class GeckoApp
 
                 mLastUri = lastHistoryEntry.mUri;
                 mLastTitle = lastHistoryEntry.mTitle;
                 getAndProcessThumbnailForTab(tab);
             }
         }
     }
 
-    void getAndProcessThumbnailForTab(Tab tab) {
-        Bitmap bitmap = null;
-        if (Tabs.getInstance().isSelectedTab(tab))
-            bitmap = mSoftwareLayerClient.getBitmap();
-
+    void getAndProcessThumbnailForTab(final Tab tab) {
+        final Bitmap bitmap = Tabs.getInstance().isSelectedTab(tab) ?
+            mSoftwareLayerClient.getBitmap() : null;
+        
         if (bitmap != null) {
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
             processThumbnail(tab, bitmap, bos.toByteArray());
         } else {
             mLastScreen = null;
             GeckoAppShell.sendEventToGecko(
                 new GeckoEvent("Tab:Screenshot", 
@@ -1047,16 +1047,18 @@ abstract public class GeckoApp
                 GeckoPreferences.setCharEncodingState(visible);
                 if (sMenu != null) {
                     mMainHandler.post(new Runnable() {
                         public void run() {
                             sMenu.findItem(R.id.char_encoding).setVisible(visible);
                         }
                     });
                 }
+            } else if (event.equals("Update:Restart")) {
+                doRestart("org.mozilla.gecko.restart_update");
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
     public void showAboutHome() {
         Runnable r = new AboutHomeRunnable(true);
@@ -1453,16 +1455,21 @@ abstract public class GeckoApp
                 mProfileDir = new File(m.group(1));
                 mLastUri = null;
                 mLastTitle = null;
                 mLastViewport = null;
                 mLastScreen = null;
             }
         }
 
+        if (ACTION_UPDATE.equals(intent.getAction()) || args != null && args.contains("-alert update-app")) {
+            Log.i(LOGTAG,"onCreate: Update request");
+            checkAndLaunchUpdate();
+        }
+
         String uri = intent.getDataString();
         String title = uri;
         if (uri != null && uri.length() > 0) {
             mLastUri = uri;
             mLastTitle = title;
         }
 
         if (mLastUri == null || mLastUri.equals("") ||
@@ -1601,16 +1608,17 @@ abstract public class GeckoApp
         GeckoAppShell.registerGeckoEventListener("Toast:Show", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("Permissions:Data", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("Downloads:Done", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("CharEncoding:State", GeckoApp.mAppContext);
+        GeckoAppShell.registerGeckoEventListener("Update:Restart", GeckoApp.mAppContext);
 
         mConnectivityFilter = new IntentFilter();
         mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         mConnectivityReceiver = new GeckoConnectivityReceiver();
 
         IntentFilter batteryFilter = new IntentFilter();
         batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         mBatteryReceiver = new GeckoBatteryManager();
@@ -1636,20 +1644,16 @@ abstract public class GeckoApp
                 if (localeCode != null && localeCode.length() > 0)
                     GeckoAppShell.setSelectedLocale(localeCode);
                 */
 
                 if (!checkLaunchState(LaunchState.Launched)) {
                     return;
                 }
 
-                // it would be good only to do this if MOZ_UPDATER was defined
-                long startTime = SystemClock.uptimeMillis();
-                checkAndLaunchUpdate();
-                Log.w(LOGTAG, "checking for an update took " + (SystemClock.uptimeMillis() - startTime) + "ms");
                 checkMigrateProfile();
             }
         }, 50);
 
         mOrientation = getResources().getConfiguration().orientation;
     }
 
     /**
@@ -2007,18 +2011,22 @@ abstract public class GeckoApp
             Map.Entry<String,String> entry = envIter.next();
             intent.putExtra("env" + c, entry.getKey() + "="
                             + entry.getValue());
             c++;
         }
     }
 
     public void doRestart() {
+        doRestart("org.mozilla.gecko.restart");
+    }
+
+    public void doRestart(String action) {
+        Log.i(LOGTAG, "doRestart(\"" + action + "\")");
         try {
-            String action = "org.mozilla.gecko.restart";
             Intent intent = new Intent(action);
             intent.setClassName(getPackageName(),
                                 getPackageName() + ".Restarter");
             /* TODO: addEnvToIntent(intent); */
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                             Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
             Log.i(LOGTAG, intent.toString());
             GeckoAppShell.killAnyZombies();
@@ -2103,22 +2111,29 @@ abstract public class GeckoApp
             Log.i(LOGTAG, "error reading update status", e);
         }
         return status;
     }
 
     private void checkMigrateProfile() {
         File profileDir = getProfileDir();
         if (profileDir != null) {
+            long currentTime = SystemClock.uptimeMillis();
             Log.i(LOGTAG, "checking profile migration in: " + profileDir.getAbsolutePath());
             final GeckoApp app = GeckoApp.mAppContext;
+            final SetupScreen setupScreen = new SetupScreen(app);
+            // don't show unless we take a while
+            setupScreen.showDelayed(mMainHandler);
             GeckoAppShell.ensureSQLiteLibsLoaded(app.getApplication().getPackageResourcePath());
             ProfileMigrator profileMigrator =
                 new ProfileMigrator(app.getContentResolver(), profileDir);
-            profileMigrator.launchBackground();
+            profileMigrator.launch();
+            setupScreen.dismiss();
+            long timeDiff = SystemClock.uptimeMillis() - currentTime;
+            Log.i(LOGTAG, "Profile migration took " + timeDiff + " ms");
         }
     }
 
     private SynchronousQueue<String> mFilePickerResult = new SynchronousQueue<String>();
     public String showFilePicker(String aMimeType) {
         Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
         intent.addCategory(Intent.CATEGORY_OPENABLE);
         intent.setType(aMimeType);
@@ -2358,17 +2373,17 @@ abstract public class GeckoApp
      */
     public void loadUrlInTab(String url) {
         ArrayList<Tab> tabs = Tabs.getInstance().getTabsInOrder();
         if (tabs != null) {
             Iterator<Tab> tabsIter = tabs.iterator();
             while (tabsIter.hasNext()) {
                 Tab tab = tabsIter.next();
                 if (url.equals(tab.getURL())) {
-                    GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", String.valueOf(tab.getId())));
+                    Tabs.getInstance().selectTab(tab.getId());
                     return;
                 }
             }
         }
 
         JSONObject args = new JSONObject();
         try {
             args.put("url", url);
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -430,28 +430,35 @@ public class GeckoInputConnection
         if (imm != null && imm.isFullscreenMode()) {
             View v = GeckoApp.mAppContext.getLayerController().getView();
             imm.updateSelection(v, start, end, -1, -1);
         }
     }
 
     public void reset() {
         mComposing = false;
+        mCompositionStart = -1;
         mBatchMode = false;
         mUpdateRequest = null;
     }
 
     // TextWatcher
     public void onTextChanged(CharSequence s, int start, int before, int count)
     {
+        if (mComposing && mCompositionStart != start) {
+            // Changed range is different from the composition, need to reset the composition
+            endComposition();
+        }
+
         if (!mComposing) {
             if (DEBUG) Log.d(LOGTAG, ". . . onTextChanged: IME_COMPOSITION_BEGIN");
             GeckoAppShell.sendEventToGecko(
                 new GeckoEvent(GeckoEvent.IME_COMPOSITION_BEGIN, 0, 0));
             mComposing = true;
+            mCompositionStart = start;
 
             if (DEBUG) Log.d(LOGTAG, ". . . onTextChanged: IME_SET_SELECTION, start=" + start + ", len=" + before);
             GeckoAppShell.sendEventToGecko(
                 new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start, before));
         }
 
         if (count == 0) {
             if (DEBUG) Log.d(LOGTAG, ". . . onTextChanged: IME_DELETE_TEXT");
@@ -469,16 +476,17 @@ public class GeckoInputConnection
         GeckoAppShell.geckoEventSync();
     }
 
     private void endComposition() {
         if (DEBUG) Log.d(LOGTAG, "IME: endComposition: IME_COMPOSITION_END");
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_COMPOSITION_END, 0, 0));
         mComposing = false;
+        mCompositionStart = -1;
     }
 
     private void sendTextToGecko(CharSequence text, int caretPos) {
         if (DEBUG) Log.d(LOGTAG, "IME: sendTextToGecko(\"" + text + "\")");
 
         // Handle composition text styles
         if (text != null && text instanceof Spanned) {
             Spanned span = (Spanned) text;
@@ -883,17 +891,18 @@ public class GeckoInputConnection
     public void initEditable(String contents)
     {
         mEditable = mEditableFactory.newEditable(contents);
         mEditable.setSpan(this, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
         Selection.setSelection(mEditable, contents.length());
     }
 
     // Is a composition active?
-    boolean mComposing;
+    private boolean mComposing;
+    private int mCompositionStart = -1;
 
     // IME stuff
     public static final int IME_STATE_DISABLED = 0;
     public static final int IME_STATE_ENABLED = 1;
     public static final int IME_STATE_PASSWORD = 2;
     public static final int IME_STATE_PLUGIN = 3;
 
     final CharacterStyle COMPOSING_SPAN = new UnderlineSpan();
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -63,16 +63,17 @@ SYNC_PP_RES_XML=res/xml/sync_syncadapter
 FENNEC_JAVA_FILES = \
   AboutHomeContent.java \
   AlertNotification.java \
   AutoCompletePopup.java \
   AwesomeBar.java \
   AwesomeBarTabs.java \
   BrowserToolbar.java \
   ConfirmPreference.java \
+  SyncPreference.java \
   db/AndroidBrowserDB.java \
   db/BrowserDB.java \
   db/LocalBrowserDB.java \
   DoorHanger.java \
   DoorHangerPopup.java \
   Favicons.java \
   FloatUtils.java \
   GeckoActionBar.java \
@@ -90,16 +91,17 @@ FENNEC_JAVA_FILES = \
   GeckoThread.java \
   GlobalHistory.java \
   LinkPreference.java \
   ProfileMigrator.java \
   PromptService.java \
   sqlite/ByteBufferInputStream.java \
   sqlite/SQLiteBridge.java \
   sqlite/SQLiteBridgeException.java \
+  SetupScreen.java \
   SurfaceLockInfo.java \
   Tab.java \
   Tabs.java \
   TabsTray.java \
   gfx/BitmapUtils.java \
   gfx/BufferedCairoImage.java \
   gfx/CairoGLInfo.java \
   gfx/CairoImage.java \
@@ -226,16 +228,17 @@ RES_LAYOUT = \
   res/layout/gecko_app.xml \
   res/layout/gecko_menu.xml \
   res/layout/launch_app_list.xml \
   res/layout/launch_app_listitem.xml \
   res/layout/notification_icon_text.xml \
   res/layout/notification_progress.xml \
   res/layout/notification_progress_text.xml \
   res/layout/site_setting_title.xml \
+  res/layout/setup_screen.xml \
   res/layout/tabs_row.xml \
   res/layout/tabs_tray.xml \
   res/layout/list_item_header.xml \
   res/layout/select_dialog_list.xml \
   res/layout/abouthome_content.xml \
   res/layout/abouthome_topsite_item.xml \
   res/layout/abouthome_addon_row.xml \
   res/layout/abouthome_last_tabs_row.xml \
@@ -272,16 +275,17 @@ RES_ANIM = \
 RES_DRAWABLE_NODPI = \
   res/drawable-nodpi/abouthome_bg.png \
   res/drawable-nodpi/abouthome_topsites_bg.png \
   res/drawable-nodpi/tabs_tray_bg.png \
   res/drawable-nodpi/tabs_tray_pressed_bg.png \
   $(NULL)
 
 RES_DRAWABLE_MDPI_V8 = \
+  res/drawable-mdpi-v8/favicon.png \
   res/drawable-mdpi-v8/abouthome_icon.png \
   res/drawable-mdpi-v8/abouthome_logo.png \
   res/drawable-mdpi-v8/abouthome_separator.9.png \
   res/drawable-mdpi-v8/abouthome_sync_logo.png \
   res/drawable-mdpi-v8/abouthome_sync_bg.9.png \
   res/drawable-mdpi-v8/abouthome_sync_pressed_bg.9.png \
   res/drawable-mdpi-v8/abouthome_topsite_placeholder.png \
   res/drawable-mdpi-v8/abouthome_topsite_shadow.9.png \
@@ -314,16 +318,17 @@ RES_DRAWABLE_MDPI_V8 = \
   res/drawable-mdpi-v8/doorhanger_shadow_bg.9.png \
   res/drawable-mdpi-v8/doorhanger_popup_bg.9.png \
   res/drawable-mdpi-v8/site_security_identified.png \
   res/drawable-mdpi-v8/site_security_verified.png \
   res/drawable-mdpi-v8/urlbar_stop.png \
   $(NULL)
 
 RES_DRAWABLE_HDPI_V8 = \
+  res/drawable-hdpi-v8/favicon.png \
   res/drawable-hdpi-v8/home_bg.png \
   res/drawable-hdpi-v8/home_star.png \
   res/drawable-hdpi-v8/abouthome_icon.png \
   res/drawable-hdpi-v8/abouthome_logo.png \
   res/drawable-hdpi-v8/abouthome_separator.9.png \
   res/drawable-hdpi-v8/abouthome_sync_logo.png \
   res/drawable-hdpi-v8/abouthome_sync_bg.9.png \
   res/drawable-hdpi-v8/abouthome_sync_pressed_bg.9.png \
@@ -378,16 +383,17 @@ RES_DRAWABLE_HDPI_V11 = \
   res/drawable-hdpi-v11/ic_menu_find_in_page.png \
   res/drawable-hdpi-v11/ic_menu_reload.png \
   res/drawable-hdpi-v11/ic_menu_save_as_pdf.png \
   res/drawable-hdpi-v11/ic_menu_share.png \
   res/drawable-hdpi-v11/ic_menu_forward.png \
   $(NULL)
 
 RES_DRAWABLE_XHDPI_V11 = \
+  res/drawable-xhdpi-v11/favicon.png \
   res/drawable-xhdpi-v11/abouthome_icon.png \
   res/drawable-xhdpi-v11/abouthome_logo.png \
   res/drawable-xhdpi-v11/abouthome_separator.9.png \
   res/drawable-xhdpi-v11/abouthome_sync_logo.png \
   res/drawable-xhdpi-v11/abouthome_sync_bg.9.png \
   res/drawable-xhdpi-v11/abouthome_sync_pressed_bg.9.png \
   res/drawable-xhdpi-v11/abouthome_topsite_placeholder.png \
   res/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png \
@@ -491,17 +497,16 @@ MOZ_ANDROID_DRAWABLES += \
   mobile/android/base/resources/drawable/autocomplete_list_bg.9.png             \
   mobile/android/base/resources/drawable/awesomebar_tab_indicator.xml           \
   mobile/android/base/resources/drawable/awesomebar_tab_press.xml               \
   mobile/android/base/resources/drawable/awesomebar_tab_press_selected.xml      \
   mobile/android/base/resources/drawable/awesomebar_tab_selected.xml            \
   mobile/android/base/resources/drawable/awesomebar_tab_unselected.xml          \
   mobile/android/base/resources/drawable/background.png                         \
   mobile/android/base/resources/drawable/desktop_notification.png               \
-  mobile/android/base/resources/drawable/favicon.png                            \
   mobile/android/base/resources/drawable/gecko_actionbar_bg.xml                 \
   mobile/android/base/resources/drawable/progress_spinner.xml                   \
   mobile/android/base/resources/drawable/progress_spinner_1.png                 \
   mobile/android/base/resources/drawable/progress_spinner_2.png                 \
   mobile/android/base/resources/drawable/progress_spinner_3.png                 \
   mobile/android/base/resources/drawable/progress_spinner_4.png                 \
   mobile/android/base/resources/drawable/progress_spinner_5.png                 \
   mobile/android/base/resources/drawable/progress_spinner_6.png                 \
--- a/mobile/android/base/ProfileMigrator.java
+++ b/mobile/android/base/ProfileMigrator.java
@@ -65,69 +65,70 @@ import java.util.Iterator;
 
 
 public class ProfileMigrator {
     private static final String LOGTAG = "ProfMigr";
     private File mProfileDir;
     private ContentResolver mCr;
 
     /*
-      Amount of Android history entries we will remember
-      to prevent moving their last access date backwards.
-    */
-    private static final int MAX_HISTORY_TO_CHECK = 1000;
-
-    /*
        These queries are derived from the low-level Places schema
        https://developer.mozilla.org/en/The_Places_database
     */
     private final String bookmarkQuery = "SELECT places.url AS a_url, "
         + "places.title AS a_title FROM "
         + "(moz_places as places JOIN moz_bookmarks as bookmarks ON "
         + "places.id = bookmarks.fk) WHERE places.hidden <> 1 "
         + "ORDER BY bookmarks.dateAdded";
     // Result column of relevant data
     private final String bookmarkUrl   = "a_url";
     private final String bookmarkTitle = "a_title";
 
+    /*
+      The sort criterion here corresponds to the one used for the
+      Awesomebar results. It's an simplification of Frecency.
+      We must divide date by 1000 due to the micro (Places)
+      vs milli (Android) distiction.
+    */
     private final String historyQuery =
-        "SELECT places.url AS a_url, places.title AS a_title, "
-        + "history.visit_date AS a_date FROM "
-        + "(moz_historyvisits AS history JOIN moz_places AS places ON "
-        + "places.id = history.place_id) WHERE places.hidden <> 1 "
-        + "ORDER BY history.visit_date DESC";
-    private final String historyUrl   = "a_url";
-    private final String historyTitle = "a_title";
-    private final String historyDate  = "a_date";
-
-    private final String faviconQuery =
-        "SELECT places.url AS a_url, favicon.data AS a_data, "
-        + "favicon.mime_type AS a_mime FROM (moz_places AS places JOIN "
-        + "moz_favicons AS favicon ON places.favicon_id = favicon.id)";
-    private final String faviconUrl  = "a_url";
-    private final String faviconData = "a_data";
-    private final String faviconMime = "a_mime";
+        "SELECT places.url AS a_url, places.title AS a_title,"
+        + "MAX(history.visit_date) AS a_date, COUNT(*) AS a_visits, "
+        // see BrowserDB.filterAllSites for this formula
+        + "MAX(1, (((MAX(history.visit_date)/1000) - ?) / 86400000 + 120)) AS a_recent, "
+        + "favicon.data AS a_favicon_data, favicon.mime_type AS a_favicon_mime "
+        + "FROM (moz_historyvisits AS history JOIN moz_places AS places "
+        + "ON places.id = history.place_id "
+        // Add favicon data if a favicon is present for this URL.
+        + "LEFT OUTER JOIN moz_favicons AS favicon "
+        + "ON places.favicon_id = favicon.id) "
+        + "WHERE places.hidden <> 1 "
+        + "GROUP BY a_url ORDER BY a_visits * a_recent DESC LIMIT ?";
+    private final String historyUrl    = "a_url";
+    private final String historyTitle  = "a_title";
+    private final String historyDate   = "a_date";
+    private final String historyVisits = "a_visits";
+    private final String faviconData   = "a_favicon_data";
+    private final String faviconMime   = "a_favicon_mime";
 
     public ProfileMigrator(ContentResolver cr, File profileDir) {
         mProfileDir = profileDir;
         mCr = cr;
     }
 
-    public void launchBackground() {
-        // Work around http://code.google.com/p/android/issues/detail?id=11291
-        // WebIconDatabase needs to be initialized within a looper thread.
-        GeckoAppShell.getHandler().post(new PlacesTask());
+    public void launch() {
+        new PlacesTask().run();
     }
 
     private class PlacesTask implements Runnable {
         // Get a list of the last times an URL was accessed
-        protected Map<String, Long> gatherAndroidHistory() {
+        protected Map<String, Long> gatherBrowserDBHistory() {
             Map<String, Long> history = new HashMap<String, Long>();
 
-            Cursor cursor = BrowserDB.getRecentHistory(mCr, MAX_HISTORY_TO_CHECK);
+            Cursor cursor =
+                BrowserDB.getRecentHistory(mCr, BrowserDB.getMaxHistoryCount());
             final int urlCol =
                 cursor.getColumnIndexOrThrow(BrowserDB.URLColumns.URL);
             final int dateCol =
                 cursor.getColumnIndexOrThrow(BrowserDB.URLColumns.DATE_LAST_VISITED);
 
             cursor.moveToFirst();
             while (!cursor.isAfterLast()) {
                 String url = cursor.getString(urlCol);
@@ -139,58 +140,81 @@ public class ProfileMigrator {
                 }
                 cursor.moveToNext();
             }
             cursor.close();
 
             return history;
         }
 
-        protected void addHistory(Map<String, Long> androidHistory,
-                                  String url, String title, long date) {
+        protected void addHistory(Map<String, Long> browserDBHistory,
+                                  String url, String title, long date, int visits) {
             boolean allowUpdate = false;
 
-            if (!androidHistory.containsKey(url)) {
-                // Android doesn't know the URL, allow it to be
-                // inserted with places date
+            if (!browserDBHistory.containsKey(url)) {
+                // BrowserDB doesn't know the URL, allow it to be
+                // inserted with places date.
                 allowUpdate = true;
             } else {
-                long androidDate = androidHistory.get(url);
+                long androidDate = browserDBHistory.get(url);
                 if (androidDate < date) {
-                    // Places URL hit is newer than Android,
-                    // allow it to be updated with places date
+                    // Places URL hit is newer than BrowserDB,
+                    // allow it to be updated with places date.
                     allowUpdate = true;
                 }
             }
 
             if (allowUpdate) {
                 BrowserDB.updateVisitedHistory(mCr, url);
-                BrowserDB.updateHistoryDate(mCr, url, date);
-                if (title != null) {
-                    BrowserDB.updateHistoryTitle(mCr, url, title);
-                }
+                // The above records one visit. Subtract that one visit here.
+                BrowserDB.updateHistoryEntry(mCr, url, title, date, visits - 1);
             }
         }
 
         protected void migrateHistory(SQLiteBridge db) {
-            Map<String, Long> androidHistory = gatherAndroidHistory();
+            Map<String, Long> browserDBHistory = gatherBrowserDBHistory();
             final ArrayList<String> placesHistory = new ArrayList<String>();
 
             try {
-                ArrayList<Object[]> queryResult = db.query(historyQuery);
+                final String[] queryParams = new String[] {
+                    /* current time */
+                    Long.toString(System.currentTimeMillis()),
+                    /*
+                       History entries to return. No point
+                       in retrieving more than we can store.
+                     */
+                    Integer.toString(BrowserDB.getMaxHistoryCount())
+                };
+                ArrayList<Object[]> queryResult =
+                    db.query(historyQuery, queryParams);
                 final int urlCol = db.getColumnIndex(historyUrl);
                 final int titleCol = db.getColumnIndex(historyTitle);
                 final int dateCol = db.getColumnIndex(historyDate);
+                final int visitsCol = db.getColumnIndex(historyVisits);
+                final int faviconMimeCol = db.getColumnIndex(faviconMime);
+                final int faviconDataCol = db.getColumnIndex(faviconData);
 
                 for (Object[] resultRow: queryResult) {
                     String url = (String)resultRow[urlCol];
                     String title = (String)resultRow[titleCol];
                     long date = Long.parseLong((String)(resultRow[dateCol])) / (long)1000;
-                    addHistory(androidHistory, url, title, date);
+                    int visits = Integer.parseInt((String)(resultRow[visitsCol]));
+                    addHistory(browserDBHistory, url, title, date, visits);
                     placesHistory.add(url);
+
+                    String mime = (String)resultRow[faviconMimeCol];
+                    if (mime != null) {
+                        // Some GIFs can cause us to lock up completely
+                        // without exceptions or anything. Not cool.
+                        if (mime.compareTo("image/gif") != 0) {
+                            ByteBuffer dataBuff =
+                                (ByteBuffer)resultRow[faviconDataCol];
+                            addFavicon(url, mime, dataBuff);
+                        }
+                    }
                 }
             } catch (SQLiteBridgeException e) {
                 Log.i(LOGTAG, "Failed to get bookmarks: " + e.getMessage());
                 return;
             }
             // GlobalHistory access communicates with Gecko
             // and must run on its thread
             GeckoAppShell.getHandler().post(new Runnable() {
@@ -236,35 +260,16 @@ public class ProfileMigrator {
                     BrowserDB.updateFaviconForUrl(mCr, url, image);
                 } catch (SQLException e) {
                     Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url
                           + " error:" + e.getMessage());
                 }
             }
         }
 
-        protected void migrateFavicons(SQLiteBridge db) {
-            try {
-                ArrayList<Object[]> queryResult = db.query(faviconQuery);
-                final int urlCol = db.getColumnIndex(faviconUrl);
-                final int mimeCol = db.getColumnIndex(faviconMime);
-                final int dataCol = db.getColumnIndex(faviconData);
-
-                for (Object[] resultRow: queryResult) {
-                    String url = (String)resultRow[urlCol];
-                    String mime = (String)resultRow[mimeCol];
-                    ByteBuffer dataBuff = (ByteBuffer)resultRow[dataCol];
-                    addFavicon(url, mime, dataBuff);
-                }
-            } catch (SQLiteBridgeException e) {
-                Log.i(LOGTAG, "Failed to get favicons: " + e.getMessage());
-                return;
-            }
-        }
-
         protected void migratePlaces(File aFile) {
             String dbPath = aFile.getPath() + "/places.sqlite";
             String dbPathWal = aFile.getPath() + "/places.sqlite-wal";
             String dbPathShm = aFile.getPath() + "/places.sqlite-shm";
             Log.i(LOGTAG, "Opening path: " + dbPath);
 
             File dbFile = new File(dbPath);
             if (!dbFile.exists()) {
@@ -274,17 +279,16 @@ public class ProfileMigrator {
             File dbFileWal = new File(dbPathWal);
             File dbFileShm = new File(dbPathShm);
 
             SQLiteBridge db = null;
             try {
                 db = new SQLiteBridge(dbPath);
                 migrateBookmarks(db);
                 migrateHistory(db);
-                migrateFavicons(db);
                 db.close();
 
                 // Clean up
                 dbFile.delete();
                 dbFileWal.delete();
                 dbFileShm.delete();
 
                 Log.i(LOGTAG, "Profile migration finished");
--- a/mobile/android/base/Restarter.java.in
+++ b/mobile/android/base/Restarter.java.in
@@ -70,17 +70,19 @@ public class Restarter extends Activity 
                         Thread.currentThread().sleep(100);
                     } catch (InterruptedException ie) {}
                 }
             }
         } catch (Exception e) {
             Log.i(LOGTAG, e.toString());
         }
         try {
-            String action = "android.intent.action.MAIN";
+            String action = "org.mozilla.gecko.restart_update".equals(getIntent().getAction()) ?
+                            App.ACTION_UPDATE : Intent.ACTION_MAIN;
+
             Intent intent = new Intent(action);
             intent.setClassName("@ANDROID_PACKAGE_NAME@",
                                 "@ANDROID_PACKAGE_NAME@.App");
             Bundle b = getIntent().getExtras();
             if (b != null)
                 intent.putExtras(b);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             Log.i(LOGTAG, intent.toString());
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/SetupScreen.java
@@ -0,0 +1,148 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Gian-Carlo Pascutto <gpascutto@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * 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 ***** */
+
+package org.mozilla.gecko;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.widget.ImageView;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.mozilla.gecko.R;
+
+public class SetupScreen extends Dialog
+{
+    private static final String LOGTAG = "SetupScreen";
+    // Default delay before showing dialog, in milliseconds
+    private static final int DEFAULT_DELAY = 100;
+    private AnimationDrawable mProgressSpinner;
+    private Context mContext;
+    private Timer mTimer;
+    private TimerTask mShowTask;
+
+    public class ShowTask extends TimerTask {
+        private Handler mHandler;
+
+        public ShowTask(Handler aHandler) {
+            mHandler = aHandler;
+        }
+
+        @Override
+        public void run() {
+            mHandler.post(new Runnable() {
+                    public void run() {
+                        SetupScreen.this.show();
+                    }
+            });
+
+        }
+
+        @Override
+        public boolean cancel() {
+            boolean stillInQueue = super.cancel();
+            if (!stillInQueue) {
+                mHandler.post(new Runnable() {
+                        public void run() {
+                            // SetupScreen.dismiss calls us,
+                            // we need to call the real Dialog.dismiss.
+                            SetupScreen.super.dismiss();
+                        }
+                });
+            }
+            return stillInQueue;
+        }
+    }
+
+    public SetupScreen(Context aContext) {
+        super(aContext, android.R.style.Theme_NoTitleBar_Fullscreen);
+        mContext = aContext;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.setup_screen);
+        setCancelable(false);
+
+        setTitle(R.string.splash_firstrun);
+
+        ImageView spinnerImage = (ImageView)findViewById(R.id.spinner_image);
+        mProgressSpinner = (AnimationDrawable)spinnerImage.getBackground();
+        spinnerImage.setImageDrawable(mProgressSpinner);
+    }
+
+    @Override
+    public void onWindowFocusChanged (boolean hasFocus) {
+        mProgressSpinner.start();
+    }
+
+    public void showDelayed(Handler aHandler) {
+        showDelayed(aHandler, DEFAULT_DELAY);
+    }
+
+    // Delay showing the dialog until at least aDelay ms have
+    // passed. We need to know the handler to (eventually) post the UI
+    // actions on.
+    public void showDelayed(Handler aHandler, int aDelay) {
+        mTimer = new Timer("SetupScreen");
+        mShowTask = new ShowTask(aHandler);
+        mTimer.schedule(mShowTask, aDelay);
+    }
+
+    @Override
+    public void dismiss() {
+        // Cancel the timers/tasks if we were using showDelayed,
+        // and post to the correct handler.
+        if (mShowTask != null) {
+            mShowTask.cancel();
+            mShowTask = null;
+        } else {
+            // If not using showDelayed, just dismiss here.
+            super.dismiss();
+        }
+        if (mTimer != null) {
+            mTimer.cancel();
+            mTimer = null;
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/SyncPreference.java
@@ -0,0 +1,73 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009-2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Nicholson <bnicholson@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * 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 ***** */
+
+package org.mozilla.gecko;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
+
+class SyncPreference extends Preference {
+    private static final String FENNEC_ACCOUNT_TYPE = "org.mozilla.firefox_sync";
+    private static final String SYNC_SETTINGS = "android.settings.SYNC_SETTINGS";
+
+    private Context mContext;
+
+    public SyncPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+    }
+
+    @Override
+    protected void onClick() {
+        // show sync setup if no accounts exist; otherwise, show account settings
+        Account[] accounts = AccountManager.get(mContext).getAccountsByType(FENNEC_ACCOUNT_TYPE);
+        Intent intent;
+        if (accounts.length > 0)
+            intent = new Intent(SYNC_SETTINGS);
+        else
+            intent = new Intent(mContext, SetupSyncActivity.class);
+        mContext.startActivity(intent);
+    }
+}
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -50,21 +50,23 @@ import org.json.JSONObject;
 import org.mozilla.gecko.db.BrowserDB;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
 public final class Tab {
     private static final String LOGTAG = "GeckoTab";
-    private static final int kThumbnailWidth = 120;
-    private static final int kThumbnailHeight = 80;
+    private static final int kThumbnailWidth = 136;
+    private static final int kThumbnailHeight = 77;
 
-    private static int sMinDim = 0;
+    private static float sMinDim = 0;
     private static float sDensity = 1;
+    private static int sMinScreenshotWidth = 0;
+    private static int sMinScreenshotHeight = 0;
     private int mId;
     private String mUrl;
     private String mTitle;
     private Drawable mFavicon;
     private String mFaviconUrl;
     private String mSecurityMode;
     private Drawable mThumbnail;
     private List<HistoryEntry> mHistory;
@@ -140,34 +142,83 @@ public final class Tab {
     public Drawable getFavicon() {
         return mFavicon;
     }
 
     public Drawable getThumbnail() {
         return mThumbnail;
     }
 
+    void initMetrics() {
+        DisplayMetrics metrics = new DisplayMetrics();
+        GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+        sMinDim = Math.min(metrics.widthPixels / kThumbnailWidth, metrics.heightPixels / kThumbnailHeight);
+        sDensity = metrics.density;
+    }
+
+    float getMinDim() {
+        if (sMinDim == 0)
+            initMetrics();
+        return sMinDim;
+    }
+
+    float getDensity() {
+        if (sDensity == 0.0f)
+            initMetrics();
+        return sDensity;
+    }
+
+    int getMinScreenshotWidth() {
+        if (sMinScreenshotWidth != 0)
+            return sMinScreenshotWidth;
+        return sMinScreenshotWidth = (int)(getMinDim() * kThumbnailWidth);
+    }
+
+    int getMinScreenshotHeight() {
+        if (sMinScreenshotHeight != 0)
+            return sMinScreenshotHeight;
+        return sMinScreenshotHeight = (int)(getMinDim() * kThumbnailHeight);
+    }
+
+    int getThumbnailWidth() {
+        return (int)(kThumbnailWidth * getDensity());
+    }
+
+    int getThumbnailHeight() {
+        return (int)(kThumbnailHeight * getDensity());
+    }
+
     public void updateThumbnail(final Bitmap b) {
         final Tab tab = this;
         GeckoAppShell.getHandler().post(new Runnable() {
             public void run() {
-                if (sMinDim == 0) {
-                    DisplayMetrics metrics = new DisplayMetrics();
-                    GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
-                    sMinDim = Math.min(metrics.widthPixels / 3, metrics.heightPixels / 2);
-                    sDensity = metrics.density;
-                }
                 if (b != null) {
                     try {
-                        Bitmap cropped = Bitmap.createBitmap(b, 0, 0, sMinDim * 3, sMinDim * 2);
-                        Bitmap bitmap = Bitmap.createScaledBitmap(cropped, (int) (kThumbnailWidth * sDensity), (int) (kThumbnailHeight * sDensity), false);
+                        Bitmap cropped = null;
+                        /* Crop to screen width if the bitmap is larger than the screen width or height. If smaller and the
+                         * the aspect ratio is correct, just use the bitmap as is. Otherwise, fit the smaller
+                         * smaller dimension, then crop the larger dimention.
+                         */
+                        if (getMinScreenshotWidth() < b.getWidth() && getMinScreenshotHeight() < b.getHeight())
+                            cropped = Bitmap.createBitmap(b, 0, 0, getMinScreenshotWidth(), getMinScreenshotHeight());
+                        else if (b.getWidth() * getMinScreenshotHeight() == b.getHeight() * getMinScreenshotWidth())
+                            cropped = b;
+                        else if (b.getWidth() * getMinScreenshotHeight() < b.getHeight() * getMinScreenshotWidth())
+                            cropped = Bitmap.createBitmap(b, 0, 0, b.getWidth(), 
+                                                          b.getWidth() * getMinScreenshotHeight() / getMinScreenshotWidth());
+                        else
+                            cropped = Bitmap.createBitmap(b, 0, 0, 
+                                                          b.getHeight() * getMinScreenshotWidth() / getMinScreenshotHeight(),
+                                                          b.getHeight());
+
+                        Bitmap bitmap = Bitmap.createScaledBitmap(cropped, getThumbnailWidth(), getThumbnailHeight(), false);
                         saveThumbnailToDB(new BitmapDrawable(bitmap));
-                        b.recycle();
 
-                        bitmap = Bitmap.createBitmap(cropped, 0, 0, (int) (138 * sDensity), (int) (78 * sDensity));
+                        if (!cropped.equals(b))
+                            b.recycle();
                         mThumbnail = new BitmapDrawable(bitmap);
                         cropped.recycle();
                     } catch (OutOfMemoryError oom) {
                         Log.e(LOGTAG, "Unable to create/scale bitmap", oom);
                         mThumbnail = null;
                     }
                 } else {
                     mThumbnail = null;
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -283,13 +283,17 @@ public class Tabs implements GeckoEventL
                 GeckoApp.mAppContext.processThumbnail(tab, null, compressed);
             }
         } catch (Exception e) { 
             Log.i(LOGTAG, "handleMessage throws " + e + " for message: " + event);
         }
     }
 
     public void refreshThumbnails() {
-        Iterator<Tab> iterator = tabs.values().iterator();
-        while (iterator.hasNext())
-            GeckoApp.mAppContext.getAndProcessThumbnailForTab(iterator.next());
+        GeckoAppShell.getHandler().post(new Runnable() {
+            public void run() {
+                Iterator<Tab> iterator = tabs.values().iterator();
+                while (iterator.hasNext())
+                    GeckoApp.mAppContext.getAndProcessThumbnailForTab(iterator.next());
+            }
+        });
     }
 }
--- a/mobile/android/base/TabsTray.java
+++ b/mobile/android/base/TabsTray.java
@@ -86,18 +86,18 @@ public class TabsTray extends Activity i
         
         LinearLayout container = (LinearLayout) findViewById(R.id.container);
         container.setOnClickListener(new Button.OnClickListener() {
             public void onClick(View v) {
                 finishActivity();
             }
         });
 
+        GeckoApp.registerOnTabsChangedListener(this);
         Tabs.getInstance().refreshThumbnails();
-        GeckoApp.registerOnTabsChangedListener(this);
         onTabsChanged(null);
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         GeckoApp.unregisterOnTabsChangedListener(this);
     }
@@ -135,16 +135,17 @@ public class TabsTray extends Activity i
             View view = mList.getChildAt(position - mList.getFirstVisiblePosition());
             mTabsAdapter.assignValues(view, tab);
         }
     }
 
     void finishActivity() {
         finish();
         overridePendingTransition(0, R.anim.shrink_fade_out);
+        GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Screenshot:Cancel",""));
     }
 
     // Adapter to bind tabs into a list 
     private class TabsAdapter extends BaseAdapter {
         public TabsAdapter(Context context, ArrayList<Tab> tabs) {
             mContext = context;
             mInflater = LayoutInflater.from(mContext);
             mTabs = new ArrayList<Tab>();
--- a/mobile/android/base/db/AndroidBrowserDB.java
+++ b/mobile/android/base/db/AndroidBrowserDB.java
@@ -113,19 +113,41 @@ public class AndroidBrowserDB implements
         values.put(Browser.BookmarkColumns.TITLE, title);
 
         cr.update(Browser.BOOKMARKS_URI,
                   values,
                   Browser.BookmarkColumns.URL + " = ?",
                   new String[] { uri });
     }
 
-    public void updateHistoryDate(ContentResolver cr, String uri, long date) {
+    public void updateHistoryEntry(ContentResolver cr, String uri, String title,
+                                   long date, int visits) {
+        int oldVisits = 0;
+        Cursor cursor = null;
+        try {
+            cursor = cr.query(Browser.BOOKMARKS_URI,
+                              new String[] { Browser.BookmarkColumns.VISITS },
+                              Browser.BookmarkColumns.URL + " = ?",
+                              new String[] { uri },
+                              null);
+
+            if (cursor.moveToFirst()) {
+                oldVisits = cursor.getInt(0);
+            }
+        } finally {
+            if (cursor != null)
+                cursor.close();
+        }
+
         ContentValues values = new ContentValues();
         values.put(Browser.BookmarkColumns.DATE, date);
+        values.put(Browser.BookmarkColumns.VISITS, oldVisits + visits);
+        if (title != null) {
+            values.put(Browser.BookmarkColumns.TITLE, title);
+        }
 
         cr.update(Browser.BOOKMARKS_URI,
                   values,
                   Browser.BookmarkColumns.URL + " = ?",
                   new String[] { uri });
     }
 
     public Cursor getAllVisitedHistory(ContentResolver cr) {
@@ -140,26 +162,32 @@ public class AndroidBrowserDB implements
     }
 
     public Cursor getRecentHistory(ContentResolver cr, int limit) {
         Cursor c = cr.query(Browser.BOOKMARKS_URI,
                             new String[] { URL_COLUMN_ID,
                                            BookmarkColumns.URL,
                                            BookmarkColumns.TITLE,
                                            BookmarkColumns.FAVICON,
-                                           BookmarkColumns.DATE },
+                                           BookmarkColumns.DATE,
+                                           BookmarkColumns.VISITS },
                             // Bookmarks that have not been visited have a date value
                             // of 0, so don't pick them up in the history view.
                             Browser.BookmarkColumns.DATE + " > 0",
                             null,
                             Browser.BookmarkColumns.DATE + " DESC LIMIT " + limit);
 
         return new AndroidDBCursor(c);
     }
 
+    public int getMaxHistoryCount() {
+        // Valid for Android versions up to 4.0.
+        return 250;
+    }
+
     public void clearHistory(ContentResolver cr) {
         Browser.clearHistory(cr);
     }
 
     public Cursor getAllBookmarks(ContentResolver cr) {
         Cursor c = cr.query(Browser.BOOKMARKS_URI,
                             new String[] { URL_COLUMN_ID,
                                            BookmarkColumns.URL,
@@ -352,16 +380,18 @@ public class AndroidBrowserDB implements
             } else if (columnName.equals(BrowserDB.URLColumns.TITLE)) {
                 columnName = Browser.BookmarkColumns.TITLE;
             } else if (columnName.equals(BrowserDB.URLColumns.FAVICON)) {
                 columnName = Browser.BookmarkColumns.FAVICON;
             } else if (columnName.equals(BrowserDB.URLColumns.THUMBNAIL)) {
                 columnName = URL_COLUMN_THUMBNAIL;
             } else if (columnName.equals(BrowserDB.URLColumns.DATE_LAST_VISITED)) {
                 columnName = Browser.BookmarkColumns.DATE;
+            } else if (columnName.equals(BrowserDB.URLColumns.VISITS)) {
+                columnName = Browser.BookmarkColumns.VISITS;
             }
 
             return columnName;
         }
 
         @Override
         public int getColumnIndex(String columnName) {
             return super.getColumnIndex(translateColumnName(columnName));
--- a/mobile/android/base/db/BrowserDB.java
+++ b/mobile/android/base/db/BrowserDB.java
@@ -45,35 +45,39 @@ public class BrowserDB {
     public static String ABOUT_PAGES_URL_FILTER = "about:%";
 
     public static interface URLColumns {
         public static String URL = "url";
         public static String TITLE = "title";
         public static String FAVICON = "favicon";
         public static String THUMBNAIL = "thumbnail";
         public static String DATE_LAST_VISITED = "date-last-visited";
+        public static String VISITS = "visits";
     }
 
     private static BrowserDBIface sDb;
 
     public interface BrowserDBIface {
         public Cursor filter(ContentResolver cr, CharSequence constraint, int limit);
 
         public Cursor getTopSites(ContentResolver cr, int limit);
 
         public void updateVisitedHistory(ContentResolver cr, String uri);
 
         public void updateHistoryTitle(ContentResolver cr, String uri, String title);
 
-        public void updateHistoryDate(ContentResolver cr, String uri, long date);
+        public void updateHistoryEntry(ContentResolver cr, String uri, String title,
+                                       long date, int visits);
 
         public Cursor getAllVisitedHistory(ContentResolver cr);
 
         public Cursor getRecentHistory(ContentResolver cr, int limit);
 
+        public int getMaxHistoryCount();
+
         public void clearHistory(ContentResolver cr);
 
         public Cursor getAllBookmarks(ContentResolver cr);
 
         public boolean isBookmark(ContentResolver cr, String uri);
 
         public void addBookmark(ContentResolver cr, String title, String uri);
 
@@ -102,28 +106,33 @@ public class BrowserDB {
     public static void updateVisitedHistory(ContentResolver cr, String uri) {
         sDb.updateVisitedHistory(cr, uri);
     }
 
     public static void updateHistoryTitle(ContentResolver cr, String uri, String title) {
         sDb.updateHistoryTitle(cr, uri, title);
     }
 
-    public static void updateHistoryDate(ContentResolver cr, String uri, long date) {
-        sDb.updateHistoryDate(cr, uri, date);
+    public static void updateHistoryEntry(ContentResolver cr, String uri, String title,
+                                          long date, int visits) {
+        sDb.updateHistoryEntry(cr, uri, title, date, visits);
     }
 
     public static Cursor getAllVisitedHistory(ContentResolver cr) {
         return sDb.getAllVisitedHistory(cr);
     }
 
     public static Cursor getRecentHistory(ContentResolver cr, int limit) {
         return sDb.getRecentHistory(cr, limit);
     }
 
+    public static int getMaxHistoryCount() {
+        return sDb.getMaxHistoryCount();
+    }
+
     public static void clearHistory(ContentResolver cr) {
         sDb.clearHistory(cr);
     }
 
     public static Cursor getAllBookmarks(ContentResolver cr) {
         return sDb.getAllBookmarks(cr);
     }
 
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -195,19 +195,41 @@ public class LocalBrowserDB implements B
         values.put(History.TITLE, title);
 
         cr.update(appendProfile(History.CONTENT_URI),
                   values,
                   History.URL + " = ?",
                   new String[] { uri });
     }
 
-    public void updateHistoryDate(ContentResolver cr, String uri, long date) {
+    public void updateHistoryEntry(ContentResolver cr, String uri, String title,
+                                   long date, int visits) {
+        int oldVisits = 0;
+        Cursor cursor = null;
+        try {
+            cursor = cr.query(appendProfile(History.CONTENT_URI),
+                              new String[] { History.VISITS },
+                              History.URL + " = ?",
+                              new String[] { uri },
+                              null);
+
+            if (cursor.moveToFirst()) {
+                oldVisits = cursor.getInt(0);
+            }
+        } finally {
+            if (cursor != null)
+                cursor.close();
+        }
+
         ContentValues values = new ContentValues();
         values.put(History.DATE_LAST_VISITED, date);
+        values.put(History.VISITS, oldVisits + visits);
+        if (title != null) {
+            values.put(History.TITLE, title);
+        }
 
         cr.update(appendProfile(History.CONTENT_URI),
                   values,
                   History.URL + " = ?",
                   new String[] { uri });
     }
 
     public Cursor getAllVisitedHistory(ContentResolver cr) {
@@ -221,24 +243,29 @@ public class LocalBrowserDB implements B
     }
 
     public Cursor getRecentHistory(ContentResolver cr, int limit) {
         Cursor c = cr.query(appendProfileAndLimit(History.CONTENT_URI, limit),
                             new String[] { History._ID,
                                            History.URL,
                                            History.TITLE,
                                            History.FAVICON,
-                                           History.DATE_LAST_VISITED },
+                                           History.DATE_LAST_VISITED,
+                                           History.VISITS },
                             History.DATE_LAST_VISITED + " > 0",
                             null,
                             History.DATE_LAST_VISITED + " DESC");
 
         return new LocalDBCursor(c);
     }
 
+    public int getMaxHistoryCount() {
+        return MAX_HISTORY_COUNT;
+    }
+
     public void clearHistory(ContentResolver cr) {
         cr.delete(appendProfile(History.CONTENT_URI), null, null);
     }
 
     public Cursor getAllBookmarks(ContentResolver cr) {
         Cursor c = cr.query(appendProfile(Bookmarks.CONTENT_URI),
                             new String[] { Bookmarks._ID,
                                            Bookmarks.URL,
@@ -396,16 +423,18 @@ public class LocalBrowserDB implements B
             } else if (columnName.equals(BrowserDB.URLColumns.TITLE)) {
                 columnName = URLColumns.TITLE;
             } else if (columnName.equals(BrowserDB.URLColumns.FAVICON)) {
                 columnName = ImageColumns.FAVICON;
             } else if (columnName.equals(BrowserDB.URLColumns.THUMBNAIL)) {
                 columnName = ImageColumns.THUMBNAIL;
             } else if (columnName.equals(BrowserDB.URLColumns.DATE_LAST_VISITED)) {
                 columnName = History.DATE_LAST_VISITED;
+            } else if (columnName.equals(BrowserDB.URLColumns.VISITS)) {
+                columnName = History.VISITS;
             }
 
             return columnName;
         }
 
         @Override
         public int getColumnIndex(String columnName) {
             return super.getColumnIndex(translateColumnName(columnName));
--- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
+++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
@@ -157,17 +157,17 @@ public class GeckoSoftwareLayerClient ex
         layerController.setRoot(mTileLayer);
         if (mGeckoViewport != null) {
             layerController.setViewportMetrics(mGeckoViewport);
         }
 
         GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this);
         GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
         GeckoAppShell.registerGeckoEventListener("Document:Shown", this);
-        GeckoAppShell.registerGeckoEventListener("Tab:Selected", this);
+        GeckoAppShell.registerGeckoEventListener("Tab:Selected:Done", this);
 
         sendResizeEventIfNecessary();
     }
 
     private boolean setHasDirectTexture(boolean hasDirectTexture) {
         if (mTileLayer != null && hasDirectTexture == mHasDirectTexture)
             return false;
 
@@ -514,17 +514,17 @@ public class GeckoSoftwareLayerClient ex
             mUpdateViewportOnEndDraw = true;
 
             // Redraw everything.
             Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height);
             GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.DRAW, rect));
         } else if ("Viewport:UpdateLater".equals(event)) {
             mUpdateViewportOnEndDraw = true;
         } else if (("Document:Shown".equals(event) ||
-                    "Tab:Selected".equals(event)) &&
+                    "Tab:Selected:Done".equals(event)) &&
                    (mTileLayer instanceof MultiTileLayer)) {
             beginTransaction(mTileLayer);
             try {
                 ((MultiTileLayer)mTileLayer).invalidateTiles();
                 ((MultiTileLayer)mTileLayer).invalidateBuffer();
             } finally {
                 endTransaction(mTileLayer);
             }
--- a/mobile/android/base/gfx/LayerController.java
+++ b/mobile/android/base/gfx/LayerController.java
@@ -184,20 +184,34 @@ public class LayerController {
      *
      * TODO: Refactor this to use an interface. Expose that interface only to the view and not
      * to the layer client. That way, the layer client won't be tempted to call this, which might
      * result in an infinite loop.
      */
     public void setViewportSize(FloatSize size) {
         // Resize the viewport, and modify its zoom factor so that the page retains proportionally
         // zoomed relative to the screen.
+        float oldHeight = mViewportMetrics.getSize().height;
         float oldWidth = mViewportMetrics.getSize().width;
         float oldZoomFactor = mViewportMetrics.getZoomFactor();
         mViewportMetrics.setSize(size);
 
+        // if the viewport got larger (presumably because the vkb went away), and the page
+        // is smaller than the new viewport size, increase the page size so that the panzoomcontroller
+        // doesn't zoom in to make it fit (bug 718270). this page size change is in anticipation of
+        // gecko increasing the page size to match the new viewport size, which will happen the next
+        // time we get a draw update.
+        if (size.width >= oldWidth && size.height >= oldHeight) {
+            FloatSize pageSize = mViewportMetrics.getPageSize();
+            if (pageSize.width < size.width || pageSize.height < size.height) {
+                mViewportMetrics.setPageSize(new FloatSize(Math.max(pageSize.width, size.width),
+                                                           Math.max(pageSize.height, size.height)));
+            }
+        }
+
         PointF newFocus = new PointF(size.width / 2.0f, size.height / 2.0f);
         float newZoomFactor = size.width * oldZoomFactor / oldWidth;
         mViewportMetrics.scaleTo(newZoomFactor, newFocus);
 
         Log.d(LOGTAG, "setViewportSize: " + mViewportMetrics);
         setForceRedraw();
 
         if (mLayerClient != null)
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -64,16 +64,17 @@
 <!ENTITY pref_plugins_disabled "Disabled">
 <!ENTITY pref_text_size "Text size">
 <!ENTITY pref_font_size_tiny "Tiny">
 <!ENTITY pref_font_size_small "Small">
 <!ENTITY pref_font_size_medium "Medium">
 <!ENTITY pref_font_size_large "Large">
 <!ENTITY pref_font_size_xlarge "Extra Large">
 <!ENTITY pref_use_master_password "Use master password">
+<!ENTITY pref_sync "Sync">
 
 <!ENTITY quit "Quit">
 
 <!ENTITY addons "Add-ons">
 <!ENTITY downloads "Downloads">
 <!ENTITY char_encoding "Character Encoding">
 
 <!ENTITY share "Share">
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b74375ec8aef65a21d935cc141856e2868b77e42
GIT binary patch
literal 1868
zc$}S8c~BEq940YB0jXMK1fg_I1Q8^=n`Fr$K_wxACEN~lR3s!T1juek784Luj`2bm
zEEEN#>VUVP1*%140Fii<W57~GrHFX(2MXc{wo<x5u$_)$XZmJ#_r2fye#iUX_nX}p
z6tK?9Vu^)`iHVh;uQ-JKLdNGaQ}P{7eY?lRWKOIyEP{xT`U~W^iYZgz(I``|Qkxiu
z=W4xLCXYu6AR3KPVnX`Mx&}I+R0!$eh!m2lMQE(jH${VnrUZn^Q{v@(1%0&_;Hejo
z0aPd<1N5o{Oe@d}>C<rq<lJZm>A*CEh!@h=8aIFlX%Ha7H7I~EA$K|C0RtW!Cd`5O
z9R5mx1;G#qvp|UJ4nqQjD}Z2NX3)uO8pT#Yh*&a{i(Comu>_$OfS^vNW9ryUToVJr
zd_Et9SRjk#P9og3$rvHiyJK3$Yy>f?m1~r0LWyI5aV?9+lL#T5K6?a}S}Oez7}L%W
zMN$UpWoi&+LZC_o&Z4zM2)gytXl+=s8U;g8EuN&2qa>X1k5r#_H3G?M1c4eQc`334
zF)mM1p%~#O7ShQbrb4M8DdIse%;i8_7UaQ$VICKjc(d5vTnR@a@<5Q;7$5T$!4eM^
z#FwxU4;Y4hV7|B5#~bFb*&H4s5+nRsUq4Js$S^rN>#O|R*XNVI0+9xl5x6D{#}j51
z5EP3OxHcA71ENrb2?J7@T!|Si#tWLM71b!Wp$drxR{_%u3zQ$J;J;7)-dFK|SOdvo
zz<<eQ)<kO1IQ<}ga`B;kP>k#u4cQ$1`|szHjb%spL=d6)RzfG!peFAb@KecRNFm4+
z$%(g6PqHyn{KRX+Vt?x^@(ZK~FOu}{-!L`r(z1GFDGZMus-b4^fc69HZ5BoN&pkb5
zxbA*GvNkFHQDJ%h{c*<C@bJu>A95z2x4`M#Qn~VMkdHX7sAz{>--C8}X)S7K1$Rd-
zeb#06P0bDc(~DjmowRuuaHrs>m3_ncq2s4D!DZfOyq~qavV2_JBY%l@I`Zs4SEt`;
zZ98AIc)?4v=2O|lfAs9xDcMB}P-hNqq;v*=de_2pDV<s7hc`H1bGdb{dG+-SA-%df
zv&gc^D);yGS(GySZc9_$L@?j2ppgMiv>yumu}+BCZVr5w>cq2-X{IpM8TJkX`}wS>
zij<+)x8{coKfV6>L_hVt-6F>Nq3%lAoiRG)`vpWw8e$u{aqf|aCxrtE)HrN#?R*}_
zz)G%~V)v}uosUJF^$+N<puOA1W!dHy<TOgRU+bkEd!E%#n{4u(D_WIa@+Pa=oaZH6
zF5L-*c`!Cl9o<!y&+dAialFcI$dwvkm70HX$xs|MWwI&c3)7KO%nqjXMXsTud_y<Q
z!ND=#^m0PU!AjYhKlgIJ+(E6eBe9xy-kIwnNUL+y)WbZ^j#H-+Bv*D*YCV+`>3+Qj
z2!yh|c23vCaBh_my={EHul9+qpv|`jiUQXayERmh@Zw#i^=Vx-savM;y?tq2oC~Qa
zy9=3jx0fNUZn1VZVunjMcj+tKNP9O;QB_wyTI|^wT^?_Hv0k*KBYuUk1faeY;aol0
z?Hg_#^ei1+HZNBH^*(d8y`y=-6Weg>ynDCv3>9O`vW?K%@0@c&ahK`yxK6e%P(P4l
z1sA@OZf6aAr3)Qu?GVRhuW3sPceq@c@VF~6yk_~9bhBiJz0<dzjRm*6D+foYmL#M8
zF;$Gw<6aGyiu@Ped@_GIaw7-6&5BlA3daT-4qLQT-SaPhVVh>-=$^f|$sAK>xgC)<
zo)~<%0=#n2*Nc6pe<Y-qH85V+>S~x;n8<6KONxuJ33fF7#bHBbn9X7X*CRi$wPw-L
zcO8~ak4BSSC~0}CUj(}po+QO-{orn*O=ZLw-DHF+7AHTNOlT^(*}S-IvRwFLz|bF5
zZJ=!$v6P+(De@Ycyz76SeYy4U@t&K=n4!OU)f?LmhhGP1+*$)|CA)z#>`F1qZ0p%=
P{0aK`1c)oWqcZ*iYHHN@
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e8be73ed522e1d054142840e87bfbbffcd48a240
GIT binary patch
literal 1588
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=hEVFlS_jM3hAM`dB6B=jtV<<R_)-
zRpb^hfPjsCML}Y6c4~=2Qfhi;o~_dR-TRdkGE;1o!cBb*d<&dYGcrA@ic*8C{6dne
zvXd=Sl<e&kY$~jP2IQueBq~(o=HwMyRoE(l&9wruLBje<3ScEA*|tiKAPI#C-v9;Y
z{GwC^Q#}LSWCJrp1v3*pLlXl_6H6ThBLhPNeM2LC19M$N11nQ=D+5CXC{P00R+N%v
z73AUuwF}6zRmvzSDX`MlFE20GD>v55FG|-pw6wI;H!#vSGSUUA&@HaaD@m--%_~-h
znc$LIoLrPyP?DLSrvTz7Cgqow*eWT(ypUVq>kBtHuNWFoz#!AFNG#Ad)HBe}&DDpg
zE-nd5O~bFcIHa;5RX-@TIKQ+gITff}8ObXAT0shduCekj$^=GAVvb9Ga%paAUWuoR
ztrAd1FC{Yt7(^BZhKA-Q2IfWvW)_Bq7UqU-PDaK~=58i#&Ss{jFf*{}bvAS}HFkD0
zF*Y?bG&FQIv~+TDbuu(DHa4*^b#^hegz5FnD=taQOHPI9%|z%m#;e!LxhOTUB)=#m
zKR*YS0s=DfOY(~|@(UE4gH81e6?_wuGxI<qpa_DxHMJ<SEH%ZgC_h&L9Ku$aSX@BZ
z=McRq<b;|&&@uXG$pt0^OoJdMJn;iL@a&VC2h1}?z|0}myCVUZu_Q}eBT9nv(@M${
zi&7aNA>f&tm<~(`i7AzZCsS=07?_egT^vIq4j-L%yX#hfNZa?R;mpi&qBUBMFPOK?
zE44XeIImZ#KX9?z-0hMQFLE6vUNHY)+xsc2^si#|>}}H)8szEQ=}(&awN`>7T43T1
zm3!x_e(P`=T0U#FG!St(cfoqjEbAZE#rqD=4rF}yOw#4f*KfZwj8r#g2d=%iC~NP^
z59ux+ZfW|RO%yLnGMAZ`{W<!8*_N1{yQkfJ$Szg=%vd<Fr6t{_>AK&?{+%8Bi}~4O
z3RX2-t(u&@%{pYIY2B6CZ$BKF*Ln1mXv}ZL1dtil_Z~C_v+}r24qqIlF4v(uZC%99
z*_#{gzUer`x|}Ox+u3HI%{R8K4}TXmXYxkdy-nPHq793dd-clb#eBM8dGK`uP~byG
z2<L-kd`tXUZEQk09>`y4{Ias)xBKD4k!!Xn)M_QX)6s9@nqkTPNpRMO8m$F2-yIUH
z%3DC{r{uloykYGA!r@nl!^Rr-1M|+cf`mo9wzGWLdktjF?4+3t^=e?zC)}z`i#v5o
zfJQ2m2dfy!+;(EWw47<v&!*!ACKsE&$ew3D{LwXl@$bWqnie&i`CJ~UMJ#I$1^`X(
zV|=N@xcWS^)C4=x2|lTOJYUxwG*v!#Ml|Nk`Gf=8U5tF^=IP&h{q?Bj%aXMkF$HY<
zm|LA}W<E-;{JZw{=0iOe!ig3Snm$*k)tESabjg~2^4ZGVvhOw$$#%61Bz7m=-~|OV
z!{*DvFMaerna;cO@UO&Ft*y7z;yeW&I=}yLU8XEiz3ecf_?+Vmbq(iV%1q>~?NsMF
s!|m*7!2EB|Kl$SIAR}f3GERRYzM|+zH}j<@Cqae1r>mdKI;Vst0LisU<NyEw
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ae307d3dd9a3dce69994be8f03904e91248da98f
GIT binary patch
literal 2195
zc$}S93se(l79IqVpr}MGQdmh75LzWOlMwPKAq7G(E8$^4W5sGnCLusFF&X3`r9mD(
z5Q|`Wthma<b*q%(A}9sR%T=MNC@Z2x+(3oeMX*&Uuz>A^io0h|k9$u4b7toL|NXxE
z`tLnw4uypT+1b8n3jlzfa1(zE{(aB<*jVH5bE-WL0l+dw6(PYSqF{~;Ra2#MG#a5M
zskMN4`S6moQdt~=k)jc$O2Z{TZEPl!RB|qP8$$$%v;jzrYEz015vPPi$Wr2DY&n_d
zOY%wL;0DwPCM6}Q<28Ct5|=!0mxIgZV~|XmhhTABvY%N&l8C}c0jLfkF{qHI4DyCa
z-d<GL3u1e*H<D-&41q8WgqWT%#9=Tw5KMZx$hbG1T*2AG7rgX^@3`a`4AXKzFflQa
znn<UjIwc6R*=!J^fi#*Yj_}kcYcOe&r$$d%u)s(3GM!3`sZb5cyq89!2^g15UPwW$
z6^ULM*63dr3NIO$B-Mg26#~_2Z~?8ywjhevqxBKVS_IsJ=+Ok748h@)|8n(usu_q!
z!wJ=?@Jo@#^HEuX8qr`vK9`JZsB)DYFA)oZVWt<vq(R;+7-lhHfj^Dz&lGqG0=yZF
z1slJ`4S)sSG{}!mXL!Rf90;@h`GNkh7oG0KVg&FR>;<k+qsOEg8M44t{fA3?EteCZ
zL!=n0i$Kx%g$@XdK`~SxgK9|uVg?l^iKH@>#(ZMFpqH^CI@Ml8F3_QB(tL$Es^5s<
z|7ZRySN?md2I9eh|I;oDM|cmK#aGRbZ(fZLqQPfIhY!c?NbmjlU^!xe5=@LLutcd2
z0e-E3P%TyBjUbgL@998%0KlqM$oGp#`eyV*e=J$JHhXDidU_Ujr_Dxu8E>~e<R8_o
zdKc!&qPwTVWx>JMHFb4!y~5pN6bMSpt4PmkFf<sloEK-J&CIxVWn>8qS-#)VYFQ?Z
zOzjHD5#*A|6Q$vx#d_Y6>S`BRRtLm1deRj!);gCD-G|wFpOR4pZKP8cA}|ch#5pxX
zy>qQG&Ws(8w)wv^vxdXnAijWU0>@6(4S#CXuUE9i7u(lHD~!H-pa;W*n$80c7We$S
zqAiEm@`hDyy9G7WE>E415=z{45ujN`nZ|R0ZG^aR`Z|Zr%E}+Jek^V-UR@D?P=BY2
z5sKWTiwo{IbySI;w-YoSY=z<W8i}f&^K<!wj_*rceK*W}J`ygjYy6P0_Dbkoa9J^-
zRw^(aE;(aaYO)Thx@7&>F1z<Ka`oCi1*0l37K-xuz64I%YCLk^efbJsZN|4%K=anN
zZzSgnbNl+w*!Q+Rd14Z}v)X!WZjIVKlZ1H`5`BBi6}F6`lJ~D1i!S<z2%42)pD}$2
z$%EI!>v_(fA}fFc-P2j_{r4s+>`y=0mFBrR^zE`Y7%$fPSV_`iCm!cw8RzW{E*mCi
z*PhPYdv?-xBFeBctx%(7EHd5A%So|hKJvT7i|Dqi78p{?Hs!zA{nse}N&2EkQ{|bb
zz)v<mPE#0W3Br`rzRQ_oN-NfI4$()0BWkTY@+$jWJc@439RY$G+E&lTcD#-8zS=zS
zqPETE)&S4#)T%;&+Z}ck9>Qs?&SRBdpP1*d)(ac^BB^CFjuuVrviysjokPSgPPw(%
z3m+_nMwM3gcq}sEVTg{<-$gX#x$bGKm=EYl@&VUFJDP64h4D5UEc35^R&Pew<n{AN
zLb_%H9>+!tpM0-x3+In(8oxVM*=lx3x0Fu`yJN0(x?wy@%ko{v`Ar?TNN*WiCoH!{
zF&ndyft-8}Y9=nLDR3kBDih5%emDyLF>2}QnGy?HqxjB<-C1+0j1R_3Ypf3bc}*d>
ziC<afEbGLb)t!7xShscCd`{le<*tuJxVU{$6Xno8i@(enfgQ6;{Uk%V%jfm=^+IAq
zT<3fY!#{BY2!4`%fA<KQ5`NLzvv*0V2-Sr9yDj@@McX-D`d6e>O1~*~-<z|?ADR}g
z>U1b7A(<?lPp1wj&p0)vUA#g$UX-JoF6e4v54~qt9aZTR<@wK*9Y5E9d4a94iC6AD
zh@K2~v+1F=FW-LI^#g6@`RT~}fBXFM&54vhIf!ly*-ob3_!n`BBr5~kPl?I}?$tc~
zL-h~k?_MZAJassE<u&7o8}WJ-tT>Qiqg@hI1uu%SPJd_S%fTJ;QFTg&>y%edb5-g9
zW8HK2yHt<juiLANw@(_JoG;v67Dia-QCfQL(umXcY}YBv!N;u7Gqw7`gpbRt#!nWH
z1RJv*;q$({Qd40moer>W4%tg{8)}`$J)V`G9h>u0wn^<mo;gztT@G&31?7W%ilL_y
z<4Y9ATRu1F!~M@QZeBIIm;i_Vrzb30rn5fox|6@saX7nSrwO=J;7BiatZi={{^?oQ
dKR}BY-|=D<#2YfH+b;9JgfK9KU+uqp|1aqWa>W1u
deleted file mode 100644
index 8e66e5ae57e04947712939bd5e9ee08d46aad029..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/mobile/android/base/resources/layout/abouthome_topsite_item.xml
+++ b/mobile/android/base/resources/layout/abouthome_topsite_item.xml
@@ -3,25 +3,25 @@
               android:layout_width="140dip"
               android:layout_height="wrap_content">
 
     <LinearLayout android:orientation="vertical"
                   android:layout_width="fill_parent"
                   android:layout_height="fill_parent">
 
         <LinearLayout android:orientation="vertical"
-                      android:layout_width="fill_parent"
-                      android:layout_height="wrap_content"
+                      android:layout_width="138dip"
+                      android:layout_height="80dip"
                       android:background="@drawable/abouthome_topsite_shadow"
                       android:padding="1dip"
-                      android:paddingTop="2dip">
+                      android:paddingBottom="2dip">
 
             <ImageView android:id="@+id/thumbnail"
-                       android:layout_width="fill_parent"
-                       android:layout_height="80dip"
+                       android:layout_width="136dip"
+                       android:layout_height="77dip"
                        android:scaleType="centerCrop"/>
 
         </LinearLayout>
 
         <TextView android:id="@+id/title"
                   android:layout_width="fill_parent"
                   android:layout_height="15dip"
                   android:layout_marginTop="3dip"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/setup_screen.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              style="@style/Screen"
+              android:background="@drawable/tabs_tray_bg_repeat"
+              android:orientation="vertical"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:gravity="center"
+              android:layout_gravity="center" >
+
+    <LinearLayout android:orientation="vertical"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:gravity="center"
+                  android:layout_gravity="center" >
+
+      <ImageView android:id="@+id/image"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:layout_marginBottom="24dp"
+                 android:src="@drawable/logo" />
+
+      <LinearLayout android:orientation="horizontal"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal" >
+
+        <ImageView android:id="@+id/spinner_image"
+                   android:layout_width="wrap_content"
+                   android:layout_height="wrap_content"
+                   android:layout_marginRight="10dp"
+                   android:background="@drawable/progress_spinner" />
+
+        <TextView android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_marginTop="4dp"
+                  android:textSize="15dp"
+                  android:textColor="#FFFFFF"
+                  android:typeface="sans"
+                  android:text="@string/splash_firstrun" />
+
+      </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
--- a/mobile/android/base/resources/layout/tabs_row.xml
+++ b/mobile/android/base/resources/layout/tabs_row.xml
@@ -2,26 +2,26 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:id="@+id/info"
                 android:layout_width="fill_parent"
                 android:layout_height="100dip"
                 android:minHeight="100dip"
                 android:background="@drawable/tabs_tray_list_selector">
 
     <ImageView android:id="@+id/thumbnail"
-               android:layout_width="138dip"
-               android:layout_height="78dip"
+               android:layout_width="136dip"
+               android:layout_height="77dip"
                android:layout_marginLeft="35dip"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:src="@drawable/tab_thumbnail_default"
                android:scaleType="fitCenter"/>
 
     <ImageView android:id="@+id/shadow"
-               android:layout_width="140dip"
+               android:layout_width="138dip"
                android:layout_height="80dip"
                android:layout_marginLeft="34dip"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:src="@drawable/tab_thumbnail_shadow"
                android:scaleType="fitCenter"/>
 
     <ImageView android:id="@+id/selected_indicator"
--- a/mobile/android/base/resources/xml/preferences.xml
+++ b/mobile/android/base/resources/xml/preferences.xml
@@ -2,19 +2,18 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                         android:enabled="false">
 
     <PreferenceCategory android:title="@string/pref_category_general">
         <org.mozilla.gecko.LinkPreference android:title="@string/pref_about_firefox"
                                           url="about:" />
 
-        <!-- TODO: Default Search Engine -->
-
-        <!-- TODO: Sync -->
+        <org.mozilla.gecko.SyncPreference android:title="@string/pref_sync"
+                                          android:persistent="false" />
 
     </PreferenceCategory>
 
     <PreferenceCategory android:title="@string/pref_category_content">
 
         <ListPreference     android:key="browser.menu.showCharacterEncoding"
                             android:title="@string/pref_char_encoding"
                             android:entries="@array/pref_char_encoding_entries"
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -69,16 +69,17 @@
   <string name="pref_plugins_tap_to_play">&pref_plugins_tap_to_play;</string>
   <string name="pref_plugins_disabled">&pref_plugins_disabled;</string>
   <string name="pref_text_size">&pref_text_size;</string>
   <string name="pref_font_size_tiny">&pref_font_size_tiny;</string>
   <string name="pref_font_size_small">&pref_font_size_small;</string>
   <string name="pref_font_size_medium">&pref_font_size_medium;</string>
   <string name="pref_font_size_large">&pref_font_size_large;</string>
   <string name="pref_font_size_xlarge">&pref_font_size_xlarge;</string>
+  <string name="pref_sync">&pref_sync;</string>
 
   <string name="reload">&reload;</string>
   <string name="forward">&forward;</string>
   <string name="new_tab">&new_tab;</string>
   <string name="addons">&addons;</string>
   <string name="downloads">&downloads;</string>
   <string name="char_encoding">&char_encoding;</string>
 
--- a/mobile/android/branding/aurora/android-resources.mn
+++ b/mobile/android/branding/aurora/android-resources.mn
@@ -1,6 +1,4 @@
 mobile/android/app/android/drawable/alertaddons.png
 mobile/android/app/android/drawable/alertdownloads.png
-mobile/android/branding/aurora/content/splash.png
-mobile/android/branding/aurora/content/splash_v9.9.png
-mobile/android/branding/aurora/content/splash_v8.9.png
+mobile/android/branding/aurora/content/logo.png
 mobile/android/branding/aurora/content/favicon32.png
--- a/mobile/android/branding/beta/android-resources.mn
+++ b/mobile/android/branding/beta/android-resources.mn
@@ -1,6 +1,4 @@
 mobile/android/app/android/drawable/alertaddons.png
 mobile/android/app/android/drawable/alertdownloads.png
-mobile/android/branding/beta/content/splash.png
-mobile/android/branding/beta/content/splash_v9.9.png
-mobile/android/branding/beta/content/splash_v8.9.png
+mobile/android/branding/beta/content/logo.png
 mobile/android/branding/beta/content/favicon32.png
--- a/mobile/android/branding/nightly/android-resources.mn
+++ b/mobile/android/branding/nightly/android-resources.mn
@@ -1,6 +1,4 @@
 mobile/android/app/android/drawable/alertaddons.png
 mobile/android/app/android/drawable/alertdownloads.png
-mobile/android/branding/nightly/content/splash.png
-mobile/android/branding/nightly/content/splash_v9.9.png
-mobile/android/branding/nightly/content/splash_v8.9.png
+mobile/android/branding/nightly/content/logo.png
 mobile/android/branding/nightly/content/favicon32.png
--- a/mobile/android/branding/official/android-resources.mn
+++ b/mobile/android/branding/official/android-resources.mn
@@ -1,6 +1,4 @@
 mobile/android/app/android/drawable/alertaddons.png
 mobile/android/app/android/drawable/alertdownloads.png
-mobile/android/branding/official/content/splash.png
-mobile/android/branding/official/content/splash_v9.9.png
-mobile/android/branding/official/content/splash_v8.9.png
+mobile/android/branding/official/content/logo.png
 mobile/android/branding/official/content/favicon32.png
--- a/mobile/android/branding/unofficial/android-resources.mn
+++ b/mobile/android/branding/unofficial/android-resources.mn
@@ -1,6 +1,4 @@
 mobile/android/app/android/drawable/alertaddons.png
 mobile/android/app/android/drawable/alertdownloads.png
-mobile/android/branding/unofficial/content/splash.png
-mobile/android/branding/unofficial/content/splash_v9.9.png
-mobile/android/branding/unofficial/content/splash_v8.9.png
 mobile/android/branding/unofficial/content/favicon32.png
+mobile/android/branding/unofficial/content/logo.png
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -499,17 +499,41 @@ var BrowserApp = {
     let evt = document.createEvent("UIEvents");
     evt.initUIEvent("TabClose", true, false, window, null);
     aTab.browser.dispatchEvent(evt);
 
     aTab.destroy();
     this._tabs.splice(this._tabs.indexOf(aTab), 1);
   },
 
+  screenshotQueue: null,
+
   screenshotTab: function screenshotTab(aData) {
+      if (this.screenshotQueue == null) {
+          this.screenShotQueue = [];
+          this.doScreenshotTab(aData);
+      } else {
+          this.screenshotQueue.push(aData);
+      }
+  },
+
+  doNextScreenshot: function() {
+      if (this.screenshotQueue == null || this.screenshotQueue.length == 0) {
+          this.screenshotQueue = null;
+          return;
+      }
+      let data = this.screenshotQueue.pop();
+      if (data == null) {
+          this.screenshotQueue = null;
+          return;
+      }
+      this.doScreenshotTab(data);
+  },
+
+  doScreenshotTab: function doScreenshotTab(aData) {
       let json = JSON.parse(aData);
       let tab = this.getTabForId(parseInt(json.tabID));
       let width = parseInt(json.width);
       let height =  parseInt(json.height);
       tab.screenshot(width, height);
   },
 
   // Use this method to select a tab from JS. This method sends a message
@@ -534,16 +558,24 @@ var BrowserApp = {
   // in the Java UI.
   _handleTabSelected: function _handleTabSelected(aTab) {
       this.selectedTab = aTab;
       aTab.active = true;
 
       let evt = document.createEvent("UIEvents");
       evt.initUIEvent("TabSelect", true, false, window, null);
       aTab.browser.dispatchEvent(evt);
+
+    let message = {
+      gecko: {
+        type: "Tab:Selected:Done",
+        tabID: aTab.id
+      }
+    };
+    sendMessageToJava(message);
   },
 
   quit: function quit() {
     // Figure out if there's at least one other browser window around.
     let lastBrowser = true;
     let e = Services.wm.getEnumerator("navigator:browser");
     while (e.hasMoreElements() && lastBrowser) {
       let win = e.getNext();
@@ -886,16 +918,18 @@ var BrowserApp = {
       else
         this.loadURI(url, browser, params);
     } else if (aTopic == "Tab:Selected") {
       this._handleTabSelected(this.getTabForId(parseInt(aData)));
     } else if (aTopic == "Tab:Closed") {
       this._handleTabClosed(this.getTabForId(parseInt(aData)));
     } else if (aTopic == "Tab:Screenshot") {
       this.screenshotTab(aData);
+    } else if (aTopic == "Tab:Screenshot:Cancel") {
+      this.screenshotQueue = null;
     } else if (aTopic == "Browser:Quit") {
       this.quit();
     } else if (aTopic == "SaveAs:PDF") {
       this.saveAsPDF(browser);
     } else if (aTopic == "Preferences:Get") {
       this.getPreferences(aData);
     } else if (aTopic == "Preferences:Set") {
       this.setPreferences(aData);
@@ -1544,16 +1578,19 @@ Tab.prototype = {
           type: "Tab:ScreenshotData",
           tabID: this.id,
           width: aWidth,
           height: aHeight,
           data: canvas.toDataURL()
         }
       };
       sendMessageToJava(message);
+      Services.tm.mainThread.dispatch(function() {
+	  BrowserApp.doNextScreenshot()
+      }, Ci.nsIThread.DISPATCH_NORMAL);
   },
 
   updateTransform: function() {
     let hasZoom = (Math.abs(this._viewport.zoom - 1.0) >= 1e-6);
     let x = this._viewport.offsetX + Math.round(-this.viewportExcess.x * this._viewport.zoom);
     let y = this._viewport.offsetY + Math.round(-this.viewportExcess.y * this._viewport.zoom);
 
     let transform =
--- a/mobile/android/components/UpdatePrompt.js
+++ b/mobile/android/components/UpdatePrompt.js
@@ -70,16 +70,21 @@ XPCOMUtils.defineLazyGetter(this, "Local
 
 function getPref(func, preference, defaultValue) {
   try {
     return Services.prefs[func](preference);
   } catch (e) {}
   return defaultValue;
 }
 
+function sendMessageToJava(aMsg) {
+  let data = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).handleGeckoMessage(JSON.stringify(aMsg));
+  return JSON.parse(data);
+}
+
 // -----------------------------------------------------------------------
 // Update Prompt
 // -----------------------------------------------------------------------
 
 function UpdatePrompt() { }
 
 UpdatePrompt.prototype = {
   classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
@@ -133,18 +138,21 @@ UpdatePrompt.prototype = {
       }
     } else if(aMode == "downloaded") {
       // Notify all windows that an application quit has been requested
       let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
       Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
 
       // If nothing aborted, restart the app
       if (cancelQuit.data == false) {
-        let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
-        appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
+        sendMessageToJava({
+          gecko: {
+            type: "Update:Restart"
+          }
+        });
       }
     }
   },
 
   _updateDownloadProgress: function UP__updateDownloadProgress(aProgress, aTotal) {
     let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
     let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
     if (progressListener)
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -165,20 +165,16 @@ pref("browser.chrome.toolbar_tips",     
 // 0 = Pictures Only, 1 = Text Only, 2 = Pictures and Text
 pref("browser.chrome.toolbar_style",        2);
 // max image size for which it is placed in the tab icon for tabbrowser.
 // if 0, no images are used for tab icons for image documents.
 pref("browser.chrome.image_icons.max_size", 1024);
 
 pref("browser.triple_click_selects_paragraph", true);
 
-// When loading <video> or <audio>, check for Access-Control-Allow-Origin
-// header, and disallow the connection if not present or permitted.
-pref("media.enforce_same_site_origin", false);
-
 // Media cache size in kilobytes
 pref("media.cache_size", 512000);
 
 // Master HTML5 media volume scale.
 pref("media.volume_scale", "1.0");
 
 #ifdef MOZ_RAW
 pref("media.raw.enabled", true);
--- a/netwerk/protocol/http/SpdySession.cpp
+++ b/netwerk/protocol/http/SpdySession.cpp
@@ -36,16 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHttp.h"
 #include "SpdySession.h"
 #include "SpdyStream.h"
 #include "nsHttpConnection.h"
+#include "nsHttpHandler.h"
 #include "prnetdb.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Preferences.h"
 #include "prprf.h"
 
 #ifdef DEBUG
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
@@ -68,21 +69,20 @@ SpdySession::SpdySession(nsAHttpTransact
                          PRInt32 firstPriority)
   : mSocketTransport(aSocketTransport),
     mSegmentReader(nsnull),
     mSegmentWriter(nsnull),
     mSendingChunkSize(kSendingChunkSize),
     mNextStreamID(1),
     mConcurrentHighWater(0),
     mDownstreamState(BUFFERING_FRAME_HEADER),
-    mPartialFrame(nsnull),
-    mFrameBufferSize(kDefaultBufferSize),
-    mFrameBufferUsed(0),
-    mFrameDataLast(false),
-    mFrameDataStream(nsnull),
+    mInputFrameBufferSize(kDefaultBufferSize),
+    mInputFrameBufferUsed(0),
+    mInputFrameDataLast(false),
+    mInputFrameDataStream(nsnull),
     mNeedsCleanup(nsnull),
     mDecompressBufferSize(kDefaultBufferSize),
     mDecompressBufferUsed(0),
     mShouldGoAway(false),
     mClosed(false),
     mCleanShutdown(false),
     mGoAwayID(0),
     mMaxConcurrent(kDefaultMaxConcurrent),
@@ -95,51 +95,53 @@ SpdySession::SpdySession(nsAHttpTransact
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
   LOG3(("SpdySession::SpdySession %p transaction 1 = %p",
         this, aHttpTransaction));
   
   mStreamIDHash.Init();
   mStreamTransactionHash.Init();
   mConnection = aHttpTransaction->Connection();
-  mFrameBuffer = new char[mFrameBufferSize];
+  mInputFrameBuffer = new char[mInputFrameBufferSize];
   mDecompressBuffer = new char[mDecompressBufferSize];
   mOutputQueueBuffer = new char[mOutputQueueSize];
   zlibInit();
   
-  mSendingChunkSize =
-    Preferences::GetInt("network.http.spdy.chunk-size", kSendingChunkSize);
+  mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
   AddStream(aHttpTransaction, firstPriority);
 }
 
 PLDHashOperator
-SpdySession::Shutdown(nsAHttpTransaction *key,
-                      nsAutoPtr<SpdyStream> &stream,
-                      void *closure)
+SpdySession::ShutdownEnumerator(nsAHttpTransaction *key,
+                                nsAutoPtr<SpdyStream> &stream,
+                                void *closure)
 {
   SpdySession *self = static_cast<SpdySession *>(closure);
-  
-  if (self->mCleanShutdown &&
-      self->mGoAwayID < stream->StreamID())
+ 
+  // On a clean server hangup the server sets the GoAwayID to be the ID of
+  // the last transaction it processed. If the ID of stream in the
+  // local session is greater than that it can safely be restarted because the
+  // server guarantees it was not partially processed.
+  if (self->mCleanShutdown && (stream->StreamID() > self->mGoAwayID))
     stream->Close(NS_ERROR_NET_RESET); // can be restarted
   else
     stream->Close(NS_ERROR_ABORT);
 
   return PL_DHASH_NEXT;
 }
 
 SpdySession::~SpdySession()
 {
   LOG3(("SpdySession::~SpdySession %p mDownstreamState=%X",
         this, mDownstreamState));
 
   inflateEnd(&mDownstreamZlib);
   deflateEnd(&mUpstreamZlib);
   
-  mStreamTransactionHash.Enumerate(Shutdown, this);
+  mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
   Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
   Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
   Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
                         mServerPushedResources);
 }
 
 void
 SpdySession::LogIO(SpdySession *self, SpdyStream *stream, const char *label,
@@ -191,31 +193,35 @@ static Control_FX sControlFunctions[] =
   SpdySession::HandleGoAway,
   SpdySession::HandleHeaders,
   SpdySession::HandleWindowUpdate
 };
 
 bool
 SpdySession::RoomForMoreConcurrent()
 {
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
   return (mConcurrent < mMaxConcurrent);
 }
 
 bool
 SpdySession::RoomForMoreStreams()
 {
   if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
     return false;
 
   return !mShouldGoAway;
 }
 
 PRUint32
 SpdySession::RegisterStreamID(SpdyStream *stream)
 {
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
   LOG3(("SpdySession::RegisterStreamID session=%p stream=%p id=0x%X "
         "concurrent=%d",this, stream, mNextStreamID, mConcurrent));
 
   NS_ABORT_IF_FALSE(mNextStreamID < 0xfffffff0,
                     "should have stopped admitting streams");
   
   PRUint32 result = mNextStreamID;
   mNextStreamID += 2;
@@ -264,49 +270,53 @@ SpdySession::AddStream(nsAHttpTransactio
   }
   
   return true;
 }
 
 void
 SpdySession::ActivateStream(SpdyStream *stream)
 {
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
   mConcurrent++;
   if (mConcurrent > mConcurrentHighWater)
     mConcurrentHighWater = mConcurrent;
   LOG3(("SpdySession::AddStream %p activating stream %p Currently %d "
         "streams in session, high water mark is %d",
         this, stream, mConcurrent, mConcurrentHighWater));
 
   mReadyForWrite.Push(stream);
-  SetWriteCallbacks(stream->Transaction());
+  SetWriteCallbacks();
 
   // Kick off the SYN transmit without waiting for the poll loop
   PRUint32 countRead;
   ReadSegments(nsnull, kDefaultBufferSize, &countRead);
 }
 
 void
 SpdySession::ProcessPending()
 {
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
   while (RoomForMoreConcurrent()) {
     SpdyStream *stream = static_cast<SpdyStream *>(mQueuedStreams.PopFront());
     if (!stream)
       return;
     LOG3(("SpdySession::ProcessPending %p stream %p activated from queue.",
           this, stream));
     ActivateStream(stream);
   }
 }
 
 void
-SpdySession::SetWriteCallbacks(nsAHttpTransaction *aTrans)
+SpdySession::SetWriteCallbacks()
 {
-  if (mConnection && (WriteQueueSize() || mOutputQueueUsed))
-      mConnection->ResumeSend(aTrans);
+  if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed))
+      mConnection->ResumeSend();
 }
 
 void
 SpdySession::FlushOutputQueue()
 {
   if (!mSegmentReader || !mOutputQueueUsed)
     return;
   
@@ -326,83 +336,93 @@ SpdySession::FlushOutputQueue()
   
   if (countRead == avail) {
     mOutputQueueUsed = 0;
     mOutputQueueSent = 0;
     return;
   }
 
   mOutputQueueSent += countRead;
-  if (mOutputQueueSize - mOutputQueueUsed < kQueueTailRoom) {
-    // The output queue is filling up and we just sent some data out, so
-    // this is a good time to rearrange the output queue.
 
+  // If the output queue is close to filling up and we have sent out a good
+  // chunk of data from the beginning then realign it.
+  
+  if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
+      ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
     mOutputQueueUsed -= mOutputQueueSent;
     memmove(mOutputQueueBuffer.get(),
             mOutputQueueBuffer.get() + mOutputQueueSent,
             mOutputQueueUsed);
     mOutputQueueSent = 0;
   }
 }
 
 void
 SpdySession::DontReuse()
 {
   mShouldGoAway = true;
-  if(!mStreamTransactionHash.Count())
+  if (!mStreamTransactionHash.Count())
     Close(NS_OK);
 }
 
 PRUint32
-SpdySession::WriteQueueSize()
+SpdySession::GetWriteQueueSize()
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
-  PRUint32 count = mUrgentForWrite.GetSize() + mReadyForWrite.GetSize();
-
-  if (mPartialFrame)
-    ++count;
-  return count;
+  return mUrgentForWrite.GetSize() + mReadyForWrite.GetSize();
 }
 
 void
 SpdySession::ChangeDownstreamState(enum stateType newState)
 {
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
   LOG3(("SpdyStream::ChangeDownstreamState() %p from %X to %X",
         this, mDownstreamState, newState));
   mDownstreamState = newState;
+}
 
-  if (mDownstreamState == BUFFERING_FRAME_HEADER) {
-    if (mFrameDataLast && mFrameDataStream) {
-      mFrameDataLast = 0;
-      if (!mFrameDataStream->RecvdFin()) {
-        mFrameDataStream->SetRecvdFin(true);
-        --mConcurrent;
-        ProcessPending();
-      }
+void
+SpdySession::ResetDownstreamState()
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+  LOG3(("SpdyStream::ResetDownstreamState() %p", this));
+  ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+
+  if (mInputFrameDataLast && mInputFrameDataStream) {
+    mInputFrameDataLast = false;
+    if (!mInputFrameDataStream->RecvdFin()) {
+      mInputFrameDataStream->SetRecvdFin(true);
+      --mConcurrent;
+      ProcessPending();
     }
-    mFrameBufferUsed = 0;
-    mFrameDataStream = nsnull;
   }
-  
-  return;
+  mInputFrameBufferUsed = 0;
+  mInputFrameDataStream = nsnull;
 }
 
 void
 SpdySession::EnsureBuffer(nsAutoArrayPtr<char> &buf,
                           PRUint32 newSize,
                           PRUint32 preserve,
                           PRUint32 &objSize)
 {
   if (objSize >= newSize)
-    return;
+      return;
   
-  objSize = newSize;
+  // Leave a little slop on the new allocation - add 2KB to
+  // what we need and then round the result up to a 4KB (page)
+  // boundary.
+
+  objSize = (newSize + 2048 + 4095) & ~4095;
+  
   nsAutoArrayPtr<char> tmp(new char[objSize]);
-  memcpy (tmp, buf, preserve);
+  memcpy(tmp, buf, preserve);
   buf = tmp;
 }
 
 void
 SpdySession::zlibInit()
 {
   mDownstreamZlib.zalloc = SpdyStream::zlib_allocator;
   mDownstreamZlib.zfree = SpdyStream::zlib_destructor;
@@ -479,18 +499,18 @@ SpdySession::FindHeader(nsCString name,
     PR_ntohs(reinterpret_cast<PRUint16 *>(mDecompressBuffer.get())[0]);
   for (PRUint16 index = 0; index < numPairs; ++index) {
     if (lastHeaderByte < nvpair + 2)
       return NS_ERROR_ILLEGAL_VALUE;
     PRUint32 nameLen = (nvpair[0] << 8) + nvpair[1];
     if (lastHeaderByte < nvpair + 2 + nameLen)
       return NS_ERROR_ILLEGAL_VALUE;
     nsDependentCSubstring nameString =
-      Substring (reinterpret_cast<const char *>(nvpair) + 2,
-                 reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
+      Substring(reinterpret_cast<const char *>(nvpair) + 2,
+                reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
     if (lastHeaderByte < nvpair + 4 + nameLen)
       return NS_ERROR_ILLEGAL_VALUE;
     PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
     if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
       return NS_ERROR_ILLEGAL_VALUE;
     if (nameString.Equals(name)) {
       value.Assign(((char *)nvpair) + 4 + nameLen, valueLen);
       return NS_OK;
@@ -499,17 +519,16 @@ SpdySession::FindHeader(nsCString name,
   }
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 nsresult
 SpdySession::ConvertHeaders(nsDependentCSubstring &status,
                             nsDependentCSubstring &version)
 {
-
   mFlatHTTPResponseHeaders.Truncate();
   mFlatHTTPResponseHeadersOut = 0;
   mFlatHTTPResponseHeaders.SetCapacity(mDecompressBufferUsed + 64);
 
   // Connection, Keep-Alive and chunked transfer encodings are to be
   // removed.
 
   // Content-Length is 'advisory'.. we will not strip it because it can
@@ -535,79 +554,87 @@ SpdySession::ConvertHeaders(nsDependentC
     if (lastHeaderByte < nvpair + 2)
       return NS_ERROR_ILLEGAL_VALUE;
 
     PRUint32 nameLen = (nvpair[0] << 8) + nvpair[1];
     if (lastHeaderByte < nvpair + 2 + nameLen)
       return NS_ERROR_ILLEGAL_VALUE;
 
     nsDependentCSubstring nameString =
-      Substring (reinterpret_cast<const char *>(nvpair) + 2,
-                 reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
-
-    // a null in the name string is particularly wrong because it will
-    // break the fix-up-nulls-in-value-string algorithm.
-    if (nameString.FindChar(0) != -1)
-      return NS_ERROR_ILLEGAL_VALUE;
+      Substring(reinterpret_cast<const char *>(nvpair) + 2,
+                reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
 
     if (lastHeaderByte < nvpair + 4 + nameLen)
       return NS_ERROR_ILLEGAL_VALUE;
-    PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
-    if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
-      return NS_ERROR_ILLEGAL_VALUE;
-    
+
+    // Look for illegal characters in the nameString.
+    // This includes upper case characters and nulls (as they will
+    // break the fixup-nulls-in-value-string algorithm)
     // Look for upper case characters in the name. They are illegal.
     for (char *cPtr = nameString.BeginWriting();
          cPtr && cPtr < nameString.EndWriting();
          ++cPtr) {
       if (*cPtr <= 'Z' && *cPtr >= 'A') {
         nsCString toLog(nameString);
 
         LOG3(("SpdySession::ConvertHeaders session=%p stream=%p "
               "upper case response header found. [%s]\n",
-              this, mFrameDataStream, toLog.get()));
+              this, mInputFrameDataStream, toLog.get()));
 
         return NS_ERROR_ILLEGAL_VALUE;
       }
+
+      // check for null characters
+      if (*cPtr == '\0')
+        return NS_ERROR_ILLEGAL_VALUE;
     }
 
     // HTTP Chunked responses are not legal over spdy. We do not need
     // to look for chunked specifically because it is the only HTTP
     // allowed default encoding and we did not negotiate further encodings
     // via TE
     if (nameString.Equals(NS_LITERAL_CSTRING("transfer-encoding"))) {
       LOG3(("SpdySession::ConvertHeaders session=%p stream=%p "
             "transfer-encoding found. Chunked is invalid and no TE sent.",
-            this, mFrameDataStream));
+            this, mInputFrameDataStream));
 
       return NS_ERROR_ILLEGAL_VALUE;
     }
 
+    PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
+    if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
+      return NS_ERROR_ILLEGAL_VALUE;
+
     if (!nameString.Equals(NS_LITERAL_CSTRING("version")) &&
         !nameString.Equals(NS_LITERAL_CSTRING("status")) &&
         !nameString.Equals(NS_LITERAL_CSTRING("connection")) &&
         !nameString.Equals(NS_LITERAL_CSTRING("keep-alive"))) {
       nsDependentCSubstring valueString =
-        Substring (reinterpret_cast<const char *>(nvpair) + 4 + nameLen,
-                   reinterpret_cast<const char *>(nvpair) + 4 + nameLen +
-                   valueLen);
+        Substring(reinterpret_cast<const char *>(nvpair) + 4 + nameLen,
+                  reinterpret_cast<const char *>(nvpair) + 4 + nameLen +
+                  valueLen);
       
       mFlatHTTPResponseHeaders.Append(nameString);
       mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(": "));
 
-      PRInt32 valueIndex;
-      // NULLs are really "\r\nhdr: "
-      while ((valueIndex = valueString.FindChar(0)) != -1) {
-        nsCString replacement = NS_LITERAL_CSTRING("\r\n");
-        replacement.Append(nameString);
-        replacement.Append(NS_LITERAL_CSTRING(": "));
-        valueString.Replace(valueIndex, 1, replacement);
+      // expand NULL bytes in the value string
+      for (char *cPtr = valueString.BeginWriting();
+           cPtr && cPtr < valueString.EndWriting();
+           ++cPtr) {
+        if (*cPtr != 0) {
+          mFlatHTTPResponseHeaders.Append(*cPtr);
+          continue;
+        }
+
+        // NULLs are really "\r\nhdr: "
+        mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
+        mFlatHTTPResponseHeaders.Append(nameString);
+        mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(": "));
       }
 
-      mFlatHTTPResponseHeaders.Append(valueString);
       mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
     }
     nvpair += 4 + nameLen + valueLen;
   }
 
   mFlatHTTPResponseHeaders.Append(
     NS_LITERAL_CSTRING("X-Firefox-Spdy: 1\r\n\r\n"));
   LOG (("decoded response headers are:\n%s",
@@ -632,17 +659,17 @@ SpdySession::GeneratePing(PRUint32 aID)
   packet[2] = 0;
   packet[3] = CONTROL_TYPE_PING;
   packet[4] = 0;                                  /* flags */
   packet[5] = 0;
   packet[6] = 0;
   packet[7] = 4;                                  /* length */
   
   aID = PR_htonl(aID);
-  memcpy (packet + 8, &aID, 4);
+  memcpy(packet + 8, &aID, 4);
 
   FlushOutputQueue();
 }
 
 void
 SpdySession::GenerateRstStream(PRUint32 aStatusCode, PRUint32 aID)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
@@ -658,35 +685,35 @@ SpdySession::GenerateRstStream(PRUint32 
   packet[2] = 0;
   packet[3] = CONTROL_TYPE_RST_STREAM;
   packet[4] = 0;                                  /* flags */
   packet[5] = 0;
   packet[6] = 0;
   packet[7] = 8;                                  /* length */
   
   aID = PR_htonl(aID);
-  memcpy (packet + 8, &aID, 4);
+  memcpy(packet + 8, &aID, 4);
   aStatusCode = PR_htonl(aStatusCode);
-  memcpy (packet + 12, &aStatusCode, 4);
+  memcpy(packet + 12, &aStatusCode, 4);
 
   FlushOutputQueue();
 }
 
 void
 SpdySession::GenerateGoAway()
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   LOG3(("SpdySession::GenerateGoAway %p\n", this));
 
   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
                mOutputQueueUsed, mOutputQueueSize);
   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
   mOutputQueueUsed += 12;
 
-  memset (packet, 0, 12);
+  memset(packet, 0, 12);
   packet[0] = kFlag_Control;
   packet[1] = 2;                                  /* version 2 */
   packet[3] = CONTROL_TYPE_GOAWAY;
   packet[7] = 4;                                  /* data length */
   
   // last-good-stream-id are bytes 8-11, when we accept server push this will
   // need to be set non zero
 
@@ -695,40 +722,28 @@ SpdySession::GenerateGoAway()
 
 void
 SpdySession::CleanupStream(SpdyStream *aStream, nsresult aResult)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   LOG3(("SpdySession::CleanupStream %p %p 0x%x %X\n",
         this, aStream, aStream->StreamID(), aResult));
 
-  nsresult abortCode = NS_OK;
-
   if (!aStream->RecvdFin() && aStream->StreamID()) {
     LOG3(("Stream had not processed recv FIN, sending RST"));
     GenerateRstStream(RST_CANCEL, aStream->StreamID());
     --mConcurrent;
     ProcessPending();
   }
   
-  // Check if partial frame writer
-  if (mPartialFrame == aStream) {
-    LOG3(("Stream had active partial write frame - need to abort session"));
-    abortCode = aResult;
-    if (NS_SUCCEEDED(abortCode))
-      abortCode = NS_ERROR_ABORT;
-    
-    mPartialFrame = nsnull;
-  }
-  
   // Check if partial frame reader
-  if (aStream == mFrameDataStream) {
+  if (aStream == mInputFrameDataStream) {
     LOG3(("Stream had active partial read frame on close"));
-    ChangeDownstreamState(DISCARD_DATA_FRAME);
-    mFrameDataStream = nsnull;
+    ChangeDownstreamState(DISCARDING_DATA_FRAME);
+    mInputFrameDataStream = nsnull;
   }
 
   // check the streams blocked on write, this is linear but the list
   // should be pretty short.
   PRUint32 size = mReadyForWrite.GetSize();
   for (PRUint32 count = 0; count < size; ++count) {
     SpdyStream *stream = static_cast<SpdyStream *>(mReadyForWrite.PopFront());
     if (stream != aStream)
@@ -760,38 +775,36 @@ SpdySession::CleanupStream(SpdyStream *a
   // Send the stream the close() indication
   aStream->Close(aResult);
 
   // removing from the stream transaction hash will
   // delete the SpdyStream and drop the reference to
   // its transaction
   mStreamTransactionHash.Remove(aStream->Transaction());
 
-  if (NS_FAILED(abortCode))
-    Close(abortCode);
-  else if (mShouldGoAway && !mStreamTransactionHash.Count())
+  if (mShouldGoAway && !mStreamTransactionHash.Count())
     Close(NS_OK);
 }
 
 nsresult
 SpdySession::HandleSynStream(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SYN_STREAM,
                     "wrong control type");
   
-  if (self->mFrameDataSize < 18) {
+  if (self->mInputFrameDataSize < 18) {
     LOG3(("SpdySession::HandleSynStream %p SYN_STREAM too short data=%d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   PRUint32 streamID =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
   PRUint32 associatedID =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[3]);
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[3]);
 
   LOG3(("SpdySession::HandleSynStream %p recv SYN_STREAM (push) "
         "for ID 0x%X associated with 0x%X.",
         self, streamID, associatedID));
     
   if (streamID & 0x01) {                   // test for odd stream ID
     LOG3(("SpdySession::HandleSynStream %p recvd SYN_STREAM id must be even.",
           self));
@@ -802,105 +815,107 @@ SpdySession::HandleSynStream(SpdySession
 
   // Anytime we start using the high bit of stream ID (either client or server)
   // begin to migrate to a new session.
   if (streamID >= kMaxStreamID)
     self->mShouldGoAway = true;
 
   // Need to decompress the headers even though we aren't using them yet in
   // order to keep the compression context consistent for other syn_reply frames
-  nsresult rv = self->DownstreamUncompress(self->mFrameBuffer + 18,
-                                           self->mFrameDataSize - 10);
+  nsresult rv = self->DownstreamUncompress(self->mInputFrameBuffer + 18,
+                                           self->mInputFrameDataSize - 10);
   if (NS_FAILED(rv)) {
     LOG(("SpdySession::HandleSynStream uncompress failed\n"));
     return rv;
   }
 
   // todo populate cache. For now, just reject server push p3
   self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
-  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  self->ResetDownstreamState();
   return NS_OK;
 }
 
 nsresult
 SpdySession::HandleSynReply(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SYN_REPLY,
                     "wrong control type");
 
-  if (self->mFrameDataSize < 8) {
+  if (self->mInputFrameDataSize < 8) {
     LOG3(("SpdySession::HandleSynReply %p SYN REPLY too short data=%d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
   
   PRUint32 streamID =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
-  self->mFrameDataStream = self->mStreamIDHash.Get(streamID);
-  if (!self->mFrameDataStream) {
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
+  self->mInputFrameDataStream = self->mStreamIDHash.Get(streamID);
+  if (!self->mInputFrameDataStream) {
     LOG3(("SpdySession::HandleSynReply %p lookup streamID in syn_reply "
           "0x%X failed. NextStreamID = 0x%x", self, streamID,
           self->mNextStreamID));
     if (streamID >= self->mNextStreamID)
       self->GenerateRstStream(RST_INVALID_STREAM, streamID);
     
     // It is likely that this is a reply to a stream ID that has been canceled.
     // For the most part we would like to ignore it, but the header needs to be
     // be parsed to keep the compression context synchronized
-    self->DownstreamUncompress(self->mFrameBuffer + 14,
-                               self->mFrameDataSize - 6);
-    self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    self->DownstreamUncompress(self->mInputFrameBuffer + 14,
+                               self->mInputFrameDataSize - 6);
+    self->ResetDownstreamState();
     return NS_OK;
   }
-  
-  self->mFrameDataStream->UpdateTransportReadEvents(self->mFrameDataSize);
 
-  if (!self->mFrameDataStream->SetFullyOpen()) {
+  self->mInputFrameDataStream->UpdateTransportReadEvents(
+    self->mInputFrameDataSize);
+
+  if (self->mInputFrameDataStream->GetFullyOpen()) {
     // "If an endpoint receives multiple SYN_REPLY frames for the same active
     // stream ID, it must drop the stream, and send a RST_STREAM for the
     // stream with the error PROTOCOL_ERROR."
     //
     // In addition to that we abort the session - this is a serious protocol
     // violation.
 
     self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
     return NS_ERROR_ILLEGAL_VALUE;
   }
+  self->mInputFrameDataStream->SetFullyOpen();
 
-  self->mFrameDataLast = self->mFrameBuffer[4] & kFlag_Data_FIN;
+  self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN;
 
-  if (self->mFrameBuffer[4] & kFlag_Data_UNI) {
+  if (self->mInputFrameBuffer[4] & kFlag_Data_UNI) {
     LOG3(("SynReply had unidirectional flag set on it - nonsensical"));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   LOG3(("SpdySession::HandleSynReply %p SYN_REPLY for 0x%X fin=%d",
-        self, streamID, self->mFrameDataLast));
+        self, streamID, self->mInputFrameDataLast));
   
   // The spdystream needs to see flattened http headers
   // The Frame Buffer currently holds the complete SYN_REPLY
   // frame. The interesting data is at offset 14, where the
   // compressed name/value header block lives.
   // We unpack that into the mDecompressBuffer - we can't do
   // it streamed because the version and status information
   // is not guaranteed to be first. This is then finally
   // converted to HTTP format in mFlatHTTPResponseHeaders
 
-  nsresult rv = self->DownstreamUncompress(self->mFrameBuffer + 14,
-                                           self->mFrameDataSize - 6);
+  nsresult rv = self->DownstreamUncompress(self->mInputFrameBuffer + 14,
+                                           self->mInputFrameDataSize - 6);
   if (NS_FAILED(rv)) {
     LOG(("SpdySession::HandleSynReply uncompress failed\n"));
     return rv;
   }
   
   Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE,
-                        self->mFrameDataSize - 6);
+                        self->mInputFrameDataSize - 6);
   if (self->mDecompressBufferUsed) {
     PRUint32 ratio =
-      (self->mFrameDataSize - 6) * 100 / self->mDecompressBufferUsed;
+      (self->mInputFrameDataSize - 6) * 100 / self->mDecompressBufferUsed;
     Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
   }
 
   // status and version are required.
   nsDependentCSubstring status, version;
   rv = self->FindHeader(NS_LITERAL_CSTRING("status"), status);
   if (NS_FAILED(rv))
     return rv;
@@ -918,84 +933,92 @@ SpdySession::HandleSynReply(SpdySession 
 }
 
 nsresult
 SpdySession::HandleRstStream(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_RST_STREAM,
                     "wrong control type");
 
-  if (self->mFrameDataSize != 8) {
+  if (self->mInputFrameDataSize != 8) {
     LOG3(("SpdySession::HandleRstStream %p RST_STREAM wrong length data=%d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
+  PRUint8 flags = reinterpret_cast<PRUint8 *>(self->mInputFrameBuffer.get())[4];
+
   PRUint32 streamID =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
 
   self->mDownstreamRstReason =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[3]);
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[3]);
+
+  LOG3(("SpdySession::HandleRstStream %p RST_STREAM Reason Code %u ID %x "
+        "flags %x", self, self->mDownstreamRstReason, streamID, flags));
 
-  LOG3(("SpdySession::HandleRstStream %p RST_STREAM Reason Code %u ID %x",
-        self, self->mDownstreamRstReason, streamID));
-
+  if (flags != 0) {
+    LOG3(("SpdySession::HandleRstStream %p RST_STREAM with flags is illegal",
+          self));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  
   if (self->mDownstreamRstReason == RST_INVALID_STREAM ||
       self->mDownstreamRstReason == RST_FLOW_CONTROL_ERROR) {
     // basically just ignore this
-    self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    self->ResetDownstreamState();
     return NS_OK;
   }
 
-  self->mFrameDataStream = self->mStreamIDHash.Get(streamID);
-  if (!self->mFrameDataStream) {
+  self->mInputFrameDataStream = self->mStreamIDHash.Get(streamID);
+  if (!self->mInputFrameDataStream) {
     LOG3(("SpdySession::HandleRstStream %p lookup streamID for RST Frame "
           "0x%X failed", self, streamID));
     return NS_ERROR_ILLEGAL_VALUE;
   }
-    
+
   self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
   return NS_OK;
 }
 
 nsresult
 SpdySession::HandleSettings(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SETTINGS,
                     "wrong control type");
 
-  if (self->mFrameDataSize < 4) {
+  if (self->mInputFrameDataSize < 4) {
     LOG3(("SpdySession::HandleSettings %p SETTINGS wrong length data=%d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   PRUint32 numEntries =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
 
   // Ensure frame is large enough for supplied number of entries
   // Each entry is 8 bytes, frame data is reduced by 4 to account for
   // the NumEntries value.
-  if ((self->mFrameDataSize - 4) < (numEntries * 8)) {
+  if ((self->mInputFrameDataSize - 4) < (numEntries * 8)) {
     LOG3(("SpdySession::HandleSettings %p SETTINGS wrong length data=%d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   LOG3(("SpdySession::HandleSettings %p SETTINGS Control Frame with %d entries",
         self, numEntries));
 
   for (PRUint32 index = 0; index < numEntries; ++index) {
     // To clarify the v2 spec:
     // Each entry is a 24 bits of a little endian id
     // followed by 8 bits of flags
     // followed by a 32 bit big endian value
     
     unsigned char *setting = reinterpret_cast<unsigned char *>
-      (self->mFrameBuffer.get()) + 12 + index * 8;
+      (self->mInputFrameBuffer.get()) + 12 + index * 8;
 
     PRUint32 id = (setting[2] << 16) + (setting[1] << 8) + setting[0];
     PRUint32 flags = setting[3];
     PRUint32 value =  PR_ntohl(reinterpret_cast<PRUint32 *>(setting)[1]);
 
     LOG3(("Settings ID %d, Flags %X, Value %d", id, flags, value));
 
     switch (id)
@@ -1030,131 +1053,131 @@ SpdySession::HandleSettings(SpdySession 
       break;
       
     default:
       break;
     }
     
   }
   
-  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  self->ResetDownstreamState();
   return NS_OK;
 }
 
 nsresult
 SpdySession::HandleNoop(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_NOOP,
                     "wrong control type");
 
-  if (self->mFrameDataSize != 0) {
+  if (self->mInputFrameDataSize != 0) {
     LOG3(("SpdySession::HandleNoop %p NOP had data %d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   LOG3(("SpdySession::HandleNoop %p NOP.", self));
 
-  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  self->ResetDownstreamState();
   return NS_OK;
 }
 
 nsresult
 SpdySession::HandlePing(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_PING,
                     "wrong control type");
 
-  if (self->mFrameDataSize != 4) {
+  if (self->mInputFrameDataSize != 4) {
     LOG3(("SpdySession::HandlePing %p PING had wrong amount of data %d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   PRUint32 pingID =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
 
   LOG3(("SpdySession::HandlePing %p PING ID 0x%X.", self, pingID));
 
   if (pingID & 0x01) {
     // We never expect to see an odd PING beacuse we never generate PING.
-      // The spec mandates ignoring this
+    // The spec mandates ignoring this
     LOG3(("SpdySession::HandlePing %p PING ID from server was odd.",
           self));
   }
   else {
     self->GeneratePing(pingID);
   }
     
-  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  self->ResetDownstreamState();
   return NS_OK;
 }
 
 nsresult
 SpdySession::HandleGoAway(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_GOAWAY,
                     "wrong control type");
 
-  if (self->mFrameDataSize != 4) {
+  if (self->mInputFrameDataSize != 4) {
     LOG3(("SpdySession::HandleGoAway %p GOAWAY had wrong amount of data %d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   self->mShouldGoAway = true;
   self->mGoAwayID =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
   self->mCleanShutdown = true;
   
   LOG3(("SpdySession::HandleGoAway %p GOAWAY Last-Good-ID 0x%X.",
         self, self->mGoAwayID));
-  self->ResumeRecv(self);
-  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  self->ResumeRecv();
+  self->ResetDownstreamState();
   return NS_OK;
 }
 
 nsresult
 SpdySession::HandleHeaders(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_HEADERS,
                     "wrong control type");
 
-  if (self->mFrameDataSize < 10) {
+  if (self->mInputFrameDataSize < 10) {
     LOG3(("SpdySession::HandleHeaders %p HEADERS had wrong amount of data %d",
-          self, self->mFrameDataSize));
+          self, self->mInputFrameDataSize));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   PRUint32 streamID =
-    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
 
   // this is actually not legal in the HTTP mapping of SPDY. All
   // headers are in the syn or syn reply. Log and ignore it.
 
   // in v3 this will be legal and we must remember to note
   // NS_NET_STATUS_RECEIVING_FROM from it
 
   LOG3(("SpdySession::HandleHeaders %p HEADERS for Stream 0x%X. "
         "They are ignored in the HTTP/SPDY mapping.",
         self, streamID));
-  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  self->ResetDownstreamState();
   return NS_OK;
 }
 
 nsresult
 SpdySession::HandleWindowUpdate(SpdySession *self)
 {
   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_WINDOW_UPDATE,
                     "wrong control type");
   LOG3(("SpdySession::HandleWindowUpdate %p WINDOW UPDATE was "
         "received. WINDOW UPDATE is no longer defined in v2. Ignoring.",
         self));
 
-  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  self->ResetDownstreamState();
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
 // of these methods
 //-----------------------------------------------------------------------------
 
@@ -1224,109 +1247,87 @@ SpdySession::ReadSegments(nsAHttpSegment
   *countRead = 0;
 
   // First priority goes to frames that were writing to the network but were
   // blocked part way through. Then to frames that have no streams (e.g ping
   // reply) and then third to streams marked urgent (generally they have
   // window updates), and finally to streams generally
   // ready to send data frames (http requests).
 
-  LOG3(("SpdySession::ReadSegments %p partial frame stream=%p",
-        this, mPartialFrame));
+  LOG3(("SpdySession::ReadSegments %p", this));
 
-  SpdyStream *stream = mPartialFrame;
-  mPartialFrame = nsnull;
-
-  if (!stream)
-    stream = static_cast<SpdyStream *>(mUrgentForWrite.PopFront());
+  SpdyStream *stream;
+  
+  stream = static_cast<SpdyStream *>(mUrgentForWrite.PopFront());
   if (!stream)
     stream = static_cast<SpdyStream *>(mReadyForWrite.PopFront());
   if (!stream) {
     LOG3(("SpdySession %p could not identify a stream to write; suspending.",
           this));
     FlushOutputQueue();
-    SetWriteCallbacks(nsnull);
+    SetWriteCallbacks();
     return NS_BASE_STREAM_WOULD_BLOCK;
   }
   
   LOG3(("SpdySession %p will write from SpdyStream %p", this, stream));
 
   NS_ABORT_IF_FALSE(!mSegmentReader || !reader || (mSegmentReader == reader),
                     "Inconsistent Write Function Callback");
 
   if (reader)
     mSegmentReader = reader;
   rv = stream->ReadSegments(this, count, countRead);
 
+  // Not every permutation of stream->ReadSegents produces data (and therefore
+  // tries to flush the output queue) - SENDING_FIN_STREAM can be an example
+  // of that. But we might still have old data buffered that would be good
+  // to flush.
   FlushOutputQueue();
 
-  if (stream->BlockedOnWrite()) {
-
-    // We are writing a frame out, but it is blocked on the output stream.
-    // Make sure to service that stream next write because we can only
-    // multiplex between complete frames.
-
-    LOG3(("SpdySession::ReadSegments %p dealing with block on write", this));
-
-    NS_ABORT_IF_FALSE(!mPartialFrame, "partial frame should be empty");
-
-    mPartialFrame = stream;
-    SetWriteCallbacks(stream->Transaction());
-    return rv;
-  }
-
   if (stream->RequestBlockedOnRead()) {
     
     // We are blocked waiting for input - either more http headers or
     // any request body data. When more data from the request stream
     // becomes available the httptransaction will call conn->ResumeSend().
     
     LOG3(("SpdySession::ReadSegments %p dealing with block on read", this));
 
     // call readsegments again if there are other streams ready
     // to run in this session
-    if (WriteQueueSize())
+    if (GetWriteQueueSize())
       rv = NS_OK;
     else
       rv = NS_BASE_STREAM_WOULD_BLOCK;
-    SetWriteCallbacks(stream->Transaction());
+    SetWriteCallbacks();
     return rv;
   }
   
-  NS_ABORT_IF_FALSE(rv != NS_BASE_STREAM_WOULD_BLOCK,
-                    "Stream Would Block inconsistency");
-  
   if (NS_FAILED(rv)) {
     LOG3(("SpdySession::ReadSegments %p returning FAIL code %X",
           this, rv));
     return rv;
   }
   
   if (*countRead > 0) {
     LOG3(("SpdySession::ReadSegments %p stream=%p generated end of frame %d",
           this, stream, *countRead));
     mReadyForWrite.Push(stream);
-    SetWriteCallbacks(stream->Transaction());
+    SetWriteCallbacks();
     return rv;
   }
   
   LOG3(("SpdySession::ReadSegments %p stream=%p stream send complete",
         this, stream));
   
-  // in normal http this is done by nshttpconnection, but that class does not
-  // know which http transaction has made this state transition.
-  stream->Transaction()->
-    OnTransportStatus(mSocketTransport, nsISocketTransport::STATUS_WAITING_FOR,
-                      LL_ZERO);
   /* we now want to recv data */
-  mConnection->ResumeRecv(stream->Transaction());
+  ResumeRecv();
 
   // call readsegments again if there are other streams ready
   // to go in this session
-  SetWriteCallbacks(stream->Transaction());
+  SetWriteCallbacks();
 
   return rv;
 }
 
 // WriteSegments() is used to read data off the socket. Generally this is
 // just the SPDY frame header and from there the appropriate SPDYStream
 // is identified from the Stream-ID. The http transaction associated with
 // that read then pulls in the data directly, which it will feed to
@@ -1347,110 +1348,113 @@ SpdySession::WriteSegments(nsAHttpSegmen
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   
   nsresult rv;
   *countWritten = 0;
 
   if (mClosed)
     return NS_ERROR_FAILURE;
 
-  SetWriteCallbacks(nsnull);
+  SetWriteCallbacks();
   
   // We buffer all control frames and act on them in this layer.
   // We buffer the first 8 bytes of data frames (the header) but
   // the actual data is passed through unprocessed.
   
   if (mDownstreamState == BUFFERING_FRAME_HEADER) {
     // The first 8 bytes of every frame is header information that
     // we are going to want to strip before passing to http. That is
     // true of both control and data packets.
     
-    NS_ABORT_IF_FALSE(mFrameBufferUsed < 8,
+    NS_ABORT_IF_FALSE(mInputFrameBufferUsed < 8,
                       "Frame Buffer Used Too Large for State");
 
-    rv = writer->OnWriteSegment(mFrameBuffer + mFrameBufferUsed,
-                                8 - mFrameBufferUsed,
+    rv = writer->OnWriteSegment(mInputFrameBuffer + mInputFrameBufferUsed,
+                                8 - mInputFrameBufferUsed,
                                 countWritten);
     if (NS_FAILED(rv)) {
       LOG3(("SpdySession %p buffering frame header read failure %x\n",
             this, rv));
       // maybe just blocked reading from network
       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
-        ResumeRecv(nsnull);
+        ResumeRecv();
       return rv;
     }
 
     LogIO(this, nsnull, "Reading Frame Header",
-          mFrameBuffer + mFrameBufferUsed, *countWritten);
+          mInputFrameBuffer + mInputFrameBufferUsed, *countWritten);
 
-    mFrameBufferUsed += *countWritten;
+    mInputFrameBufferUsed += *countWritten;
 
-    if (mFrameBufferUsed < 8)
+    if (mInputFrameBufferUsed < 8)
     {
       LOG3(("SpdySession::WriteSegments %p "
             "BUFFERING FRAME HEADER incomplete size=%d",
-            this, mFrameBufferUsed));
+            this, mInputFrameBufferUsed));
       return rv;
     }
 
     // For both control and data frames the second 32 bit word of the header
     // is 8-flags, 24-length. (network byte order)
-    mFrameDataSize =
-      PR_ntohl(reinterpret_cast<PRUint32 *>(mFrameBuffer.get())[1]);
-    mFrameDataSize &= 0x00ffffff;
-    mFrameDataRead = 0;
+    mInputFrameDataSize =
+      PR_ntohl(reinterpret_cast<PRUint32 *>(mInputFrameBuffer.get())[1]);
+    mInputFrameDataSize &= 0x00ffffff;
+    mInputFrameDataRead = 0;
     
-    if (mFrameBuffer[0] & kFlag_Control) {
-      EnsureBuffer(mFrameBuffer, mFrameDataSize + 8, 8, mFrameBufferSize);
+    if (mInputFrameBuffer[0] & kFlag_Control) {
+      EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8,
+                   mInputFrameBufferSize);
       ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
       
       // The first 32 bit word of the header is
       // 1 ctrl - 15 version - 16 type
       PRUint16 version =
-        PR_ntohs(reinterpret_cast<PRUint16 *>(mFrameBuffer.get())[0]);
+        PR_ntohs(reinterpret_cast<PRUint16 *>(mInputFrameBuffer.get())[0]);
       version &= 0x7fff;
       
       mFrameControlType =
-        PR_ntohs(reinterpret_cast<PRUint16 *>(mFrameBuffer.get())[1]);
+        PR_ntohs(reinterpret_cast<PRUint16 *>(mInputFrameBuffer.get())[1]);
       
       LOG3(("SpdySession::WriteSegments %p - Control Frame Identified "
             "type %d version %d data len %d",
-            this, mFrameControlType, version, mFrameDataSize));
+            this, mFrameControlType, version, mInputFrameDataSize));
 
       if (mFrameControlType >= CONTROL_TYPE_LAST ||
           mFrameControlType <= CONTROL_TYPE_FIRST)
         return NS_ERROR_ILLEGAL_VALUE;
 
       // The protocol document says this value must be 1 even though this
       // is known as version 2.. Testing interop indicates that is a typo
       // in the protocol document
       if (version != 2) {
         return NS_ERROR_ILLEGAL_VALUE;
       }
     }
     else {
       ChangeDownstreamState(PROCESSING_DATA_FRAME);
 
       PRUint32 streamID =
-        PR_ntohl(reinterpret_cast<PRUint32 *>(mFrameBuffer.get())[0]);
-      mFrameDataStream = mStreamIDHash.Get(streamID);
-      if (!mFrameDataStream) {
+        PR_ntohl(reinterpret_cast<PRUint32 *>(mInputFrameBuffer.get())[0]);
+      mInputFrameDataStream = mStreamIDHash.Get(streamID);
+      if (!mInputFrameDataStream) {
         LOG3(("SpdySession::WriteSegments %p lookup streamID 0x%X failed. "
               "Next = 0x%x", this, streamID, mNextStreamID));
         if (streamID >= mNextStreamID)
           GenerateRstStream(RST_INVALID_STREAM, streamID);
-          ChangeDownstreamState(DISCARD_DATA_FRAME);
+        ChangeDownstreamState(DISCARDING_DATA_FRAME);
       }
-      mFrameDataLast = (mFrameBuffer[4] & kFlag_Data_FIN);
-      Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD, mFrameDataSize >> 10);
+      mInputFrameDataLast = (mInputFrameBuffer[4] & kFlag_Data_FIN);
+      Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
+                            mInputFrameDataSize >> 10);
       LOG3(("Start Processing Data Frame. "
             "Session=%p Stream ID 0x%x Stream Ptr %p Fin=%d Len=%d",
-            this, streamID, mFrameDataStream, mFrameDataLast, mFrameDataSize));
+            this, streamID, mInputFrameDataStream, mInputFrameDataLast,
+            mInputFrameDataSize));
 
-      if (mFrameBuffer[4] & kFlag_Data_ZLIB) {
+      if (mInputFrameBuffer[4] & kFlag_Data_ZLIB) {
         LOG3(("Data flag has ZLIB flag set which is not valid >=2 spdy"));
         return NS_ERROR_ILLEGAL_VALUE;
       }
     }
   }
 
   if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
     if (mDownstreamRstReason == RST_REFUSED_STREAM)
@@ -1462,105 +1466,118 @@ SpdySession::WriteSegments(nsAHttpSegmen
       rv = NS_ERROR_NET_INTERRUPT;
     else
       rv = NS_ERROR_ILLEGAL_VALUE;
 
     if (mDownstreamRstReason != RST_REFUSED_STREAM &&
         mDownstreamRstReason != RST_CANCEL)
       mShouldGoAway = true;
 
-    // mFrameDataStream is reset by ChangeDownstreamState
-    SpdyStream *stream = mFrameDataStream;
-    ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    // mInputFrameDataStream is reset by ChangeDownstreamState
+    SpdyStream *stream = mInputFrameDataStream;
+    ResetDownstreamState();
     CleanupStream(stream, rv);
     return NS_OK;
   }
 
   if (mDownstreamState == PROCESSING_DATA_FRAME ||
       mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
 
     mSegmentWriter = writer;
-    rv = mFrameDataStream->WriteSegments(this, count, countWritten);
+    rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
     mSegmentWriter = nsnull;
 
     if (rv == NS_BASE_STREAM_CLOSED) {
       // This will happen when the transaction figures out it is EOF, generally
       // due to a content-length match being made
-      SpdyStream *stream = mFrameDataStream;
-      if (mFrameDataRead == mFrameDataSize)
-        ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+      SpdyStream *stream = mInputFrameDataStream;
+      if (mInputFrameDataRead == mInputFrameDataSize)
+        ResetDownstreamState();
       CleanupStream(stream, NS_OK);
       NS_ABORT_IF_FALSE(!mNeedsCleanup, "double cleanup out of data frame");
       return NS_OK;
     }
     
     if (mNeedsCleanup) {
       CleanupStream(mNeedsCleanup, NS_OK);
       mNeedsCleanup = nsnull;
     }
 
     // In v3 this is where we would generate a window update
 
     return rv;
   }
 
-  if (mDownstreamState == DISCARD_DATA_FRAME) {
+  if (mDownstreamState == DISCARDING_DATA_FRAME) {
     char trash[4096];
-    PRUint32 count = NS_MIN(4096U, mFrameDataSize - mFrameDataRead);
+    PRUint32 count = NS_MIN(4096U, mInputFrameDataSize - mInputFrameDataRead);
 
     if (!count) {
-      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
-      *countWritten = 1;
-      return NS_OK;
+      ResetDownstreamState();
+      ResumeRecv();
+      return NS_BASE_STREAM_WOULD_BLOCK;
     }
 
     rv = writer->OnWriteSegment(trash, count, countWritten);
 
     if (NS_FAILED(rv)) {
       LOG3(("SpdySession %p discard frame read failure %x\n", this, rv));
       // maybe just blocked reading from network
       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
-        ResumeRecv(nsnull);
+        ResumeRecv();
       return rv;
     }
 
     LogIO(this, nsnull, "Discarding Frame", trash, *countWritten);
 
-    mFrameDataRead += *countWritten;
+    mInputFrameDataRead += *countWritten;
 
-    if (mFrameDataRead == mFrameDataSize)
-      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    if (mInputFrameDataRead == mInputFrameDataSize)
+      ResetDownstreamState();
     return rv;
   }
   
-  NS_ABORT_IF_FALSE(mDownstreamState == BUFFERING_CONTROL_FRAME,
-                    "Not in Bufering Control Frame State");
-  NS_ABORT_IF_FALSE(mFrameBufferUsed == 8,
+  if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
+    // this cannot happen
+    NS_ABORT_IF_FALSE(false, "Not in Bufering Control Frame State");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  NS_ABORT_IF_FALSE(mInputFrameBufferUsed == 8,
                     "Frame Buffer Header Not Present");
 
-  rv = writer->OnWriteSegment(mFrameBuffer + 8 + mFrameDataRead,
-                              mFrameDataSize - mFrameDataRead,
+  rv = writer->OnWriteSegment(mInputFrameBuffer + 8 + mInputFrameDataRead,
+                              mInputFrameDataSize - mInputFrameDataRead,
                               countWritten);
   if (NS_FAILED(rv)) {
     LOG3(("SpdySession %p buffering control frame read failure %x\n",
           this, rv));
     // maybe just blocked reading from network
     if (rv == NS_BASE_STREAM_WOULD_BLOCK)
-      ResumeRecv(nsnull);
+      ResumeRecv();
     return rv;
   }
 
   LogIO(this, nsnull, "Reading Control Frame",
-        mFrameBuffer + 8 + mFrameDataRead, *countWritten);
+        mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten);
 
-  mFrameDataRead += *countWritten;
+  mInputFrameDataRead += *countWritten;
 
-  if (mFrameDataRead != mFrameDataSize)
+  if (mInputFrameDataRead != mInputFrameDataSize)
     return NS_OK;
 
+  // This check is actually redundant, the control type was previously
+  // checked to make sure it was in range, but we will check it again
+  // at time of use to make sure a regression doesn't creep in.
+  if (mFrameControlType >= CONTROL_TYPE_LAST ||
+      mFrameControlType <= CONTROL_TYPE_FIRST) 
+  {
+    NS_ABORT_IF_FALSE(false, "control type out of range");
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
   rv = sControlFunctions[mFrameControlType](this);
 
   NS_ABORT_IF_FALSE(NS_FAILED(rv) ||
                     mDownstreamState != BUFFERING_CONTROL_FRAME,
                     "Control Handler returned OK but did not change state");
 
   if (mShouldGoAway && !mStreamTransactionHash.Count())
     Close(NS_OK);
@@ -1573,19 +1590,22 @@ SpdySession::Close(nsresult aReason)
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
   if (mClosed)
     return;
 
   LOG3(("SpdySession::Close %p %X", this, aReason));
 
   mClosed = true;
-  mStreamTransactionHash.Enumerate(Shutdown, this);
-  GenerateGoAway();
+  mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
+  if (NS_SUCCEEDED(aReason))
+    GenerateGoAway();
   mConnection = nsnull;
+  mSegmentReader = nsnull;
+  mSegmentWriter = nsnull;
 }
 
 void
 SpdySession::CloseTransaction(nsAHttpTransaction *aTransaction,
                               nsresult aResult)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   LOG3(("SpdySession::CloseTransaction %p %p %x", this, aTransaction, aResult));
@@ -1598,162 +1618,246 @@ SpdySession::CloseTransaction(nsAHttpTra
     LOG3(("SpdySession::CloseTransaction %p %p %x - not found.",
           this, aTransaction, aResult));
     return;
   }
   LOG3(("SpdySession::CloseTranscation probably a cancel. "
         "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p",
         this, aTransaction, aResult, stream->StreamID(), stream));
   CleanupStream(stream, aResult);
-  ResumeRecv(this);
+  ResumeRecv();
 }
 
 
 //-----------------------------------------------------------------------------
 // nsAHttpSegmentReader
 //-----------------------------------------------------------------------------
 
 nsresult
 SpdySession::OnReadSegment(const char *buf,
                            PRUint32 count,
                            PRUint32 *countRead)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   
   nsresult rv;
   
+  // If we can release old queued data then we can try and write the new
+  // data directly to the network without using the output queue at all
+  if (mOutputQueueUsed)
+    FlushOutputQueue();
+
   if (!mOutputQueueUsed && mSegmentReader) {
-
     // try and write directly without output queue
     rv = mSegmentReader->OnReadSegment(buf, count, countRead);
-    if (NS_SUCCEEDED(rv) || (rv != NS_BASE_STREAM_WOULD_BLOCK))
+
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+      *countRead = 0;
+    else if (NS_FAILED(rv))
       return rv;
+    
+    if (*countRead < count) {
+      PRUint32 required = count - *countRead;
+      // assuming a commitment() happened, this ensurebuffer is a nop
+      // but just in case the queuesize is too small for the required data
+      // call ensurebuffer().
+      EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
+      memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
+      mOutputQueueUsed = required;
+    }
+    
+    *countRead = count;
+    return NS_OK;
   }
-  
-  if (mOutputQueueUsed + count > mOutputQueueSize)
-    FlushOutputQueue();
+
+  // At this point we are going to buffer the new data in the output
+  // queue if it fits. By coalescing multiple small submissions into one larger
+  // buffer we can get larger writes out to the network later on.
 
-  if (mOutputQueueUsed + count > mOutputQueueSize)
-    count = mOutputQueueSize - mOutputQueueUsed;
+  // This routine should not be allowed to fill up the output queue
+  // all on its own - at least kQueueReserved bytes are always left
+  // for other routines to use - but this is an all-or-nothing function,
+  // so if it will not all fit just return WOULD_BLOCK
 
-  if (!count)
+  if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
     return NS_BASE_STREAM_WOULD_BLOCK;
   
   memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
   mOutputQueueUsed += count;
   *countRead = count;
 
   FlushOutputQueue();
-    
+
+  return NS_OK;
+}
+
+nsresult
+SpdySession::CommitToSegmentSize(PRUint32 count)
+{
+  if (mOutputQueueUsed)
+    FlushOutputQueue();
+
+  // would there be enough room to buffer this if needed?
+  if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
+    return NS_OK;
+  
+  // if we are using part of our buffers already, try again later
+  if (mOutputQueueUsed)
+    return NS_BASE_STREAM_WOULD_BLOCK;
+
+  // not enough room to buffer even with completely empty buffers.
+  // normal frames are max 4kb, so the only case this can really happen
+  // is a SYN_STREAM with technically unbounded headers. That is highly
+  // unlikely, but possible. Create enough room for it because the buffers
+  // will be necessary - SSL does not absorb writes of very large sizes
+  // in single sends.
+
+  EnsureBuffer(mOutputQueueBuffer, count + kQueueReserved, 0, mOutputQueueSize);
+
+  NS_ABORT_IF_FALSE((mOutputQueueUsed + count) <=
+                    (mOutputQueueSize - kQueueReserved),
+                    "buffer not as large as expected");
+
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsAHttpSegmentWriter
 //-----------------------------------------------------------------------------
 
 nsresult
 SpdySession::OnWriteSegment(char *buf,
                             PRUint32 count,
                             PRUint32 *countWritten)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
-  NS_ABORT_IF_FALSE(mSegmentWriter, "OnWriteSegment with null mSegmentWriter");
   nsresult rv;
 
+  if (!mSegmentWriter) {
+    // the only way this could happen would be if Close() were called on the
+    // stack with WriteSegments()
+    return NS_ERROR_FAILURE;
+  }
+  
   if (mDownstreamState == PROCESSING_DATA_FRAME) {
 
-    if (mFrameDataLast &&
-        mFrameDataRead == mFrameDataSize) {
+    if (mInputFrameDataLast &&
+        mInputFrameDataRead == mInputFrameDataSize) {
       // This will result in Close() being called
-      mNeedsCleanup = mFrameDataStream;
+      NS_ABORT_IF_FALSE(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
+      mNeedsCleanup = mInputFrameDataStream;
 
       LOG3(("SpdySession::OnWriteSegment %p - recorded downstream fin of "
-            "stream %p 0x%X", this, mFrameDataStream,
-            mFrameDataStream->StreamID()));
+            "stream %p 0x%X", this, mInputFrameDataStream,
+            mInputFrameDataStream->StreamID()));
       *countWritten = 0;
-      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+      ResetDownstreamState();
       return NS_BASE_STREAM_CLOSED;
     }
     
-    count = NS_MIN(count, mFrameDataSize - mFrameDataRead);
+    count = NS_MIN(count, mInputFrameDataSize - mInputFrameDataRead);
     rv = mSegmentWriter->OnWriteSegment(buf, count, countWritten);
     if (NS_FAILED(rv))
       return rv;
 
-    LogIO(this, mFrameDataStream, "Reading Data Frame", buf, *countWritten);
+    LogIO(this, mInputFrameDataStream, "Reading Data Frame",
+          buf, *countWritten);
 
-    mFrameDataRead += *countWritten;
+    mInputFrameDataRead += *countWritten;
     
-    mFrameDataStream->UpdateTransportReadEvents(*countWritten);
-    if ((mFrameDataRead == mFrameDataSize) && !mFrameDataLast)
-      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
+    if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameDataLast)
+      ResetDownstreamState();
 
     return rv;
   }
   
   if (mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
     
     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
-        mFrameDataLast) {
+        mInputFrameDataLast) {
       *countWritten = 0;
-      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+      ResetDownstreamState();
       return NS_BASE_STREAM_CLOSED;
     }
       
     count = NS_MIN(count,
                    mFlatHTTPResponseHeaders.Length() -
                    mFlatHTTPResponseHeadersOut);
     memcpy(buf,
            mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
            count);
     mFlatHTTPResponseHeadersOut += count;
     *countWritten = count;
 
     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
-        !mFrameDataLast)
-      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+        !mInputFrameDataLast)
+      ResetDownstreamState();
     return NS_OK;
   }
 
   return NS_ERROR_UNEXPECTED;
 }
 
 //-----------------------------------------------------------------------------
 // Modified methods of nsAHttpConnection
 //-----------------------------------------------------------------------------
 
 nsresult
-SpdySession::ResumeSend(nsAHttpTransaction *caller)
+SpdySession::ResumeSend()
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
-  LOG3(("SpdySession::ResumeSend %p caller=%p", this, caller));
-
-  // a trapped signal from the http transaction to the connection that
-  // it is no longer blocked on read.
+  LOG3(("SpdySession::ResumeSend %p", this));
 
   if (!mConnection)
     return NS_ERROR_FAILURE;
 
+  return mConnection->ResumeSend();
+}
+
+void
+SpdySession::TransactionHasDataToWrite(nsAHttpTransaction *caller)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  LOG3(("SpdySession::TransactionHasDataToWrite %p trans=%p", this, caller));
+
+  // a trapped signal from the http transaction to the connection that
+  // it is no longer blocked on read.
+
   SpdyStream *stream = mStreamTransactionHash.Get(caller);
-  if (stream)
-    mReadyForWrite.Push(stream);
-  else
-    LOG3(("SpdySession::ResumeSend %p caller %p not found", this, caller));
+  if (!stream) {
+    LOG3(("SpdySession::TransactionHasDataToWrite %p caller %p not found",
+          this, caller));
+    return;
+  }
   
-  return mConnection->ResumeSend(caller);
+  LOG3(("SpdySession::TransactionHasDataToWrite %p ID is %x",
+        this, stream->StreamID()));
+
+  mReadyForWrite.Push(stream);
+}
+
+void
+SpdySession::TransactionHasDataToWrite(SpdyStream *stream)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  LOG3(("SpdySession::TransactionHasDataToWrite %p stream=%p ID=%x",
+        this, stream, stream->StreamID()));
+
+  mReadyForWrite.Push(stream);
+  SetWriteCallbacks();
 }
 
 nsresult
-SpdySession::ResumeRecv(nsAHttpTransaction *caller)
+SpdySession::ResumeRecv()
 {
   if (!mConnection)
     return NS_ERROR_FAILURE;
 
-  return mConnection->ResumeRecv(caller);
+  return mConnection->ResumeRecv();
 }
 
 bool
 SpdySession::IsPersistent()
 {
   return true;
 }
 
@@ -1771,19 +1875,19 @@ SpdySession::TakeHttpConnection()
 {
   NS_ABORT_IF_FALSE(false, "TakeHttpConnection of SpdySession");
   return nsnull;
 }
 
 nsISocketTransport *
 SpdySession::Transport()
 {
-    if (!mConnection)
-        return nsnull;
-    return mConnection->Transport();
+  if (!mConnection)
+    return nsnull;
+  return mConnection->Transport();
 }
 
 //-----------------------------------------------------------------------------
 // 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
 //-----------------------------------------------------------------------------
 
@@ -1847,18 +1951,18 @@ SpdySession::Http1xTransactionCount()
 
 //-----------------------------------------------------------------------------
 // Pass through methods of nsAHttpConnection
 //-----------------------------------------------------------------------------
 
 nsAHttpConnection *
 SpdySession::Connection()
 {
-    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
-    return mConnection;
+  NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  return mConnection;
 }
 
 nsresult
 SpdySession::OnHeadersAvailable(nsAHttpTransaction *transaction,
                                 nsHttpRequestHead *requestHead,
                                 nsHttpResponseHead *responseHead,
                                 bool *reset)
 {
--- a/netwerk/protocol/http/SpdySession.h
+++ b/netwerk/protocol/http/SpdySession.h
@@ -127,21 +127,25 @@ public:
     SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE = 6, // percentage
     SETTINGS_TYPE_INITIAL_WINDOW = 7  // bytes. Not used in v2.
   };
 
   // This should be big enough to hold all of your control packets,
   // but if it needs to grow for huge headers it can do so dynamically.
   // About 1% of requests to SPDY google services seem to be > 1000
   // with all less than 2000.
-  const static PRUint32 kDefaultBufferSize = 2000;
+  const static PRUint32 kDefaultBufferSize = 2048;
 
-  const static PRUint32 kDefaultQueueSize =  16000;
-  const static PRUint32 kQueueTailRoom    =  4000;
-  const static PRUint32 kSendingChunkSize = 4000;
+  // kDefaultQueueSize must be >= other queue size constants
+  const static PRUint32 kDefaultQueueSize =  16384;
+  const static PRUint32 kQueueMinimumCleanup = 8192;
+  const static PRUint32 kQueueTailRoom    =  4096;
+  const static PRUint32 kQueueReserved    =  1024;
+
+  const static PRUint32 kSendingChunkSize = 4096;
   const static PRUint32 kDefaultMaxConcurrent = 100;
   const static PRUint32 kMaxStreamID = 0x7800000;
   
   static nsresult HandleSynStream(SpdySession *);
   static nsresult HandleSynReply(SpdySession *);
   static nsresult HandleRstStream(SpdySession *);
   static nsresult HandleSettings(SpdySession *);
   static nsresult HandleNoop(SpdySession *);
@@ -152,49 +156,59 @@ public:
 
   static void EnsureBuffer(nsAutoArrayPtr<char> &,
                            PRUint32, PRUint32, PRUint32 &);
 
   // For writing the SPDY data stream to LOG4
   static void LogIO(SpdySession *, SpdyStream *, const char *,
                     const char *, PRUint32);
 
+  // an overload of nsAHttpConnection
+  void TransactionHasDataToWrite(nsAHttpTransaction *);
+
+  // a similar version for SpdyStream
+  void TransactionHasDataToWrite(SpdyStream *);
+
+  // an overload of nsAHttpSegementReader
+  virtual nsresult CommitToSegmentSize(PRUint32 size);
+  
 private:
 
   enum stateType {
     BUFFERING_FRAME_HEADER,
     BUFFERING_CONTROL_FRAME,
     PROCESSING_DATA_FRAME,
-    DISCARD_DATA_FRAME,
+    DISCARDING_DATA_FRAME,
     PROCESSING_CONTROL_SYN_REPLY,
     PROCESSING_CONTROL_RST_STREAM
   };
 
-  PRUint32    WriteQueueSize();
+  PRUint32    GetWriteQueueSize();
   void        ChangeDownstreamState(enum stateType);
+  void        ResetDownstreamState();
   nsresult    DownstreamUncompress(char *, PRUint32);
   void        zlibInit();
   nsresult    FindHeader(nsCString, nsDependentCSubstring &);
   nsresult    ConvertHeaders(nsDependentCSubstring &,
                              nsDependentCSubstring &);
   void        GeneratePing(PRUint32);
   void        GenerateRstStream(PRUint32, PRUint32);
   void        GenerateGoAway();
   void        CleanupStream(SpdyStream *, nsresult);
 
-  void        SetWriteCallbacks(nsAHttpTransaction *);
+  void        SetWriteCallbacks();
   void        FlushOutputQueue();
 
   bool        RoomForMoreConcurrent();
   void        ActivateStream(SpdyStream *);
   void        ProcessPending();
 
-  static PLDHashOperator Shutdown(nsAHttpTransaction *,
-                                  nsAutoPtr<SpdyStream> &,
-                                  void *);
+  static PLDHashOperator ShutdownEnumerator(nsAHttpTransaction *,
+                                            nsAutoPtr<SpdyStream> &,
+                                            void *);
 
   // This is intended to be nsHttpConnectionMgr:nsHttpConnectionHandle taken
   // from the first transaction on this session. That object contains the
   // pointer to the real network-level nsHttpConnection object.
   nsRefPtr<nsAHttpConnection> mConnection;
 
   // The underlying socket transport object is needed to propogate some events
   nsISocketTransport         *mSocketTransport;
@@ -225,46 +239,45 @@ private:
   nsDeque                                             mQueuedStreams;
 
   // UrgentForWrite is meant to carry window updates. They were defined in
   // the v2 spec but apparently never implemented so are now scheduled to
   // be removed. But they will be reintroduced for v3, so we will leave
   // this queue in place to ease that transition.
   nsDeque           mUrgentForWrite;
 
-  // If we block while wrting out a frame then this points to the stream
-  // that was blocked. When writing again that stream must be the first
-  // one to write. It is null if there is not a partial frame.
-  SpdyStream        *mPartialFrame;
-
   // Compression contexts for header transport using deflate.
   // SPDY compresses only HTTP headers and does not reset zlib in between
   // frames.
   z_stream            mDownstreamZlib;
   z_stream            mUpstreamZlib;
 
-  // mFrameBuffer is used to store received control packets and the 8 bytes
+  // mInputFrameBuffer is used to store received control packets and the 8 bytes
   // of header on data packets
-  PRUint32             mFrameBufferSize;
-  PRUint32             mFrameBufferUsed;
-  nsAutoArrayPtr<char> mFrameBuffer;
+  PRUint32             mInputFrameBufferSize;
+  PRUint32             mInputFrameBufferUsed;
+  nsAutoArrayPtr<char> mInputFrameBuffer;
   
-  // mFrameDataSize/Read are used for tracking the amount of data consumed
+  // mInputFrameDataSize/Read are used for tracking the amount of data consumed
   // in a data frame. the data itself is not buffered in spdy
-  // The frame size is mFrameDataSize + the constant 8 byte header
-  PRUint32             mFrameDataSize;
-  PRUint32             mFrameDataRead;
-  bool                 mFrameDataLast; // This frame was marked FIN
+  // The frame size is mInputFrameDataSize + the constant 8 byte header
+  PRUint32             mInputFrameDataSize;
+  PRUint32             mInputFrameDataRead;
+  bool                 mInputFrameDataLast; // This frame was marked FIN
 
   // When a frame has been received that is addressed to a particular stream
   // (e.g. a data frame after the stream-id has been decoded), this points
   // to the stream.
-  SpdyStream          *mFrameDataStream;
+  SpdyStream          *mInputFrameDataStream;
   
-  // A state variable to cleanup a closed stream after the stack has unwound.
+  // mNeedsCleanup is a state variable to defer cleanup of a closed stream
+  // If needed, It is set in session::OnWriteSegments() and acted on and
+  // cleared when the stack returns to session::WriteSegments(). The stream
+  // cannot be destroyed directly out of OnWriteSegments because
+  // stream::writeSegments() is on the stack at that time.
   SpdyStream          *mNeedsCleanup;
 
   // The CONTROL_TYPE value for a control frame
   PRUint32             mFrameControlType;
 
   // This reason code in the last processed RESET frame
   PRUint32             mDownstreamRstReason;
 
@@ -275,17 +288,21 @@ private:
   PRUint32             mDecompressBufferSize;
   PRUint32             mDecompressBufferUsed;
   nsAutoArrayPtr<char> mDecompressBuffer;
 
   // for the conversion of downstream http headers into spdy formatted headers
   nsCString            mFlatHTTPResponseHeaders;
   PRUint32             mFlatHTTPResponseHeadersOut;
 
-  // when set, the session will go away when it reaches 0 streams
+  // when set, the session will go away when it reaches 0 streams. This flag
+  // is set when: the stream IDs are running out (at either the client or the
+  // server), when DontReuse() is called, a RST that is not specific to a
+  // particular stream is received, a GOAWAY frame has been received from
+  // the server.
   bool                 mShouldGoAway;
 
   // the session has received a nsAHttpTransaction::Close()  call
   bool                 mClosed;
 
   // the session received a GoAway frame with a valid GoAwayID
   bool                 mCleanShutdown;
 
--- a/netwerk/protocol/http/SpdyStream.cpp
+++ b/netwerk/protocol/http/SpdyStream.cpp
@@ -65,38 +65,35 @@ SpdyStream::SpdyStream(nsAHttpTransactio
     mTransaction(httpTransaction),
     mSession(spdySession),
     mSocketTransport(socketTransport),
     mSegmentReader(nsnull),
     mSegmentWriter(nsnull),
     mStreamID(0),
     mChunkSize(chunkSize),
     mSynFrameComplete(0),
-    mBlockedOnWrite(0),
     mRequestBlockedOnRead(0),
     mSentFinOnData(0),
     mRecvdFin(0),
     mFullyOpen(0),
     mSentWaitingFor(0),
-    mTxInlineFrameAllocation(SpdySession::kDefaultBufferSize),
-    mTxInlineFrameSize(0),
-    mTxInlineFrameSent(0),
+    mTxInlineFrameSize(SpdySession::kDefaultBufferSize),
+    mTxInlineFrameUsed(0),
     mTxStreamFrameSize(0),
-    mTxStreamFrameSent(0),
     mZlib(compressionContext),
-    mRequestBodyLen(0),
+    mRequestBodyLenRemaining(0),
     mPriority(priority),
     mTotalSent(0),
     mTotalRead(0)
 {
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
   LOG3(("SpdyStream::SpdyStream %p", this));
 
-  mTxInlineFrame = new char[mTxInlineFrameAllocation];
+  mTxInlineFrame = new char[mTxInlineFrameSize];
 }
 
 SpdyStream::~SpdyStream()
 {
 }
 
 // ReadSegments() is used to write data down the socket. Generally, HTTP
 // request data is pulled from the approriate transaction and
@@ -109,98 +106,114 @@ SpdyStream::ReadSegments(nsAHttpSegmentR
                          PRUint32 *countRead)
 {
   LOG3(("SpdyStream %p ReadSegments reader=%p count=%d state=%x",
         this, reader, count, mUpstreamState));
 
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   
   nsresult rv = NS_ERROR_UNEXPECTED;
-  mBlockedOnWrite = 0;
   mRequestBlockedOnRead = 0;
 
   switch (mUpstreamState) {
   case GENERATING_SYN_STREAM:
   case GENERATING_REQUEST_BODY:
   case SENDING_REQUEST_BODY:
     // Call into the HTTP Transaction to generate the HTTP request
     // stream. That stream will show up in OnReadSegment().
     mSegmentReader = reader;
     rv = mTransaction->ReadSegments(this, count, countRead);
     mSegmentReader = nsnull;
 
+    // Check to see if the transaction's request could be written out now.
+    // If not, mark the stream for callback when writing can proceed.
     if (NS_SUCCEEDED(rv) &&
         mUpstreamState == GENERATING_SYN_STREAM &&
         !mSynFrameComplete)
-      mBlockedOnWrite = 1;
+      mSession->TransactionHasDataToWrite(this);
     
-    // Mark that we are blocked on read if we the http transaction
-    // is going to get us going again.
-    if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mBlockedOnWrite)
+    // mTxinlineFrameUsed represents any queued un-sent frame. It might
+    // be 0 if there is no such frame, which is not a gurantee that we
+    // don't have more request body to send - just that any data that was
+    // sent comprised a complete SPDY frame. Likewise, a non 0 value is
+    // a queued, but complete, spdy frame length.
+
+    // Mark that we are blocked on read if the http transaction needs to
+    // provide more of the request message body and there is nothing queued
+    // for writing
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed)
       mRequestBlockedOnRead = 1;
 
-    if (!mBlockedOnWrite && NS_SUCCEEDED(rv) && (!*countRead)) {
-      LOG3(("ReadSegments %p Send Request data complete from %x",
+    if (!mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
+      LOG3(("ReadSegments %p: Sending request data complete, mUpstreamState=%x",
             this, mUpstreamState));
       if (mSentFinOnData) {
         ChangeState(UPSTREAM_COMPLETE);
       }
       else {
         GenerateDataFrameHeader(0, true);
         ChangeState(SENDING_FIN_STREAM);
-        mBlockedOnWrite = 1;
+        mSession->TransactionHasDataToWrite(this);
         rv = NS_BASE_STREAM_WOULD_BLOCK;
       }
     }
 
     break;
 
   case SENDING_SYN_STREAM:
-    // We were trying to send the SYN-STREAM but only got part of it out
-    // before being blocked. Try and send more.
+    // We were trying to send the SYN-STREAM but were blocked from trying
+    // to transmit it the first time(s).
     mSegmentReader = reader;
     rv = TransmitFrame(nsnull, nsnull);
     mSegmentReader = nsnull;
     *countRead = 0;
-    if (NS_SUCCEEDED(rv))
-      rv = NS_BASE_STREAM_WOULD_BLOCK;
-
-    if (!mTxInlineFrameSize) {
+    if (NS_SUCCEEDED(rv)) {
+      NS_ABORT_IF_FALSE(!mTxInlineFrameUsed,
+                        "Transmit Frame should be all or nothing");
+    
       if (mSentFinOnData) {
         ChangeState(UPSTREAM_COMPLETE);
         rv = NS_OK;
       }
       else {
+        rv = NS_BASE_STREAM_WOULD_BLOCK;
         ChangeState(GENERATING_REQUEST_BODY);
-        mBlockedOnWrite = 1;
+        mSession->TransactionHasDataToWrite(this);
       }
     }
     break;
 
   case SENDING_FIN_STREAM:
-    // We were trying to send the SYN-STREAM but only got part of it out
-    // before being blocked. Try and send more.
+    // We were trying to send the FIN-STREAM but were blocked from
+    // sending it out - try again.
     if (!mSentFinOnData) {
       mSegmentReader = reader;
       rv = TransmitFrame(nsnull, nsnull);
       mSegmentReader = nsnull;
-      if (!mTxInlineFrameSize)
+      NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
+                        "Transmit Frame should be all or nothing");
+      if (NS_SUCCEEDED(rv))
         ChangeState(UPSTREAM_COMPLETE);
     }
     else {
       rv = NS_OK;
-      mTxInlineFrameSize = 0;         // cancel fin data packet
+      mTxInlineFrameUsed = 0;         // cancel fin data packet
       ChangeState(UPSTREAM_COMPLETE);
     }
     
     *countRead = 0;
 
     // don't change OK to WOULD BLOCK. we are really done sending if OK
     break;
 
+  case UPSTREAM_COMPLETE:
+    *countRead = 0;
+    rv = NS_OK;
+    break;
+
   default:
     NS_ABORT_IF_FALSE(false, "SpdyStream::ReadSegments unknown state");
     break;
   }
 
   return rv;
 }
 
@@ -253,17 +266,17 @@ SpdyStream::ParseHttpRequestHeaders(cons
         this, avail, mUpstreamState));
 
   mFlatHttpRequestHeaders.Append(buf, avail);
 
   // We can use the simple double crlf because firefox is the
   // only client we are parsing
   PRInt32 endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
   
-  if (endHeader == -1) {
+  if (endHeader == kNotFound) {
     // We don't have all the headers yet
     LOG3(("SpdyStream::ParseHttpRequestHeaders %p "
           "Need more header bytes. Len = %d",
           this, mFlatHttpRequestHeaders.Length()));
     *countUsed = avail;
     return NS_OK;
   }
            
@@ -328,18 +341,16 @@ SpdyStream::ParseHttpRequestHeaders(cons
     mTxInlineFrame[16] = SpdySession::kPri01;
   else if (mPriority >= nsISupportsPriority::PRIORITY_HIGH)
     mTxInlineFrame[16] = SpdySession::kPri02;
   else
     mTxInlineFrame[16] = SpdySession::kPri03;
 
   mTxInlineFrame[17] = 0;                         /* unused */
   
-//  nsCString methodHeader;
-//  mTransaction->RequestHead()->Method()->ToUTF8String(methodHeader);
   const char *methodHeader = mTransaction->RequestHead()->Method().get();
 
   nsCString hostHeader;
   mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
 
   nsCString versionHeader;
   if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1)
     versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
@@ -401,24 +412,25 @@ SpdyStream::ParseHttpRequestHeaders(cons
                                         beginBuffer + crlfIndex);
     if (!val->IsEmpty())
       val->Append(static_cast<char>(0));
     val->Append(v);
 
     if (name.Equals("content-length")) {
       PRInt64 len;
       if (nsHttp::ParseInt64(val->get(), nsnull, &len))
-        mRequestBodyLen = len;
+        mRequestBodyLenRemaining = len;
     }
   }
   
-  mTxInlineFrameSize = 18;
+  mTxInlineFrameUsed = 18;
 
-  LOG3(("http request headers to encode are: \n%s",
-        mFlatHttpRequestHeaders.get()));
+  // Do not naively log the request headers here beacuse they might
+  // contain auth. The http transaction already logs the sanitized request
+  // headers at this same level so it is not necessary to do so here.
 
   // The header block length
   PRUint16 count = hdrHash.Count() + 4; /* method, scheme, url, version */
   CompressToFrame(count);
 
   // method, scheme, url, and version headers for request line
 
   CompressToFrame(NS_LITERAL_CSTRING("method"));
@@ -430,35 +442,36 @@ SpdyStream::ParseHttpRequestHeaders(cons
   CompressToFrame(NS_LITERAL_CSTRING("version"));
   CompressToFrame(versionHeader);
   
   hdrHash.Enumerate(hdrHashEnumerate, this);
   CompressFlushFrame();
   
   // 4 to 7 are length and flags, which we can now fill in
   (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
-    PR_htonl(mTxInlineFrameSize - 8);
+    PR_htonl(mTxInlineFrameUsed - 8);
 
   NS_ABORT_IF_FALSE(!mTxInlineFrame[4],
                     "Size greater than 24 bits");
   
   // For methods other than POST and PUT, we will set the fin bit
   // right on the syn stream packet.
 
   if (mTransaction->RequestHead()->Method() != nsHttp::Post &&
-      mTransaction->RequestHead()->Method() != nsHttp::Put) {
+      mTransaction->RequestHead()->Method() != nsHttp::Put &&
+      mTransaction->RequestHead()->Method() != nsHttp::Options) {
     mSentFinOnData = 1;
     mTxInlineFrame[4] = SpdySession::kFlag_Data_FIN;
   }
 
-  Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameSize - 18);
+  Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18);
 
   // The size of the input headers is approximate
   PRUint32 ratio =
-    (mTxInlineFrameSize - 18) * 100 /
+    (mTxInlineFrameUsed - 18) * 100 /
     (11 + mTransaction->RequestHead()->RequestURI().Length() +
      mFlatHttpRequestHeaders.Length());
   
   Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
   return NS_OK;
 }
 
 void
@@ -476,120 +489,132 @@ SpdyStream::UpdateTransportSendEvents(PR
 {
   mTotalSent += count;
 
   if (mUpstreamState != SENDING_FIN_STREAM)
     mTransaction->OnTransportStatus(mSocketTransport,
                                     NS_NET_STATUS_SENDING_TO,
                                     mTotalSent);
 
-  if (!mSentWaitingFor && !mRequestBodyLen &&
-      mTxInlineFrameSent == mTxInlineFrameSize  &&
-      mTxStreamFrameSent == mTxStreamFrameSize) {
+  if (!mSentWaitingFor && !mRequestBodyLenRemaining) {
     mSentWaitingFor = 1;
     mTransaction->OnTransportStatus(mSocketTransport,
                                     NS_NET_STATUS_WAITING_FOR,
                                     LL_ZERO);
   }
 }
 
 nsresult
 SpdyStream::TransmitFrame(const char *buf,
                           PRUint32 *countUsed)
 {
-  NS_ABORT_IF_FALSE(mTxInlineFrameSize, "empty stream frame in transmit");
+  // If TransmitFrame returns SUCCESS than all the data is sent (or at least
+  // buffered at the session level), if it returns WOULD_BLOCK then none of
+  // the data is sent.
+
+  // You can call this function with no data and no out parameter in order to
+  // flush internal buffers that were previously blocked on writing. You can
+  // of course feed new data to it as well.
+
+  NS_ABORT_IF_FALSE(mTxInlineFrameUsed, "empty stream frame in transmit");
   NS_ABORT_IF_FALSE(mSegmentReader, "TransmitFrame with null mSegmentReader");
-  
+  NS_ABORT_IF_FALSE((buf && countUsed) || (!buf && !countUsed),
+                    "TransmitFrame arguments inconsistent");
+
   PRUint32 transmittedCount;
   nsresult rv;
   
-  LOG3(("SpdyStream::TransmitFrame %p inline=%d of %d stream=%d of %d",
-        this, mTxInlineFrameSent, mTxInlineFrameSize,
-        mTxStreamFrameSent, mTxStreamFrameSize));
+  LOG3(("SpdyStream::TransmitFrame %p inline=%d stream=%d",
+        this, mTxInlineFrameUsed, mTxStreamFrameSize));
   if (countUsed)
     *countUsed = 0;
-  mBlockedOnWrite = 0;
 
   // In the (relatively common) event that we have a small amount of data
   // split between the inlineframe and the streamframe, then move the stream
   // data into the inlineframe via copy in order to coalesce into one write.
   // Given the interaction with ssl this is worth the small copy cost.
-  if (mTxStreamFrameSize && mTxInlineFrameSize &&
-      !mTxInlineFrameSent && !mTxStreamFrameSent &&
+  if (mTxStreamFrameSize && mTxInlineFrameUsed &&
       mTxStreamFrameSize < SpdySession::kDefaultBufferSize &&
-      mTxInlineFrameSize + mTxStreamFrameSize < mTxInlineFrameAllocation) {
+      mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) {
     LOG3(("Coalesce Transmit"));
-    memcpy (mTxInlineFrame + mTxInlineFrameSize,
+    memcpy (mTxInlineFrame + mTxInlineFrameUsed,
             buf, mTxStreamFrameSize);
     if (countUsed)
       *countUsed += mTxStreamFrameSize;
-    mTxInlineFrameSize += mTxStreamFrameSize;
-    mTxStreamFrameSent = 0;
+    mTxInlineFrameUsed += mTxStreamFrameSize;
     mTxStreamFrameSize = 0;
   }
 
+  rv = mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize +
+                                           mTxInlineFrameUsed);
+  if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+    mSession->TransactionHasDataToWrite(this);
+  if (NS_FAILED(rv))     // this will include WOULD_BLOCK
+    return rv;
+
   // This function calls mSegmentReader->OnReadSegment to report the actual SPDY
   // bytes through to the SpdySession and then the HttpConnection which calls
-  // the socket write function.
+  // the socket write function. It will accept all of the inline and stream
+  // data because of the above 'commitment' even if it has to buffer
   
-  while (mTxInlineFrameSent < mTxInlineFrameSize) {
-    rv = mSegmentReader->OnReadSegment(mTxInlineFrame + mTxInlineFrameSent,
-                                       mTxInlineFrameSize - mTxInlineFrameSent,
-                                       &transmittedCount);
-    LOG3(("SpdyStream::TransmitFrame for inline session=%p "
-          "stream=%p result %x len=%d",
-          mSession, this, rv, transmittedCount));
-    if (rv == NS_BASE_STREAM_WOULD_BLOCK)
-      mBlockedOnWrite = 1;
+  rv = mSegmentReader->OnReadSegment(mTxInlineFrame, mTxInlineFrameUsed,
+                                     &transmittedCount);
+  LOG3(("SpdyStream::TransmitFrame for inline session=%p "
+        "stream=%p result %x len=%d",
+        mSession, this, rv, transmittedCount));
 
-    if (NS_FAILED(rv))     // this will include WOULD_BLOCK
-      return rv;
+  NS_ABORT_IF_FALSE(rv != NS_BASE_STREAM_WOULD_BLOCK,
+                    "inconsistent inline commitment result");
+
+  if (NS_FAILED(rv))
+    return rv;
+
+  NS_ABORT_IF_FALSE(transmittedCount == mTxInlineFrameUsed,
+                    "inconsistent inline commitment count");
     
-    SpdySession::LogIO(mSession, this, "Writing from Inline Buffer",
-                       mTxInlineFrame + mTxInlineFrameSent,
-                       transmittedCount);
-
-    mTxInlineFrameSent += transmittedCount;
-  }
+  SpdySession::LogIO(mSession, this, "Writing from Inline Buffer",
+                     mTxInlineFrame, transmittedCount);
 
-  PRUint32 offset = 0;
-  NS_ABORT_IF_FALSE(mTxStreamFrameSize >= mTxStreamFrameSent,
-                    "negative unsent");
-  PRUint32 avail =  mTxStreamFrameSize - mTxStreamFrameSent;
+  if (mTxStreamFrameSize) {
+    if (!buf) {
+      // this cannot happen
+      NS_ABORT_IF_FALSE(false, "Stream transmit with null buf argument to "
+                        "TransmitFrame()");
+      LOG(("Stream transmit with null buf argument to TransmitFrame()\n"));
+      return NS_ERROR_UNEXPECTED;
+    }
 
-  while (avail) {
-    NS_ABORT_IF_FALSE(countUsed, "null countused pointer in a stream context");
-    rv = mSegmentReader->OnReadSegment(buf + offset, avail, &transmittedCount);
+    rv = mSegmentReader->OnReadSegment(buf, mTxStreamFrameSize,
+                                       &transmittedCount);
 
     LOG3(("SpdyStream::TransmitFrame for regular session=%p "
           "stream=%p result %x len=%d",
           mSession, this, rv, transmittedCount));
-    if (rv == NS_BASE_STREAM_WOULD_BLOCK)
-      mBlockedOnWrite = 1;
+  
+    NS_ABORT_IF_FALSE(rv != NS_BASE_STREAM_WOULD_BLOCK,
+                      "inconsistent stream commitment result");
 
-    if (NS_FAILED(rv))     // this will include WOULD_BLOCK
+    if (NS_FAILED(rv))
       return rv;
+
+    NS_ABORT_IF_FALSE(transmittedCount == mTxStreamFrameSize,
+                      "inconsistent stream commitment count");
     
     SpdySession::LogIO(mSession, this, "Writing from Transaction Buffer",
-                       buf + offset, transmittedCount);
+                       buf, transmittedCount);
 
-    *countUsed += transmittedCount;
-    avail -= transmittedCount;
-    offset += transmittedCount;
-    mTxStreamFrameSent += transmittedCount;
-    UpdateTransportSendEvents(transmittedCount);
+    *countUsed += mTxStreamFrameSize;
   }
+  
+  // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
+  UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
 
-  if (!avail) {
-    mTxInlineFrameSent = 0;
-    mTxInlineFrameSize = 0;
-    mTxStreamFrameSent = 0;
-    mTxStreamFrameSize = 0;
-  }
-    
+  mTxInlineFrameUsed = 0;
+  mTxStreamFrameSize = 0;
+
   return NS_OK;
 }
 
 void
 SpdyStream::ChangeState(enum stateType newState)
 {
   LOG3(("SpdyStream::ChangeState() %p from %X to %X",
         this, mUpstreamState, newState));
@@ -599,31 +624,29 @@ SpdyStream::ChangeState(enum stateType n
 
 void
 SpdyStream::GenerateDataFrameHeader(PRUint32 dataLength, bool lastFrame)
 {
   LOG3(("SpdyStream::GenerateDataFrameHeader %p len=%d last=%d",
         this, dataLength, lastFrame));
 
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
-  NS_ABORT_IF_FALSE(!mTxInlineFrameSize, "inline frame not empty");
-  NS_ABORT_IF_FALSE(!mTxInlineFrameSent, "inline partial send not 0");
+  NS_ABORT_IF_FALSE(!mTxInlineFrameUsed, "inline frame not empty");
   NS_ABORT_IF_FALSE(!mTxStreamFrameSize, "stream frame not empty");
-  NS_ABORT_IF_FALSE(!mTxStreamFrameSent, "stream partial send not 0");
   NS_ABORT_IF_FALSE(!(dataLength & 0xff000000), "datalength > 24 bits");
   
   (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[0] = PR_htonl(mStreamID);
   (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
     PR_htonl(dataLength);
   
   NS_ABORT_IF_FALSE(!(mTxInlineFrame[0] & 0x80),
                     "control bit set unexpectedly");
   NS_ABORT_IF_FALSE(!mTxInlineFrame[4], "flag bits set unexpectedly");
   
-  mTxInlineFrameSize = 8;
+  mTxInlineFrameUsed = 8;
   mTxStreamFrameSize = dataLength;
 
   if (lastFrame) {
     mTxInlineFrame[4] |= SpdySession::kFlag_Data_FIN;
     if (dataLength)
       mSentFinOnData = 1;
   }
 }
@@ -679,30 +702,30 @@ SpdyStream::zlib_destructor(void *opaque
 void
 SpdyStream::ExecuteCompress(PRUint32 flushMode)
 {
   // Expect mZlib->avail_in and mZlib->next_in to be set.
   // Append the compressed version of next_in to mTxInlineFrame
 
   do
   {
-    PRUint32 avail = mTxInlineFrameAllocation - mTxInlineFrameSize;
+    PRUint32 avail = mTxInlineFrameSize - mTxInlineFrameUsed;
     if (avail < 1) {
       SpdySession::EnsureBuffer(mTxInlineFrame,
-                                mTxInlineFrameAllocation + 2000,
-                                mTxInlineFrameSize,
-                                mTxInlineFrameAllocation);
-      avail = mTxInlineFrameAllocation - mTxInlineFrameSize;
+                                mTxInlineFrameSize + 2000,
+                                mTxInlineFrameUsed,
+                                mTxInlineFrameSize);
+      avail = mTxInlineFrameSize - mTxInlineFrameUsed;
     }
 
     mZlib->next_out = reinterpret_cast<unsigned char *> (mTxInlineFrame.get()) +
-      mTxInlineFrameSize;
+      mTxInlineFrameUsed;
     mZlib->avail_out = avail;
     deflate(mZlib, flushMode);
-    mTxInlineFrameSize += avail - mZlib->avail_out;
+    mTxInlineFrameUsed += avail - mZlib->avail_out;
   } while (mZlib->avail_in > 0 || !mZlib->avail_out);
 }
 
 void
 SpdyStream::CompressToFrame(PRUint16 data)
 {
   // convert the data to network byte order and write that
   // to the compressed stream
@@ -721,18 +744,17 @@ SpdyStream::CompressToFrame(const char *
   // Format calls for a network ordered 16 bit length
   // followed by the utf8 string
 
   // for now, silently truncate headers greater than 64KB. Spdy/3 will
   // fix this by making the len a 32 bit quantity
   if (len > 0xffff)
     len = 0xffff;
 
-  PRUint16 networkLen = len;
-  networkLen = PR_htons(len);
+  PRUint16 networkLen = PR_htons(len);
   
   // write out the length
   mZlib->next_in = reinterpret_cast<unsigned char *> (&networkLen);
   mZlib->avail_in = 2;
   ExecuteCompress(Z_NO_FLUSH);
   
   // write out the data
   mZlib->next_in = (unsigned char *)data;
@@ -782,61 +804,76 @@ SpdyStream::OnReadSegment(const char *bu
     // header bytes)
 
     rv = ParseHttpRequestHeaders(buf, count, countRead);
     if (NS_FAILED(rv))
       return rv;
     LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d",
           this, *countRead, count, mSynFrameComplete));
     if (mSynFrameComplete) {
-      NS_ABORT_IF_FALSE(mTxInlineFrameSize,
+      NS_ABORT_IF_FALSE(mTxInlineFrameUsed,
                         "OnReadSegment SynFrameComplete 0b");
       rv = TransmitFrame(nsnull, nsnull);
+      NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
+                        "Transmit Frame should be all or nothing");
+
+      // normalize a blocked write into an ok one if we have consumed the data
+      // while parsing headers as some code will take WOULD_BLOCK to mean an
+      // error with nothing processed.
+      // (e.g. nsHttpTransaction::ReadRequestSegment())
       if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
         rv = NS_OK;
-      if (mTxInlineFrameSize)
+
+      // mTxInlineFrameUsed > 0 means the current frame is in progress
+      // of sending.  mTxInlineFrameUsed is dropped to 0 after both the frame
+      // and its payload (if any) are completely sent out.  Here during
+      // GENERATING_SYN_STREAM state we are sending just the http headers.
+      // Only when the frame is completely sent out do we proceed to
+      // GENERATING_REQUEST_BODY state.
+
+      if (mTxInlineFrameUsed)
         ChangeState(SENDING_SYN_STREAM);
       else
         ChangeState(GENERATING_REQUEST_BODY);
       break;
     }
     NS_ABORT_IF_FALSE(*countRead == count,
                       "Header parsing not complete but unused data");
     break;
 
   case GENERATING_REQUEST_BODY:
-    NS_ABORT_IF_FALSE(!mTxInlineFrameSent,
-                      "OnReadSegment in generating_request_body with "
-                      "frame in progress");
-    
     dataLength = NS_MIN(count, mChunkSize);
     LOG3(("SpdyStream %p id %x request len remaining %d, "
           "count avail %d, chunk used %d",
-          this, mStreamID, mRequestBodyLen, count, dataLength));
-    if (dataLength > mRequestBodyLen)
+          this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
+    if (dataLength > mRequestBodyLenRemaining)
       return NS_ERROR_UNEXPECTED;
-    mRequestBodyLen -= dataLength;
-    GenerateDataFrameHeader(dataLength, !mRequestBodyLen);
+    mRequestBodyLenRemaining -= dataLength;
+    GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
     ChangeState(SENDING_REQUEST_BODY);
     // NO BREAK
 
   case SENDING_REQUEST_BODY:
-    NS_ABORT_IF_FALSE(mTxInlineFrameSize, "OnReadSegment Send Data Header 0b");
+    NS_ABORT_IF_FALSE(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b");
     rv = TransmitFrame(buf, countRead);
+    NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
+                      "Transmit Frame should be all or nothing");
+
     LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
-          "Header is %d/%d Body is %d/%d.",
-          rv, *countRead,
-          mTxInlineFrameSent, mTxInlineFrameSize,
-          mTxStreamFrameSent, mTxStreamFrameSize));
+          "Header is %d Body is %d.",
+          rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize));
 
+    // normalize a partial write with a WOULD_BLOCK into just a partial write
+    // as some code will take WOULD_BLOCK to mean an error with nothing
+    // written (e.g. nsHttpTransaction::ReadRequestSegment()
     if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
       rv = NS_OK;
 
     // If that frame was all sent, look for another one
-    if (!mTxInlineFrameSize)
+    if (!mTxInlineFrameUsed)
         ChangeState(GENERATING_REQUEST_BODY);
     break;
 
   case SENDING_SYN_STREAM:
     rv = NS_BASE_STREAM_WOULD_BLOCK;
     break;
 
   case SENDING_FIN_STREAM:
--- a/netwerk/protocol/http/SpdyStream.h
+++ b/netwerk/protocol/http/SpdyStream.h
@@ -49,39 +49,33 @@ class SpdyStream : public nsAHttpSegment
 {
 public:
   NS_DECL_NSAHTTPSEGMENTREADER
   NS_DECL_NSAHTTPSEGMENTWRITER
 
   SpdyStream(nsAHttpTransaction *,
              SpdySession *, nsISocketTransport *,
              PRUint32, z_stream *, PRInt32);
-  ~SpdyStream();
 
   PRUint32 StreamID() { return mStreamID; }
 
   nsresult ReadSegments(nsAHttpSegmentReader *,  PRUint32, PRUint32 *);
   nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *);
 
-  bool BlockedOnWrite()
-  {
-    return static_cast<bool>(mBlockedOnWrite);
-  }
-
   bool RequestBlockedOnRead()
   {
     return static_cast<bool>(mRequestBlockedOnRead);
   }
 
   // returns false if called more than once
-  bool SetFullyOpen()
+  bool GetFullyOpen() {return mFullyOpen;}
+  void SetFullyOpen() 
   {
-    bool result = !mFullyOpen;
+    NS_ABORT_IF_FALSE(!mFullyOpen, "SetFullyOpen already open");
     mFullyOpen = 1;
-    return result;
   }
 
   nsAHttpTransaction *Transaction()
   {
     return mTransaction;
   }
 
   void Close(nsresult reason);
@@ -95,47 +89,56 @@ public:
   // The zlib header compression dictionary defined by SPDY,
   // and hooks to the mozilla allocator for zlib to use.
   static const char *kDictionary;
   static void *zlib_allocator(void *, uInt, uInt);
   static void zlib_destructor(void *, void *);
 
 private:
 
+  // a SpdyStream object is only destroyed by being removed from the
+  // SpdySession mStreamTransactionHash - make the dtor private to
+  // just the AutoPtr implementation needed for that hash.
+  friend class nsAutoPtr<SpdyStream>;
+  ~SpdyStream();
+
   enum stateType {
     GENERATING_SYN_STREAM,
     SENDING_SYN_STREAM,
     GENERATING_REQUEST_BODY,
     SENDING_REQUEST_BODY,
     SENDING_FIN_STREAM,
     UPSTREAM_COMPLETE
   };
 
   static PLDHashOperator hdrHashEnumerate(const nsACString &,
                                           nsAutoPtr<nsCString> &,
                                           void *);
 
-  void     ChangeState(enum stateType );
+  void     ChangeState(enum stateType);
   nsresult ParseHttpRequestHeaders(const char *, PRUint32, PRUint32 *);
   nsresult TransmitFrame(const char *, PRUint32 *);
   void     GenerateDataFrameHeader(PRUint32, bool);
 
   void     CompressToFrame(const nsACString &);
   void     CompressToFrame(const nsACString *);
   void     CompressToFrame(const char *, PRUint32);
   void     CompressToFrame(PRUint16);
   void     CompressFlushFrame();
   void     ExecuteCompress(PRUint32);
   
   // Each stream goes from syn_stream to upstream_complete, perhaps
   // looping on multiple instances of generating_request_body and
   // sending_request_body for each SPDY chunk in the upload.
   enum stateType mUpstreamState;
 
-  // The underlying HTTP transaction
+  // The underlying HTTP transaction. This pointer is used as the key
+  // in the SpdySession mStreamTransactionHash so it is important to
+  // keep a reference to it as long as this stream is a member of that hash.
+  // (i.e. don't change it or release it after it is set in the ctor).
   nsRefPtr<nsAHttpTransaction> mTransaction;
 
   // The session that this stream is a subset of
   SpdySession                *mSession;
 
   // The underlying socket transport object is needed to propogate some events
   nsISocketTransport         *mSocketTransport;
 
@@ -149,21 +152,16 @@ private:
   PRUint32                    mStreamID;
 
   // The quanta upstream data frames are chopped into
   PRUint32                    mChunkSize;
 
   // Flag is set when all http request headers have been read
   PRUint32                     mSynFrameComplete     : 1;
 
-  // Flag is set when there is more request data to send and the
-  // stream needs to be rescheduled for writing. Sometimes this
-  // is done as a matter of fairness, not really due to blocking
-  PRUint32                     mBlockedOnWrite       : 1;
-
   // Flag is set when the HTTP processor has more data to send
   // but has blocked in doing so.
   PRUint32                     mRequestBlockedOnRead : 1;
 
   // Flag is set when a FIN has been placed on a data or syn packet
   // (i.e after the client has closed)
   PRUint32                     mSentFinOnData        : 1;
 
@@ -175,38 +173,36 @@ private:
   PRUint32                     mFullyOpen            : 1;
 
   // Flag is set after the WAITING_FOR Transport event has been generated
   PRUint32                     mSentWaitingFor       : 1;
 
   // The InlineFrame and associated data is used for composing control
   // frames and data frame headers.
   nsAutoArrayPtr<char>         mTxInlineFrame;
-  PRUint32                     mTxInlineFrameAllocation;
   PRUint32                     mTxInlineFrameSize;
-  PRUint32                     mTxInlineFrameSent;
+  PRUint32                     mTxInlineFrameUsed;
 
-  // mTxStreamFrameSize and mTxStreamFrameSent track the progress of
+  // mTxStreamFrameSize tracks the progress of
   // transmitting a request body data frame. The data frame itself
   // is never copied into the spdy layer.
   PRUint32                     mTxStreamFrameSize;
-  PRUint32                     mTxStreamFrameSent;
 
   // Compression context and buffer for request header compression.
   // This is a copy of SpdySession::mUpstreamZlib because it needs
   //  to remain the same in all streams of a session.
   z_stream                     *mZlib;
   nsCString                    mFlatHttpRequestHeaders;
 
   // Track the content-length of a request body so that we can
   // place the fin flag on the last data packet instead of waiting
   // for a stream closed indication. Relying on stream close results
   // in an extra 0-length runt packet and seems to have some interop
   // problems with the google servers.
-  PRInt64                      mRequestBodyLen;
+  PRInt64                      mRequestBodyLenRemaining;
 
   // based on nsISupportsPriority definitions
   PRInt32                      mPriority;
 
   // For Progress Events
   PRUint64                     mTotalSent;
   PRUint64                     mTotalRead;
 };
--- a/netwerk/protocol/http/nsAHttpConnection.h
+++ b/netwerk/protocol/http/nsAHttpConnection.h
@@ -72,19 +72,30 @@ public:
                                         nsHttpResponseHead *,
                                         bool *reset) = 0;
 
     //
     // called by a transaction to resume either sending or receiving data
     // after a transaction returned NS_BASE_STREAM_WOULD_BLOCK from its
     // ReadSegments/WriteSegments methods.
     //
-    virtual nsresult ResumeSend(nsAHttpTransaction *caller) = 0;
-    virtual nsresult ResumeRecv(nsAHttpTransaction *caller) = 0;
+    virtual nsresult ResumeSend() = 0;
+    virtual nsresult ResumeRecv() = 0;
 
+    // After a connection has had ResumeSend() called by a transaction,
+    // and it is ready to write to the network it may need to know the
+    // transaction that has data to write. This is only an issue for
+    // multiplexed protocols like SPDY - plain HTTP or pipelined HTTP
+    // implicitly have this information in a 1:1 relationship with the
+    // transaction(s) they manage.
+    virtual void TransactionHasDataToWrite(nsAHttpTransaction *)
+    {
+        // by default do nothing - only multiplexed protocols need to overload
+        return;
+    }
     //
     // called by the connection manager to close a transaction being processed
     // by this connection.
     //
     // @param transaction
     //        the transaction being closed.
     // @param reason
     //        the reason for closing the transaction.  NS_BASE_STREAM_CLOSED
@@ -127,18 +138,18 @@ public:
 
     // Get the nsISocketTransport used by the connection without changing
     //  references or ownership.
     virtual nsISocketTransport *Transport() = 0;
 };
 
 #define NS_DECL_NSAHTTPCONNECTION \
     nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, bool *reset); \
-    nsresult ResumeSend(nsAHttpTransaction *); \
-    nsresult ResumeRecv(nsAHttpTransaction *); \
+    nsresult ResumeSend(); \
+    nsresult ResumeRecv(); \
     void CloseTransaction(nsAHttpTransaction *, nsresult); \
     void GetConnectionInfo(nsHttpConnectionInfo **); \
     nsresult TakeTransport(nsISocketTransport **,    \
                            nsIAsyncInputStream **,   \
                            nsIAsyncOutputStream **); \
     void GetSecurityInfo(nsISupports **); \
     bool IsPersistent(); \
     bool IsReused(); \
--- a/netwerk/protocol/http/nsAHttpTransaction.h
+++ b/netwerk/protocol/http/nsAHttpTransaction.h
@@ -126,16 +126,30 @@ public:
 
 class nsAHttpSegmentReader
 {
 public:
     // any returned failure code stops segment iteration
     virtual nsresult OnReadSegment(const char *segment,
                                    PRUint32 count,
                                    PRUint32 *countRead) = 0;
+
+    // Ask the segment reader to commit to accepting size bytes of
+    // data from subsequent OnReadSegment() calls or throw hard
+    // (i.e. not wouldblock) exceptions. Implementations
+    // can return NS_ERROR_FAILURE if they never make commitments of that size
+    // (the default), NS_BASE_STREAM_WOULD_BLOCK if they cannot make
+    // the commitment now but might in the future, or NS_OK
+    // if they make the commitment.
+    //
+    // Spdy uses this to make sure frames are atomic.
+    virtual nsresult CommitToSegmentSize(PRUint32 size)
+    {
+        return NS_ERROR_FAILURE;
+    }
 };
 
 #define NS_DECL_NSAHTTPSEGMENTREADER \
     nsresult OnReadSegment(const char *, PRUint32, PRUint32 *);
 
 //-----------------------------------------------------------------------------
 // nsAHttpSegmentWriter
 //-----------------------------------------------------------------------------
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -4126,17 +4126,17 @@ nsHttpChannel::OnStartRequest(nsIRequest
                  "If we have both pumps, the cache content must be partial");
 
     if (!mSecurityInfo && !mCachePump && mTransaction) {
         // grab the security info from the connection object; the transaction
         // is guaranteed to own a reference to the connection.
         mSecurityInfo = mTransaction->SecurityInfo();
     }
 
-    if (gHttpHandler->IsSpdyEnabled() && !mCachePump && NS_FAILED(mStatus) &&
+    if (!mCachePump && NS_FAILED(mStatus) &&
         (mLoadFlags & LOAD_REPLACE) && mOriginalURI && mAllowSpdy) {
         // For sanity's sake we may want to cancel an alternate protocol
         // redirection involving the original host name
 
         nsCAutoString hostPort;
         if (NS_SUCCEEDED(mOriginalURI->GetHostPort(hostPort)))
             gHttpHandler->ConnMgr()->RemoveSpdyAlternateProtocol(hostPort);
     }
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -173,18 +173,23 @@ nsHttpConnection::Init(nsHttpConnectionI
 bool
 nsHttpConnection::EnsureNPNComplete()
 {
     // NPN is only used by SPDY right now.
     //
     // If for some reason the components to check on NPN aren't available,
     // this function will just return true to continue on and disable SPDY
 
-    NS_ABORT_IF_FALSE(mSocketTransport, "EnsureNPNComplete "
-                      "socket transport precondition");
+    if (!mSocketTransport) {
+        // this cannot happen
+        NS_ABORT_IF_FALSE(false,
+                          "EnsureNPNComplete socket transport precondition");
+        mNPNComplete = true;
+        return true;
+    }
 
     if (mNPNComplete)
         return true;
     
     nsresult rv;
 
     nsCOMPtr<nsISupports> securityInfo;
     nsCOMPtr<nsISSLSocketControl> ssl;
@@ -215,17 +220,22 @@ nsHttpConnection::EnsureNPNComplete()
         goto npnComplete;
 
     LOG(("nsHttpConnection::EnsureNPNComplete %p negotiated to '%s'",
          this, negotiatedNPN.get()));
     
     if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2"))) {
         mUsingSpdy = true;
         mEverUsedSpdy = true;
-        mIsReused = true;    /* all spdy streams are reused */
+
+        // Setting the connection as reused allows some transactions that fail
+        // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
+        // to handle clean rejections (such as those that arrived after
+        // a server goaway was generated).
+        mIsReused = true;
 
         // Wrap the old http transaction into the new spdy session
         // as the first stream
         mSpdySession = new SpdySession(mTransaction,
                                        mSocketTransport,
                                        mPriority);
         mTransaction = mSpdySession;
         mIdleTimeout = gHttpHandler->SpdyTimeout();
@@ -385,17 +395,17 @@ nsHttpConnection::AddTransaction(nsAHttp
                       "AddTransaction to idle http connection");
     
     if (!mSpdySession->AddStream(httpTransaction, priority)) {
         NS_ABORT_IF_FALSE(0, "AddStream should never fail due to"
                           "RoomForMore() admission check");
         return NS_ERROR_FAILURE;
     }
 
-    ResumeSend(httpTransaction);
+    ResumeSend();
 
     return NS_OK;
 }
 
 void
 nsHttpConnection::Close(nsresult reason)
 {
     LOG(("nsHttpConnection::Close [this=%x reason=%x]\n", this, reason));
@@ -435,17 +445,17 @@ nsHttpConnection::ProxyStartSSL()
 }
 
 void
 nsHttpConnection::DontReuse()
 {
     mKeepAliveMask = false;
     mKeepAlive = false;
     mIdleTimeout = 0;
-    if (mUsingSpdy)
+    if (mSpdySession)
         mSpdySession->DontReuse();
 }
 
 bool
 nsHttpConnection::CanReuse()
 {
     bool canReuse;
     
@@ -791,31 +801,31 @@ nsHttpConnection::PushBack(const char *d
         return NS_ERROR_UNEXPECTED;
     }
     
     mInputOverflow = new nsPreloadedStream(mSocketIn, data, length);
     return NS_OK;
 }
 
 nsresult
-nsHttpConnection::ResumeSend(nsAHttpTransaction *)
+nsHttpConnection::ResumeSend()
 {
     LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mSocketOut)
         return mSocketOut->AsyncWait(this, 0, 0, nsnull);
 
     NS_NOTREACHED("no socket output stream");
     return NS_ERROR_UNEXPECTED;
 }
 
 nsresult
-nsHttpConnection::ResumeRecv(nsAHttpTransaction *)
+nsHttpConnection::ResumeRecv()
 {
     LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mSocketIn)
         return mSocketIn->AsyncWait(this, 0, 0, nsnull);
 
@@ -969,20 +979,19 @@ nsHttpConnection::OnSocketWritable()
             // see the results of the handshake to know what bytes to send, so
             // we cannot proceed with the request headers.
 
             rv = NS_OK;
             mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
             n = 0;
         }
         else {
-            if (gHttpHandler->IsSpdyEnabled() && !mReportedSpdy) {
+            if (!mReportedSpdy) {
                 mReportedSpdy = true;
-                gHttpHandler->ConnMgr()->
-                    ReportSpdyConnection(this, mUsingSpdy);
+                gHttpHandler->ConnMgr()->ReportSpdyConnection(this, mUsingSpdy);
             }
 
             LOG(("  writing transaction request stream\n"));
             rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize, &n);
         }
 
         LOG(("  ReadSegments returned [rv=%x read=%u sock-cond=%x]\n",
             rv, n, mSocketOutCondition));
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -137,18 +137,18 @@ public:
                            nsIAsyncInputStream **,
                            nsIAsyncOutputStream **);
     void     GetSecurityInfo(nsISupports **);
     bool     IsPersistent() { return IsKeepAlive(); }
     bool     IsReused();
     void     SetIsReusedAfter(PRUint32 afterMilliseconds);
     void     SetIdleTimeout(PRUint16 val) {mIdleTimeout = val;}
     nsresult PushBack(const char *data, PRUint32 length);
-    nsresult ResumeSend(nsAHttpTransaction *caller);
-    nsresult ResumeRecv(nsAHttpTransaction *caller);
+    nsresult ResumeSend();
+    nsresult ResumeRecv();
     PRInt64  MaxBytesRead() {return mMaxBytesRead;}
 
     static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *,
                                     PRUint32, PRUint32, PRUint32 *);
 
     // When a persistent connection is in the connection manager idle 
     // connection pool, the nsHttpConnection still reads errors and hangups
     // on the socket so that it can be proactively released if the server
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -94,16 +94,17 @@ nsHttpConnectionMgr::nsHttpConnectionMgr
     , mIsShuttingDown(false)
     , mNumActiveConns(0)
     , mNumIdleConns(0)
     , mTimeOfNextWakeUp(LL_MAXUINT)
 {
     LOG(("Creating nsHttpConnectionMgr @%x\n", this));
     mCT.Init();
     mAlternateProtocolHash.Init(16);
+    mSpdyPreferredHash.Init();
 }
 
 nsHttpConnectionMgr::~nsHttpConnectionMgr()
 {
     LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
 }
 
 nsresult
@@ -140,17 +141,16 @@ nsHttpConnectionMgr::Init(PRUint16 maxCo
                           PRUint16 maxPersistConnsPerProxy,
                           PRUint16 maxRequestDelay,
                           PRUint16 maxPipelinedRequests)
 {
     LOG(("nsHttpConnectionMgr::Init\n"));
 
     {
         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-        mSpdyPreferredHash.Init();
 
         mMaxConns = maxConns;
         mMaxConnsPerHost = maxConnsPerHost;
         mMaxConnsPerProxy = maxConnsPerProxy;
         mMaxPersistConnsPerHost = maxPersistConnsPerHost;
         mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
         mMaxRequestDelay = maxRequestDelay;
         mMaxPipelinedRequests = maxPipelinedRequests;
@@ -412,18 +412,17 @@ nsHttpConnectionMgr::LookupConnectionEnt
 {
     if (!ci)
         return nsnull;
 
     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
     
     // If there is no sign of coalescing (or it is disabled) then just
     // return the primary hash lookup
-    if (!gHttpHandler->IsSpdyEnabled() || !gHttpHandler->CoalesceSpdy() ||
-        !ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
+    if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
         return ent;
 
     // If there is no preferred coalescing entry for this host (or the
     // preferred entry is the one that matched the mCT hash lookup) then
     // there is only option
     nsConnectionEntry *preferred = mSpdyPreferredHash.Get(ent->mCoalescingKey);
     if (!preferred || (preferred == ent))
         return ent;
@@ -462,74 +461,90 @@ nsHttpConnectionMgr::CloseIdleConnection
 
     conn->Close(NS_ERROR_ABORT);
     NS_RELEASE(conn);
     mNumIdleConns--;
     ConditionallyStopPruneDeadConnectionsTimer();
     return NS_OK;
 }
 
+// This function lets a connection, after completing the NPN phase,
+// report whether or not it is using spdy through the usingSpdy
+// argument. It would not be necessary if NPN were driven out of
+// the connection manager. The connection entry associated with the
+// connection is then updated to indicate whether or not we want to use
+// spdy with that host and update the preliminary preferred host
+// entries used for de-sharding hostsnames.
 void
 nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
                                           bool usingSpdy)
 {
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     
     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
                                                    conn, nsnull);
 
     NS_ABORT_IF_FALSE(ent, "no connection entry");
     if (!ent)
         return;
 
     ent->mTestedSpdy = true;
 
-    if (!usingSpdy) {
-        if (ent->mUsingSpdy)
-            conn->DontReuse();
+    if (!usingSpdy)
         return;
-    }
     
     ent->mUsingSpdy = true;
 
     PRUint32 ttl = conn->TimeToLive();
     PRUint64 timeOfExpire = NowInSeconds() + ttl;
     if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
         PruneDeadConnectionsAfter(ttl);
 
     // Lookup preferred directly from the hash instead of using
-    // GetSpdyPreferred() because we want to avoid the cert compatibility
+    // GetSpdyPreferredEnt() because we want to avoid the cert compatibility
     // check at this point because the cert is never part of the hash
     // lookup. Filtering on that has to be done at the time of use
     // rather than the time of registration (i.e. now).
     nsConnectionEntry *preferred =
         mSpdyPreferredHash.Get(ent->mCoalescingKey);
 
-    LOG(("ReportSpdyConnection %s %s ent=%p ispreferred=%d\n",
+    LOG(("ReportSpdyConnection %s %s ent=%p preferred=%p\n",
          ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
          ent, preferred));
     
     if (!preferred) {
-        ent->mSpdyPreferred = true;
-        SetSpdyPreferred(ent);
-        preferred = ent;
+        if (!ent->mCoalescingKey.IsEmpty()) {
+            mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
+            ent->mSpdyPreferred = true;
+            preferred = ent;
+        }
     }
     else if (preferred != ent) {
         // A different hostname is the preferred spdy host for this
-        // IP address.
-        ent->mUsingSpdy = true;
+        // IP address. That preferred mapping must have been setup while
+        // this connection was negotiating NPN.
+
+        // Call don't reuse on the current connection to shut it down as soon
+        // as possible without causing any errors.
+        // i.e. the current transaction(s) on this connection will be processed
+        // normally, but then it will go away and future connections will be
+        // coalesced through the preferred entry.
+
         conn->DontReuse();
     }
 
-    ProcessSpdyPendingQ();
+    ProcessAllSpdyPendingQ();
 }
 
 bool
 nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
 {
+    if (!gHttpHandler->UseAlternateProtocol())
+        return false;
+
     // The Alternate Protocol hash is protected under the monitor because
     // it is read from both the main and the network thread.
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
     return mAlternateProtocolHash.Contains(hostPortKey);
 }
 
 void
@@ -584,17 +599,17 @@ nsHttpConnectionMgr::TrimAlternateProtoc
     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
     
     if (self->mAlternateProtocolHash.mHashTable.entryCount > 2000)
         return PL_DHASH_REMOVE;
     return PL_DHASH_STOP;
 }
 
 nsHttpConnectionMgr::nsConnectionEntry *
-nsHttpConnectionMgr::GetSpdyPreferred(nsConnectionEntry *aOriginalEntry)
+nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
 {
     if (!gHttpHandler->IsSpdyEnabled() ||
         !gHttpHandler->CoalesceSpdy() ||
         aOriginalEntry->mCoalescingKey.IsEmpty())
         return nsnull;
 
     nsConnectionEntry *preferred =
         mSpdyPreferredHash.Get(aOriginalEntry->mCoalescingKey);
@@ -621,17 +636,17 @@ nsHttpConnectionMgr::GetSpdyPreferred(ns
             break;
         }
     }
 
     if (!activeSpdy) {
         // remove the preferred status of this entry if it cannot be
         // used for pooling.
         preferred->mSpdyPreferred = false;
-        RemoveSpdyPreferred(preferred->mCoalescingKey);
+        RemoveSpdyPreferredEnt(preferred->mCoalescingKey);
         LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
              "preferred host mapping %s to %s removed due to inactivity.\n",
              aOriginalEntry->mConnInfo->Host(),
              preferred->mConnInfo->Host()));
 
         return nsnull;
     }
 
@@ -639,64 +654,56 @@ nsHttpConnectionMgr::GetSpdyPreferred(ns
     nsresult rv;
     bool isJoined = false;
 
     nsCOMPtr<nsISupports> securityInfo;
     nsCOMPtr<nsISSLSocketControl> sslSocketControl;
     nsCAutoString negotiatedNPN;
     
     activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
-    if (!securityInfo)
+    if (!securityInfo) {
+        NS_WARNING("cannot obtain spdy security info");
         return nsnull;
+    }
 
     sslSocketControl = do_QueryInterface(securityInfo, &rv);
-    if (NS_FAILED(rv))
+    if (NS_FAILED(rv)) {
+        NS_WARNING("sslSocketControl QI Failed");
         return nsnull;
+    }
 
     rv = sslSocketControl->JoinConnection(NS_LITERAL_CSTRING("spdy/2"),
                                           aOriginalEntry->mConnInfo->GetHost(),
                                           aOriginalEntry->mConnInfo->Port(),
                                           &isJoined);
 
     if (NS_FAILED(rv) || !isJoined) {
         LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
              "Host %s cannot be confirmed to be joined "
-             "with %s connections",
-             preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host()));
+             "with %s connections. rv=%x isJoined=%d",
+             preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
+             rv, isJoined));
         mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_JOIN,
                                        false);
         return nsnull;
     }
 
     // IP pooling confirmed
     LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
-         "Host %s has cert valid for %s connections",
-         preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host()));
+         "Host %s has cert valid for %s connections, "
+         "so %s will be coalesced with %s",
+         preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
+         aOriginalEntry->mConnInfo->Host(), preferred->mConnInfo->Host()));
     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_JOIN, true);
     return preferred;
 }
 
 void
-nsHttpConnectionMgr::SetSpdyPreferred(nsConnectionEntry *ent)
+nsHttpConnectionMgr::RemoveSpdyPreferredEnt(nsACString &aHashKey)
 {
-    if (!gHttpHandler->CoalesceSpdy())
-        return;
-
-    if (ent->mCoalescingKey.IsEmpty())
-        return;
-    
-    mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
-}
-
-void
-nsHttpConnectionMgr::RemoveSpdyPreferred(nsACString &aHashKey)
-{
-    if (!gHttpHandler->CoalesceSpdy())
-        return;
-
     if (aHashKey.IsEmpty())
         return;
     
     mSpdyPreferredHash.Remove(aHashKey);
 }
 
 //-----------------------------------------------------------------------------
 // enumeration callbacks
@@ -745,55 +752,52 @@ nsHttpConnectionMgr::PruneDeadConnection
                                             void *closure)
 {
     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
 
     LOG(("  pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
 
     // Find out how long it will take for next idle connection to not be reusable
     // anymore.
-    bool liveConnections = false;
     PRUint32 timeToNextExpire = PR_UINT32_MAX;
     PRInt32 count = ent->mIdleConns.Length();
     if (count > 0) {
         for (PRInt32 i=count-1; i>=0; --i) {
             nsHttpConnection *conn = ent->mIdleConns[i];
             if (!conn->CanReuse()) {
                 ent->mIdleConns.RemoveElementAt(i);
                 conn->Close(NS_ERROR_ABORT);
                 NS_RELEASE(conn);
                 self->mNumIdleConns--;
             } else {
                 timeToNextExpire = NS_MIN(timeToNextExpire, conn->TimeToLive());
-                liveConnections = true;
             }
         }
     }
 
     if (ent->mUsingSpdy) {
         for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index) {
             nsHttpConnection *conn = ent->mActiveConns[index];
             if (conn->UsingSpdy()) {
                 if (!conn->CanReuse()) {
                     // marking it dont reuse will create an active tear down if
                     // the spdy session is idle.
                     conn->DontReuse();
                 }
                 else {
                     timeToNextExpire = NS_MIN(timeToNextExpire,
                                               conn->TimeToLive());
-                    liveConnections = true;
                 }
             }
         }
     }
     
     // If time to next expire found is shorter than time to next wake-up, we need to
     // change the time for next wake-up.
-    if (liveConnections) {
+    if (timeToNextExpire != PR_UINT32_MAX) {
         PRUint32 now = NowInSeconds();
         PRUint64 timeOfNextExpire = now + timeToNextExpire;
         // If pruning of dead connections is not already scheduled to happen
         // or time found for next connection to expire is is before
         // mTimeOfNextWakeUp, we need to schedule the pruning to happen
         // after timeToNextExpire.
         if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
             self->PruneDeadConnectionsAfter(timeToNextExpire);
@@ -887,18 +891,17 @@ 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()));
 
-    if (gHttpHandler->IsSpdyEnabled())
-        ProcessSpdyPendingQ(ent);
+    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];
@@ -915,22 +918,19 @@ nsHttpConnectionMgr::ProcessPendingQForE
                     break;
                 }
             }
 
             GetConnection(ent, trans, alreadyHalfOpen, &conn);
             if (conn)
                 break;
 
-            // Check to see if a pending transaction was dispatched with the
-            // coalesce logic
-            if (count != ent->mPendingQ.Length()) {
-                count = ent->mPendingQ.Length();
-                i = 0;
-            }
+            NS_ABORT_IF_FALSE(count == ent->mPendingQ.Length(),
+                              "something mutated pending queue from "
+                              "GetConnection()");
         }
         if (conn) {
             LOG(("  dispatching pending transaction...\n"));
 
             // remove pending transaction
             ent->mPendingQ.RemoveElementAt(i);
 
             nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
@@ -1106,19 +1106,19 @@ nsHttpConnectionMgr::GetConnection(nsCon
         // does not create new transports under any circumstances.
         if (onlyReusedConnection)
             return;
         
         if (gHttpHandler->IsSpdyEnabled() &&
             ent->mConnInfo->UsingSSL() &&
             !ent->mConnInfo->UsingHttpProxy())
         {
-            // If this is a possible Spdy connection we need to limit the number
-            // of connections outstanding to 1 while we wait for the spdy/https
-            // ReportSpdyConnection()
+            // 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;
         }
         
         // 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
@@ -1326,17 +1326,17 @@ nsHttpConnectionMgr::ProcessNewTransacti
         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 = GetSpdyPreferred(ent);
+    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;
     }
 
@@ -1398,48 +1398,56 @@ 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 rv2 = DispatchTransaction(ent, trans, trans->Caps(), conn);
-        NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv2), "Dispatch SPDY Transaction");
+        nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), 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);
+        }
         NS_RELEASE(trans);
     }
 }
 
 PLDHashOperator
 nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
                                            nsAutoPtr<nsConnectionEntry> &ent,
                                            void *closure)
 {
     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
     self->ProcessSpdyPendingQ(ent);
     return PL_DHASH_NEXT;
 }
 
 void
-nsHttpConnectionMgr::ProcessSpdyPendingQ()
+nsHttpConnectionMgr::ProcessAllSpdyPendingQ()
 {
     mCT.Enumerate(ProcessSpdyPendingQCB, this);
 }
 
 nsHttpConnection *
 nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
 {
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ABORT_IF_FALSE(ent, "no connection entry");
 
-    nsConnectionEntry *preferred = GetSpdyPreferred(ent);
+    nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
 
     // this entry is spdy-enabled if it is involved in a redirect
     if (preferred)
+        // all new connections for this entry will use spdy too
         ent->mUsingSpdy = true;
     else
         preferred = ent;
     
     nsHttpConnection *conn = nsnull;
     
     if (preferred->mUsingSpdy) {
         for (PRUint32 index = 0;
@@ -1589,30 +1597,38 @@ nsHttpConnectionMgr::OnMsgReclaimConnect
     //
 
     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
                                                    conn, nsnull);
     nsHttpConnectionInfo *ci = nsnull;
 
     if (!ent) {
         // this should never happen
-        NS_ASSERTION(ent, "no connection entry");
+        LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
+        NS_ABORT_IF_FALSE(false, "no connection entry");
         NS_ADDREF(ci = conn->ConnectionInfo());
     }
     else {
         NS_ADDREF(ci = ent->mConnInfo);
 
         // If the connection is in the active list, remove that entry
         // and the reference held by the mActiveConns list.
         // This is never the final reference on conn as the event context
         // is also holding one that is released at the end of this function.
 
-        if (ent->mUsingSpdy)
+        if (ent->mUsingSpdy) {
+            // Spdy connections aren't reused in the traditional HTTP way in
+            // the idleconns list, they are actively multplexed as active
+            // 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)) {
             nsHttpConnection *temp = conn;
             NS_RELEASE(temp);
             mNumActiveConns--;
         }
 
         if (conn->CanReuse()) {
             LOG(("  adding connection to idle list\n"));
@@ -1685,17 +1701,17 @@ nsHttpConnectionMgr::OnMsgUpdateParam(PR
         NS_NOTREACHED("unexpected parameter name");
     }
 }
 
 // nsHttpConnectionMgr::nsConnectionEntry
 nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
 {
     if (mSpdyPreferred)
-        gHttpHandler->ConnMgr()->RemoveSpdyPreferred(mCoalescingKey);
+        gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
 
     NS_RELEASE(mConnInfo);
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpConnectionMgr::nsConnectionHandle
 
 nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
@@ -1713,25 +1729,25 @@ nsHttpConnectionMgr::nsConnectionHandle:
                                                             nsHttpRequestHead *req,
                                                             nsHttpResponseHead *resp,
                                                             bool *reset)
 {
     return mConn->OnHeadersAvailable(trans, req, resp, reset);
 }
 
 nsresult
-nsHttpConnectionMgr::nsConnectionHandle::ResumeSend(nsAHttpTransaction *caller)
+nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
 {
-    return mConn->ResumeSend(caller);
+    return mConn->ResumeSend();
 }
 
 nsresult
-nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv(nsAHttpTransaction *caller)
+nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
 {
-    return mConn->ResumeRecv(caller);
+    return mConn->ResumeRecv();
 }
 
 void
 nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
 {
     mConn->CloseTransaction(trans, reason);
 }
 
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -195,22 +195,27 @@ private:
         // 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.
         //
         nsCString mCoalescingKey;
 
-        // To have the UsingSpdy flag means some host with the same hash information
-        // has done NPN=spdy/2 at some point. It does not mean every connection
-        // is currently using spdy.
+        // To have the UsingSpdy flag means some host with the same connection
+        // entry has done NPN=spdy/2 at some point. It does not mean every
+        // connection is currently using spdy.
         bool mUsingSpdy;
 
+        // mTestedSpdy is set after NPN negotiation has occurred and we know
+        // with confidence whether a host speaks spdy or not (which is reflected
+        // in mUsingSpdy). Before mTestedSpdy is set, handshake parallelism is
+        // minimized so that we can multiplex on a single spdy connection.
         bool mTestedSpdy;
+
         bool mSpdyPreferred;
     };
 
     // nsConnectionHandle
     //
     // thin wrapper around a real connection, used to keep track of references
     // to the connection to determine when the connection may be reused.  the
     // transaction (or pipeline) owns a reference to this handle.  this extra
@@ -316,27 +321,26 @@ private:
     nsresult EnsureSocketThreadTargetIfOnline();
     void     ClosePersistentConnections(nsConnectionEntry *ent);
     nsresult CreateTransport(nsConnectionEntry *, nsHttpTransaction *);
     void     AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
     void     StartedConnect();
     void     RecvdConnect();
 
     // Manage the preferred spdy connection entry for this address
-    nsConnectionEntry *GetSpdyPreferred(nsConnectionEntry *aOriginalEntry);
-    void               SetSpdyPreferred(nsConnectionEntry *ent);
-    void               RemoveSpdyPreferred(nsACString &aDottedDecimal);
+    nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
+    void               RemoveSpdyPreferredEnt(nsACString &aDottedDecimal);
     nsHttpConnection  *GetSpdyPreferredConn(nsConnectionEntry *ent);
     nsDataHashtable<nsCStringHashKey, nsConnectionEntry *>   mSpdyPreferredHash;
     nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci,
                                              nsHttpConnection *conn,
                                              nsHttpTransaction *trans);
 
     void               ProcessSpdyPendingQ(nsConnectionEntry *ent);
-    void               ProcessSpdyPendingQ();
+    void               ProcessAllSpdyPendingQ();
     static PLDHashOperator ProcessSpdyPendingQCB(
         const nsACString &key, nsAutoPtr<nsConnectionEntry> &ent,
         void *closure);
 
     // message handlers have this signature
     typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(PRInt32, void *);
 
     // nsConnEvent
@@ -409,17 +413,17 @@ private:
     //
     // the connection table
     //
     // this table is indexed by connection key.  each entry is a
     // nsConnectionEntry object.
     //
     nsClassHashtable<nsCStringHashKey, nsConnectionEntry> mCT;
 
-    // this table is protected by the monitor
-    nsCStringHashSet mAlternateProtocolHash;
+    // mAlternateProtocolHash is used only for spdy/2 upgrades for now
+    nsCStringHashSet mAlternateProtocolHash; // protected by the monitor
     static PLDHashOperator TrimAlternateProtocolHash(PLDHashTable *table,
                                                      PLDHashEntryHdr *hdr,
                                                      PRUint32 number,
                                                      void *closure);
 };
 
 #endif // !nsHttpConnectionMgr_h__
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -72,16 +72,17 @@
 #include "prprf.h"
 #include "nsReadableUtils.h"
 #include "nsQuickSort.h"
 #include "nsNetUtil.h"
 #include "nsIOService.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "nsSocketTransportService2.h"
 #include "nsAlgorithm.h"
+#include "SpdySession.h"
 
 #include "nsIXULAppInfo.h"
 
 #include "mozilla/net/NeckoChild.h"
 
 #if defined(XP_UNIX)
 #include <sys/utsname.h>
 #endif
@@ -197,16 +198,17 @@ nsHttpHandler::nsHttpHandler()
     , mUseCache(true)
     , mPromptTempRedirect(true)
     , mSendSecureXSiteReferrer(true)
     , mEnablePersistentHttpsCaching(false)
     , mDoNotTrackEnabled(false)
     , mEnableSpdy(false)
     , mCoalesceSpdy(true)
     , mUseAlternateProtocol(false)
+    , mSpdySendingChunkSize(SpdySession::kSendingChunkSize)
 {
 #if defined(PR_LOGGING)
     gHttpLog = PR_NewLogModule("nsHttp");
 #endif
 
     LOG(("Creating nsHttpHandler [this=%x].\n", this));
 
     NS_ASSERTION(!gHttpHandler, "HTTP handler already created!");
@@ -1089,16 +1091,22 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
     }
 
     if (PREF_CHANGED(HTTP_PREF("spdy.timeout"))) {
         rv = prefs->GetIntPref(HTTP_PREF("spdy.timeout"), &val);
         if (NS_SUCCEEDED(rv))
             mSpdyTimeout = (PRUint16) clamped(val, 1, 0xffff);
     }
 
+    if (PREF_CHANGED(HTTP_PREF("spdy.chunk-size"))) {
+        rv = prefs->GetIntPref(HTTP_PREF("spdy.chunk-size"), &val);
+        if (NS_SUCCEEDED(rv))
+            mSpdySendingChunkSize = (PRUint32) clamped(val, 1, 0x7fffffff);
+    }
+
     //
     // INTL options
     //
 
     if (PREF_CHANGED(INTL_ACCEPT_LANGUAGES)) {
         nsCOMPtr<nsIPrefLocalizedString> pls;
         prefs->GetComplexValue(INTL_ACCEPT_LANGUAGES,
                                 NS_GET_IID(nsIPrefLocalizedString),
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -111,16 +111,17 @@ public:
     bool           FastFallbackToIPv4()      { return mFastFallbackToIPv4; }
     PRUint32       MaxSocketCount();
 
     bool           IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; }
 
     bool           IsSpdyEnabled() { return mEnableSpdy; }
     bool           CoalesceSpdy() { return mCoalesceSpdy; }
     bool           UseAlternateProtocol() { return mUseAlternateProtocol; }
+    PRUint32       SpdySendingChunkSize() { return mSpdySendingChunkSize; }
 
     bool           PromptTempRedirect()      { return mPromptTempRedirect; }
 
     nsHttpAuthCache     *AuthCache() { return &mAuthCache; }
     nsHttpConnectionMgr *ConnMgr()   { return mConnMgr; }
 
     // cache support
     nsresult GetCacheSession(nsCacheStoragePolicy, nsICacheSession **);
@@ -337,16 +338,17 @@ private:
 
     // For broadcasting the preference to not be tracked
     bool           mDoNotTrackEnabled;
     
     // Try to use SPDY features instead of HTTP/1.1 over SSL
     bool           mEnableSpdy;
     bool           mCoalesceSpdy;
     bool           mUseAlternateProtocol;
+    PRUint32       mSpdySendingChunkSize;
 };
 
 //-----------------------------------------------------------------------------
 
 extern nsHttpHandler *gHttpHandler;
 
 //-----------------------------------------------------------------------------
 // nsHttpsHandler - thin wrapper to distinguish the HTTP handler from the
--- a/netwerk/protocol/http/nsHttpPipeline.cpp
+++ b/netwerk/protocol/http/nsHttpPipeline.cpp
@@ -126,17 +126,17 @@ nsHttpPipeline::AddTransaction(nsAHttpTr
 
     NS_ADDREF(trans);
     mRequestQ.AppendElement(trans);
 
     if (mConnection && !mClosed) {
         trans->SetConnection(this);
 
         if (mRequestQ.Length() == 1)
-            mConnection->ResumeSend(trans);
+            mConnection->ResumeSend();
     }
 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpPipeline::nsISupports
 //-----------------------------------------------------------------------------
@@ -165,29 +165,29 @@ nsHttpPipeline::OnHeadersAvailable(nsAHt
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ASSERTION(mConnection, "no connection");
 
     // trans has now received its response headers; forward to the real connection
     return mConnection->OnHeadersAvailable(trans, requestHead, responseHead, reset);
 }
 
 nsresult
-nsHttpPipeline::ResumeSend(nsAHttpTransaction *trans)
+nsHttpPipeline::ResumeSend()
 {
     if (mConnection)
-        return mConnection->ResumeSend(trans);
+        return mConnection->ResumeSend();
     return NS_ERROR_UNEXPECTED;
 }
 
 nsresult
-nsHttpPipeline::ResumeRecv(nsAHttpTransaction *trans)
+nsHttpPipeline::ResumeRecv()
 {
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ASSERTION(mConnection, "no connection");
-    return mConnection->ResumeRecv(trans);
+    return mConnection->ResumeRecv();
 }
 
 void
 nsHttpPipeline::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
 {
     LOG(("nsHttpPipeline::CloseTransaction [this=%x trans=%x reason=%x]\n",
         this, trans, reason));
 
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -1293,30 +1293,31 @@ NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsHt
 // nsHttpTransaction::nsIInputStreamCallback
 //-----------------------------------------------------------------------------
 
 // called on the socket thread
 NS_IMETHODIMP
 nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
 {
     if (mConnection) {
-        nsresult rv = mConnection->ResumeSend(this);
+        mConnection->TransactionHasDataToWrite(this);
+        nsresult rv = mConnection->ResumeSend();
         if (NS_FAILED(rv))
             NS_ERROR("ResumeSend failed");
     }
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpTransaction::nsIOutputStreamCallback
 //-----------------------------------------------------------------------------
 
 // called on the socket thread
 NS_IMETHODIMP
 nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
 {
     if (mConnection) {
-        nsresult rv = mConnection->ResumeRecv(this);
+        nsresult rv = mConnection->ResumeRecv();
         if (NS_FAILED(rv))
             NS_ERROR("ResumeRecv failed");
     }
     return NS_OK;
 }
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -519,17 +519,17 @@ nsAutoCompleteController::HandleKeyNavig
         // We usually try to preserve the casing of what user has typed, but
         // if he wants to autocomplete, we will replace the value with the
         // actual autocomplete result.
         // The user wants explicitely to use that result, so this ensures
         // association of the result with the autocompleted text.
         nsAutoString value;
         nsAutoString inputValue;
         input->GetTextValue(inputValue);
-        if (NS_SUCCEEDED(GetDefaultCompleteValue(selectedIndex, false, value)) &&
+        if (NS_SUCCEEDED(GetDefaultCompleteValue(-1, false, value)) &&
             value.Equals(inputValue, nsCaseInsensitiveStringComparator())) {
           input->SetTextValue(value);
           input->SelectTextRange(value.Length(), value.Length());
         }
       }
       // Close the pop-up even if nothing was selected
       ClearSearchTimer();
       ClosePopup();
@@ -1157,17 +1157,17 @@ nsAutoCompleteController::EnterMatch(boo
       // We usually try to preserve the casing of what user has typed, but
       // if he wants to autocomplete, we will replace the value with the
       // actual autocomplete result.
       // The user wants explicitely to use that result, so this ensures
       // association of the result with the autocompleted text.
       nsAutoString defaultIndexValue;
       nsAutoString inputValue;
       input->GetTextValue(inputValue);
-      if (NS_SUCCEEDED(GetDefaultCompleteValue(selectedIndex, false, defaultIndexValue)) &&
+      if (NS_SUCCEEDED(GetDefaultCompleteValue(-1, false, defaultIndexValue)) &&
           defaultIndexValue.Equals(inputValue, nsCaseInsensitiveStringComparator()))
         value = defaultIndexValue;
     }
 
     if (forceComplete && value.IsEmpty()) {
       // Since nothing was selected, and forceComplete is specified, that means
       // we have to find the first default match and enter it instead
       PRUint32 count = mResults.Count();
new file mode 100644
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js
@@ -0,0 +1,63 @@
+/* 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/. */
+
+function AutoCompleteResult(aValues) {
+  this._values = aValues;
+  this.defaultIndex = 0;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+  this.searches = aSearches;
+  this.popup.selectedIndex = -1;
+  this.completeDefaultIndex = true;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+function run_test() {
+  run_next_test();
+}
+
+add_test(function test_keyNavigation() {
+  doSearch("MOZ", "mozilla", function(aController) {
+    do_check_eq(aController.input.textValue, "MOZilla");
+    aController.handleKeyNavigation(Ci.nsIDOMKeyEvent.DOM_VK_RIGHT);
+    do_check_eq(aController.input.textValue, "mozilla");
+  });
+});
+
+add_test(function test_handleEnter() {
+  doSearch("MOZ", "mozilla", function(aController) {
+    do_check_eq(aController.input.textValue, "MOZilla");
+    aController.handleEnter(false);
+    do_check_eq(aController.input.textValue, "mozilla");
+  });
+});
+
+function doSearch(aSearchString, aResultValue, aOnCompleteCallback) {
+  let search = new AutoCompleteSearchBase("search",
+                                          new AutoCompleteResult([ "mozilla", "toolkit" ], 0));
+  registerAutoCompleteSearch(search);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);  
+  
+  // Make an AutoCompleteInput that uses our searches and confirms results.
+  let input = new AutoCompleteInput([ search.name ]);
+  input.textValue = aSearchString;
+
+  // Caret must be at the end for autofill to happen.
+  let strLen = aSearchString.length;
+  input.selectTextRange(strLen, strLen);
+  controller.input = input;
+  controller.startSearch(aSearchString);
+
+  input.onSearchComplete = function onSearchComplete() {
+    aOnCompleteCallback(controller);
+
+    // Clean up.
+    unregisterAutoCompleteSearch(search);
+    run_next_test();
+  };
+}
--- a/toolkit/components/autocomplete/tests/unit/xpcshell.ini
+++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini
@@ -5,11 +5,12 @@ tail =
 [test_330578.js]
 [test_378079.js]
 [test_393191.js]
 [test_440866.js]
 [test_463023.js]
 [test_660156.js]
 [test_autocomplete_multiple.js]
 [test_badDefaultIndex.js]
+[test_completeDefaultIndex_casing.js]
 [test_hiddenResult.js]
 [test_previousResult.js]
 [test_stopSearch.js]
--- a/widget/gonk/nsWindow.cpp
+++ b/widget/gonk/nsWindow.cpp
@@ -444,8 +444,20 @@ nsWindow::UserActivity()
     if (!mIdleService) {
         mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
     }
 
     if (mIdleService) {
         mIdleService->ResetIdleTimeOut();
     }
 }
+
+PRUint32
+nsWindow::GetGLFrameBufferFormat()
+{
+    if (mLayerManager &&
+        mLayerManager->GetBackendType() == LayerManager::LAYERS_OPENGL) {
+        // We directly map the hardware fb on Gonk.  The hardware fb
+        // has RGB format.
+        return LOCAL_GL_RGB;
+    }
+    return LOCAL_GL_NONE;
+}
--- a/widget/gonk/nsWindow.h
+++ b/widget/gonk/nsWindow.h
@@ -121,16 +121,18 @@ public:
                         LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                         bool* aAllowRetaining = nsnull);
     gfxASurface* GetThebesSurface();
 
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction);
     NS_IMETHOD_(InputContext) GetInputContext();
 
+    virtual PRUint32 GetGLFrameBufferFormat() MOZ_OVERRIDE;
+
 protected:
     nsWindow* mParent;
     bool mVisible;
     nsIntRegion mDirtyRegion;
     InputContext mInputContext;
     nsCOMPtr<nsIdleService> mIdleService;
 
     void BringToTop();
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1505,16 +1505,22 @@ class nsIWidget : public nsISupports {
     CreatePuppetWidget(PBrowserChild *aTabChild);
 
     /**
      * Reparent this widget's native widget.
      * @param aNewParent the native widget of aNewParent is the new native
      *                   parent widget
      */
     NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent) = 0;
+
+    /**
+     * Return the internal format of the default framebuffer for this
+     * widget.
+     */
+    virtual PRUint32 GetGLFrameBufferFormat() { return 0; /*GL_NONE*/ }
 protected:
 
     // keep the list of children.  We also keep track of our siblings.
     // The ownership model is as follows: parent holds a strong ref to
     // the first element of the list, and each element holds a strong
     // ref to the next element in the list.  The prevsibling and
     // lastchild pointers are weak, which is fine as long as they are
     // maintained properly.
--- a/widget/xpwidgets/nsBaseWidget.cpp
+++ b/widget/xpwidgets/nsBaseWidget.cpp
@@ -1254,16 +1254,28 @@ nsBaseWidget::BeginResizeDrag(nsGUIEvent
 }
 
 NS_IMETHODIMP
 nsBaseWidget::BeginMoveDrag(nsMouseEvent* aEvent)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+PRUint32
+nsBaseWidget::GetGLFrameBufferFormat()
+{
+  if (mLayerManager &&
+      mLayerManager->GetBackendType() == LayerManager::LAYERS_OPENGL) {
+    // Assume that the default framebuffer has RGBA format.  Specific
+    // backends that know differently will override this method.
+    return LOCAL_GL_RGBA;
+  }
+  return LOCAL_GL_NONE;
+}
+
 #ifdef DEBUG
 //////////////////////////////////////////////////////////////
 //
 // Convert a GUI event message code to a string.
 // Makes it a lot easier to debug events.
 //
 // See gtk/nsWidget.cpp and windows/nsWindow.cpp
 // for a DebugPrintEvent() function that uses
--- a/widget/xpwidgets/nsBaseWidget.h
+++ b/widget/xpwidgets/nsBaseWidget.h
@@ -196,16 +196,19 @@ public:
   bool IsPopupWithTitleBar() const
   {
     return (mWindowType == eWindowType_popup && 
             mBorderStyle != eBorderStyle_default &&
             mBorderStyle & eBorderStyle_title);
   }
 
   NS_IMETHOD              ReparentNativeWidget(nsIWidget* aNewParent) = 0;
+
+  virtual PRUint32 GetGLFrameBufferFormat() MOZ_OVERRIDE;
+
   /**
    * Use this when GetLayerManager() returns a BasicLayerManager
    * (nsBaseWidget::GetLayerManager() does). This sets up the widget's
    * layer manager to temporarily render into aTarget.
    */
   class AutoLayerManagerSetup {
   public:
     AutoLayerManagerSetup(nsBaseWidget* aWidget, gfxContext* aTarget,