Merginig mozilla-inbound with mozilla-central.
authorMounir Lamouri <mounir.lamouri@gmail.com>
Fri, 10 Jun 2011 09:58:03 +0200
changeset 70854 1e3af440ce23
parent 70842 443c7cd5bdf8 (current diff)
parent 70853 6c13a600bc3a (diff)
child 70855 bd3d7772ecf9
child 70866 92f4836116a3
child 70871 251688029a9d
push id20429
push usermlamouri@mozilla.com
push dateFri, 10 Jun 2011 07:58:50 +0000
treeherdermozilla-central@1e3af440ce23 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone7.0a1
first release with
nightly win64
1e3af440ce23 / 7.0a1 / 20110610030240 / files
nightly linux32
nightly linux64
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win64
Merginig mozilla-inbound with mozilla-central.
layout/base/crashtests/331883-2-inner.html
layout/base/crashtests/331883-2.html
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -1177,16 +1177,17 @@ nsCanvasRenderingContext2D::InitializeWi
     mThebes->Rectangle(gfxRect(0, 0, mWidth, mHeight));
     mThebes->Fill();
 
     mThebes->SetLineWidth(1.0);
     mThebes->SetOperator(gfxContext::OPERATOR_OVER);
     mThebes->SetMiterLimit(10.0);
     mThebes->SetLineCap(gfxContext::LINE_CAP_BUTT);
     mThebes->SetLineJoin(gfxContext::LINE_JOIN_MITER);
+    mThebes->SetFillRule(gfxContext::FILL_RULE_WINDING);
 
     mThebes->NewPath();
 
     // always force a redraw, because if the surface dimensions were reset
     // then the surface became cleared, and we need to redraw everything.
     Redraw();
 
     return mValid ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
@@ -1595,16 +1596,47 @@ nsCanvasRenderingContext2D::SetFillStyle
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetFillStyle_multi(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType)
 {
     return GetStyleAsStringOrInterface(aStr, aInterface, aType, STYLE_FILL);
 }
 
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::SetMozFillRule(const nsAString& aString)
+{
+    gfxContext::FillRule rule;
+
+    if (aString.EqualsLiteral("evenodd"))
+        rule = gfxContext::FILL_RULE_EVEN_ODD;
+    else if (aString.EqualsLiteral("nonzero"))
+        rule = gfxContext::FILL_RULE_WINDING;
+    else
+        // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+        return NS_OK;
+
+    mThebes->SetFillRule(rule);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::GetMozFillRule(nsAString& aString)
+{
+    switch (mThebes->CurrentFillRule()) {
+    case gfxContext::FILL_RULE_WINDING:
+        aString.AssignLiteral("nonzero"); break;
+    case gfxContext::FILL_RULE_EVEN_ODD:
+        aString.AssignLiteral("evenodd"); break;
+    default:
+        return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
+}
+
 //
 // gradients and patterns
 //
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::CreateLinearGradient(float x0, float y0, float x1, float y1,
                                                  nsIDOMCanvasGradient **_retval)
 {
     if (!FloatValidate(x0,y0,x1,y1))
--- a/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl
+++ b/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl
@@ -57,17 +57,17 @@ interface nsIDOMCanvasPattern : nsISuppo
 };
 
 [scriptable, uuid(2d01715c-ec7d-424a-ab85-e0fd70c8665c)]
 interface nsIDOMTextMetrics : nsISupports
 {
   readonly attribute float width;
 };
 
-[scriptable, uuid(408be1b9-4d75-4873-b50b-9b651626e41d)]
+[scriptable, uuid(bf5e52a3-6ec1-4a6e-8364-9f9222ec8edb)]
 interface nsIDOMCanvasRenderingContext2D : nsISupports
 {
   // back-reference to the canvas element for which
   // this context was created
   readonly attribute nsIDOMHTMLCanvasElement canvas;
 
   // state
   void save();
@@ -101,16 +101,19 @@ enum CanvasMultiGetterType {
       CMG_STYLE_GRADIENT = 2
 };
 %}
   [noscript] void setStrokeStyle_multi(in DOMString str, in nsISupports iface);
   [noscript] void getStrokeStyle_multi(out DOMString str, out nsISupports iface, out long type);
   [noscript] void setFillStyle_multi(in DOMString str, in nsISupports iface);
   [noscript] void getFillStyle_multi(out DOMString str, out nsISupports iface, out long type);
 
+  //attribute DOMString fillRule;
+  attribute DOMString mozFillRule; /* "evenodd", "nonzero" (default) */
+
   nsIDOMCanvasGradient createLinearGradient (in float x0, in float y0, in float x1, in float y1);
   nsIDOMCanvasGradient createRadialGradient(in float x0, in float y0, in float r0, in float x1, in float y1, in float r1);
   nsIDOMCanvasPattern createPattern(in nsIDOMHTMLElement image, in DOMString repetition);
   attribute float lineWidth; /* default 1 */
   attribute DOMString lineCap; /* "butt", "round", "square" (default) */
   attribute DOMString lineJoin; /* "round", "bevel", "miter" (default) */
   attribute float miterLimit; /* default 10 */
 
--- a/gfx/cairo/cairo/src/cairo-d2d-private.h
+++ b/gfx/cairo/cairo/src/cairo-d2d-private.h
@@ -44,16 +44,17 @@
 #include <dxgi.h>
 
 #include "cairoint.h"
 #include "cairo-surface-clipper-private.h"
 
 #include "cairo-win32-refptr.h"
 #include "cairo-d2d-private-fx.h"
 #include "cairo-win32.h"
+#include "cairo-list-private.h"
 
 /* describes the type of the currently applied clip so that we can pop it */
 struct d2d_clip;
 
 #define MAX_OPERATORS CAIRO_OPERATOR_HSL_LUMINOSITY + 1
 
 struct _cairo_d2d_device
 {
@@ -76,17 +77,21 @@ const unsigned int TEXT_TEXTURE_WIDTH = 
 const unsigned int TEXT_TEXTURE_HEIGHT = 512;
 typedef struct _cairo_d2d_device cairo_d2d_device_t;
 
 struct _cairo_d2d_surface {
     _cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false),
             textRenderingState(TEXT_RENDERING_UNINITIALIZED)
     {
 	_cairo_clip_init (&this->clip);
+        cairo_list_init(&this->dependent_surfaces);
     }
+    
+    ~_cairo_d2d_surface();
+
 
     cairo_surface_t base;
     /* Device used by this surface 
      * NOTE: In upstream cairo this is in the surface base class */
     cairo_d2d_device_t *device;
 
     /** Render target of the texture we render to */
     RefPtr<ID2D1RenderTarget> rt;
@@ -134,21 +139,33 @@ struct _cairo_d2d_surface {
         TEXT_RENDERING_NORMAL,
         TEXT_RENDERING_GDI_CLASSIC
     };
     TextRenderingState textRenderingState;
 
     RefPtr<ID3D10RenderTargetView> buffer_rt_view;
     RefPtr<ID3D10ShaderResourceView> buffer_sr_view;
 
-
+    // Other d2d surfaces which depend on this one and need to be flushed if
+    // it is drawn to. This is required for situations where this surface is
+    // drawn to another surface, but may be modified before the other surface
+    // has flushed. When the flush of the other surface then happens and the
+    // drawing command is actually executed, the contents of this surface will
+    // no longer be what it was when the drawing command was issued.
+    cairo_list_t dependent_surfaces;
     //cairo_surface_clipper_t clipper;
 };
 typedef struct _cairo_d2d_surface cairo_d2d_surface_t;
 
+struct _cairo_d2d_surface_entry
+{
+    cairo_list_t link;
+    cairo_d2d_surface_t *surface;
+};
+
 typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
     __in D2D1_FACTORY_TYPE factoryType,
     __in REFIID iid,
     __in_opt CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
     __out void **factory
 );
 
 typedef HRESULT (WINAPI*D3D10CreateDevice1Func)(
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -1048,25 +1048,45 @@ static cairo_status_t
 
     /* push the new clip paths up to current_clip_path */
     if (current_clip_path != clip->path)
 	return clipper_intersect_clip_path_recursive (d2dsurf, current_clip_path, clip->path);
     else
 	return CAIRO_STATUS_SUCCESS;
 }
 
+static void _cairo_d2d_add_dependent_surface(cairo_d2d_surface_t *surf, cairo_d2d_surface_t *user)
+{
+    _cairo_d2d_surface_entry *entry = new _cairo_d2d_surface_entry;
+    entry->surface = user;
+    cairo_surface_reference(&user->base);
+    cairo_list_add(&entry->link, &surf->dependent_surfaces);
+};
+
+static void _cairo_d2d_flush_dependent_surfaces(cairo_d2d_surface_t *surf)
+{
+    _cairo_d2d_surface_entry *entry, *next;
+    cairo_list_foreach_entry_safe(entry, next, _cairo_d2d_surface_entry, &surf->dependent_surfaces, link) {
+	_cairo_d2d_flush(entry->surface);
+	cairo_surface_destroy(&entry->surface->base);
+	delete entry;
+    }
+    cairo_list_init(&surf->dependent_surfaces);
+}
+
 /**
  * Enter the state where the surface is ready for drawing. This will guarantee
  * the surface is in the correct state, and the correct clipping area is pushed.
  *
  * \param surface D2D surface
  */
 static void _begin_draw_state(cairo_d2d_surface_t* surface)
 {
     if (!surface->isDrawing) {
+	_cairo_d2d_flush_dependent_surfaces(surface);
 	surface->rt->BeginDraw();
 	surface->isDrawing = true;
     }
 }
 
 /**
  * Get a D2D matrix from a cairo matrix. Note that D2D uses row vectors where cairo
  * uses column vectors. Hence the transposition.
@@ -1773,16 +1793,19 @@ static RefPtr<ID2D1Brush>
 		 * fairly efficient here, for now, fallback.
 		 */
 		return NULL;
 	    }
 
 	    _cairo_d2d_update_surface_bitmap(srcSurf);
 	    _cairo_d2d_flush(srcSurf);
 
+	    // Mark a dependency on the source surface.
+	    _cairo_d2d_add_dependent_surface(srcSurf, d2dsurf);
+
 	    if (pattern->extend == CAIRO_EXTEND_NONE) {
 		ID2D1Bitmap *srcSurfBitmap = srcSurf->surfaceBitmap;
 		d2dsurf->rt->CreateBitmap(
 		    D2D1::SizeU(srcSurfBitmap->GetPixelSize().width + 2,
 				srcSurfBitmap->GetPixelSize().height + 2),
 		    D2D1::BitmapProperties(srcSurfBitmap->GetPixelFormat()),
 		    &sourceBitmap);
 		D2D1_POINT_2U point = D2D1::Point2U(1, 1);
@@ -2322,16 +2345,28 @@ void
 _cairo_d2d_surface_init(cairo_d2d_surface_t *newSurf, cairo_d2d_device_t *d2d_device, cairo_format_t format)
 {
     newSurf->format = format;
 
     newSurf->device = d2d_device;
     cairo_addref_device(&d2d_device->base);
     d2d_device->mVRAMUsage += _cairo_d2d_compute_surface_mem_size(newSurf);
 }
+    
+_cairo_d2d_surface::~_cairo_d2d_surface()
+{
+    _cairo_d2d_surface_entry *entry, *next;
+    cairo_list_foreach_entry_safe(entry, next, _cairo_d2d_surface_entry, &dependent_surfaces, link) {
+	// We do not need to flush, the contents of our texture has not changed,
+	// our users have their own reference and can just use it later.
+	cairo_surface_destroy(&entry->surface->base);
+	delete entry;
+    }
+
+}
 
 // Implementation
 static cairo_surface_t*
 _cairo_d2d_create_similar(void			*surface,
 			  cairo_content_t	 content,
 			  int			 width,
 			  int			 height)
 {
@@ -2788,16 +2823,18 @@ static cairo_int_status_t
     }
     if (rectSrc.top > rectSrc.bottom) {
 	rectDst.top = -rectDst.top;
 	rectDst.bottom = -rectDst.bottom;
 	matrix._22 = -1.0;
 	needsTransform = true;
     }
 
+    _cairo_d2d_add_dependent_surface(src, dst);
+
     D2D1_BITMAP_INTERPOLATION_MODE interpMode =
       D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
 
     if (needsTransform) {
 	dst->rt->SetTransform(matrix);
     }
 
     if (filter == CAIRO_FILTER_NEAREST) {
@@ -2981,16 +3018,18 @@ static RefPtr<ID2D1RenderTarget>
 	}
     }
     return new_rt;
 }
 
 static cairo_int_status_t
 _cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds = NULL)
 {
+    _cairo_d2d_flush_dependent_surfaces(surf);
+
     int numPaths = 0;
     if (clip) {
 	cairo_clip_path_t *path = clip->path;
 	while (path) {
 	    numPaths++;
 	    path = path->prev;
 	}
 	
@@ -3585,16 +3624,20 @@ cairo_int_status_t
     // aligned quads.
     if (clip) {
 	_cairo_clip_get_region(clip, &clip_region);
 	if (!clip_region) {
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
 	}
     }
 
+    if (!dst->isDrawing) {
+	_cairo_d2d_flush_dependent_surfaces(dst);
+    }
+
     _cairo_d2d_set_clip(dst, NULL);
     dst->rt->Flush();
 
     DWRITE_GLYPH_RUN run;
     _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, scaled_font, &run, &transform);
 
     RefPtr<IDWriteGlyphRunAnalysis> analysis;
     DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat);
--- a/layout/base/crashtests/331883-1.html
+++ b/layout/base/crashtests/331883-1.html
@@ -1,9 +1,16 @@
 <html class="reftest-wait">
 <head>
 <script>
-setTimeout('document.documentElement.className = ""', 1000);
+var numLoads = 0;
+function loaded()
+{
+  numLoads++;
+  if (numLoads == 5) {
+    document.documentElement.className = "";
+  }
+}
 </script>
 <body>
-<iframe src="331883-1-inner.html"></iframe>
+<iframe onload="loaded()" src="331883-1-inner.html"></iframe>
 </body>
 </html>
deleted file mode 100644
--- a/layout/base/crashtests/331883-2-inner.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<html>
-
-<head style="display: none">
-
-<style id="style">
-.lizard:first-line { }
-</style>
-
-<script>
-
-function init()
-{
-
-  document.getElementById("style").textContent += "* { position: relative; }";
-
-  document.getElementById("comment10div").setAttribute("class", "lizard");
-
-  document.getElementById("style").textContent += "*::-moz-line-frame { position: absolute; }";
-
-  setTimeout(function() { location.reload(); }, 200);
-
-} 
-
-window.addEventListener("load", init, false);
-
-</script>
-
-</head>
-
-<body>
-
-<div id="comment10div">XXXXXXXXXXXXXXXXXXXXXXXX <span>YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ JJJJJJJJJJJJJJJ PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP</span></div>
-
-</body>
-</html>
deleted file mode 100644
--- a/layout/base/crashtests/331883-2.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<html class="reftest-wait">
-<head>
-<script>
-setTimeout('document.documentElement.className = ""', 1000);
-</script>
-<body>
-<iframe src="331883-2-inner.html"></iframe>
-</body>
-</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -78,17 +78,16 @@ load 325984-2.html
 load 328944-1.xul
 load 329900-1.html
 load 330015-1.html
 load 331204-1.html
 load 331679-1.xhtml
 load 331679-2.xml
 load 331679-3.xml
 load 331883-1.html
-load 331883-2.html
 load 335140-1.html
 load 336291-1.html
 load 336999-1.xul
 load 337066-1.xhtml
 load 337268-1.html
 load 337419-1.html
 load 337476-1.xul
 load 338703-1.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/canvas/evenodd-fill-1.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+  <script type="text/javascript">
+window.onload = function() {
+    var ctx = document.getElementById("c1").getContext("2d");
+    ctx.mozFillRule = "evenodd";
+    ctx.rect(50, 50, 200, 200);
+    ctx.rect(100, 100, 100, 100);
+    ctx.fill();
+}
+  </script>
+</head>
+<body>
+  <div><canvas id="c1" width="300" height="300"></canvas></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/canvas/evenodd-fill-2.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+  <script type="text/javascript">
+window.onload = function() {
+    var ctx = document.getElementById("c1").getContext("2d");
+    ctx.mozFillRule = "evenodd";
+    ctx.rect(50, 50, 200, 200);
+    ctx.rect(100, 100, 100, 100);
+    ctx.clip();
+
+    ctx.beginPath();
+    ctx.fillRect(0, 0, 300, 300);
+}
+  </script>
+</head>
+<body>
+  <div><canvas id="c1" width="300" height="300"></canvas></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/canvas/evenodd-fill-3.html
@@ -0,0 +1,27 @@
+<html>
+<head>
+  <script type="text/javascript">
+window.onload = function() {
+    var ctx = document.getElementById("c1").getContext("2d");
+    ctx.mozFillRule = "evenodd";
+
+    ctx.moveTo(50, 50);
+    ctx.lineTo(250, 50);
+    ctx.lineTo(250, 250);
+    ctx.lineTo(50, 250);
+    ctx.lineTo(50, 50);
+
+    ctx.moveTo(100, 100);
+    ctx.lineTo(100, 200);
+    ctx.lineTo(200, 200);
+    ctx.lineTo(200, 100);
+    ctx.lineTo(100, 100);
+
+    ctx.fill();
+}
+  </script>
+</head>
+<body>
+  <div><canvas id="c1" width="300" height="300"></canvas></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/canvas/evenodd-fill-ref.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+  <script type="text/javascript">
+window.onload = function() {
+    var ctx = document.getElementById("c1").getContext("2d");
+    ctx.rect(50, 50, 200, 50);
+    ctx.rect(50, 50, 50, 200);
+    ctx.rect(250, 250, -200, -50);
+    ctx.rect(250, 250, -50, -200);
+    ctx.fill();
+}
+  </script>
+</head>
+<body>
+  <div><canvas id="c1" width="300" height="300"></canvas></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/canvas/evenodd-fill-sanity.html
@@ -0,0 +1,43 @@
+<html>
+<head>
+  <script type="text/javascript">
+function assert(cond, msg) { if (!cond) { throw msg; } }
+window.onload = function() {
+    try {
+        var ctx = document.getElementById("c1").getContext("2d");
+
+        assert("nonzero" == ctx.mozFillRule,
+               "Default fillRule is 'nonzero'");
+
+        ctx.mozFillRule = "evenodd";
+        assert("evenodd" == ctx.mozFillRule,
+               "fillRule understands 'evenodd'");
+        ctx.mozFillRule = "nonzero";
+
+        ctx.mozFillRule = "garbageLSKJDF 29879234";
+        assert("nonzero" == ctx.mozFillRule,
+               "Garbage fillRule string has no effect");
+
+        ctx.mozFillRule = "evenodd";
+        ctx.mozFillRule = "garbageLSKJDF 29879234";
+        assert("evenodd" == ctx.mozFillRule,
+               "Garbage fillRule string has no effect");
+        ctx.mozFillRule = "nonzero";
+
+        ctx.save();
+        ctx.mozFillRule = "evenodd";
+        ctx.restore();
+        assert("nonzero" == ctx.mozFillRule,
+               "fillRule was saved then restored");
+    } catch (e) {
+        document.body.innerHTML = "FAIL: "+ e.toString();
+        return;
+    }
+    document.body.innerHTML = "Pass";
+}
+  </script>
+</head>
+<body>
+  <div><canvas id="c1" width="300" height="300"></canvas></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/canvas/nonzero-fill-1.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+  <script type="text/javascript">
+window.onload = function() {
+    var ctx = document.getElementById("c1").getContext("2d");
+    ctx.rect(50, 50, 200, 200);
+    ctx.rect(100, 100, 100, 100);
+    ctx.fill();
+}
+  </script>
+</head>
+<body>
+  <div><canvas id="c1" width="300" height="300"></canvas></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/canvas/nonzero-fill-2.html
@@ -0,0 +1,26 @@
+<html>
+<head>
+  <script type="text/javascript">
+window.onload = function() {
+    var ctx = document.getElementById("c1").getContext("2d");
+
+    ctx.moveTo(50, 50);
+    ctx.lineTo(250, 50);
+    ctx.lineTo(250, 250);
+    ctx.lineTo(50, 250);
+    ctx.lineTo(50, 50);
+
+    ctx.moveTo(100, 100);
+    ctx.lineTo(100, 200);
+    ctx.lineTo(200, 200);
+    ctx.lineTo(200, 100);
+    ctx.lineTo(100, 100);
+
+    ctx.fill();
+}
+  </script>
+</head>
+<body>
+  <div><canvas id="c1" width="300" height="300"></canvas></div>
+</body>
+</html>
--- a/layout/reftests/canvas/reftest.list
+++ b/layout/reftests/canvas/reftest.list
@@ -42,8 +42,14 @@ fails-if(Android) != text-font-lang.html
 
 == strokeText-path.html strokeText-path-ref.html
 
 # gradient off-by-one, fails on windows and linux
 == linear-gradient-1a.html linear-gradient-1-ref.html
 fails-if(cocoaWidget) == linear-gradient-1b.html linear-gradient-1-ref.html
 
 == zero-dimensions.html zero-dimensions-ref.html
+
+== evenodd-fill-sanity.html data:text/html,<body>Pass
+!= evenodd-fill-1.html nonzero-fill-1.html
+== evenodd-fill-1.html evenodd-fill-ref.html
+== evenodd-fill-2.html evenodd-fill-ref.html
+== evenodd-fill-3.html nonzero-fill-2.html
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -642,12 +642,12 @@ pref("urlclassifier.confirm-age", 2700);
 pref("urlclassifier.updatecachemax", 4194304);
 
 // URL for checking the reason for a malware warning.
 pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
 #endif
 
 // True if this is the first time we are showing about:firstrun
 pref("browser.firstrun.show.uidiscovery", true);
-pref("browser.firstrun.show.localepicker", false);
+pref("browser.firstrun.show.localepicker", true);
 
 // initiated by a user
 pref("content.ime.strict_policy", true);
--- a/mobile/chrome/content/Util.js
+++ b/mobile/chrome/content/Util.js
@@ -157,17 +157,17 @@ let Util = {
   },
 
   isParentProcess: function isInParentProcess() {
     let appInfo = Cc["@mozilla.org/xre/app-info;1"];
     return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
   },
 
   isTablet: function isTablet() {
-    let dpi = Util.getWindowUtils(window).displayDPI;
+    let dpi = this.displayDPI;
     if (dpi <= 96)
       return (window.innerWidth > 1024);
 
     // See the tablet_panel_minwidth from mobile/themes/core/defines.inc
     let tablet_panel_minwidth = 124;
     let dpmm = 25.4 * window.innerWidth / dpi;
     return (dpmm >= tablet_panel_minwidth);
   },
@@ -183,16 +183,22 @@ let Util = {
   },
 
   get isKeyboardOpened() {
     let isChromeWindow = this.isParentProcess() && window["ViewableAreaObserver"];
     if (isChromeWindow)
       return ViewableAreaObserver.isKeyboardOpened;
 
     return (sendSyncMessage("Content:IsKeyboardOpened", {}))[0];
+  },
+
+  // because this uses the global window, will only work in the parent process
+  get displayDPI() function() {
+    delete this.displayDPI;
+    return this.displayDPI = this.getWindowUtils(window).displayDPI;
   }
 };
 
 
 /**
  * Helper class to nsITimer that adds a little more pizazz.  Callback can be an
  * object with a notify method or a function.
  */
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -359,17 +359,17 @@ var BrowserUI = {
   _isKeyboardFullscreen: function _isKeyboardFullscreen() {
 #ifdef ANDROID
     if (!Util.isPortrait()) {
       switch (Services.prefs.getIntPref("widget.ime.android.landscape_fullscreen")) {
         case 1:
           return true;
         case -1: {
           let threshold = Services.prefs.getIntPref("widget.ime.android.fullscreen_threshold");
-          let dpi = Util.getWindowUtils(window).displayDPI;
+          let dpi = Util.displayDPI;
           return (window.innerHeight * 100 < threshold * dpi);
         }
       }
     }
 #endif
     return false;
   },
 
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -1107,17 +1107,17 @@ var Browser = {
 
   // The device-pixel-to-CSS-px ratio used to adjust meta viewport values.
   // This is higher on higher-dpi displays, so pages stay about the same physical size.
   getScaleRatio: function getScaleRatio() {
     let prefValue = Services.prefs.getIntPref("browser.viewport.scaleRatio");
     if (prefValue > 0)
       return prefValue / 100;
 
-    let dpi = this.windowUtils.displayDPI;
+    let dpi = Utils.displayDPI;
     if (dpi < 200) // Includes desktop displays, and LDPI and MDPI Android devices
       return 1;
     else if (dpi < 300) // Includes Nokia N900, and HDPI Android devices
       return 1.5;
 
     // For very high-density displays like the iPhone 4, calculate an integer ratio.
     return Math.floor(dpi / 150);
   },
@@ -1804,17 +1804,17 @@ const ContentTouchHandler = {
   },
 
   touchTimeout: null,
   canCancelPan: false,
   clickPrevented: false,
   panningPrevented: false,
 
   updateCanCancel: function(aX, aY) {
-    let dpi = Browser.windowUtils.displayDPI;
+    let dpi = Utils.displayDPI;
 
     const kSafetyX = Services.prefs.getIntPref("dom.w3c_touch_events.safetyX") / 240 * dpi;
     const kSafetyY = Services.prefs.getIntPref("dom.w3c_touch_events.safetyY") / 240 * dpi;
     let browser = getBrowser();
     let bcr = browser.getBoundingClientRect();
     let rect = new Rect(0, 0, window.innerWidth, window.innerHeight);
     rect.restrictTo(Rect.fromRect(bcr));
 
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -63,17 +63,21 @@ const ElementTouchHelper = {
   get weight() {
     delete this.weight;
     return this.weight = { "visited": Services.prefs.getIntPref("browser.ui.touch.weight.visited")
                          };
   },
 
   /* Retrieve the closest element to a point by looking at borders position */
   getClosest: function getClosest(aWindowUtils, aX, aY) {
-    let dpiRatio = aWindowUtils.displayDPI / kReferenceDpi;
+    // cached for the child process, since Utils.displayDPI is only available in the parent
+    if (!this.dpiRatio)
+      this.dpiRatio = aWindowUtils.displayDPI / kReferenceDpi;
+
+    let dpiRatio = this.dpiRatio;
 
     let target = aWindowUtils.elementFromPoint(aX, aY,
                                                true,   /* ignore root scroll frame*/
                                                false); /* don't flush layout */
 
     // return early if the click is just over a clickable element
     if (this._isElementClickable(target))
       return target;
--- a/mobile/chrome/content/input.js
+++ b/mobile/chrome/content/input.js
@@ -113,17 +113,17 @@ function MouseModule() {
 
   this._kinetic = new KineticController(this._dragBy.bind(this),
                                         this._kineticStop.bind(this));
 
   this._singleClickTimeout = new Util.Timeout(this._doSingleClick.bind(this));
   this._mouseOverTimeout = new Util.Timeout(this._doMouseOver.bind(this));
   this._longClickTimeout = new Util.Timeout(this._doLongClick.bind(this));
 
-  this._doubleClickRadius = Util.getWindowUtils(window).displayDPI * kDoubleClickRadius;
+  this._doubleClickRadius = Util.displayDPI * kDoubleClickRadius;
 
   window.addEventListener("mousedown", this, true);
   window.addEventListener("mouseup", this, true);
   window.addEventListener("mousemove", this, true);
   window.addEventListener("contextmenu", this, false);
   window.addEventListener("CancelTouchSequence", this, true);
 }
 
@@ -565,17 +565,17 @@ MouseModule.prototype = {
       + 'length=' + this._downUpEvents.length + ', '
       + '\n\ttargetScroller=' + this._targetScrollInterface + '}';
   }
 };
 
 var ScrollUtils = {
   // threshold in pixels for sensing a tap as opposed to a pan
   get tapRadius() {
-    let dpi = Util.getWindowUtils(window).displayDPI;
+    let dpi = Util.displayDPI;
 
     delete this.tapRadius;
     return this.tapRadius = Services.prefs.getIntPref("ui.dragThresholdX") / 240 * dpi;
   },
 
   /**
    * Walk up (parentward) the DOM tree from elem in search of a scrollable element.
    * Return the element and its scroll interface if one is found, two nulls otherwise.
@@ -681,17 +681,17 @@ var ScrollUtils = {
 };
 
 /**
  * DragData handles processing drags on the screen, handling both
  * locking of movement on one axis, and click detection.
  */
 function DragData() {
   this._domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-  this._lockRevertThreshold = Util.getWindowUtils(window).displayDPI * kAxisLockRevertThreshold;
+  this._lockRevertThreshold = Util.displayDPI * kAxisLockRevertThreshold;
   this.reset();
 };
 
 DragData.prototype = {
   reset: function reset() {
     this.dragging = false;
     this.sX = null;
     this.sY = null;
--- a/mobile/chrome/tests/browser_tabs.js
+++ b/mobile/chrome/tests/browser_tabs.js
@@ -143,17 +143,17 @@ function tab_undo() {
   checkExpectedSize();
   tab_on_undo();
 }
 
 function tab_on_undo() {
   let undoBox = document.getElementById("tabs")._tabsUndo;
   is(undoBox.firstChild, null, "It should be no tab in the undo box");
 
-  Browser.loadURI("about:firstrun");
+  Browser.loadURI("about:home");
   is(undoBox.firstChild, null, "It should be no tab in the undo box when opening a new local page");
 
   // loadURI will open a new tab so ensure new_tab_05 point to the newly opened tab
   new_tab_05 = Browser.selectedTab;
 
   let tabs = [new_tab_01, new_tab_02, new_tab_03, new_tab_04, new_tab_05];
   while (tabs.length)
     Browser.closeTab(tabs.shift(), { forceClose: true });
--- a/mobile/components/AboutRedirector.js
+++ b/mobile/components/AboutRedirector.js
@@ -115,22 +115,16 @@ AboutGeneric.prototype = {
 };
 
 function AboutEmpty() {}
 AboutEmpty.prototype = {
   __proto__: AboutGeneric.prototype,
   classID: Components.ID("{433d2d75-5923-49b0-854d-f37267b03dc7}")
 }
 
-function AboutFirstrun() {}
-AboutFirstrun.prototype = {
-  __proto__: AboutGeneric.prototype,
-  classID: Components.ID("{077ea23e-0f22-4168-a744-8e444b560197}")
-}
-
 function AboutFennec() {}
 AboutFennec.prototype = {
   __proto__: AboutGeneric.prototype,
   classID: Components.ID("{842a6d11-b369-4610-ba66-c3b5217e82be}")
 }
 
 function AboutFirefox() {}
 AboutFirefox.prototype = {
@@ -157,11 +151,11 @@ AboutHome.prototype = {
 }
 
 function AboutBlocked() {}
 AboutBlocked.prototype = {
   __proto__: AboutGeneric.prototype,
   classID: Components.ID("{88fd40b6-c5c2-4120-9238-f2cb9ff98928}")
 }
 
-const components = [AboutEmpty, AboutFirstrun, AboutFennec, AboutRights,
+const components = [AboutEmpty, AboutFennec, AboutRights,
                     AboutCertError, AboutFirefox, AboutHome, AboutBlocked];
 const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
--- a/mobile/components/MobileComponents.manifest
+++ b/mobile/components/MobileComponents.manifest
@@ -1,13 +1,11 @@
 # AboutRedirector.js
 component {433d2d75-5923-49b0-854d-f37267b03dc7} AboutRedirector.js
 contract @mozilla.org/network/protocol/about;1?what=empty {433d2d75-5923-49b0-854d-f37267b03dc7}
-component {077ea23e-0f22-4168-a744-8e444b560197} AboutRedirector.js
-contract @mozilla.org/network/protocol/about;1?what=firstrun {077ea23e-0f22-4168-a744-8e444b560197}
 component {842a6d11-b369-4610-ba66-c3b5217e82be} AboutRedirector.js
 contract @mozilla.org/network/protocol/about;1?what=fennec {842a6d11-b369-4610-ba66-c3b5217e82be}
 component {dd40c467-d206-4f22-9215-8fcc74c74e38} AboutRedirector.js
 contract @mozilla.org/network/protocol/about;1?what=firefox {dd40c467-d206-4f22-9215-8fcc74c74e38}
 component {3b988fbf-ec97-4e1c-a5e4-573d999edc9c} AboutRedirector.js
 contract @mozilla.org/network/protocol/about;1?what=rights {3b988fbf-ec97-4e1c-a5e4-573d999edc9c}
 component {972efe64-8ac0-4e91-bdb0-22835d987815} AboutRedirector.js
 contract @mozilla.org/network/protocol/about;1?what=certerror {972efe64-8ac0-4e91-bdb0-22835d987815}
--- a/mobile/locales/en-US/profile/bookmarks.inc
+++ b/mobile/locales/en-US/profile/bookmarks.inc
@@ -6,20 +6,16 @@
 
 # LOCALIZATION NOTE: Some of these URLs are currently 404s, but should be coming
 # online shortly.
 
 # LOCALIZATION NOTE (bookmarks_title):
 # title for the folder that will contains the default bookmarks
 #define bookmarks_title Mobile
 
-# LOCALIZATION NOTE (bookmarks_welcome):
-# link title for about:firstrun
-#define bookmarks_welcome Firefox: Welcome
-
 # LOCALIZATION NOTE (bookmarks_aboutBrowser):
 # link title for about:fennec
 #define bookmarks_aboutBrowser Firefox: About your browser
 
 # LOCALIZATION NOTE (bookmarks_addons):
 # link title for https://addons.mozilla.org/en-US/mobile
 #define bookmarks_addons Firefox: Customize with add-ons
 
--- a/mobile/locales/generic/profile/bookmarks.json.in
+++ b/mobile/locales/generic/profile/bookmarks.json.in
@@ -1,16 +1,13 @@
 #filter substitution
 {"type":"text/x-moz-place-container","root":"placesRoot","children":
   [{"type":"text/x-moz-place-container","title":"@bookmarks_title@","annos":[{"name":"mobile/bookmarksRoot","expires":4,"type":1,"value":1}],
     "children":
      [
-       {          "title":"@bookmarks_welcome@",  "type":"text/x-moz-place", "uri":"about:firstrun",
-        "iconUri":"chrome://branding/content/favicon32.png"
-       },
        {"index":1,"title":"@bookmarks_aboutBrowser@", "type":"text/x-moz-place", "uri":"about:firefox",
         "iconUri":"chrome://branding/content/favicon32.png"
        },
        {"index":2,"title":"@bookmarks_addons@",   "type":"text/x-moz-place", "uri":"https://addons.mozilla.org/@AB_CD@/mobile/",
         "icon":"data:image/png;base64,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"
        },
        {"index":3,"title":"@bookmarks_support@",  "type":"text/x-moz-place", "uri":"http://support.mozilla.com/@AB_CD@/mobile",
         "icon":"data:image/png;base64,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"
--- a/modules/libpr0n/src/imgRequest.cpp
+++ b/modules/libpr0n/src/imgRequest.cpp
@@ -221,16 +221,18 @@ nsresult imgRequest::Init(nsIURI *aURI,
   mProperties = do_CreateInstance("@mozilla.org/properties;1");
 
   mStatusTracker = new imgStatusTracker(nsnull);
 
   mURI = aURI;
   mKeyURI = aKeyURI;
   mRequest = aRequest;
   mChannel = aChannel;
+  mTimedChannel = do_QueryInterface(mChannel);
+
   mChannel->GetNotificationCallbacks(getter_AddRefs(mPrevChannelSink));
 
   NS_ASSERTION(mPrevChannelSink != this,
                "Initializing with a channel that already calls back to us!");
 
   mChannel->SetNotificationCallbacks(this);
 
   mCacheEntry = aCacheEntry;
@@ -951,16 +953,17 @@ NS_IMETHODIMP imgRequest::OnStopRequest(
   }
 
   /* notify the kids */
   nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mObservers);
   while (srIter.HasMore()) {
     statusTracker.SendStopRequest(srIter.GetNext(), lastPart, status);
   }
 
+  mTimedChannel = nsnull;
   return NS_OK;
 }
 
 /* prototype for these defined below */
 static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
                                          PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount);
 
 /** nsIStreamListener methods **/
@@ -1272,16 +1275,17 @@ imgRequest::OnRedirectVerifyCallback(nsr
   if (NS_FAILED(result)) {
       mRedirectCallback->OnRedirectVerifyCallback(result);
       mRedirectCallback = nsnull;
       mNewRedirectChannel = nsnull;
       return NS_OK;
   }
 
   mChannel = mNewRedirectChannel;
+  mTimedChannel = do_QueryInterface(mChannel);
   mNewRedirectChannel = nsnull;
 
   // Don't make any cache changes if we're going to point to the same thing. We
   // compare specs and not just URIs here because URIs that compare as
   // .Equals() might have different hashes.
   nsCAutoString oldspec;
   if (mKeyURI)
     mKeyURI->GetSpec(oldspec);
--- a/modules/libpr0n/src/imgRequest.h
+++ b/modules/libpr0n/src/imgRequest.h
@@ -46,16 +46,17 @@
 #include "nsIChannelEventSink.h"
 #include "nsIContentSniffer.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIRequest.h"
 #include "nsIProperties.h"
 #include "nsIStreamListener.h"
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
+#include "nsITimedChannel.h"
 
 #include "nsCategoryCache.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTObserverArray.h"
 #include "nsWeakReference.h"
 #include "ImageErrors.h"
 #include "imgIRequest.h"
@@ -214,16 +215,18 @@ private:
   nsRefPtr<mozilla::imagelib::Image> mImage;
   nsCOMPtr<nsIProperties> mProperties;
   nsCOMPtr<nsISupports> mSecurityInfo;
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIInterfaceRequestor> mPrevChannelSink;
 
   nsTObserverArray<imgRequestProxy*> mObservers;
 
+  nsCOMPtr<nsITimedChannel> mTimedChannel;
+
   nsCString mContentType;
 
   nsRefPtr<imgCacheEntry> mCacheEntry; /* we hold on to this to this so long as we have observers */
 
   void *mCacheId;
 
   void *mLoadId;
   PRTime mLoadTime;
--- a/modules/libpr0n/src/imgRequestProxy.cpp
+++ b/modules/libpr0n/src/imgRequestProxy.cpp
@@ -52,18 +52,27 @@
 #include "Image.h"
 #include "ImageErrors.h"
 #include "ImageLogging.h"
 
 #include "nspr.h"
 
 using namespace mozilla::imagelib;
 
-NS_IMPL_ISUPPORTS4(imgRequestProxy, imgIRequest, nsIRequest,
-                   nsISupportsPriority, nsISecurityInfoProvider)
+NS_IMPL_ADDREF(imgRequestProxy)
+NS_IMPL_RELEASE(imgRequestProxy)
+
+NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest)
+  NS_INTERFACE_MAP_ENTRY(imgIRequest)
+  NS_INTERFACE_MAP_ENTRY(nsIRequest)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
+  NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel, TimedChannel() != nsnull)
+NS_INTERFACE_MAP_END
 
 imgRequestProxy::imgRequestProxy() :
   mOwner(nsnull),
   mURI(nsnull),
   mImage(nsnull),
   mPrincipal(nsnull),
   mListener(nsnull),
   mLoadFlags(nsIRequest::LOAD_NORMAL),
--- a/modules/libpr0n/src/imgRequestProxy.h
+++ b/modules/libpr0n/src/imgRequestProxy.h
@@ -43,16 +43,17 @@
 #include "imgIRequest.h"
 #include "imgIDecoderObserver.h"
 #include "nsISecurityInfoProvider.h"
 
 #include "nsIRequestObserver.h"
 #include "nsIChannel.h"
 #include "nsILoadGroup.h"
 #include "nsISupportsPriority.h"
+#include "nsITimedChannel.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 
 #include "imgRequest.h"
 
 #define NS_IMGREQUESTPROXY_CID \
 { /* 20557898-1dd2-11b2-8f65-9c462ee2bc95 */         \
@@ -66,24 +67,28 @@ class imgRequestNotifyRunnable;
 class imgStatusNotifyRunnable;
 
 namespace mozilla {
 namespace imagelib {
 class Image;
 } // namespace imagelib
 } // namespace mozilla
 
-class imgRequestProxy : public imgIRequest, public nsISupportsPriority, public nsISecurityInfoProvider
+class imgRequestProxy : public imgIRequest, 
+                        public nsISupportsPriority, 
+                        public nsISecurityInfoProvider,
+                        public nsITimedChannel
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGIREQUEST
   NS_DECL_NSIREQUEST
   NS_DECL_NSISUPPORTSPRIORITY
   NS_DECL_NSISECURITYINFOPROVIDER
+  // nsITimedChannel declared below
 
   imgRequestProxy();
   virtual ~imgRequestProxy();
 
   // Callers to Init or ChangeOwner are required to call NotifyListener after
   // (although not immediately after) doing so.
   nsresult Init(imgRequest *request, nsILoadGroup *aLoadGroup,
                 mozilla::imagelib::Image* aImage,
@@ -191,16 +196,26 @@ protected:
   }
 
   // Return the imgStatusTracker associated with mOwner and/or mImage. It may
   // live either on mOwner or mImage, depending on whether
   //   (a) we have an mOwner at all
   //   (b) whether mOwner has instantiated its image yet
   imgStatusTracker& GetStatusTracker();
 
+  nsITimedChannel* TimedChannel()
+  {
+    if (!mOwner)
+      return nsnull;
+    return mOwner->mTimedChannel;
+  }
+
+public:
+  NS_FORWARD_SAFE_NSITIMEDCHANNEL(TimedChannel())
+
 private:
   friend class imgCacheValidator;
 
   // We maintain the following invariant:
   // The proxy is registered at most with a single imgRequest as an observer,
   // and whenever it is, mOwner points to that object. This helps ensure that
   // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
   // from whatever request it was registered with (if any). This, in turn,
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -339,16 +339,19 @@ struct ParamTraits<PRNetAddr>
     } else if (aParam.raw.family == PR_AF_INET6) {
       WriteParam(aMsg, aParam.ipv6.port);
       WriteParam(aMsg, aParam.ipv6.flowinfo);
       WriteParam(aMsg, aParam.ipv6.ip.pr_s6_addr64[0]);
       WriteParam(aMsg, aParam.ipv6.ip.pr_s6_addr64[1]);
       WriteParam(aMsg, aParam.ipv6.scope_id);
 #if defined(XP_UNIX) || defined(XP_OS2)
     } else if (aParam.raw.family == PR_AF_LOCAL) {
+      // Train's already off the rails:  let's get a stack trace at least...
+      NS_RUNTIMEABORT("Error: please post stack trace to "
+                      "https://bugzilla.mozilla.org/show_bug.cgi?id=661158");
       aMsg->WriteBytes(aParam.local.path, sizeof(aParam.local.path));
 #endif
     }
 
     /* If we get here without hitting any of the cases above, there's not much
      * we can do but let the deserializer fail when it gets this message */
   }
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -82,16 +82,20 @@ HttpBaseChannel::HttpBaseChannel()
   , mTracingEnabled(PR_TRUE)
   , mTimingEnabled(PR_FALSE)
   , mRedirectedCachekeys(nsnull)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // grab a reference to the handler to ensure that it doesn't go away.
   NS_ADDREF(gHttpHandler);
+
+  // Subfields of unions cannot be targeted in an initializer list
+  mSelfAddr.raw.family = PR_AF_UNSPEC;
+  mPeerAddr.raw.family = PR_AF_UNSPEC;
 }
 
 HttpBaseChannel::~HttpBaseChannel()
 {
   LOG(("Destroying HttpBaseChannel @%x\n", this));
 
   // Make sure we don't leak
   CleanRedirectCacheChainIfNecessary();
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -133,19 +133,16 @@ nsHttpChannel::nsHttpChannel()
     , mCustomConditionalRequest(PR_FALSE)
     , mFallingBack(PR_FALSE)
     , mWaitingForRedirectCallback(PR_FALSE)
     , mRequestTimeInitialized(PR_FALSE)
 {
     LOG(("Creating nsHttpChannel [this=%p]\n", this));
     mChannelCreationTime = PR_Now();
     mChannelCreationTimestamp = mozilla::TimeStamp::Now();
-    // Subfields of unions cannot be targeted in an initializer list
-    mSelfAddr.raw.family = PR_AF_UNSPEC;
-    mPeerAddr.raw.family = PR_AF_UNSPEC;
 }
 
 nsHttpChannel::~nsHttpChannel()
 {
     LOG(("Destroying nsHttpChannel [this=%p]\n", this));
 
     if (mAuthProvider)
         mAuthProvider->Disconnect(NS_ERROR_ABORT);