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 | #include "FileReader.h" |
33 | |
34 | #include "EventNames.h" |
35 | #include "File.h" |
36 | #include "Logging.h" |
37 | #include "ProgressEvent.h" |
38 | #include "ScriptExecutionContext.h" |
39 | #include <JavaScriptCore/ArrayBuffer.h> |
40 | #include <wtf/IsoMallocInlines.h> |
41 | #include <wtf/text/CString.h> |
42 | |
43 | namespace WebCore { |
44 | |
45 | WTF_MAKE_ISO_ALLOCATED_IMPL(FileReader); |
46 | |
47 | // Fire the progress event at least every 50ms. |
48 | static const auto progressNotificationInterval = 50_ms; |
49 | |
50 | Ref<FileReader> FileReader::create(ScriptExecutionContext& context) |
51 | { |
52 | auto fileReader = adoptRef(*new FileReader(context)); |
53 | fileReader->suspendIfNeeded(); |
54 | return fileReader; |
55 | } |
56 | |
57 | FileReader::FileReader(ScriptExecutionContext& context) |
58 | : ActiveDOMObject(&context) |
59 | { |
60 | } |
61 | |
62 | FileReader::~FileReader() |
63 | { |
64 | if (m_loader) |
65 | m_loader->cancel(); |
66 | } |
67 | |
68 | bool FileReader::canSuspendForDocumentSuspension() const |
69 | { |
70 | return !hasPendingActivity(); |
71 | } |
72 | |
73 | const char* FileReader::activeDOMObjectName() const |
74 | { |
75 | return "FileReader" ; |
76 | } |
77 | |
78 | void FileReader::stop() |
79 | { |
80 | if (m_loader) { |
81 | m_loader->cancel(); |
82 | m_loader = nullptr; |
83 | } |
84 | m_state = DONE; |
85 | m_loadingActivity = nullptr; |
86 | } |
87 | |
88 | ExceptionOr<void> FileReader::readAsArrayBuffer(Blob* blob) |
89 | { |
90 | if (!blob) |
91 | return { }; |
92 | |
93 | LOG(FileAPI, "FileReader: reading as array buffer: %s %s\n" , blob->url().string().utf8().data(), is<File>(*blob) ? downcast<File>(*blob).path().utf8().data() : "" ); |
94 | |
95 | return readInternal(*blob, FileReaderLoader::ReadAsArrayBuffer); |
96 | } |
97 | |
98 | ExceptionOr<void> FileReader::readAsBinaryString(Blob* blob) |
99 | { |
100 | if (!blob) |
101 | return { }; |
102 | |
103 | LOG(FileAPI, "FileReader: reading as binary: %s %s\n" , blob->url().string().utf8().data(), is<File>(*blob) ? downcast<File>(*blob).path().utf8().data() : "" ); |
104 | |
105 | return readInternal(*blob, FileReaderLoader::ReadAsBinaryString); |
106 | } |
107 | |
108 | ExceptionOr<void> FileReader::readAsText(Blob* blob, const String& encoding) |
109 | { |
110 | if (!blob) |
111 | return { }; |
112 | |
113 | LOG(FileAPI, "FileReader: reading as text: %s %s\n" , blob->url().string().utf8().data(), is<File>(*blob) ? downcast<File>(*blob).path().utf8().data() : "" ); |
114 | |
115 | m_encoding = encoding; |
116 | return readInternal(*blob, FileReaderLoader::ReadAsText); |
117 | } |
118 | |
119 | ExceptionOr<void> FileReader::readAsDataURL(Blob* blob) |
120 | { |
121 | if (!blob) |
122 | return { }; |
123 | |
124 | LOG(FileAPI, "FileReader: reading as data URL: %s %s\n" , blob->url().string().utf8().data(), is<File>(*blob) ? downcast<File>(*blob).path().utf8().data() : "" ); |
125 | |
126 | return readInternal(*blob, FileReaderLoader::ReadAsDataURL); |
127 | } |
128 | |
129 | ExceptionOr<void> FileReader::readInternal(Blob& blob, FileReaderLoader::ReadType type) |
130 | { |
131 | // If multiple concurrent read methods are called on the same FileReader, InvalidStateError should be thrown when the state is LOADING. |
132 | if (m_state == LOADING) |
133 | return Exception { InvalidStateError }; |
134 | |
135 | m_loadingActivity = makePendingActivity(*this); |
136 | |
137 | m_blob = &blob; |
138 | m_readType = type; |
139 | m_state = LOADING; |
140 | m_error = nullptr; |
141 | |
142 | m_loader = std::make_unique<FileReaderLoader>(m_readType, static_cast<FileReaderLoaderClient*>(this)); |
143 | m_loader->setEncoding(m_encoding); |
144 | m_loader->setDataType(m_blob->type()); |
145 | m_loader->start(scriptExecutionContext(), blob); |
146 | |
147 | return { }; |
148 | } |
149 | |
150 | void FileReader::abort() |
151 | { |
152 | LOG(FileAPI, "FileReader: aborting\n" ); |
153 | |
154 | if (m_aborting || m_state != LOADING) |
155 | return; |
156 | m_aborting = true; |
157 | |
158 | // 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. |
159 | scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) { |
160 | if (isContextStopped()) |
161 | return; |
162 | |
163 | ASSERT(m_state != DONE); |
164 | |
165 | stop(); |
166 | m_aborting = false; |
167 | |
168 | m_error = FileError::create(FileError::ABORT_ERR); |
169 | |
170 | fireEvent(eventNames().errorEvent); |
171 | fireEvent(eventNames().abortEvent); |
172 | fireEvent(eventNames().loadendEvent); |
173 | }); |
174 | } |
175 | |
176 | void FileReader::didStartLoading() |
177 | { |
178 | fireEvent(eventNames().loadstartEvent); |
179 | } |
180 | |
181 | void FileReader::didReceiveData() |
182 | { |
183 | auto now = MonotonicTime::now(); |
184 | if (std::isnan(m_lastProgressNotificationTime)) { |
185 | m_lastProgressNotificationTime = now; |
186 | return; |
187 | } |
188 | if (now - m_lastProgressNotificationTime > progressNotificationInterval) { |
189 | fireEvent(eventNames().progressEvent); |
190 | m_lastProgressNotificationTime = now; |
191 | } |
192 | } |
193 | |
194 | void FileReader::didFinishLoading() |
195 | { |
196 | if (m_aborting) |
197 | return; |
198 | |
199 | ASSERT(m_state != DONE); |
200 | m_state = DONE; |
201 | |
202 | fireEvent(eventNames().progressEvent); |
203 | fireEvent(eventNames().loadEvent); |
204 | fireEvent(eventNames().loadendEvent); |
205 | |
206 | m_loadingActivity = nullptr; |
207 | } |
208 | |
209 | void FileReader::didFail(int errorCode) |
210 | { |
211 | // If we're aborting, do not proceed with normal error handling since it is covered in aborting code. |
212 | if (m_aborting) |
213 | return; |
214 | |
215 | ASSERT(m_state != DONE); |
216 | m_state = DONE; |
217 | |
218 | m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode)); |
219 | fireEvent(eventNames().errorEvent); |
220 | fireEvent(eventNames().loadendEvent); |
221 | |
222 | m_loadingActivity = nullptr; |
223 | } |
224 | |
225 | void FileReader::fireEvent(const AtomicString& type) |
226 | { |
227 | dispatchEvent(ProgressEvent::create(type, true, m_loader ? m_loader->bytesLoaded() : 0, m_loader ? m_loader->totalBytes() : 0)); |
228 | } |
229 | |
230 | Optional<Variant<String, RefPtr<JSC::ArrayBuffer>>> FileReader::result() const |
231 | { |
232 | if (!m_loader || m_error) |
233 | return WTF::nullopt; |
234 | if (m_readType == FileReaderLoader::ReadAsArrayBuffer) { |
235 | auto result = m_loader->arrayBufferResult(); |
236 | if (!result) |
237 | return WTF::nullopt; |
238 | return { result }; |
239 | } |
240 | String result = m_loader->stringResult(); |
241 | if (result.isNull()) |
242 | return WTF::nullopt; |
243 | return { WTFMove(result) }; |
244 | } |
245 | |
246 | } // namespace WebCore |
247 | |