void ScriptRunner::queueScriptForExecution(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript, ExecutionType executionType) { ASSERT(scriptElement); ASSERT(cachedScript.get()); Element* element = scriptElement->element(); ASSERT(element); ASSERT(element->inDocument()); m_document->incrementLoadEventDelayCount(); switch (executionType) { case ASYNC_EXECUTION: m_scriptsToExecuteSoon.append(PendingScript(element, cachedScript.get())); if (!m_timer.isActive()) m_timer.startOneShot(0); break; case IN_ORDER_EXECUTION: m_scriptsToExecuteInOrder.append(PendingScript(element, cachedScript.get())); break; default: ASSERT_NOT_REACHED(); } }
void ScriptRunner::queueScriptForExecution(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript, ExecutionType executionType) { ASSERT(scriptElement); // Temporary: intended to help debug how null CachedScript objects can even get queued for execution, which // seems to sometimes happen (see http://code.google.com/p/chromium/issues/detail?id=75604 ) if (!cachedScript.get()) CRASH(); Element* element = scriptElement->element(); ASSERT(element); ASSERT(element->inDocument()); m_document->incrementLoadEventDelayCount(); switch (executionType) { case ASYNC_EXECUTION: m_scriptsToExecuteSoon.append(PendingScript(element, cachedScript.get())); if (!m_timer.isActive()) m_timer.startOneShot(0); break; case IN_ORDER_EXECUTION: m_scriptsToExecuteInOrder.append(PendingScript(element, cachedScript.get())); break; default: ASSERT_NOT_REACHED(); } }
void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request) { if (resource->type() == CachedResource::MainResource) { // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations. if (frame()->ownerElement() && m_documentLoader->frameLoader()->stateMachine()->committingFirstRealLoad()) { InitiatorInfo info = { frame()->ownerElement()->localName(), monotonicallyIncreasingTime() }; m_initiatorMap.add(resource.get(), info); } } else { InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime() }; m_initiatorMap.add(resource.get(), info); } }
void ResourceTimingInformation::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request, Frame* frame) { ASSERT(RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()); ASSERT(resource.get()); if (resource->type() == CachedResource::MainResource) { // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations. ASSERT(frame); if (frame->ownerElement()) { InitiatorInfo info = { frame->ownerElement()->localName(), monotonicallyIncreasingTime(), NotYetAdded }; m_initiatorMap.add(resource.get(), info); } } else { InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime(), NotYetAdded }; m_initiatorMap.add(resource.get(), info); } }
CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest& request) { KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url()); #if ENABLE(CACHE_PARTITIONING) request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition()); #endif if (CachedResource* existing = memoryCache()->resourceForRequest(request.resourceRequest())) { if (existing->type() == CachedResource::CSSStyleSheet) return static_cast<CachedCSSStyleSheet*>(existing); memoryCache()->remove(existing); } if (url.string() != request.resourceRequest().url()) request.mutableResourceRequest().setURL(url); CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request.resourceRequest(), request.charset()); memoryCache()->add(userSheet.get()); // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too? userSheet->load(this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck)); return userSheet; }
void ScriptRunner::queueScriptForExecution(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript, ExecutionType executionType) { ASSERT(scriptElement); ASSERT(cachedScript.get()); Element& element = scriptElement->element(); ASSERT(element.inDocument()); m_document.incrementLoadEventDelayCount(); switch (executionType) { case ASYNC_EXECUTION: m_pendingAsyncScripts.add(scriptElement, PendingScript(&element, cachedScript.get())); break; case IN_ORDER_EXECUTION: m_scriptsToExecuteInOrder.append(PendingScript(&element, cachedScript.get())); break; } }
void AsyncScriptRunner::executeScriptSoon(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript) { ASSERT_ARG(scriptElement, scriptElement); Element* element = scriptElement->element(); ASSERT(element); ASSERT(element->inDocument()); m_document->incrementLoadEventDelayCount(); m_scriptsToExecuteSoon.append(PendingScript(element, cachedScript.get())); if (!m_timer.isActive()) m_timer.startOneShot(0); }
void CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest& request, const String& charset) { String encoding; if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet) encoding = charset.isEmpty() ? m_document->charset() : charset; request.setCharset(encoding); request.setForPreload(true); CachedResourceHandle<CachedResource> resource = requestResource(type, request); if (!resource || (m_preloads && m_preloads->contains(resource.get()))) return; resource->increasePreloadCount(); if (!m_preloads) m_preloads = adoptPtr(new ListHashSet<CachedResource*>); m_preloads->add(resource.get()); #if PRELOAD_DEBUG printf("PRELOADING %s\n", resource->url().latin1().data()); #endif }
void ScriptRunner::queueScriptForExecution(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript, ExecutionType executionType) { ASSERT(scriptElement); ASSERT(cachedScript.get()); Element* element = scriptElement->element(); ASSERT(element); ASSERT(element->inDocument()); m_document->incrementLoadEventDelayCount(); switch (executionType) { case ASYNC_EXECUTION: m_pendingAsyncScripts.add(scriptElement, PendingScript(element, cachedScript.get())); break; case IN_ORDER_EXECUTION: m_scriptsToExecuteInOrder.append(PendingScript(element, cachedScript.get())); ActionLogFormat(ActionLog::WRITE_MEMORY, "ScriptRunner-%p-%p", static_cast<void*>(this), static_cast<void*>(scriptElement)); break; } }
CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest& request, const String& charset) { ASSERT(!memoryCache()->resourceForRequest(request.resourceRequest())); LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().elidedString().latin1().data()); CachedResourceHandle<CachedResource> resource = createResource(type, request.mutableResourceRequest(), charset); if (!memoryCache()->add(resource.get())) resource->setOwningCachedResourceLoader(this); #if ENABLE(RESOURCE_TIMING) storeResourceTimingInitiatorInformation(resource, request); #endif return resource; }
CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(const CachedResourceRequest& request, CachedResource* resource) { ASSERT(resource); ASSERT(resource->inCache()); ASSERT(!memoryCache()->disabled()); ASSERT(resource->canUseCacheValidator()); ASSERT(!resource->resourceToRevalidate()); // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below. String url = resource->url(); CachedResourceHandle<CachedResource> newResource = createResource(resource->type(), resource->resourceRequest(), resource->encoding()); LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource); newResource->setResourceToRevalidate(resource); memoryCache()->remove(resource); memoryCache()->add(newResource.get()); #if ENABLE(RESOURCE_TIMING) storeResourceTimingInitiatorInformation(resource, request); #else UNUSED_PARAM(request); #endif return newResource; }
void ImageLoader::updateFromElement() { // If we're not making renderers for the page, then don't load images. We don't want to slow // down the raw HTML parsing case by loading images we don't intend to display. Document* document = m_element->document(); if (!document->renderer()) return; AtomicString attr = m_element->imageSourceURL(); if (attr == m_failedLoadURL) return; // Do not load any image if the 'src' attribute is missing or if it is // an empty string. CachedResourceHandle<CachedImage> newImage = 0; if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) { CachedResourceRequest request(ResourceRequest(document->completeURL(sourceURI(attr)))); request.setInitiator(element()); String crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr); if (!crossOriginMode.isNull()) { StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials; updateRequestForAccessControl(request.mutableResourceRequest(), document->securityOrigin(), allowCredentials); } if (m_loadManually) { bool autoLoadOtherImages = document->cachedResourceLoader()->autoLoadImages(); document->cachedResourceLoader()->setAutoLoadImages(false); newImage = new CachedImage(request.resourceRequest()); newImage->setLoading(true); newImage->setOwningCachedResourceLoader(document->cachedResourceLoader()); document->cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage.get()); document->cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages); } else newImage = document->cachedResourceLoader()->requestImage(request); // If we do not have an image here, it means that a cross-site // violation occurred, or that the image was blocked via Content // Security Policy, or the page is being dismissed. Trigger an // error event if the page is not being dismissed. if (!newImage && !pageIsBeingDismissed(document)) { m_failedLoadURL = attr; m_hasPendingErrorEvent = true; errorEventSender().dispatchEventSoon(this); } else clearFailedLoadURL(); } else if (!attr.isNull()) { // Fire an error event if the url is empty. // FIXME: Should we fire this event asynchronoulsy via errorEventSender()? m_element->dispatchEvent(Event::create(eventNames().errorEvent, false, false)); } CachedImage* oldImage = m_image.get(); if (newImage != oldImage) { if (m_hasPendingBeforeLoadEvent) { beforeLoadEventSender().cancelEvent(this); m_hasPendingBeforeLoadEvent = false; } if (m_hasPendingLoadEvent) { loadEventSender().cancelEvent(this); m_hasPendingLoadEvent = false; } // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute. // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by // this load and we should not cancel the event. // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two. if (m_hasPendingErrorEvent && newImage) { errorEventSender().cancelEvent(this); m_hasPendingErrorEvent = false; } m_image = newImage; m_hasPendingBeforeLoadEvent = !m_element->document()->isImageDocument() && newImage; m_hasPendingLoadEvent = newImage; m_imageComplete = !newImage; if (newImage) { if (!m_element->document()->isImageDocument()) { if (!m_element->document()->hasListenerType(Document::BEFORELOAD_LISTENER)) dispatchPendingBeforeLoadEvent(); else beforeLoadEventSender().dispatchEventSoon(this); } else updateRenderer(); // If newImage is cached, addClient() will result in the load event // being queued to fire. Ensure this happens after beforeload is // dispatched. newImage->addClient(this); } if (oldImage) oldImage->removeClient(this); } if (RenderImageResource* imageResource = renderImageResource()) imageResource->resetAnimation(); // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. updatedHasPendingEvent(); }
void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize) { if (m_inPruneResources) return; TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); int size = m_allResources.size(); // See if we have any purged resources we can evict. for (int i = 0; i < size; i++) { CachedResource* current = m_allResources[i].m_tail; while (current) { CachedResource* prev = current->m_prevInAllResourcesList; if (current->wasPurged()) { ASSERT(!current->hasClients()); ASSERT(!current->isPreloaded()); evict(current); } current = prev; } } if (targetSize && m_deadSize <= targetSize) return; bool canShrinkLRULists = true; for (int i = size - 1; i >= 0; i--) { // Remove from the tail, since this is the least frequently accessed of the objects. CachedResource* current = m_allResources[i].m_tail; // First flush all the decoded data in this queue. while (current) { // Protect 'previous' so it can't get deleted during destroyDecodedData(). CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList; ASSERT(!previous || previous->inCache()); if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) { // Destroy our decoded data. This will remove us from // m_liveDecodedResources, and possibly move us to a different // LRU list in m_allResources. current->destroyDecodedData(); if (targetSize && m_deadSize <= targetSize) return; } // Decoded data may reference other resources. Stop iterating if 'previous' somehow got // kicked out of cache during destroyDecodedData(). if (previous && !previous->inCache()) break; current = previous.get(); } // Now evict objects from this queue. current = m_allResources[i].m_tail; while (current) { CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList; ASSERT(!previous || previous->inCache()); if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) { if (!makeResourcePurgeable(current)) evict(current); if (targetSize && m_deadSize <= targetSize) return; } if (previous && !previous->inCache()) break; current = previous.get(); } // Shrink the vector back down so we don't waste time inspecting // empty LRU lists on future prunes. if (m_allResources[i].m_head) canShrinkLRULists = false; else if (canShrinkLRULists) m_allResources.resize(i); } }
CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request) { KURL url = request.resourceRequest().url(); LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload()); // If only the fragment identifiers differ, it is the same resource. url = MemoryCache::removeFragmentIdentifierIfNeeded(url); if (!url.isValid()) return 0; if (!canRequest(type, url, request.forPreload())) return 0; if (Frame* f = frame()) f->loader()->client()->dispatchWillRequestResource(&request); if (memoryCache()->disabled()) { DocumentResourceMap::iterator it = m_documentResources.find(url.string()); if (it != m_documentResources.end()) { it->value->setOwningCachedResourceLoader(0); m_documentResources.remove(it); } } // See if we can use an existing resource from the cache. CachedResourceHandle<CachedResource> resource; #if ENABLE(CACHE_PARTITIONING) if (document()) request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition()); #endif resource = memoryCache()->resourceForRequest(request.resourceRequest()); const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer()); switch (policy) { case Reload: memoryCache()->remove(resource.get()); // Fall through case Load: resource = loadResource(type, request, request.charset()); break; case Revalidate: resource = revalidateResource(request, resource.get()); break; case Use: memoryCache()->resourceAccessed(resource.get()); notifyLoadedFromMemoryCache(resource.get()); break; } if (!resource) return 0; if (!request.forPreload() || policy != Use) resource->setLoadPriority(request.priority()); if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) { resource->load(this, request.options()); // We don't support immediate loads, but we do support immediate failure. if (resource->errorOccurred()) { if (resource->inCache()) memoryCache()->remove(resource.get()); return 0; } } #if PLATFORM(CHROMIUM) // FIXME: Temporarily leave main resource caching disabled for chromium, see https://bugs.webkit.org/show_bug.cgi?id=107962 // Ensure main resources aren't preloaded, and other main resource loads are removed from cache to prevent reuse. if (type == CachedResource::MainResource) { ASSERT(policy != Use); ASSERT(policy != Revalidate); memoryCache()->remove(resource.get()); if (request.forPreload()) return 0; } #endif if (!request.resourceRequest().url().protocolIsData()) m_validatedURLs.add(request.resourceRequest().url()); ASSERT(resource->url() == url.string()); m_documentResources.set(resource->url(), resource); return resource; }