| Index: content/renderer/history_controller.cc
|
| diff --git a/content/renderer/history_controller.cc b/content/renderer/history_controller.cc
|
| index c8553ff2e277ecf1fadec475ebd0fdaa588380d2..5de3172d79d57301de45d093d4d6f45d28865a78 100644
|
| --- a/content/renderer/history_controller.cc
|
| +++ b/content/renderer/history_controller.cc
|
| @@ -189,7 +189,36 @@ void HistoryController::UpdateForCommit(RenderFrameImpl* frame,
|
| case blink::WebBackForwardCommit:
|
| if (!provisional_entry_)
|
| return;
|
| - current_entry_.reset(provisional_entry_.release());
|
| +
|
| + // If the current entry is null, this must be a main frame commit.
|
| + DCHECK(current_entry_ || frame->IsMainFrame());
|
| +
|
| + // Commit the provisional entry, but only if it is a plausible transition.
|
| + // Do not commit it if the navigation is in a subframe and the provisional
|
| + // entry's main frame item does not match the current entry's main frame,
|
| + // which can happen if multiple forward navigations occur. In that case,
|
| + // committing the provisional entry would corrupt it, leading to a URL
|
| + // spoof. See https://crbug.com/597322. (Note that the race in this bug
|
| + // does not affect main frame navigations, only navigations in subframes.)
|
| + //
|
| + // Note that we cannot compare the provisional entry against |item|, since
|
| + // |item| may have redirected to a different URL and ISN. We also cannot
|
| + // compare against the main frame's URL, since that may have changed due
|
| + // to a replaceState. (Even origin can change on replaceState in certain
|
| + // modes.)
|
| + //
|
| + // It would be safe to additionally check the ISNs of all parent frames
|
| + // (and not just the root), but that is less critical because it won't
|
| + // lead to a URL spoof.
|
| + if (frame->IsMainFrame() ||
|
| + current_entry_->root().itemSequenceNumber() ==
|
| + provisional_entry_->root().itemSequenceNumber()) {
|
| + current_entry_.reset(provisional_entry_.release());
|
| + }
|
| +
|
| + // We're guaranteed to have a current entry now.
|
| + DCHECK(current_entry_);
|
| +
|
| if (HistoryEntry::HistoryNode* node =
|
| current_entry_->GetHistoryNodeForFrame(frame)) {
|
| node->set_item(item);
|
|
|