dom/plugins/ipc/BrowserStreamChild.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/plugins/BrowserStreamChild.h"
michael@0 7
michael@0 8 #include "mozilla/Attributes.h"
michael@0 9 #include "mozilla/plugins/PluginInstanceChild.h"
michael@0 10 #include "mozilla/plugins/StreamNotifyChild.h"
michael@0 11
michael@0 12 namespace mozilla {
michael@0 13 namespace plugins {
michael@0 14
michael@0 15 BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance,
michael@0 16 const nsCString& url,
michael@0 17 const uint32_t& length,
michael@0 18 const uint32_t& lastmodified,
michael@0 19 StreamNotifyChild* notifyData,
michael@0 20 const nsCString& headers,
michael@0 21 const nsCString& mimeType,
michael@0 22 const bool& seekable,
michael@0 23 NPError* rv,
michael@0 24 uint16_t* stype)
michael@0 25 : mInstance(instance)
michael@0 26 , mStreamStatus(kStreamOpen)
michael@0 27 , mDestroyPending(NOT_DESTROYED)
michael@0 28 , mNotifyPending(false)
michael@0 29 , mStreamAsFilePending(false)
michael@0 30 , mInstanceDying(false)
michael@0 31 , mState(CONSTRUCTING)
michael@0 32 , mURL(url)
michael@0 33 , mHeaders(headers)
michael@0 34 , mStreamNotify(notifyData)
michael@0 35 , mDeliveryTracker(MOZ_THIS_IN_INITIALIZER_LIST())
michael@0 36 {
michael@0 37 PLUGIN_LOG_DEBUG(("%s (%s, %i, %i, %p, %s, %s)", FULLFUNCTION,
michael@0 38 url.get(), length, lastmodified, (void*) notifyData,
michael@0 39 headers.get(), mimeType.get()));
michael@0 40
michael@0 41 AssertPluginThread();
michael@0 42
michael@0 43 memset(&mStream, 0, sizeof(mStream));
michael@0 44 mStream.ndata = static_cast<AStream*>(this);
michael@0 45 mStream.url = NullableStringGet(mURL);
michael@0 46 mStream.end = length;
michael@0 47 mStream.lastmodified = lastmodified;
michael@0 48 mStream.headers = NullableStringGet(mHeaders);
michael@0 49 if (notifyData)
michael@0 50 mStream.notifyData = notifyData->mClosure;
michael@0 51 }
michael@0 52
michael@0 53 NPError
michael@0 54 BrowserStreamChild::StreamConstructed(
michael@0 55 const nsCString& mimeType,
michael@0 56 const bool& seekable,
michael@0 57 uint16_t* stype)
michael@0 58 {
michael@0 59 NPError rv = NPERR_NO_ERROR;
michael@0 60
michael@0 61 *stype = NP_NORMAL;
michael@0 62 rv = mInstance->mPluginIface->newstream(
michael@0 63 &mInstance->mData, const_cast<char*>(NullableStringGet(mimeType)),
michael@0 64 &mStream, seekable, stype);
michael@0 65 if (rv != NPERR_NO_ERROR) {
michael@0 66 mState = DELETING;
michael@0 67 mStreamNotify = nullptr;
michael@0 68 }
michael@0 69 else {
michael@0 70 mState = ALIVE;
michael@0 71
michael@0 72 if (mStreamNotify)
michael@0 73 mStreamNotify->SetAssociatedStream(this);
michael@0 74 }
michael@0 75
michael@0 76 return rv;
michael@0 77 }
michael@0 78
michael@0 79 BrowserStreamChild::~BrowserStreamChild()
michael@0 80 {
michael@0 81 NS_ASSERTION(!mStreamNotify, "Should have nulled it by now!");
michael@0 82 }
michael@0 83
michael@0 84 bool
michael@0 85 BrowserStreamChild::RecvWrite(const int32_t& offset,
michael@0 86 const Buffer& data,
michael@0 87 const uint32_t& newlength)
michael@0 88 {
michael@0 89 PLUGIN_LOG_DEBUG_FUNCTION;
michael@0 90
michael@0 91 AssertPluginThread();
michael@0 92
michael@0 93 if (ALIVE != mState)
michael@0 94 NS_RUNTIMEABORT("Unexpected state: received data after NPP_DestroyStream?");
michael@0 95
michael@0 96 if (kStreamOpen != mStreamStatus)
michael@0 97 return true;
michael@0 98
michael@0 99 mStream.end = newlength;
michael@0 100
michael@0 101 NS_ASSERTION(data.Length() > 0, "Empty data");
michael@0 102
michael@0 103 PendingData* newdata = mPendingData.AppendElement();
michael@0 104 newdata->offset = offset;
michael@0 105 newdata->data = data;
michael@0 106 newdata->curpos = 0;
michael@0 107
michael@0 108 EnsureDeliveryPending();
michael@0 109
michael@0 110 return true;
michael@0 111 }
michael@0 112
michael@0 113 bool
michael@0 114 BrowserStreamChild::RecvNPP_StreamAsFile(const nsCString& fname)
michael@0 115 {
michael@0 116 PLUGIN_LOG_DEBUG(("%s (fname=%s)", FULLFUNCTION, fname.get()));
michael@0 117
michael@0 118 AssertPluginThread();
michael@0 119
michael@0 120 if (ALIVE != mState)
michael@0 121 NS_RUNTIMEABORT("Unexpected state: received file after NPP_DestroyStream?");
michael@0 122
michael@0 123 if (kStreamOpen != mStreamStatus)
michael@0 124 return true;
michael@0 125
michael@0 126 mStreamAsFilePending = true;
michael@0 127 mStreamAsFileName = fname;
michael@0 128 EnsureDeliveryPending();
michael@0 129
michael@0 130 return true;
michael@0 131 }
michael@0 132
michael@0 133 bool
michael@0 134 BrowserStreamChild::RecvNPP_DestroyStream(const NPReason& reason)
michael@0 135 {
michael@0 136 PLUGIN_LOG_DEBUG_METHOD;
michael@0 137
michael@0 138 if (ALIVE != mState)
michael@0 139 NS_RUNTIMEABORT("Unexpected state: recevied NPP_DestroyStream twice?");
michael@0 140
michael@0 141 mState = DYING;
michael@0 142 mDestroyPending = DESTROY_PENDING;
michael@0 143 if (NPRES_DONE != reason)
michael@0 144 mStreamStatus = reason;
michael@0 145
michael@0 146 EnsureDeliveryPending();
michael@0 147 return true;
michael@0 148 }
michael@0 149
michael@0 150 bool
michael@0 151 BrowserStreamChild::Recv__delete__()
michael@0 152 {
michael@0 153 AssertPluginThread();
michael@0 154
michael@0 155 if (DELETING != mState)
michael@0 156 NS_RUNTIMEABORT("Bad state, not DELETING");
michael@0 157
michael@0 158 return true;
michael@0 159 }
michael@0 160
michael@0 161 NPError
michael@0 162 BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
michael@0 163 {
michael@0 164 PLUGIN_LOG_DEBUG_FUNCTION;
michael@0 165
michael@0 166 AssertPluginThread();
michael@0 167
michael@0 168 if (ALIVE != mState || kStreamOpen != mStreamStatus)
michael@0 169 return NPERR_GENERIC_ERROR;
michael@0 170
michael@0 171 IPCByteRanges ranges;
michael@0 172 for (; aRangeList; aRangeList = aRangeList->next) {
michael@0 173 IPCByteRange br = {aRangeList->offset, aRangeList->length};
michael@0 174 ranges.push_back(br);
michael@0 175 }
michael@0 176
michael@0 177 NPError result;
michael@0 178 CallNPN_RequestRead(ranges, &result);
michael@0 179 return result;
michael@0 180 }
michael@0 181
michael@0 182 void
michael@0 183 BrowserStreamChild::NPN_DestroyStream(NPReason reason)
michael@0 184 {
michael@0 185 mStreamStatus = reason;
michael@0 186 if (ALIVE == mState)
michael@0 187 SendNPN_DestroyStream(reason);
michael@0 188
michael@0 189 EnsureDeliveryPending();
michael@0 190 }
michael@0 191
michael@0 192 void
michael@0 193 BrowserStreamChild::EnsureDeliveryPending()
michael@0 194 {
michael@0 195 MessageLoop::current()->PostTask(FROM_HERE,
michael@0 196 mDeliveryTracker.NewRunnableMethod(&BrowserStreamChild::Deliver));
michael@0 197 }
michael@0 198
michael@0 199 void
michael@0 200 BrowserStreamChild::Deliver()
michael@0 201 {
michael@0 202 while (kStreamOpen == mStreamStatus && mPendingData.Length()) {
michael@0 203 if (DeliverPendingData() && kStreamOpen == mStreamStatus) {
michael@0 204 SetSuspendedTimer();
michael@0 205 return;
michael@0 206 }
michael@0 207 }
michael@0 208 ClearSuspendedTimer();
michael@0 209
michael@0 210 NS_ASSERTION(kStreamOpen != mStreamStatus || 0 == mPendingData.Length(),
michael@0 211 "Exit out of the data-delivery loop with pending data");
michael@0 212 mPendingData.Clear();
michael@0 213
michael@0 214 // NPP_StreamAsFile() is documented (at MDN) to be called "when the stream
michael@0 215 // is complete" -- i.e. after all calls to NPP_WriteReady() and NPP_Write()
michael@0 216 // have finished. We make these calls asynchronously (from
michael@0 217 // DeliverPendingData()). So we need to make sure all the "pending data"
michael@0 218 // has been "delivered" before calling NPP_StreamAsFile() (also
michael@0 219 // asynchronously). Doing this resolves bug 687610, bug 670036 and possibly
michael@0 220 // also other bugs.
michael@0 221 if (mStreamAsFilePending) {
michael@0 222 if (mStreamStatus == kStreamOpen)
michael@0 223 mInstance->mPluginIface->asfile(&mInstance->mData, &mStream,
michael@0 224 mStreamAsFileName.get());
michael@0 225 mStreamAsFilePending = false;
michael@0 226 }
michael@0 227
michael@0 228 if (DESTROY_PENDING == mDestroyPending) {
michael@0 229 mDestroyPending = DESTROYED;
michael@0 230 if (mState != DYING)
michael@0 231 NS_RUNTIMEABORT("mDestroyPending but state not DYING");
michael@0 232
michael@0 233 NS_ASSERTION(NPRES_DONE != mStreamStatus, "Success status set too early!");
michael@0 234 if (kStreamOpen == mStreamStatus)
michael@0 235 mStreamStatus = NPRES_DONE;
michael@0 236
michael@0 237 (void) mInstance->mPluginIface
michael@0 238 ->destroystream(&mInstance->mData, &mStream, mStreamStatus);
michael@0 239 }
michael@0 240 if (DESTROYED == mDestroyPending && mNotifyPending) {
michael@0 241 NS_ASSERTION(mStreamNotify, "mDestroyPending but no mStreamNotify?");
michael@0 242
michael@0 243 mNotifyPending = false;
michael@0 244 mStreamNotify->NPP_URLNotify(mStreamStatus);
michael@0 245 delete mStreamNotify;
michael@0 246 mStreamNotify = nullptr;
michael@0 247 }
michael@0 248 if (DYING == mState && DESTROYED == mDestroyPending
michael@0 249 && !mStreamNotify && !mInstanceDying) {
michael@0 250 SendStreamDestroyed();
michael@0 251 mState = DELETING;
michael@0 252 }
michael@0 253 }
michael@0 254
michael@0 255 bool
michael@0 256 BrowserStreamChild::DeliverPendingData()
michael@0 257 {
michael@0 258 if (mState != ALIVE && mState != DYING)
michael@0 259 NS_RUNTIMEABORT("Unexpected state");
michael@0 260
michael@0 261 NS_ASSERTION(mPendingData.Length(), "Called from Deliver with empty pending");
michael@0 262
michael@0 263 while (mPendingData[0].curpos < static_cast<int32_t>(mPendingData[0].data.Length())) {
michael@0 264 int32_t r = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
michael@0 265 if (kStreamOpen != mStreamStatus)
michael@0 266 return false;
michael@0 267 if (0 == r) // plugin wants to suspend delivery
michael@0 268 return true;
michael@0 269
michael@0 270 r = mInstance->mPluginIface->write(
michael@0 271 &mInstance->mData, &mStream,
michael@0 272 mPendingData[0].offset + mPendingData[0].curpos, // offset
michael@0 273 mPendingData[0].data.Length() - mPendingData[0].curpos, // length
michael@0 274 const_cast<char*>(mPendingData[0].data.BeginReading() + mPendingData[0].curpos));
michael@0 275 if (kStreamOpen != mStreamStatus)
michael@0 276 return false;
michael@0 277 if (0 == r)
michael@0 278 return true;
michael@0 279 if (r < 0) { // error condition
michael@0 280 NPN_DestroyStream(NPRES_NETWORK_ERR);
michael@0 281 return false;
michael@0 282 }
michael@0 283 mPendingData[0].curpos += r;
michael@0 284 }
michael@0 285 mPendingData.RemoveElementAt(0);
michael@0 286 return false;
michael@0 287 }
michael@0 288
michael@0 289 void
michael@0 290 BrowserStreamChild::SetSuspendedTimer()
michael@0 291 {
michael@0 292 if (mSuspendedTimer.IsRunning())
michael@0 293 return;
michael@0 294 mSuspendedTimer.Start(
michael@0 295 base::TimeDelta::FromMilliseconds(100), // 100ms copied from Mozilla plugin host
michael@0 296 this, &BrowserStreamChild::Deliver);
michael@0 297 }
michael@0 298
michael@0 299 void
michael@0 300 BrowserStreamChild::ClearSuspendedTimer()
michael@0 301 {
michael@0 302 mSuspendedTimer.Stop();
michael@0 303 }
michael@0 304
michael@0 305 } /* namespace plugins */
michael@0 306 } /* namespace mozilla */

mercurial