Backed out 8 changesets (bug 1201796, bug 1196066) for mulet gij(28) failures CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Fri, 18 Sep 2015 13:01:25 -0700
changeset 295931 9244da13f5e871e49c066a796f9cf28239a8200e
parent 295930 2a56db89602545da44a7dd7396a683ddf1d695f1
child 295932 4e8dd2742625a606946026babc82478765ed51de
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1201796, 1196066
milestone43.0a1
backs out159d5d2946d35063ea553e6f9cc081db547f7985
9589882189767a704768a6a08a6f4e029647d629
494e7553d6419f9dc7be7003a2448eb508469691
d58149411b7df9eb757f4b6089b6f73b90fa886a
35bd769b49f8cabf9dff6d3b2ec0976fe01f85cf
e4f3d4279b4cb22c49d9aae0e8bb33f2a2a86bfc
ca467297fa07c6ae3ac42eab2b5f38f2a33152cc
b4851ce6637da13db086a93bad3a0faa5fde579b
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 8 changesets (bug 1201796, bug 1196066) for mulet gij(28) failures CLOSED TREE Backed out changeset 159d5d2946d3 (bug 1201796) Backed out changeset 958988218976 (bug 1201796) Backed out changeset 494e7553d641 (bug 1201796) Backed out changeset d58149411b7d (bug 1201796) Backed out changeset 35bd769b49f8 (bug 1196066) Backed out changeset e4f3d4279b4c (bug 1196066) Backed out changeset ca467297fa07 (bug 1196066) Backed out changeset b4851ce6637d (bug 1196066)
image/ClippedImage.cpp
image/ClippedImage.h
image/DynamicImage.cpp
image/FrozenImage.cpp
image/FrozenImage.h
image/ImageFactory.cpp
image/ImageWrapper.cpp
image/OrientedImage.cpp
image/OrientedImage.h
image/RasterImage.cpp
image/RasterImage.h
image/StreamingLexer.h
image/VectorImage.cpp
image/decoders/nsICODecoder.cpp
image/decoders/nsICODecoder.h
image/imgIContainer.idl
image/imgTools.cpp
image/test/crashtests/crashtests.list
image/test/crashtests/invalid_ico_height.ico
image/test/crashtests/invalid_ico_width.ico
image/test/gtest/TestDecoders.cpp
image/test/gtest/TestStreamingLexer.cpp
image/test/gtest/moz.build
image/test/mochitest/test_has_transparency.html
image/test/reftest/ico/cur/pointer.cur
image/test/reftest/ico/ico-bmp-corrupted/invalid_ico_height.ico
image/test/reftest/ico/ico-bmp-corrupted/invalid_ico_width.ico
image/test/reftest/ico/ico-bmp-corrupted/reftest.list
image/test/reftest/ico/ico-mixed/reftest.list
image/test/reftest/ico/ico-png/ico-size-1x1-png.ico
image/test/reftest/ico/ico-png/ico-size-256x256-png.ico
image/test/reftest/ico/ico-png/transparent-png.ico
image/test/unit/image1png16x16.jpg
image/test/unit/image2jpg16x16.png
image/test/unit/test_imgtools.js
toolkit/components/places/tests/favicons/expected-favicon-big32.jpg.png
toolkit/components/places/tests/favicons/expected-favicon-big64.png.png
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -215,26 +215,16 @@ ClippedImage::GetIntrinsicRatio(nsSize* 
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 ClippedImage::GetFrame(uint32_t aWhichFrame,
                        uint32_t aFlags)
 {
   return GetFrameInternal(mClip.Size(), Nothing(), aWhichFrame, aFlags);
 }
 
-NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
-ClippedImage::GetFrameAtSize(const IntSize& aSize,
-                             uint32_t aWhichFrame,
-                             uint32_t aFlags)
-{
-  // XXX(seth): It'd be nice to support downscale-during-decode for this case,
-  // but right now we just fall back to the intrinsic size.
-  return GetFrame(aWhichFrame, aFlags);
-}
-
 already_AddRefed<SourceSurface>
 ClippedImage::GetFrameInternal(const nsIntSize& aSize,
                                const Maybe<SVGImageContext>& aSVGContext,
                                uint32_t aWhichFrame,
                                uint32_t aFlags)
 {
   if (!ShouldClip()) {
     return InnerImage()->GetFrame(aWhichFrame, aFlags);
--- a/image/ClippedImage.h
+++ b/image/ClippedImage.h
@@ -32,20 +32,16 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD GetWidth(int32_t* aWidth) override;
   NS_IMETHOD GetHeight(int32_t* aHeight) override;
   NS_IMETHOD GetIntrinsicSize(nsSize* aSize) override;
   NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) override;
   NS_IMETHOD_(already_AddRefed<SourceSurface>)
     GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override;
-  NS_IMETHOD_(already_AddRefed<SourceSurface>)
-    GetFrameAtSize(const gfx::IntSize& aSize,
-                   uint32_t aWhichFrame,
-                   uint32_t aFlags) override;
   NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager,
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -163,36 +163,28 @@ DynamicImage::GetAnimated(bool* aAnimate
   return NS_OK;
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 DynamicImage::GetFrame(uint32_t aWhichFrame,
                        uint32_t aFlags)
 {
   gfxIntSize size(mDrawable->Size());
-  return GetFrameAtSize(IntSize(size.width, size.height),
-                        aWhichFrame,
-                        aFlags);
-}
 
-NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
-DynamicImage::GetFrameAtSize(const IntSize& aSize,
-                             uint32_t aWhichFrame,
-                             uint32_t aFlags)
-{
   RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
-    CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
+    CreateOffscreenContentDrawTarget(IntSize(size.width, size.height),
+                                     SurfaceFormat::B8G8R8A8);
   if (!dt) {
     gfxWarning() <<
       "DynamicImage::GetFrame failed in CreateOffscreenContentDrawTarget";
     return nullptr;
   }
   nsRefPtr<gfxContext> context = new gfxContext(dt);
 
-  auto result = Draw(context, aSize, ImageRegion::Create(aSize),
+  auto result = Draw(context, size, ImageRegion::Create(size),
                      aWhichFrame, GraphicsFilter::FILTER_NEAREST,
                      Nothing(), aFlags);
 
   return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
 }
 
 NS_IMETHODIMP_(bool)
 DynamicImage::IsOpaque()
--- a/image/FrozenImage.cpp
+++ b/image/FrozenImage.cpp
@@ -39,24 +39,16 @@ FrozenImage::GetAnimated(bool* aAnimated
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 FrozenImage::GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags)
 {
   return InnerImage()->GetFrame(FRAME_FIRST, aFlags);
 }
 
-NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
-FrozenImage::GetFrameAtSize(const IntSize& aSize,
-                            uint32_t aWhichFrame,
-                            uint32_t aFlags)
-{
-  return InnerImage()->GetFrameAtSize(aSize, FRAME_FIRST, aFlags);
-}
-
 NS_IMETHODIMP_(bool)
 FrozenImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 {
   return false;
 }
 
 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
 FrozenImage::GetImageContainer(layers::LayerManager* aManager, uint32_t aFlags)
--- a/image/FrozenImage.h
+++ b/image/FrozenImage.h
@@ -32,20 +32,16 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual void IncrementAnimationConsumers() override;
   virtual void DecrementAnimationConsumers() override;
 
   NS_IMETHOD GetAnimated(bool* aAnimated) override;
   NS_IMETHOD_(already_AddRefed<SourceSurface>)
     GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override;
-  NS_IMETHOD_(already_AddRefed<SourceSurface>)
-    GetFrameAtSize(const gfx::IntSize& aSize,
-                   uint32_t aWhichFrame,
-                   uint32_t aFlags) override;
   NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager,
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
--- a/image/ImageFactory.cpp
+++ b/image/ImageFactory.cpp
@@ -33,17 +33,16 @@ ImageFactory::Initialize()
 { }
 
 static bool
 ShouldDownscaleDuringDecode(const nsCString& aMimeType)
 {
   DecoderType type = DecoderFactory::GetDecoderType(aMimeType.get());
   return type == DecoderType::JPEG ||
          type == DecoderType::ICON ||
-         type == DecoderType::ICO ||
          type == DecoderType::PNG ||
          type == DecoderType::BMP ||
          type == DecoderType::GIF;
 }
 
 static uint32_t
 ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
 {
@@ -138,23 +137,17 @@ ImageFactory::CreateAnonymousImage(const
   nsresult rv;
 
   nsRefPtr<RasterImage> newImage = new RasterImage();
 
   nsRefPtr<ProgressTracker> newTracker = new ProgressTracker();
   newTracker->SetImage(newImage);
   newImage->SetProgressTracker(newTracker);
 
-  uint32_t imageFlags = Image::INIT_FLAG_SYNC_LOAD;
-  if (gfxPrefs::ImageDownscaleDuringDecodeEnabled() &&
-      ShouldDownscaleDuringDecode(aMimeType)) {
-    imageFlags |= Image::INIT_FLAG_DOWNSCALE_DURING_DECODE;
-  }
-
-  rv = newImage->Init(aMimeType.get(), imageFlags);
+  rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_SYNC_LOAD);
   if (NS_FAILED(rv)) {
     return BadImage("RasterImage::Init failed", newImage);
   }
 
   return newImage.forget();
 }
 
 /* static */ already_AddRefed<MultipartImage>
--- a/image/ImageWrapper.cpp
+++ b/image/ImageWrapper.cpp
@@ -169,24 +169,16 @@ ImageWrapper::GetAnimated(bool* aAnimate
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 ImageWrapper::GetFrame(uint32_t aWhichFrame,
                        uint32_t aFlags)
 {
   return mInnerImage->GetFrame(aWhichFrame, aFlags);
 }
 
-NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
-ImageWrapper::GetFrameAtSize(const IntSize& aSize,
-                             uint32_t aWhichFrame,
-                             uint32_t aFlags)
-{
-  return mInnerImage->GetFrameAtSize(aSize, aWhichFrame, aFlags);
-}
-
 NS_IMETHODIMP_(bool)
 ImageWrapper::IsOpaque()
 {
   return mInnerImage->IsOpaque();
 }
 
 NS_IMETHODIMP_(bool)
 ImageWrapper::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -117,26 +117,16 @@ OrientedImage::GetFrame(uint32_t aWhichF
   ctx->Multiply(OrientationMatrix(size));
   gfxUtils::DrawPixelSnapped(ctx, drawable, size,
                              ImageRegion::Create(size),
                              surfaceFormat, GraphicsFilter::FILTER_FAST);
 
   return target->Snapshot();
 }
 
-NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
-OrientedImage::GetFrameAtSize(const IntSize& aSize,
-                              uint32_t aWhichFrame,
-                              uint32_t aFlags)
-{
-  // XXX(seth): It'd be nice to support downscale-during-decode for this case,
-  // but right now we just fall back to the intrinsic size.
-  return GetFrame(aWhichFrame, aFlags);
-}
-
 NS_IMETHODIMP_(bool)
 OrientedImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 {
   if (mOrientation.IsIdentity()) {
     return InnerImage()->IsImageContainerAvailable(aManager, aFlags);
   }
   return false;
 }
--- a/image/OrientedImage.h
+++ b/image/OrientedImage.h
@@ -29,20 +29,16 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD GetWidth(int32_t* aWidth) override;
   NS_IMETHOD GetHeight(int32_t* aHeight) override;
   NS_IMETHOD GetIntrinsicSize(nsSize* aSize) override;
   NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) override;
   NS_IMETHOD_(already_AddRefed<SourceSurface>)
     GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override;
-  NS_IMETHOD_(already_AddRefed<SourceSurface>)
-    GetFrameAtSize(const gfx::IntSize& aSize,
-                   uint32_t aWhichFrame,
-                   uint32_t aFlags) override;
   NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager,
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -693,68 +693,54 @@ RasterImage::CopyFrame(uint32_t aWhichFr
 
 //******************************************************************************
 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
  *                                   in uint32_t aFlags); */
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 RasterImage::GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags)
 {
-  return GetFrameInternal(mSize, aWhichFrame, aFlags).second().forget();
-}
-
-NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
-RasterImage::GetFrameAtSize(const IntSize& aSize,
-                            uint32_t aWhichFrame,
-                            uint32_t aFlags)
-{
-  return GetFrameInternal(aSize, aWhichFrame, aFlags).second().forget();
+  return GetFrameInternal(aWhichFrame, aFlags).second().forget();
 }
 
 Pair<DrawResult, RefPtr<SourceSurface>>
-RasterImage::GetFrameInternal(const IntSize& aSize,
-                              uint32_t aWhichFrame,
-                              uint32_t aFlags)
+RasterImage::GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags)
 {
   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
 
-  if (aSize.IsEmpty()) {
-    return MakePair(DrawResult::BAD_ARGS, RefPtr<SourceSurface>());
-  }
-
   if (aWhichFrame > FRAME_MAX_VALUE) {
     return MakePair(DrawResult::BAD_ARGS, RefPtr<SourceSurface>());
   }
 
   if (mError) {
     return MakePair(DrawResult::BAD_IMAGE, RefPtr<SourceSurface>());
   }
 
   // Get the frame. If it's not there, it's probably the caller's fault for
   // not waiting for the data to be loaded from the network or not passing
   // FLAG_SYNC_DECODE
   DrawableFrameRef frameRef =
-    LookupFrame(GetRequestedFrameIndex(aWhichFrame), aSize, aFlags);
+    LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags);
   if (!frameRef) {
     // The OS threw this frame away and we couldn't redecode it.
     return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
   }
 
   // If this frame covers the entire image, we can just reuse its existing
   // surface.
   RefPtr<SourceSurface> frameSurf;
-  if (!frameRef->NeedsPadding() &&
-      frameRef->GetSize() == aSize) {
+  IntRect frameRect = frameRef->GetRect();
+  if (frameRect.x == 0 && frameRect.y == 0 &&
+      frameRect.width == mSize.width &&
+      frameRect.height == mSize.height) {
     frameSurf = frameRef->GetSurface();
   }
 
   // The image doesn't have a usable surface because it's been optimized away or
-  // because it's a partial update frame from an animation. Create one. (In this
-  // case we fall back to returning a surface at our intrinsic size, even if a
-  // different size was originally specified.)
+  // because it's a partial update frame from an animation. Create one.
   if (!frameSurf) {
     frameSurf = CopyFrame(aWhichFrame, aFlags);
   }
 
   if (!frameRef->IsImageComplete()) {
     return MakePair(DrawResult::INCOMPLETE, Move(frameSurf));
   }
 
@@ -765,17 +751,17 @@ Pair<DrawResult, nsRefPtr<layers::Image>
 RasterImage::GetCurrentImage(ImageContainer* aContainer, uint32_t aFlags)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aContainer);
 
   DrawResult drawResult;
   RefPtr<SourceSurface> surface;
   Tie(drawResult, surface) =
-    GetFrameInternal(mSize, FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY);
+    GetFrameInternal(FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY);
   if (!surface) {
     // The OS threw out some or all of our buffer. We'll need to wait for the
     // redecode (which was automatically triggered by GetFrame) to complete.
     return MakePair(drawResult, nsRefPtr<layers::Image>());
   }
 
   CairoImage::Data cairoData;
   GetWidth(&cairoData.mSize.width);
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -260,19 +260,17 @@ private:
                                           const ImageRegion& aRegion,
                                           GraphicsFilter aFilter,
                                           uint32_t aFlags);
 
   already_AddRefed<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
                                              uint32_t aFlags);
 
   Pair<DrawResult, RefPtr<gfx::SourceSurface>>
-    GetFrameInternal(const gfx::IntSize& aSize,
-                     uint32_t aWhichFrame,
-                     uint32_t aFlags);
+    GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags);
 
   LookupResult LookupFrameInternal(uint32_t aFrameNum,
                                    const gfx::IntSize& aSize,
                                    uint32_t aFlags);
   DrawableFrameRef LookupFrame(uint32_t aFrameNum,
                                const nsIntSize& aSize,
                                uint32_t aFlags);
   uint32_t GetCurrentFrameIndex() const;
deleted file mode 100644
--- a/image/StreamingLexer.h
+++ /dev/null
@@ -1,355 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-/**
- * StreamingLexer is a lexing framework designed to make it simple to write
- * image decoders without worrying about the details of how the data is arriving
- * from the network.
- */
-
-#ifndef mozilla_image_StreamingLexer_h
-#define mozilla_image_StreamingLexer_h
-
-#include <algorithm>
-#include "mozilla/Assertions.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/Vector.h"
-
-namespace mozilla {
-namespace image {
-
-/// Buffering behaviors for StreamingLexer transitions.
-enum class BufferingStrategy
-{
-  BUFFERED,   // Data will be buffered and processed in one chunk.
-  UNBUFFERED  // Data will be processed as it arrives, in multiple chunks.
-};
-
-/// @return true if @aState is a terminal state.
-template <typename State>
-bool IsTerminalState(State aState)
-{
-  return aState == State::SUCCESS ||
-         aState == State::FAILURE;
-}
-
-/**
- * LexerTransition is a type used to give commands to the lexing framework.
- * Code that uses StreamingLexer can create LexerTransition values using the
- * static methods on Transition, and then return them to the lexing framework
- * for execution.
- */
-template <typename State>
-class LexerTransition
-{
-public:
-  State NextState() const { return mNextState; }
-  State UnbufferedState() const { return *mUnbufferedState; }
-  size_t Size() const { return mSize; }
-  BufferingStrategy Buffering() const { return mBufferingStrategy; }
-
-private:
-  friend struct Transition;
-
-  LexerTransition(const State& aNextState,
-                  const Maybe<State>& aUnbufferedState,
-                  size_t aSize,
-                  BufferingStrategy aBufferingStrategy)
-    : mNextState(aNextState)
-    , mUnbufferedState(aUnbufferedState)
-    , mSize(aSize)
-    , mBufferingStrategy(aBufferingStrategy)
-  {
-    MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
-                  mUnbufferedState);
-    MOZ_ASSERT_IF(mUnbufferedState,
-                  mBufferingStrategy == BufferingStrategy::UNBUFFERED);
-  }
-
-  State mNextState;
-  Maybe<State> mUnbufferedState;
-  size_t mSize;
-  BufferingStrategy mBufferingStrategy;
-};
-
-struct Transition
-{
-  /// Transition to @aNextState, buffering @aSize bytes of data.
-  template <typename State>
-  static LexerTransition<State>
-  To(const State& aNextState, size_t aSize)
-  {
-    MOZ_ASSERT(!IsTerminalState(aNextState));
-    return LexerTransition<State>(aNextState, Nothing(), aSize,
-                                  BufferingStrategy::BUFFERED);
-  }
-
-  /**
-   * Transition to @aNextState via @aUnbufferedState, reading @aSize bytes of
-   * data unbuffered.
-   *
-   * The unbuffered data will be delivered in state @aUnbufferedState, which may
-   * be invoked repeatedly until all @aSize bytes have been delivered. Then,
-   * @aNextState will be invoked with no data. No state transitions are allowed
-   * from @aUnbufferedState except for transitions to a terminal state, so
-   * @aNextState will always be reached unless lexing terminates early.
-   */
-  template <typename State>
-  static LexerTransition<State>
-  ToUnbuffered(const State& aNextState,
-               const State& aUnbufferedState,
-               size_t aSize)
-  {
-    MOZ_ASSERT(!IsTerminalState(aNextState));
-    MOZ_ASSERT(!IsTerminalState(aUnbufferedState));
-    return LexerTransition<State>(aNextState, Some(aUnbufferedState), aSize,
-                                  BufferingStrategy::UNBUFFERED);
-  }
-
-  /**
-   * Continue receiving unbuffered data. @aUnbufferedState should be the same
-   * state as the @aUnbufferedState specified in the preceding call to
-   * ToUnbuffered().
-   *
-   * This should be used during an unbuffered read initiated by ToUnbuffered().
-   */
-  template <typename State>
-  static LexerTransition<State>
-  ContinueUnbuffered(const State& aUnbufferedState)
-  {
-    MOZ_ASSERT(!IsTerminalState(aUnbufferedState));
-    return LexerTransition<State>(aUnbufferedState, Nothing(), 0,
-                                  BufferingStrategy::BUFFERED);
-  }
-
-  /**
-   * Terminate lexing, ending up in terminal state @aFinalState.
-   *
-   * No more data will be delivered after Terminate() is used.
-   */
-  template <typename State>
-  static LexerTransition<State>
-  Terminate(const State& aFinalState)
-  {
-    MOZ_ASSERT(IsTerminalState(aFinalState));
-    return LexerTransition<State>(aFinalState, Nothing(), 0,
-                                  BufferingStrategy::BUFFERED);
-  }
-
-private:
-  Transition();
-};
-
-/**
- * StreamingLexer is a lexing framework designed to make it simple to write
- * image decoders without worrying about the details of how the data is arriving
- * from the network.
- *
- * To use StreamingLexer:
- *
- *  - Create a State type. This should be an |enum class| listing all of the
- *    states that you can be in while lexing the image format you're trying to
- *    read. It must contain the two terminal states SUCCESS and FAILURE.
- *
- *  - Add an instance of StreamingLexer<State> to your decoder class. Initialize
- *    it with a Transition::To() the state that you want to start lexing in.
- *
- *  - In your decoder's WriteInternal method(), call Lex(), passing in the input
- *    data and length that are passed to WriteInternal(). You also need to pass
- *    a lambda which dispatches to lexing code for each state based on the State
- *    value that's passed in. The lambda generally should just continue a
- *    |switch| statement that calls different methods for each State value. Each
- *    method should return a LexerTransition<State>, which the lambda should
- *    return in turn.
- *
- *  - Write the methods that actually implement lexing for your image format.
- *    These methods should return either Transition::To(), to move on to another
- *    state, or Transition::Terminate(), if lexing has terminated in either
- *    success or failure. (There are also additional transitions for unbuffered
- *    reads; see below.)
- *
- * That's all there is to it. The StreamingLexer will track your position in the
- * input and buffer enough data so that your lexing methods can process
- * everything in one pass. Lex() returns Nothing() if more data is needed, in
- * which case you should just return from WriteInternal(). If lexing reaches a
- * terminal state, Lex() returns Some(State::SUCCESS) or Some(State::FAILURE),
- * and you can check which one to determine if lexing succeeded or failed and do
- * any necessary cleanup.
- *
- * There's one more wrinkle: some lexers may want to *avoid* buffering in some
- * cases, and just process the data as it comes in. This is useful if, for
- * example, you just want to skip over a large section of data; there's no point
- * in buffering data you're just going to ignore.
- *
- * You can begin an unbuffered read with Transition::ToUnbuffered(). This works
- * a little differently than Transition::To() in that you specify *two* states.
- * The @aUnbufferedState argument specifies a state that will be called
- * repeatedly with unbuffered data, as soon as it arrives. The implementation
- * for that state should return either a transition to a terminal state, or
- * Transition::ContinueUnbuffered(). Once the amount of data requested in the
- * original call to Transition::ToUnbuffered() has been delivered, Lex() will
- * transition to the @aNextState state specified via Transition::ToUnbuffered().
- * That state will be invoked with *no* data; it's just called to signal that
- * the unbuffered read is over.
- *
- * XXX(seth): We should be able to get of the |State| stuff totally once bug
- * 1198451 lands, since we can then just return a function representing the next
- * state directly.
- */
-template <typename State, size_t InlineBufferSize = 16>
-class StreamingLexer
-{
-public:
-  explicit StreamingLexer(LexerTransition<State> aStartState)
-    : mTransition(aStartState)
-    , mToReadUnbuffered(0)
-  { }
-
-  template <typename Func>
-  Maybe<State> Lex(const char* aInput, size_t aLength, Func aFunc)
-  {
-    if (IsTerminalState(mTransition.NextState())) {
-      // We've already reached a terminal state. We never deliver any more data
-      // in this case; just return the terminal state again immediately.
-      return Some(mTransition.NextState());
-    }
-
-    if (mToReadUnbuffered > 0) {
-      // We're continuing an unbuffered read.
-
-      MOZ_ASSERT(mBuffer.empty(),
-                 "Shouldn't be continuing an unbuffered read and a buffered "
-                 "read at the same time");
-
-      size_t toRead = std::min(mToReadUnbuffered, aLength);
-
-      // Call aFunc with the unbuffered state to indicate that we're in the middle
-      // of an unbuffered read. We enforce that any state transition passed back
-      // to us is either a terminal states or takes us back to the unbuffered
-      // state.
-      LexerTransition<State> unbufferedTransition =
-        aFunc(mTransition.UnbufferedState(), aInput, toRead);
-      if (IsTerminalState(unbufferedTransition.NextState())) {
-        mTransition = unbufferedTransition;
-        return Some(mTransition.NextState());  // Done!
-      }
-      MOZ_ASSERT(mTransition.UnbufferedState() ==
-                   unbufferedTransition.NextState());
-
-      aInput += toRead;
-      aLength -= toRead;
-      mToReadUnbuffered -= toRead;
-      if (mToReadUnbuffered != 0) {
-        return Nothing();  // Need more input.
-      }
-
-      // We're done with the unbuffered read, so transition to the next state.
-      mTransition = aFunc(mTransition.NextState(), nullptr, 0);
-      if (IsTerminalState(mTransition.NextState())) {
-        return Some(mTransition.NextState());  // Done!
-      }
-    } else if (0 < mBuffer.length()) {
-      // We're continuing a buffered read.
-
-      MOZ_ASSERT(mToReadUnbuffered == 0,
-                 "Shouldn't be continuing an unbuffered read and a buffered "
-                 "read at the same time");
-      MOZ_ASSERT(mBuffer.length() < mTransition.Size(),
-                 "Buffered more than we needed?");
-
-      size_t toRead = std::min(aLength, mTransition.Size() - mBuffer.length());
-
-      mBuffer.append(aInput, toRead);
-      aInput += toRead;
-      aLength -= toRead;
-      if (mBuffer.length() != mTransition.Size()) {
-        return Nothing();  // Need more input.
-      }
-
-      // We've buffered everything, so transition to the next state.
-      mTransition =
-        aFunc(mTransition.NextState(), mBuffer.begin(), mBuffer.length());
-      mBuffer.clear();
-      if (IsTerminalState(mTransition.NextState())) {
-        return Some(mTransition.NextState());  // Done!
-      }
-    }
-
-    MOZ_ASSERT(mToReadUnbuffered == 0);
-    MOZ_ASSERT(mBuffer.empty());
-
-    // Process states as long as we continue to have enough input to do so.
-    while (mTransition.Size() <= aLength) {
-      size_t toRead = mTransition.Size();
-
-      if (mTransition.Buffering() == BufferingStrategy::BUFFERED) {
-        mTransition = aFunc(mTransition.NextState(), aInput, toRead);
-      } else {
-        MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
-
-        // Call aFunc with the unbuffered state to indicate that we're in the
-        // middle of an unbuffered read. We enforce that any state transition
-        // passed back to us is either a terminal states or takes us back to the
-        // unbuffered state.
-        LexerTransition<State> unbufferedTransition =
-          aFunc(mTransition.UnbufferedState(), aInput, toRead);
-        if (IsTerminalState(unbufferedTransition.NextState())) {
-          mTransition = unbufferedTransition;
-          return Some(mTransition.NextState());  // Done!
-        }
-        MOZ_ASSERT(mTransition.UnbufferedState() ==
-                     unbufferedTransition.NextState());
-
-        // We're done with the unbuffered read, so transition to the next state.
-        mTransition = aFunc(mTransition.NextState(), nullptr, 0);
-      }
-
-      aInput += toRead;
-      aLength -= toRead;
-
-      if (IsTerminalState(mTransition.NextState())) {
-        return Some(mTransition.NextState());  // Done!
-      }
-    }
-
-    if (aLength == 0) {
-      // We finished right at a transition point. Just wait for more data.
-      return Nothing();
-    }
-
-    // If the next state is unbuffered, deliver what we can and then wait.
-    if (mTransition.Buffering() == BufferingStrategy::UNBUFFERED) {
-      LexerTransition<State> unbufferedTransition =
-        aFunc(mTransition.UnbufferedState(), aInput, aLength);
-      if (IsTerminalState(unbufferedTransition.NextState())) {
-        mTransition = unbufferedTransition;
-        return Some(mTransition.NextState());  // Done!
-      }
-      MOZ_ASSERT(mTransition.UnbufferedState() ==
-                   unbufferedTransition.NextState());
-
-      mToReadUnbuffered = mTransition.Size() - aLength;
-      return Nothing();  // Need more input.
-    }
-    
-    // If the next state is buffered, buffer what we can and then wait.
-    MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
-    if (!mBuffer.reserve(mTransition.Size())) {
-      return Some(State::FAILURE);  // Done due to allocation failure.
-    }
-    mBuffer.append(aInput, aLength);
-    return Nothing();  // Need more input.
-  }
-
-private:
-  Vector<char, InlineBufferSize> mBuffer;
-  LexerTransition<State> mTransition;
-  size_t mToReadUnbuffered;
-};
-
-} // namespace image
-} // namespace mozilla
-
-#endif // mozilla_image_StreamingLexer_h
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -663,67 +663,58 @@ VectorImage::IsOpaque()
 {
   return false; // In general, SVG content is not opaque.
 }
 
 //******************************************************************************
 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
  *                                   in uint32_t aFlags; */
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
-VectorImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags)
+VectorImage::GetFrame(uint32_t aWhichFrame,
+                      uint32_t aFlags)
 {
+  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
+
+  if (aWhichFrame > FRAME_MAX_VALUE) {
+    return nullptr;
+  }
+
+  if (mError || !mIsFullyLoaded) {
+    return nullptr;
+  }
+
   // Look up height & width
   // ----------------------
   SVGSVGElement* svgElem = mSVGDocumentWrapper->GetRootSVGElem();
   MOZ_ASSERT(svgElem, "Should have a root SVG elem, since we finished "
                       "loading without errors");
   nsIntSize imageIntSize(svgElem->GetIntrinsicWidth(),
                          svgElem->GetIntrinsicHeight());
 
   if (imageIntSize.IsEmpty()) {
     // We'll get here if our SVG doc has a percent-valued or negative width or
     // height.
     return nullptr;
   }
 
-  return GetFrameAtSize(imageIntSize, aWhichFrame, aFlags);
-}
-
-NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
-VectorImage::GetFrameAtSize(const IntSize& aSize,
-                            uint32_t aWhichFrame,
-                            uint32_t aFlags)
-{
-  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
-
-  if (aSize.IsEmpty()) {
-    return nullptr;
-  }
-
-  if (aWhichFrame > FRAME_MAX_VALUE) {
-    return nullptr;
-  }
-
-  if (mError || !mIsFullyLoaded) {
-    return nullptr;
-  }
-
   // Make our surface the size of what will ultimately be drawn to it.
   // (either the full image size, or the restricted region)
   RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
-    CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
+    CreateOffscreenContentDrawTarget(IntSize(imageIntSize.width,
+                                             imageIntSize.height),
+                                     SurfaceFormat::B8G8R8A8);
   if (!dt) {
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
 
   nsRefPtr<gfxContext> context = new gfxContext(dt);
 
-  auto result = Draw(context, aSize,
-                     ImageRegion::Create(aSize),
+  auto result = Draw(context, imageIntSize,
+                     ImageRegion::Create(imageIntSize),
                      aWhichFrame, GraphicsFilter::FILTER_NEAREST,
                      Nothing(), aFlags);
 
   return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
 }
 
 NS_IMETHODIMP_(bool)
 VectorImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -9,24 +9,23 @@
 #include <stdlib.h>
 
 #include "mozilla/Endian.h"
 #include "mozilla/Move.h"
 #include "nsICODecoder.h"
 
 #include "RasterImage.h"
 
-using namespace mozilla::gfx;
-
 namespace mozilla {
 namespace image {
 
-// Constants.
-static const uint32_t ICOHEADERSIZE = 6;
-static const uint32_t BITMAPINFOSIZE = 40;
+#define ICONCOUNTOFFSET 4
+#define DIRENTRYOFFSET 6
+#define BITMAPINFOSIZE 40
+#define PREFICONSIZE 16
 
 // ----------------------------------------
 // Actual Data Processing
 // ----------------------------------------
 
 uint32_t
 nsICODecoder::CalcAlphaRowSize()
 {
@@ -53,78 +52,68 @@ nsICODecoder::GetNumColors()
       break;
     default:
       numColors = (uint16_t)-1;
     }
   }
   return numColors;
 }
 
-nsICODecoder::nsICODecoder(RasterImage* aImage)
-  : Decoder(aImage)
-  , mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE))
-  , mBiggestResourceColorDepth(0)
-  , mBestResourceDelta(INT_MIN)
-  , mBestResourceColorDepth(0)
-  , mNumIcons(0)
-  , mCurrIcon(0)
-  , mBPP(0)
-  , mMaskRowSize(0)
-  , mCurrMaskLine(0)
-{ }
 
-nsresult
-nsICODecoder::SetTargetSize(const nsIntSize& aSize)
+nsICODecoder::nsICODecoder(RasterImage* aImage)
+ : Decoder(aImage)
 {
-  // Make sure the size is reasonable.
-  if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
-    return NS_ERROR_FAILURE;
-  }
+  mPos = mImageOffset = mCurrIcon = mNumIcons = mBPP = mRowBytes = 0;
+  mIsPNG = false;
+  mRow = nullptr;
+  mOldLine = mCurLine = 1; // Otherwise decoder will never start
+}
 
-  // Create a downscaler that we'll filter our output through.
-  mDownscaler.emplace(aSize);
-
-  return NS_OK;
+nsICODecoder::~nsICODecoder()
+{
+  if (mRow) {
+    free(mRow);
+  }
 }
 
 void
 nsICODecoder::FinishInternal()
 {
   // We shouldn't be called in error cases
   MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
 
+  // Finish the internally used decoder as well.
+  if (mContainedDecoder && !mContainedDecoder->HasError()) {
+    mContainedDecoder->FinishInternal();
+  }
+
   GetFinalStateFromContainedDecoder();
 }
 
 void
 nsICODecoder::FinishWithErrorInternal()
 {
   GetFinalStateFromContainedDecoder();
 }
 
 void
 nsICODecoder::GetFinalStateFromContainedDecoder()
 {
   if (!mContainedDecoder) {
     return;
   }
 
-  // Finish the internally used decoder.
-  mContainedDecoder->CompleteDecode();
-
   mDecodeDone = mContainedDecoder->GetDecodeDone();
   mDataError = mDataError || mContainedDecoder->HasDataError();
   mFailCode = NS_SUCCEEDED(mFailCode) ? mContainedDecoder->GetDecoderError()
                                       : mFailCode;
   mDecodeAborted = mContainedDecoder->WasAborted();
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
   mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
-
-  MOZ_ASSERT(HasError() || !mCurrentFrame || mCurrentFrame->IsImageComplete());
 }
 
 // Returns a buffer filled with the bitmap file header in little endian:
 // Signature 2 bytes 'BM'
 // FileSize      4 bytes File size in bytes
 // reserved      4 bytes unused (=0)
 // DataOffset    4 bytes File offset to Raster Data
 // Returns true if successful
@@ -212,481 +201,445 @@ nsICODecoder::FixBitmapWidth(int8_t* bih
     mDirEntry.mWidth = 0;
   } else {
     mDirEntry.mWidth = (int8_t)width;
   }
   return true;
 }
 
 // The BMP information header's bits per pixel should be trusted
-// more than what we have.  Usually the ICO's BPP is set to 0.
+// more than what we have.  Usually the ICO's BPP is set to 0
 int32_t
-nsICODecoder::ReadBPP(const char* aBIH)
+nsICODecoder::ExtractBPPFromBitmap(int8_t* bih)
 {
-  const int8_t* bih = reinterpret_cast<const int8_t*>(aBIH);
   int32_t bitsPerPixel;
   memcpy(&bitsPerPixel, bih + 14, sizeof(bitsPerPixel));
   NativeEndian::swapFromLittleEndianInPlace(&bitsPerPixel, 1);
   return bitsPerPixel;
 }
 
 int32_t
-nsICODecoder::ReadBIHSize(const char* aBIH)
+nsICODecoder::ExtractBIHSizeFromBitmap(int8_t* bih)
 {
-  const int8_t* bih = reinterpret_cast<const int8_t*>(aBIH);
   int32_t headerSize;
   memcpy(&headerSize, bih, sizeof(headerSize));
   NativeEndian::swapFromLittleEndianInPlace(&headerSize, 1);
   return headerSize;
 }
 
-LexerTransition<ICOState>
-nsICODecoder::ReadHeader(const char* aData)
-{
-  // If the third byte is 1, this is an icon. If 2, a cursor.
-  if ((aData[2] != 1) && (aData[2] != 2)) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-  mIsCursor = (aData[2] == 2);
-
-  // The fifth and sixth bytes specify the number of resources in the file.
-  mNumIcons =
-    LittleEndian::readUint16(reinterpret_cast<const uint16_t*>(aData + 4));
-  if (mNumIcons == 0) {
-    return Transition::Terminate(ICOState::SUCCESS); // Nothing to do.
-  }
-
-  // Downscale-during-decode can end up decoding different resources in the ICO
-  // file depending on the target size. Since the resources are not necessarily
-  // scaled versions of the same image, some may be transparent and some may not
-  // be. We could be precise about transparency if we decoded the metadata of
-  // every resource, but for now we don't and it's safest to assume that
-  // transparency could be present.
-  PostHasTransparency();
-
-  return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
-}
-
-size_t
-nsICODecoder::FirstResourceOffset() const
-{
-  MOZ_ASSERT(mNumIcons > 0,
-             "Calling FirstResourceOffset before processing header");
-
-  // The first resource starts right after the directory, which starts right
-  // after the ICO header.
-  return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
-}
-
-LexerTransition<ICOState>
-nsICODecoder::ReadDirEntry(const char* aData)
+void
+nsICODecoder::SetHotSpotIfCursor()
 {
-  mCurrIcon++;
-
-  // Read the directory entry.
-  IconDirEntry e;
-  memset(&e, 0, sizeof(e));
-  memcpy(&e.mWidth, aData, sizeof(e.mWidth));
-  memcpy(&e.mHeight, aData + 1, sizeof(e.mHeight));
-  memcpy(&e.mColorCount, aData + 2, sizeof(e.mColorCount));
-  memcpy(&e.mReserved, aData + 3, sizeof(e.mReserved));
-  memcpy(&e.mPlanes, aData + 4, sizeof(e.mPlanes));
-  e.mPlanes = LittleEndian::readUint16(&e.mPlanes);
-  memcpy(&e.mBitCount, aData + 6, sizeof(e.mBitCount));
-  e.mBitCount = LittleEndian::readUint16(&e.mBitCount);
-  memcpy(&e.mBytesInRes, aData + 8, sizeof(e.mBytesInRes));
-  e.mBytesInRes = LittleEndian::readUint32(&e.mBytesInRes);
-  memcpy(&e.mImageOffset, aData + 12, sizeof(e.mImageOffset));
-  e.mImageOffset = LittleEndian::readUint32(&e.mImageOffset);
-
-  // Determine if this is the biggest resource we've seen so far. We always use
-  // the biggest resource for the intrinsic size, and if we're not downscaling,
-  // we select it as the best resource as well.
-  IntSize entrySize(GetRealWidth(e), GetRealHeight(e));
-  if (e.mBitCount >= mBiggestResourceColorDepth &&
-      entrySize.width * entrySize.height >=
-        mBiggestResourceSize.width * mBiggestResourceSize.height) {
-    mBiggestResourceSize = entrySize;
-    mBiggestResourceColorDepth = e.mBitCount;
-    mBiggestResourceHotSpot = IntSize(e.mXHotspot, e.mYHotspot);
-
-    if (!mDownscaler) {
-      mDirEntry = e;
-    }
-  }
-
-  if (mDownscaler) {
-    // Calculate the delta between this resource's size and the desired size, so
-    // we can see if it is better than our current-best option.  In the case of
-    // several equally-good resources, we use the last one. "Better" in this
-    // case is determined by |delta|, a measure of the difference in size
-    // between the entry we've found and the downscaler's target size. We will
-    // choose the smallest resource that is >= the target size (i.e. we assume
-    // it's better to downscale a larger icon than to upscale a smaller one).
-    IntSize desiredSize = mDownscaler->TargetSize();
-    int32_t delta = entrySize.width - desiredSize.width +
-                    entrySize.height - desiredSize.height;
-    if (e.mBitCount >= mBestResourceColorDepth &&
-        ((mBestResourceDelta < 0 && delta >= mBestResourceDelta) ||
-         (delta >= 0 && delta <= mBestResourceDelta))) {
-      mBestResourceDelta = delta;
-      mBestResourceColorDepth = e.mBitCount;
-      mDirEntry = e;
-    }
-  }
-
-  if (mCurrIcon == mNumIcons) {
-    // Ensure the resource we selected has an offset past the ICO headers.
-    if (mDirEntry.mImageOffset < FirstResourceOffset()) {
-      return Transition::Terminate(ICOState::FAILURE);
-    }
-
-    // If this is a cursor, set the hotspot. We use the hotspot from the biggest
-    // resource since we also use that resource for the intrinsic size.
-    if (mIsCursor) {
-      mImageMetadata.SetHotspot(mBiggestResourceHotSpot.width,
-                                mBiggestResourceHotSpot.height);
-    }
-
-    // We always report the biggest resource's size as the intrinsic size; this
-    // is necessary for downscale-during-decode to work since we won't even
-    // attempt to *upscale* while decoding.
-    PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
-    if (IsMetadataDecode()) {
-      return Transition::Terminate(ICOState::SUCCESS);
-    }
-
-    // If the resource we selected matches the downscaler's target size
-    // perfectly, we don't need to do any downscaling.
-    if (mDownscaler && GetRealSize() == mDownscaler->TargetSize()) {
-      mDownscaler.reset();
-    }
-
-    size_t offsetToResource = mDirEntry.mImageOffset - FirstResourceOffset();
-    return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
-                                    ICOState::SKIP_TO_RESOURCE,
-                                    offsetToResource);
+  if (!mIsCursor) {
+    return;
   }
 
-  return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
-}
-
-LexerTransition<ICOState>
-nsICODecoder::SniffResource(const char* aData)
-{
-  // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
-  // is a PNG or a BMP.
-  bool isPNG = !memcmp(aData, nsPNGDecoder::pngSignatureBytes,
-                       PNGSIGNATURESIZE);
-  if (isPNG) {
-    // Create a PNG decoder which will do the rest of the work for us.
-    mContainedDecoder = new nsPNGDecoder(mImage);
-    mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
-    mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
-    mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
-    if (mDownscaler) {
-      mContainedDecoder->SetTargetSize(mDownscaler->TargetSize());
-    }
-    mContainedDecoder->Init();
-
-    if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
-      return Transition::Terminate(ICOState::FAILURE);
-    }
-
-    if (mDirEntry.mBytesInRes <= PNGSIGNATURESIZE) {
-      return Transition::Terminate(ICOState::FAILURE);
-    }
-
-    // Read in the rest of the PNG unbuffered.
-    size_t toRead = mDirEntry.mBytesInRes - PNGSIGNATURESIZE;
-    return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
-                                    ICOState::READ_PNG,
-                                    toRead);
-  } else {
-    // Create a BMP decoder which will do most of the work for us; the exception
-    // is the AND mask, which isn't present in standalone BMPs.
-    nsBMPDecoder* bmpDecoder = new nsBMPDecoder(mImage);
-    mContainedDecoder = bmpDecoder;
-    bmpDecoder->SetUseAlphaData(true);
-    mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
-    mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
-    mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
-    if (mDownscaler) {
-      mContainedDecoder->SetTargetSize(mDownscaler->TargetSize());
-    }
-    mContainedDecoder->Init();
-
-    // Make sure we have a sane size for the bitmap information header.
-    int32_t bihSize = ReadBIHSize(aData);
-    if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
-      return Transition::Terminate(ICOState::FAILURE);
-    }
-
-    // Buffer the first part of the bitmap information header.
-    memcpy(mBIHraw, aData, PNGSIGNATURESIZE);
-
-    // Read in the rest of the bitmap information header.
-    return Transition::To(ICOState::READ_BIH,
-                          BITMAPINFOSIZE - PNGSIGNATURESIZE);
-  }
-}
-
-LexerTransition<ICOState>
-nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
-{
-  if (!WriteToContainedDecoder(aData, aLen)) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  // Raymond Chen says that 32bpp only are valid PNG ICOs
-  // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
-  if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  return Transition::ContinueUnbuffered(ICOState::READ_PNG);
-}
-
-LexerTransition<ICOState>
-nsICODecoder::ReadBIH(const char* aData)
-{
-  // Buffer the rest of the bitmap information header.
-  memcpy(mBIHraw + PNGSIGNATURESIZE, aData, BITMAPINFOSIZE - PNGSIGNATURESIZE);
-
-  // Extracting the BPP from the BIH header; it should be trusted over the one
-  // we have from the ICO header.
-  mBPP = ReadBPP(mBIHraw);
-
-  // The ICO format when containing a BMP does not include the 14 byte
-  // bitmap file header. To use the code of the BMP decoder we need to
-  // generate this header ourselves and feed it to the BMP decoder.
-  int8_t bfhBuffer[BMPFILEHEADERSIZE];
-  if (!FillBitmapFileHeaderBuffer(bfhBuffer)) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  if (!WriteToContainedDecoder(reinterpret_cast<const char*>(bfhBuffer),
-                               sizeof(bfhBuffer))) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  // Fix the ICO height from the BIH. It needs to be halved so our BMP decoder
-  // will understand, because the BMP decoder doesn't expect the alpha mask that
-  // follows the BMP data in an ICO.
-  if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  // Fix the ICO width from the BIH.
-  if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  // Write out the BMP's bitmap info header.
-  if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  // Sometimes the ICO BPP header field is not filled out so we should trust the
-  // contained resource over our own information.
-  // XXX(seth): Is this ever different than the value we obtained from
-  // ReadBPP() above?
-  nsRefPtr<nsBMPDecoder> bmpDecoder =
-    static_cast<nsBMPDecoder*>(mContainedDecoder.get());
-  mBPP = bmpDecoder->GetBitsPerPixel();
-
-  // Check to make sure we have valid color settings.
-  uint16_t numColors = GetNumColors();
-  if (numColors == uint16_t(-1)) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  // Do we have an AND mask on this BMP? If so, we need to read it after we read
-  // the BMP data itself.
-  uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
-  bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry.mBytesInRes;
-  ICOState afterBMPState = hasANDMask ? ICOState::PREPARE_FOR_MASK
-                                      : ICOState::FINISHED_RESOURCE;
-
-  // Read in the rest of the BMP unbuffered.
-  return Transition::ToUnbuffered(afterBMPState,
-                                  ICOState::READ_BMP,
-                                  bmpDataLength);
-}
-
-LexerTransition<ICOState>
-nsICODecoder::ReadBMP(const char* aData, uint32_t aLen)
-{
-  if (!WriteToContainedDecoder(aData, aLen)) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  return Transition::ContinueUnbuffered(ICOState::READ_BMP);
-}
-
-LexerTransition<ICOState>
-nsICODecoder::PrepareForMask()
-{
-  nsRefPtr<nsBMPDecoder> bmpDecoder =
-    static_cast<nsBMPDecoder*>(mContainedDecoder.get());
-
-  uint16_t numColors = GetNumColors();
-  MOZ_ASSERT(numColors != uint16_t(-1));
-
-  // Determine the length of the AND mask.
-  uint32_t bmpLengthWithHeader =
-    BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
-  MOZ_ASSERT(bmpLengthWithHeader < mDirEntry.mBytesInRes);
-  uint32_t maskLength = mDirEntry.mBytesInRes - bmpLengthWithHeader;
-
-  // If we have a 32-bpp BMP with alpha data, we ignore the AND mask. We can
-  // also obviously ignore it if the image has zero width or zero height.
-  if ((bmpDecoder->GetBitsPerPixel() == 32 && bmpDecoder->HasAlphaData()) ||
-      GetRealWidth() == 0 || GetRealHeight() == 0) {
-    return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
-                                    ICOState::SKIP_MASK,
-                                    maskLength);
-  }
-
-  // Compute the row size for the mask.
-  mMaskRowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
-
-  // If the expected size of the AND mask is larger than its actual size, then
-  // we must have a truncated (and therefore corrupt) AND mask.
-  uint32_t expectedLength = mMaskRowSize * GetRealHeight();
-  if (maskLength < expectedLength) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  mCurrMaskLine = GetRealHeight();
-  return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
-}
-
-
-LexerTransition<ICOState>
-nsICODecoder::ReadMaskRow(const char* aData)
-{
-  mCurrMaskLine--;
-
-  nsRefPtr<nsBMPDecoder> bmpDecoder =
-    static_cast<nsBMPDecoder*>(mContainedDecoder.get());
-
-  uint32_t* imageData = bmpDecoder->GetImageData();
-  if (!imageData) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  uint8_t sawTransparency = 0;
-  uint32_t* decoded = imageData + mCurrMaskLine * GetRealWidth();
-  uint32_t* decodedRowEnd = decoded + GetRealWidth();
-  const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
-  const uint8_t* maskRowEnd = mask + mMaskRowSize;
-
-  // Iterate simultaneously through the AND mask and the image data.
-  while (mask < maskRowEnd) {
-    uint8_t idx = *mask++;
-    sawTransparency |= idx;
-    for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
-      // Clear pixel completely for transparency.
-      if (idx & bit) {
-        *decoded = 0;
-      }
-      decoded++;
-    }
-  }
-
-  // If any bits are set in sawTransparency, then we know at least one pixel was
-  // transparent.
-  if (sawTransparency) {
-    PostHasTransparency();
-    bmpDecoder->SetHasAlphaData();
-  }
-
-  if (mCurrMaskLine == 0) {
-    return Transition::To(ICOState::FINISHED_RESOURCE, 0);
-  }
-
-  return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
-}
-
-LexerTransition<ICOState>
-nsICODecoder::FinishResource()
-{
-  // Make sure the actual size of the resource matches the size in the directory
-  // entry. If not, we consider the image corrupt.
-  if (mContainedDecoder->HasSize() &&
-      mContainedDecoder->GetSize() != GetRealSize()) {
-    return Transition::Terminate(ICOState::FAILURE);
-  }
-
-  return Transition::Terminate(ICOState::SUCCESS);
+  mImageMetadata.SetHotspot(mDirEntry.mXHotspot, mDirEntry.mYHotspot);
 }
 
 void
 nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
   MOZ_ASSERT(aBuffer);
   MOZ_ASSERT(aCount > 0);
 
-  Maybe<ICOState> terminalState =
-    mLexer.Lex(aBuffer, aCount,
-               [=](ICOState aState, const char* aData, size_t aLength) {
-      switch (aState) {
-        case ICOState::HEADER:
-          return ReadHeader(aData);
-        case ICOState::DIR_ENTRY:
-          return ReadDirEntry(aData);
-        case ICOState::SKIP_TO_RESOURCE:
-          return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
-        case ICOState::FOUND_RESOURCE:
-          return Transition::To(ICOState::SNIFF_RESOURCE, PNGSIGNATURESIZE);
-        case ICOState::SNIFF_RESOURCE:
-          return SniffResource(aData);
-        case ICOState::READ_PNG:
-          return ReadPNG(aData, aLength);
-        case ICOState::READ_BIH:
-          return ReadBIH(aData);
-        case ICOState::READ_BMP:
-          return ReadBMP(aData, aLength);
-        case ICOState::PREPARE_FOR_MASK:
-          return PrepareForMask();
-        case ICOState::READ_MASK_ROW:
-          return ReadMaskRow(aData);
-        case ICOState::SKIP_MASK:
-          return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
-        case ICOState::FINISHED_RESOURCE:
-          return FinishResource();
-        default:
-          MOZ_ASSERT_UNREACHABLE("Unknown ICOState");
-          return Transition::Terminate(ICOState::FAILURE);
+  while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons.
+    if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor
+      if ((*aBuffer != 1) && (*aBuffer != 2)) {
+        PostDataError();
+        return;
       }
-    });
+      mIsCursor = (*aBuffer == 2);
+    }
+    mPos++; aBuffer++; aCount--;
+  }
 
-  if (!terminalState) {
-    return;  // Need more data.
+  if (mPos == ICONCOUNTOFFSET && aCount >= 2) {
+    mNumIcons =
+      LittleEndian::readUint16(reinterpret_cast<const uint16_t*>(aBuffer));
+    aBuffer += 2;
+    mPos += 2;
+    aCount -= 2;
+  }
+
+  if (mNumIcons == 0) {
+    return; // Nothing to do.
+  }
+
+  uint16_t colorDepth = 0;
+
+  // If we didn't get a #-moz-resolution, default to PREFICONSIZE.
+  if (mResolution.width == 0 && mResolution.height == 0) {
+    mResolution.SizeTo(PREFICONSIZE, PREFICONSIZE);
   }
 
-  if (*terminalState == ICOState::FAILURE) {
-    PostDataError();
+  // A measure of the difference in size between the entry we've found
+  // and the requested size. We will choose the smallest image that is
+  // >= requested size (i.e. we assume it's better to downscale a larger
+  // icon than to upscale a smaller one).
+  int32_t diff = INT_MIN;
+
+  // Loop through each entry's dir entry
+  while (mCurrIcon < mNumIcons) {
+    if (mPos >= DIRENTRYOFFSET + (mCurrIcon * sizeof(mDirEntryArray)) &&
+        mPos < DIRENTRYOFFSET + ((mCurrIcon + 1) * sizeof(mDirEntryArray))) {
+      uint32_t toCopy = sizeof(mDirEntryArray) -
+                        (mPos - DIRENTRYOFFSET - mCurrIcon *
+                         sizeof(mDirEntryArray));
+      if (toCopy > aCount) {
+        toCopy = aCount;
+      }
+      memcpy(mDirEntryArray + sizeof(mDirEntryArray) - toCopy, aBuffer, toCopy);
+      mPos += toCopy;
+      aCount -= toCopy;
+      aBuffer += toCopy;
+    }
+    if (aCount == 0) {
+      return; // Need more data
+    }
+
+    IconDirEntry e;
+    if (mPos == (DIRENTRYOFFSET + ICODIRENTRYSIZE) +
+                (mCurrIcon * sizeof(mDirEntryArray))) {
+      mCurrIcon++;
+      ProcessDirEntry(e);
+      // We can't use GetRealWidth and GetRealHeight here because those operate
+      // on mDirEntry, here we are going through each item in the directory.
+      // Calculate the delta between this image's size and the desired size,
+      // so we can see if it is better than our current-best option.
+      // In the case of several equally-good images, we use the last one.
+      int32_t delta = (e.mWidth == 0 ? 256 : e.mWidth) - mResolution.width +
+                      (e.mHeight == 0 ? 256 : e.mHeight) - mResolution.height;
+      if (e.mBitCount >= colorDepth &&
+          ((diff < 0 && delta >= diff) || (delta >= 0 && delta <= diff))) {
+        diff = delta;
+        mImageOffset = e.mImageOffset;
+
+        // ensure mImageOffset is >= size of the direntry headers (bug #245631)
+        uint32_t minImageOffset = DIRENTRYOFFSET +
+                                  mNumIcons * sizeof(mDirEntryArray);
+        if (mImageOffset < minImageOffset) {
+          PostDataError();
+          return;
+        }
+
+        colorDepth = e.mBitCount;
+        memcpy(&mDirEntry, &e, sizeof(IconDirEntry));
+      }
+    }
+  }
+
+  if (mPos < mImageOffset) {
+    // Skip to (or at least towards) the desired image offset
+    uint32_t toSkip = mImageOffset - mPos;
+    if (toSkip > aCount) {
+      toSkip = aCount;
+    }
+
+    mPos    += toSkip;
+    aBuffer += toSkip;
+    aCount  -= toSkip;
+  }
+
+  // If we are within the first PNGSIGNATURESIZE bytes of the image data,
+  // then we have either a BMP or a PNG.  We use the first PNGSIGNATURESIZE
+  // bytes to determine which one we have.
+  if (mCurrIcon == mNumIcons && mPos >= mImageOffset &&
+      mPos < mImageOffset + PNGSIGNATURESIZE) {
+    uint32_t toCopy = PNGSIGNATURESIZE - (mPos - mImageOffset);
+    if (toCopy > aCount) {
+      toCopy = aCount;
+    }
+
+    memcpy(mSignature + (mPos - mImageOffset), aBuffer, toCopy);
+    mPos += toCopy;
+    aCount -= toCopy;
+    aBuffer += toCopy;
+
+    mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes,
+                     PNGSIGNATURESIZE);
+    if (mIsPNG) {
+      mContainedDecoder = new nsPNGDecoder(mImage);
+      mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
+      mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
+      mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
+      mContainedDecoder->Init();
+      if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE)) {
+        return;
+      }
+    }
+  }
+
+  // If we have a PNG, let the PNG decoder do all of the rest of the work
+  if (mIsPNG && mContainedDecoder && mPos >= mImageOffset + PNGSIGNATURESIZE) {
+    if (!WriteToContainedDecoder(aBuffer, aCount)) {
+      return;
+    }
+
+    if (!HasSize() && mContainedDecoder->HasSize()) {
+      nsIntSize size = mContainedDecoder->GetSize();
+      PostSize(size.width, size.height);
+    }
+
+    mPos += aCount;
+    aBuffer += aCount;
+    aCount = 0;
+
+    // Raymond Chen says that 32bpp only are valid PNG ICOs
+    // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
+    if (!IsMetadataDecode() &&
+        !static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
+      PostDataError();
+    }
     return;
   }
 
-  MOZ_ASSERT(*terminalState == ICOState::SUCCESS);
+  // We've processed all of the icon dir entries and are within the
+  // bitmap info size
+  if (!mIsPNG && mCurrIcon == mNumIcons && mPos >= mImageOffset &&
+      mPos >= mImageOffset + PNGSIGNATURESIZE &&
+      mPos < mImageOffset + BITMAPINFOSIZE) {
+
+    // As we were decoding, we did not know if we had a PNG signature or the
+    // start of a bitmap information header.  At this point we know we had
+    // a bitmap information header and not a PNG signature, so fill the bitmap
+    // information header with the data it should already have.
+    memcpy(mBIHraw, mSignature, PNGSIGNATURESIZE);
+
+    // We've found the icon.
+    uint32_t toCopy = sizeof(mBIHraw) - (mPos - mImageOffset);
+    if (toCopy > aCount) {
+      toCopy = aCount;
+    }
+
+    memcpy(mBIHraw + (mPos - mImageOffset), aBuffer, toCopy);
+    mPos += toCopy;
+    aCount -= toCopy;
+    aBuffer += toCopy;
+  }
+
+  // If we have a BMP inside the ICO and we have read the BIH header
+  if (!mIsPNG && mPos == mImageOffset + BITMAPINFOSIZE) {
+
+    // Make sure we have a sane value for the bitmap information header
+    int32_t bihSize = ExtractBIHSizeFromBitmap(reinterpret_cast<int8_t*>
+                                               (mBIHraw));
+    if (bihSize != BITMAPINFOSIZE) {
+      PostDataError();
+      return;
+    }
+    // We are extracting the BPP from the BIH header as it should be trusted
+    // over the one we have from the icon header
+    mBPP = ExtractBPPFromBitmap(reinterpret_cast<int8_t*>(mBIHraw));
+
+    // Init the bitmap decoder which will do most of the work for us
+    // It will do everything except the AND mask which isn't present in bitmaps
+    // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder
+    nsBMPDecoder* bmpDecoder = new nsBMPDecoder(mImage);
+    mContainedDecoder = bmpDecoder;
+    bmpDecoder->SetUseAlphaData(true);
+    mContainedDecoder->SetMetadataDecode(IsMetadataDecode());
+    mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
+    mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
+    mContainedDecoder->Init();
+
+    // The ICO format when containing a BMP does not include the 14 byte
+    // bitmap file header. To use the code of the BMP decoder we need to
+    // generate this header ourselves and feed it to the BMP decoder.
+    int8_t bfhBuffer[BMPFILEHEADERSIZE];
+    if (!FillBitmapFileHeaderBuffer(bfhBuffer)) {
+      PostDataError();
+      return;
+    }
+    if (!WriteToContainedDecoder((const char*)bfhBuffer, sizeof(bfhBuffer))) {
+      return;
+    }
+
+    // Setup the cursor hot spot if one is present
+    SetHotSpotIfCursor();
+
+    // Fix the ICO height from the BIH.
+    // Fix the height on the BIH to be /2 so our BMP decoder will understand.
+    if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) {
+      PostDataError();
+      return;
+    }
+
+    // Fix the ICO width from the BIH.
+    if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) {
+      PostDataError();
+      return;
+    }
+
+    // Write out the BMP's bitmap info header
+    if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
+      return;
+    }
+
+    nsIntSize size = mContainedDecoder->GetSize();
+    PostSize(size.width, size.height);
+
+    // We have the size. If we're doing a metadata decode, we're done.
+    if (IsMetadataDecode()) {
+      return;
+    }
+
+    // Sometimes the ICO BPP header field is not filled out
+    // so we should trust the contained resource over our own
+    // information.
+    mBPP = bmpDecoder->GetBitsPerPixel();
+
+    // Check to make sure we have valid color settings
+    uint16_t numColors = GetNumColors();
+    if (numColors == (uint16_t)-1) {
+      PostDataError();
+      return;
+    }
+  }
+
+  // If we have a BMP
+  if (!mIsPNG && mContainedDecoder && mPos >= mImageOffset + BITMAPINFOSIZE) {
+    uint16_t numColors = GetNumColors();
+    if (numColors == (uint16_t)-1) {
+      PostDataError();
+      return;
+    }
+    // Feed the actual image data (not including headers) into the BMP decoder
+    uint32_t bmpDataOffset = mDirEntry.mImageOffset + BITMAPINFOSIZE;
+    uint32_t bmpDataEnd = mDirEntry.mImageOffset + BITMAPINFOSIZE +
+                          static_cast<nsBMPDecoder*>(mContainedDecoder.get())->
+                            GetCompressedImageSize() +
+                          4 * numColors;
+
+    // If we are feeding in the core image data, but we have not yet
+    // reached the ICO's 'AND buffer mask'
+    if (mPos >= bmpDataOffset && mPos < bmpDataEnd) {
+
+      // Figure out how much data the BMP decoder wants
+      uint32_t toFeed = bmpDataEnd - mPos;
+      if (toFeed > aCount) {
+        toFeed = aCount;
+      }
+
+      if (!WriteToContainedDecoder(aBuffer, toFeed)) {
+        return;
+      }
+
+      mPos += toFeed;
+      aCount -= toFeed;
+      aBuffer += toFeed;
+    }
+
+    // If the bitmap is fully processed, treat any left over data as the ICO's
+    // 'AND buffer mask' which appears after the bitmap resource.
+    if (!mIsPNG && mPos >= bmpDataEnd) {
+      nsRefPtr<nsBMPDecoder> bmpDecoder =
+        static_cast<nsBMPDecoder*>(mContainedDecoder.get());
+
+      // There may be an optional AND bit mask after the data.  This is
+      // only used if the alpha data is not already set. The alpha data
+      // is used for 32bpp bitmaps as per the comment in ICODecoder.h
+      // The alpha mask should be checked in all other cases.
+      if (bmpDecoder->GetBitsPerPixel() != 32 || !bmpDecoder->HasAlphaData()) {
+        uint32_t rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
+        if (mPos == bmpDataEnd) {
+          mPos++;
+          mRowBytes = 0;
+          mCurLine = GetRealHeight();
+          mRow = (uint8_t*)realloc(mRow, rowSize);
+          if (!mRow) {
+            PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
+            return;
+          }
+        }
+
+        // Ensure memory has been allocated before decoding.
+        MOZ_ASSERT(mRow, "mRow is null");
+        if (!mRow) {
+          PostDataError();
+          return;
+        }
+
+        uint8_t sawTransparency = 0;
+
+        while (mCurLine > 0 && aCount > 0) {
+          uint32_t toCopy = std::min(rowSize - mRowBytes, aCount);
+          if (toCopy) {
+            memcpy(mRow + mRowBytes, aBuffer, toCopy);
+            aCount -= toCopy;
+            aBuffer += toCopy;
+            mRowBytes += toCopy;
+          }
+          if (rowSize == mRowBytes) {
+            mCurLine--;
+            mRowBytes = 0;
+
+            uint32_t* imageData = bmpDecoder->GetImageData();
+            if (!imageData) {
+              PostDataError();
+              return;
+            }
+            uint32_t* decoded = imageData + mCurLine * GetRealWidth();
+            uint32_t* decoded_end = decoded + GetRealWidth();
+            uint8_t* p = mRow;
+            uint8_t* p_end = mRow + rowSize;
+            while (p < p_end) {
+              uint8_t idx = *p++;
+              sawTransparency |= idx;
+              for (uint8_t bit = 0x80; bit && decoded<decoded_end; bit >>= 1) {
+                // Clear pixel completely for transparency.
+                if (idx & bit) {
+                  *decoded = 0;
+                }
+                decoded++;
+              }
+            }
+          }
+        }
+
+        // If any bits are set in sawTransparency, then we know at least one
+        // pixel was transparent.
+        if (sawTransparency) {
+          PostHasTransparency();
+          bmpDecoder->SetHasAlphaData();
+        }
+      }
+    }
+  }
 }
 
 bool
 nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount)
 {
   mContainedDecoder->Write(aBuffer, aCount);
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
   if (mContainedDecoder->HasDataError()) {
-    PostDataError();
+    mDataError = mContainedDecoder->HasDataError();
   }
   if (mContainedDecoder->HasDecoderError()) {
     PostDecoderError(mContainedDecoder->GetDecoderError());
   }
   return !HasError();
 }
 
+void
+nsICODecoder::ProcessDirEntry(IconDirEntry& aTarget)
+{
+  memset(&aTarget, 0, sizeof(aTarget));
+  memcpy(&aTarget.mWidth, mDirEntryArray, sizeof(aTarget.mWidth));
+  memcpy(&aTarget.mHeight, mDirEntryArray + 1, sizeof(aTarget.mHeight));
+  memcpy(&aTarget.mColorCount, mDirEntryArray + 2, sizeof(aTarget.mColorCount));
+  memcpy(&aTarget.mReserved, mDirEntryArray + 3, sizeof(aTarget.mReserved));
+  memcpy(&aTarget.mPlanes, mDirEntryArray + 4, sizeof(aTarget.mPlanes));
+  aTarget.mPlanes = LittleEndian::readUint16(&aTarget.mPlanes);
+  memcpy(&aTarget.mBitCount, mDirEntryArray + 6, sizeof(aTarget.mBitCount));
+  aTarget.mBitCount = LittleEndian::readUint16(&aTarget.mBitCount);
+  memcpy(&aTarget.mBytesInRes, mDirEntryArray + 8, sizeof(aTarget.mBytesInRes));
+  aTarget.mBytesInRes = LittleEndian::readUint32(&aTarget.mBytesInRes);
+  memcpy(&aTarget.mImageOffset, mDirEntryArray + 12,
+         sizeof(aTarget.mImageOffset));
+  aTarget.mImageOffset = LittleEndian::readUint32(&aTarget.mImageOffset);
+}
+
 } // namespace image
 } // namespace mozilla
--- a/image/decoders/nsICODecoder.h
+++ b/image/decoders/nsICODecoder.h
@@ -3,80 +3,49 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #ifndef mozilla_image_decoders_nsICODecoder_h
 #define mozilla_image_decoders_nsICODecoder_h
 
 #include "nsAutoPtr.h"
-#include "StreamingLexer.h"
 #include "Decoder.h"
 #include "imgFrame.h"
 #include "nsBMPDecoder.h"
 #include "nsPNGDecoder.h"
 #include "ICOFileHeaders.h"
 
 namespace mozilla {
 namespace image {
 
 class RasterImage;
 
-enum class ICOState
-{
-  SUCCESS,
-  FAILURE,
-  HEADER,
-  DIR_ENTRY,
-  SKIP_TO_RESOURCE,
-  FOUND_RESOURCE,
-  SNIFF_RESOURCE,
-  READ_PNG,
-  READ_BIH,
-  READ_BMP,
-  PREPARE_FOR_MASK,
-  READ_MASK_ROW,
-  SKIP_MASK,
-  FINISHED_RESOURCE
-};
-
 class nsICODecoder : public Decoder
 {
 public:
-  virtual ~nsICODecoder() { }
-
-  nsresult SetTargetSize(const nsIntSize& aSize) override;
+  virtual ~nsICODecoder();
 
-  /// @return the width of the icon directory entry @aEntry.
-  static uint32_t GetRealWidth(const IconDirEntry& aEntry)
+  // Obtains the width of the icon directory entry
+  uint32_t GetRealWidth() const
   {
-    return aEntry.mWidth == 0 ? 256 : aEntry.mWidth;
+    return mDirEntry.mWidth == 0 ? 256 : mDirEntry.mWidth;
   }
 
-  /// @return the width of the selected directory entry (mDirEntry).
-  uint32_t GetRealWidth() const { return GetRealWidth(mDirEntry); }
-
-  /// @return the height of the icon directory entry @aEntry.
-  static uint32_t GetRealHeight(const IconDirEntry& aEntry)
+  // Obtains the height of the icon directory entry
+  uint32_t GetRealHeight() const
   {
-    return aEntry.mHeight == 0 ? 256 : aEntry.mHeight;
+    return mDirEntry.mHeight == 0 ? 256 : mDirEntry.mHeight;
   }
 
-  /// @return the height of the selected directory entry (mDirEntry).
-  uint32_t GetRealHeight() const { return GetRealHeight(mDirEntry); }
-
-  /// @return the size of the selected directory entry (mDirEntry).
-  gfx::IntSize GetRealSize() const
+  virtual void SetResolution(const gfx::IntSize& aResolution) override
   {
-    return gfx::IntSize(GetRealWidth(), GetRealHeight());
+    mResolution = aResolution;
   }
 
-  /// @return The offset from the beginning of the ICO to the first resource.
-  size_t FirstResourceOffset() const;
-
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
   virtual void FinishInternal() override;
   virtual void FinishWithErrorInternal() override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
@@ -84,59 +53,59 @@ private:
 
   // Writes to the contained decoder and sets the appropriate errors
   // Returns true if there are no errors.
   bool WriteToContainedDecoder(const char* aBuffer, uint32_t aCount);
 
   // Gets decoder state from the contained decoder so it's visible externally.
   void GetFinalStateFromContainedDecoder();
 
+  // Processes a single dir entry of the icon resource
+  void ProcessDirEntry(IconDirEntry& aTarget);
+  // Sets the hotspot property of if we have a cursor
+  void SetHotSpotIfCursor();
   // Creates a bitmap file header buffer, returns true if successful
   bool FillBitmapFileHeaderBuffer(int8_t* bfh);
   // Fixes the ICO height to match that of the BIH.
   // and also fixes the BIH height to be /2 of what it was.
   // See definition for explanation.
   // Returns false if invalid information is contained within.
   bool FixBitmapHeight(int8_t* bih);
   // Fixes the ICO width to match that of the BIH.
   // Returns false if invalid information is contained within.
   bool FixBitmapWidth(int8_t* bih);
   // Extract bitmap info header size count from BMP information header
-  int32_t ReadBIHSize(const char* aBIH);
+  int32_t ExtractBIHSizeFromBitmap(int8_t* bih);
   // Extract bit count from BMP information header
-  int32_t ReadBPP(const char* aBIH);
+  int32_t ExtractBPPFromBitmap(int8_t* bih);
   // Calculates the row size in bytes for the AND mask table
   uint32_t CalcAlphaRowSize();
   // Obtains the number of colors from the BPP, mBPP must be filled in
   uint16_t GetNumColors();
 
-  LexerTransition<ICOState> ReadHeader(const char* aData);
-  LexerTransition<ICOState> ReadDirEntry(const char* aData);
-  LexerTransition<ICOState> SniffResource(const char* aData);
-  LexerTransition<ICOState> ReadPNG(const char* aData, uint32_t aLen);
-  LexerTransition<ICOState> ReadBIH(const char* aData);
-  LexerTransition<ICOState> ReadBMP(const char* aData, uint32_t aLen);
-  LexerTransition<ICOState> PrepareForMask();
-  LexerTransition<ICOState> ReadMaskRow(const char* aData);
-  LexerTransition<ICOState> FinishResource();
+  gfx::IntSize mResolution;  // The requested -moz-resolution for this icon.
+  uint16_t mBPP; // Stores the images BPP
+  uint32_t mPos; // Keeps track of the position we have decoded up until
+  uint16_t mNumIcons; // Stores the number of icons in the ICO file
+  uint16_t mCurrIcon; // Stores the current dir entry index we are processing
+  uint32_t mImageOffset; // Stores the offset of the image data we want
+  uint8_t* mRow;      // Holds one raw line of the image
+  int32_t mCurLine;   // Line index of the image that's currently being decoded
+  uint32_t mRowBytes; // How many bytes of the row were already received
+  int32_t mOldLine;   // Previous index of the line
+  nsRefPtr<Decoder> mContainedDecoder; // Contains either a BMP or PNG resource
 
-  StreamingLexer<ICOState, 32> mLexer; // The lexer.
-  Maybe<Downscaler> mDownscaler;       // Our downscaler, if we're downscaling.
-  nsRefPtr<Decoder> mContainedDecoder; // Either a BMP or PNG decoder.
-  char mBIHraw[40];                    // The bitmap information header.
-  IconDirEntry mDirEntry;              // The dir entry for the selected resource.
-  IntSize mBiggestResourceSize;        // Used to select the intrinsic size.
-  IntSize mBiggestResourceHotSpot;     // Used to select the intrinsic size.
-  uint16_t mBiggestResourceColorDepth; // Used to select the intrinsic size.
-  int32_t mBestResourceDelta;          // Used to select the best resource.
-  uint16_t mBestResourceColorDepth;    // Used to select the best resource.
-  uint16_t mNumIcons; // Stores the number of icons in the ICO file.
-  uint16_t mCurrIcon; // Stores the current dir entry index we are processing.
-  uint16_t mBPP;      // The BPP of the resource we're decoding.
-  uint32_t mMaskRowSize;  // The size in bytes of each row in the BMP alpha mask.
-  uint32_t mCurrMaskLine; // The line of the BMP alpha mask we're processing.
-  bool mIsCursor;         // Is this ICO a cursor?
+  char mDirEntryArray[ICODIRENTRYSIZE]; // Holds the current dir entry buffer
+  IconDirEntry mDirEntry; // Holds a decoded dir entry
+  // Holds the potential bytes that can be a PNG signature
+  char mSignature[PNGSIGNATURESIZE];
+  // Holds the potential bytes for a bitmap information header
+  char mBIHraw[40];
+  // Stores whether or not the icon file we are processing has type 1 (icon)
+  bool mIsCursor;
+  // Stores whether or not the contained resource is a PNG
+  bool mIsPNG;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_decoders_nsICODecoder_h
--- a/image/imgIContainer.idl
+++ b/image/imgIContainer.idl
@@ -114,17 +114,17 @@ native nsIntSizeByVal(nsIntSize);
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces. It also allows drawing of images
  * onto Thebes contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, builtinclass, uuid(4e5a0547-6c54-4051-8b52-1f2fdd667696)]
+[scriptable, builtinclass, uuid(4880727a-5673-44f7-b248-f6c86e22a434)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -263,31 +263,16 @@ interface imgIContainer : nsISupports
    *
    * @param aWhichFrame Frame specifier of the FRAME_* variety.
    * @param aFlags Flags of the FLAG_* variety
    */
   [noscript, notxpcom] TempRefSourceSurface getFrame(in uint32_t aWhichFrame,
                                                      in uint32_t aFlags);
 
   /**
-   * Get a surface for the given frame at the specified size. Matching the
-   * requested size is best effort; it's not guaranteed that the surface you get
-   * will be a perfect match. (Some reasons you may get a surface of a different
-   * size include: if you requested upscaling, if downscale-during-decode is
-   * disabled, or if you didn't request the first frame.)
-   *
-   * @param aSize The desired size.
-   * @param aWhichFrame Frame specifier of the FRAME_* variety.
-   * @param aFlags Flags of the FLAG_* variety
-   */
-  [noscript, notxpcom] TempRefSourceSurface getFrameAtSize([const] in nsIntSize aSize,
-                                                           in uint32_t aWhichFrame,
-                                                           in uint32_t aFlags);
-
-  /**
    * Whether this image is opaque (i.e., needs a background painted behind it).
    */
   [notxpcom] boolean isOpaque();
 
   /**
    * @return true if getImageContainer() is expected to return a valid
    *         ImageContainer when passed the given @Manager and @Flags
    *         parameters.
--- a/image/imgTools.cpp
+++ b/image/imgTools.cpp
@@ -193,37 +193,36 @@ imgTools::EncodeScaledImage(imgIContaine
   NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0);
 
   // If no scaled size is specified, we'll just encode the image at its
   // original size (no scaling).
   if (aScaledWidth == 0 && aScaledHeight == 0) {
     return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
   }
 
-  // Retrieve the image's size.
-  int32_t imageWidth = 0;
-  int32_t imageHeight = 0;
-  aContainer->GetWidth(&imageWidth);
-  aContainer->GetHeight(&imageHeight);
+  // Use frame 0 from the image container.
+  RefPtr<SourceSurface> frame =
+    aContainer->GetFrame(imgIContainer::FRAME_FIRST,
+                         imgIContainer::FLAG_SYNC_DECODE);
+  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
+
+  int32_t frameWidth = frame->GetSize().width;
+  int32_t frameHeight = frame->GetSize().height;
 
   // If the given width or height is zero we'll replace it with the image's
   // original dimensions.
-  IntSize scaledSize(aScaledWidth == 0 ? imageWidth : aScaledWidth,
-                     aScaledHeight == 0 ? imageHeight : aScaledHeight);
-
-  // Use frame 0 from the image container.
-  RefPtr<SourceSurface> frame =
-    aContainer->GetFrameAtSize(scaledSize,
-                               imgIContainer::FRAME_FIRST,
-                               imgIContainer::FLAG_HIGH_QUALITY_SCALING |
-                               imgIContainer::FLAG_SYNC_DECODE);
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
+  if (aScaledWidth == 0) {
+    aScaledWidth = frameWidth;
+  } else if (aScaledHeight == 0) {
+    aScaledHeight = frameHeight;
+  }
 
   RefPtr<DataSourceSurface> dataSurface =
-    Factory::CreateDataSourceSurface(scaledSize, SurfaceFormat::B8G8R8A8);
+    Factory::CreateDataSourceSurface(IntSize(aScaledWidth, aScaledHeight),
+                                     SurfaceFormat::B8G8R8A8);
   if (NS_WARN_IF(!dataSurface)) {
     return NS_ERROR_FAILURE;
   }
 
   DataSourceSurface::MappedSurface map;
   if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
     return NS_ERROR_FAILURE;
   }
@@ -234,20 +233,19 @@ imgTools::EncodeScaledImage(imgIContaine
                                      dataSurface->GetSize(),
                                      map.mStride,
                                      SurfaceFormat::B8G8R8A8);
   if (!dt) {
     gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  IntSize frameSize = frame->GetSize();
   dt->DrawSurface(frame,
-                  Rect(0, 0, scaledSize.width, scaledSize.height),
-                  Rect(0, 0, frameSize.width, frameSize.height),
+                  Rect(0, 0, aScaledWidth, aScaledHeight),
+                  Rect(0, 0, frameWidth, frameHeight),
                   DrawSurfaceOptions(),
                   DrawOptions(1.0f, CompositionOp::OP_SOURCE));
 
   dataSurface->Unmap();
 
   return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
 }
 
--- a/image/test/crashtests/crashtests.list
+++ b/image/test/crashtests/crashtests.list
@@ -47,13 +47,8 @@ load multiple-png-hassize.ico
 load 856616.gif
 
 skip-if(AddressSanitizer) skip-if(B2G) load 944353.jpg
 
 # Bug 1160801: Ensure that we handle invalid disposal types.
 load invalid-disposal-method-1.gif
 load invalid-disposal-method-2.gif
 load invalid-disposal-method-3.gif
-
-# Ensure we handle ICO directory entries which specify the wrong size for the
-# contained resource.
-load invalid_ico_height.ico
-load invalid_ico_width.ico
--- a/image/test/gtest/TestDecoders.cpp
+++ b/image/test/gtest/TestDecoders.cpp
@@ -207,17 +207,18 @@ TEST(ImageDecoders, BMPMultiChunk)
   CheckDecoderMultiChunk(GreenBMPTestCase());
 }
 
 TEST(ImageDecoders, ICOSingleChunk)
 {
   CheckDecoderSingleChunk(GreenICOTestCase());
 }
 
-TEST(ImageDecoders, ICOMultiChunk)
+// XXX(seth): Disabled. We'll fix this in bug 1196066.
+TEST(ImageDecoders, DISABLED_ICOMultiChunk)
 {
   CheckDecoderMultiChunk(GreenICOTestCase());
 }
 
 TEST(ImageDecoders, AnimatedGIFSingleChunk)
 {
   CheckDecoderSingleChunk(GreenFirstFrameAnimatedGIFTestCase());
 }
deleted file mode 100644
--- a/image/test/gtest/TestStreamingLexer.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/* 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/. */
-
-#include "gtest/gtest.h"
-
-#include "mozilla/Vector.h"
-#include "StreamingLexer.h"
-
-using namespace mozilla;
-using namespace mozilla::image;
-
-enum class TestState
-{
-  ONE,
-  TWO,
-  THREE,
-  UNBUFFERED,
-  SUCCESS,
-  FAILURE
-};
-
-void
-CheckData(const char* aData, size_t aLength)
-{
-  EXPECT_TRUE(aLength == 3);
-  EXPECT_EQ(1, aData[0]);
-  EXPECT_EQ(2, aData[1]);
-  EXPECT_EQ(3, aData[2]);
-}
-
-LexerTransition<TestState>
-DoLex(TestState aState, const char* aData, size_t aLength)
-{
-  switch (aState) {
-    case TestState::ONE:
-      CheckData(aData, aLength);
-      return Transition::To(TestState::TWO, 3);
-    case TestState::TWO:
-      CheckData(aData, aLength);
-      return Transition::To(TestState::THREE, 3);
-    case TestState::THREE:
-      CheckData(aData, aLength);
-      return Transition::Terminate(TestState::SUCCESS);
-    default:
-      EXPECT_TRUE(false);  // Shouldn't get here.
-      return Transition::Terminate(TestState::FAILURE);
-  }
-}
-
-LexerTransition<TestState>
-DoLexWithUnbuffered(TestState aState, const char* aData, size_t aLength,
-                    Vector<char>& aUnbufferedVector)
-{
-  switch (aState) {
-    case TestState::ONE:
-      CheckData(aData, aLength);
-      return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
-    case TestState::UNBUFFERED:
-      EXPECT_TRUE(aLength <= 3);
-      aUnbufferedVector.append(aData, aLength);
-      return Transition::ContinueUnbuffered(TestState::UNBUFFERED);
-    case TestState::TWO:
-      CheckData(aUnbufferedVector.begin(), aUnbufferedVector.length());
-      return Transition::To(TestState::THREE, 3);
-    case TestState::THREE:
-      CheckData(aData, aLength);
-      return Transition::Terminate(TestState::SUCCESS);
-    default:
-      EXPECT_TRUE(false);
-      return Transition::Terminate(TestState::FAILURE);
-  }
-}
-
-LexerTransition<TestState>
-DoLexWithUnbufferedTerminate(TestState aState, const char* aData, size_t aLength)
-{
-  switch (aState) {
-    case TestState::ONE:
-      CheckData(aData, aLength);
-      return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
-    case TestState::UNBUFFERED:
-      return Transition::Terminate(TestState::SUCCESS);
-    default:
-      EXPECT_TRUE(false);
-      return Transition::Terminate(TestState::FAILURE);
-  }
-}
-
-TEST(ImageStreamingLexer, SingleChunk)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-
-  // Test delivering all the data at once.
-  Maybe<TestState> result = lexer.Lex(data, sizeof(data), DoLex);
-  EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::SUCCESS, *result);
-}
-
-TEST(ImageStreamingLexer, SingleChunkWithUnbuffered)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-  Vector<char> unbufferedVector;
-
-  // Test delivering all the data at once.
-  Maybe<TestState> result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
-      return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
-  });
-  EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::SUCCESS, *result);
-}
-
-TEST(ImageStreamingLexer, ChunkPerState)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-
-  // Test delivering in perfectly-sized chunks, one per state.
-  for (unsigned i = 0 ; i < 3 ; ++i) {
-    Maybe<TestState> result = lexer.Lex(data + 3 * i, 3, DoLex);
-
-    if (i == 2) {
-      EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
-    } else {
-      EXPECT_TRUE(result.isNothing());
-    }
-  }
-}
-
-TEST(ImageStreamingLexer, ChunkPerStateWithUnbuffered)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-  Vector<char> unbufferedVector;
-
-  // Test delivering in perfectly-sized chunks, one per state.
-  for (unsigned i = 0 ; i < 3 ; ++i) {
-    Maybe<TestState> result =
-      lexer.Lex(data + 3 * i, 3,
-                [&](TestState aState, const char* aData, size_t aLength) {
-        return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
-    });
-
-    if (i == 2) {
-      EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
-    } else {
-      EXPECT_TRUE(result.isNothing());
-    }
-  }
-}
-
-TEST(ImageStreamingLexer, OneByteChunks)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-
-  // Test delivering in one byte chunks.
-  for (unsigned i = 0 ; i < 9 ; ++i) {
-    Maybe<TestState> result = lexer.Lex(data + i, 1, DoLex);
-
-    if (i == 8) {
-      EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
-    } else {
-      EXPECT_TRUE(result.isNothing());
-    }
-  }
-}
-
-TEST(ImageStreamingLexer, OneByteChunksWithUnbuffered)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-  Vector<char> unbufferedVector;
-
-  // Test delivering in one byte chunks.
-  for (unsigned i = 0 ; i < 9 ; ++i) {
-    Maybe<TestState> result =
-      lexer.Lex(data + i, 1,
-                [&](TestState aState, const char* aData, size_t aLength) {
-        return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
-    });
-
-    if (i == 8) {
-      EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
-    } else {
-      EXPECT_TRUE(result.isNothing());
-    }
-  }
-}
-
-TEST(ImageStreamingLexer, TerminateSuccess)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-
-  // Test that Terminate is "sticky".
-  Maybe<TestState> result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
-      EXPECT_TRUE(aState == TestState::ONE);
-      return Transition::Terminate(TestState::SUCCESS);
-  });
-  EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::SUCCESS, *result);
-
-  result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
-      EXPECT_TRUE(false);  // Shouldn't get here.
-      return Transition::Terminate(TestState::FAILURE);
-  });
-  EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::SUCCESS, *result);
-}
-
-TEST(ImageStreamingLexer, TerminateFailure)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-
-  // Test that Terminate is "sticky".
-  Maybe<TestState> result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
-      EXPECT_TRUE(aState == TestState::ONE);
-      return Transition::Terminate(TestState::FAILURE);
-  });
-  EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::FAILURE, *result);
-
-  result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
-      EXPECT_TRUE(false);  // Shouldn't get here.
-      return Transition::Terminate(TestState::FAILURE);
-  });
-  EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::FAILURE, *result);
-}
-
-TEST(ImageStreamingLexer, TerminateUnbuffered)
-{
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
-
-  // Test that Terminate works during an unbuffered read.
-  for (unsigned i = 0 ; i < 9 ; ++i) {
-    Maybe<TestState> result =
-      lexer.Lex(data + i, 1, DoLexWithUnbufferedTerminate);
-
-    if (i > 2) {
-      EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
-    } else {
-      EXPECT_TRUE(result.isNothing());
-    }
-  }
-}
--- a/image/test/gtest/moz.build
+++ b/image/test/gtest/moz.build
@@ -7,17 +7,16 @@
 Library('imagetest')
 
 UNIFIED_SOURCES = [
     'Common.cpp',
     'TestCopyOnWrite.cpp',
     'TestDecoders.cpp',
     'TestDecodeToSurface.cpp',
     'TestMetadata.cpp',
-    'TestStreamingLexer.cpp',
 ]
 
 TEST_HARNESS_FILES.gtest += [
     'corrupt.jpg',
     'first-frame-green.gif',
     'first-frame-green.png',
     'first-frame-padding.gif',
     'green.bmp',
--- a/image/test/mochitest/test_has_transparency.html
+++ b/image/test/mochitest/test_has_transparency.html
@@ -52,19 +52,18 @@ function testFiles() {
   yield ["damon.jpg", false];
 
   // Most BMPs are not transparent. (The TestMetadata GTest, which will
   // eventually replace this test totally, has coverage for the kinds that can be
   // transparent.)
   yield ["opaque.bmp", false];
 
   // ICO files which contain BMPs have an additional type of transparency - the
-  // AND mask - that warrants separate testing. (Although, after bug 1201796,
-  // all ICOs are considered transparent.)
-  yield ["ico-bmp-opaque.ico", true];
+  // AND mask - that warrants separate testing.
+  yield ["ico-bmp-opaque.ico", false];
   yield ["ico-bmp-transparent.ico", true];
 
   // SVGs are always transparent.
   yield ["lime100x100.svg", true];
 }
 
 function loadNext() {
   var currentFile = "";
index 025ebaed1ff12279d4e07cfcaf3bd1cbbeb0884b..d37f2f1cc0534d9aec717a087a13dd4eb5b83a8f
GIT binary patch
literal 13942
zc%1E;&1w@-6o7B6*n&b_N*8T5iaV<dBW~gg2;u{X(!H<H7f~1DPe2M?q$1K{ks|a*
zO9*Y!#x%{3F-DRmkd~CDIi52)S1#kEX%>?^BljzZQ|?T2zccB;<eUg`4u7Vmga`;B
zqF05uhB*`x7SYQ>e8aWt_&c5=9t%2;4;l;xgTY`h7z_r3!C?3=8NV=c7h@h{8snC3
zpFu(A51$qm;N|mY@O1hKJbL&5aP5X}ud{%j;~2@EogGM}Qjkn0VKy2i|F~{93A!v|
zlqN2Y-ElY;o1dG5OeO=e23o_oufx>|borS{r^%~{5mwg2bnSDw9AvXutA;Pj%XD5s
zr>`CIZ#J8d-rklq+~)Pz$M^G4C=@`};I(T{kpJP~A=qo6>s&6Ep;RhaHGExL_1v{5
z$iLNUfxU*=w{M_Qser5j*A~2X?FsTfIywTSh7Q%RzrPRFYV~goe4qCWo&Lwi$6%`g
z8jZ$3HOT)ZhkIJ9|H;V-3{pcrpSNmQTUn9sOSu0|?)i5*9T>ERjrDcy?$RHSL25|E
z<DPI={rlBWuh*>_lAD{JaVJ#&el_f7zI)1@u>1F_VQ)9%Id{UzU)IoWx2+oTdwZUA
zCwlqI8cM~YXWfa@{L7^uo^~gC`TN10aPs$yJ7M?tlRKgM`^}w@{(f{P$p3fcmoME3
z^7pGd!T!E<C%Sj84|0(6NH=#lPjJ38+Rf)Vi=ZuwNRK?jna>Mw=GPVW*PG-V;XK2c
zJp-IaIFE7;a-QTI;Y{Dt3Srz6=6hNOgTY|%4|2wZ@dD$fhW%romzE%rNYFFzx{LcP
z_L?!#T#Ovtoa@nZrZQg%^O`lEOLymAuFi&N|AT`Ab;d)rC#aXreQZ}}J918=I=p-R
zYG|_=Ij2w^YPFixPaeiRMV&K92kL7}2RT=eGX*#Hrzh6a9O4@ru6+(w`wUJFsjV$n
zKLgwQG^#^(2z_m*J{LI@@jV~L=V#r0Hr;(P-F+_IeJa{tXOB#GpGbF~$A0gfg?6MH
iS1`sfs8?Y{P>D$lc?|!C`WMu@puPq5EbuVkVfX_}Lb8$o
rename from image/test/crashtests/invalid_ico_height.ico
rename to image/test/reftest/ico/ico-bmp-corrupted/invalid_ico_height.ico
rename from image/test/crashtests/invalid_ico_width.ico
rename to image/test/reftest/ico/ico-bmp-corrupted/invalid_ico_width.ico
--- a/image/test/reftest/ico/ico-bmp-corrupted/reftest.list
+++ b/image/test/reftest/ico/ico-bmp-corrupted/reftest.list
@@ -3,8 +3,13 @@
 # Invalid value for bits per pixel (BPP) - detected when decoding the header.
 == wrapper.html?invalid-bpp.ico about:blank
 # Invalid BPP values for RLE4 - detected when decoding the image data.
 == wrapper.html?invalid-compression-RLE4.ico about:blank
 # Invalid BPP values for RLE8 - detected when decoding the image data.
 == wrapper.html?invalid-compression-RLE8.ico about:blank
 # Invalid compression value - detected when decoding the image data.
 == wrapper.html?invalid-compression.ico about:blank
+
+# Invalid ICO width and heigth should be ignored if the
+# contained BMP is correct.
+== invalid_ico_height.ico 16x16.png
+== invalid_ico_width.ico 16x16.png
--- a/image/test/reftest/ico/ico-mixed/reftest.list
+++ b/image/test/reftest/ico/ico-mixed/reftest.list
@@ -1,3 +1,14 @@
 # ICO BMP and PNG mixed tests
 
-== mixed-bmp-png.ico mixed-bmp-png48.png
+== mixed-bmp-png.ico mixed-bmp-png.png
+
+# Using media fragments to select different resolutions
+
+== mixed-bmp-png.ico#-moz-resolution=8,8 mixed-bmp-png.png
+== mixed-bmp-png.ico#test=true&-moz-resolution=8,8&other mixed-bmp-png.png
+== mixed-bmp-png.ico#-moz-resolution=32,32 mixed-bmp-png32.png
+== mixed-bmp-png.ico#-moz-resolution=39,39 mixed-bmp-png48.png
+== mixed-bmp-png.ico#-moz-resolution=40,40 mixed-bmp-png48.png
+== mixed-bmp-png.ico#-moz-resolution=48,48 mixed-bmp-png48.png
+== mixed-bmp-png.ico#-moz-resolution=64,64 mixed-bmp-png48.png
+== mixed-bmp-png.ico#-moz-resolution=64 mixed-bmp-png.png # Bad syntax will fall back to lowest resolution
index 8eb80c7db2ee450eaf16449d307986f7b1736c46..988a6201822f036d1d93c7916f3f3c4f9d7aaaf3
GIT binary patch
literal 92
zc${NkU<5%%1|X@xV8Fn@AO^&p0e<ehTvA*>Ca<T5OArG{8i+aAfTX<WkxsCLi(?4K
g%;Xj8!~gR$u$*R6{l?#61e9R#boFyt=akR{0MFbHZ~y=R
index ecb88edf3ca8ff586d009cac9d2b1cb40aff8999..3968a13f7222d4eadf4784d71d5703c7f8feb88c
GIT binary patch
literal 5934
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
KGDaEQiTfWk<8tEw
index cc8a4a31db973df11dd9c21b56d12eabab61cb62..df422207e7b9918f582896c7d7889c5103adf8f7
GIT binary patch
literal 3084
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
index ea14dbedede96973213586f1754a29c774e72993..645bc114c8de46cc0a4f6afb02c190f3aea9c7a3
GIT binary patch
literal 1078
zc%1ux<NpH&0WUXCHwH#V1_nkTWcYuZ!I^=X2?RhSGZ3(_v48*v8yhPdCkGc7CkH1d
z7Y{!V7dIa_Cnv8UFCV{vfS>>ukC3pCfH06P05XITq?4J21E^7eo0D6BWbpq0gCGZk
z0D}NCqaXv5AS1IN<NqTJ@<4a8GJ*jE6fiO|v#_$Ub8vET0~Kr)U|<5;&BVgY%F4n5
zl&uBIGq4D<3Mm>ovIz$!vMUve7&T5@$f4}C@t|nX#SbdRNkvVZTw>x9l2WQ_>Kd9_
zCZ=ZQ7M51dF0O9w9-dyoA)#U65s^{JDXD4c8JStdC8cHM6_r)ZEv;?s9i3g1CQq3<
zZTgIvvlcC0vUJ(<6)RV5+Pr1!w(UE1?mBe%$kAiRPn<k;>GGAU*RJ2VdF$b$$4{O<
zd;a3(tB;>PfBE|D`;VW$K>lK6U}l5?to{N90wWU(Fi6=!{$gY*2PqI_VO2C_6LJh>
zPb?HxGHT=yahkYr<3Ubk<Dd_sNktdA#8gZks(u7{4eT@GJk~^(&)^<I`0Ew}4>Kb$
z@|Xn~>={1CH`p`R@&2*Cv_&@i=s*6;rE}{ew``sDNVmkbR>Ww=#3g+aTyK>tpYBvv
zf5xT#WJi9l;fyyApO$PmRq%ZJe$n?UR)4xZ%ggYp_Fie<)#j)3mpxj?wXpfbe}=99
zwrUjz^zT-GXSusLev)3k_sy(zldFnF|Eblm{80V4?DHcXc|IG1UFqF6mW#bt-iprH
zuyhvZnn&d`c{d)i5w4u5w1LUtqZrT2-S(c3<n;FQooAhKY)z=-m5}A^qUGmyPIWa|
zf0mWgT)g6x?pu|~KlC_P1{=4==qc&XbUkOhyK1$o)7e{Rx30Q*d$-MhhKK5JxgW}w
zAF@hof7JbX>5tido1}88rc87-3uX9pXU+T?0lNgd{JobISAUMFbDx!RbI!({IZIZa
z+;&A(!`i;f=Hl&BzjF7^vS0Km=DgOUIm;|;Qda(sSTZMDWA{n5DUX(>YQMYIYrS^O
zeb4HT-4CZ9ly93aVCtPOr{8O*{KsKauF|z=g>7roma#YZD>>EdE^X5^TKqR<(_;&V
zA9Idb9+ydv@GAZAX=&qOQ~P_KXLqX2`o3i6E6<y&UrOl4-tvm)pYhP>pV;9m3tLx9
ZUSC<N&MGo#SEk0XkNx$h_tgKt2>>6_q)q?;
index 5722223c26fe0024d8c4d18f198114d5fde708bb..c3a4aee616675d212178f70a97d8ce73fc1813e2
GIT binary patch
literal 948
zc$@*W155mgP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV000AfNkl<ZIE_Wq
zZA{b!0LSrv8M=bR6`*jJ0!oI$G}1Y1KH(H^n&*TDN(PBSpuyeoq=3OvdO#V9B2%$2
zDo=>V4UD_Ey98Hw^3d_z0SAGilu96u$N#q%XukhFy!r@;Ac`UYA}|4121ocoXC$|(
zmhAFs@_()8{-eiC&%|c3PdfsLAWkBRQ*{45jN(!i!58%;Yg)+fe#rTTK4OcE_(y-i
z$&(q34D~;~c{U(UFxXPX_2L57Mw}+>yBnmHYDp>8aoIdTX7gQ?W%Wd!%An+eFYOh+
zJm@TfsS!X#VCo6YXF|{?$jJLDiqg!(XfynY&%Hv@4HE~-YB^GE=94SsZ1eTOQ0l{@
z`kfdnk`bpy5ttg`_Gg<gC%VyoWC^V?u4oUskd<|uV})fX4NZKiZy`$4#E#VO2|N}<
zWAQuOtMcJ~pAliW`zGekz37T{q&wOPi%Lq<p+(%v2qNkDQTF|)MV?cIb4WV=IeLQ6
zswvD|$I#7n^q4Lo+^I~%oV=8-=ml6*61r83Xh~kfmno4P%qc;hUCC^36;^8_SRa$i
zHbojW1zQ>Klw&OMN9f5@(wXRrB}PI=goL6^_M`@S6LBJwy;(mKa9)FFP&zNU@5N^M
z9@f9VhX(Zu#(R7)UEP9Em$DB_f*Tf<6zyhP)GJ>mHQ0}!_%j41T_7|i3-6Gxd11j;
zX21R(r%&u>pg_WKgDY*CaD?B_rqB`PMvqEDQ@}hLcUWNxf0Ih(Ms#tT`6YQLAN%?7
z;+&N@*n3fs9l*bpOBt#6rqPgwaL-tSK`y07A)#04h~<DS#t&X2-NS}>uZ66zb779)
z%-mTn#O+!|%TLRBXz*aPKa@Xe3lSK5Lb2Zlnm>~8m+B2H3Og(cCk&xW2w&~NMwczD
zneWc2?aOFSa-}cVnu-4H+}7>p@o*mk;xu=vbzFPfjyjnG?Lks3hn(nANa=`O#-E9+
z=}7RRGuD|_r4>WJEvCQjU21jbK%7KC1SThFxR^zr^E}EIzQT3sEQ}lMG07ci`_Pe&
zFni2#*8HP(pjY!2nyZJI7#n(qX~v;AK2BS{nr}Saxn@6?qWP~<BC)2z(+0iFmRpgI
zXj50A&rfB1^ucpX|5Fx4n4V^A;4XFFUZx^!KZSA`h5j<ikA_gAKF45hD@;F^2=E{F
WUdnCPA!Vxo0000<MNUMnLSTZj*T-Z4
--- a/image/test/unit/test_imgtools.js
+++ b/image/test/unit/test_imgtools.js
@@ -164,17 +164,17 @@ testdesc = "test encoding a scaled JPEG"
 // we'll reuse the container from the previous test
 istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 16);
 
 var encodedBytes = streamToArray(istream);
 // Get bytes for exected result
 var refName = "image1png16x16.jpg";
 var refFile = do_get_file(refName);
 istream = getFileInputStream(refFile);
-do_check_eq(istream.available(), 1051);
+do_check_eq(istream.available(), 1078);
 var referenceBytes = streamToArray(istream);
 
 // compare the encoder's output to the reference file.
 compareArrays(encodedBytes, referenceBytes);
 
 
 /* ========== 3 ========== */
 testnum++;
@@ -223,17 +223,17 @@ if (!isWindows) {
 // we'll reuse the container from the previous test
 istream = imgTools.encodeScaledImage(container, "image/png", 16, 16);
 
 encodedBytes = streamToArray(istream);
 // Get bytes for exected result
 refName = isWindows ? "image2jpg16x16-win.png" : "image2jpg16x16.png";
 refFile = do_get_file(refName);
 istream = getFileInputStream(refFile);
-do_check_eq(istream.available(), 950);
+do_check_eq(istream.available(), 948);
 referenceBytes = streamToArray(istream);
 
 // compare the encoder's output to the reference file.
 compareArrays(encodedBytes, referenceBytes);
 }
 
 
 /* ========== 6 ========== */
@@ -686,32 +686,30 @@ imgFile = do_get_file(imgName);
 
 istream = getFileInputStream(imgFile);
 do_check_eq(istream.available(), 17759);
 var errsrc = "none";
 
 try {
   container = imgTools.decodeImage(istream, inMimeType);
 
-  // We expect to hit an error during encoding because the ICO header of the
-  // image is fine, but the actual resources are corrupt. Since decodeImage()
-  // only performs a metadata decode, it doesn't decode far enough to realize
-  // this, but we'll find out when we do a full decode during encodeImage().
+  // We should never hit this - decodeImage throws an assertion because the
+  // image decoded doesn't have enough frames.
   try {
       istream = imgTools.encodeImage(container, "image/png");
   } catch (e) {
       err = e;
       errsrc = "encode";
   }
 } catch (e) {
   err = e;
   errsrc = "decode";
 }
 
-do_check_eq(errsrc, "encode");
+do_check_eq(errsrc, "decode");
 checkExpectedError(/NS_ERROR_FAILURE/, err);
 
 
 /* ========== bug 815359  ========== */
 testnum = 815359;
 testdesc = "test correct ico hotspots (bug 815359)";
 
 imgName = "bug815359.ico";
index 5722223c26fe0024d8c4d18f198114d5fde708bb..c3a4aee616675d212178f70a97d8ce73fc1813e2
GIT binary patch
literal 948
zc$@*W155mgP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV000AfNkl<ZIE_Wq
zZA{b!0LSrv8M=bR6`*jJ0!oI$G}1Y1KH(H^n&*TDN(PBSpuyeoq=3OvdO#V9B2%$2
zDo=>V4UD_Ey98Hw^3d_z0SAGilu96u$N#q%XukhFy!r@;Ac`UYA}|4121ocoXC$|(
zmhAFs@_()8{-eiC&%|c3PdfsLAWkBRQ*{45jN(!i!58%;Yg)+fe#rTTK4OcE_(y-i
z$&(q34D~;~c{U(UFxXPX_2L57Mw}+>yBnmHYDp>8aoIdTX7gQ?W%Wd!%An+eFYOh+
zJm@TfsS!X#VCo6YXF|{?$jJLDiqg!(XfynY&%Hv@4HE~-YB^GE=94SsZ1eTOQ0l{@
z`kfdnk`bpy5ttg`_Gg<gC%VyoWC^V?u4oUskd<|uV})fX4NZKiZy`$4#E#VO2|N}<
zWAQuOtMcJ~pAliW`zGekz37T{q&wOPi%Lq<p+(%v2qNkDQTF|)MV?cIb4WV=IeLQ6
zswvD|$I#7n^q4Lo+^I~%oV=8-=ml6*61r83Xh~kfmno4P%qc;hUCC^36;^8_SRa$i
zHbojW1zQ>Klw&OMN9f5@(wXRrB}PI=goL6^_M`@S6LBJwy;(mKa9)FFP&zNU@5N^M
z9@f9VhX(Zu#(R7)UEP9Em$DB_f*Tf<6zyhP)GJ>mHQ0}!_%j41T_7|i3-6Gxd11j;
zX21R(r%&u>pg_WKgDY*CaD?B_rqB`PMvqEDQ@}hLcUWNxf0Ih(Ms#tT`6YQLAN%?7
z;+&N@*n3fs9l*bpOBt#6rqPgwaL-tSK`y07A)#04h~<DS#t&X2-NS}>uZ66zb779)
z%-mTn#O+!|%TLRBXz*aPKa@Xe3lSK5Lb2Zlnm>~8m+B2H3Og(cCk&xW2w&~NMwczD
zneWc2?aOFSa-}cVnu-4H+}7>p@o*mk;xu=vbzFPfjyjnG?Lks3hn(nANa=`O#-E9+
z=}7RRGuD|_r4>WJEvCQjU21jbK%7KC1SThFxR^zr^E}EIzQT3sEQ}lMG07ci`_Pe&
zFni2#*8HP(pjY!2nyZJI7#n(qX~v;AK2BS{nr}Saxn@6?qWP~<BC)2z(+0iFmRpgI
zXj50A&rfB1^ucpX|5Fx4n4V^A;4XFFUZx^!KZSA`h5j<ikA_gAKF45hD@;F^2=E{F
WUdnCPA!Vxo0000<MNUMnLSTZj*T-Z4
index 238973189040030ed5071fb3f9ed8c141a802211..03a01c6de84a633c5a4a73a9b0187d92f4ba9125
GIT binary patch
literal 868
zc$@)b1DpJbP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0009kNkl<ZIE{Ui
zZ%ot$9LB%*&pYJKgB;y?N7{v(rijZTS#vDb_#?W}a%UqfDQ5)&Zuf&D=q}QF!A8#L
z&xrm^lAV?7x`A9GlMr*(;N0k7ID)(1Nh6K*qMFMi|9pB;xtZDX`uRN1o97X^gDm2C
z#IoTHZ%TGsU8?NOxs&fUEh+urn;pRkUON>dG&|3GV?VHeC`Q%`o#;}k0{;me@;vMo
zF=m<f2HA6bit4^e+WQCSRxi;NyGYHKQ|xa0n1*dDSr{~sze-%U1YD@i{B_QsOXRSh
z;g7uh+`gXL&{tH2qwEZxV_#$jZ-0dQ9(;=5VmfZj<<s{3vhJG!(Px)jPc+|ys@PE#
zJCnQ2RQC>3b1F*B@C18?#;G0{<56FLvbTD<9M*C*>_S8=A|l3wfBE0A6IF3CU2Er3
zYY|;<H&S^l!p@!%OvOGb0%ve{403pXKFMG@7QJkevyH1hO!zWy!m8rrLX(TaT0a#>
zzGlZ~XE7FdS(H~pfiJ*X_W_dMKfv{0UgUMR?zo&*)*e&6HdK!Twcg3m^^4h7RK$}l
z@8Iq@$@Y$uY^>`<e}4t~6_1ls>gDR74zAS^ee>>%()W;kJkk7Db7+H!o;6zB&(*WV
zA7bmi9!mTnN_}6TTULU`n$NqfySehdiEDFOdJpD~NUccKOic!=;vnj_Ftu4w6$deo
zlUc=1LU9so-;Nk_5p53P1BE1y8dw-~v3Z^L6%motkxi+@JvLOuNn)3o=}Ie|4m}66
z(=ad1Lv%TaHiu8EviT#TC3z$ZXhKBRiHJzIMt+<ux1u)Kh<mKWJxiHwaFVzBF<g!o
z78yzixL1&<cQD^6+*oMkyB_nri0J>Bg5!)!!@1%N#>&!B8*RirX{cU1iI?sr(X<k^
zK9l$}I+C+ylIQh^NbxOeTq9C;db!YN)NvwB&uBpg6Xh<>SK64Vvoqab<fpKn%cB~y
z9oiNV(cJcl6p@Xei(KiqnmBQng@FtUL#xevTez6<Y7=A4X}=qE^0bI(|L>4S5h*D*
u8$Nk8-8lBL-88zzVdxT(Cq%^hZ}Xqb0K7F1=KLuD0000<MNUMnLSTYT0jf3t