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