| Index: third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
|
| diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
|
| index 5ff0d0d3861a31c60532f733a76016d704734229..74005e60f3f56b11b95c5e97942d217c8d46bdb6 100644
|
| --- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
|
| +++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
|
| @@ -32,13 +32,13 @@
|
| #include "core/fetch/FetchInitiatorTypeNames.h"
|
| #include "core/fetch/ImageResource.h"
|
| #include "core/fetch/MemoryCache.h"
|
| -#include "core/fetch/ResourceLoader.h"
|
| #include "core/fetch/ResourceLoadingLog.h"
|
| #include "core/fetch/UniqueIdentifier.h"
|
| #include "platform/Histogram.h"
|
| #include "platform/RuntimeEnabledFeatures.h"
|
| #include "platform/TraceEvent.h"
|
| #include "platform/TracedValue.h"
|
| +#include "platform/exported/WrappedResourceRequest.h"
|
| #include "platform/mhtml/ArchiveResource.h"
|
| #include "platform/mhtml/MHTMLArchive.h"
|
| #include "platform/network/ResourceTimingInfo.h"
|
| @@ -49,6 +49,7 @@
|
| #include "public/platform/WebCachePolicy.h"
|
| #include "public/platform/WebURL.h"
|
| #include "public/platform/WebURLRequest.h"
|
| +#include "public/platform/WebURLResponse.h"
|
| #include "wtf/text/CString.h"
|
| #include "wtf/text/WTFString.h"
|
| #include <memory>
|
| @@ -373,12 +374,47 @@ Resource* ResourceFetcher::resourceForStaticData(const FetchRequest& request, co
|
| return resource;
|
| }
|
|
|
| +void ResourceFetcher::requestSynchronously(WebURLLoader* loader, Resource* resource, const ResourceRequest& request)
|
| +{
|
| + // downloadToFile is not supported for synchronous requests.
|
| + DCHECK(!request.downloadToFile());
|
| + DCHECK(request.priority() == ResourceLoadPriorityHighest);
|
| +
|
| + if (context().defersLoading()) {
|
| + resource->error(ResourceError::cancelledError(resource->url()));
|
| + return;
|
| + }
|
| +
|
| + WebURLResponse responseOut;
|
| + WebURLError errorOut;
|
| + WebData dataOut;
|
| + int64_t encodedDataLength = WebURLLoaderClient::kUnknownEncodedDataLength;
|
| + loader->loadSynchronously(WrappedResourceRequest(request), responseOut, errorOut, dataOut, encodedDataLength);
|
| +
|
| + if (errorOut.reason) {
|
| + didFail(loader, errorOut);
|
| + return;
|
| + }
|
| + didReceiveResponse(loader, responseOut, nullptr);
|
| + if (resource->isLoaded())
|
| + return;
|
| + DCHECK_GE(responseOut.toResourceResponse().encodedBodyLength(), 0);
|
| + if (dataOut.size()) {
|
| + Resource* resource = resourceForLoader(loader);
|
| + context().dispatchDidReceiveData(resource->identifier(), dataOut.data(), dataOut.size(), encodedDataLength);
|
| + resource->setResourceBuffer(dataOut);
|
| + }
|
| + didFinishLoading(loader, monotonicallyIncreasingTime(), encodedDataLength);
|
| +}
|
| +
|
| void ResourceFetcher::moveCachedNonBlockingResourceToBlocking(Resource* resource, const FetchRequest& request)
|
| {
|
| // TODO(yoav): Test that non-blocking resources (video/audio/track) continue to not-block even after being preloaded and discovered.
|
| - if (resource && resource->loader() && resource->isLoadEventBlockingResourceType() && resource->isLinkPreload() && !request.forPreload()) {
|
| - m_nonBlockingLoaders.remove(resource->loader());
|
| - m_loaders.add(resource->loader());
|
| + if (!resource || !resource->isLoadEventBlockingResourceType() || !resource->isLinkPreload() || request.forPreload())
|
| + return;
|
| + if (WebURLLoader* loader = m_resourcesInProgress.get(resource)) {
|
| + m_nonBlockingLoaders.remove(loader);
|
| + m_loaders.add(loader, resource);
|
| }
|
| }
|
|
|
| @@ -497,8 +533,11 @@ Resource* ResourceFetcher::requestResource(FetchRequest& request, const Resource
|
| // isn't at the same priority as the in-flight request, only allow promotions.
|
| // This can happen when a visible image's priority is increased and then another
|
| // reference to the image is parsed (which would be at a lower priority).
|
| - if (request.resourceRequest().priority() > resource->resourceRequest().priority())
|
| + if (request.resourceRequest().priority() > resource->resourceRequest().priority()) {
|
| resource->didChangePriority(request.resourceRequest().priority(), 0);
|
| + if (WebURLLoader* loader = m_resourcesInProgress.get(resource))
|
| + loader->didChangePriority(static_cast<WebURLRequest::Priority>(request.resourceRequest().priority()), 0);
|
| + }
|
| }
|
|
|
| // If only the fragment identifiers differ, it is the same resource.
|
| @@ -915,20 +954,29 @@ ArchiveResource* ResourceFetcher::createArchive(Resource* resource)
|
| return m_archive ? m_archive->mainResource() : nullptr;
|
| }
|
|
|
| -void ResourceFetcher::didFinishLoading(Resource* resource, double finishTime, int64_t encodedDataLength, DidFinishLoadingReason finishReason)
|
| +void ResourceFetcher::didFinishLoadingFirstPartInMultipart(Resource* resource)
|
| {
|
| - TRACE_EVENT_ASYNC_END0("blink.net", "Resource", resource->identifier());
|
| - DCHECK(resource);
|
| -
|
| + DCHECK(m_resourcesInProgress.contains(resource));
|
| + WebURLLoader* loader = m_resourcesInProgress.get(resource);
|
| // When loading a multipart resource, make the loader non-block when
|
| // finishing loading the first part.
|
| - if (finishReason == DidFinishFirstPartInMultipart)
|
| - moveResourceLoaderToNonBlocking(resource->loader());
|
| - else
|
| - removeResourceLoader(resource->loader());
|
| - DCHECK(!m_loaders.contains(resource->loader()));
|
| - DCHECK(finishReason == DidFinishFirstPartInMultipart || !m_nonBlockingLoaders.contains(resource->loader()));
|
| + m_loaders.remove(loader);
|
| + m_nonBlockingLoaders.add(loader, resource);
|
| + didFinishLoading(resource, 0, WebURLLoaderClient::kUnknownEncodedDataLength);
|
| +}
|
|
|
| +void ResourceFetcher::didFinishLoading(WebURLLoader* loader, double finishTime, int64_t encodedDataLength)
|
| +{
|
| + Resource* resource = resourceForLoader(loader);
|
| + DCHECK(resource);
|
| + removeLoader(loader, resource);
|
| + DCHECK(!m_resourcesInProgress.contains(resource));
|
| + didFinishLoading(resource, finishTime, encodedDataLength);
|
| +}
|
| +
|
| +void ResourceFetcher::didFinishLoading(Resource* resource, double finishTime, int64_t encodedDataLength)
|
| +{
|
| + TRACE_EVENT_ASYNC_END0("blink.net", "Resource", resource->identifier());
|
| if (std::unique_ptr<ResourceTimingInfo> info = m_resourceTimingInfoMap.take(resource)) {
|
| if (resource->response().isHTTP() && resource->response().httpStatusCode() < 400) {
|
| populateResourceTiming(info.get(), resource);
|
| @@ -944,15 +992,24 @@ void ResourceFetcher::didFinishLoading(Resource* resource, double finishTime, in
|
| }
|
| }
|
| context().dispatchDidFinishLoading(resource->identifier(), finishTime, encodedDataLength);
|
| - if (finishReason == DidFinishLoading)
|
| + // If this is just the completion of the first part of a multipart response,
|
| + // don't mark the resource as finished yet.
|
| + if (!m_resourcesInProgress.contains(resource))
|
| resource->finish(finishTime);
|
| context().didLoadResource(resource);
|
| }
|
|
|
| -void ResourceFetcher::didFailLoading(Resource* resource, const ResourceError& error)
|
| +void ResourceFetcher::cancelResourceLoad(Resource* resource)
|
| {
|
| + DCHECK(m_resourcesInProgress.contains(resource));
|
| + didFail(m_resourcesInProgress.get(resource), ResourceError::cancelledError(resource->lastResourceRequest().url()));
|
| +}
|
| +
|
| +void ResourceFetcher::didFail(WebURLLoader* loader, const WebURLError& error)
|
| +{
|
| + Resource* resource = resourceForLoader(loader);
|
| TRACE_EVENT_ASYNC_END0("blink.net", "Resource", resource->identifier());
|
| - removeResourceLoader(resource->loader());
|
| + removeLoader(loader, resource);
|
| m_resourceTimingInfoMap.take(const_cast<Resource*>(resource));
|
| bool isInternalRequest = resource->options().initiatorInfo.name == FetchInitiatorTypeNames::internal;
|
| context().dispatchDidFail(resource->identifier(), error, isInternalRequest);
|
| @@ -960,10 +1017,29 @@ void ResourceFetcher::didFailLoading(Resource* resource, const ResourceError& er
|
| context().didLoadResource(resource);
|
| }
|
|
|
| -void ResourceFetcher::didReceiveResponse(Resource* resource, const ResourceResponse& response, WebDataConsumerHandle* rawHandle)
|
| +void ResourceFetcher::didSendData(WebURLLoader* loader, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
|
| +{
|
| + resourceForLoader(loader)->didSendData(bytesSent, totalBytesToBeSent);
|
| +}
|
| +
|
| +void ResourceFetcher::didReceiveCachedMetadata(WebURLLoader* loader, const char* data, int length)
|
| +{
|
| + resourceForLoader(loader)->setSerializedCachedMetadata(data, length);
|
| +}
|
| +
|
| +void ResourceFetcher::didReceiveResponse(WebURLLoader* loader, const WebURLResponse& response)
|
| {
|
| + didReceiveResponse(loader, response, nullptr);
|
| +}
|
| +
|
| +void ResourceFetcher::didReceiveResponse(WebURLLoader* loader, const WebURLResponse& webURLResponse, WebDataConsumerHandle* rawHandle)
|
| +{
|
| + DCHECK(!webURLResponse.isNull());
|
| +
|
| // |rawHandle|'s ownership is transferred to the callee.
|
| std::unique_ptr<WebDataConsumerHandle> handle = wrapUnique(rawHandle);
|
| + Resource* resource = resourceForLoader(loader);
|
| + const ResourceResponse& response = webURLResponse.toResourceResponse();
|
|
|
| if (response.wasFetchedViaServiceWorker()) {
|
| if (resource->options().corsEnabled == IsCORSEnabled && response.wasFallbackRequiredByServiceWorker()) {
|
| @@ -977,48 +1053,46 @@ void ResourceFetcher::didReceiveResponse(Resource* resource, const ResourceRespo
|
| // wasFallbackRequiredByServiceWorker flag is never set when foreign fetch
|
| // handled a request.
|
| request.setSkipServiceWorker(WebURLRequest::SkipServiceWorker::Controlling);
|
| - resource->loader()->restartForServiceWorkerFallback(request);
|
| + removeLoader(loader, resource);
|
| + startRequestForResource(resource, request);
|
| return;
|
| }
|
|
|
| // If the response is fetched via ServiceWorker, the original URL of the response could be different from the URL of the request.
|
| // We check the URL not to load the resources which are forbidden by the page CSP.
|
| // https://w3c.github.io/webappsec-csp/#should-block-response
|
| + resource->initializeServiceWorkerCachedMetadataHandler(context().getSecurityOrigin());
|
| const KURL& originalURL = response.originalURLViaServiceWorker();
|
| if (!originalURL.isEmpty() && !context().allowResponse(resource->getType(), resource->resourceRequest(), originalURL, resource->options())) {
|
| - resource->loader()->didFail(nullptr, ResourceError::cancelledDueToAccessCheckError(originalURL));
|
| + didFail(loader, ResourceError::cancelledDueToAccessCheckError(originalURL));
|
| return;
|
| }
|
| } else if (resource->options().corsEnabled == IsCORSEnabled && !canAccessResponse(resource, response)) {
|
| - resource->loader()->didFail(nullptr, ResourceError::cancelledDueToAccessCheckError(response.url()));
|
| + didFail(loader, ResourceError::cancelledDueToAccessCheckError(response.url()));
|
| return;
|
| }
|
|
|
| context().dispatchDidReceiveResponse(resource->identifier(), response, resource->resourceRequest().frameType(), resource->resourceRequest().requestContext(), resource);
|
| resource->responseReceived(response, std::move(handle));
|
| - if (resource->loader() && response.httpStatusCode() >= 400 && !resource->shouldIgnoreHTTPStatusCodeErrors())
|
| - resource->loader()->didFail(nullptr, ResourceError::cancelledError(response.url()));
|
| + if (response.httpStatusCode() >= 400 && !resource->shouldIgnoreHTTPStatusCodeErrors())
|
| + didFail(loader, ResourceError::cancelledError(response.url()));
|
| }
|
|
|
| -void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength)
|
| +void ResourceFetcher::didReceiveData(WebURLLoader* loader, const char* data, int dataLength, int encodedDataLength, int encodedBodyLength)
|
| {
|
| + RELEASE_ASSERT(dataLength >= 0);
|
| + Resource* resource = resourceForLoader(loader);
|
| context().dispatchDidReceiveData(resource->identifier(), data, dataLength, encodedDataLength);
|
| + resource->addToEncodedBodyLength(encodedBodyLength);
|
| + resource->addToDecodedBodyLength(dataLength);
|
| + resource->appendData(data, dataLength);
|
| }
|
|
|
| -void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength)
|
| +void ResourceFetcher::didDownloadData(WebURLLoader* loader, int dataLength, int encodedDataLength)
|
| {
|
| + Resource* resource = resourceForLoader(loader);
|
| context().dispatchDidDownloadData(resource->identifier(), dataLength, encodedDataLength);
|
| -}
|
| -
|
| -void ResourceFetcher::acceptDataFromThreadedReceiver(unsigned long identifier, const char* data, int dataLength, int encodedDataLength)
|
| -{
|
| - context().dispatchDidReceiveData(identifier, data, dataLength, encodedDataLength);
|
| -}
|
| -
|
| -void ResourceFetcher::moveResourceLoaderToNonBlocking(ResourceLoader* loader)
|
| -{
|
| - m_nonBlockingLoaders.add(loader);
|
| - m_loaders.remove(loader);
|
| + resource->didDownloadData(dataLength);
|
| }
|
|
|
| bool ResourceFetcher::startLoad(Resource* resource)
|
| @@ -1032,39 +1106,55 @@ bool ResourceFetcher::startLoad(Resource* resource)
|
| ResourceRequest request(resource->resourceRequest());
|
| willSendRequest(resource->identifier(), request, ResourceResponse(), resource->options());
|
|
|
| - ResourceLoader* loader = ResourceLoader::create(this, resource);
|
| + storeResourceTimingInitiatorInformation(resource);
|
| + resource->setFetcher(this);
|
| + startRequestForResource(resource, request);
|
| + return true;
|
| +}
|
| +
|
| +void ResourceFetcher::startRequestForResource(Resource* resource, const ResourceRequest& request)
|
| +{
|
| + DCHECK(!m_resourcesInProgress.contains(resource));
|
| + m_resourcesInProgress.add(resource, wrapUnique(Platform::current()->createURLLoader()));
|
| + WebURLLoader* loader = m_resourcesInProgress.get(resource);
|
| if (resource->shouldBlockLoadEvent())
|
| - m_loaders.add(loader);
|
| + m_loaders.add(loader, resource);
|
| else
|
| - m_nonBlockingLoaders.add(loader);
|
| + m_nonBlockingLoaders.add(loader, resource);
|
|
|
| - storeResourceTimingInitiatorInformation(resource);
|
| - resource->setFetcherSecurityOrigin(context().getSecurityOrigin());
|
| - loader->start(request, context().loadingTaskRunner(), context().defersLoading());
|
| - return true;
|
| + loader->setDefersLoading(context().defersLoading());
|
| + loader->setLoadingTaskRunner(context().loadingTaskRunner());
|
| + if (resource->options().synchronousPolicy == RequestSynchronously)
|
| + requestSynchronously(loader, resource, request);
|
| + else
|
| + loader->loadAsynchronously(WrappedResourceRequest(request), this);
|
| }
|
|
|
| -void ResourceFetcher::removeResourceLoader(ResourceLoader* loader)
|
| +void ResourceFetcher::removeLoader(WebURLLoader* loader, Resource* resource)
|
| {
|
| + DCHECK(m_resourcesInProgress.contains(resource));
|
| + DCHECK(m_resourcesInProgress.get(resource) == loader);
|
| + DCHECK(m_loaders.get(loader) == resource || m_nonBlockingLoaders.get(loader) == resource);
|
| if (m_loaders.contains(loader))
|
| m_loaders.remove(loader);
|
| else if (m_nonBlockingLoaders.contains(loader))
|
| m_nonBlockingLoaders.remove(loader);
|
| else
|
| - ASSERT_NOT_REACHED();
|
| + NOTREACHED();
|
| + m_resourcesInProgress.remove(resource);
|
| }
|
|
|
| void ResourceFetcher::stopFetching()
|
| {
|
| - HeapVector<Member<ResourceLoader>> loadersToCancel;
|
| + Vector<WebURLLoader*> loadersToCancel;
|
| for (const auto& loader : m_nonBlockingLoaders)
|
| - loadersToCancel.append(loader);
|
| + loadersToCancel.append(loader.key);
|
| for (const auto& loader : m_loaders)
|
| - loadersToCancel.append(loader);
|
| + loadersToCancel.append(loader.key);
|
|
|
| for (const auto& loader : loadersToCancel) {
|
| if (m_loaders.contains(loader) || m_nonBlockingLoaders.contains(loader))
|
| - loader->cancel();
|
| + didFail(loader, ResourceError::cancelledError(resourceForLoader(loader)->url()));
|
| }
|
| }
|
|
|
| @@ -1076,9 +1166,9 @@ bool ResourceFetcher::isFetching() const
|
| void ResourceFetcher::setDefersLoading(bool defers)
|
| {
|
| for (const auto& loader : m_nonBlockingLoaders)
|
| - loader->setDefersLoading(defers);
|
| + loader.key->setDefersLoading(defers);
|
| for (const auto& loader : m_loaders)
|
| - loader->setDefersLoading(defers);
|
| + loader.key->setDefersLoading(defers);
|
| }
|
|
|
| bool ResourceFetcher::defersLoading() const
|
| @@ -1086,12 +1176,29 @@ bool ResourceFetcher::defersLoading() const
|
| return context().defersLoading();
|
| }
|
|
|
| +void ResourceFetcher::setDefersLoadingForResource(Resource* resource, bool defers)
|
| +{
|
| + DCHECK(resource);
|
| + DCHECK(m_resourcesInProgress.contains(resource));
|
| + m_resourcesInProgress.get(resource)->setDefersLoading(defers);
|
| +}
|
| +
|
| static bool isManualRedirectFetchRequest(const ResourceRequest& request)
|
| {
|
| return request.fetchRedirectMode() == WebURLRequest::FetchRedirectModeManual && request.requestContext() == WebURLRequest::RequestContextFetch;
|
| }
|
|
|
| -bool ResourceFetcher::willFollowRedirect(Resource* resource, ResourceRequest& newRequest, const ResourceResponse& redirectResponse, int64_t encodedDataLength)
|
| +Resource* ResourceFetcher::resourceForLoader(WebURLLoader* loader)
|
| +{
|
| + if (m_loaders.contains(loader))
|
| + return m_loaders.get(loader);
|
| + if (m_nonBlockingLoaders.contains(loader))
|
| + return m_nonBlockingLoaders.get(loader);
|
| + NOTREACHED();
|
| + return nullptr;
|
| +}
|
| +
|
| +bool ResourceFetcher::canFollowRedirect(Resource* resource, ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
|
| {
|
| if (!isManualRedirectFetchRequest(resource->resourceRequest())) {
|
| if (!context().canRequest(resource->getType(), newRequest, newRequest.url(), resource->options(), resource->isUnusedPreload(), FetchRequest::UseDefaultOriginRestrictionForType))
|
| @@ -1112,6 +1219,24 @@ bool ResourceFetcher::willFollowRedirect(Resource* resource, ResourceRequest& ne
|
| if (resource->getType() == Resource::Image && shouldDeferImageLoad(newRequest.url()))
|
| return false;
|
| }
|
| + return true;
|
| +}
|
| +
|
| +void ResourceFetcher::willFollowRedirect(WebURLLoader* loader, WebURLRequest& passedNewRequest, const WebURLResponse& passedRedirectResponse, int64_t encodedDataLength)
|
| +{
|
| + DCHECK(!passedNewRequest.isNull());
|
| + DCHECK(!passedRedirectResponse.isNull());
|
| + ResourceRequest& newRequest(passedNewRequest.toMutableResourceRequest());
|
| + const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceResponse());
|
| + newRequest.setRedirectStatus(ResourceRequest::RedirectStatus::FollowedRedirect);
|
| +
|
| + Resource* resource = resourceForLoader(loader);
|
| + if (!canFollowRedirect(resource, newRequest, redirectResponse)) {
|
| + resource->willNotFollowRedirect();
|
| + if (m_resourcesInProgress.contains(resource))
|
| + didFail(loader, ResourceError::cancelledDueToAccessCheckError(newRequest.url()));
|
| + return;
|
| + }
|
|
|
| ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
|
| if (it != m_resourceTimingInfoMap.end()) {
|
| @@ -1122,7 +1247,7 @@ bool ResourceFetcher::willFollowRedirect(Resource* resource, ResourceRequest& ne
|
| }
|
| newRequest.setAllowStoredCredentials(resource->options().allowCredentials == AllowStoredCredentials);
|
| willSendRequest(resource->identifier(), newRequest, redirectResponse, resource->options());
|
| - return true;
|
| + resource->willFollowRedirect(newRequest, redirectResponse);
|
| }
|
|
|
| void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& newRequest, const ResourceResponse& redirectResponse, const ResourceLoaderOptions& options)
|
| @@ -1133,9 +1258,9 @@ void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest&
|
| void ResourceFetcher::updateAllImageResourcePriorities()
|
| {
|
| TRACE_EVENT0("blink", "ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities");
|
| - for (const auto& documentResource : m_documentResources) {
|
| - Resource* resource = documentResource.value.get();
|
| - if (!resource || !resource->isImage() || !resource->isLoading())
|
| + for (const auto& resourceInProgress : m_resourcesInProgress) {
|
| + Resource* resource = resourceInProgress.key.get();
|
| + if (!resource || !resource->isImage())
|
| continue;
|
|
|
| ResourcePriority resourcePriority = resource->priorityFromObservers();
|
| @@ -1144,6 +1269,7 @@ void ResourceFetcher::updateAllImageResourcePriorities()
|
| continue;
|
|
|
| resource->didChangePriority(resourceLoadPriority, resourcePriority.intraPriorityValue);
|
| + resourceInProgress.value->didChangePriority(static_cast<WebURLRequest::Priority>(resourceLoadPriority), resourcePriority.intraPriorityValue);
|
| TRACE_EVENT_ASYNC_STEP_INTO1("blink.net", "Resource", resource->identifier(), "ChangePriority", "priority", resourceLoadPriority);
|
| context().dispatchDidChangeResourcePriority(resource->identifier(), resourceLoadPriority, resourcePriority.intraPriorityValue);
|
| }
|
| @@ -1324,6 +1450,7 @@ DEFINE_TRACE(ResourceFetcher)
|
| visitor->trace(m_archive);
|
| visitor->trace(m_loaders);
|
| visitor->trace(m_nonBlockingLoaders);
|
| + visitor->trace(m_resourcesInProgress);
|
| visitor->trace(m_documentResources);
|
| visitor->trace(m_preloads);
|
| visitor->trace(m_resourceTimingInfoMap);
|
|
|