Skip to content

Commit 696e8ca

Browse files
Olli PettayOlli Pettay
Olli Pettay
authored and
Olli Pettay
committed
Bug 1775501 - multipart boundary should be handled as mixed case, r=kershaw
Since w3c/FileAPI#43 is still open, it is unclear how body.type should work. The current wpts expect some behavior which isn't in the specifications. So, since the situation is very messy in the specifications, the patch is doing a spot fix for boundary handling. It is ugly, but shouldn't change other behavior. Differential Revision: https://phabricator.services.mozilla.com/D150018
1 parent 4931c60 commit 696e8ca

File tree

9 files changed

+95
-52
lines changed

9 files changed

+95
-52
lines changed

dom/base/BodyConsumer.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ NS_IMPL_ISUPPORTS(ConsumeBodyDoneObserver, nsIStreamLoaderObserver)
276276
nsIInputStream* aBodyStream, AbortSignalImpl* aSignalImpl,
277277
ConsumeType aType, const nsACString& aBodyBlobURISpec,
278278
const nsAString& aBodyLocalPath, const nsACString& aBodyMimeType,
279+
const nsACString& aMixedCaseMimeType,
279280
MutableBlobStorage::MutableBlobStorageType aBlobStorageType,
280281
ErrorResult& aRv) {
281282
MOZ_ASSERT(aBodyStream);
@@ -286,9 +287,10 @@ NS_IMPL_ISUPPORTS(ConsumeBodyDoneObserver, nsIStreamLoaderObserver)
286287
return nullptr;
287288
}
288289

289-
RefPtr<BodyConsumer> consumer = new BodyConsumer(
290-
aMainThreadEventTarget, aGlobal, aBodyStream, promise, aType,
291-
aBodyBlobURISpec, aBodyLocalPath, aBodyMimeType, aBlobStorageType);
290+
RefPtr<BodyConsumer> consumer =
291+
new BodyConsumer(aMainThreadEventTarget, aGlobal, aBodyStream, promise,
292+
aType, aBodyBlobURISpec, aBodyLocalPath, aBodyMimeType,
293+
aMixedCaseMimeType, aBlobStorageType);
292294

293295
RefPtr<ThreadSafeWorkerRef> workerRef;
294296

@@ -360,13 +362,14 @@ BodyConsumer::BodyConsumer(
360362
nsIEventTarget* aMainThreadEventTarget, nsIGlobalObject* aGlobalObject,
361363
nsIInputStream* aBodyStream, Promise* aPromise, ConsumeType aType,
362364
const nsACString& aBodyBlobURISpec, const nsAString& aBodyLocalPath,
363-
const nsACString& aBodyMimeType,
365+
const nsACString& aBodyMimeType, const nsACString& aMixedCaseMimeType,
364366
MutableBlobStorage::MutableBlobStorageType aBlobStorageType)
365367
: mTargetThread(NS_GetCurrentThread()),
366368
mMainThreadEventTarget(aMainThreadEventTarget),
367369
mBodyStream(aBodyStream),
368370
mBlobStorageType(aBlobStorageType),
369371
mBodyMimeType(aBodyMimeType),
372+
mMixedCaseMimeType(aMixedCaseMimeType),
370373
mBodyBlobURISpec(aBodyBlobURISpec),
371374
mBodyLocalPath(aBodyLocalPath),
372375
mGlobal(aGlobalObject),
@@ -718,8 +721,8 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
718721
data.Adopt(reinterpret_cast<char*>(aResult), aResultLength);
719722
aResult = nullptr;
720723

721-
RefPtr<dom::FormData> fd =
722-
BodyUtil::ConsumeFormData(mGlobal, mBodyMimeType, data, error);
724+
RefPtr<dom::FormData> fd = BodyUtil::ConsumeFormData(
725+
mGlobal, mBodyMimeType, mMixedCaseMimeType, data, error);
723726
if (!error.Failed()) {
724727
localPromise->MaybeResolve(fd);
725728
}

dom/base/BodyConsumer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class BodyConsumer final : public nsIObserver,
5353
* file. Used only by CONSUME_BLOB. Optional.
5454
* @param aBodyMimeType the mime-type for blob. Used only by CONSUME_BLOB.
5555
* Optional.
56+
* @param aMixedCaseMimeType is needed to get mixed case multipart
57+
* boundary value to FormDataParser.
5658
* @param aBlobStorageType Blobs can be saved in temporary file. This is the
5759
* type of blob storage to use. Used only by CONSUME_BLOB.
5860
* @param aRv An ErrorResult.
@@ -62,6 +64,7 @@ class BodyConsumer final : public nsIObserver,
6264
nsIInputStream* aBodyStream, AbortSignalImpl* aSignalImpl,
6365
ConsumeType aType, const nsACString& aBodyBlobURISpec,
6466
const nsAString& aBodyLocalPath, const nsACString& aBodyMimeType,
67+
const nsACString& aMixedCaseMimeType,
6568
MutableBlobStorage::MutableBlobStorageType aBlobStorageType,
6669
ErrorResult& aRv);
6770

@@ -96,6 +99,7 @@ class BodyConsumer final : public nsIObserver,
9699
Promise* aPromise, ConsumeType aType,
97100
const nsACString& aBodyBlobURISpec,
98101
const nsAString& aBodyLocalPath, const nsACString& aBodyMimeType,
102+
const nsACString& aMixedCaseMimeType,
99103
MutableBlobStorage::MutableBlobStorageType aBlobStorageType);
100104

101105
~BodyConsumer();
@@ -112,6 +116,7 @@ class BodyConsumer final : public nsIObserver,
112116

113117
MutableBlobStorage::MutableBlobStorageType mBlobStorageType;
114118
nsCString mBodyMimeType;
119+
nsCString mMixedCaseMimeType;
115120

116121
nsCString mBodyBlobURISpec;
117122
nsString mBodyLocalPath;

dom/base/BodyUtil.cpp

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include "nsString.h"
1111
#include "nsIGlobalObject.h"
1212
#include "mozilla/Encoding.h"
13-
13+
#include "mozilla/dom/MimeType.h"
1414
#include "nsCRT.h"
1515
#include "nsCharSeparatedTokenizer.h"
1616
#include "nsDOMString.h"
@@ -68,6 +68,7 @@ class MOZ_STACK_CLASS FormDataParser {
6868
private:
6969
RefPtr<FormData> mFormData;
7070
nsCString mMimeType;
71+
nsCString mMixedCaseMimeType;
7172
nsCString mData;
7273

7374
// Entry state, reset in START_PART.
@@ -251,9 +252,11 @@ class MOZ_STACK_CLASS FormDataParser {
251252
}
252253

253254
public:
254-
FormDataParser(const nsACString& aMimeType, const nsACString& aData,
255+
FormDataParser(const nsACString& aMimeType,
256+
const nsACString& aMixedCaseMimeType, const nsACString& aData,
255257
nsIGlobalObject* aParent)
256258
: mMimeType(aMimeType),
259+
mMixedCaseMimeType(aMixedCaseMimeType),
257260
mData(aData),
258261
mState(START_PART),
259262
mParentObject(aParent) {}
@@ -264,29 +267,13 @@ class MOZ_STACK_CLASS FormDataParser {
264267
}
265268

266269
// Determine boundary from mimetype.
267-
const char* boundaryId = nullptr;
268-
boundaryId = strstr(mMimeType.BeginWriting(), "boundary");
269-
if (!boundaryId) {
270-
return false;
271-
}
272-
273-
boundaryId = strchr(boundaryId, '=');
274-
if (!boundaryId) {
270+
UniquePtr<CMimeType> parsed = CMimeType::Parse(mMixedCaseMimeType);
271+
if (!parsed) {
275272
return false;
276273
}
277274

278-
// Skip over '='.
279-
boundaryId++;
280-
281-
char* attrib = (char*)strchr(boundaryId, ';');
282-
if (attrib) *attrib = '\0';
283-
284-
nsAutoCString boundaryString(boundaryId);
285-
if (attrib) *attrib = ';';
286-
287-
boundaryString.Trim(" \"");
288-
289-
if (boundaryString.Length() == 0) {
275+
nsAutoCString boundaryString;
276+
if (!parsed->GetParameterValue("boundary"_ns, boundaryString)) {
290277
return false;
291278
}
292279

@@ -396,10 +383,10 @@ already_AddRefed<Blob> BodyUtil::ConsumeBlob(nsIGlobalObject* aParent,
396383
}
397384

398385
// static
399-
already_AddRefed<FormData> BodyUtil::ConsumeFormData(nsIGlobalObject* aParent,
400-
const nsCString& aMimeType,
401-
const nsCString& aStr,
402-
ErrorResult& aRv) {
386+
already_AddRefed<FormData> BodyUtil::ConsumeFormData(
387+
nsIGlobalObject* aParent, const nsCString& aMimeType,
388+
const nsACString& aMixedCaseMimeType, const nsCString& aStr,
389+
ErrorResult& aRv) {
403390
constexpr auto formDataMimeType = "multipart/form-data"_ns;
404391

405392
// Allow semicolon separated boundary/encoding suffix like
@@ -412,7 +399,7 @@ already_AddRefed<FormData> BodyUtil::ConsumeFormData(nsIGlobalObject* aParent,
412399
}
413400

414401
if (isValidFormDataMimeType) {
415-
FormDataParser parser(aMimeType, aStr, aParent);
402+
FormDataParser parser(aMimeType, aMixedCaseMimeType, aStr, aParent);
416403
if (!parser.Parse()) {
417404
aRv.ThrowTypeError<MSG_BAD_FORMDATA>();
418405
return nullptr;

dom/base/BodyUtil.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ class BodyUtil final {
4646
* Creates a form data object from a UTF-8 encoded |aStr|. Returns |nullptr|
4747
* and sets |aRv| to MSG_BAD_FORMDATA if |aStr| contains invalid data.
4848
*/
49-
static already_AddRefed<FormData> ConsumeFormData(nsIGlobalObject* aParent,
50-
const nsCString& aMimeType,
51-
const nsCString& aStr,
52-
ErrorResult& aRv);
49+
static already_AddRefed<FormData> ConsumeFormData(
50+
nsIGlobalObject* aParent, const nsCString& aMimeType,
51+
const nsACString& aMixedCaseMimeType, const nsCString& aStr,
52+
ErrorResult& aRv);
5353

5454
/**
5555
* UTF-8 decodes |aInput| into |aText|. The caller may free |aInput|

dom/fetch/Fetch.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,8 @@ already_AddRefed<Promise> FetchBody<Derived>::ConsumeBody(
11871187
}
11881188

11891189
nsAutoCString mimeType;
1190-
DerivedClass()->GetMimeType(mimeType);
1190+
nsAutoCString mixedCaseMimeType;
1191+
DerivedClass()->GetMimeType(mimeType, mixedCaseMimeType);
11911192

11921193
// Null bodies are a special-case in the fetch spec. The Body mix-in can only
11931194
// be "disturbed" or "locked" if its associated "body" is non-null.
@@ -1201,9 +1202,10 @@ already_AddRefed<Promise> FetchBody<Derived>::ConsumeBody(
12011202
nsCOMPtr<nsIInputStream> bodyStream;
12021203
DerivedClass()->GetBody(getter_AddRefs(bodyStream));
12031204
if (!bodyStream) {
1204-
RefPtr<EmptyBody> emptyBody = EmptyBody::Create(
1205-
DerivedClass()->GetParentObject(),
1206-
DerivedClass()->GetPrincipalInfo().get(), signalImpl, mimeType, aRv);
1205+
RefPtr<EmptyBody> emptyBody =
1206+
EmptyBody::Create(DerivedClass()->GetParentObject(),
1207+
DerivedClass()->GetPrincipalInfo().get(), signalImpl,
1208+
mimeType, mixedCaseMimeType, aRv);
12071209
if (NS_WARN_IF(aRv.Failed())) {
12081210
return nullptr;
12091211
}
@@ -1236,7 +1238,8 @@ already_AddRefed<Promise> FetchBody<Derived>::ConsumeBody(
12361238

12371239
RefPtr<Promise> promise = BodyConsumer::Create(
12381240
global, mMainThreadEventTarget, bodyStream, signalImpl, aType,
1239-
BodyBlobURISpec(), BodyLocalPath(), mimeType, blobStorageType, aRv);
1241+
BodyBlobURISpec(), BodyLocalPath(), mimeType, mixedCaseMimeType,
1242+
blobStorageType, aRv);
12401243
if (NS_WARN_IF(aRv.Failed())) {
12411244
return nullptr;
12421245
}
@@ -1254,7 +1257,8 @@ template already_AddRefed<Promise> FetchBody<EmptyBody>::ConsumeBody(
12541257
JSContext* aCx, BodyConsumer::ConsumeType aType, ErrorResult& aRv);
12551258

12561259
template <class Derived>
1257-
void FetchBody<Derived>::GetMimeType(nsACString& aMimeType) {
1260+
void FetchBody<Derived>::GetMimeType(nsACString& aMimeType,
1261+
nsACString& aMixedCaseMimeType) {
12581262
// Extract mime type.
12591263
ErrorResult result;
12601264
nsCString contentTypeValues;
@@ -1268,12 +1272,15 @@ void FetchBody<Derived>::GetMimeType(nsACString& aMimeType) {
12681272
if (!contentTypeValues.IsVoid() && contentTypeValues.Find(",") == -1) {
12691273
// Convert from a bytestring to a UTF8 CString.
12701274
CopyLatin1toUTF8(contentTypeValues, aMimeType);
1275+
aMixedCaseMimeType = aMimeType;
12711276
ToLowerCase(aMimeType);
12721277
}
12731278
}
12741279

1275-
template void FetchBody<Request>::GetMimeType(nsACString& aMimeType);
1276-
template void FetchBody<Response>::GetMimeType(nsACString& aMimeType);
1280+
template void FetchBody<Request>::GetMimeType(nsACString& aMimeType,
1281+
nsACString& aMixedCaseMimeType);
1282+
template void FetchBody<Response>::GetMimeType(nsACString& aMimeType,
1283+
nsACString& aMixedCaseMimeType);
12771284

12781285
template <class Derived>
12791286
const nsACString& FetchBody<Derived>::BodyBlobURISpec() const {
@@ -1510,10 +1517,12 @@ EmptyBody::EmptyBody(nsIGlobalObject* aGlobal,
15101517
mozilla::ipc::PrincipalInfo* aPrincipalInfo,
15111518
AbortSignalImpl* aAbortSignalImpl,
15121519
const nsACString& aMimeType,
1520+
const nsACString& aMixedCaseMimeType,
15131521
already_AddRefed<nsIInputStream> aBodyStream)
15141522
: FetchBody<EmptyBody>(aGlobal),
15151523
mAbortSignalImpl(aAbortSignalImpl),
15161524
mMimeType(aMimeType),
1525+
mMixedCaseMimeType(aMixedCaseMimeType),
15171526
mBodyStream(std::move(aBodyStream)) {
15181527
if (aPrincipalInfo) {
15191528
mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*aPrincipalInfo);
@@ -1526,7 +1535,7 @@ EmptyBody::~EmptyBody() = default;
15261535
already_AddRefed<EmptyBody> EmptyBody::Create(
15271536
nsIGlobalObject* aGlobal, mozilla::ipc::PrincipalInfo* aPrincipalInfo,
15281537
AbortSignalImpl* aAbortSignalImpl, const nsACString& aMimeType,
1529-
ErrorResult& aRv) {
1538+
const nsACString& aMixedCaseMimeType, ErrorResult& aRv) {
15301539
nsCOMPtr<nsIInputStream> bodyStream;
15311540
aRv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), ""_ns);
15321541
if (NS_WARN_IF(aRv.Failed())) {
@@ -1535,7 +1544,7 @@ already_AddRefed<EmptyBody> EmptyBody::Create(
15351544

15361545
RefPtr<EmptyBody> emptyBody =
15371546
new EmptyBody(aGlobal, aPrincipalInfo, aAbortSignalImpl, aMimeType,
1538-
bodyStream.forget());
1547+
aMixedCaseMimeType, bodyStream.forget());
15391548
return emptyBody.forget();
15401549
}
15411550

dom/fetch/Fetch.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ class FetchBody : public BodyStreamHolder, public AbortFollower {
163163
}
164164

165165
already_AddRefed<ReadableStream> GetBody(JSContext* aCx, ErrorResult& aRv);
166-
void GetMimeType(nsACString& aMimeType);
166+
void GetMimeType(nsACString& aMimeType, nsACString& aMixedCaseMimeType);
167167

168168
const nsACString& BodyBlobURISpec() const;
169169

@@ -277,7 +277,7 @@ class EmptyBody final : public FetchBody<EmptyBody> {
277277
static already_AddRefed<EmptyBody> Create(
278278
nsIGlobalObject* aGlobal, mozilla::ipc::PrincipalInfo* aPrincipalInfo,
279279
AbortSignalImpl* aAbortSignalImpl, const nsACString& aMimeType,
280-
ErrorResult& aRv);
280+
const nsACString& aMixedCaseMimeType, ErrorResult& aRv);
281281

282282
nsIGlobalObject* GetParentObject() const { return mOwner; }
283283

@@ -288,7 +288,10 @@ class EmptyBody final : public FetchBody<EmptyBody> {
288288
return mPrincipalInfo;
289289
}
290290

291-
void GetMimeType(nsACString& aMimeType) { aMimeType = mMimeType; }
291+
void GetMimeType(nsACString& aMimeType, nsACString& aMixedCaseMimeType) {
292+
aMimeType = mMimeType;
293+
aMixedCaseMimeType = mMixedCaseMimeType;
294+
}
292295

293296
void GetBody(nsIInputStream** aStream, int64_t* aBodyLength = nullptr);
294297

@@ -304,13 +307,15 @@ class EmptyBody final : public FetchBody<EmptyBody> {
304307
EmptyBody(nsIGlobalObject* aGlobal,
305308
mozilla::ipc::PrincipalInfo* aPrincipalInfo,
306309
AbortSignalImpl* aAbortSignalImpl, const nsACString& aMimeType,
310+
const nsACString& aMixedCaseMimeType,
307311
already_AddRefed<nsIInputStream> aBodyStream);
308312

309313
~EmptyBody();
310314

311315
UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
312316
RefPtr<AbortSignalImpl> mAbortSignalImpl;
313317
nsCString mMimeType;
318+
nsCString mMixedCaseMimeType;
314319
nsCOMPtr<nsIInputStream> mBodyStream;
315320
};
316321
} // namespace dom

dom/fetch/FetchUtil.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,8 @@ bool FetchUtil::StreamResponseToJS(JSContext* aCx, JS::Handle<JSObject*> aObj,
692692
}
693693

694694
nsAutoCString mimeType;
695-
response->GetMimeType(mimeType);
695+
nsAutoCString mixedCaseMimeType; // unused
696+
response->GetMimeType(mimeType, mixedCaseMimeType);
696697

697698
if (!mimeType.EqualsASCII(requiredMimeType)) {
698699
JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,

dom/file/Blob.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ already_AddRefed<Promise> Blob::ConsumeBody(
294294

295295
return BodyConsumer::Create(mGlobal, mainThreadEventTarget, inputStream,
296296
nullptr, aConsumeType, VoidCString(),
297-
VoidString(), VoidCString(),
297+
VoidString(), VoidCString(), VoidCString(),
298298
MutableBlobStorage::eOnlyInMemory, aRv);
299299
}
300300

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// META: title=Ensure capital letters can be used in the boundary value.
2+
setup({ single_test: true });
3+
(async () => {
4+
const form_string =
5+
"--Boundary_with_capital_letters\r\n" +
6+
"Content-Type: application/json\r\n" +
7+
'Content-Disposition: form-data; name="does_this_work"\r\n' +
8+
"\r\n" +
9+
'YES\r\n' +
10+
"--Boundary_with_capital_letters--\r\n";
11+
12+
const r = new Response(new Blob([form_string]), {
13+
headers: [
14+
[
15+
"Content-Type",
16+
"multipart/form-data; boundary=Boundary_with_capital_letters",
17+
],
18+
],
19+
});
20+
21+
var s = "";
22+
try {
23+
const fd = await r.formData();
24+
for (const [key, value] of fd.entries()) {
25+
s += (`${key} = ${value}`);
26+
}
27+
} catch (ex) {
28+
s = ex;
29+
}
30+
31+
assert_equals(s, "does_this_work = YES");
32+
done();
33+
})();

0 commit comments

Comments
 (0)