void Resource::addClient(ResourceClient* client) { ASSERT(!isPurgeable()); if (m_preloadResult == PreloadNotReferenced) { if (isLoaded()) m_preloadResult = PreloadReferencedWhileComplete; else if (m_requestedFromNetworkingLayer) m_preloadResult = PreloadReferencedWhileLoading; else m_preloadResult = PreloadReferenced; } if (!hasClients()) memoryCache()->makeLive(this); if (!m_revalidatingRequest.isNull()) { m_clients.add(client); return; } // If we have existing data to send to the new client and the resource type supprts it, send it asynchronously. if (!m_response.isNull() && !shouldSendCachedDataSynchronouslyForType(type()) && !m_needsSynchronousCacheHit) { m_clientsAwaitingCallback.add(client); ResourceCallback::callbackHandler()->schedule(this); return; } m_clients.add(client); didAddClient(client); return; }
void Resource::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail, WebProcessMemoryDump* memoryDump) const { static const size_t kMaxURLReportLength = 128; static const int kMaxResourceClientToShowInMemoryInfra = 10; const String dumpName = getMemoryDumpName(); WebMemoryAllocatorDump* dump = memoryDump->createMemoryAllocatorDump(dumpName); dump->addScalar("encoded_size", "bytes", m_encodedSize); if (canDelete()) { dump->addScalar("dead_size", "bytes", m_encodedSize); } else { dump->addScalar("live_size", "bytes", m_encodedSize); } if (m_data) { dump->addScalar("purgeable_size", "bytes", isPurgeable() && !wasPurged() ? encodedSize() + overheadSize() : 0); m_data->onMemoryDump(dumpName, memoryDump); } if (levelOfDetail == WebMemoryDumpLevelOfDetail::Detailed) { String urlToReport = url().string(); if (urlToReport.length() > kMaxURLReportLength) { urlToReport.truncate(kMaxURLReportLength); urlToReport = urlToReport + "..."; } dump->addString("url", "", urlToReport); dump->addString("reason_not_deletable", "", reasonNotDeletable()); Vector<String> clientNames; ResourceClientWalker<ResourceClient> walker(m_clients); while (ResourceClient* client = walker.next()) clientNames.append(client->debugName()); ResourceClientWalker<ResourceClient> walker2(m_clientsAwaitingCallback); while (ResourceClient* client = walker2.next()) clientNames.append("(awaiting) " + client->debugName()); ResourceClientWalker<ResourceClient> walker3(m_finishedClients); while (ResourceClient* client = walker3.next()) clientNames.append("(finished) " + client->debugName()); std::sort(clientNames.begin(), clientNames.end(), codePointCompareLessThan); StringBuilder builder; for (size_t i = 0; i < clientNames.size() && i < kMaxResourceClientToShowInMemoryInfra; ++i) { if (i > 0) builder.append(" / "); builder.append(clientNames[i]); } if (clientNames.size() > kMaxResourceClientToShowInMemoryInfra) { builder.append(" / and "); builder.appendNumber(clientNames.size() - kMaxResourceClientToShowInMemoryInfra); builder.append(" more"); } dump->addString("ResourceClient", "", builder.toString()); } const String overheadName = dumpName + "/metadata"; WebMemoryAllocatorDump* overheadDump = memoryDump->createMemoryAllocatorDump(overheadName); overheadDump->addScalar("size", "bytes", overheadSize()); memoryDump->addSuballocation(overheadDump->guid(), String(WTF::Partitions::kAllocatedObjectPoolName)); }
void Resource::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail, WebProcessMemoryDump* memoryDump) const { static const size_t kMaxURLReportLength = 128; const String dumpName = getMemoryDumpName(); WebMemoryAllocatorDump* dump = memoryDump->createMemoryAllocatorDump(dumpName); dump->addScalar("encoded_size", "bytes", m_encodedSize); if (canDelete()) { dump->addScalar("dead_size", "bytes", m_encodedSize); } else { dump->addScalar("live_size", "bytes", m_encodedSize); } if (m_data) { dump->addScalar("purgeable_size", "bytes", isPurgeable() && !wasPurged() ? encodedSize() + overheadSize() : 0); m_data->onMemoryDump(dumpName, memoryDump); } if (levelOfDetail == WebMemoryDumpLevelOfDetail::Detailed) { String urlToReport = url().string(); if (urlToReport.length() > kMaxURLReportLength) { urlToReport.truncate(kMaxURLReportLength); urlToReport = urlToReport + "..."; } dump->addString("url", "", urlToReport); } const String overheadName = dumpName + "/metadata"; WebMemoryAllocatorDump* overheadDump = memoryDump->createMemoryAllocatorDump(overheadName); overheadDump->addScalar("size", "bytes", overheadSize()); memoryDump->addSuballocation(overheadDump->guid(), String(WTF::Partitions::kAllocatedObjectPoolName)); }
bool CachedResource::addClientToSet(CachedResourceClient* client) { ASSERT(!isPurgeable()); if (m_preloadResult == PreloadNotReferenced) { if (isLoaded()) m_preloadResult = PreloadReferencedWhileComplete; else if (m_requestedFromNetworkingLayer) m_preloadResult = PreloadReferencedWhileLoading; else m_preloadResult = PreloadReferenced; } if (!hasClients() && inCache()) memoryCache()->addToLiveResourcesSize(this); if ((m_type == RawResource || m_type == MainResource) && !m_response.isNull() && !m_proxyResource) { // Certain resources (especially XHRs and main resources) do crazy things if an asynchronous load returns // synchronously (e.g., scripts may not have set all the state they need to handle the load). // Therefore, rather than immediately sending callbacks on a cache hit like other CachedResources, // we schedule the callbacks and ensure we never finish synchronously. ASSERT(!m_clientsAwaitingCallback.contains(client)); m_clientsAwaitingCallback.add(client, CachedResourceCallback::schedule(this, client)); return false; } m_clients.add(client); return true; }
LayoutSize ImageResource::imageSizeForLayoutObject(const LayoutObject* layoutObject, float multiplier, SizeType sizeType) { ASSERT(!isPurgeable()); if (!m_image) return LayoutSize(); LayoutSize imageSize; if (m_image->isBitmapImage() && (layoutObject && layoutObject->shouldRespectImageOrientation() == RespectImageOrientation)) imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); else if (m_image->isSVGImage() && sizeType == NormalSize) imageSize = LayoutSize(svgImageSizeForLayoutObject(layoutObject)); else imageSize = LayoutSize(m_image->size()); if (multiplier == 1.0f) return imageSize; // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier; float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier; LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0); imageSize.scale(widthScale, heightScale); imageSize.clampToMinimumSize(minimumSize); ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f)); return imageSize; }
const String& CachedScript::script() { ASSERT(!isPurgeable()); if (!m_script && m_data) { m_script = m_decoder->decode(m_data->data(), encodedSize()); m_script.append(m_decoder->flush()); #if ENABLE(MEMORY_OUT_HANDLING) // If we fail to decode the script (because of lack of memory) // and return an empty string the Lexer will barf. It // assumes that the data ptr is not going to be null. if (encodedSize() > 0 && m_script.length() == 0) m_script = " "; #endif setDecodedSize(m_script.sizeInBytes()); } #if !ENABLE(MEMORY_OUT_HANDLING) // If memory out handling is enabled, don't delete the decoded data. // JS parser assumes that if a script has been decoded once we'll always // be able to decode it. m_decodedDataDeletionTimer.startOneShot(0); #endif return m_script; }
IntRect CachedImage::imageRect(float multiplier) const { ASSERT(!isPurgeable()); if (!m_image) return IntRect(); if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight())) return m_image->rect(); float widthMultiplier = (m_image->hasRelativeWidth() ? 1.0f : multiplier); float heightMultiplier = (m_image->hasRelativeHeight() ? 1.0f : multiplier); // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. bool hasWidth = m_image->rect().width() > 0; bool hasHeight = m_image->rect().height() > 0; int width = static_cast<int>(m_image->rect().width() * widthMultiplier); int height = static_cast<int>(m_image->rect().height() * heightMultiplier); if (hasWidth) width = max(1, width); if (hasHeight) height = max(1, height); int x = static_cast<int>(m_image->rect().x() * widthMultiplier); int y = static_cast<int>(m_image->rect().y() * heightMultiplier); return IntRect(x, y, width, height); }
LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType) { ASSERT(!isPurgeable()); if (!m_image) return LayoutSize(); LayoutSize imageSize(m_image->size()); #if ENABLE(CSS_IMAGE_ORIENTATION) if (renderer && m_image->isBitmapImage()) { ImageOrientationDescription orientationDescription(renderer->shouldRespectImageOrientation(), renderer->style().imageOrientation()); if (orientationDescription.respectImageOrientation() == RespectImageOrientation) imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation(orientationDescription)); } #else if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)) #if !PLATFORM(IOS) imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); #else { // On iOS, the image may have been subsampled to accommodate our size restrictions. However // we should tell the renderer what the original size was. imageSize = LayoutSize(toBitmapImage(m_image.get())->originalSizeRespectingOrientation()); } else if (m_image->isBitmapImage())
const String& CachedScript::script() { ASSERT(!isPurgeable()); if (!m_script && m_data) { m_script = m_decoder->decodeAndFlush(m_data->data(), encodedSize()); setDecodedSize(m_script.sizeInBytes()); } m_decodedDataDeletionTimer.restart(); return m_script; }
Image* CachedImage::image() const { ASSERT(!isPurgeable()); if (errorOccurred() && m_shouldPaintBrokenImage) return brokenImage(); if (m_image) return m_image.get(); return Image::nullImage(); }
const String& CachedScript::script() { ASSERT(!isPurgeable()); if (!m_script && m_data) { m_script = m_decoder->decode(m_data->data(), encodedSize()); m_script += m_decoder->flush(); setDecodedSize(m_script.length() * sizeof(UChar)); } m_decodedDataDeletionTimer.startOneShot(0); return m_script; }
Image* CachedImage::image() const { ASSERT(!isPurgeable()); if (m_errorOccurred) return brokenImage(); if (m_image) return m_image.get(); return nullImage(); }
const String& CachedScript::script() { ASSERT(!isPurgeable()); if (!m_script && m_data) { m_script = m_decoder->decode(m_data->data(), encodedSize()); m_script.append(m_decoder->flush()); setDecodedSize(m_script.sizeInBytes()); } m_decodedDataDeletionTimer.startOneShot(0); return m_script; }
const String CachedCSSStyleSheet::sheetText(bool enforceMIMEType, bool* hasValidMIMEType) const { ASSERT(!isPurgeable()); if (!m_data || m_data->isEmpty() || !canUseSheet(enforceMIMEType, hasValidMIMEType)) return String(); if (!m_decodedSheetText.isNull()) return m_decodedSheetText; // Don't cache the decoded text, regenerating is cheap and it can use quite a bit of memory String sheetText = m_decoder->decode(m_data->data(), m_data->size()); sheetText += m_decoder->flush(); return sheetText; }
Image* CachedImage::imageForRenderer(const RenderObject* renderer) { ASSERT(!isPurgeable()); if (errorOccurred() && m_shouldPaintBrokenImage) { // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() // when they need the real, deviceScaleFactor-appropriate broken image icon. return brokenImage(); } if (m_image) return lookupImageForRenderer(renderer); return Image::nullImage(); }
blink::Image* ImageResource::image() { ASSERT(!isPurgeable()); if (errorOccurred()) { // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage() // when they need the real, deviceScaleFactor-appropriate broken image icon. return brokenImage(1).first; } if (m_image) return m_image.get(); return blink::Image::nullImage(); }
const String& ScriptResource::script() { ASSERT(!isPurgeable()); ASSERT(isLoaded()); if (!m_script && m_data) { String script = decodedText(); m_data.clear(); // We lie a it here and claim that script counts as encoded data (even though it's really decoded data). // That's because the MemoryCache thinks that it can clear out decoded data by calling destroyDecodedData(), // but we can't destroy script in destroyDecodedData because that's our only copy of the data! setEncodedSize(script.sizeInBytes()); m_script = AtomicString(script); } return m_script.string(); }
Image* CachedImage::image(bool brokenImageNeeded) const // CAPP_WEB_HIDE_BROKEN_IMAGE { ASSERT(!isPurgeable()); // CAPP_WEB_HIDE_BROKEN_IMAGE if (!brokenImageNeeded) { if (m_image&&!errorOccurred()) return m_image.get(); return Image::nullImage(); } // CAPP_WEB_HIDE_BROKEN_IMAGE_END if (errorOccurred() && m_shouldPaintBrokenImage) return brokenImage(); if (m_image) return m_image.get(); return Image::nullImage(); }
IntSize CachedImage::imageSize(float multiplier) const { ASSERT(!isPurgeable()); if (!m_image) return IntSize(); if (multiplier == 1.0f) return m_image->size(); // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. bool hasWidth = m_image->size().width() > 0; bool hasHeight = m_image->size().height() > 0; int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier); int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier); if (hasWidth) width = max(1, width); if (hasHeight) height = max(1, height); return IntSize(width, height); }