Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / renderer / history_controller.cc
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 "content/renderer/render_frame_impl.h"
39 #include "content/renderer/render_view_impl.h"
40 #include "third_party/WebKit/public/web/WebFrame.h"
41
42 using blink::WebFrame;
43 using blink::WebHistoryCommitType;
44 using blink::WebHistoryItem;
45 using blink::WebURLRequest;
46
47 namespace content {
48
49 HistoryController::HistoryController(RenderViewImpl* render_view)
50     : render_view_(render_view) {
51 }
52
53 HistoryController::~HistoryController() {
54 }
55
56 void HistoryController::GoToEntry(scoped_ptr<HistoryEntry> target_entry,
57                                   WebURLRequest::CachePolicy cache_policy) {
58   HistoryFrameLoadVector same_document_loads;
59   HistoryFrameLoadVector different_document_loads;
60
61   provisional_entry_ = target_entry.Pass();
62
63   WebFrame* main_frame = render_view_->main_render_frame()->GetWebFrame();
64   if (current_entry_) {
65     RecursiveGoToEntry(
66         main_frame, same_document_loads, different_document_loads);
67   }
68
69   if (same_document_loads.empty() && different_document_loads.empty()) {
70     // If we don't have any frames to navigate at this point, either
71     // (1) there is no previous history entry to compare against, or
72     // (2) we were unable to match any frames by name. In the first case,
73     // doing a different document navigation to the root item is the only valid
74     // thing to do. In the second case, we should have been able to find a
75     // frame to navigate based on names if this were a same document
76     // navigation, so we can safely assume this is the different document case.
77     different_document_loads.push_back(
78         std::make_pair(main_frame, provisional_entry_->root()));
79   } else if (different_document_loads.empty()) {
80     // If we have only same document navigations to perform, immediately
81     // declare the load "committed" by updating the current entry.
82     // TODO(japhet): This is a historical quirk, because same-document
83     // history navigations call UpdateForCommit() with commit type
84     // HistoryInertCommit. If that is fixed, we can remove this block.
85     previous_entry_.reset(current_entry_.release());
86     current_entry_.reset(provisional_entry_.release());
87   }
88
89   for (size_t i = 0; i < same_document_loads.size(); ++i) {
90     WebFrame* frame = same_document_loads[i].first;
91     if (!RenderFrameImpl::FromWebFrame(frame))
92       continue;
93     frame->loadHistoryItem(same_document_loads[i].second,
94                            blink::WebHistorySameDocumentLoad,
95                            cache_policy);
96   }
97   for (size_t i = 0; i < different_document_loads.size(); ++i) {
98     WebFrame* frame = different_document_loads[i].first;
99     if (!RenderFrameImpl::FromWebFrame(frame))
100       continue;
101     frame->loadHistoryItem(different_document_loads[i].second,
102                            blink::WebHistoryDifferentDocumentLoad,
103                            cache_policy);
104   }
105 }
106
107 void HistoryController::RecursiveGoToEntry(
108     WebFrame* frame,
109     HistoryFrameLoadVector& same_document_loads,
110     HistoryFrameLoadVector& different_document_loads) {
111   DCHECK(provisional_entry_);
112   DCHECK(current_entry_);
113   RenderFrameImpl* render_frame = RenderFrameImpl::FromWebFrame(frame);
114   const WebHistoryItem& new_item =
115       provisional_entry_->GetItemForFrame(render_frame);
116   const WebHistoryItem& old_item =
117       current_entry_->GetItemForFrame(render_frame);
118   if (new_item.isNull())
119     return;
120
121   if (old_item.isNull() ||
122       new_item.itemSequenceNumber() != old_item.itemSequenceNumber()) {
123     if (!old_item.isNull() &&
124         new_item.documentSequenceNumber() == old_item.documentSequenceNumber())
125       same_document_loads.push_back(std::make_pair(frame, new_item));
126     else
127       different_document_loads.push_back(std::make_pair(frame, new_item));
128     return;
129   }
130
131   for (WebFrame* child = frame->firstChild(); child;
132        child = child->nextSibling()) {
133     RecursiveGoToEntry(child, same_document_loads, different_document_loads);
134   }
135 }
136
137 void HistoryController::UpdateForInitialLoadInChildFrame(
138     RenderFrameImpl* frame,
139     const WebHistoryItem& item) {
140   DCHECK_NE(frame->GetWebFrame()->top(), frame->GetWebFrame());
141   if (!current_entry_)
142     return;
143   if (HistoryEntry::HistoryNode* existing_node =
144           current_entry_->GetHistoryNodeForFrame(frame)) {
145     existing_node->set_item(item);
146     return;
147   }
148   RenderFrameImpl* parent =
149       RenderFrameImpl::FromWebFrame(frame->GetWebFrame()->parent());
150   if (HistoryEntry::HistoryNode* parent_history_node =
151           current_entry_->GetHistoryNodeForFrame(parent)) {
152     parent_history_node->AddChild(item, frame->GetRoutingID());
153   }
154 }
155
156 void HistoryController::UpdateForCommit(RenderFrameImpl* frame,
157                                         const WebHistoryItem& item,
158                                         WebHistoryCommitType commit_type,
159                                         bool navigation_within_page) {
160   if (commit_type == blink::WebBackForwardCommit) {
161     if (!provisional_entry_)
162       return;
163     previous_entry_.reset(current_entry_.release());
164     current_entry_.reset(provisional_entry_.release());
165   } else if (commit_type == blink::WebStandardCommit) {
166     CreateNewBackForwardItem(frame, item, navigation_within_page);
167   } else if (commit_type == blink::WebInitialCommitInChildFrame) {
168     UpdateForInitialLoadInChildFrame(frame, item);
169   }
170 }
171
172 HistoryEntry* HistoryController::GetCurrentEntry() {
173   return current_entry_.get();
174 }
175
176 HistoryEntry* HistoryController::GetPreviousEntry() {
177   return previous_entry_.get();
178 }
179
180 WebHistoryItem HistoryController::GetItemForNewChildFrame(
181     RenderFrameImpl* frame) const {
182   if (!current_entry_)
183     return WebHistoryItem();
184   return current_entry_->GetItemForFrame(frame);
185 }
186
187 void HistoryController::RemoveChildrenForRedirect(RenderFrameImpl* frame) {
188   if (!provisional_entry_)
189     return;
190   if (HistoryEntry::HistoryNode* node =
191           provisional_entry_->GetHistoryNodeForFrame(frame))
192     node->RemoveChildren();
193 }
194
195 void HistoryController::CreateNewBackForwardItem(
196     RenderFrameImpl* target_frame,
197     const WebHistoryItem& new_item,
198     bool clone_children_of_target) {
199   if (!current_entry_) {
200     current_entry_.reset(
201         new HistoryEntry(new_item, target_frame->GetRoutingID()));
202   } else {
203     previous_entry_.reset(current_entry_.release());
204     current_entry_.reset(previous_entry_->CloneAndReplace(
205         new_item, clone_children_of_target, target_frame, render_view_));
206   }
207 }
208
209 }  // namespace content