NetworkResourcesData.cpp [plain text]
#include "config.h"
#if ENABLE(INSPECTOR)
#include "NetworkResourcesData.h"
#include "CachedResource.h"
#include "DOMImplementation.h"
#include "ResourceResponse.h"
#include "SharedBuffer.h"
#include "TextResourceDecoder.h"
namespace {
static size_t maximumResourcesContentSize = 100 * 1000 * 1000;
static size_t maximumSingleResourceContentSize = 10 * 1000 * 1000;
}
using namespace Inspector;
namespace WebCore {
PassRefPtr<XHRReplayData> XHRReplayData::create(const String &method, const URL& url, bool async, PassRefPtr<FormData> formData, const HTTPHeaderMap& headers, bool includeCredentials)
{
return adoptRef(new XHRReplayData(method, url, async, formData, headers, includeCredentials));
}
XHRReplayData::XHRReplayData(const String &method, const URL& url, bool async, PassRefPtr<FormData> formData, const HTTPHeaderMap& headers, bool includeCredentials)
: m_method(method)
, m_url(url)
, m_async(async)
, m_formData(formData)
, m_headers(headers)
, m_includeCredentials(includeCredentials)
{
}
NetworkResourcesData::ResourceData::ResourceData(const String& requestId, const String& loaderId)
: m_requestId(requestId)
, m_loaderId(loaderId)
, m_base64Encoded(false)
, m_isContentEvicted(false)
, m_type(InspectorPageAgent::OtherResource)
, m_cachedResource(nullptr)
{
}
void NetworkResourcesData::ResourceData::setContent(const String& content, bool base64Encoded)
{
ASSERT(!hasData());
ASSERT(!hasContent());
m_content = content;
m_base64Encoded = base64Encoded;
}
static size_t contentSizeInBytes(const String& content)
{
return content.isNull() ? 0 : content.impl()->sizeInBytes();
}
unsigned NetworkResourcesData::ResourceData::removeContent()
{
unsigned result = 0;
if (hasData()) {
ASSERT(!hasContent());
result = m_dataBuffer->size();
m_dataBuffer = nullptr;
}
if (hasContent()) {
ASSERT(!hasData());
result = contentSizeInBytes(m_content);
m_content = String();
}
return result;
}
unsigned NetworkResourcesData::ResourceData::evictContent()
{
m_isContentEvicted = true;
return removeContent();
}
size_t NetworkResourcesData::ResourceData::dataLength() const
{
return m_dataBuffer ? m_dataBuffer->size() : 0;
}
void NetworkResourcesData::ResourceData::appendData(const char* data, size_t dataLength)
{
ASSERT(!hasContent());
if (!m_dataBuffer)
m_dataBuffer = SharedBuffer::create(data, dataLength);
else
m_dataBuffer->append(data, dataLength);
}
size_t NetworkResourcesData::ResourceData::decodeDataToContent()
{
ASSERT(!hasContent());
size_t dataLength = m_dataBuffer->size();
m_content = m_decoder->decodeAndFlush(m_dataBuffer->data(), m_dataBuffer->size());
m_dataBuffer = nullptr;
return contentSizeInBytes(m_content) - dataLength;
}
NetworkResourcesData::NetworkResourcesData()
: m_contentSize(0)
, m_maximumResourcesContentSize(maximumResourcesContentSize)
, m_maximumSingleResourceContentSize(maximumSingleResourceContentSize)
{
}
NetworkResourcesData::~NetworkResourcesData()
{
clear();
}
void NetworkResourcesData::resourceCreated(const String& requestId, const String& loaderId)
{
ensureNoDataForRequestId(requestId);
m_requestIdToResourceDataMap.set(requestId, new ResourceData(requestId, loaderId));
}
static PassRefPtr<TextResourceDecoder> createOtherResourceTextDecoder(const String& mimeType, const String& textEncodingName)
{
RefPtr<TextResourceDecoder> decoder;
if (!textEncodingName.isEmpty())
decoder = TextResourceDecoder::create("text/plain", textEncodingName);
else if (DOMImplementation::isXMLMIMEType(mimeType.lower())) {
decoder = TextResourceDecoder::create("application/xml");
decoder->useLenientXMLDecoding();
} else if (equalIgnoringCase(mimeType, "text/html"))
decoder = TextResourceDecoder::create("text/html", "UTF-8");
else if (mimeType == "text/plain")
decoder = TextResourceDecoder::create("text/plain", "ISO-8859-1");
return decoder;
}
void NetworkResourcesData::responseReceived(const String& requestId, const String& frameId, const ResourceResponse& response)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return;
resourceData->setFrameId(frameId);
resourceData->setUrl(response.url());
resourceData->setDecoder(createOtherResourceTextDecoder(response.mimeType(), response.textEncodingName()));
resourceData->setHTTPStatusCode(response.httpStatusCode());
}
void NetworkResourcesData::setResourceType(const String& requestId, InspectorPageAgent::ResourceType type)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return;
resourceData->setType(type);
}
InspectorPageAgent::ResourceType NetworkResourcesData::resourceType(const String& requestId)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return InspectorPageAgent::OtherResource;
return resourceData->type();
}
void NetworkResourcesData::setResourceContent(const String& requestId, const String& content, bool base64Encoded)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return;
size_t dataLength = contentSizeInBytes(content);
if (dataLength > m_maximumSingleResourceContentSize)
return;
if (resourceData->isContentEvicted())
return;
if (ensureFreeSpace(dataLength) && !resourceData->isContentEvicted()) {
if (resourceData->hasContent())
m_contentSize -= resourceData->removeContent();
m_requestIdsDeque.append(requestId);
resourceData->setContent(content, base64Encoded);
m_contentSize += dataLength;
}
}
void NetworkResourcesData::maybeAddResourceData(const String& requestId, const char* data, size_t dataLength)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return;
if (!resourceData->decoder())
return;
if (resourceData->dataLength() + dataLength > m_maximumSingleResourceContentSize)
m_contentSize -= resourceData->evictContent();
if (resourceData->isContentEvicted())
return;
if (ensureFreeSpace(dataLength) && !resourceData->isContentEvicted()) {
m_requestIdsDeque.append(requestId);
resourceData->appendData(data, dataLength);
m_contentSize += dataLength;
}
}
void NetworkResourcesData::maybeDecodeDataToContent(const String& requestId)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return;
if (!resourceData->hasData())
return;
m_contentSize += resourceData->decodeDataToContent();
size_t dataLength = contentSizeInBytes(resourceData->content());
if (dataLength > m_maximumSingleResourceContentSize)
m_contentSize -= resourceData->evictContent();
}
void NetworkResourcesData::addCachedResource(const String& requestId, CachedResource* cachedResource)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return;
resourceData->setCachedResource(cachedResource);
}
void NetworkResourcesData::addResourceSharedBuffer(const String& requestId, PassRefPtr<SharedBuffer> buffer, const String& textEncodingName)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return;
resourceData->setBuffer(buffer);
resourceData->setTextEncodingName(textEncodingName);
}
NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const String& requestId)
{
return resourceDataForRequestId(requestId);
}
XHRReplayData* NetworkResourcesData::xhrReplayData(const String& requestId)
{
if (m_reusedXHRReplayDataRequestIds.contains(requestId))
return xhrReplayData(m_reusedXHRReplayDataRequestIds.get(requestId));
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return nullptr;
return resourceData->xhrReplayData();
}
void NetworkResourcesData::setXHRReplayData(const String& requestId, XHRReplayData* xhrReplayData)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData) {
Vector<String> result;
ReusedRequestIds::iterator it;
ReusedRequestIds::iterator end = m_reusedXHRReplayDataRequestIds.end();
for (it = m_reusedXHRReplayDataRequestIds.begin(); it != end; ++it) {
if (it->value == requestId)
setXHRReplayData(it->key, xhrReplayData);
}
return;
}
resourceData->setXHRReplayData(xhrReplayData);
}
void NetworkResourcesData::reuseXHRReplayData(const String& requestId, const String& reusedRequestId)
{
ResourceData* reusedResourceData = resourceDataForRequestId(reusedRequestId);
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!reusedResourceData || !resourceData) {
m_reusedXHRReplayDataRequestIds.set(requestId, reusedRequestId);
return;
}
resourceData->setXHRReplayData(reusedResourceData->xhrReplayData());
}
Vector<String> NetworkResourcesData::removeCachedResource(CachedResource* cachedResource)
{
Vector<String> result;
ResourceDataMap::iterator it;
ResourceDataMap::iterator end = m_requestIdToResourceDataMap.end();
for (it = m_requestIdToResourceDataMap.begin(); it != end; ++it) {
ResourceData* resourceData = it->value;
if (resourceData->cachedResource() == cachedResource) {
resourceData->setCachedResource(nullptr);
result.append(it->key);
}
}
return result;
}
void NetworkResourcesData::clear(const String& preservedLoaderId)
{
m_requestIdsDeque.clear();
m_contentSize = 0;
ResourceDataMap preservedMap;
ResourceDataMap::iterator it;
ResourceDataMap::iterator end = m_requestIdToResourceDataMap.end();
for (it = m_requestIdToResourceDataMap.begin(); it != end; ++it) {
ResourceData* resourceData = it->value;
if (!preservedLoaderId.isNull() && resourceData->loaderId() == preservedLoaderId)
preservedMap.set(it->key, it->value);
else
delete resourceData;
}
m_requestIdToResourceDataMap.swap(preservedMap);
m_reusedXHRReplayDataRequestIds.clear();
}
NetworkResourcesData::ResourceData* NetworkResourcesData::resourceDataForRequestId(const String& requestId)
{
if (requestId.isNull())
return nullptr;
return m_requestIdToResourceDataMap.get(requestId);
}
void NetworkResourcesData::ensureNoDataForRequestId(const String& requestId)
{
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (!resourceData)
return;
if (resourceData->hasContent() || resourceData->hasData())
m_contentSize -= resourceData->evictContent();
delete resourceData;
m_requestIdToResourceDataMap.remove(requestId);
}
bool NetworkResourcesData::ensureFreeSpace(size_t size)
{
if (size > m_maximumResourcesContentSize)
return false;
while (size > m_maximumResourcesContentSize - m_contentSize) {
String requestId = m_requestIdsDeque.takeFirst();
ResourceData* resourceData = resourceDataForRequestId(requestId);
if (resourceData)
m_contentSize -= resourceData->evictContent();
}
return true;
}
}
#endif // ENABLE(INSPECTOR)