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/WebLocalFrame.h"
43 using blink::WebFrame
;
44 using blink::WebHistoryCommitType
;
45 using blink::WebHistoryItem
;
46 using blink::WebURLRequest
;
50 HistoryController::HistoryController(RenderViewImpl
* render_view
)
51 : render_view_(render_view
) {
54 HistoryController::~HistoryController() {
57 void HistoryController::GoToEntry(
58 scoped_ptr
<HistoryEntry
> target_entry
,
59 scoped_ptr
<NavigationParams
> navigation_params
,
60 WebURLRequest::CachePolicy cache_policy
) {
61 HistoryFrameLoadVector same_document_loads
;
62 HistoryFrameLoadVector different_document_loads
;
64 provisional_entry_
= target_entry
.Pass();
65 navigation_params_
= navigation_params
.Pass();
67 WebFrame
* main_frame
= render_view_
->GetMainRenderFrame()->GetWebFrame();
70 main_frame
, same_document_loads
, different_document_loads
);
73 if (same_document_loads
.empty() && different_document_loads
.empty()) {
74 // If we don't have any frames to navigate at this point, either
75 // (1) there is no previous history entry to compare against, or
76 // (2) we were unable to match any frames by name. In the first case,
77 // doing a different document navigation to the root item is the only valid
78 // thing to do. In the second case, we should have been able to find a
79 // frame to navigate based on names if this were a same document
80 // navigation, so we can safely assume this is the different document case.
81 different_document_loads
.push_back(
82 std::make_pair(main_frame
, provisional_entry_
->root()));
85 for (const auto& item
: same_document_loads
) {
86 WebFrame
* frame
= item
.first
;
87 RenderFrameImpl
* render_frame
= RenderFrameImpl::FromWebFrame(frame
);
90 render_frame
->SetPendingNavigationParams(make_scoped_ptr(
91 new NavigationParams(*navigation_params_
.get())));
92 frame
->loadHistoryItem(item
.second
,
93 blink::WebHistorySameDocumentLoad
,
96 for (const auto& item
: different_document_loads
) {
97 WebFrame
* frame
= item
.first
;
98 RenderFrameImpl
* render_frame
= RenderFrameImpl::FromWebFrame(frame
);
101 render_frame
->SetPendingNavigationParams(make_scoped_ptr(
102 new NavigationParams(*navigation_params_
.get())));
103 frame
->loadHistoryItem(item
.second
,
104 blink::WebHistoryDifferentDocumentLoad
,
109 void HistoryController::RecursiveGoToEntry(
111 HistoryFrameLoadVector
& same_document_loads
,
112 HistoryFrameLoadVector
& different_document_loads
) {
113 DCHECK(provisional_entry_
);
114 DCHECK(current_entry_
);
115 RenderFrameImpl
* render_frame
= RenderFrameImpl::FromWebFrame(frame
);
116 const WebHistoryItem
& new_item
=
117 provisional_entry_
->GetItemForFrame(render_frame
);
118 const WebHistoryItem
& old_item
=
119 current_entry_
->GetItemForFrame(render_frame
);
120 if (new_item
.isNull())
123 if (old_item
.isNull() ||
124 new_item
.itemSequenceNumber() != old_item
.itemSequenceNumber()) {
125 if (!old_item
.isNull() &&
126 new_item
.documentSequenceNumber() == old_item
.documentSequenceNumber())
127 same_document_loads
.push_back(std::make_pair(frame
, new_item
));
129 different_document_loads
.push_back(std::make_pair(frame
, new_item
));
133 for (WebFrame
* child
= frame
->firstChild(); child
;
134 child
= child
->nextSibling()) {
135 RecursiveGoToEntry(child
, same_document_loads
, different_document_loads
);
139 void HistoryController::UpdateForInitialLoadInChildFrame(
140 RenderFrameImpl
* frame
,
141 const WebHistoryItem
& item
) {
142 DCHECK_NE(frame
->GetWebFrame()->top(), frame
->GetWebFrame());
145 if (HistoryEntry::HistoryNode
* existing_node
=
146 current_entry_
->GetHistoryNodeForFrame(frame
)) {
147 existing_node
->set_item(item
);
150 RenderFrameImpl
* parent
=
151 RenderFrameImpl::FromWebFrame(frame
->GetWebFrame()->parent());
154 if (HistoryEntry::HistoryNode
* parent_history_node
=
155 current_entry_
->GetHistoryNodeForFrame(parent
)) {
156 parent_history_node
->AddChild(item
);
160 void HistoryController::UpdateForCommit(RenderFrameImpl
* frame
,
161 const WebHistoryItem
& item
,
162 WebHistoryCommitType commit_type
,
163 bool navigation_within_page
) {
164 switch (commit_type
) {
165 case blink::WebBackForwardCommit
:
166 if (!provisional_entry_
)
168 current_entry_
.reset(provisional_entry_
.release());
170 case blink::WebStandardCommit
:
171 CreateNewBackForwardItem(frame
, item
, navigation_within_page
);
173 case blink::WebInitialCommitInChildFrame
:
174 UpdateForInitialLoadInChildFrame(frame
, item
);
176 case blink::WebHistoryInertCommit
:
177 // Even for inert commits (e.g., location.replace, client redirects), make
178 // sure the current entry gets updated, if there is one.
179 if (current_entry_
) {
180 if (HistoryEntry::HistoryNode
* node
=
181 current_entry_
->GetHistoryNodeForFrame(frame
)) {
182 node
->set_item(item
);
187 NOTREACHED() << "Invalid commit type: " << commit_type
;
191 HistoryEntry
* HistoryController::GetCurrentEntry() {
192 return current_entry_
.get();
195 WebHistoryItem
HistoryController::GetItemForNewChildFrame(
196 RenderFrameImpl
* frame
) const {
197 if (navigation_params_
.get()) {
198 frame
->SetPendingNavigationParams(make_scoped_ptr(
199 new NavigationParams(*navigation_params_
.get())));
203 return WebHistoryItem();
204 return current_entry_
->GetItemForFrame(frame
);
207 void HistoryController::RemoveChildrenForRedirect(RenderFrameImpl
* frame
) {
208 if (!provisional_entry_
)
210 if (HistoryEntry::HistoryNode
* node
=
211 provisional_entry_
->GetHistoryNodeForFrame(frame
))
212 node
->RemoveChildren();
215 void HistoryController::CreateNewBackForwardItem(
216 RenderFrameImpl
* target_frame
,
217 const WebHistoryItem
& new_item
,
218 bool clone_children_of_target
) {
219 if (!current_entry_
) {
220 current_entry_
.reset(new HistoryEntry(new_item
));
222 current_entry_
.reset(current_entry_
->CloneAndReplace(
223 new_item
, clone_children_of_target
, target_frame
, render_view_
));
227 } // namespace content