| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |  | 
| 2 // Use of this source code is governed by a BSD-style license that can be |  | 
| 3 // found in the LICENSE file. |  | 
| 4 |  | 
| 5 /* |  | 
| 6  * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |  | 
| 7  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |  | 
| 8  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. |  | 
| 9  *     (http://www.torchmobile.com/) |  | 
| 10  * |  | 
| 11  * Redistribution and use in source and binary forms, with or without |  | 
| 12  * modification, are permitted provided that the following conditions |  | 
| 13  * are met: |  | 
| 14  * |  | 
| 15  * 1.  Redistributions of source code must retain the above copyright |  | 
| 16  *     notice, this list of conditions and the following disclaimer. |  | 
| 17  * 2.  Redistributions in binary form must reproduce the above copyright |  | 
| 18  *     notice, this list of conditions and the following disclaimer in the |  | 
| 19  *     documentation and/or other materials provided with the distribution. |  | 
| 20  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of |  | 
| 21  *     its contributors may be used to endorse or promote products derived |  | 
| 22  *     from this software without specific prior written permission. |  | 
| 23  * |  | 
| 24  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |  | 
| 25  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |  | 
| 26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |  | 
| 27  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |  | 
| 28  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |  | 
| 29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |  | 
| 30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |  | 
| 31  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |  | 
| 32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |  | 
| 33  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | 
| 34  */ |  | 
| 35 |  | 
| 36 #include "content/renderer/history_controller.h" |  | 
| 37 |  | 
| 38 #include <utility> |  | 
| 39 |  | 
| 40 #include "base/memory/ptr_util.h" |  | 
| 41 #include "content/common/navigation_params.h" |  | 
| 42 #include "content/common/site_isolation_policy.h" |  | 
| 43 #include "content/renderer/render_frame_impl.h" |  | 
| 44 #include "content/renderer/render_view_impl.h" |  | 
| 45 #include "third_party/WebKit/public/web/WebFrameLoadType.h" |  | 
| 46 #include "third_party/WebKit/public/web/WebLocalFrame.h" |  | 
| 47 |  | 
| 48 using blink::WebCachePolicy; |  | 
| 49 using blink::WebFrame; |  | 
| 50 using blink::WebHistoryCommitType; |  | 
| 51 using blink::WebHistoryItem; |  | 
| 52 using blink::WebURLRequest; |  | 
| 53 |  | 
| 54 namespace content { |  | 
| 55 |  | 
| 56 HistoryController::HistoryController(RenderViewImpl* render_view) |  | 
| 57     : render_view_(render_view) { |  | 
| 58   // We don't use HistoryController in OOPIF enabled modes. |  | 
| 59   DCHECK(!SiteIsolationPolicy::UseSubframeNavigationEntries()); |  | 
| 60 } |  | 
| 61 |  | 
| 62 HistoryController::~HistoryController() { |  | 
| 63 } |  | 
| 64 |  | 
| 65 bool HistoryController::GoToEntry( |  | 
| 66     blink::WebLocalFrame* main_frame, |  | 
| 67     std::unique_ptr<HistoryEntry> target_entry, |  | 
| 68     std::unique_ptr<NavigationParams> navigation_params, |  | 
| 69     WebCachePolicy cache_policy) { |  | 
| 70   DCHECK(!main_frame->parent()); |  | 
| 71   HistoryFrameLoadVector same_document_loads; |  | 
| 72   HistoryFrameLoadVector different_document_loads; |  | 
| 73 |  | 
| 74   set_provisional_entry(std::move(target_entry)); |  | 
| 75   navigation_params_ = std::move(navigation_params); |  | 
| 76 |  | 
| 77   if (current_entry_) { |  | 
| 78     RecursiveGoToEntry( |  | 
| 79         main_frame, same_document_loads, different_document_loads); |  | 
| 80   } |  | 
| 81 |  | 
| 82   if (same_document_loads.empty() && different_document_loads.empty()) { |  | 
| 83     // If we don't have any frames to navigate at this point, either |  | 
| 84     // (1) there is no previous history entry to compare against, or |  | 
| 85     // (2) we were unable to match any frames by name. In the first case, |  | 
| 86     // doing a different document navigation to the root item is the only valid |  | 
| 87     // thing to do. In the second case, we should have been able to find a |  | 
| 88     // frame to navigate based on names if this were a same document |  | 
| 89     // navigation, so we can safely assume this is the different document case. |  | 
| 90     different_document_loads.push_back( |  | 
| 91         std::make_pair(main_frame, provisional_entry_->root())); |  | 
| 92   } |  | 
| 93 |  | 
| 94   bool has_main_frame_request = false; |  | 
| 95   for (const auto& item : same_document_loads) { |  | 
| 96     WebFrame* frame = item.first; |  | 
| 97     RenderFrameImpl* render_frame = RenderFrameImpl::FromWebFrame(frame); |  | 
| 98     if (!render_frame) |  | 
| 99       continue; |  | 
| 100     render_frame->SetPendingNavigationParams( |  | 
| 101         base::MakeUnique<NavigationParams>(*navigation_params_.get())); |  | 
| 102     WebURLRequest request = frame->toWebLocalFrame()->requestFromHistoryItem( |  | 
| 103         item.second, cache_policy); |  | 
| 104     frame->toWebLocalFrame()->load( |  | 
| 105         request, blink::WebFrameLoadType::BackForward, item.second, |  | 
| 106         blink::WebHistorySameDocumentLoad); |  | 
| 107     if (frame == main_frame) |  | 
| 108       has_main_frame_request = true; |  | 
| 109   } |  | 
| 110   for (const auto& item : different_document_loads) { |  | 
| 111     WebFrame* frame = item.first; |  | 
| 112     RenderFrameImpl* render_frame = RenderFrameImpl::FromWebFrame(frame); |  | 
| 113     if (!render_frame) |  | 
| 114       continue; |  | 
| 115     render_frame->SetPendingNavigationParams( |  | 
| 116         base::MakeUnique<NavigationParams>(*navigation_params_.get())); |  | 
| 117     WebURLRequest request = frame->toWebLocalFrame()->requestFromHistoryItem( |  | 
| 118         item.second, cache_policy); |  | 
| 119     frame->toWebLocalFrame()->load( |  | 
| 120         request, blink::WebFrameLoadType::BackForward, item.second, |  | 
| 121         blink::WebHistoryDifferentDocumentLoad); |  | 
| 122     if (frame == main_frame) |  | 
| 123       has_main_frame_request = true; |  | 
| 124   } |  | 
| 125 |  | 
| 126   return has_main_frame_request; |  | 
| 127 } |  | 
| 128 |  | 
| 129 void HistoryController::RecursiveGoToEntry( |  | 
| 130     WebFrame* frame, |  | 
| 131     HistoryFrameLoadVector& same_document_loads, |  | 
| 132     HistoryFrameLoadVector& different_document_loads) { |  | 
| 133   DCHECK(provisional_entry_); |  | 
| 134   DCHECK(current_entry_); |  | 
| 135   RenderFrameImpl* render_frame = RenderFrameImpl::FromWebFrame(frame); |  | 
| 136   const WebHistoryItem& new_item = |  | 
| 137       provisional_entry_->GetItemForFrame(render_frame); |  | 
| 138 |  | 
| 139   // Use the last committed history item for the frame rather than |  | 
| 140   // current_entry_, since the latter may not accurately reflect which URL is |  | 
| 141   // currently committed in the frame.  See https://crbug.com/612713#c12. |  | 
| 142   const WebHistoryItem& old_item = render_frame->current_history_item(); |  | 
| 143 |  | 
| 144   if (new_item.isNull()) |  | 
| 145     return; |  | 
| 146 |  | 
| 147   if (old_item.isNull() || |  | 
| 148       new_item.itemSequenceNumber() != old_item.itemSequenceNumber()) { |  | 
| 149     if (!old_item.isNull() && |  | 
| 150         new_item.documentSequenceNumber() == |  | 
| 151             old_item.documentSequenceNumber()) { |  | 
| 152       same_document_loads.push_back(std::make_pair(frame, new_item)); |  | 
| 153 |  | 
| 154       // Returning here (and omitting child frames which have also changed) is |  | 
| 155       // wrong, but not returning here is worse. See the discussion in |  | 
| 156       // NavigationControllerImpl::FindFramesToNavigate for more information. |  | 
| 157       return; |  | 
| 158     } else { |  | 
| 159       different_document_loads.push_back(std::make_pair(frame, new_item)); |  | 
| 160       // For a different document, the subframes will be destroyed, so there's |  | 
| 161       // no need to consider them. |  | 
| 162       return; |  | 
| 163     } |  | 
| 164   } |  | 
| 165 |  | 
| 166   for (WebFrame* child = frame->firstChild(); child; |  | 
| 167        child = child->nextSibling()) { |  | 
| 168     RecursiveGoToEntry(child, same_document_loads, different_document_loads); |  | 
| 169   } |  | 
| 170 } |  | 
| 171 |  | 
| 172 void HistoryController::UpdateForInitialLoadInChildFrame( |  | 
| 173     RenderFrameImpl* frame, |  | 
| 174     const WebHistoryItem& item) { |  | 
| 175   DCHECK_NE(frame->GetWebFrame()->top(), frame->GetWebFrame()); |  | 
| 176   if (!current_entry_) |  | 
| 177     return; |  | 
| 178   if (HistoryEntry::HistoryNode* existing_node = |  | 
| 179           current_entry_->GetHistoryNodeForFrame(frame)) { |  | 
| 180     // Clear the children and any NavigationParams if this commit isn't for |  | 
| 181     // the same item.  Otherwise we might have stale data after a redirect. |  | 
| 182     if (existing_node->item().itemSequenceNumber() != |  | 
| 183         item.itemSequenceNumber()) { |  | 
| 184       existing_node->RemoveChildren(); |  | 
| 185       navigation_params_.reset(); |  | 
| 186     } |  | 
| 187     existing_node->set_item(item); |  | 
| 188     return; |  | 
| 189   } |  | 
| 190   RenderFrameImpl* parent = |  | 
| 191       RenderFrameImpl::FromWebFrame(frame->GetWebFrame()->parent()); |  | 
| 192   if (!parent) |  | 
| 193     return; |  | 
| 194   if (HistoryEntry::HistoryNode* parent_history_node = |  | 
| 195           current_entry_->GetHistoryNodeForFrame(parent)) { |  | 
| 196     parent_history_node->AddChild(item); |  | 
| 197   } |  | 
| 198 } |  | 
| 199 |  | 
| 200 void HistoryController::UpdateForCommit(RenderFrameImpl* frame, |  | 
| 201                                         const WebHistoryItem& item, |  | 
| 202                                         WebHistoryCommitType commit_type, |  | 
| 203                                         bool navigation_within_page) { |  | 
| 204   switch (commit_type) { |  | 
| 205     case blink::WebBackForwardCommit: |  | 
| 206       if (!provisional_entry_) { |  | 
| 207         // The provisional entry may have been discarded due to a navigation in |  | 
| 208         // a different frame.  For main frames, it is not safe to leave the |  | 
| 209         // current_entry_ in place, which may have a cross-site page and will be |  | 
| 210         // included in the PageState for this commit.  Replace it with a new |  | 
| 211         // HistoryEntry corresponding to the commit, and clear any stale |  | 
| 212         // NavigationParams which might point to the wrong entry. |  | 
| 213         // |  | 
| 214         // This will lack any subframe history items that were in the original |  | 
| 215         // provisional entry, but we don't know what those were after discarding |  | 
| 216         // it.  We'll load the default URL in those subframes instead. |  | 
| 217         // |  | 
| 218         // TODO(creis): It's also possible to get here for subframe commits. |  | 
| 219         // We'll leave a stale current_entry_ in that case, but that only causes |  | 
| 220         // an earlier URL to load in the subframe when leaving and coming back, |  | 
| 221         // and only in rare cases.  It does not risk a URL spoof, unlike the |  | 
| 222         // main frame case.  Since this bug is not present in the new |  | 
| 223         // FrameNavigationEntry-based navigation path (https://crbug.com/236848) |  | 
| 224         // we'll wait for that to fix the subframe case. |  | 
| 225         if (frame->IsMainFrame()) { |  | 
| 226           current_entry_.reset(new HistoryEntry(item)); |  | 
| 227           navigation_params_.reset(); |  | 
| 228         } |  | 
| 229 |  | 
| 230         return; |  | 
| 231       } |  | 
| 232 |  | 
| 233       // If the current entry is null, this must be a main frame commit. |  | 
| 234       DCHECK(current_entry_ || frame->IsMainFrame()); |  | 
| 235 |  | 
| 236       // Commit the provisional entry, but only if it is a plausible transition. |  | 
| 237       // Do not commit it if the navigation is in a subframe and the provisional |  | 
| 238       // entry's main frame item does not match the current entry's main frame, |  | 
| 239       // which can happen if multiple forward navigations occur.  In that case, |  | 
| 240       // committing the provisional entry would corrupt it, leading to a URL |  | 
| 241       // spoof.  See https://crbug.com/597322.  (Note that the race in this bug |  | 
| 242       // does not affect main frame navigations, only navigations in subframes.) |  | 
| 243       // |  | 
| 244       // Note that we cannot compare the provisional entry against |item|, since |  | 
| 245       // |item| may have redirected to a different URL and ISN.  We also cannot |  | 
| 246       // compare against the main frame's URL, since that may have changed due |  | 
| 247       // to a replaceState.  (Even origin can change on replaceState in certain |  | 
| 248       // modes.) |  | 
| 249       // |  | 
| 250       // It would be safe to additionally check the ISNs of all parent frames |  | 
| 251       // (and not just the root), but that is less critical because it won't |  | 
| 252       // lead to a URL spoof. |  | 
| 253       if (frame->IsMainFrame() || |  | 
| 254           current_entry_->root().itemSequenceNumber() == |  | 
| 255               provisional_entry_->root().itemSequenceNumber()) { |  | 
| 256         current_entry_ = std::move(provisional_entry_); |  | 
| 257       } |  | 
| 258 |  | 
| 259       // We're guaranteed to have a current entry now. |  | 
| 260       DCHECK(current_entry_); |  | 
| 261 |  | 
| 262       if (HistoryEntry::HistoryNode* node = |  | 
| 263               current_entry_->GetHistoryNodeForFrame(frame)) { |  | 
| 264         // Clear the children and any NavigationParams if this commit isn't for |  | 
| 265         // the same item.  Otherwise we might have stale data from a race. |  | 
| 266         if (node->item().itemSequenceNumber() != item.itemSequenceNumber()) { |  | 
| 267           node->RemoveChildren(); |  | 
| 268           navigation_params_.reset(); |  | 
| 269         } |  | 
| 270 |  | 
| 271         node->set_item(item); |  | 
| 272       } |  | 
| 273       break; |  | 
| 274     case blink::WebStandardCommit: |  | 
| 275       CreateNewBackForwardItem(frame, item, navigation_within_page); |  | 
| 276       break; |  | 
| 277     case blink::WebInitialCommitInChildFrame: |  | 
| 278       UpdateForInitialLoadInChildFrame(frame, item); |  | 
| 279       break; |  | 
| 280     case blink::WebHistoryInertCommit: |  | 
| 281       // Even for inert commits (e.g., location.replace, client redirects), make |  | 
| 282       // sure the current entry gets updated, if there is one. |  | 
| 283       if (current_entry_) { |  | 
| 284         if (HistoryEntry::HistoryNode* node = |  | 
| 285                 current_entry_->GetHistoryNodeForFrame(frame)) { |  | 
| 286           // Inert commits that reset the page without changing the item (e.g., |  | 
| 287           // reloads, location.replace) shouldn't keep the old subtree. |  | 
| 288           if (!navigation_within_page) |  | 
| 289             node->RemoveChildren(); |  | 
| 290           node->set_item(item); |  | 
| 291         } |  | 
| 292       } |  | 
| 293       break; |  | 
| 294     default: |  | 
| 295       NOTREACHED() << "Invalid commit type: " << commit_type; |  | 
| 296   } |  | 
| 297 } |  | 
| 298 |  | 
| 299 HistoryEntry* HistoryController::GetCurrentEntry() { |  | 
| 300   return current_entry_.get(); |  | 
| 301 } |  | 
| 302 |  | 
| 303 WebHistoryItem HistoryController::GetItemForNewChildFrame( |  | 
| 304     RenderFrameImpl* frame) const { |  | 
| 305   if (navigation_params_.get()) { |  | 
| 306     frame->SetPendingNavigationParams( |  | 
| 307         base::MakeUnique<NavigationParams>(*navigation_params_.get())); |  | 
| 308   } |  | 
| 309 |  | 
| 310   if (!current_entry_) |  | 
| 311     return WebHistoryItem(); |  | 
| 312   return current_entry_->GetItemForFrame(frame); |  | 
| 313 } |  | 
| 314 |  | 
| 315 void HistoryController::RemoveChildrenForRedirect(RenderFrameImpl* frame) { |  | 
| 316   if (!provisional_entry_) |  | 
| 317     return; |  | 
| 318   if (HistoryEntry::HistoryNode* node = |  | 
| 319           provisional_entry_->GetHistoryNodeForFrame(frame)) |  | 
| 320     node->RemoveChildren(); |  | 
| 321 } |  | 
| 322 |  | 
| 323 void HistoryController::CreateNewBackForwardItem( |  | 
| 324     RenderFrameImpl* target_frame, |  | 
| 325     const WebHistoryItem& new_item, |  | 
| 326     bool clone_children_of_target) { |  | 
| 327   if (!current_entry_) { |  | 
| 328     current_entry_.reset(new HistoryEntry(new_item)); |  | 
| 329   } else { |  | 
| 330     current_entry_.reset(current_entry_->CloneAndReplace( |  | 
| 331         new_item, clone_children_of_target, target_frame, render_view_)); |  | 
| 332   } |  | 
| 333 } |  | 
| 334 |  | 
| 335 }  // namespace content |  | 
| OLD | NEW | 
|---|