613668bdccbde7493df418defdcb6f3b8c09e7c6
[WebKit-https.git] / Source / WebCore / fileapi / FileReader.cpp
1 /*
2  * Copyright (C) 2010 Google Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #include "FileReader.h"
34
35 #include "ExceptionCode.h"
36 #include "File.h"
37 #include "Logging.h"
38 #include "ProgressEvent.h"
39 #include "ScriptExecutionContext.h"
40 #include <runtime/ArrayBuffer.h>
41 #include <wtf/CurrentTime.h>
42 #include <wtf/text/CString.h>
43
44 namespace WebCore {
45
46 static const auto progressNotificationInterval = std::chrono::milliseconds(50);
47
48 PassRefPtr<FileReader> FileReader::create(ScriptExecutionContext& context)
49 {
50     RefPtr<FileReader> fileReader(adoptRef(new FileReader(context)));
51     fileReader->suspendIfNeeded();
52     return fileReader.release();
53 }
54
55 FileReader::FileReader(ScriptExecutionContext& context)
56     : ActiveDOMObject(&context)
57     , m_state(EMPTY)
58     , m_aborting(false)
59     , m_readType(FileReaderLoader::ReadAsBinaryString)
60 {
61 }
62
63 FileReader::~FileReader()
64 {
65     terminate();
66 }
67
68 bool FileReader::canSuspend() const
69 {
70     // FIXME: It is not currently possible to suspend a FileReader, so pages with FileReader can not go into page cache.
71     return false;
72 }
73
74 void FileReader::stop()
75 {
76     terminate();
77 }
78
79 void FileReader::readAsArrayBuffer(Blob* blob, ExceptionCode& ec)
80 {
81     if (!blob)
82         return;
83
84     LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n", blob->url().string().utf8().data(), is<File>(blob) ? downcast<File>(*blob).path().utf8().data() : "");
85
86     readInternal(blob, FileReaderLoader::ReadAsArrayBuffer, ec);
87 }
88
89 void FileReader::readAsBinaryString(Blob* blob, ExceptionCode& ec)
90 {
91     if (!blob)
92         return;
93
94     LOG(FileAPI, "FileReader: reading as binary: %s %s\n", blob->url().string().utf8().data(), is<File>(blob) ? downcast<File>(*blob).path().utf8().data() : "");
95
96     readInternal(blob, FileReaderLoader::ReadAsBinaryString, ec);
97 }
98
99 void FileReader::readAsText(Blob* blob, const String& encoding, ExceptionCode& ec)
100 {
101     if (!blob)
102         return;
103
104     LOG(FileAPI, "FileReader: reading as text: %s %s\n", blob->url().string().utf8().data(), is<File>(blob) ? downcast<File>(*blob).path().utf8().data() : "");
105
106     m_encoding = encoding;
107     readInternal(blob, FileReaderLoader::ReadAsText, ec);
108 }
109
110 void FileReader::readAsText(Blob* blob, ExceptionCode& ec)
111 {
112     readAsText(blob, String(), ec);
113 }
114
115 void FileReader::readAsDataURL(Blob* blob, ExceptionCode& ec)
116 {
117     if (!blob)
118         return;
119
120     LOG(FileAPI, "FileReader: reading as data URL: %s %s\n", blob->url().string().utf8().data(), is<File>(blob) ? downcast<File>(*blob).path().utf8().data() : "");
121
122     readInternal(blob, FileReaderLoader::ReadAsDataURL, ec);
123 }
124
125 void FileReader::readInternal(Blob* blob, FileReaderLoader::ReadType type, ExceptionCode& ec)
126 {
127     // If multiple concurrent read methods are called on the same FileReader, INVALID_STATE_ERR should be thrown when the state is LOADING.
128     if (m_state == LOADING) {
129         ec = INVALID_STATE_ERR;
130         return;
131     }
132
133     setPendingActivity(this);
134
135     m_blob = blob;
136     m_readType = type;
137     m_state = LOADING;
138     m_error = 0;
139
140     m_loader = std::make_unique<FileReaderLoader>(m_readType, this);
141     m_loader->setEncoding(m_encoding);
142     m_loader->setDataType(m_blob->type());
143     m_loader->start(scriptExecutionContext(), m_blob.get());
144 }
145
146 void FileReader::abort()
147 {
148     LOG(FileAPI, "FileReader: aborting\n");
149
150     if (m_aborting || m_state != LOADING)
151         return;
152     m_aborting = true;
153
154     // Schedule to have the abort done later since abort() might be called from the event handler and we do not want the resource loading code to be in the stack.
155     scriptExecutionContext()->postTask([=](ScriptExecutionContext&) {
156         ASSERT(m_state != DONE);
157
158         terminate();
159         m_aborting = false;
160
161         m_error = FileError::create(FileError::ABORT_ERR);
162
163         fireEvent(eventNames().errorEvent);
164         fireEvent(eventNames().abortEvent);
165         fireEvent(eventNames().loadendEvent);
166
167         // All possible events have fired and we're done, no more pending activity.
168         unsetPendingActivity(this);
169     });
170 }
171
172 void FileReader::terminate()
173 {
174     if (m_loader) {
175         m_loader->cancel();
176         m_loader = nullptr;
177     }
178     m_state = DONE;
179 }
180
181 void FileReader::didStartLoading()
182 {
183     fireEvent(eventNames().loadstartEvent);
184 }
185
186 void FileReader::didReceiveData()
187 {
188     // Fire the progress event at least every 50ms.
189     auto now = std::chrono::steady_clock::now();
190     if (!m_lastProgressNotificationTime.time_since_epoch().count()) {
191         m_lastProgressNotificationTime = now;
192         return;
193     }
194
195     if (now - m_lastProgressNotificationTime > progressNotificationInterval) {
196         fireEvent(eventNames().progressEvent);
197         m_lastProgressNotificationTime = now;
198     }
199 }
200
201 void FileReader::didFinishLoading()
202 {
203     if (m_aborting)
204         return;
205
206     ASSERT(m_state != DONE);
207     m_state = DONE;
208
209     fireEvent(eventNames().progressEvent);
210     fireEvent(eventNames().loadEvent);
211     fireEvent(eventNames().loadendEvent);
212     
213     // All possible events have fired and we're done, no more pending activity.
214     unsetPendingActivity(this);
215 }
216
217 void FileReader::didFail(int errorCode)
218 {
219     // If we're aborting, do not proceed with normal error handling since it is covered in aborting code.
220     if (m_aborting)
221         return;
222
223     ASSERT(m_state != DONE);
224     m_state = DONE;
225
226     m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
227     fireEvent(eventNames().errorEvent);
228     fireEvent(eventNames().loadendEvent);
229     
230     // All possible events have fired and we're done, no more pending activity.
231     unsetPendingActivity(this);
232 }
233
234 void FileReader::fireEvent(const AtomicString& type)
235 {
236     dispatchEvent(ProgressEvent::create(type, true, m_loader ? m_loader->bytesLoaded() : 0, m_loader ? m_loader->totalBytes() : 0));
237 }
238
239 PassRefPtr<ArrayBuffer> FileReader::arrayBufferResult() const
240 {
241     if (!m_loader || m_error)
242         return 0;
243     return m_loader->arrayBufferResult();
244 }
245
246 String FileReader::stringResult()
247 {
248     if (!m_loader || m_error)
249         return String();
250     return m_loader->stringResult();
251 }
252
253 } // namespace WebCore