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