Unreviewed, rolling out r244158.
[WebKit-https.git] / Source / WebCore / inspector / NetworkResourcesData.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2017 Apple Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
21  * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "NetworkResourcesData.h"
32
33 #include "CachedResource.h"
34 #include "InspectorNetworkAgent.h"
35 #include "ResourceResponse.h"
36 #include "SharedBuffer.h"
37 #include "TextResourceDecoder.h"
38 #include <wtf/text/Base64.h>
39
40 namespace WebCore {
41
42 using namespace Inspector;
43
44 static const size_t maximumResourcesContentSize = 200 * 1000 * 1000; // 200MB
45 static const size_t maximumSingleResourceContentSize = 50 * 1000 * 1000; // 50MB
46
47 NetworkResourcesData::ResourceData::ResourceData(const String& requestId, const String& loaderId)
48     : m_requestId(requestId)
49     , m_loaderId(loaderId)
50 {
51 }
52
53 void NetworkResourcesData::ResourceData::setContent(const String& content, bool base64Encoded)
54 {
55     ASSERT(!hasData());
56     ASSERT(!hasContent());
57     m_content = content;
58     m_base64Encoded = base64Encoded;
59 }
60
61 static size_t contentSizeInBytes(const String& content)
62 {
63     return content.isNull() ? 0 : content.impl()->sizeInBytes();
64 }
65
66 unsigned NetworkResourcesData::ResourceData::removeContent()
67 {
68     unsigned result = 0;
69     if (hasData()) {
70         ASSERT(!hasContent());
71         result = m_dataBuffer->size();
72         m_dataBuffer = nullptr;
73     }
74
75     if (hasContent()) {
76         ASSERT(!hasData());
77         result = contentSizeInBytes(m_content);
78         m_content = String();
79     }
80     return result;
81 }
82
83 unsigned NetworkResourcesData::ResourceData::evictContent()
84 {
85     m_isContentEvicted = true;
86     return removeContent();
87 }
88
89 size_t NetworkResourcesData::ResourceData::dataLength() const
90 {
91     return m_dataBuffer ? m_dataBuffer->size() : 0;
92 }
93
94 void NetworkResourcesData::ResourceData::appendData(const char* data, size_t dataLength)
95 {
96     ASSERT(!hasContent());
97     if (!m_dataBuffer)
98         m_dataBuffer = SharedBuffer::create(data, dataLength);
99     else
100         m_dataBuffer->append(data, dataLength);
101 }
102
103 size_t NetworkResourcesData::ResourceData::decodeDataToContent()
104 {
105     ASSERT(!hasContent());
106
107     size_t dataLength = m_dataBuffer->size();
108
109     if (m_decoder) {
110         m_base64Encoded = false;
111         m_content = m_decoder->decodeAndFlush(m_dataBuffer->data(), dataLength);
112     } else {
113         m_base64Encoded = true;
114         m_content = base64Encode(m_dataBuffer->data(), dataLength);
115     }
116
117     m_dataBuffer = nullptr;
118
119     size_t decodedLength = contentSizeInBytes(m_content);
120     ASSERT(decodedLength >= dataLength);
121     return decodedLength - dataLength;
122 }
123
124 NetworkResourcesData::NetworkResourcesData()
125     : m_maximumResourcesContentSize(maximumResourcesContentSize)
126     , m_maximumSingleResourceContentSize(maximumSingleResourceContentSize)
127 {
128 }
129
130 NetworkResourcesData::~NetworkResourcesData()
131 {
132     clear();
133 }
134
135 void NetworkResourcesData::resourceCreated(const String& requestId, const String& loaderId, InspectorPageAgent::ResourceType type)
136 {
137     ensureNoDataForRequestId(requestId);
138
139     auto resourceData = std::make_unique<ResourceData>(requestId, loaderId);
140     resourceData->setType(type);
141     m_requestIdToResourceDataMap.set(requestId, WTFMove(resourceData));
142 }
143
144 void NetworkResourcesData::resourceCreated(const String& requestId, const String& loaderId, CachedResource& cachedResource)
145 {
146     ensureNoDataForRequestId(requestId);
147
148     auto resourceData = std::make_unique<ResourceData>(requestId, loaderId);
149     resourceData->setCachedResource(&cachedResource);
150     m_requestIdToResourceDataMap.set(requestId, WTFMove(resourceData));
151 }
152
153 void NetworkResourcesData::responseReceived(const String& requestId, const String& frameId, const ResourceResponse& response, InspectorPageAgent::ResourceType type, bool forceBufferData)
154 {
155     ResourceData* resourceData = resourceDataForRequestId(requestId);
156     if (!resourceData)
157         return;
158
159     resourceData->setFrameId(frameId);
160     resourceData->setURL(response.url());
161     resourceData->setHTTPStatusCode(response.httpStatusCode());
162     resourceData->setType(type);
163     resourceData->setForceBufferData(forceBufferData);
164
165     if (InspectorNetworkAgent::shouldTreatAsText(response.mimeType()))
166         resourceData->setDecoder(InspectorNetworkAgent::createTextDecoder(response.mimeType(), response.textEncodingName()));
167
168     if (auto& certificateInfo = response.certificateInfo())
169         resourceData->setCertificateInfo(certificateInfo);
170 }
171
172 void NetworkResourcesData::setResourceType(const String& requestId, InspectorPageAgent::ResourceType type)
173 {
174     ResourceData* resourceData = resourceDataForRequestId(requestId);
175     if (!resourceData)
176         return;
177     resourceData->setType(type);
178 }
179
180 InspectorPageAgent::ResourceType NetworkResourcesData::resourceType(const String& requestId)
181 {
182     ResourceData* resourceData = resourceDataForRequestId(requestId);
183     if (!resourceData)
184         return InspectorPageAgent::OtherResource;
185     return resourceData->type();
186 }
187
188 void NetworkResourcesData::setResourceContent(const String& requestId, const String& content, bool base64Encoded)
189 {
190     if (content.isNull())
191         return;
192
193     ResourceData* resourceData = resourceDataForRequestId(requestId);
194     if (!resourceData)
195         return;
196
197     size_t dataLength = contentSizeInBytes(content);
198     if (dataLength > m_maximumSingleResourceContentSize)
199         return;
200     if (resourceData->isContentEvicted())
201         return;
202
203     if (ensureFreeSpace(dataLength) && !resourceData->isContentEvicted()) {
204         // We can not be sure that we didn't try to save this request data while it was loading, so remove it, if any.
205         if (resourceData->hasContent() || resourceData->hasData())
206             m_contentSize -= resourceData->removeContent();
207         m_requestIdsDeque.append(requestId);
208         resourceData->setContent(content, base64Encoded);
209         m_contentSize += dataLength;
210     }
211 }
212
213 static bool shouldBufferResourceData(const NetworkResourcesData::ResourceData& resourceData)
214 {
215     if (resourceData.forceBufferData())
216         return true;
217
218     if (resourceData.decoder())
219         return true;
220
221     // Buffer data for Web Inspector when the rest of the system would not normally buffer.
222     if (resourceData.cachedResource() && resourceData.cachedResource()->dataBufferingPolicy() == DataBufferingPolicy::DoNotBufferData)
223         return true;
224
225     return false;
226 }
227
228 NetworkResourcesData::ResourceData const* NetworkResourcesData::maybeAddResourceData(const String& requestId, const char* data, size_t dataLength)
229 {
230     ResourceData* resourceData = resourceDataForRequestId(requestId);
231     if (!resourceData)
232         return nullptr;
233
234     if (!shouldBufferResourceData(*resourceData))
235         return resourceData;
236
237     if (resourceData->dataLength() + dataLength > m_maximumSingleResourceContentSize)
238         m_contentSize -= resourceData->evictContent();
239     if (resourceData->isContentEvicted())
240         return resourceData;
241
242     if (ensureFreeSpace(dataLength) && !resourceData->isContentEvicted()) {
243         m_requestIdsDeque.append(requestId);
244         resourceData->appendData(data, dataLength);
245         m_contentSize += dataLength;
246     }
247
248     return resourceData;
249 }
250
251 void NetworkResourcesData::maybeDecodeDataToContent(const String& requestId)
252 {
253     ResourceData* resourceData = resourceDataForRequestId(requestId);
254     if (!resourceData)
255         return;
256
257     if (!resourceData->hasData())
258         return;
259
260     m_contentSize += resourceData->decodeDataToContent();
261     size_t dataLength = contentSizeInBytes(resourceData->content());
262     if (dataLength > m_maximumSingleResourceContentSize)
263         m_contentSize -= resourceData->evictContent();
264 }
265
266 void NetworkResourcesData::addCachedResource(const String& requestId, CachedResource* cachedResource)
267 {
268     ResourceData* resourceData = resourceDataForRequestId(requestId);
269     if (!resourceData)
270         return;
271     resourceData->setCachedResource(cachedResource);
272 }
273
274 void NetworkResourcesData::addResourceSharedBuffer(const String& requestId, RefPtr<SharedBuffer>&& buffer, const String& textEncodingName)
275 {
276     ResourceData* resourceData = resourceDataForRequestId(requestId);
277     if (!resourceData)
278         return;
279     resourceData->setBuffer(WTFMove(buffer));
280     resourceData->setTextEncodingName(textEncodingName);
281 }
282
283 NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const String& requestId)
284 {
285     return resourceDataForRequestId(requestId);
286 }
287
288 Vector<String> NetworkResourcesData::removeCachedResource(CachedResource* cachedResource)
289 {
290     Vector<String> result;
291     for (auto& entry : m_requestIdToResourceDataMap) {
292         ResourceData* resourceData = entry.value.get();
293         if (resourceData->cachedResource() == cachedResource) {
294             resourceData->setCachedResource(nullptr);
295             result.append(entry.key);
296         }
297     }
298
299     return result;
300 }
301
302 void NetworkResourcesData::clear(Optional<String> preservedLoaderId)
303 {
304     m_requestIdsDeque.clear();
305     m_contentSize = 0;
306
307     if (!preservedLoaderId)
308         m_requestIdToResourceDataMap.clear();
309     else {
310         m_requestIdToResourceDataMap.removeIf([loaderId = *preservedLoaderId] (auto& entry) {
311             return entry.value->loaderId() != loaderId;
312         });
313     }
314 }
315
316 Vector<NetworkResourcesData::ResourceData*> NetworkResourcesData::resources()
317 {
318     return WTF::map(m_requestIdToResourceDataMap.values(), [] (const auto& v) { return v.get(); });
319 }
320
321 NetworkResourcesData::ResourceData* NetworkResourcesData::resourceDataForRequestId(const String& requestId)
322 {
323     if (requestId.isNull())
324         return nullptr;
325     return m_requestIdToResourceDataMap.get(requestId);
326 }
327
328 void NetworkResourcesData::ensureNoDataForRequestId(const String& requestId)
329 {
330     auto result = m_requestIdToResourceDataMap.take(requestId);
331     if (!result)
332         return;
333
334     ResourceData* resourceData = result.get();
335     if (resourceData->hasContent() || resourceData->hasData())
336         m_contentSize -= resourceData->evictContent();
337 }
338
339 bool NetworkResourcesData::ensureFreeSpace(size_t size)
340 {
341     if (size > m_maximumResourcesContentSize)
342         return false;
343
344     while (size > m_maximumResourcesContentSize - m_contentSize) {
345         String requestId = m_requestIdsDeque.takeFirst();
346         ResourceData* resourceData = resourceDataForRequestId(requestId);
347         if (resourceData)
348             m_contentSize -= resourceData->evictContent();
349     }
350     return true;
351 }
352
353 } // namespace WebCore