Bug 76686 - HistoryItem not updated properly when a form submission begins before a previous form submission has finished
Summary: HistoryItem not updated properly when a form submission begins before a previ...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: History (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Darin Fisher (:fishd, Google)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-01-19 22:52 PST by Darin Fisher (:fishd, Google)
Modified: 2012-04-02 13:14 PDT (History)
12 users (show)

See Also:


Attachments
layout tests (3.05 KB, patch)
2012-03-12 11:15 PDT, Darin Fisher (:fishd, Google)
no flags Details | Formatted Diff | Diff
v1 patch (6.51 KB, patch)
2012-03-15 16:30 PDT, Darin Fisher (:fishd, Google)
webkit.review.bot: commit-queue-
Details | Formatted Diff | Diff
v2 patch (6.51 KB, patch)
2012-03-16 23:11 PDT, Darin Fisher (:fishd, Google)
no flags Details | Formatted Diff | Diff
v3 patch (6.82 KB, patch)
2012-03-19 14:08 PDT, Darin Fisher (:fishd, Google)
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Darin Fisher (:fishd, Google) 2012-01-19 22:52:28 PST
HistoryItem not updated properly when a form submission begins before a previous form submission has finished

Given content such as the following:

a.php:
  <form method="post" action="b.php">...</form>

b.php:
  <form method="post" action="c.php">...</form>
  <script>document.forms[0].submit()</script>

c.php:
  <b>hello world</b>

The submission to c.php that occurs while b.php is still loading (following the form submission from a.php to b.php) results in WebKit failing to properly update the HistoryItem.  While the browser will think it is on c.php, navigating away from c.php and then back again will actually cause a form submission to b.php to be repeated!

I believe this occurs because FrameLoader::loadPostRequest() fails to call DocumentLoader::setIsClientRedirect() in the same manner that FrameLoader::loadURL() does.  It should do so after calling loadWithNavigationAction() based on the prior value of m_quickRedirectComing.  As a result, HistoryController::updateForRedirectWithLockedBackForwardList() does not see the isClientRedirect() flag set to true, and it thus fails to call updateCurrentItem().

Incidentally, this bug does not exist if the testcase is placed in a subframe.  That appears to be due to the code in updateForRedirectWithLockedBackForwardList(), which will re-create the HistoryItems for subframes only.  I'm not sure what to make of that.
Comment 1 Darin Fisher (:fishd, Google) 2012-03-12 11:15:58 PDT
Created attachment 131357 [details]
layout tests

Here's a pair of layout tests for this bug.  One uses the GET method and the other uses the POST method.  WebKit currently fails the POST test.  In both cases, the session history entry for the initial page should be replaced with a session history entry for the second page, but that doesn't happen in the POST case.
Comment 2 Darin Fisher (:fishd, Google) 2012-03-12 11:17:31 PDT
The error diff for the POST test looks like this:

 ============== Back Forward List ==============
-        (file test):fast/history/resources/form-submission-before-load-page2.html  **nav target**
+        (file test):fast/history/form-submission-before-load-post.html  **nav target**
 curr->  (file test):fast/history/resources/form-submission-before-load-page3.html  **nav target**
 ===============================================
Comment 3 Darin Fisher (:fishd, Google) 2012-03-15 16:30:01 PDT
Created attachment 132143 [details]
v1 patch

Here's a very simple patch that corrects the bug.  This copies logic from
the end of FrameLoader::loadURL().  I'm not super keen on just copying that
code, but at least it is a small amount of code.  I considered moving this
code into loadWithNavigationAction, but I couldn't convince myself that it
was correct to run it in all cases as loadWithNavigationAction gets called
elsewhere under different conditions.

I've included a layout test for the case that passed before my change,
namely that of using GET instead of POST.
Comment 4 WebKit Review Bot 2012-03-15 17:38:20 PDT
Comment on attachment 132143 [details]
v1 patch

Attachment 132143 [details] did not pass chromium-ews (chromium-xvfb):
Output: http://queues.webkit.org/results/11961070

New failing tests:
fast/loader/form-submission-before-load-get.html
fast/loader/form-submission-before-load-post.html
Comment 5 Darin Fisher (:fishd, Google) 2012-03-16 23:11:03 PDT
Created attachment 132447 [details]
v2 patch

Now, with corrected test expectations.
Comment 6 Darin Fisher (:fishd, Google) 2012-03-19 14:08:03 PDT
Created attachment 132665 [details]
v3 patch

With some comments in the test.
Comment 7 Nate Chapin 2012-03-19 14:47:53 PDT
Comment on attachment 132665 [details]
v3 patch

View in context: https://bugs.webkit.org/attachment.cgi?id=132665&action=review

> Source/WebCore/loader/FrameLoader.cpp:2669
> -    } else
> +    } else {
> +        // must grab this now, since this load may stop the previous load and clear this flag
> +        bool isRedirect = m_quickRedirectComing;
>          loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());    
> +        if (isRedirect) {
> +            m_quickRedirectComing = false;
> +            if (m_provisionalDocumentLoader)
> +                m_provisionalDocumentLoader->setIsClientRedirect(true);
> +        }
> +    }

Is there any sense in factoring this logic out into a helper? I notice it's identical to code in loadURL().
Comment 8 Darin Fisher (:fishd, Google) 2012-03-19 15:22:44 PDT
(In reply to comment #7)
> Is there any sense in factoring this logic out into a helper? I notice it's identical to code in loadURL().

I could imagine factoring out the body of the isRedirect branch:

...
    bool isRedirect = m_quickRedirectComing;
    loadWithNavigationAction(workingResourceRequest, action, ...);
    if (isRedirect)
        didClientRedirect();
...

void FrameLoader::didClientRedirect() {
    m_quickRedirectComing = false;
    if (m_provisionalDocumentLoader)
        m_provisionalDocumentLoader->setIsClientRedirect(true);
}

Is that what you had in mind?  Is didClientRedirect a good name for this?
Comment 9 Collabora GTK+ EWS bot 2012-03-19 16:38:42 PDT
Comment on attachment 132665 [details]
v3 patch

Attachment 132665 [details] did not pass gtk-ews (gtk):
Output: http://queues.webkit.org/results/11991054
Comment 10 Darin Fisher (:fishd, Google) 2012-04-02 12:33:11 PDT
Comment on attachment 132665 [details]
v3 patch

As discussed offline with Nate, I'm going to go ahead and commit this.  I think
refactoring is best as a separate task.
Comment 11 WebKit Review Bot 2012-04-02 13:14:24 PDT
Comment on attachment 132665 [details]
v3 patch

Clearing flags on attachment: 132665

Committed r112924: <http://trac.webkit.org/changeset/112924>
Comment 12 WebKit Review Bot 2012-04-02 13:14:30 PDT
All reviewed patches have been landed.  Closing bug.