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/WebLocalFrame.h"
41
42using blink::WebFrame;
43using blink::WebHistoryCommitType;
44using blink::WebHistoryItem;
45using blink::WebURLRequest;
46
47namespace content {
48
49HistoryController::HistoryController(RenderViewImpl* render_view)
50    : render_view_(render_view) {
51}
52
53HistoryController::~HistoryController() {
54}
55
56void 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_->GetMainRenderFrame()->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  }
80
81  for (size_t i = 0; i < same_document_loads.size(); ++i) {
82    WebFrame* frame = same_document_loads[i].first;
83    if (!RenderFrameImpl::FromWebFrame(frame))
84      continue;
85    frame->loadHistoryItem(same_document_loads[i].second,
86                           blink::WebHistorySameDocumentLoad,
87                           cache_policy);
88  }
89  for (size_t i = 0; i < different_document_loads.size(); ++i) {
90    WebFrame* frame = different_document_loads[i].first;
91    if (!RenderFrameImpl::FromWebFrame(frame))
92      continue;
93    frame->loadHistoryItem(different_document_loads[i].second,
94                           blink::WebHistoryDifferentDocumentLoad,
95                           cache_policy);
96  }
97}
98
99void HistoryController::RecursiveGoToEntry(
100    WebFrame* frame,
101    HistoryFrameLoadVector& same_document_loads,
102    HistoryFrameLoadVector& different_document_loads) {
103  DCHECK(provisional_entry_);
104  DCHECK(current_entry_);
105  RenderFrameImpl* render_frame = RenderFrameImpl::FromWebFrame(frame);
106  const WebHistoryItem& new_item =
107      provisional_entry_->GetItemForFrame(render_frame);
108  const WebHistoryItem& old_item =
109      current_entry_->GetItemForFrame(render_frame);
110  if (new_item.isNull())
111    return;
112
113  if (old_item.isNull() ||
114      new_item.itemSequenceNumber() != old_item.itemSequenceNumber()) {
115    if (!old_item.isNull() &&
116        new_item.documentSequenceNumber() == old_item.documentSequenceNumber())
117      same_document_loads.push_back(std::make_pair(frame, new_item));
118    else
119      different_document_loads.push_back(std::make_pair(frame, new_item));
120    return;
121  }
122
123  for (WebFrame* child = frame->firstChild(); child;
124       child = child->nextSibling()) {
125    RecursiveGoToEntry(child, same_document_loads, different_document_loads);
126  }
127}
128
129void HistoryController::UpdateForInitialLoadInChildFrame(
130    RenderFrameImpl* frame,
131    const WebHistoryItem& item) {
132  DCHECK_NE(frame->GetWebFrame()->top(), frame->GetWebFrame());
133  if (!current_entry_)
134    return;
135  if (HistoryEntry::HistoryNode* existing_node =
136          current_entry_->GetHistoryNodeForFrame(frame)) {
137    existing_node->set_item(item);
138    return;
139  }
140  RenderFrameImpl* parent =
141      RenderFrameImpl::FromWebFrame(frame->GetWebFrame()->parent());
142  if (HistoryEntry::HistoryNode* parent_history_node =
143          current_entry_->GetHistoryNodeForFrame(parent)) {
144    parent_history_node->AddChild(item, frame->GetRoutingID());
145  }
146}
147
148void HistoryController::UpdateForCommit(RenderFrameImpl* frame,
149                                        const WebHistoryItem& item,
150                                        WebHistoryCommitType commit_type,
151                                        bool navigation_within_page) {
152  if (commit_type == blink::WebBackForwardCommit) {
153    if (!provisional_entry_)
154      return;
155    current_entry_.reset(provisional_entry_.release());
156  } else if (commit_type == blink::WebStandardCommit) {
157    CreateNewBackForwardItem(frame, item, navigation_within_page);
158  } else if (commit_type == blink::WebInitialCommitInChildFrame) {
159    UpdateForInitialLoadInChildFrame(frame, item);
160  }
161}
162
163HistoryEntry* HistoryController::GetCurrentEntry() {
164  return current_entry_.get();
165}
166
167WebHistoryItem HistoryController::GetItemForNewChildFrame(
168    RenderFrameImpl* frame) const {
169  if (!current_entry_)
170    return WebHistoryItem();
171  return current_entry_->GetItemForFrame(frame);
172}
173
174void HistoryController::RemoveChildrenForRedirect(RenderFrameImpl* frame) {
175  if (!provisional_entry_)
176    return;
177  if (HistoryEntry::HistoryNode* node =
178          provisional_entry_->GetHistoryNodeForFrame(frame))
179    node->RemoveChildren();
180}
181
182void HistoryController::CreateNewBackForwardItem(
183    RenderFrameImpl* target_frame,
184    const WebHistoryItem& new_item,
185    bool clone_children_of_target) {
186  if (!current_entry_) {
187    current_entry_.reset(
188        new HistoryEntry(new_item, target_frame->GetRoutingID()));
189  } else {
190    current_entry_.reset(current_entry_->CloneAndReplace(
191        new_item, clone_children_of_target, target_frame, render_view_));
192  }
193}
194
195}  // namespace content
196