Commit cafc8e4f authored by Stephen Pohl's avatar Stephen Pohl
Browse files

Bug 916128 - Ensure that image encoding callbacks are released on the main thread. r=khuey

parent 0ef46a7c
Loading
Loading
Loading
Loading
+48 −17
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ public:
    , mScriptContext(aScriptContext)
    , mEncoderThread(aEncoderThread)
    , mCallback(&aCallback)
    , mFailed(false)
  {}
  virtual ~EncodingCompleteEvent() {}

@@ -31,6 +32,9 @@ public:
  {
    MOZ_ASSERT(NS_IsMainThread());

    mozilla::ErrorResult rv;

    if (!mFailed) {
      nsRefPtr<nsDOMMemoryFile> blob =
        new nsDOMMemoryFile(mImgData, mImgSize, mType);

@@ -41,9 +45,15 @@ public:
        }
      }

    mozilla::ErrorResult rv;
      mCallback->Call(blob, rv);
    NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
    }

    // These members aren't thread-safe. We're making sure that they're being
    // released on the main thread here. Otherwise, they could be getting
    // released by EncodingRunnable's destructor on the encoding thread
    // (bug 916128).
    mScriptContext = nullptr;
    mCallback = nullptr;

    mEncoderThread->Shutdown();
    return rv.ErrorCode();
@@ -56,6 +66,11 @@ public:
    mType = aType;
  }

  void SetFailed()
  {
    mFailed = true;
  }

private:
  uint64_t mImgSize;
  nsAutoString mType;
@@ -63,6 +78,7 @@ private:
  nsCOMPtr<nsIScriptContext> mScriptContext;
  nsCOMPtr<nsIThread> mEncoderThread;
  nsRefPtr<FileCallback> mCallback;
  bool mFailed;
};

NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
@@ -91,7 +107,7 @@ public:
  {}
  virtual ~EncodingRunnable() {}

  NS_IMETHOD Run()
  nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData)
  {
    nsCOMPtr<nsIInputStream> stream;
    nsresult rv = ImageEncoder::ExtractDataInternal(mType,
@@ -117,18 +133,33 @@ public:
    }
    NS_ENSURE_SUCCESS(rv, rv);

    uint64_t imgSize;
    rv = stream->Available(&imgSize);
    rv = stream->Available(aImgSize);
    NS_ENSURE_SUCCESS(rv, rv);
    NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
    NS_ENSURE_TRUE(*aImgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);

    void* imgData = nullptr;
    rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
    rv = NS_ReadInputStreamToBuffer(stream, aImgData, *aImgSize);
    NS_ENSURE_SUCCESS(rv, rv);

    return rv;
  }

  NS_IMETHOD Run()
  {
    uint64_t imgSize;
    void* imgData = nullptr;

    nsresult rv = ProcessImageData(&imgSize, &imgData);
    if (NS_FAILED(rv)) {
      mEncodingCompleteEvent->SetFailed();
    } else {
      mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
    }
    rv = NS_DispatchToMainThread(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
    NS_ENSURE_SUCCESS(rv, rv);
    if (NS_FAILED(rv)) {
      // Better to leak than to crash.
      mEncodingCompleteEvent.forget();
      return rv;
    }

    return rv;
  }