image/src/Decoder.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "Decoder.h"
     8 #include "nsIConsoleService.h"
     9 #include "nsIScriptError.h"
    10 #include "GeckoProfiler.h"
    11 #include "nsServiceManagerUtils.h"
    12 #include "nsComponentManagerUtils.h"
    14 namespace mozilla {
    15 namespace image {
    17 Decoder::Decoder(RasterImage &aImage)
    18   : mImage(aImage)
    19   , mCurrentFrame(nullptr)
    20   , mImageData(nullptr)
    21   , mColormap(nullptr)
    22   , mDecodeFlags(0)
    23   , mDecodeDone(false)
    24   , mDataError(false)
    25   , mFrameCount(0)
    26   , mFailCode(NS_OK)
    27   , mNeedsNewFrame(false)
    28   , mInitialized(false)
    29   , mSizeDecode(false)
    30   , mInFrame(false)
    31   , mIsAnimated(false)
    32 {
    33 }
    35 Decoder::~Decoder()
    36 {
    37   mInitialized = false;
    38 }
    40 /*
    41  * Common implementation of the decoder interface.
    42  */
    44 void
    45 Decoder::Init()
    46 {
    47   // No re-initializing
    48   NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
    49   NS_ABORT_IF_FALSE(mObserver, "Need an observer!");
    51   // Fire OnStartDecode at init time to support bug 512435.
    52   if (!IsSizeDecode())
    53       mObserver->OnStartDecode();
    55   // Implementation-specific initialization
    56   InitInternal();
    58   mInitialized = true;
    59 }
    61 // Initializes a decoder whose image and observer is already being used by a
    62 // parent decoder
    63 void
    64 Decoder::InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
    65                            uint32_t* colormap, uint32_t colormapSize,
    66                            imgFrame* currentFrame)
    67 {
    68   // No re-initializing
    69   NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
    70   NS_ABORT_IF_FALSE(mObserver, "Need an observer!");
    72   mImageData = imageData;
    73   mImageDataLength = imageDataLength;
    74   mColormap = colormap;
    75   mColormapSize = colormapSize;
    76   mCurrentFrame = currentFrame;
    77   // We have all the frame data, so we've started the frame.
    78   if (!IsSizeDecode()) {
    79     PostFrameStart();
    80   }
    82   // Implementation-specific initialization
    83   InitInternal();
    84   mInitialized = true;
    85 }
    87 void
    88 Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
    89 {
    90   PROFILER_LABEL("ImageDecoder", "Write");
    91   MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC);
    93   // We're strict about decoder errors
    94   NS_ABORT_IF_FALSE(!HasDecoderError(),
    95                     "Not allowed to make more decoder calls after error!");
    97   // If a data error occured, just ignore future data
    98   if (HasDataError())
    99     return;
   101   if (IsSizeDecode() && HasSize()) {
   102     // More data came in since we found the size. We have nothing to do here.
   103     return;
   104   }
   106   // Pass the data along to the implementation
   107   WriteInternal(aBuffer, aCount, aStrategy);
   109   // If we're a synchronous decoder and we need a new frame to proceed, let's
   110   // create one and call it again.
   111   while (aStrategy == DECODE_SYNC && NeedsNewFrame() && !HasDataError()) {
   112     nsresult rv = AllocateFrame();
   114     if (NS_SUCCEEDED(rv)) {
   115       // Tell the decoder to use the data it saved when it asked for a new frame.
   116       WriteInternal(nullptr, 0, aStrategy);
   117     }
   118   }
   119 }
   121 void
   122 Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
   123 {
   124   MOZ_ASSERT(NS_IsMainThread());
   126   // Implementation-specific finalization
   127   if (!HasError())
   128     FinishInternal();
   130   // If the implementation left us mid-frame, finish that up.
   131   if (mInFrame && !HasError())
   132     PostFrameStop();
   134   // If PostDecodeDone() has not been called, we need to sent teardown
   135   // notifications.
   136   if (!IsSizeDecode() && !mDecodeDone) {
   138     // Log data errors to the error console
   139     nsCOMPtr<nsIConsoleService> consoleService =
   140       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   141     nsCOMPtr<nsIScriptError> errorObject =
   142       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
   144     if (consoleService && errorObject && !HasDecoderError()) {
   145       nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
   146                        NS_ConvertUTF8toUTF16(mImage.GetURIString()));
   148       if (NS_SUCCEEDED(errorObject->InitWithWindowID(
   149                          msg,
   150                          NS_ConvertUTF8toUTF16(mImage.GetURIString()),
   151                          EmptyString(), 0, 0, nsIScriptError::errorFlag,
   152                          "Image", mImage.InnerWindowID()
   153                        ))) {
   154         consoleService->LogMessage(errorObject);
   155       }
   156     }
   158     bool usable = !HasDecoderError();
   159     if (aShutdownIntent != RasterImage::eShutdownIntent_NotNeeded && !HasDecoderError()) {
   160       // If we only have a data error, we're usable if we have at least one complete frame.
   161       if (GetCompleteFrameCount() == 0) {
   162         usable = false;
   163       }
   164     }
   166     // If we're usable, do exactly what we should have when the decoder
   167     // completed.
   168     if (usable) {
   169       if (mInFrame) {
   170         PostFrameStop();
   171       }
   172       PostDecodeDone();
   173     } else {
   174       if (mObserver) {
   175         mObserver->OnStopDecode(NS_ERROR_FAILURE);
   176       }
   177     }
   178   }
   180   // Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize().
   181   mImageMetadata.SetOnImage(&mImage);
   183   if (mDecodeDone) {
   184     mImage.DecodingComplete();
   185   }
   186 }
   188 void
   189 Decoder::FinishSharedDecoder()
   190 {
   191   MOZ_ASSERT(NS_IsMainThread());
   193   if (!HasError()) {
   194     FinishInternal();
   195   }
   196 }
   198 nsresult
   199 Decoder::AllocateFrame()
   200 {
   201   MOZ_ASSERT(mNeedsNewFrame);
   202   MOZ_ASSERT(NS_IsMainThread());
   204   MarkFrameDirty();
   206   nsresult rv;
   207   imgFrame* frame = nullptr;
   208   if (mNewFrameData.mPaletteDepth) {
   209     rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
   210                             mNewFrameData.mOffsetY, mNewFrameData.mWidth,
   211                             mNewFrameData.mHeight, mNewFrameData.mFormat,
   212                             mNewFrameData.mPaletteDepth,
   213                             &mImageData, &mImageDataLength,
   214                             &mColormap, &mColormapSize, &frame);
   215   } else {
   216     rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
   217                             mNewFrameData.mOffsetY, mNewFrameData.mWidth,
   218                             mNewFrameData.mHeight, mNewFrameData.mFormat,
   219                             &mImageData, &mImageDataLength, &frame);
   220   }
   222   if (NS_SUCCEEDED(rv)) {
   223     mCurrentFrame = frame;
   224   } else {
   225     mCurrentFrame = nullptr;
   226   }
   228   // Notify if appropriate
   229   if (NS_SUCCEEDED(rv) && mNewFrameData.mFrameNum == mFrameCount) {
   230     PostFrameStart();
   231   } else if (NS_FAILED(rv)) {
   232     PostDataError();
   233   }
   235   // Mark ourselves as not needing another frame before talking to anyone else
   236   // so they can tell us if they need yet another.
   237   mNeedsNewFrame = false;
   239   return rv;
   240 }
   242 void
   243 Decoder::FlushInvalidations()
   244 {
   245   NS_ABORT_IF_FALSE(!HasDecoderError(),
   246                     "Not allowed to make more decoder calls after error!");
   248   // If we've got an empty invalidation rect, we have nothing to do
   249   if (mInvalidRect.IsEmpty())
   250     return;
   252   if (mObserver) {
   253 #ifdef XP_MACOSX
   254     // Bug 703231
   255     // Because of high quality down sampling on mac we show scan lines while decoding.
   256     // Bypass this problem by redrawing the border.
   257     if (mImageMetadata.HasSize()) {
   258       nsIntRect mImageBound(0, 0, mImageMetadata.GetWidth(), mImageMetadata.GetHeight());
   260       mInvalidRect.Inflate(1);
   261       mInvalidRect = mInvalidRect.Intersect(mImageBound);
   262     }
   263 #endif
   264     mObserver->FrameChanged(&mInvalidRect);
   265   }
   267   // Clear the invalidation rectangle
   268   mInvalidRect.SetEmpty();
   269 }
   271 void
   272 Decoder::SetSizeOnImage()
   273 {
   274   MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
   275   MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
   277   mImage.SetSize(mImageMetadata.GetWidth(),
   278                  mImageMetadata.GetHeight(),
   279                  mImageMetadata.GetOrientation());
   280 }
   282 /*
   283  * Hook stubs. Override these as necessary in decoder implementations.
   284  */
   286 void Decoder::InitInternal() { }
   287 void Decoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) { }
   288 void Decoder::FinishInternal() { }
   290 /*
   291  * Progress Notifications
   292  */
   294 void
   295 Decoder::PostSize(int32_t aWidth,
   296                   int32_t aHeight,
   297                   Orientation aOrientation /* = Orientation()*/)
   298 {
   299   // Validate
   300   NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!");
   301   NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!");
   303   // Tell the image
   304   mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
   306   // Notify the observer
   307   if (mObserver)
   308     mObserver->OnStartContainer();
   309 }
   311 void
   312 Decoder::PostFrameStart()
   313 {
   314   // We shouldn't already be mid-frame
   315   NS_ABORT_IF_FALSE(!mInFrame, "Starting new frame but not done with old one!");
   317   // We should take care of any invalidation region when wrapping up the
   318   // previous frame
   319   NS_ABORT_IF_FALSE(mInvalidRect.IsEmpty(),
   320                     "Start image frame with non-empty invalidation region!");
   322   // Update our state to reflect the new frame
   323   mFrameCount++;
   324   mInFrame = true;
   326   // Decoder implementations should only call this method if they successfully
   327   // appended the frame to the image. So mFrameCount should always match that
   328   // reported by the Image.
   329   NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(),
   330                     "Decoder frame count doesn't match image's!");
   332   // Fire notifications
   333   if (mObserver) {
   334     mObserver->OnStartFrame();
   335   }
   336 }
   338 void
   339 Decoder::PostFrameStop(FrameBlender::FrameAlpha aFrameAlpha /* = FrameBlender::kFrameHasAlpha */,
   340                        FrameBlender::FrameDisposalMethod aDisposalMethod /* = FrameBlender::kDisposeKeep */,
   341                        int32_t aTimeout /* = 0 */,
   342                        FrameBlender::FrameBlendMethod aBlendMethod /* = FrameBlender::kBlendOver */)
   343 {
   344   // We should be mid-frame
   345   NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!");
   346   NS_ABORT_IF_FALSE(mCurrentFrame, "Stopping frame when we don't have one!");
   348   // Update our state
   349   mInFrame = false;
   351   if (aFrameAlpha == FrameBlender::kFrameOpaque) {
   352     mCurrentFrame->SetHasNoAlpha();
   353   }
   355   mCurrentFrame->SetFrameDisposalMethod(aDisposalMethod);
   356   mCurrentFrame->SetRawTimeout(aTimeout);
   357   mCurrentFrame->SetBlendMethod(aBlendMethod);
   358   mCurrentFrame->ImageUpdated(mCurrentFrame->GetRect());
   360   // Flush any invalidations before we finish the frame
   361   FlushInvalidations();
   363   // Fire notifications
   364   if (mObserver) {
   365     mObserver->OnStopFrame();
   366     if (mFrameCount > 1 && !mIsAnimated) {
   367       mIsAnimated = true;
   368       mObserver->OnImageIsAnimated();
   369     }
   370   }
   371 }
   373 void
   374 Decoder::PostInvalidation(nsIntRect& aRect)
   375 {
   376   // We should be mid-frame
   377   NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
   378   NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
   380   // Account for the new region
   381   mInvalidRect.UnionRect(mInvalidRect, aRect);
   382   mCurrentFrame->ImageUpdated(aRect);
   383 }
   385 void
   386 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
   387 {
   388   NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
   389   NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
   390   NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
   391   mDecodeDone = true;
   393   mImageMetadata.SetLoopCount(aLoopCount);
   394   mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA);
   396   if (mObserver) {
   397     mObserver->OnStopDecode(NS_OK);
   398   }
   399 }
   401 void
   402 Decoder::PostDataError()
   403 {
   404   mDataError = true;
   405 }
   407 void
   408 Decoder::PostDecoderError(nsresult aFailureCode)
   409 {
   410   NS_ABORT_IF_FALSE(NS_FAILED(aFailureCode), "Not a failure code!");
   412   mFailCode = aFailureCode;
   414   // XXXbholley - we should report the image URI here, but imgContainer
   415   // needs to know its URI first
   416   NS_WARNING("Image decoding error - This is probably a bug!");
   417 }
   419 void
   420 Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
   421                       uint32_t width, uint32_t height,
   422                       gfxImageFormat format,
   423                       uint8_t palette_depth /* = 0 */)
   424 {
   425   // Decoders should never call NeedNewFrame without yielding back to Write().
   426   MOZ_ASSERT(!mNeedsNewFrame);
   428   // We don't want images going back in time or skipping frames.
   429   MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1));
   431   mNewFrameData = NewFrameData(framenum, x_offset, y_offset, width, height, format, palette_depth);
   432   mNeedsNewFrame = true;
   433 }
   435 void
   436 Decoder::MarkFrameDirty()
   437 {
   438   MOZ_ASSERT(NS_IsMainThread());
   440   if (mCurrentFrame) {
   441     mCurrentFrame->ApplyDirtToSurfaces();
   442   }
   443 }
   445 } // namespace image
   446 } // namespace mozilla

mercurial