Web Inspector: replace HTMLCanvasElement with CanvasRenderingContext for instrumentat...
[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 = 100 * 1000 * 1000; // 100MB
45 static const size_t maximumSingleResourceContentSize = 10 * 1000 * 1000; // 10MB
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
169 void NetworkResourcesData::setResourceType(const String& requestId, InspectorPageAgent::ResourceType type)
170 {
171     ResourceData* resourceData = resourceDataForRequestId(requestId);
172     if (!resourceData)
173         return;
174     resourceData->setType(type);
175 }
176
177 InspectorPageAgent::ResourceType NetworkResourcesData::resourceType(const String& requestId)
178 {
179     ResourceData* resourceData = resourceDataForRequestId(requestId);
180     if (!resourceData)
181         return InspectorPageAgent::OtherResource;
182     return resourceData->type();
183 }
184
185 void NetworkResourcesData::setResourceContent(const String& requestId, const String& content, bool base64Encoded)
186 {
187     ResourceData* resourceData = resourceDataForRequestId(requestId);
188     if (!resourceData)
189         return;
190
191     size_t dataLength = contentSizeInBytes(content);
192     if (dataLength > m_maximumSingleResourceContentSize)
193         return;
194     if (resourceData->isContentEvicted())
195         return;
196
197     if (ensureFreeSpace(dataLength) && !resourceData->isContentEvicted()) {
198         // We can not be sure that we didn't try to save this request data while it was loading, so remove it, if any.
199         if (resourceData->hasContent() || resourceData->hasData())
200             m_contentSize -= resourceData->removeContent();
201         m_requestIdsDeque.append(requestId);
202         resourceData->setContent(content, base64Encoded);
203         m_contentSize += dataLength;
204     }
205 }
206
207 static bool shouldBufferResourceData(const NetworkResourcesData::ResourceData& resourceData)
208 {
209     if (resourceData.forceBufferData())
210         return true;
211
212     if (resourceData.decoder())
213         return true;
214
215     // Buffer data for Web Inspector when the rest of the system would not normally buffer.
216     if (resourceData.cachedResource() && resourceData.cachedResource()->dataBufferingPolicy() == DoNotBufferData)
217         return true;
218
219     return false;
220 }
221
222 void NetworkResourcesData::maybeAddResourceData(const String& requestId, const char* data, size_t dataLength)
223 {
224     ResourceData* resourceData = resourceDataForRequestId(requestId);
225     if (!resourceData)
226         return;
227
228     if (!shouldBufferResourceData(*resourceData))
229         return;
230
231     if (resourceData->dataLength() + dataLength > m_maximumSingleResourceContentSize)
232         m_contentSize -= resourceData->evictContent();
233     if (resourceData->isContentEvicted())
234         return;
235
236     if (ensureFreeSpace(dataLength) && !resourceData->isContentEvicted()) {
237         m_requestIdsDeque.append(requestId);
238         resourceData->appendData(data, dataLength);
239         m_contentSize += dataLength;
240     }
241 }
242
243 void NetworkResourcesData::maybeDecodeDataToContent(const String& requestId)
244 {
245     ResourceData* resourceData = resourceDataForRequestId(requestId);
246     if (!resourceData)
247         return;
248
249     if (!resourceData->hasData())
250         return;
251
252     m_contentSize += resourceData->decodeDataToContent();
253     size_t dataLength = contentSizeInBytes(resourceData->content());
254     if (dataLength > m_maximumSingleResourceContentSize)
255         m_contentSize -= resourceData->evictContent();
256 }
257
258 void NetworkResourcesData::addCachedResource(const String& requestId, CachedResource* cachedResource)
259 {
260     ResourceData* resourceData = resourceDataForRequestId(requestId);
261     if (!resourceData)
262         return;
263     resourceData->setCachedResource(cachedResource);
264 }
265
266 void NetworkResourcesData::addResourceSharedBuffer(const String& requestId, RefPtr<SharedBuffer>&& buffer, const String& textEncodingName)
267 {
268     ResourceData* resourceData = resourceDataForRequestId(requestId);
269     if (!resourceData)
270         return;
271     resourceData->setBuffer(WTFMove(buffer));
272     resourceData->setTextEncodingName(textEncodingName);
273 }
274
275 NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const String& requestId)
276 {
277     return resourceDataForRequestId(requestId);
278 }
279
280 Vector<String> NetworkResourcesData::removeCachedResource(CachedResource* cachedResource)
281 {
282     Vector<String> result;
283     for (auto& entry : m_requestIdToResourceDataMap) {
284         ResourceData* resourceData = entry.value.get();
285         if (resourceData->cachedResource() == cachedResource) {
286             resourceData->setCachedResource(nullptr);
287             result.append(entry.key);
288         }
289     }
290
291     return result;
292 }
293
294 void NetworkResourcesData::clear(std::optional<String> preservedLoaderId)
295 {
296     m_requestIdsDeque.clear();
297     m_contentSize = 0;
298
299     if (!preservedLoaderId)
300         m_requestIdToResourceDataMap.clear();
301     else {
302         m_requestIdToResourceDataMap.removeIf([loaderId = *preservedLoaderId] (auto& entry) {
303             return entry.value->loaderId() != loaderId;
304         });
305     }
306 }
307
308 Vector<NetworkResourcesData::ResourceData*> NetworkResourcesData::resources()
309 {
310     return WTF::map(m_requestIdToResourceDataMap.values(), [] (const auto& v) { return v.get(); });
311 }
312
313 NetworkResourcesData::ResourceData* NetworkResourcesData::resourceDataForRequestId(const String& requestId)
314 {
315     if (requestId.isNull())
316         return nullptr;
317     return m_requestIdToResourceDataMap.get(requestId);
318 }
319
320 void NetworkResourcesData::ensureNoDataForRequestId(const String& requestId)
321 {
322     auto result = m_requestIdToResourceDataMap.take(requestId);
323     if (!result)
324         return;
325
326     ResourceData* resourceData = result.get();
327     if (resourceData->hasContent() || resourceData->hasData())
328         m_contentSize -= resourceData->evictContent();
329 }
330
331 bool NetworkResourcesData::ensureFreeSpace(size_t size)
332 {
333     if (size > m_maximumResourcesContentSize)
334         return false;
335
336     while (size > m_maximumResourcesContentSize - m_contentSize) {
337         String requestId = m_requestIdsDeque.takeFirst();
338         ResourceData* resourceData = resourceDataForRequestId(requestId);
339         if (resourceData)
340             m_contentSize -= resourceData->evictContent();
341     }
342     return true;
343 }
344
345 } // namespace WebCore