/src/mozilla-central/layout/style/StreamLoader.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "mozilla/css/StreamLoader.h" |
8 | | |
9 | | #include "mozilla/IntegerTypeTraits.h" |
10 | | #include "mozilla/Encoding.h" |
11 | | #include "nsIChannel.h" |
12 | | #include "nsIInputStream.h" |
13 | | |
14 | | using namespace mozilla; |
15 | | |
16 | | namespace mozilla { |
17 | | namespace css { |
18 | | |
19 | | StreamLoader::StreamLoader(mozilla::css::SheetLoadData* aSheetLoadData) |
20 | | : mSheetLoadData(aSheetLoadData) |
21 | | , mStatus(NS_OK) |
22 | 0 | { |
23 | 0 | } |
24 | | |
25 | | StreamLoader::~StreamLoader() |
26 | 0 | { |
27 | 0 | } |
28 | | |
29 | | NS_IMPL_ISUPPORTS(StreamLoader, nsIStreamListener) |
30 | | |
31 | | /* nsIRequestObserver implementation */ |
32 | | NS_IMETHODIMP |
33 | | StreamLoader::OnStartRequest(nsIRequest* aRequest, nsISupports*) |
34 | 0 | { |
35 | 0 | // It's kinda bad to let Web content send a number that results |
36 | 0 | // in a potentially large allocation directly, but efficiency of |
37 | 0 | // compression bombs is so great that it doesn't make much sense |
38 | 0 | // to require a site to send one before going ahead and allocating. |
39 | 0 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); |
40 | 0 | if (channel) { |
41 | 0 | int64_t length; |
42 | 0 | nsresult rv = channel->GetContentLength(&length); |
43 | 0 | if (NS_SUCCEEDED(rv) && length > 0) { |
44 | 0 | if (length > MaxValue<nsACString::size_type>::value) { |
45 | 0 | return (mStatus = NS_ERROR_OUT_OF_MEMORY); |
46 | 0 | } |
47 | 0 | if (!mBytes.SetCapacity(length, mozilla::fallible_t())) { |
48 | 0 | return (mStatus = NS_ERROR_OUT_OF_MEMORY); |
49 | 0 | } |
50 | 0 | } |
51 | 0 | } |
52 | 0 | return NS_OK; |
53 | 0 | } |
54 | | |
55 | | NS_IMETHODIMP |
56 | | StreamLoader::OnStopRequest(nsIRequest* aRequest, |
57 | | nsISupports* aContext, |
58 | | nsresult aStatus) |
59 | 0 | { |
60 | 0 | // Decoded data |
61 | 0 | nsCString utf8String; |
62 | 0 | { |
63 | 0 | // Hold the nsStringBuffer for the bytes from the stack to ensure release |
64 | 0 | // no matter which return branch is taken. |
65 | 0 | nsCString bytes(mBytes); |
66 | 0 | mBytes.Truncate(); |
67 | 0 |
|
68 | 0 | nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); |
69 | 0 |
|
70 | 0 | if (NS_FAILED(mStatus)) { |
71 | 0 | mSheetLoadData->VerifySheetReadyToParse(mStatus, EmptyCString(), EmptyCString(), channel); |
72 | 0 | return mStatus; |
73 | 0 | } |
74 | 0 | |
75 | 0 | nsresult rv = |
76 | 0 | mSheetLoadData->VerifySheetReadyToParse(aStatus, mBOMBytes, bytes, channel); |
77 | 0 | if (rv != NS_OK_PARSE_SHEET) { |
78 | 0 | return rv; |
79 | 0 | } |
80 | 0 | rv = NS_OK; |
81 | 0 |
|
82 | 0 | // BOM detection generally happens during the write callback, but that won't |
83 | 0 | // have happened if fewer than three bytes were received. |
84 | 0 | if (mEncodingFromBOM.isNothing()) { |
85 | 0 | HandleBOM(); |
86 | 0 | MOZ_ASSERT(mEncodingFromBOM.isSome()); |
87 | 0 | } |
88 | 0 |
|
89 | 0 | // The BOM handling has happened, but we still may not have an encoding if |
90 | 0 | // there was no BOM. Ensure we have one. |
91 | 0 | const Encoding* encoding = mEncodingFromBOM.value(); |
92 | 0 | if (!encoding) { |
93 | 0 | // No BOM |
94 | 0 | encoding = mSheetLoadData->DetermineNonBOMEncoding(bytes, channel); |
95 | 0 | } |
96 | 0 | mSheetLoadData->mEncoding = encoding; |
97 | 0 |
|
98 | 0 | size_t validated = 0; |
99 | 0 | if (encoding == UTF_8_ENCODING) { |
100 | 0 | validated = Encoding::UTF8ValidUpTo(bytes); |
101 | 0 | } |
102 | 0 |
|
103 | 0 | if (validated == bytes.Length()) { |
104 | 0 | // Either this is UTF-8 and all valid, or it's not UTF-8 but is an |
105 | 0 | // empty string. This assumes that an empty string in any encoding |
106 | 0 | // decodes to empty string, which seems like a plausible assumption. |
107 | 0 | utf8String.Assign(bytes); |
108 | 0 | } else { |
109 | 0 | rv = encoding->DecodeWithoutBOMHandling(bytes, utf8String, validated); |
110 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
111 | 0 | } |
112 | 0 | } // run destructor for `bytes` |
113 | 0 |
|
114 | 0 | // For reasons I don't understand, factoring the below lines into |
115 | 0 | // a method on SheetLoadData resulted in a linker error. Hence, |
116 | 0 | // accessing fields of mSheetLoadData from here. |
117 | 0 | mSheetLoadData->mLoader->ParseSheet( |
118 | 0 | utf8String, |
119 | 0 | mSheetLoadData, |
120 | 0 | Loader::AllowAsyncParse::Yes); |
121 | 0 | return NS_OK; |
122 | 0 | } |
123 | | |
124 | | /* nsIStreamListener implementation */ |
125 | | NS_IMETHODIMP |
126 | | StreamLoader::OnDataAvailable(nsIRequest*, |
127 | | nsISupports*, |
128 | | nsIInputStream* aInputStream, |
129 | | uint64_t, |
130 | | uint32_t aCount) |
131 | 0 | { |
132 | 0 | if (NS_FAILED(mStatus)) { |
133 | 0 | return mStatus; |
134 | 0 | } |
135 | 0 | uint32_t dummy; |
136 | 0 | return aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy); |
137 | 0 | } |
138 | | |
139 | | void |
140 | | StreamLoader::HandleBOM() |
141 | 0 | { |
142 | 0 | MOZ_ASSERT(mEncodingFromBOM.isNothing()); |
143 | 0 | MOZ_ASSERT(mBytes.IsEmpty()); |
144 | 0 |
|
145 | 0 | const Encoding* encoding; |
146 | 0 | size_t bomLength; |
147 | 0 | Tie(encoding, bomLength) = Encoding::ForBOM(mBOMBytes); |
148 | 0 | mEncodingFromBOM.emplace(encoding); // Null means no BOM. |
149 | 0 |
|
150 | 0 | // BOMs are three bytes at most, but may be fewer. Copy over anything |
151 | 0 | // that wasn't part of the BOM to mBytes. Note that we need to track |
152 | 0 | // any BOM bytes as well for SRI handling. |
153 | 0 | mBytes.Append(Substring(mBOMBytes, bomLength)); |
154 | 0 | mBOMBytes.Truncate(bomLength); |
155 | 0 | } |
156 | | |
157 | | nsresult |
158 | | StreamLoader::WriteSegmentFun(nsIInputStream*, |
159 | | void* aClosure, |
160 | | const char* aSegment, |
161 | | uint32_t, |
162 | | uint32_t aCount, |
163 | | uint32_t* aWriteCount) |
164 | 0 | { |
165 | 0 | *aWriteCount = 0; |
166 | 0 | StreamLoader* self = static_cast<StreamLoader*>(aClosure); |
167 | 0 | if (NS_FAILED(self->mStatus)) { |
168 | 0 | return self->mStatus; |
169 | 0 | } |
170 | 0 | |
171 | 0 | // If we haven't done BOM detection yet, divert bytes into the special buffer. |
172 | 0 | if (self->mEncodingFromBOM.isNothing()) { |
173 | 0 | size_t bytesToCopy = std::min(3 - self->mBOMBytes.Length(), aCount); |
174 | 0 | self->mBOMBytes.Append(aSegment, bytesToCopy); |
175 | 0 | aSegment += bytesToCopy; |
176 | 0 | *aWriteCount += bytesToCopy; |
177 | 0 | aCount -= bytesToCopy; |
178 | 0 |
|
179 | 0 | if (self->mBOMBytes.Length() == 3) { |
180 | 0 | self->HandleBOM(); |
181 | 0 | } else { |
182 | 0 | return NS_OK; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | |
186 | 0 | if (!self->mBytes.Append(aSegment, aCount, mozilla::fallible_t())) { |
187 | 0 | self->mBytes.Truncate(); |
188 | 0 | return (self->mStatus = NS_ERROR_OUT_OF_MEMORY); |
189 | 0 | } |
190 | 0 | |
191 | 0 | *aWriteCount += aCount; |
192 | 0 | return NS_OK; |
193 | 0 | } |
194 | | |
195 | | } // namespace css |
196 | | } // namespace mozilla |