| |
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
| |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
| |
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
4 |
| |
5 #include "nsIOService.h" |
| |
6 #include "nsSyncStreamListener.h" |
| |
7 #include "nsIPipe.h" |
| |
8 #include "nsThreadUtils.h" |
| |
9 #include <algorithm> |
| |
10 |
| |
11 nsresult |
| |
12 nsSyncStreamListener::Init() |
| |
13 { |
| |
14 return NS_NewPipe(getter_AddRefs(mPipeIn), |
| |
15 getter_AddRefs(mPipeOut), |
| |
16 nsIOService::gDefaultSegmentSize, |
| |
17 UINT32_MAX, // no size limit |
| |
18 false, |
| |
19 false); |
| |
20 } |
| |
21 |
| |
22 nsresult |
| |
23 nsSyncStreamListener::WaitForData() |
| |
24 { |
| |
25 mKeepWaiting = true; |
| |
26 |
| |
27 while (mKeepWaiting) |
| |
28 NS_ENSURE_STATE(NS_ProcessNextEvent(NS_GetCurrentThread())); |
| |
29 |
| |
30 return NS_OK; |
| |
31 } |
| |
32 |
| |
33 //----------------------------------------------------------------------------- |
| |
34 // nsSyncStreamListener::nsISupports |
| |
35 //----------------------------------------------------------------------------- |
| |
36 |
| |
37 NS_IMPL_ISUPPORTS(nsSyncStreamListener, |
| |
38 nsIStreamListener, |
| |
39 nsIRequestObserver, |
| |
40 nsIInputStream, |
| |
41 nsISyncStreamListener) |
| |
42 |
| |
43 //----------------------------------------------------------------------------- |
| |
44 // nsSyncStreamListener::nsISyncStreamListener |
| |
45 //----------------------------------------------------------------------------- |
| |
46 |
| |
47 NS_IMETHODIMP |
| |
48 nsSyncStreamListener::GetInputStream(nsIInputStream **result) |
| |
49 { |
| |
50 NS_ADDREF(*result = this); |
| |
51 return NS_OK; |
| |
52 } |
| |
53 |
| |
54 //----------------------------------------------------------------------------- |
| |
55 // nsSyncStreamListener::nsIStreamListener |
| |
56 //----------------------------------------------------------------------------- |
| |
57 |
| |
58 NS_IMETHODIMP |
| |
59 nsSyncStreamListener::OnStartRequest(nsIRequest *request, |
| |
60 nsISupports *context) |
| |
61 { |
| |
62 return NS_OK; |
| |
63 } |
| |
64 |
| |
65 NS_IMETHODIMP |
| |
66 nsSyncStreamListener::OnDataAvailable(nsIRequest *request, |
| |
67 nsISupports *context, |
| |
68 nsIInputStream *stream, |
| |
69 uint64_t offset, |
| |
70 uint32_t count) |
| |
71 { |
| |
72 uint32_t bytesWritten; |
| |
73 |
| |
74 nsresult rv = mPipeOut->WriteFrom(stream, count, &bytesWritten); |
| |
75 |
| |
76 // if we get an error, then return failure. this will cause the |
| |
77 // channel to be canceled, and as a result our OnStopRequest method |
| |
78 // will be called immediately. because of this we do not need to |
| |
79 // set mStatus or mKeepWaiting here. |
| |
80 if (NS_FAILED(rv)) |
| |
81 return rv; |
| |
82 |
| |
83 // we expect that all data will be written to the pipe because |
| |
84 // the pipe was created to have "infinite" room. |
| |
85 NS_ASSERTION(bytesWritten == count, "did not write all data"); |
| |
86 |
| |
87 mKeepWaiting = false; // unblock Read |
| |
88 return NS_OK; |
| |
89 } |
| |
90 |
| |
91 NS_IMETHODIMP |
| |
92 nsSyncStreamListener::OnStopRequest(nsIRequest *request, |
| |
93 nsISupports *context, |
| |
94 nsresult status) |
| |
95 { |
| |
96 mStatus = status; |
| |
97 mKeepWaiting = false; // unblock Read |
| |
98 mDone = true; |
| |
99 return NS_OK; |
| |
100 } |
| |
101 |
| |
102 //----------------------------------------------------------------------------- |
| |
103 // nsSyncStreamListener::nsIInputStream |
| |
104 //----------------------------------------------------------------------------- |
| |
105 |
| |
106 NS_IMETHODIMP |
| |
107 nsSyncStreamListener::Close() |
| |
108 { |
| |
109 mStatus = NS_BASE_STREAM_CLOSED; |
| |
110 mDone = true; |
| |
111 |
| |
112 // It'd be nice if we could explicitly cancel the request at this point, |
| |
113 // but we don't have a reference to it, so the best we can do is close the |
| |
114 // pipe so that the next OnDataAvailable event will fail. |
| |
115 if (mPipeIn) { |
| |
116 mPipeIn->Close(); |
| |
117 mPipeIn = nullptr; |
| |
118 } |
| |
119 return NS_OK; |
| |
120 } |
| |
121 |
| |
122 NS_IMETHODIMP |
| |
123 nsSyncStreamListener::Available(uint64_t *result) |
| |
124 { |
| |
125 if (NS_FAILED(mStatus)) |
| |
126 return mStatus; |
| |
127 |
| |
128 mStatus = mPipeIn->Available(result); |
| |
129 if (NS_SUCCEEDED(mStatus) && (*result == 0) && !mDone) { |
| |
130 mStatus = WaitForData(); |
| |
131 if (NS_SUCCEEDED(mStatus)) |
| |
132 mStatus = mPipeIn->Available(result); |
| |
133 } |
| |
134 return mStatus; |
| |
135 } |
| |
136 |
| |
137 NS_IMETHODIMP |
| |
138 nsSyncStreamListener::Read(char *buf, |
| |
139 uint32_t bufLen, |
| |
140 uint32_t *result) |
| |
141 { |
| |
142 if (mStatus == NS_BASE_STREAM_CLOSED) { |
| |
143 *result = 0; |
| |
144 return NS_OK; |
| |
145 } |
| |
146 |
| |
147 uint64_t avail64; |
| |
148 if (NS_FAILED(Available(&avail64))) |
| |
149 return mStatus; |
| |
150 |
| |
151 uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)bufLen); |
| |
152 mStatus = mPipeIn->Read(buf, avail, result); |
| |
153 return mStatus; |
| |
154 } |
| |
155 |
| |
156 NS_IMETHODIMP |
| |
157 nsSyncStreamListener::ReadSegments(nsWriteSegmentFun writer, |
| |
158 void *closure, |
| |
159 uint32_t count, |
| |
160 uint32_t *result) |
| |
161 { |
| |
162 if (mStatus == NS_BASE_STREAM_CLOSED) { |
| |
163 *result = 0; |
| |
164 return NS_OK; |
| |
165 } |
| |
166 |
| |
167 uint64_t avail64; |
| |
168 if (NS_FAILED(Available(&avail64))) |
| |
169 return mStatus; |
| |
170 |
| |
171 uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)count); |
| |
172 mStatus = mPipeIn->ReadSegments(writer, closure, avail, result); |
| |
173 return mStatus; |
| |
174 } |
| |
175 |
| |
176 NS_IMETHODIMP |
| |
177 nsSyncStreamListener::IsNonBlocking(bool *result) |
| |
178 { |
| |
179 *result = false; |
| |
180 return NS_OK; |
| |
181 } |