Changeset 138962 in webkit


Ignore:
Timestamp:
Jan 7, 2013 10:40:55 AM (11 years ago)
Author:
ap@apple.com
Message:

ResourceHandle::willLoadFromCache is evil
https://bugs.webkit.org/show_bug.cgi?id=106147

Reviewed by Brady Eidson.

For back/forward navigations to a page that's a result of form submission, we may
never silently re-submit the form. So, we show a warning dialog when about to re-submit,
but try to load from cache if possible.

This patch changes the logic so that we always try to fetch from cache, without
any preflighting. If cache load fails, we restart the load as a known re-submit.

No behavior change expected, so no tests.

  • html/HTMLAnchorElement.cpp: (WebCore::HTMLAnchorElement::handleClick): Added a FIXME.
  • loader/DocumentLoader.cpp: (WebCore::DocumentLoader::startLoadingMainResource): Amended a FIXME with some information about why this call may still be needed.
  • loader/FrameLoader.h:
  • loader/FrameLoader.cpp: (WebCore::FrameLoader::loadURLIntoChildFrame): Pass an explicit argument for unchanged caching behavior. (WebCore::FrameLoader::reloadWithOverrideEncoding): Added a FIXME. This function can silently re-submit a form. (WebCore::FrameLoader::addExtraFieldsToMainResourceRequest): Added a FIXME about an incorrect use of current load type. (WebCore::FrameLoader::addExtraFieldsToRequest): Make sure that a correct caching policy is used for subresources even if main resource was loaded from cache. We didn't need that before because initial request had wrong extra fields due to a use of m_loadType when it was first called. Removed code to change caching policy for b/f navigations. This function does not have enough context to decide what the policy should be. (WebCore::FrameLoader::loadDifferentDocumentItem): Added an argument telling the function whether it should attempt loading from cache. It should do that on first attempt to navigate to a form submission result, but not if that failed. Pass a correct loadType - m_loadType is one for _previous_ load. Removed a special case for https - we've long stopped prohibiting caching of https resources, and using a resource that's already cached should definitely be allowed. (WebCore::FrameLoader::loadItem): Pass an explicit argument for unchanged caching behavior. (WebCore::FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad): Added.
  • loader/MainResourceLoader.cpp: (WebCore::MainResourceLoader::notifyFinished): Removed a check for m_resource being null, because we were immediately dereferencing it anyway. Call retryAfterFailedCacheOnlyMainResourceLoad() to let FrameLoader restart the navigation.
  • platform/network/ResourceHandle.h:
  • platform/network/blackberry/ResourceHandleBlackBerry.cpp:
  • platform/network/cf/ResourceHandleCFNet.cpp:
  • platform/network/chromium/ResourceHandle.cpp:
  • platform/network/curl/ResourceHandleCurl.cpp:
  • platform/network/mac/ResourceHandleMac.mm:
  • platform/network/qt/ResourceHandleQt.cpp:
  • platform/network/soup/ResourceHandleSoup.cpp:
  • platform/network/win/ResourceHandleWin.cpp: Removed willLoadFromCache() - the new logic is cross-platform.
Location:
trunk/Source/WebCore
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r138960 r138962  
     12013-01-04  Alexey Proskuryakov  <ap@apple.com>
     2
     3        ResourceHandle::willLoadFromCache is evil
     4        https://bugs.webkit.org/show_bug.cgi?id=106147
     5
     6        Reviewed by Brady Eidson.
     7
     8        For back/forward navigations to a page that's a result of form submission, we may
     9        never silently re-submit the form. So, we show a warning dialog when about to re-submit,
     10        but try to load from cache if possible.
     11
     12        This patch changes the logic so that we always try to fetch from cache, without
     13        any preflighting. If cache load fails, we restart the load as a known re-submit.
     14
     15        No behavior change expected, so no tests.
     16
     17        * html/HTMLAnchorElement.cpp: (WebCore::HTMLAnchorElement::handleClick):
     18        Added a FIXME.
     19
     20        * loader/DocumentLoader.cpp: (WebCore::DocumentLoader::startLoadingMainResource):
     21        Amended a FIXME with some information about why this call may still be needed.
     22
     23        * loader/FrameLoader.h:
     24        * loader/FrameLoader.cpp:
     25        (WebCore::FrameLoader::loadURLIntoChildFrame): Pass an explicit argument for unchanged caching behavior.
     26        (WebCore::FrameLoader::reloadWithOverrideEncoding): Added a FIXME. This function
     27        can silently re-submit a form.
     28        (WebCore::FrameLoader::addExtraFieldsToMainResourceRequest): Added a FIXME about
     29        an incorrect use of current load type.
     30        (WebCore::FrameLoader::addExtraFieldsToRequest): Make sure that a correct caching
     31        policy is used for subresources even if main resource was loaded from cache. We
     32        didn't need that before because initial request had wrong extra fields due to a use
     33        of m_loadType when it was first called.
     34        Removed code to change caching policy for b/f navigations. This function does not
     35        have enough context to decide what the policy should be.
     36        (WebCore::FrameLoader::loadDifferentDocumentItem): Added an argument telling the
     37        function whether it should attempt loading from cache. It should do that on first
     38        attempt to navigate to a form submission result, but not if that failed.
     39        Pass a correct loadType - m_loadType is one for _previous_ load.
     40        Removed a special case for https - we've long stopped prohibiting caching of https
     41        resources, and using a resource that's already cached should definitely be allowed.
     42        (WebCore::FrameLoader::loadItem): Pass an explicit argument for unchanged caching behavior.
     43        (WebCore::FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad): Added.
     44
     45        * loader/MainResourceLoader.cpp: (WebCore::MainResourceLoader::notifyFinished):
     46        Removed a check for m_resource being null, because we were immediately dereferencing
     47        it anyway.
     48        Call retryAfterFailedCacheOnlyMainResourceLoad() to let FrameLoader restart the navigation.
     49
     50        * platform/network/ResourceHandle.h:
     51        * platform/network/blackberry/ResourceHandleBlackBerry.cpp:
     52        * platform/network/cf/ResourceHandleCFNet.cpp:
     53        * platform/network/chromium/ResourceHandle.cpp:
     54        * platform/network/curl/ResourceHandleCurl.cpp:
     55        * platform/network/mac/ResourceHandleMac.mm:
     56        * platform/network/qt/ResourceHandleQt.cpp:
     57        * platform/network/soup/ResourceHandleSoup.cpp:
     58        * platform/network/win/ResourceHandleWin.cpp:
     59        Removed willLoadFromCache() - the new logic is cross-platform.
     60
    1612013-01-07  Alberto Garcia  <agarcia@igalia.com>
    262
  • trunk/Source/WebCore/html/HTMLAnchorElement.cpp

    r135690 r138962  
    513513        ResourceRequest request(kurl);
    514514
     515        // FIXME: Why are we not calling addExtraFieldsToMainResourceRequest() if this check fails? It sets many important header fields.
    515516        if (!hasRel(RelationNoReferrer)) {
    516517            String referrer = SecurityPolicy::generateReferrerHeader(document()->referrerPolicy(), kurl, frame->loader()->outgoingReferrer());
  • trunk/Source/WebCore/loader/DocumentLoader.cpp

    r138926 r138962  
    885885    // FIXME: Is there any way the extra fields could have not been added by now?
    886886    // If not, it would be great to remove this line of code.
     887    // Note that currently, some requests may have incorrect extra fields even if this function has been called,
     888    // because we pass a wrong loadType (see FIXME in addExtraFieldsToMainResourceRequest()).
    887889    frameLoader()->addExtraFieldsToMainResourceRequest(m_request);
    888890    m_mainResourceLoader->load(m_request, m_substituteData);
  • trunk/Source/WebCore/loader/FrameLoader.cpp

    r137573 r138962  
    853853        HistoryItem* childItem = parentItem->childItemWithTarget(childFrame->tree()->uniqueName());
    854854        if (childItem) {
    855             childFrame->loader()->loadDifferentDocumentItem(childItem, loadType());
     855            childFrame->loader()->loadDifferentDocumentItem(childItem, loadType(), MayAttemptCacheOnlyLoadForFormSubmissionItem);
    856856            return;
    857857        }
     
    14621462        request.setURL(unreachableURL);
    14631463
     1464    // FIXME: If the resource is a result of form submission and is not cached, the form will be silently resubmitted.
     1465    // We should ask the user for confirmation in this case.
    14641466    request.setCachePolicy(ReturnCacheDataElseLoad);
    14651467
     
    24192421void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request)
    24202422{
     2423    // FIXME: Using m_loadType seems wrong for some callers.
     2424    // If we are only preparing to load the main resource, that is previous load's load type!
    24212425    addExtraFieldsToRequest(request, m_loadType, true);
    24222426}
     
    24252429{
    24262430    // Don't set the cookie policy URL if it's already been set.
    2427     // But make sure to set it on all requests, as it has significance beyond the cookie policy for all protocols (<rdar://problem/6616664>).
     2431    // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>).
    24282432    if (request.firstPartyForCookies().isEmpty()) {
    24292433        if (mainResource && isLoadingMainFrame())
     
    24382442
    24392443    applyUserAgent(request);
    2440    
    2441     // If we inherit cache policy from a main resource, we use the DocumentLoader's
    2442     // original request cache policy for two reasons:
    2443     // 1. For POST requests, we mutate the cache policy for the main resource,
    2444     //    but we do not want this to apply to subresources
    2445     // 2. Delegates that modify the cache policy using willSendRequest: should
    2446     //    not affect any other resources. Such changes need to be done
    2447     //    per request.
     2444
    24482445    if (!mainResource) {
    24492446        if (request.isConditional())
    24502447            request.setCachePolicy(ReloadIgnoringCacheData);
    2451         else if (documentLoader()->isLoadingInAPISense())
    2452             request.setCachePolicy(documentLoader()->originalRequest().cachePolicy());
    2453         else
     2448        else if (documentLoader()->isLoadingInAPISense()) {
     2449            // If we inherit cache policy from a main resource, we use the DocumentLoader's
     2450            // original request cache policy for two reasons:
     2451            // 1. For POST requests, we mutate the cache policy for the main resource,
     2452            //    but we do not want this to apply to subresources
     2453            // 2. Delegates that modify the cache policy using willSendRequest: should
     2454            //    not affect any other resources. Such changes need to be done
     2455            //    per request.
     2456            ResourceRequestCachePolicy mainDocumentOriginalCachePolicy = documentLoader()->originalRequest().cachePolicy();
     2457            // Back-forward navigations try to load main resource from cache only to avoid re-submitting form data, and start over (with a warning dialog) if that fails.
     2458            // This policy is set on initial request too, but should not be inherited.
     2459            ResourceRequestCachePolicy subresourceCachePolicy = (mainDocumentOriginalCachePolicy == ReturnCacheDataDontLoad) ? ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy;
     2460            request.setCachePolicy(subresourceCachePolicy);
     2461        } else
    24542462            request.setCachePolicy(UseProtocolCachePolicy);
     2463
     2464    // FIXME: Other FrameLoader functions have duplicated code for setting cache policy of main request when reloading.
     2465    // It seems better to manage it explicitly than to hide the logic inside addExtraFieldsToRequest().
    24552466    } else if (loadType == FrameLoadTypeReload || loadType == FrameLoadTypeReloadFromOrigin || request.isConditional())
    24562467        request.setCachePolicy(ReloadIgnoringCacheData);
    2457     else if (isBackForwardLoadType(loadType) && m_stateMachine.committedFirstRealDocumentLoad())
    2458         request.setCachePolicy(ReturnCacheDataElseLoad);
    2459        
     2468
    24602469    if (request.cachePolicy() == ReloadIgnoringCacheData) {
    24612470        if (loadType == FrameLoadTypeReload)
     
    30343043// which should be methods of HistoryController and some of which should be
    30353044// methods of FrameLoader.
    3036 void FrameLoader::loadDifferentDocumentItem(HistoryItem* item, FrameLoadType loadType)
     3045void FrameLoader::loadDifferentDocumentItem(HistoryItem* item, FrameLoadType loadType, FormSubmissionCacheLoadPolicy cacheLoadPolicy)
    30373046{
    30383047    // Remember this item so we can traverse any child items as child frames load
     
    30693078        // Make sure to add extra fields to the request after the Origin header is added for the FormData case.
    30703079        // See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion.
    3071         addExtraFieldsToRequest(request, m_loadType, true);
     3080        addExtraFieldsToRequest(request, loadType, true);
    30723081       
    30733082        // FIXME: Slight hack to test if the NSURL cache contains the page we're going to.
     
    30793088        // extremely rare, but in that case the user will get an error on the navigation.
    30803089       
    3081         if (ResourceHandle::willLoadFromCache(request, m_frame))
     3090        if (cacheLoadPolicy == MayAttemptCacheOnlyLoadForFormSubmissionItem) {
     3091            request.setCachePolicy(ReturnCacheDataDontLoad);
    30823092            action = NavigationAction(request, loadType, false);
    3083         else {
    3084             request.setCachePolicy(ReloadIgnoringCacheData);
     3093        } else {
     3094            request.setCachePolicy(ReturnCacheDataElseLoad);
    30853095            action = NavigationAction(request, NavigationTypeFormResubmitted);
    30863096        }
     
    30963106                // If the first load within a frame is a navigation within a back/forward list that was attached
    30973107                // without any of the items being loaded then we should use the default caching policy (<rdar://problem/8131355>).
    3098                 if (m_stateMachine.committedFirstRealDocumentLoad() && !itemURL.protocolIs("https"))
     3108                if (m_stateMachine.committedFirstRealDocumentLoad())
    30993109                    request.setCachePolicy(ReturnCacheDataElseLoad);
    31003110                break;
     
    31073117        }
    31083118
    3109         addExtraFieldsToRequest(request, m_loadType, true);
     3119        addExtraFieldsToRequest(request, loadType, true);
    31103120
    31113121        ResourceRequest requestForOriginalURL(request);
     
    31273137        loadSameDocumentItem(item);
    31283138    else
    3129         loadDifferentDocumentItem(item, loadType);
     3139        loadDifferentDocumentItem(item, loadType, MayAttemptCacheOnlyLoadForFormSubmissionItem);
     3140}
     3141
     3142void FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad()
     3143{
     3144    ASSERT(m_state == FrameStateProvisional);
     3145    ASSERT(!m_loadingFromCachedPage);
     3146    // We only use cache-only loads to avoid resubmitting forms.
     3147    ASSERT(isBackForwardLoadType(m_loadType));
     3148    ASSERT(m_history.provisionalItem()->formData());
     3149    ASSERT(m_history.provisionalItem() == m_requestedHistoryItem.get());
     3150
     3151    FrameLoadType loadType = m_loadType;
     3152    HistoryItem* item = m_history.provisionalItem();
     3153
     3154    stopAllLoaders(ShouldNotClearProvisionalItem);
     3155    loadDifferentDocumentItem(item, loadType, MayNotAttemptCacheOnlyLoadForFormSubmissionItem);
    31303156}
    31313157
  • trunk/Source/WebCore/loader/FrameLoader.h

    r135952 r138962  
    124124    HistoryItem* requestedHistoryItem() const { return m_requestedHistoryItem.get(); }
    125125
     126    void retryAfterFailedCacheOnlyMainResourceLoad();
     127
    126128    static void reportLocalLoadFailed(Frame*, const String& url);
    127129
     
    283285
    284286private:
     287    enum FormSubmissionCacheLoadPolicy {
     288        MayAttemptCacheOnlyLoadForFormSubmissionItem,
     289        MayNotAttemptCacheOnlyLoadForFormSubmissionItem
     290    };
     291
    285292    bool allChildrenAreComplete() const; // immediate children, not all descendants
    286293
     
    288295   
    289296    void loadSameDocumentItem(HistoryItem*);
    290     void loadDifferentDocumentItem(HistoryItem*, FrameLoadType);
     297    void loadDifferentDocumentItem(HistoryItem*, FrameLoadType, FormSubmissionCacheLoadPolicy);
    291298   
    292299    void loadProvisionalItemFromCachedPage();
  • trunk/Source/WebCore/loader/MainResourceLoader.cpp

    r138782 r138962  
    551551{
    552552    ASSERT_UNUSED(resource, m_resource == resource);
    553     if (!m_resource || (!m_resource->errorOccurred() && !m_resource->wasCanceled())) {
     553    ASSERT(m_resource);
     554    if (!m_resource->errorOccurred() && !m_resource->wasCanceled()) {
    554555        didFinishLoading(m_resource->loadFinishTime());
     556        return;
     557    }
     558
     559    if (m_documentLoader->request().cachePolicy() == ReturnCacheDataDontLoad && !m_resource->wasCanceled()) {
     560        frameLoader()->retryAfterFailedCacheOnlyMainResourceLoad();
    555561        return;
    556562    }
  • trunk/Source/WebCore/platform/network/ResourceHandle.h

    r138413 r138962  
    105105    static void loadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data);
    106106
    107     static bool willLoadFromCache(ResourceRequest&, Frame*);
    108107    static void cacheMetadata(const ResourceResponse&, const Vector<char>&);
    109108
  • trunk/Source/WebCore/platform/network/blackberry/ResourceHandleBlackBerry.cpp

    r135243 r138962  
    127127}
    128128
    129 bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*)
    130 {
    131     notImplemented();
    132     return false;
    133 }
    134 
    135129void ResourceHandle::cancel()
    136130{
  • trunk/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp

    r138413 r138962  
    809809}
    810810
    811 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
    812 {
    813     request.setCachePolicy(ReturnCacheDataDontLoad);
    814    
    815     CFURLResponseRef cfResponse = 0;
    816     CFErrorRef cfError = 0;
    817     RetainPtr<CFDataRef> data = adoptCF(CFURLConnectionSendSynchronousRequest(request.cfURLRequest(), &cfResponse, &cfError, request.timeoutInterval()));
    818     bool cached = cfResponse && !cfError;
    819 
    820     if (cfError)
    821         CFRelease(cfError);
    822     if (cfResponse)
    823         CFRelease(cfResponse);
    824 
    825     return cached;
    826 }
    827 
    828811#if PLATFORM(MAC)
    829812void ResourceHandle::schedule(SchedulePair* pair)
  • trunk/Source/WebCore/platform/network/chromium/ResourceHandle.cpp

    r138413 r138962  
    278278
    279279// static
    280 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
    281 {
    282     // This method is used to determine if a POST request can be repeated from
    283     // cache, but you cannot really know until you actually try to read from the
    284     // cache. Even if we checked now, something else could come along and wipe
    285     // out the cache entry by the time we fetch it.
    286     //
    287     // So, we always say yes here, to prevent the FrameLoader from initiating a
    288     // reload. Then in FrameLoaderClientImpl::dispatchWillSendRequest, we
    289     // fix-up the cache policy of the request to force a load from the cache.
    290     //
    291     ASSERT(request.httpMethod() == "POST");
    292     return true;
    293 }
    294 
    295 // static
    296280void ResourceHandle::cacheMetadata(const ResourceResponse& response, const Vector<char>& data)
    297281{
  • trunk/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp

    r138413 r138962  
    167167}
    168168
    169 bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*)
    170 {
    171     notImplemented();
    172     return false;
    173 }
    174 
    175169bool ResourceHandle::loadsBlocked()
    176170{
  • trunk/Source/WebCore/platform/network/mac/ResourceHandleMac.mm

    r138413 r138962  
    358358}
    359359
    360 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
    361 {
    362     request.setCachePolicy(ReturnCacheDataDontLoad);
    363     NSURLResponse *nsURLResponse = nil;
    364     BEGIN_BLOCK_OBJC_EXCEPTIONS;
    365 
    366     [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil];
    367    
    368     END_BLOCK_OBJC_EXCEPTIONS;
    369    
    370     return nsURLResponse;
    371 }
    372 
    373360CFStringRef ResourceHandle::synchronousLoadRunLoopMode()
    374361{
  • trunk/Source/WebCore/platform/network/qt/ResourceHandleQt.cpp

    r132053 r138962  
    126126}
    127127
    128 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame* frame)
    129 {
    130     if (!frame)
    131         return false;
    132 
    133     QNetworkAccessManager* manager = 0;
    134     QAbstractNetworkCache* cache = 0;
    135     if (frame->loader()->networkingContext()) {
    136         manager = frame->loader()->networkingContext()->networkAccessManager();
    137         cache = manager->cache();
    138     }
    139 
    140     if (!cache)
    141         return false;
    142 
    143     QNetworkCacheMetaData data = cache->metaData(request.url());
    144     if (data.isValid()) {
    145         request.setCachePolicy(ReturnCacheDataDontLoad);
    146         return true;
    147     }
    148 
    149     return false;
    150 }
    151 
    152128void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials /*storedCredentials*/, ResourceError& error, ResourceResponse& response, Vector<char>& data)
    153129{
  • trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp

    r138413 r138962  
    11741174}
    11751175
    1176 bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*)
    1177 {
    1178     // Not having this function means that we'll ask the user about re-posting a form
    1179     // even when we go back to a page that's still in the cache.
    1180     notImplemented();
    1181     return false;
    1182 }
    1183 
    11841176void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials /*storedCredentials*/, ResourceError& error, ResourceResponse& response, Vector<char>& data)
    11851177{
  • trunk/Source/WebCore/platform/network/win/ResourceHandleWin.cpp

    r138413 r138962  
    432432}
    433433
    434 bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*)
     434void prefetchDNS(const String&)
    435435{
    436436    notImplemented();
     437}
     438
     439bool ResourceHandle::loadsBlocked()
     440{
    437441    return false;
    438442}
    439443
    440 void prefetchDNS(const String&)
     444void ResourceHandle::platformSetDefersLoading(bool)
    441445{
    442446    notImplemented();
    443447}
    444448
    445 bool ResourceHandle::loadsBlocked()
    446 {
    447     return false;
    448 }
    449 
    450 void ResourceHandle::platformSetDefersLoading(bool)
    451 {
    452     notImplemented();
    453 }
    454 
    455449} // namespace WebCore
Note: See TracChangeset for help on using the changeset viewer.