1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozila.org code.
16 : *
17 : * The Initial Developer of the Original Code is the Mozilla Foundation
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Kyle Huey <me@kylehuey.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "FileIOObject.h"
39 : #include "nsDOMFile.h"
40 : #include "nsDOMError.h"
41 : #include "nsIPrivateDOMEvent.h"
42 : #include "nsIDOMEvent.h"
43 : #include "nsIDOMProgressEvent.h"
44 : #include "nsComponentManagerUtils.h"
45 : #include "nsEventDispatcher.h"
46 : #include "xpcprivate.h"
47 :
48 : #define ERROR_STR "error"
49 : #define ABORT_STR "abort"
50 : #define PROGRESS_STR "progress"
51 :
52 : namespace mozilla {
53 : namespace dom {
54 :
55 : const PRUint64 kUnknownSize = PRUint64(-1);
56 :
57 142 : NS_IMPL_ADDREF_INHERITED(FileIOObject, nsDOMEventTargetHelper)
58 142 : NS_IMPL_RELEASE_INHERITED(FileIOObject, nsDOMEventTargetHelper)
59 :
60 128 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileIOObject)
61 128 : NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
62 128 : NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
63 126 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
64 126 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
65 :
66 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(FileIOObject)
67 :
68 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileIOObject,
69 : nsDOMEventTargetHelper)
70 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mProgressNotifier)
71 4 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort)
72 4 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
73 4 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(progress)
74 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
75 :
76 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileIOObject,
77 : nsDOMEventTargetHelper)
78 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mProgressNotifier)
79 2 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort)
80 2 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
81 2 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(progress)
82 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
83 :
84 2 : FileIOObject::FileIOObject()
85 : : mProgressEventWasDelayed(false),
86 : mTimerIsActive(false),
87 : mReadyState(0),
88 2 : mTotal(0), mTransferred(0)
89 2 : {}
90 :
91 : void
92 2 : FileIOObject::StartProgressEventTimer()
93 : {
94 2 : if (!mProgressNotifier) {
95 2 : mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
96 : }
97 2 : if (mProgressNotifier) {
98 2 : mProgressEventWasDelayed = false;
99 2 : mTimerIsActive = true;
100 2 : mProgressNotifier->Cancel();
101 2 : mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
102 2 : nsITimer::TYPE_ONE_SHOT);
103 : }
104 2 : }
105 :
106 : void
107 2 : FileIOObject::ClearProgressEventTimer()
108 : {
109 2 : mProgressEventWasDelayed = false;
110 2 : mTimerIsActive = false;
111 2 : if (mProgressNotifier) {
112 2 : mProgressNotifier->Cancel();
113 : }
114 2 : }
115 :
116 : void
117 0 : FileIOObject::DispatchError(nsresult rv, nsAString& finalEvent)
118 : {
119 : // Set the status attribute, and dispatch the error event
120 0 : switch (rv) {
121 : case NS_ERROR_FILE_NOT_FOUND:
122 0 : mError = DOMError::CreateWithName(NS_LITERAL_STRING("NotFoundError"));
123 0 : break;
124 : case NS_ERROR_FILE_ACCESS_DENIED:
125 0 : mError = DOMError::CreateWithName(NS_LITERAL_STRING("SecurityError"));
126 0 : break;
127 : default:
128 0 : mError = DOMError::CreateWithName(NS_LITERAL_STRING("NotReadableError"));
129 0 : break;
130 : }
131 :
132 : // Dispatch error event to signify load failure
133 0 : DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR));
134 0 : DispatchProgressEvent(finalEvent);
135 0 : }
136 :
137 : nsresult
138 8 : FileIOObject::DispatchProgressEvent(const nsAString& aType)
139 : {
140 16 : nsCOMPtr<nsIDOMEvent> event;
141 : nsresult rv = nsEventDispatcher::CreateEvent(nsnull, nsnull,
142 8 : NS_LITERAL_STRING("ProgressEvent"),
143 16 : getter_AddRefs(event));
144 8 : NS_ENSURE_SUCCESS(rv, rv);
145 :
146 16 : nsCOMPtr<nsIPrivateDOMEvent> privevent(do_QueryInterface(event));
147 8 : NS_ENSURE_TRUE(privevent, NS_ERROR_UNEXPECTED);
148 :
149 8 : privevent->SetTrusted(true);
150 16 : nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
151 8 : NS_ENSURE_TRUE(progress, NS_ERROR_UNEXPECTED);
152 :
153 : bool known;
154 : PRUint64 size;
155 8 : if (mTotal != kUnknownSize) {
156 8 : known = true;
157 8 : size = mTotal;
158 : } else {
159 0 : known = false;
160 0 : size = 0;
161 : }
162 8 : rv = progress->InitProgressEvent(aType, false, false, known,
163 8 : mTransferred, size);
164 8 : NS_ENSURE_SUCCESS(rv, rv);
165 :
166 8 : return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
167 : }
168 :
169 : // nsITimerCallback
170 : NS_IMETHODIMP
171 0 : FileIOObject::Notify(nsITimer* aTimer)
172 : {
173 : nsresult rv;
174 0 : mTimerIsActive = false;
175 :
176 0 : if (mProgressEventWasDelayed) {
177 0 : rv = DispatchProgressEvent(NS_LITERAL_STRING("progress"));
178 0 : NS_ENSURE_SUCCESS(rv, rv);
179 :
180 0 : StartProgressEventTimer();
181 : }
182 :
183 0 : return NS_OK;
184 : }
185 :
186 : // nsIStreamListener
187 : NS_IMETHODIMP
188 2 : FileIOObject::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
189 : {
190 2 : return DoOnStartRequest(aRequest, aContext);
191 : }
192 :
193 : NS_IMETHODIMP
194 2 : FileIOObject::DoOnStartRequest(nsIRequest *request, nsISupports *ctxt)
195 : {
196 2 : return NS_OK;
197 : }
198 :
199 : NS_IMETHODIMP
200 2 : FileIOObject::OnDataAvailable(nsIRequest *aRequest,
201 : nsISupports *aContext,
202 : nsIInputStream *aInputStream,
203 : PRUint32 aOffset,
204 : PRUint32 aCount)
205 : {
206 : nsresult rv;
207 2 : rv = DoOnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
208 2 : NS_ENSURE_SUCCESS(rv, rv);
209 :
210 2 : mTransferred += aCount;
211 :
212 : //Notify the timer is the appropriate timeframe has passed
213 2 : if (mTimerIsActive) {
214 0 : mProgressEventWasDelayed = true;
215 : } else {
216 2 : rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR));
217 2 : NS_ENSURE_SUCCESS(rv, rv);
218 :
219 2 : StartProgressEventTimer();
220 : }
221 :
222 2 : return NS_OK;
223 : }
224 :
225 : NS_IMETHODIMP
226 2 : FileIOObject::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
227 : nsresult aStatus)
228 : {
229 : // If we're here as a result of a call from Abort(),
230 : // simply ignore the request.
231 2 : if (aRequest != mChannel)
232 0 : return NS_OK;
233 :
234 : // Cancel the progress event timer
235 2 : ClearProgressEventTimer();
236 :
237 : // FileIOObject must be in DONE stage after an operation
238 2 : mReadyState = 2;
239 :
240 4 : nsString successEvent, termEvent;
241 : nsresult rv = DoOnStopRequest(aRequest, aContext, aStatus,
242 2 : successEvent, termEvent);
243 2 : NS_ENSURE_SUCCESS(rv, rv);
244 :
245 : // Set the status field as appropriate
246 2 : if (NS_FAILED(aStatus)) {
247 0 : DispatchError(aStatus, termEvent);
248 0 : return NS_OK;
249 : }
250 :
251 : // Dispatch event to signify end of a successful operation
252 2 : DispatchProgressEvent(successEvent);
253 2 : DispatchProgressEvent(termEvent);
254 :
255 2 : return NS_OK;
256 : }
257 :
258 : NS_IMETHODIMP
259 2 : FileIOObject::Abort()
260 : {
261 2 : if (mReadyState != 1) {
262 : // XXX The spec doesn't say this
263 2 : return NS_ERROR_DOM_FILE_ABORT_ERR;
264 : }
265 :
266 0 : ClearProgressEventTimer();
267 :
268 0 : mReadyState = 2; // There are DONE constants on multiple interfaces,
269 : // but they all have value 2.
270 : // XXX The spec doesn't say this
271 0 : mError = DOMError::CreateWithName(NS_LITERAL_STRING("AbortError"));
272 :
273 0 : nsString finalEvent;
274 0 : nsresult rv = DoAbort(finalEvent);
275 :
276 : // Dispatch the events
277 0 : DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR));
278 0 : DispatchProgressEvent(finalEvent);
279 :
280 0 : return rv;
281 : }
282 :
283 : NS_IMETHODIMP
284 2 : FileIOObject::GetReadyState(PRUint16 *aReadyState)
285 : {
286 2 : *aReadyState = mReadyState;
287 2 : return NS_OK;
288 : }
289 :
290 : NS_IMETHODIMP
291 0 : FileIOObject::GetError(nsIDOMDOMError** aError)
292 : {
293 0 : NS_IF_ADDREF(*aError = mError);
294 0 : return NS_OK;
295 : }
296 :
297 : } // namespace dom
298 4392 : } // namespace mozilla
|