|
1 /* -*- Mode: C++; tab-width: 2; 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 #include "FileIOObject.h" |
|
7 #include "mozilla/EventDispatcher.h" |
|
8 #include "nsDOMFile.h" |
|
9 #include "nsError.h" |
|
10 #include "nsIDOMEvent.h" |
|
11 #include "nsIDOMProgressEvent.h" |
|
12 #include "nsComponentManagerUtils.h" |
|
13 |
|
14 #define ERROR_STR "error" |
|
15 #define ABORT_STR "abort" |
|
16 #define PROGRESS_STR "progress" |
|
17 |
|
18 namespace mozilla { |
|
19 namespace dom { |
|
20 |
|
21 const uint64_t kUnknownSize = uint64_t(-1); |
|
22 |
|
23 NS_IMPL_ADDREF_INHERITED(FileIOObject, DOMEventTargetHelper) |
|
24 NS_IMPL_RELEASE_INHERITED(FileIOObject, DOMEventTargetHelper) |
|
25 |
|
26 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileIOObject) |
|
27 NS_INTERFACE_MAP_ENTRY(nsITimerCallback) |
|
28 NS_INTERFACE_MAP_ENTRY(nsIStreamListener) |
|
29 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) |
|
30 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) |
|
31 |
|
32 NS_IMPL_CYCLE_COLLECTION_CLASS(FileIOObject) |
|
33 |
|
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileIOObject, |
|
35 DOMEventTargetHelper) |
|
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier) |
|
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) |
|
38 // Can't traverse mChannel because it's a multithreaded object. |
|
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
40 |
|
41 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileIOObject, |
|
42 DOMEventTargetHelper) |
|
43 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier) |
|
44 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) |
|
45 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel) |
|
46 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
47 |
|
48 NS_IMPL_EVENT_HANDLER(FileIOObject, abort) |
|
49 NS_IMPL_EVENT_HANDLER(FileIOObject, error) |
|
50 NS_IMPL_EVENT_HANDLER(FileIOObject, progress) |
|
51 |
|
52 FileIOObject::FileIOObject() |
|
53 : mProgressEventWasDelayed(false), |
|
54 mTimerIsActive(false), |
|
55 mReadyState(0), |
|
56 mTotal(0), mTransferred(0) |
|
57 {} |
|
58 |
|
59 void |
|
60 FileIOObject::StartProgressEventTimer() |
|
61 { |
|
62 if (!mProgressNotifier) { |
|
63 mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID); |
|
64 } |
|
65 if (mProgressNotifier) { |
|
66 mProgressEventWasDelayed = false; |
|
67 mTimerIsActive = true; |
|
68 mProgressNotifier->Cancel(); |
|
69 mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL, |
|
70 nsITimer::TYPE_ONE_SHOT); |
|
71 } |
|
72 } |
|
73 |
|
74 void |
|
75 FileIOObject::ClearProgressEventTimer() |
|
76 { |
|
77 mProgressEventWasDelayed = false; |
|
78 mTimerIsActive = false; |
|
79 if (mProgressNotifier) { |
|
80 mProgressNotifier->Cancel(); |
|
81 } |
|
82 } |
|
83 |
|
84 void |
|
85 FileIOObject::DispatchError(nsresult rv, nsAString& finalEvent) |
|
86 { |
|
87 // Set the status attribute, and dispatch the error event |
|
88 switch (rv) { |
|
89 case NS_ERROR_FILE_NOT_FOUND: |
|
90 mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotFoundError")); |
|
91 break; |
|
92 case NS_ERROR_FILE_ACCESS_DENIED: |
|
93 mError = new DOMError(GetOwner(), NS_LITERAL_STRING("SecurityError")); |
|
94 break; |
|
95 default: |
|
96 mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotReadableError")); |
|
97 break; |
|
98 } |
|
99 |
|
100 // Dispatch error event to signify load failure |
|
101 DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR)); |
|
102 DispatchProgressEvent(finalEvent); |
|
103 } |
|
104 |
|
105 nsresult |
|
106 FileIOObject::DispatchProgressEvent(const nsAString& aType) |
|
107 { |
|
108 nsCOMPtr<nsIDOMEvent> event; |
|
109 nsresult rv = NS_NewDOMProgressEvent(getter_AddRefs(event), this, |
|
110 nullptr, nullptr); |
|
111 NS_ENSURE_SUCCESS(rv, rv); |
|
112 |
|
113 event->SetTrusted(true); |
|
114 nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event); |
|
115 NS_ENSURE_TRUE(progress, NS_ERROR_UNEXPECTED); |
|
116 |
|
117 bool known; |
|
118 uint64_t size; |
|
119 if (mTotal != kUnknownSize) { |
|
120 known = true; |
|
121 size = mTotal; |
|
122 } else { |
|
123 known = false; |
|
124 size = 0; |
|
125 } |
|
126 rv = progress->InitProgressEvent(aType, false, false, known, |
|
127 mTransferred, size); |
|
128 NS_ENSURE_SUCCESS(rv, rv); |
|
129 |
|
130 return DispatchDOMEvent(nullptr, event, nullptr, nullptr); |
|
131 } |
|
132 |
|
133 // nsITimerCallback |
|
134 NS_IMETHODIMP |
|
135 FileIOObject::Notify(nsITimer* aTimer) |
|
136 { |
|
137 nsresult rv; |
|
138 mTimerIsActive = false; |
|
139 |
|
140 if (mProgressEventWasDelayed) { |
|
141 rv = DispatchProgressEvent(NS_LITERAL_STRING("progress")); |
|
142 NS_ENSURE_SUCCESS(rv, rv); |
|
143 |
|
144 StartProgressEventTimer(); |
|
145 } |
|
146 |
|
147 return NS_OK; |
|
148 } |
|
149 |
|
150 // nsIStreamListener |
|
151 NS_IMETHODIMP |
|
152 FileIOObject::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) |
|
153 { |
|
154 return DoOnStartRequest(aRequest, aContext); |
|
155 } |
|
156 |
|
157 NS_IMETHODIMP |
|
158 FileIOObject::DoOnStartRequest(nsIRequest *request, nsISupports *ctxt) |
|
159 { |
|
160 return NS_OK; |
|
161 } |
|
162 |
|
163 NS_IMETHODIMP |
|
164 FileIOObject::OnDataAvailable(nsIRequest *aRequest, |
|
165 nsISupports *aContext, |
|
166 nsIInputStream *aInputStream, |
|
167 uint64_t aOffset, |
|
168 uint32_t aCount) |
|
169 { |
|
170 nsresult rv; |
|
171 rv = DoOnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount); |
|
172 NS_ENSURE_SUCCESS(rv, rv); |
|
173 |
|
174 mTransferred += aCount; |
|
175 |
|
176 //Notify the timer is the appropriate timeframe has passed |
|
177 if (mTimerIsActive) { |
|
178 mProgressEventWasDelayed = true; |
|
179 } else { |
|
180 rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR)); |
|
181 NS_ENSURE_SUCCESS(rv, rv); |
|
182 |
|
183 StartProgressEventTimer(); |
|
184 } |
|
185 |
|
186 return NS_OK; |
|
187 } |
|
188 |
|
189 NS_IMETHODIMP |
|
190 FileIOObject::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, |
|
191 nsresult aStatus) |
|
192 { |
|
193 // If we're here as a result of a call from Abort(), |
|
194 // simply ignore the request. |
|
195 if (aRequest != mChannel) |
|
196 return NS_OK; |
|
197 |
|
198 // Cancel the progress event timer |
|
199 ClearProgressEventTimer(); |
|
200 |
|
201 // FileIOObject must be in DONE stage after an operation |
|
202 mReadyState = 2; |
|
203 |
|
204 nsString successEvent, termEvent; |
|
205 nsresult rv = DoOnStopRequest(aRequest, aContext, aStatus, |
|
206 successEvent, termEvent); |
|
207 NS_ENSURE_SUCCESS(rv, rv); |
|
208 |
|
209 // Set the status field as appropriate |
|
210 if (NS_FAILED(aStatus)) { |
|
211 DispatchError(aStatus, termEvent); |
|
212 return NS_OK; |
|
213 } |
|
214 |
|
215 // Dispatch event to signify end of a successful operation |
|
216 DispatchProgressEvent(successEvent); |
|
217 DispatchProgressEvent(termEvent); |
|
218 |
|
219 return NS_OK; |
|
220 } |
|
221 |
|
222 void |
|
223 FileIOObject::Abort(ErrorResult& aRv) |
|
224 { |
|
225 if (mReadyState != 1) { |
|
226 // XXX The spec doesn't say this |
|
227 aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR); |
|
228 return; |
|
229 } |
|
230 |
|
231 ClearProgressEventTimer(); |
|
232 |
|
233 mReadyState = 2; // There are DONE constants on multiple interfaces, |
|
234 // but they all have value 2. |
|
235 // XXX The spec doesn't say this |
|
236 mError = new DOMError(GetOwner(), NS_LITERAL_STRING("AbortError")); |
|
237 |
|
238 nsString finalEvent; |
|
239 DoAbort(finalEvent); |
|
240 |
|
241 // Dispatch the events |
|
242 DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR)); |
|
243 DispatchProgressEvent(finalEvent); |
|
244 } |
|
245 |
|
246 } // namespace dom |
|
247 } // namespace mozilla |