Web Inspector: Resource agent's reference to cached resources should be weak.
authorvsevik@chromium.org <vsevik@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Jul 2012 08:48:01 +0000 (08:48 +0000)
committervsevik@chromium.org <vsevik@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Jul 2012 08:48:01 +0000 (08:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=92108

Reviewed by Pavel Feldman.

Source/WebCore:

NetworkResourcesData now stores a raw pointer to CachedResource.
CachedResource now notifies InspectorInstrumentation that it will be destroyed.
InspectorInstrumentation stores a set of InstrumentingAgents and broadcasts willDestroyCachedResource event to all available resourceAgents.
Destroyed resources content is saved to NetworkResourcesData.
Changed content size variables type in NetworkResourcesData from int to size_t.

Tests: http/tests/inspector/network/cached-resource-destroyed-moved-to-storage.html
       http/tests/inspector/network/cached-resource-destroyed-too-big-discarded.html

* inspector/InspectorController.cpp:
(WebCore::InspectorController::InspectorController):
(WebCore::InspectorController::inspectedPageDestroyed):
* inspector/InspectorInstrumentation.cpp:
(WebCore):
(WebCore::InspectorInstrumentation::willDestroyCachedResourceImpl):
(WebCore::InspectorInstrumentation::registerInstrumentingAgents):
(WebCore::InspectorInstrumentation::unregisterInstrumentingAgents):
* inspector/InspectorInstrumentation.h:
(InspectorInstrumentation):
(WebCore::InspectorInstrumentation::willDestroyCachedResource):
(WebCore):
* inspector/InspectorResourceAgent.cpp:
(WebCore::InspectorResourceAgent::didReceiveData):
(WebCore::InspectorResourceAgent::willDestroyCachedResource):
(WebCore):
(WebCore::InspectorResourceAgent::getResponseBody):
* inspector/InspectorResourceAgent.h:
(InspectorResourceAgent):
* inspector/NetworkResourcesData.cpp:
(WebCore::NetworkResourcesData::ResourceData::ResourceData):
(WebCore::NetworkResourcesData::ResourceData::setContent):
(WebCore::NetworkResourcesData::ResourceData::dataLength):
(WebCore::NetworkResourcesData::ResourceData::appendData):
(WebCore::NetworkResourcesData::ResourceData::decodeDataToContent):
(WebCore::NetworkResourcesData::setResourceContent):
(WebCore::NetworkResourcesData::maybeAddResourceData):
(WebCore::NetworkResourcesData::maybeDecodeDataToContent):
(WebCore::NetworkResourcesData::removeCachedResource):
(WebCore):
(WebCore::NetworkResourcesData::setResourcesDataSizeLimits):
(WebCore::NetworkResourcesData::ensureFreeSpace):
* inspector/NetworkResourcesData.h:
(ResourceData):
(WebCore::NetworkResourcesData::ResourceData::base64Encoded):
(WebCore::NetworkResourcesData::ResourceData::cachedResource):
(NetworkResourcesData):
* loader/cache/CachedResource.cpp:
(WebCore::CachedResource::~CachedResource):
(WebCore::CachedResource::removeClient):
(WebCore::CachedResource::deleteIfPossible):
* loader/cache/CachedResource.h:
(CachedResource):
* loader/cache/CachedResourceLoader.cpp:
(WebCore::CachedResourceLoader::garbageCollectDocumentResourcesTimerFired):
(WebCore::CachedResourceLoader::garbageCollectDocumentResources):
(WebCore::CachedResourceLoader::clearPreloads):
* loader/cache/CachedResourceLoader.h:
(CachedResourceLoader):
* loader/cache/MemoryCache.cpp:
(WebCore::MemoryCache::evict):
* testing/InternalSettings.cpp:
* testing/Internals.cpp:
(WebCore::Internals::garbageCollectDocumentResources):
(WebCore):
* testing/Internals.h:
(Internals):

LayoutTests:

* http/tests/inspector/network/cached-resource-destroyed-moved-to-storage-expected.txt: Added.
* http/tests/inspector/network/cached-resource-destroyed-moved-to-storage.html: Added.
* http/tests/inspector/network/cached-resource-destroyed-too-big-discarded-expected.txt: Added.
* http/tests/inspector/network/cached-resource-destroyed-too-big-discarded.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@123715 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/inspector/network/cached-resource-destroyed-moved-to-storage-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/inspector/network/cached-resource-destroyed-moved-to-storage.html [new file with mode: 0644]
LayoutTests/http/tests/inspector/network/cached-resource-destroyed-too-big-discarded-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/inspector/network/cached-resource-destroyed-too-big-discarded.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/inspector/InspectorController.cpp
Source/WebCore/inspector/InspectorInstrumentation.cpp
Source/WebCore/inspector/InspectorInstrumentation.h
Source/WebCore/inspector/InspectorResourceAgent.cpp
Source/WebCore/inspector/InspectorResourceAgent.h
Source/WebCore/inspector/NetworkResourcesData.cpp
Source/WebCore/inspector/NetworkResourcesData.h
Source/WebCore/loader/cache/CachedResource.cpp
Source/WebCore/loader/cache/CachedResource.h
Source/WebCore/loader/cache/CachedResourceLoader.cpp
Source/WebCore/loader/cache/CachedResourceLoader.h
Source/WebCore/loader/cache/MemoryCache.cpp
Source/WebCore/testing/InternalSettings.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h

index 31479b8..1f65ff2 100644 (file)
@@ -1,3 +1,15 @@
+2012-07-26  Vsevolod Vlasov  <vsevik@chromium.org>
+
+        Web Inspector: Resource agent's reference to cached resources should be weak.
+        https://bugs.webkit.org/show_bug.cgi?id=92108
+
+        Reviewed by Pavel Feldman.
+
+        * http/tests/inspector/network/cached-resource-destroyed-moved-to-storage-expected.txt: Added.
+        * http/tests/inspector/network/cached-resource-destroyed-moved-to-storage.html: Added.
+        * http/tests/inspector/network/cached-resource-destroyed-too-big-discarded-expected.txt: Added.
+        * http/tests/inspector/network/cached-resource-destroyed-too-big-discarded.html: Added.
+
 2012-07-26  Shinya Kawanaka  <shinyak@chromium.org>
 
         Add UserAgentShadowDOM to FormControlElement just before ading UserShadowDOM
diff --git a/LayoutTests/http/tests/inspector/network/cached-resource-destroyed-moved-to-storage-expected.txt b/LayoutTests/http/tests/inspector/network/cached-resource-destroyed-moved-to-storage-expected.txt
new file mode 100644 (file)
index 0000000..73b41eb
--- /dev/null
@@ -0,0 +1,11 @@
+CONSOLE MESSAGE: line 16: Done1.
+CONSOLE MESSAGE: line 26: Done2.
+Tests content is moved from cached resource to resource agent's data storage when cached resource is destroyed.
+
+Bug 92108  
+http://127.0.0.1:8000/inspector/network/resources/resource.php?type=image&random=1&size=400
+request.type: image
+request.content.length after requesting content: 536
+Releasing cached resource.
+request.content.length after requesting content: 536
+
diff --git a/LayoutTests/http/tests/inspector/network/cached-resource-destroyed-moved-to-storage.html b/LayoutTests/http/tests/inspector/network/cached-resource-destroyed-moved-to-storage.html
new file mode 100644 (file)
index 0000000..29e88ec
--- /dev/null
@@ -0,0 +1,109 @@
+<html>
+<head>
+<script src="../inspector-test.js"></script>
+<script src="../network-test.js"></script>
+<script>
+var image;
+function loadFirstImage() {
+    image = new Image();
+    image.onload = firstImageLoaded;
+    document.body.appendChild(image);
+    image.src = "resources/resource.php?type=image&random=1&size=400";
+}
+
+function firstImageLoaded()
+{
+    console.log("Done1.");
+}
+
+function loadSecondImage() {
+    image.onload = secondImageLoaded;
+    image.src = "resources/resource.php?type=image&random=1&size=200";
+}
+
+function secondImageLoaded()
+{
+    console.log("Done2.");
+}
+
+function forceCachedResourceLoaderGC() {
+    if (window.internals)
+        window.internals.garbageCollectDocumentResources();
+}
+
+function test()
+{
+    // Since this test could be run together with other inspector backend cache
+    // tests, we need to reset size limits to default ones.
+    InspectorTest.resetInspectorResourcesData(step1);
+
+    var imageRequest;
+    function step1()
+    {
+        InspectorTest.addConsoleSniffer(step2);
+        InspectorTest.evaluateInPage("loadFirstImage()");
+    }
+
+    function step2()
+    {
+        imageRequest = WebInspector.panels.network.requests[WebInspector.panels.network.requests.length - 1];
+        imageRequest.requestContent(step3);
+    }
+
+    var originalContentLength;
+    function step3()
+    {
+        InspectorTest.addResult(imageRequest.url);
+        InspectorTest.addResult("request.type: " + imageRequest.type);
+        InspectorTest.addResult("request.content.length after requesting content: " + imageRequest.content.length);
+        originalContentLength = imageRequest.content.length;
+        InspectorTest.assertTrue(imageRequest.content.length > 0, "No content before destroying CachedResource.");
+
+        InspectorTest.addResult("Releasing cached resource.");
+        // Loading another image to the same image element so that the original image cached resource is released.
+        InspectorTest.addConsoleSniffer(step4);
+        InspectorTest.evaluateInPage("loadSecondImage()");
+    }
+
+    function step4(msg)
+    {
+        // Disable-enable cache to force MemoryCache::evictResources().
+        NetworkAgent.setCacheDisabled(true, step5);
+    }
+
+    function step5(msg)
+    {
+        NetworkAgent.setCacheDisabled(false, step6);
+    }
+
+    function step6()
+    {
+        // Force CachedResourceLoader garbage collector run.
+        InspectorTest.evaluateInPage("forceCachedResourceLoaderGC()");
+
+        // Re-request content now that CachedResource should have been destroyed.
+        delete imageRequest._content;
+        imageRequest.requestContent(step7);
+    }
+
+    function step7()
+    {
+        InspectorTest.addResult("request.content.length after requesting content: " + imageRequest.content.length);
+        originalContentLength = imageRequest.content.length;
+        InspectorTest.assertTrue(imageRequest.content.length === originalContentLength, "Content changed after cached resource was destroyed");
+        InspectorTest.resetInspectorResourcesData(step8);
+    }
+
+    function step8()
+    {
+        InspectorTest.completeTest();
+    }
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Tests content is moved from cached resource to resource agent's data storage when cached resource is destroyed.</p>
+<a href="https://bugs.webkit.org/show_bug.cgi?id=92108">Bug 92108</a>
+</body>
+</html>
+
diff --git a/LayoutTests/http/tests/inspector/network/cached-resource-destroyed-too-big-discarded-expected.txt b/LayoutTests/http/tests/inspector/network/cached-resource-destroyed-too-big-discarded-expected.txt
new file mode 100644 (file)
index 0000000..6a42745
--- /dev/null
@@ -0,0 +1,11 @@
+CONSOLE MESSAGE: line 23: Done1.
+CONSOLE MESSAGE: line 33: Done2.
+Tests cached resource content is discarded when cached resource is destroyed if content size is too big for the resource agent's data storage.
+
+Bug 92108  
+http://127.0.0.1:8000/inspector/network/resources/resource.php?type=image&random=1&size=400
+request.type: image
+request.content.length after requesting content: 536
+Releasing cached resource.
+request.content after requesting content: null
+
diff --git a/LayoutTests/http/tests/inspector/network/cached-resource-destroyed-too-big-discarded.html b/LayoutTests/http/tests/inspector/network/cached-resource-destroyed-too-big-discarded.html
new file mode 100644 (file)
index 0000000..6dc150f
--- /dev/null
@@ -0,0 +1,106 @@
+<html>
+<head>
+<script src="../inspector-test.js"></script>
+<script src="../network-test.js"></script>
+<script>
+var image;
+function loadFirstImage() {
+    if (!window.internals) {
+        console.log("This test can not be run as window.internals is not available.");
+        return;
+    }
+    // Make sure there is no enough space to save content in resource agent's storage.
+    internals.settings.setInspectorResourcesDataSizeLimits(100, 100);
+
+    image = new Image();
+    image.onload = firstImageLoaded;
+    document.body.appendChild(image);
+    image.src = "resources/resource.php?type=image&random=1&size=400";
+}
+
+function firstImageLoaded()
+{
+    console.log("Done1.");
+}
+
+function loadSecondImage() {
+    image.onload = secondImageLoaded;
+    image.src = "resources/resource.php?type=image&random=1&size=200";
+}
+
+function secondImageLoaded()
+{
+    console.log("Done2.");
+}
+
+function forceCachedResourceLoaderGC() {
+    if (window.internals)
+        window.internals.garbageCollectDocumentResources();
+}
+
+function test()
+{
+    var imageRequest;
+    InspectorTest.addConsoleSniffer(step2);
+    InspectorTest.evaluateInPage("loadFirstImage()");
+
+    function step2()
+    {
+        imageRequest = WebInspector.panels.network.requests[WebInspector.panels.network.requests.length - 1];
+        imageRequest.requestContent(step3);
+    }
+
+    function step3()
+    {
+        InspectorTest.addResult(imageRequest.url);
+        InspectorTest.addResult("request.type: " + imageRequest.type);
+        InspectorTest.addResult("request.content.length after requesting content: " + imageRequest.content.length);
+        InspectorTest.assertTrue(imageRequest.content.length > 0, "No content before destroying CachedResource.");
+
+        InspectorTest.addResult("Releasing cached resource.");
+        // Loading another image to the same image element so that the original image cached resource is released.
+        InspectorTest.addConsoleSniffer(step4);
+        InspectorTest.evaluateInPage("loadSecondImage()");
+    }
+
+    function step4(msg)
+    {
+        // Disable-enable cache to force MemoryCache::evictResources().
+        NetworkAgent.setCacheDisabled(true, step5);
+    }
+
+    function step5(msg)
+    {
+        NetworkAgent.setCacheDisabled(false, step6);
+    }
+
+    function step6()
+    {
+        // Force CachedResourceLoader garbage collector run.
+        InspectorTest.evaluateInPage("forceCachedResourceLoaderGC()");
+
+        // Re-request content now that CachedResource should have been destroyed.
+        delete imageRequest._content;
+        imageRequest.requestContent(step7);
+    }
+
+    function step7()
+    {
+        InspectorTest.addResult("request.content after requesting content: " + imageRequest.content);
+        InspectorTest.assertTrue(!imageRequest.content, "Content available after CachedResource was destroyed.");
+        InspectorTest.resetInspectorResourcesData(step8);
+    }
+
+    function step8()
+    {
+        InspectorTest.completeTest();
+    }
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Tests cached resource content is discarded when cached resource is destroyed if content size is too big for the resource agent's data storage.</p>
+<a href="https://bugs.webkit.org/show_bug.cgi?id=92108">Bug 92108</a>
+</body>
+</html>
+
index 228e579..0cfdbfb 100644 (file)
@@ -1,3 +1,77 @@
+2012-07-26  Vsevolod Vlasov  <vsevik@chromium.org>
+
+        Web Inspector: Resource agent's reference to cached resources should be weak.
+        https://bugs.webkit.org/show_bug.cgi?id=92108
+
+        Reviewed by Pavel Feldman.
+
+        NetworkResourcesData now stores a raw pointer to CachedResource.
+        CachedResource now notifies InspectorInstrumentation that it will be destroyed.
+        InspectorInstrumentation stores a set of InstrumentingAgents and broadcasts willDestroyCachedResource event to all available resourceAgents.
+        Destroyed resources content is saved to NetworkResourcesData.
+        Changed content size variables type in NetworkResourcesData from int to size_t.
+
+        Tests: http/tests/inspector/network/cached-resource-destroyed-moved-to-storage.html
+               http/tests/inspector/network/cached-resource-destroyed-too-big-discarded.html
+
+        * inspector/InspectorController.cpp:
+        (WebCore::InspectorController::InspectorController):
+        (WebCore::InspectorController::inspectedPageDestroyed):
+        * inspector/InspectorInstrumentation.cpp:
+        (WebCore):
+        (WebCore::InspectorInstrumentation::willDestroyCachedResourceImpl):
+        (WebCore::InspectorInstrumentation::registerInstrumentingAgents):
+        (WebCore::InspectorInstrumentation::unregisterInstrumentingAgents):
+        * inspector/InspectorInstrumentation.h:
+        (InspectorInstrumentation):
+        (WebCore::InspectorInstrumentation::willDestroyCachedResource):
+        (WebCore):
+        * inspector/InspectorResourceAgent.cpp:
+        (WebCore::InspectorResourceAgent::didReceiveData):
+        (WebCore::InspectorResourceAgent::willDestroyCachedResource):
+        (WebCore):
+        (WebCore::InspectorResourceAgent::getResponseBody):
+        * inspector/InspectorResourceAgent.h:
+        (InspectorResourceAgent):
+        * inspector/NetworkResourcesData.cpp:
+        (WebCore::NetworkResourcesData::ResourceData::ResourceData):
+        (WebCore::NetworkResourcesData::ResourceData::setContent):
+        (WebCore::NetworkResourcesData::ResourceData::dataLength):
+        (WebCore::NetworkResourcesData::ResourceData::appendData):
+        (WebCore::NetworkResourcesData::ResourceData::decodeDataToContent):
+        (WebCore::NetworkResourcesData::setResourceContent):
+        (WebCore::NetworkResourcesData::maybeAddResourceData):
+        (WebCore::NetworkResourcesData::maybeDecodeDataToContent):
+        (WebCore::NetworkResourcesData::removeCachedResource):
+        (WebCore):
+        (WebCore::NetworkResourcesData::setResourcesDataSizeLimits):
+        (WebCore::NetworkResourcesData::ensureFreeSpace):
+        * inspector/NetworkResourcesData.h:
+        (ResourceData):
+        (WebCore::NetworkResourcesData::ResourceData::base64Encoded):
+        (WebCore::NetworkResourcesData::ResourceData::cachedResource):
+        (NetworkResourcesData):
+        * loader/cache/CachedResource.cpp:
+        (WebCore::CachedResource::~CachedResource):
+        (WebCore::CachedResource::removeClient):
+        (WebCore::CachedResource::deleteIfPossible):
+        * loader/cache/CachedResource.h:
+        (CachedResource):
+        * loader/cache/CachedResourceLoader.cpp:
+        (WebCore::CachedResourceLoader::garbageCollectDocumentResourcesTimerFired):
+        (WebCore::CachedResourceLoader::garbageCollectDocumentResources):
+        (WebCore::CachedResourceLoader::clearPreloads):
+        * loader/cache/CachedResourceLoader.h:
+        (CachedResourceLoader):
+        * loader/cache/MemoryCache.cpp:
+        (WebCore::MemoryCache::evict):
+        * testing/InternalSettings.cpp:
+        * testing/Internals.cpp:
+        (WebCore::Internals::garbageCollectDocumentResources):
+        (WebCore):
+        * testing/Internals.h:
+        (Internals):
+
 2012-07-26  Luke Macpherson   <macpherson@chromium.org>
 
         Fix null ptr deref in CSSParser::storeVariableDeclaration().
index c3f9f08..ab6e377 100644 (file)
@@ -165,6 +165,8 @@ InspectorController::InspectorController(Page* page, InspectorClient* inspectorC
 #if ENABLE(JAVASCRIPT_DEBUGGER)
     runtimeAgent->setScriptDebugServer(&m_debuggerAgent->scriptDebugServer());
 #endif
+
+    InspectorInstrumentation::registerInstrumentingAgents(m_instrumentingAgents.get());
 }
 
 InspectorController::~InspectorController()
@@ -183,6 +185,7 @@ PassOwnPtr<InspectorController> InspectorController::create(Page* page, Inspecto
 void InspectorController::inspectedPageDestroyed()
 {
     disconnectFrontend();
+    InspectorInstrumentation::unregisterInstrumentingAgents(m_instrumentingAgents.get());
     m_injectedScriptManager->disconnect();
     m_inspectorClient->inspectorDestroyed();
     m_inspectorClient = 0;
index dfaa0a4..0bc16f4 100644 (file)
@@ -80,6 +80,10 @@ static const char* const setTimerEventName = "setTimer";
 static const char* const clearTimerEventName = "clearTimer";
 static const char* const timerFiredEventName = "timerFired";
 
+namespace {
+static HashSet<InstrumentingAgents*>* instrumentingAgentsSet = 0;
+}
+
 int InspectorInstrumentation::s_frontendCounter = 0;
 
 static bool eventHasListeners(const AtomicString& eventType, DOMWindow* window, Node* node, const Vector<EventContext>& ancestors)
@@ -844,6 +848,18 @@ void InspectorInstrumentation::loaderDetachedFromFrameImpl(InstrumentingAgents*
         inspectorPageAgent->loaderDetachedFromFrame(loader);
 }
 
+void InspectorInstrumentation::willDestroyCachedResourceImpl(CachedResource* cachedResource)
+{
+    if (!instrumentingAgentsSet)
+        return;
+    HashSet<InstrumentingAgents*>::iterator end = instrumentingAgentsSet->end();
+    for (HashSet<InstrumentingAgents*>::iterator it = instrumentingAgentsSet->begin(); it != end; ++it) {
+        InstrumentingAgents* instrumentingAgents = *it;
+        if (InspectorResourceAgent* inspectorResourceAgent = instrumentingAgents->inspectorResourceAgent())
+            inspectorResourceAgent->willDestroyCachedResource(cachedResource);
+    }
+}
+
 InspectorInstrumentationCookie InspectorInstrumentation::willWriteHTMLImpl(InstrumentingAgents* instrumentingAgents, unsigned int length, unsigned int startLine, Frame* frame)
 {
     int timelineAgentId = 0;
@@ -1141,6 +1157,22 @@ WTF::ThreadSpecific<InspectorTimelineAgent*>& InspectorInstrumentation::threadSp
     return *instance;
 }
 
+void InspectorInstrumentation::registerInstrumentingAgents(InstrumentingAgents* instrumentingAgents)
+{
+    if (!instrumentingAgentsSet)
+        instrumentingAgentsSet = new HashSet<InstrumentingAgents*>();
+    instrumentingAgentsSet->add(instrumentingAgents);
+}
+
+void InspectorInstrumentation::unregisterInstrumentingAgents(InstrumentingAgents* instrumentingAgents)
+{
+    if (!instrumentingAgentsSet)
+        return;
+    instrumentingAgentsSet->remove(instrumentingAgents);
+    if (!instrumentingAgentsSet->size())
+        delete instrumentingAgentsSet;
+}
+
 InspectorTimelineAgent* InspectorInstrumentation::retrieveTimelineAgent(const InspectorInstrumentationCookie& cookie)
 {
     if (!cookie.first)
index 3287f71..6147f77 100644 (file)
@@ -187,6 +187,7 @@ public:
     static void frameDetachedFromParent(Frame*);
     static void didCommitLoad(Frame*, DocumentLoader*);
     static void loaderDetachedFromFrame(Frame*, DocumentLoader*);
+    static void willDestroyCachedResource(CachedResource*);
 
     static InspectorInstrumentationCookie willWriteHTML(Document*, unsigned int length, unsigned int startLine);
     static void didWriteHTML(const InspectorInstrumentationCookie&, unsigned int endLine);
@@ -263,6 +264,9 @@ public:
     static GeolocationPosition* overrideGeolocationPosition(Page*, GeolocationPosition*);
 #endif
 
+    static void registerInstrumentingAgents(InstrumentingAgents*);
+    static void unregisterInstrumentingAgents(InstrumentingAgents*);
+
 private:
 #if ENABLE(INSPECTOR)
     static WTF::ThreadSpecific<InspectorTimelineAgent*>& threadSpecificTimelineAgentForOrphanEvents();
@@ -359,6 +363,7 @@ private:
     static void frameDetachedFromParentImpl(InstrumentingAgents*, Frame*);
     static void didCommitLoadImpl(InstrumentingAgents*, Page*, DocumentLoader*);
     static void loaderDetachedFromFrameImpl(InstrumentingAgents*, DocumentLoader*);
+    static void willDestroyCachedResourceImpl(CachedResource*);
 
     static InspectorInstrumentationCookie willWriteHTMLImpl(InstrumentingAgents*, unsigned int length, unsigned int startLine, Frame*);
     static void didWriteHTMLImpl(const InspectorInstrumentationCookie&, unsigned int endLine);
@@ -1200,6 +1205,14 @@ inline void InspectorInstrumentation::loaderDetachedFromFrame(Frame* frame, Docu
 #endif
 }
 
+inline void InspectorInstrumentation::willDestroyCachedResource(CachedResource* cachedResource)
+{
+#if ENABLE(INSPECTOR)
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    willDestroyCachedResourceImpl(cachedResource);
+#endif
+}
+
 inline InspectorInstrumentationCookie InspectorInstrumentation::willWriteHTML(Document* document, unsigned int length, unsigned int startLine)
 {
 #if ENABLE(INSPECTOR)
index 8776bb8..65148b2 100644 (file)
@@ -283,7 +283,7 @@ void InspectorResourceAgent::didReceiveData(unsigned long identifier, const char
 
     if (data) {
         NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
-        if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::OtherResource || (resourceData && isErrorStatusCode(resourceData->httpStatusCode()) && (resourceData->cachedResource())))
+        if (m_resourcesData->resourceType(requestId) == InspectorPageAgent::OtherResource || (resourceData && isErrorStatusCode(resourceData->httpStatusCode()) && resourceData->cachedResource()))
             m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
     }
 
@@ -364,6 +364,21 @@ void InspectorResourceAgent::didLoadXHRSynchronously()
     m_loadingXHRSynchronously = false;
 }
 
+void InspectorResourceAgent::willDestroyCachedResource(CachedResource* cachedResource)
+{
+    Vector<String> requestIds = m_resourcesData->removeCachedResource(cachedResource);
+    if (!requestIds.size())
+        return;
+
+    String content;
+    bool base64Encoded;
+    if (!InspectorPageAgent::cachedResourceContent(cachedResource, &content, &base64Encoded))
+        return;
+    Vector<String>::iterator end = requestIds.end();
+    for (Vector<String>::iterator it = requestIds.begin(); it != end; ++it)
+        m_resourcesData->setResourceContent(*it, content, base64Encoded);
+}
+
 void InspectorResourceAgent::applyUserAgentOverride(String* userAgent)
 {
     String userAgentOverride = m_state->getString(ResourceAgentState::userAgentOverride);
@@ -529,7 +544,7 @@ void InspectorResourceAgent::getResponseBody(ErrorString* errorString, const Str
     }
 
     if (resourceData->hasContent()) {
-        *base64Encoded = false;
+        *base64Encoded = resourceData->base64Encoded();
         *content = resourceData->content();
         return;
     }
index 54cf48e..8dab253 100644 (file)
@@ -103,6 +103,7 @@ public:
     void didReceiveXHRResponse(unsigned long identifier);
     void willLoadXHRSynchronously();
     void didLoadXHRSynchronously();
+    void willDestroyCachedResource(CachedResource*);
 
     void applyUserAgentOverride(String* userAgent);
 
index 619a19a..9ef75fb 100644 (file)
 
 namespace {
 // 100MB
-static int maximumResourcesContentSize = 100 * 1000 * 1000;
+static size_t maximumResourcesContentSize = 100 * 1000 * 1000;
 
 // 10MB
-static int maximumSingleResourceContentSize = 10 * 1000 * 1000;
+static size_t maximumSingleResourceContentSize = 10 * 1000 * 1000;
 }
 
 namespace WebCore {
@@ -51,16 +51,19 @@ namespace WebCore {
 NetworkResourcesData::ResourceData::ResourceData(const String& requestId, const String& loaderId)
     : m_requestId(requestId)
     , m_loaderId(loaderId)
+    , m_base64Encoded(false)
     , m_isContentPurged(false)
     , m_type(InspectorPageAgent::OtherResource)
+    , m_cachedResource(0)
 {
 }
 
-void NetworkResourcesData::ResourceData::setContent(const String& content)
+void NetworkResourcesData::ResourceData::setContent(const String& content, bool base64Encoded)
 {
     ASSERT(!hasData());
     ASSERT(!hasContent());
     m_content = content;
+    m_base64Encoded = base64Encoded;
 }
 
 unsigned NetworkResourcesData::ResourceData::removeContent()
@@ -86,12 +89,12 @@ unsigned NetworkResourcesData::ResourceData::purgeContent()
     return removeContent();
 }
 
-int NetworkResourcesData::ResourceData::dataLength() const
+size_t NetworkResourcesData::ResourceData::dataLength() const
 {
     return m_dataBuffer ? m_dataBuffer->size() : 0;
 }
 
-void NetworkResourcesData::ResourceData::appendData(const char* data, int dataLength)
+void NetworkResourcesData::ResourceData::appendData(const char* data, size_t dataLength)
 {
     ASSERT(!hasContent());
     if (!m_dataBuffer)
@@ -100,10 +103,10 @@ void NetworkResourcesData::ResourceData::appendData(const char* data, int dataLe
         m_dataBuffer->append(data, dataLength);
 }
 
-int NetworkResourcesData::ResourceData::decodeDataToContent()
+size_t NetworkResourcesData::ResourceData::decodeDataToContent()
 {
     ASSERT(!hasContent());
-    int dataLength = m_dataBuffer->size();
+    size_t dataLength = m_dataBuffer->size();
     m_content = m_decoder->decode(m_dataBuffer->data(), m_dataBuffer->size());
     m_content += m_decoder->flush();
     m_dataBuffer = nullptr;
@@ -171,12 +174,12 @@ InspectorPageAgent::ResourceType NetworkResourcesData::resourceType(const String
     return resourceData->type();
 }
 
-void NetworkResourcesData::setResourceContent(const String& requestId, const String& content)
+void NetworkResourcesData::setResourceContent(const String& requestId, const String& content, bool base64Encoded)
 {
     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
     if (!resourceData)
         return;
-    int dataLength = 2 * content.length();
+    size_t dataLength = 2 * content.length();
     if (dataLength > m_maximumSingleResourceContentSize)
         return;
     if (resourceData->isContentPurged())
@@ -186,12 +189,12 @@ void NetworkResourcesData::setResourceContent(const String& requestId, const Str
         if (resourceData->hasContent())
             m_contentSize -= resourceData->removeContent();
         m_requestIdsDeque.append(requestId);
-        resourceData->setContent(content);
+        resourceData->setContent(content, base64Encoded);
         m_contentSize += dataLength;
     }
 }
 
-void NetworkResourcesData::maybeAddResourceData(const String& requestId, const char* data, int dataLength)
+void NetworkResourcesData::maybeAddResourceData(const String& requestId, const char* data, size_t dataLength)
 {
     ResourceData* resourceData = m_requestIdToResourceDataMap.get(requestId);
     if (!resourceData)
@@ -217,7 +220,7 @@ void NetworkResourcesData::maybeDecodeDataToContent(const String& requestId)
     if (!resourceData->hasData())
         return;
     m_contentSize += resourceData->decodeDataToContent();
-    int dataLength = 2 * resourceData->content().length();
+    size_t dataLength = 2 * resourceData->content().length();
     if (dataLength > m_maximumSingleResourceContentSize)
         m_contentSize -= resourceData->purgeContent();
 }
@@ -245,6 +248,22 @@ NetworkResourcesData::ResourceData const* NetworkResourcesData::data(const Strin
     return m_requestIdToResourceDataMap.get(requestId);
 }
 
+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->second;
+        if (resourceData->cachedResource() == cachedResource) {
+            resourceData->setCachedResource(0);
+            result.append(it->first);
+        }
+    }
+
+    return result;
+}
+
 void NetworkResourcesData::clear(const String& preservedLoaderId)
 {
     m_requestIdsDeque.clear();
@@ -264,7 +283,7 @@ void NetworkResourcesData::clear(const String& preservedLoaderId)
     m_requestIdToResourceDataMap.swap(preservedMap);
 }
 
-void NetworkResourcesData::setResourcesDataSizeLimits(int maximumResourcesContentSize, int maximumSingleResourceContentSize)
+void NetworkResourcesData::setResourcesDataSizeLimits(size_t maximumResourcesContentSize, size_t maximumSingleResourceContentSize)
 {
     clear();
     m_maximumResourcesContentSize = maximumResourcesContentSize;
@@ -283,7 +302,7 @@ void NetworkResourcesData::ensureNoDataForRequestId(const String& requestId)
     }
 }
 
-bool NetworkResourcesData::ensureFreeSpace(int size)
+bool NetworkResourcesData::ensureFreeSpace(size_t size)
 {
     if (size > m_maximumResourcesContentSize)
         return false;
index 1b172e7..7026d2c 100644 (file)
@@ -29,7 +29,6 @@
 #ifndef NetworkResourcesData_h
 #define NetworkResourcesData_h
 
-#include "CachedResourceHandle.h"
 #include "InspectorPageAgent.h"
 #include "TextResourceDecoder.h"
 
@@ -65,7 +64,9 @@ public:
 
         bool hasContent() const { return !m_content.isNull(); }
         String content() const { return m_content; }
-        void setContent(const String&);
+        void setContent(const String&, bool base64Encoded);
+
+        bool base64Encoded() const { return m_base64Encoded; }
 
         unsigned removeContent();
         bool isContentPurged() const { return m_isContentPurged; }
@@ -86,20 +87,21 @@ public:
         PassRefPtr<SharedBuffer> buffer() const { return m_buffer; }
         void setBuffer(PassRefPtr<SharedBuffer> buffer) { m_buffer = buffer; }
 
-        CachedResource* cachedResource() const { return m_cachedResource.get(); }
+        CachedResource* cachedResource() const { return m_cachedResource; }
         void setCachedResource(CachedResource* cachedResource) { m_cachedResource = cachedResource; }
 
     private:
         bool hasData() const { return m_dataBuffer; }
-        int dataLength() const;
-        void appendData(const char* data, int dataLength);
-        int decodeDataToContent();
+        size_t dataLength() const;
+        void appendData(const char* data, size_t dataLength);
+        size_t decodeDataToContent();
 
         String m_requestId;
         String m_loaderId;
         String m_frameId;
         String m_url;
         String m_content;
+        bool m_base64Encoded;
         RefPtr<SharedBuffer> m_dataBuffer;
         bool m_isContentPurged;
         InspectorPageAgent::ResourceType m_type;
@@ -109,7 +111,7 @@ public:
         RefPtr<TextResourceDecoder> m_decoder;
 
         RefPtr<SharedBuffer> m_buffer;
-        CachedResourceHandle<CachedResource> m_cachedResource;
+        CachedResource* m_cachedResource;
     };
 
     NetworkResourcesData();
@@ -120,27 +122,28 @@ public:
     void responseReceived(const String& requestId, const String& frameId, const ResourceResponse&);
     void setResourceType(const String& requestId, InspectorPageAgent::ResourceType);
     InspectorPageAgent::ResourceType resourceType(const String& requestId);
-    void setResourceContent(const String& requestId, const String& content);
-    void maybeAddResourceData(const String& requestId, const char* data, int dataLength);
+    void setResourceContent(const String& requestId, const String& content, bool base64Encoded = false);
+    void maybeAddResourceData(const String& requestId, const char* data, size_t dataLength);
     void maybeDecodeDataToContent(const String& requestId);
     void addCachedResource(const String& requestId, CachedResource*);
     void addResourceSharedBuffer(const String& requestId, PassRefPtr<SharedBuffer>, const String& textEncodingName);
     ResourceData const* data(const String& requestId);
+    Vector<String> removeCachedResource(CachedResource*);
     void clear(const String& preservedLoaderId = String());
 
-    void setResourcesDataSizeLimits(int maximumResourcesContentSize, int maximumSingleResourceContentSize);
+    void setResourcesDataSizeLimits(size_t maximumResourcesContentSize, size_t maximumSingleResourceContentSize);
 
 private:
     void ensureNoDataForRequestId(const String& requestId);
-    bool ensureFreeSpace(int size);
+    bool ensureFreeSpace(size_t size);
 
     Deque<String> m_requestIdsDeque;
 
     typedef HashMap<String, ResourceData*> ResourceDataMap;
     ResourceDataMap m_requestIdToResourceDataMap;
-    int m_contentSize;
-    int m_maximumResourcesContentSize;
-    int m_maximumSingleResourceContentSize;
+    size_t m_contentSize;
+    size_t m_maximumResourcesContentSize;
+    size_t m_maximumSingleResourceContentSize;
 };
 
 } // namespace WebCore
index 3998efd..5714396 100755 (executable)
@@ -34,6 +34,7 @@
 #include "Document.h"
 #include "Frame.h"
 #include "FrameLoaderClient.h"
+#include "InspectorInstrumentation.h"
 #include "KURL.h"
 #include "Logging.h"
 #include "PurgeableBuffer.h"
@@ -176,7 +177,7 @@ CachedResource::~CachedResource()
     ASSERT(!inCache());
     ASSERT(!m_deleted);
     ASSERT(url().isNull() || memoryCache()->resourceForURL(KURL(ParsedURLString, url())) != this);
-    
+
 #ifndef NDEBUG
     m_deleted = true;
     cachedResourceLeakCounter.decrement();
@@ -430,9 +431,8 @@ void CachedResource::removeClient(CachedResourceClient* client)
         didRemoveClient(client);
     }
 
-    if (canDelete() && !inCache())
-        delete this;
-    else if (!hasClients() && inCache()) {
+    bool deleted = deleteIfPossible();
+    if (!deleted && !hasClients() && inCache()) {
         memoryCache()->removeFromLiveResourcesSize(this);
         memoryCache()->removeFromLiveDecodedResourcesList(this);
         allClientsRemoved();
@@ -449,10 +449,14 @@ void CachedResource::removeClient(CachedResourceClient* client)
     // This object may be dead here.
 }
 
-void CachedResource::deleteIfPossible()
+bool CachedResource::deleteIfPossible()
 {
-    if (canDelete() && !inCache())
+    if (canDelete() && !inCache()) {
+        InspectorInstrumentation::willDestroyCachedResource(this);
         delete this;
+        return true;
+    }
+    return false;
 }
     
 void CachedResource::setDecodedSize(unsigned size)
index 7fbf733..9aa1f6a 100644 (file)
@@ -115,7 +115,7 @@ public:
     void addClient(CachedResourceClient*);
     void removeClient(CachedResourceClient*);
     bool hasClients() const { return !m_clients.isEmpty() || !m_clientsAwaitingCallback.isEmpty(); }
-    void deleteIfPossible();
+    bool deleteIfPossible();
 
     enum PreloadResult {
         PreloadNotReferenced,
index e1987c3..9a88724 100644 (file)
@@ -676,7 +676,11 @@ void CachedResourceLoader::loadDone()
 void CachedResourceLoader::garbageCollectDocumentResourcesTimerFired(Timer<CachedResourceLoader>* timer)
 {
     ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
+    garbageCollectDocumentResources();
+}
 
+void CachedResourceLoader::garbageCollectDocumentResources()
+{
     typedef Vector<String, 10> StringVector;
     StringVector resourcesToDelete;
 
@@ -813,9 +817,8 @@ void CachedResourceLoader::clearPreloads()
     for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
         CachedResource* res = *it;
         res->decreasePreloadCount();
-        if (res->canDelete() && !res->inCache())
-            delete res;
-        else if (res->preloadResult() == CachedResource::PreloadNotReferenced)
+        bool deleted = res->deleteIfPossible();
+        if (!deleted && res->preloadResult() == CachedResource::PreloadNotReferenced)
             memoryCache()->remove(res);
     }
     m_preloads.clear();
index d82b3bf..5eeb387 100644 (file)
@@ -105,6 +105,7 @@ public:
 
     void removeCachedResource(CachedResource*) const;
     void loadDone();
+    void garbageCollectDocumentResources();
     
     void incrementRequestCount(const CachedResource*);
     void decrementRequestCount(const CachedResource*);
index 299d10a..5dc0474 100644 (file)
@@ -402,8 +402,7 @@ void MemoryCache::evict(CachedResource* resource)
     } else
         ASSERT(m_resources.get(resource->url()) != resource);
 
-    if (resource->canDelete())
-        delete resource;
+    resource->deleteIfPossible();
 }
 
 static inline unsigned fastLog2(unsigned i)
index 531b04f..445b878 100644 (file)
@@ -26,7 +26,6 @@
 #include "config.h"
 #include "InternalSettings.h"
 
-#include "CachedResourceLoader.h"
 #include "Chrome.h"
 #include "ChromeClient.h"
 #include "Document.h"
index 9a95537..e5a90c1 100644 (file)
@@ -1095,6 +1095,19 @@ void Internals::resumeAnimations(Document* document, ExceptionCode& ec) const
     controller->resumeAnimations();
 }
 
+void Internals::garbageCollectDocumentResources(Document* document, ExceptionCode& ec) const
+{
+    if (!document) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+
+    CachedResourceLoader* cachedResourceLoader = document->cachedResourceLoader();
+    if (!cachedResourceLoader)
+        return;
+    cachedResourceLoader->garbageCollectDocumentResources();
+}
+
 void Internals::allowRoundingHacks() const
 {
     settings()->allowRoundingHacks();
index 167b529..d510bb4 100644 (file)
@@ -182,6 +182,8 @@ public:
     void suspendAnimations(Document*, ExceptionCode&) const;
     void resumeAnimations(Document*, ExceptionCode&) const;
 
+    void garbageCollectDocumentResources(Document*, ExceptionCode&) const;
+
     void allowRoundingHacks() const;
 
 #if ENABLE(INSPECTOR)