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.
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/)
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
36 #include "content/renderer/history_controller.h"
38 #include "content/common/navigation_params.h"
39 #include "content/renderer/render_frame_impl.h"
40 #include "content/renderer/render_view_impl.h"
41 #include "third_party/WebKit/public/web/WebFrameLoadType.h"
42 #include "third_party/WebKit/public/web/WebLocalFrame.h"
44 using blink::WebFrame
;
45 using blink::WebHistoryCommitType
;
46 using blink::WebHistoryItem
;
47 using blink::WebURLRequest
;
51 HistoryController::HistoryController(RenderViewImpl
* render_view
)
52 : render_view_(render_view
) {
55 HistoryController::~HistoryController() {
58 void HistoryController::GoToEntry(
59 blink::WebLocalFrame
* main_frame
,
60 scoped_ptr
<HistoryEntry
> target_entry
,
61 scoped_ptr
<NavigationParams
> navigation_params
,
62 WebURLRequest::CachePolicy cache_policy
) {
63 DCHECK(!main_frame
->parent());
64 HistoryFrameLoadVector same_document_loads
;
65 HistoryFrameLoadVector different_document_loads
;
67 set_provisional_entry(target_entry
.Pass());
68 navigation_params_
= navigation_params
.Pass();
72 main_frame
, same_document_loads
, different_document_loads
);
75 if (same_document_loads
.empty() && different_document_loads
.empty()) {
76 // If we don't have any frames to navigate at this point, either
77 // (1) there is no previous history entry to compare against, or
78 // (2) we were unable to match any frames by name. In the first case,
79 // doing a different document navigation to the root item is the only valid
80 // thing to do. In the second case, we should have been able to find a
81 // frame to navigate based on names if this were a same document
82 // navigation, so we can safely assume this is the different document case.
83 different_document_loads
.push_back(
84 std::make_pair(main_frame
, provisional_entry_
->root()));
87 for (const auto& item
: same_document_loads
) {
88 WebFrame
* frame
= item
.first
;
89 RenderFrameImpl
* render_frame
= RenderFrameImpl::FromWebFrame(frame
);
92 render_frame
->SetPendingNavigationParams(make_scoped_ptr(
93 new NavigationParams(*navigation_params_
.get())));
94 WebURLRequest request
= frame
->toWebLocalFrame()->requestFromHistoryItem(
95 item
.second
, cache_policy
);
96 frame
->toWebLocalFrame()->load(
97 request
, blink::WebFrameLoadType::BackForward
, item
.second
,
98 blink::WebHistorySameDocumentLoad
);
100 for (const auto& item
: different_document_loads
) {
101 WebFrame
* frame
= item
.first
;
102 RenderFrameImpl
* render_frame
= RenderFrameImpl::FromWebFrame(frame
);
105 render_frame
->SetPendingNavigationParams(make_scoped_ptr(
106 new NavigationParams(*navigation_params_
.get())));
107 WebURLRequest request
= frame
->toWebLocalFrame()->requestFromHistoryItem(
108 item
.second
, cache_policy
);
109 frame
->toWebLocalFrame()->load(
110 request
, blink::WebFrameLoadType::BackForward
, item
.second
,
111 blink::WebHistoryDifferentDocumentLoad
);
115 void HistoryController::RecursiveGoToEntry(
117 HistoryFrameLoadVector
& same_document_loads
,
118 HistoryFrameLoadVector
& different_document_loads
) {
119 DCHECK(provisional_entry_
);
120 DCHECK(current_entry_
);
121 RenderFrameImpl
* render_frame
= RenderFrameImpl::FromWebFrame(frame
);
122 const WebHistoryItem
& new_item
=
123 provisional_entry_
->GetItemForFrame(render_frame
);
124 const WebHistoryItem
& old_item
=
125 current_entry_
->GetItemForFrame(render_frame
);
126 if (new_item
.isNull())
129 if (old_item
.isNull() ||
130 new_item
.itemSequenceNumber() != old_item
.itemSequenceNumber()) {
131 if (!old_item
.isNull() &&
132 new_item
.documentSequenceNumber() == old_item
.documentSequenceNumber())
133 same_document_loads
.push_back(std::make_pair(frame
, new_item
));
135 different_document_loads
.push_back(std::make_pair(frame
, new_item
));
139 for (WebFrame
* child
= frame
->firstChild(); child
;
140 child
= child
->nextSibling()) {
141 RecursiveGoToEntry(child
, same_document_loads
, different_document_loads
);
145 void HistoryController::UpdateForInitialLoadInChildFrame(
146 RenderFrameImpl
* frame
,
147 const WebHistoryItem
& item
) {
148 DCHECK_NE(frame
->GetWebFrame()->top(), frame
->GetWebFrame());
151 if (HistoryEntry::HistoryNode
* existing_node
=
152 current_entry_
->GetHistoryNodeForFrame(frame
)) {
153 existing_node
->set_item(item
);
156 RenderFrameImpl
* parent
=
157 RenderFrameImpl::FromWebFrame(frame
->GetWebFrame()->parent());
160 if (HistoryEntry::HistoryNode
* parent_history_node
=
161 current_entry_
->GetHistoryNodeForFrame(parent
)) {
162 parent_history_node
->AddChild(item
);
166 void HistoryController::UpdateForCommit(RenderFrameImpl
* frame
,
167 const WebHistoryItem
& item
,
168 WebHistoryCommitType commit_type
,
169 bool navigation_within_page
) {
170 switch (commit_type
) {
171 case blink::WebBackForwardCommit
:
172 if (!provisional_entry_
)
174 current_entry_
.reset(provisional_entry_
.release());
175 if (HistoryEntry::HistoryNode
* node
=
176 current_entry_
->GetHistoryNodeForFrame(frame
)) {
177 node
->set_item(item
);
180 case blink::WebStandardCommit
:
181 CreateNewBackForwardItem(frame
, item
, navigation_within_page
);
183 case blink::WebInitialCommitInChildFrame
:
184 UpdateForInitialLoadInChildFrame(frame
, item
);
186 case blink::WebHistoryInertCommit
:
187 // Even for inert commits (e.g., location.replace, client redirects), make
188 // sure the current entry gets updated, if there is one.
189 if (current_entry_
) {
190 if (HistoryEntry::HistoryNode
* node
=
191 current_entry_
->GetHistoryNodeForFrame(frame
)) {
192 // Inert commits that reset the page without changing the item (e.g.,
193 // reloads, location.replace) shouldn't keep the old subtree.
194 if (!navigation_within_page
)
195 node
->RemoveChildren();
196 node
->set_item(item
);
201 NOTREACHED() << "Invalid commit type: " << commit_type
;
205 HistoryEntry
* HistoryController::GetCurrentEntry() {
206 return current_entry_
.get();
209 WebHistoryItem
HistoryController::GetItemForNewChildFrame(
210 RenderFrameImpl
* frame
) const {
211 if (navigation_params_
.get()) {
212 frame
->SetPendingNavigationParams(make_scoped_ptr(
213 new NavigationParams(*navigation_params_
.get())));
217 return WebHistoryItem();
218 return current_entry_
->GetItemForFrame(frame
);
221 void HistoryController::RemoveChildrenForRedirect(RenderFrameImpl
* frame
) {
222 if (!provisional_entry_
)
224 if (HistoryEntry::HistoryNode
* node
=
225 provisional_entry_
->GetHistoryNodeForFrame(frame
))
226 node
->RemoveChildren();
229 void HistoryController::CreateNewBackForwardItem(
230 RenderFrameImpl
* target_frame
,
231 const WebHistoryItem
& new_item
,
232 bool clone_children_of_target
) {
233 if (!current_entry_
) {
234 current_entry_
.reset(new HistoryEntry(new_item
));
236 current_entry_
.reset(current_entry_
->CloneAndReplace(
237 new_item
, clone_children_of_target
, target_frame
, render_view_
));
241 } // namespace content