dom/plugins/ipc/BrowserStreamChild.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/plugins/ipc/BrowserStreamChild.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,306 @@
     1.4 +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/plugins/BrowserStreamChild.h"
    1.10 +
    1.11 +#include "mozilla/Attributes.h"
    1.12 +#include "mozilla/plugins/PluginInstanceChild.h"
    1.13 +#include "mozilla/plugins/StreamNotifyChild.h"
    1.14 +
    1.15 +namespace mozilla {
    1.16 +namespace plugins {
    1.17 +
    1.18 +BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance,
    1.19 +                                       const nsCString& url,
    1.20 +                                       const uint32_t& length,
    1.21 +                                       const uint32_t& lastmodified,
    1.22 +                                       StreamNotifyChild* notifyData,
    1.23 +                                       const nsCString& headers,
    1.24 +                                       const nsCString& mimeType,
    1.25 +                                       const bool& seekable,
    1.26 +                                       NPError* rv,
    1.27 +                                       uint16_t* stype)
    1.28 +  : mInstance(instance)
    1.29 +  , mStreamStatus(kStreamOpen)
    1.30 +  , mDestroyPending(NOT_DESTROYED)
    1.31 +  , mNotifyPending(false)
    1.32 +  , mStreamAsFilePending(false)
    1.33 +  , mInstanceDying(false)
    1.34 +  , mState(CONSTRUCTING)
    1.35 +  , mURL(url)
    1.36 +  , mHeaders(headers)
    1.37 +  , mStreamNotify(notifyData)
    1.38 +  , mDeliveryTracker(MOZ_THIS_IN_INITIALIZER_LIST())
    1.39 +{
    1.40 +  PLUGIN_LOG_DEBUG(("%s (%s, %i, %i, %p, %s, %s)", FULLFUNCTION,
    1.41 +                    url.get(), length, lastmodified, (void*) notifyData,
    1.42 +                    headers.get(), mimeType.get()));
    1.43 +
    1.44 +  AssertPluginThread();
    1.45 +
    1.46 +  memset(&mStream, 0, sizeof(mStream));
    1.47 +  mStream.ndata = static_cast<AStream*>(this);
    1.48 +  mStream.url = NullableStringGet(mURL);
    1.49 +  mStream.end = length;
    1.50 +  mStream.lastmodified = lastmodified;
    1.51 +  mStream.headers = NullableStringGet(mHeaders);
    1.52 +  if (notifyData)
    1.53 +    mStream.notifyData = notifyData->mClosure;
    1.54 +}
    1.55 +
    1.56 +NPError
    1.57 +BrowserStreamChild::StreamConstructed(
    1.58 +            const nsCString& mimeType,
    1.59 +            const bool& seekable,
    1.60 +            uint16_t* stype)
    1.61 +{
    1.62 +  NPError rv = NPERR_NO_ERROR;
    1.63 +
    1.64 +  *stype = NP_NORMAL;
    1.65 +  rv = mInstance->mPluginIface->newstream(
    1.66 +    &mInstance->mData, const_cast<char*>(NullableStringGet(mimeType)),
    1.67 +    &mStream, seekable, stype);
    1.68 +  if (rv != NPERR_NO_ERROR) {
    1.69 +    mState = DELETING;
    1.70 +    mStreamNotify = nullptr;
    1.71 +  }
    1.72 +  else {
    1.73 +    mState = ALIVE;
    1.74 +
    1.75 +    if (mStreamNotify)
    1.76 +      mStreamNotify->SetAssociatedStream(this);
    1.77 +  }
    1.78 +
    1.79 +  return rv;
    1.80 +}
    1.81 +
    1.82 +BrowserStreamChild::~BrowserStreamChild()
    1.83 +{
    1.84 +  NS_ASSERTION(!mStreamNotify, "Should have nulled it by now!");
    1.85 +}
    1.86 +
    1.87 +bool
    1.88 +BrowserStreamChild::RecvWrite(const int32_t& offset,
    1.89 +                              const Buffer& data,
    1.90 +                              const uint32_t& newlength)
    1.91 +{
    1.92 +  PLUGIN_LOG_DEBUG_FUNCTION;
    1.93 +
    1.94 +  AssertPluginThread();
    1.95 +
    1.96 +  if (ALIVE != mState)
    1.97 +    NS_RUNTIMEABORT("Unexpected state: received data after NPP_DestroyStream?");
    1.98 +
    1.99 +  if (kStreamOpen != mStreamStatus)
   1.100 +    return true;
   1.101 +
   1.102 +  mStream.end = newlength;
   1.103 +
   1.104 +  NS_ASSERTION(data.Length() > 0, "Empty data");
   1.105 +
   1.106 +  PendingData* newdata = mPendingData.AppendElement();
   1.107 +  newdata->offset = offset;
   1.108 +  newdata->data = data;
   1.109 +  newdata->curpos = 0;
   1.110 +
   1.111 +  EnsureDeliveryPending();
   1.112 +
   1.113 +  return true;
   1.114 +}
   1.115 +
   1.116 +bool
   1.117 +BrowserStreamChild::RecvNPP_StreamAsFile(const nsCString& fname)
   1.118 +{
   1.119 +  PLUGIN_LOG_DEBUG(("%s (fname=%s)", FULLFUNCTION, fname.get()));
   1.120 +
   1.121 +  AssertPluginThread();
   1.122 +
   1.123 +  if (ALIVE != mState)
   1.124 +    NS_RUNTIMEABORT("Unexpected state: received file after NPP_DestroyStream?");
   1.125 +
   1.126 +  if (kStreamOpen != mStreamStatus)
   1.127 +    return true;
   1.128 +
   1.129 +  mStreamAsFilePending = true;
   1.130 +  mStreamAsFileName = fname;
   1.131 +  EnsureDeliveryPending();
   1.132 +
   1.133 +  return true;
   1.134 +}
   1.135 +
   1.136 +bool
   1.137 +BrowserStreamChild::RecvNPP_DestroyStream(const NPReason& reason)
   1.138 +{
   1.139 +  PLUGIN_LOG_DEBUG_METHOD;
   1.140 +
   1.141 +  if (ALIVE != mState)
   1.142 +    NS_RUNTIMEABORT("Unexpected state: recevied NPP_DestroyStream twice?");
   1.143 +
   1.144 +  mState = DYING;
   1.145 +  mDestroyPending = DESTROY_PENDING;
   1.146 +  if (NPRES_DONE != reason)
   1.147 +    mStreamStatus = reason;
   1.148 +
   1.149 +  EnsureDeliveryPending();
   1.150 +  return true;
   1.151 +}
   1.152 +
   1.153 +bool
   1.154 +BrowserStreamChild::Recv__delete__()
   1.155 +{
   1.156 +  AssertPluginThread();
   1.157 +
   1.158 +  if (DELETING != mState)
   1.159 +    NS_RUNTIMEABORT("Bad state, not DELETING");
   1.160 +
   1.161 +  return true;
   1.162 +}
   1.163 +
   1.164 +NPError
   1.165 +BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
   1.166 +{
   1.167 +  PLUGIN_LOG_DEBUG_FUNCTION;
   1.168 +
   1.169 +  AssertPluginThread();
   1.170 +
   1.171 +  if (ALIVE != mState || kStreamOpen != mStreamStatus)
   1.172 +    return NPERR_GENERIC_ERROR;
   1.173 +
   1.174 +  IPCByteRanges ranges;
   1.175 +  for (; aRangeList; aRangeList = aRangeList->next) {
   1.176 +    IPCByteRange br = {aRangeList->offset, aRangeList->length};
   1.177 +    ranges.push_back(br);
   1.178 +  }
   1.179 +
   1.180 +  NPError result;
   1.181 +  CallNPN_RequestRead(ranges, &result);
   1.182 +  return result;
   1.183 +}
   1.184 +
   1.185 +void
   1.186 +BrowserStreamChild::NPN_DestroyStream(NPReason reason)
   1.187 +{
   1.188 +  mStreamStatus = reason;
   1.189 +  if (ALIVE == mState)
   1.190 +    SendNPN_DestroyStream(reason);
   1.191 +
   1.192 +  EnsureDeliveryPending();
   1.193 +}
   1.194 +
   1.195 +void
   1.196 +BrowserStreamChild::EnsureDeliveryPending()
   1.197 +{
   1.198 +  MessageLoop::current()->PostTask(FROM_HERE,
   1.199 +    mDeliveryTracker.NewRunnableMethod(&BrowserStreamChild::Deliver));
   1.200 +}
   1.201 +
   1.202 +void
   1.203 +BrowserStreamChild::Deliver()
   1.204 +{
   1.205 +  while (kStreamOpen == mStreamStatus && mPendingData.Length()) {
   1.206 +    if (DeliverPendingData() && kStreamOpen == mStreamStatus) {
   1.207 +      SetSuspendedTimer();
   1.208 +      return;
   1.209 +    }
   1.210 +  }
   1.211 +  ClearSuspendedTimer();
   1.212 +
   1.213 +  NS_ASSERTION(kStreamOpen != mStreamStatus || 0 == mPendingData.Length(),
   1.214 +               "Exit out of the data-delivery loop with pending data");
   1.215 +  mPendingData.Clear();
   1.216 +
   1.217 +  // NPP_StreamAsFile() is documented (at MDN) to be called "when the stream
   1.218 +  // is complete" -- i.e. after all calls to NPP_WriteReady() and NPP_Write()
   1.219 +  // have finished.  We make these calls asynchronously (from
   1.220 +  // DeliverPendingData()).  So we need to make sure all the "pending data"
   1.221 +  // has been "delivered" before calling NPP_StreamAsFile() (also
   1.222 +  // asynchronously).  Doing this resolves bug 687610, bug 670036 and possibly
   1.223 +  // also other bugs.
   1.224 +  if (mStreamAsFilePending) {
   1.225 +    if (mStreamStatus == kStreamOpen)
   1.226 +      mInstance->mPluginIface->asfile(&mInstance->mData, &mStream,
   1.227 +                                      mStreamAsFileName.get());
   1.228 +    mStreamAsFilePending = false;
   1.229 +  }
   1.230 +
   1.231 +  if (DESTROY_PENDING == mDestroyPending) {
   1.232 +    mDestroyPending = DESTROYED;
   1.233 +    if (mState != DYING)
   1.234 +      NS_RUNTIMEABORT("mDestroyPending but state not DYING");
   1.235 +
   1.236 +    NS_ASSERTION(NPRES_DONE != mStreamStatus, "Success status set too early!");
   1.237 +    if (kStreamOpen == mStreamStatus)
   1.238 +      mStreamStatus = NPRES_DONE;
   1.239 +
   1.240 +    (void) mInstance->mPluginIface
   1.241 +      ->destroystream(&mInstance->mData, &mStream, mStreamStatus);
   1.242 +  }
   1.243 +  if (DESTROYED == mDestroyPending && mNotifyPending) {
   1.244 +    NS_ASSERTION(mStreamNotify, "mDestroyPending but no mStreamNotify?");
   1.245 +      
   1.246 +    mNotifyPending = false;
   1.247 +    mStreamNotify->NPP_URLNotify(mStreamStatus);
   1.248 +    delete mStreamNotify;
   1.249 +    mStreamNotify = nullptr;
   1.250 +  }
   1.251 +  if (DYING == mState && DESTROYED == mDestroyPending
   1.252 +      && !mStreamNotify && !mInstanceDying) {
   1.253 +    SendStreamDestroyed();
   1.254 +    mState = DELETING;
   1.255 +  }
   1.256 +}
   1.257 +
   1.258 +bool
   1.259 +BrowserStreamChild::DeliverPendingData()
   1.260 +{
   1.261 +  if (mState != ALIVE && mState != DYING)
   1.262 +    NS_RUNTIMEABORT("Unexpected state");
   1.263 +
   1.264 +  NS_ASSERTION(mPendingData.Length(), "Called from Deliver with empty pending");
   1.265 +
   1.266 +  while (mPendingData[0].curpos < static_cast<int32_t>(mPendingData[0].data.Length())) {
   1.267 +    int32_t r = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
   1.268 +    if (kStreamOpen != mStreamStatus)
   1.269 +      return false;
   1.270 +    if (0 == r) // plugin wants to suspend delivery
   1.271 +      return true;
   1.272 +
   1.273 +    r = mInstance->mPluginIface->write(
   1.274 +      &mInstance->mData, &mStream,
   1.275 +      mPendingData[0].offset + mPendingData[0].curpos, // offset
   1.276 +      mPendingData[0].data.Length() - mPendingData[0].curpos, // length
   1.277 +      const_cast<char*>(mPendingData[0].data.BeginReading() + mPendingData[0].curpos));
   1.278 +    if (kStreamOpen != mStreamStatus)
   1.279 +      return false;
   1.280 +    if (0 == r)
   1.281 +      return true;
   1.282 +    if (r < 0) { // error condition
   1.283 +      NPN_DestroyStream(NPRES_NETWORK_ERR);
   1.284 +      return false;
   1.285 +    }
   1.286 +    mPendingData[0].curpos += r;
   1.287 +  }
   1.288 +  mPendingData.RemoveElementAt(0);
   1.289 +  return false;
   1.290 +}
   1.291 +
   1.292 +void
   1.293 +BrowserStreamChild::SetSuspendedTimer()
   1.294 +{
   1.295 +  if (mSuspendedTimer.IsRunning())
   1.296 +    return;
   1.297 +  mSuspendedTimer.Start(
   1.298 +    base::TimeDelta::FromMilliseconds(100), // 100ms copied from Mozilla plugin host
   1.299 +    this, &BrowserStreamChild::Deliver);
   1.300 +}
   1.301 +
   1.302 +void
   1.303 +BrowserStreamChild::ClearSuspendedTimer()
   1.304 +{
   1.305 +  mSuspendedTimer.Stop();
   1.306 +}
   1.307 +
   1.308 +} /* namespace plugins */
   1.309 +} /* namespace mozilla */

mercurial