Regression(r191673): Crash in RunLoopTimer::schedule()
[WebKit-https.git] / Source / WebCore / platform / network / DataURLDecoder.cpp
1 /*
2  * Copyright (C) 2015 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DataURLDecoder.h"
28
29 #include "DecodeEscapeSequences.h"
30 #include "HTTPParsers.h"
31 #include "SharedBuffer.h"
32 #include "URL.h"
33 #include <wtf/MainThread.h>
34 #include <wtf/RunLoop.h>
35 #include <wtf/WorkQueue.h>
36 #include <wtf/text/Base64.h>
37
38 namespace WebCore {
39 namespace DataURLDecoder {
40
41 static WorkQueue& decodeQueue()
42 {
43     static auto& queue = WorkQueue::create("org.webkit.DataURLDecoder").leakRef();
44     return queue;
45 }
46
47 struct DecodeTask {
48     const String urlString;
49     const StringView encodedData;
50     const bool isBase64;
51     const ScheduleContext scheduleContext;
52     const DecodeCompletionHandler completionHandler;
53
54     Result result;
55 };
56
57 #if HAVE(RUNLOOP_TIMER)
58
59 class DecodingResultDispatcher : public ThreadSafeRefCounted<DecodingResultDispatcher> {
60 public:
61     static void dispatch(std::unique_ptr<DecodeTask> decodeTask)
62     {
63         Ref<DecodingResultDispatcher> dispatcher = adoptRef(*new DecodingResultDispatcher(WTF::move(decodeTask)));
64         dispatcher->startTimer();
65     }
66
67 private:
68     DecodingResultDispatcher(std::unique_ptr<DecodeTask> decodeTask)
69         : m_timer(*this, &DecodingResultDispatcher::timerFired)
70         , m_decodeTask(WTF::move(decodeTask))
71     {
72     }
73
74     void startTimer()
75     {
76         // Keep alive until the timer has fired.
77         ref();
78         m_timer.startOneShot(0);
79         m_timer.schedule(m_decodeTask->scheduleContext.scheduledPairs);
80     }
81
82     void timerFired()
83     {
84         if (m_decodeTask->result.data)
85             m_decodeTask->completionHandler(WTF::move(m_decodeTask->result));
86         else
87             m_decodeTask->completionHandler({ });
88
89         deref();
90     }
91
92     RunLoopTimer<DecodingResultDispatcher> m_timer;
93     std::unique_ptr<DecodeTask> m_decodeTask;
94 };
95
96 #endif // HAVE(RUNLOOP_TIMER)
97
98 static Result parseMediaType(const String& mediaType)
99 {
100     auto mimeType = extractMIMETypeFromMediaType(mediaType);
101     auto charset = extractCharsetFromMediaType(mediaType);
102
103     // https://tools.ietf.org/html/rfc2397
104     // If <mediatype> is omitted, it defaults to text/plain;charset=US-ASCII. As a shorthand,
105     // "text/plain" can be omitted but the charset parameter supplied.
106     if (mimeType.isEmpty()) {
107         mimeType = ASCIILiteral("text/plain");
108         if (charset.isEmpty())
109             charset = ASCIILiteral("US-ASCII");
110     }
111     return { mimeType, charset, nullptr };
112 }
113
114 static std::unique_ptr<DecodeTask> createDecodeTask(const URL& url, const ScheduleContext& scheduleContext, DecodeCompletionHandler completionHandler)
115 {
116     const char dataString[] = "data:";
117     const char base64String[] = ";base64";
118
119     auto urlString = url.string();
120     ASSERT(urlString.startsWith(dataString));
121
122     size_t headerEnd = urlString.find(',', strlen(dataString));
123     size_t encodedDataStart = headerEnd == notFound ? headerEnd : headerEnd + 1;
124
125     auto encodedData = StringView(urlString).substring(encodedDataStart);
126     auto header = StringView(urlString).substring(strlen(dataString), headerEnd - strlen(dataString));
127     bool isBase64 = header.endsWithIgnoringASCIICase(StringView(base64String));
128     auto mediaType = (isBase64 ? header.substring(0, header.length() - strlen(base64String)) : header).toString();
129
130     return std::make_unique<DecodeTask>(DecodeTask {
131         WTF::move(urlString),
132         WTF::move(encodedData),
133         isBase64,
134         scheduleContext,
135         WTF::move(completionHandler),
136         parseMediaType(mediaType)
137     });
138 }
139
140 static void decodeBase64(DecodeTask& task)
141 {
142     Vector<char> buffer;
143     // First try base64url.
144     if (!base64URLDecode(task.encodedData.toStringWithoutCopying(), buffer)) {
145         // Didn't work, try unescaping and decoding as base64.
146         auto unescapedString = decodeURLEscapeSequences(task.encodedData.toStringWithoutCopying());
147         if (!base64Decode(unescapedString, buffer, Base64IgnoreWhitespace))
148             return;
149     }
150     buffer.shrinkToFit();
151     task.result.data = SharedBuffer::adoptVector(buffer);
152 }
153
154 static void decodeEscaped(DecodeTask& task)
155 {
156     TextEncoding encodingFromCharset(task.result.charset);
157     auto& encoding = encodingFromCharset.isValid() ? encodingFromCharset : UTF8Encoding();
158     auto buffer = decodeURLEscapeSequencesAsData(task.encodedData, encoding);
159
160     buffer.shrinkToFit();
161     task.result.data = SharedBuffer::adoptVector(buffer);
162 }
163
164 void decode(const URL& url, const ScheduleContext& scheduleContext, DecodeCompletionHandler completionHandler)
165 {
166     ASSERT(url.protocolIsData());
167
168     auto decodeTask = createDecodeTask(url, scheduleContext, WTF::move(completionHandler));
169     auto* decodeTaskPtr = decodeTask.release();
170     decodeQueue().dispatch([decodeTaskPtr] {
171         auto& decodeTask = *decodeTaskPtr;
172
173         if (decodeTask.isBase64)
174             decodeBase64(decodeTask);
175         else
176             decodeEscaped(decodeTask);
177
178 #if HAVE(RUNLOOP_TIMER)
179         DecodingResultDispatcher::dispatch(std::unique_ptr<DecodeTask>(decodeTaskPtr));
180 #else
181         callOnMainThread([decodeTaskPtr] {
182             std::unique_ptr<DecodeTask> decodeTask(decodeTaskPtr);
183             if (!decodeTask->result.data) {
184                 decodeTask->completionHandler({ });
185                 return;
186             }
187             decodeTask->completionHandler(WTF::move(decodeTask->result));
188         });
189 #endif
190     });
191 }
192
193 }
194 }