Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
5
6
/**
7
* The MIME stream separates headers and a datastream. It also allows
8
* automatic creation of the content-length header.
9
*/
10
11
#include "ipc/IPCMessageUtils.h"
12
13
#include "nsCOMPtr.h"
14
#include "nsComponentManagerUtils.h"
15
#include "nsIAsyncInputStream.h"
16
#include "nsIInputStreamLength.h"
17
#include "nsIHttpHeaderVisitor.h"
18
#include "nsIMIMEInputStream.h"
19
#include "nsISeekableStream.h"
20
#include "nsString.h"
21
#include "nsMIMEInputStream.h"
22
#include "nsIClassInfoImpl.h"
23
#include "nsIIPCSerializableInputStream.h"
24
#include "mozilla/Move.h"
25
#include "mozilla/Mutex.h"
26
#include "mozilla/ipc/InputStreamUtils.h"
27
28
using namespace mozilla::ipc;
29
using mozilla::Maybe;
30
31
class nsMIMEInputStream : public nsIMIMEInputStream,
32
public nsISeekableStream,
33
public nsIIPCSerializableInputStream,
34
public nsIAsyncInputStream,
35
public nsIInputStreamCallback,
36
public nsIInputStreamLength,
37
public nsIAsyncInputStreamLength,
38
public nsIInputStreamLengthCallback,
39
public nsICloneableInputStream {
40
virtual ~nsMIMEInputStream() = default;
41
42
public:
43
nsMIMEInputStream();
44
45
NS_DECL_THREADSAFE_ISUPPORTS
46
NS_DECL_NSIINPUTSTREAM
47
NS_DECL_NSIMIMEINPUTSTREAM
48
NS_DECL_NSISEEKABLESTREAM
49
NS_DECL_NSITELLABLESTREAM
50
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
51
NS_DECL_NSIASYNCINPUTSTREAM
52
NS_DECL_NSIINPUTSTREAMCALLBACK
53
NS_DECL_NSIINPUTSTREAMLENGTH
54
NS_DECL_NSIASYNCINPUTSTREAMLENGTH
55
NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
56
NS_DECL_NSICLONEABLEINPUTSTREAM
57
58
private:
59
void InitStreams();
60
61
template <typename M>
62
void SerializeInternal(InputStreamParams& aParams,
63
FileDescriptorArray& aFileDescriptors,
64
bool aDelayedStart, uint32_t aMaxSize,
65
uint32_t* aSizeUsed, M* aManager);
66
67
struct MOZ_STACK_CLASS ReadSegmentsState {
68
nsCOMPtr<nsIInputStream> mThisStream;
69
nsWriteSegmentFun mWriter;
70
void* mClosure;
71
};
72
static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
73
const char* aFromRawSegment, uint32_t aToOffset,
74
uint32_t aCount, uint32_t* aWriteCount);
75
76
bool IsSeekableInputStream() const;
77
bool IsAsyncInputStream() const;
78
bool IsIPCSerializable() const;
79
bool IsInputStreamLength() const;
80
bool IsAsyncInputStreamLength() const;
81
bool IsCloneableInputStream() const;
82
83
nsTArray<HeaderEntry> mHeaders;
84
85
nsCOMPtr<nsIInputStream> mStream;
86
bool mStartedReading;
87
88
mozilla::Mutex mMutex;
89
90
// This is protected by mutex.
91
nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
92
93
// This is protected by mutex.
94
nsCOMPtr<nsIInputStreamLengthCallback> mAsyncInputStreamLengthCallback;
95
};
96
97
NS_IMPL_ADDREF(nsMIMEInputStream)
98
NS_IMPL_RELEASE(nsMIMEInputStream)
99
100
NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
101
NS_MIMEINPUTSTREAM_CID)
102
103
NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream)
104
NS_INTERFACE_MAP_ENTRY(nsIMIMEInputStream)
105
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMIMEInputStream)
106
NS_INTERFACE_MAP_ENTRY(nsITellableStream)
107
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableInputStream())
108
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
109
IsIPCSerializable())
110
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, IsAsyncInputStream())
111
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
112
IsAsyncInputStream())
113
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMIMEInputStream)
114
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
115
IsInputStreamLength())
116
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
117
IsAsyncInputStreamLength())
118
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback,
119
IsAsyncInputStreamLength())
120
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
121
IsCloneableInputStream())
122
NS_IMPL_QUERY_CLASSINFO(nsMIMEInputStream)
123
NS_INTERFACE_MAP_END
124
125
NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream, nsIMIMEInputStream,
126
nsIAsyncInputStream, nsIInputStream,
127
nsISeekableStream, nsITellableStream)
128
129
nsMIMEInputStream::nsMIMEInputStream()
130
: mStartedReading(false), mMutex("nsMIMEInputStream::mMutex") {}
131
132
NS_IMETHODIMP
133
nsMIMEInputStream::AddHeader(const char* aName, const char* aValue) {
134
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
135
136
HeaderEntry* entry = mHeaders.AppendElement();
137
entry->name().Append(aName);
138
entry->value().Append(aValue);
139
140
return NS_OK;
141
}
142
143
NS_IMETHODIMP
144
nsMIMEInputStream::VisitHeaders(nsIHttpHeaderVisitor* visitor) {
145
nsresult rv;
146
147
for (auto& header : mHeaders) {
148
rv = visitor->VisitHeader(header.name(), header.value());
149
if (NS_FAILED(rv)) {
150
return rv;
151
}
152
}
153
return NS_OK;
154
}
155
156
NS_IMETHODIMP
157
nsMIMEInputStream::SetData(nsIInputStream* aStream) {
158
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
159
160
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
161
if (!seekable) {
162
return NS_ERROR_INVALID_ARG;
163
}
164
165
mStream = aStream;
166
return NS_OK;
167
}
168
169
NS_IMETHODIMP
170
nsMIMEInputStream::GetData(nsIInputStream** aStream) {
171
NS_ENSURE_ARG_POINTER(aStream);
172
*aStream = mStream;
173
NS_IF_ADDREF(*aStream);
174
return NS_OK;
175
}
176
177
// set up the internal streams
178
void nsMIMEInputStream::InitStreams() {
179
NS_ASSERTION(!mStartedReading,
180
"Don't call initStreams twice without rewinding");
181
182
mStartedReading = true;
183
}
184
185
#define INITSTREAMS \
186
if (!mStartedReading) { \
187
NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED); \
188
InitStreams(); \
189
}
190
191
// Reset mStartedReading when Seek-ing to start
192
NS_IMETHODIMP
193
nsMIMEInputStream::Seek(int32_t whence, int64_t offset) {
194
NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED);
195
196
nsresult rv;
197
nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
198
199
if (whence == NS_SEEK_SET && offset == 0) {
200
rv = stream->Seek(whence, offset);
201
if (NS_SUCCEEDED(rv)) mStartedReading = false;
202
} else {
203
INITSTREAMS;
204
rv = stream->Seek(whence, offset);
205
}
206
207
return rv;
208
}
209
210
// Proxy ReadSegments since we need to be a good little nsIInputStream
211
NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
212
void* aClosure, uint32_t aCount,
213
uint32_t* _retval) {
214
INITSTREAMS;
215
ReadSegmentsState state;
216
// Disambiguate ambiguous nsIInputStream.
217
state.mThisStream =
218
static_cast<nsIInputStream*>(static_cast<nsIMIMEInputStream*>(this));
219
state.mWriter = aWriter;
220
state.mClosure = aClosure;
221
return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
222
}
223
224
nsresult nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
225
const char* aFromRawSegment,
226
uint32_t aToOffset, uint32_t aCount,
227
uint32_t* aWriteCount) {
228
ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
229
return (state->mWriter)(state->mThisStream, state->mClosure, aFromRawSegment,
230
aToOffset, aCount, aWriteCount);
231
}
232
233
/**
234
* Forward everything else to the mStream after calling InitStreams()
235
*/
236
237
// nsIInputStream
238
NS_IMETHODIMP nsMIMEInputStream::Close(void) {
239
INITSTREAMS;
240
return mStream->Close();
241
}
242
NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t* _retval) {
243
INITSTREAMS;
244
return mStream->Available(_retval);
245
}
246
NS_IMETHODIMP nsMIMEInputStream::Read(char* buf, uint32_t count,
247
uint32_t* _retval) {
248
INITSTREAMS;
249
return mStream->Read(buf, count, _retval);
250
}
251
NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool* aNonBlocking) {
252
INITSTREAMS;
253
return mStream->IsNonBlocking(aNonBlocking);
254
}
255
256
// nsIAsyncInputStream
257
NS_IMETHODIMP
258
nsMIMEInputStream::CloseWithStatus(nsresult aStatus) {
259
INITSTREAMS;
260
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
261
return asyncStream->CloseWithStatus(aStatus);
262
}
263
264
NS_IMETHODIMP
265
nsMIMEInputStream::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
266
uint32_t aRequestedCount,
267
nsIEventTarget* aEventTarget) {
268
INITSTREAMS;
269
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
270
if (NS_WARN_IF(!asyncStream)) {
271
return NS_ERROR_FAILURE;
272
}
273
274
nsCOMPtr<nsIInputStreamCallback> callback = aCallback ? this : nullptr;
275
{
276
MutexAutoLock lock(mMutex);
277
if (mAsyncWaitCallback && aCallback) {
278
return NS_ERROR_FAILURE;
279
}
280
281
mAsyncWaitCallback = aCallback;
282
}
283
284
return asyncStream->AsyncWait(callback, aFlags, aRequestedCount,
285
aEventTarget);
286
}
287
288
// nsIInputStreamCallback
289
290
NS_IMETHODIMP
291
nsMIMEInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream) {
292
nsCOMPtr<nsIInputStreamCallback> callback;
293
294
{
295
MutexAutoLock lock(mMutex);
296
297
// We have been canceled in the meanwhile.
298
if (!mAsyncWaitCallback) {
299
return NS_OK;
300
}
301
302
callback.swap(mAsyncWaitCallback);
303
}
304
305
MOZ_ASSERT(callback);
306
return callback->OnInputStreamReady(this);
307
}
308
309
// nsITellableStream
310
NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t* _retval) {
311
INITSTREAMS;
312
nsCOMPtr<nsITellableStream> stream = do_QueryInterface(mStream);
313
return stream->Tell(_retval);
314
}
315
316
// nsISeekableStream
317
NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
318
INITSTREAMS;
319
nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
320
return stream->SetEOF();
321
}
322
323
/**
324
* Factory method used by do_CreateInstance
325
*/
326
327
nsresult nsMIMEInputStreamConstructor(nsISupports* outer, REFNSIID iid,
328
void** result) {
329
*result = nullptr;
330
331
if (outer) return NS_ERROR_NO_AGGREGATION;
332
333
RefPtr<nsMIMEInputStream> inst = new nsMIMEInputStream();
334
if (!inst) return NS_ERROR_OUT_OF_MEMORY;
335
336
return inst->QueryInterface(iid, result);
337
}
338
339
void nsMIMEInputStream::Serialize(InputStreamParams& aParams,
340
FileDescriptorArray& aFileDescriptors,
341
bool aDelayedStart, uint32_t aMaxSize,
342
uint32_t* aSizeUsed,
343
mozilla::dom::ContentChild* aManager) {
344
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
345
aSizeUsed, aManager);
346
}
347
348
void nsMIMEInputStream::Serialize(InputStreamParams& aParams,
349
FileDescriptorArray& aFileDescriptors,
350
bool aDelayedStart, uint32_t aMaxSize,
351
uint32_t* aSizeUsed,
352
PBackgroundChild* aManager) {
353
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
354
aSizeUsed, aManager);
355
}
356
357
void nsMIMEInputStream::Serialize(InputStreamParams& aParams,
358
FileDescriptorArray& aFileDescriptors,
359
bool aDelayedStart, uint32_t aMaxSize,
360
uint32_t* aSizeUsed,
361
mozilla::dom::ContentParent* aManager) {
362
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
363
aSizeUsed, aManager);
364
}
365
366
void nsMIMEInputStream::Serialize(InputStreamParams& aParams,
367
FileDescriptorArray& aFileDescriptors,
368
bool aDelayedStart, uint32_t aMaxSize,
369
uint32_t* aSizeUsed,
370
PBackgroundParent* aManager) {
371
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
372
aSizeUsed, aManager);
373
}
374
375
template <typename M>
376
void nsMIMEInputStream::SerializeInternal(InputStreamParams& aParams,
377
FileDescriptorArray& aFileDescriptors,
378
bool aDelayedStart, uint32_t aMaxSize,
379
uint32_t* aSizeUsed, M* aManager) {
380
MOZ_ASSERT(aSizeUsed);
381
*aSizeUsed = 0;
382
383
MIMEInputStreamParams params;
384
385
if (mStream) {
386
InputStreamParams wrappedParams;
387
InputStreamHelper::SerializeInputStream(mStream, wrappedParams,
388
aFileDescriptors, aDelayedStart,
389
aMaxSize, aSizeUsed, aManager);
390
391
NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
392
"Wrapped stream failed to serialize!");
393
394
params.optionalStream().emplace(wrappedParams);
395
}
396
397
params.headers() = mHeaders;
398
params.startedReading() = mStartedReading;
399
400
aParams = params;
401
}
402
403
bool nsMIMEInputStream::Deserialize(
404
const InputStreamParams& aParams,
405
const FileDescriptorArray& aFileDescriptors) {
406
if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) {
407
NS_ERROR("Received unknown parameters from the other process!");
408
return false;
409
}
410
411
const MIMEInputStreamParams& params = aParams.get_MIMEInputStreamParams();
412
const Maybe<InputStreamParams>& wrappedParams = params.optionalStream();
413
414
mHeaders = params.headers();
415
mStartedReading = params.startedReading();
416
417
if (wrappedParams.isSome()) {
418
nsCOMPtr<nsIInputStream> stream;
419
stream = InputStreamHelper::DeserializeInputStream(wrappedParams.ref(),
420
aFileDescriptors);
421
if (!stream) {
422
NS_WARNING("Failed to deserialize wrapped stream!");
423
return false;
424
}
425
426
mStream = stream;
427
}
428
429
return true;
430
}
431
432
NS_IMETHODIMP
433
nsMIMEInputStream::Length(int64_t* aLength) {
434
nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
435
if (NS_WARN_IF(!stream)) {
436
return NS_ERROR_FAILURE;
437
}
438
439
return stream->Length(aLength);
440
}
441
442
NS_IMETHODIMP
443
nsMIMEInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
444
nsIEventTarget* aEventTarget) {
445
nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
446
if (NS_WARN_IF(!stream)) {
447
return NS_ERROR_FAILURE;
448
}
449
450
nsCOMPtr<nsIInputStreamLengthCallback> callback = aCallback ? this : nullptr;
451
{
452
MutexAutoLock lock(mMutex);
453
mAsyncInputStreamLengthCallback = aCallback;
454
}
455
456
return stream->AsyncLengthWait(callback, aEventTarget);
457
}
458
459
NS_IMETHODIMP
460
nsMIMEInputStream::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
461
int64_t aLength) {
462
nsCOMPtr<nsIInputStreamLengthCallback> callback;
463
{
464
MutexAutoLock lock(mMutex);
465
// We have been canceled in the meanwhile.
466
if (!mAsyncInputStreamLengthCallback) {
467
return NS_OK;
468
}
469
470
callback.swap(mAsyncInputStreamLengthCallback);
471
}
472
473
MOZ_ASSERT(callback);
474
return callback->OnInputStreamLengthReady(this, aLength);
475
}
476
477
bool nsMIMEInputStream::IsSeekableInputStream() const {
478
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
479
return !!seekable;
480
}
481
482
bool nsMIMEInputStream::IsAsyncInputStream() const {
483
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
484
return !!asyncStream;
485
}
486
487
bool nsMIMEInputStream::IsIPCSerializable() const {
488
// If SetData() or Deserialize() has not be called yet, mStream is null.
489
if (!mStream) {
490
return true;
491
}
492
493
nsCOMPtr<nsIIPCSerializableInputStream> serializable =
494
do_QueryInterface(mStream);
495
return !!serializable;
496
}
497
498
bool nsMIMEInputStream::IsInputStreamLength() const {
499
nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
500
return !!stream;
501
}
502
503
bool nsMIMEInputStream::IsAsyncInputStreamLength() const {
504
nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
505
return !!stream;
506
}
507
508
bool nsMIMEInputStream::IsCloneableInputStream() const {
509
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream);
510
return !!stream;
511
}
512
513
// nsICloneableInputStream interface
514
515
NS_IMETHODIMP
516
nsMIMEInputStream::GetCloneable(bool* aCloneable) {
517
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream);
518
if (!mStream) {
519
return NS_ERROR_FAILURE;
520
}
521
522
return stream->GetCloneable(aCloneable);
523
}
524
525
NS_IMETHODIMP
526
nsMIMEInputStream::Clone(nsIInputStream** aResult) {
527
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream);
528
if (!mStream) {
529
return NS_ERROR_FAILURE;
530
}
531
532
nsCOMPtr<nsIInputStream> clonedStream;
533
nsresult rv = stream->Clone(getter_AddRefs(clonedStream));
534
if (NS_WARN_IF(NS_FAILED(rv))) {
535
return rv;
536
}
537
538
nsCOMPtr<nsIMIMEInputStream> mimeStream = new nsMIMEInputStream();
539
540
rv = mimeStream->SetData(clonedStream);
541
if (NS_WARN_IF(NS_FAILED(rv))) {
542
return rv;
543
}
544
545
for (const HeaderEntry& entry : mHeaders) {
546
rv = mimeStream->AddHeader(entry.name().get(), entry.value().get());
547
MOZ_ASSERT(NS_SUCCEEDED(rv));
548
}
549
550
static_cast<nsMIMEInputStream*>(mimeStream.get())->mStartedReading =
551
mStartedReading;
552
553
mimeStream.forget(aResult);
554
return NS_OK;
555
}