1 /* 2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 4 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 16 * its contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/loader/HistoryController.h" 33 34 #include "core/loader/FrameLoader.h" 35 #include "core/frame/Frame.h" 36 #include "core/page/FrameTree.h" 37 #include "core/page/Page.h" 38 #include "wtf/Deque.h" 39 #include "wtf/text/StringHash.h" 40 41 namespace WebCore { 42 43 PassOwnPtr<HistoryNode> HistoryNode::create(HistoryEntry* entry, HistoryItem* value) 44 { 45 return adoptPtr(new HistoryNode(entry, value)); 46 } 47 48 HistoryNode* HistoryNode::addChild(PassRefPtr<HistoryItem> item) 49 { 50 m_children.append(HistoryNode::create(m_entry, item.get())); 51 return m_children.last().get(); 52 } 53 54 PassOwnPtr<HistoryNode> HistoryNode::cloneAndReplace(HistoryEntry* newEntry, HistoryItem* newItem, bool clipAtTarget, Frame* targetFrame, Frame* currentFrame) 55 { 56 bool isNodeBeingNavigated = targetFrame == currentFrame; 57 HistoryItem* itemForCreate = isNodeBeingNavigated ? newItem : m_value.get(); 58 OwnPtr<HistoryNode> newHistoryNode = create(newEntry, itemForCreate); 59 60 if (!clipAtTarget || !isNodeBeingNavigated) { 61 for (Frame* child = currentFrame->tree().firstChild(); child; child = child->tree().nextSibling()) { 62 HistoryNode* childHistoryNode = m_entry->historyNodeForFrame(child); 63 if (!childHistoryNode) 64 continue; 65 newHistoryNode->m_children.append(childHistoryNode->cloneAndReplace(newEntry, newItem, clipAtTarget, targetFrame, child)); 66 } 67 } 68 return newHistoryNode.release(); 69 } 70 71 HistoryNode::HistoryNode(HistoryEntry* entry, HistoryItem* value) 72 : m_entry(entry) 73 , m_value(value) 74 { 75 m_entry->m_framesToItems.add(value->targetFrameID(), this); 76 String target = value->target(); 77 if (target.isNull()) 78 target = emptyString(); 79 m_entry->m_uniqueNamesToItems.add(target, this); 80 } 81 82 void HistoryNode::removeChildren() 83 { 84 // FIXME: This is inefficient. Figure out a cleaner way to ensure this HistoryNode isn't cached anywhere. 85 for (unsigned i = 0; i < m_children.size(); i++) { 86 m_children[i]->removeChildren(); 87 88 HashMap<uint64_t, HistoryNode*>::iterator framesEnd = m_entry->m_framesToItems.end(); 89 HashMap<String, HistoryNode*>::iterator uniqueNamesEnd = m_entry->m_uniqueNamesToItems.end(); 90 for (HashMap<uint64_t, HistoryNode*>::iterator it = m_entry->m_framesToItems.begin(); it != framesEnd; ++it) { 91 if (it->value == m_children[i]) 92 m_entry->m_framesToItems.remove(it); 93 } 94 for (HashMap<String, HistoryNode*>::iterator it = m_entry->m_uniqueNamesToItems.begin(); it != uniqueNamesEnd; ++it) { 95 if (it->value == m_children[i]) 96 m_entry->m_uniqueNamesToItems.remove(it); 97 } 98 } 99 m_children.clear(); 100 } 101 102 HistoryEntry::HistoryEntry(HistoryItem* root) 103 { 104 m_root = HistoryNode::create(this, root); 105 } 106 107 PassOwnPtr<HistoryEntry> HistoryEntry::create(HistoryItem* root) 108 { 109 return adoptPtr(new HistoryEntry(root)); 110 } 111 112 PassOwnPtr<HistoryEntry> HistoryEntry::cloneAndReplace(HistoryItem* newItem, bool clipAtTarget, Frame* targetFrame, Page* page) 113 { 114 OwnPtr<HistoryEntry> newEntry = adoptPtr(new HistoryEntry()); 115 newEntry->m_root = m_root->cloneAndReplace(newEntry.get(), newItem, clipAtTarget, targetFrame, page->mainFrame()); 116 return newEntry.release(); 117 } 118 119 HistoryNode* HistoryEntry::historyNodeForFrame(Frame* frame) 120 { 121 if (HistoryNode* historyNode = m_framesToItems.get(frame->frameID())) 122 return historyNode; 123 String target = frame->tree().uniqueName(); 124 if (target.isNull()) 125 target = emptyString(); 126 return m_uniqueNamesToItems.get(target); 127 } 128 129 HistoryItem* HistoryEntry::itemForFrame(Frame* frame) 130 { 131 if (HistoryNode* historyNode = historyNodeForFrame(frame)) 132 return historyNode->value(); 133 return 0; 134 } 135 136 HistoryController::HistoryController(Page* page) 137 : m_page(page) 138 , m_defersLoading(false) 139 { 140 } 141 142 HistoryController::~HistoryController() 143 { 144 } 145 146 void HistoryController::updateBackForwardListForFragmentScroll(Frame* frame, HistoryItem* item) 147 { 148 m_provisionalEntry.clear(); 149 createNewBackForwardItem(frame, item, false); 150 } 151 152 void HistoryController::goToEntry(PassOwnPtr<HistoryEntry> targetEntry) 153 { 154 ASSERT(m_sameDocumentLoadsInProgress.isEmpty()); 155 ASSERT(m_differentDocumentLoadsInProgress.isEmpty()); 156 157 m_provisionalEntry = targetEntry; 158 if (m_currentEntry) 159 recursiveGoToEntry(m_page->mainFrame()); 160 else 161 m_differentDocumentLoadsInProgress.set(m_page->mainFrame(), m_provisionalEntry->root()); 162 163 if (m_sameDocumentLoadsInProgress.isEmpty() && m_differentDocumentLoadsInProgress.isEmpty()) 164 m_sameDocumentLoadsInProgress.set(m_page->mainFrame(), m_provisionalEntry->root()); 165 166 if (m_differentDocumentLoadsInProgress.isEmpty()) { 167 m_previousEntry = m_currentEntry.release(); 168 m_currentEntry = m_provisionalEntry.release(); 169 } else { 170 m_page->mainFrame()->loader().stopAllLoaders(); 171 } 172 173 for (HistoryFrameLoadSet::iterator it = m_sameDocumentLoadsInProgress.begin(); it != m_sameDocumentLoadsInProgress.end(); ++it) 174 it->key->loader().loadHistoryItem(it->value.get(), HistorySameDocumentLoad); 175 for (HistoryFrameLoadSet::iterator it = m_differentDocumentLoadsInProgress.begin(); it != m_differentDocumentLoadsInProgress.end(); ++it) 176 it->key->loader().loadHistoryItem(it->value.get(), HistoryDifferentDocumentLoad); 177 m_sameDocumentLoadsInProgress.clear(); 178 m_differentDocumentLoadsInProgress.clear(); 179 } 180 181 void HistoryController::recursiveGoToEntry(Frame* frame) 182 { 183 ASSERT(m_provisionalEntry); 184 ASSERT(m_currentEntry); 185 HistoryItem* newItem = m_provisionalEntry->itemForFrame(frame); 186 HistoryItem* oldItem = m_currentEntry->itemForFrame(frame); 187 if (!newItem) 188 return; 189 190 if (!oldItem || (newItem != oldItem && newItem->itemSequenceNumber() != oldItem->itemSequenceNumber())) { 191 if (oldItem && newItem->documentSequenceNumber() == oldItem->documentSequenceNumber()) 192 m_sameDocumentLoadsInProgress.set(frame, newItem); 193 else 194 m_differentDocumentLoadsInProgress.set(frame, newItem); 195 return; 196 } 197 198 for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) 199 recursiveGoToEntry(child); 200 } 201 202 void HistoryController::goToItem(HistoryItem* targetItem) 203 { 204 if (m_defersLoading) { 205 m_deferredItem = targetItem; 206 return; 207 } 208 209 OwnPtr<HistoryEntry> newEntry = HistoryEntry::create(targetItem); 210 Deque<HistoryNode*> historyNodes; 211 historyNodes.append(newEntry->rootHistoryNode()); 212 while (!historyNodes.isEmpty()) { 213 // For each item, read the children (if any) off the HistoryItem, 214 // create a new HistoryNode for each child and attach it, 215 // then clear the children on the HistoryItem. 216 HistoryNode* historyNode = historyNodes.takeFirst(); 217 const HistoryItemVector& children = historyNode->value()->children(); 218 for (size_t i = 0; i < children.size(); i++) { 219 HistoryNode* childHistoryNode = historyNode->addChild(children[i].get()); 220 historyNodes.append(childHistoryNode); 221 } 222 historyNode->value()->clearChildren(); 223 } 224 goToEntry(newEntry.release()); 225 } 226 227 void HistoryController::setDefersLoading(bool defer) 228 { 229 m_defersLoading = defer; 230 if (!defer && m_deferredItem) { 231 goToItem(m_deferredItem.get()); 232 m_deferredItem = 0; 233 } 234 } 235 236 void HistoryController::updateForInitialLoadInChildFrame(Frame* frame, HistoryItem* item) 237 { 238 ASSERT(frame->tree().parent()); 239 if (!m_currentEntry) 240 return; 241 if (HistoryNode* existingChildHistoryNode = m_currentEntry->historyNodeForFrame(frame)) 242 existingChildHistoryNode->updateValue(item); 243 else if (HistoryNode* parentHistoryNode = m_currentEntry->historyNodeForFrame(frame->tree().parent())) 244 parentHistoryNode->addChild(item); 245 } 246 247 // FIXME: This is a temporary hack designed to be mergeable to the 1750 branch. 248 // As trunk stands currently, we should never clear the provisional entry, since it's 249 // possible to clear based on a commit in an irrelevant frame. On trunk, the provisional entry is 250 // an implementation detail of HistoryController and only used when we know that we're in 251 // a back/forward navigation. Also, it is clobbered when a new history navigation begins, 252 // so we can be sure that a stale provisional entry won't be confused with a new one. 253 // On the branch, however, the provisional entry is observable because 254 // WebFrameImpl::currentHistoryItem() will return data based on the provisional entry preferentially 255 // over the current entry, so we can't leave a stale provisional entry around indefinitely. 256 // Therefore, search the frame tree for any back/forward navigations in progress, and only clear 257 // the provisional entry if none are found. 258 // Once the fix is merged to the branch, this can be removed, along with all places that we clear 259 // m_provisionalEntry. 260 static bool shouldClearProvisionalEntry(Page* page) 261 { 262 for (Frame* frame = page->mainFrame(); frame; frame = frame->tree().traverseNext()) { 263 if (frame->loader().loadType() == FrameLoadTypeBackForward) 264 return false; 265 } 266 return true; 267 } 268 269 void HistoryController::updateForCommit(Frame* frame, HistoryItem* item) 270 { 271 FrameLoadType type = frame->loader().loadType(); 272 if (isBackForwardLoadType(type) && m_provisionalEntry) { 273 // Once committed, we want to use current item for saving DocState, and 274 // the provisional item for restoring state. 275 // Note previousItem must be set before we close the URL, which will 276 // happen when the data source is made non-provisional below 277 m_previousEntry = m_currentEntry.release(); 278 ASSERT(m_provisionalEntry); 279 m_currentEntry = m_provisionalEntry.release(); 280 } else if (type != FrameLoadTypeRedirectWithLockedBackForwardList && shouldClearProvisionalEntry(m_page)) { 281 m_provisionalEntry.clear(); 282 } 283 284 if (type == FrameLoadTypeStandard) 285 createNewBackForwardItem(frame, item, true); 286 else if (type == FrameLoadTypeInitialInChildFrame) 287 updateForInitialLoadInChildFrame(frame, item); 288 } 289 290 static PassRefPtr<HistoryItem> itemForExport(HistoryNode* historyNode) 291 { 292 RefPtr<HistoryItem> item = historyNode->value()->copy(); 293 const Vector<OwnPtr<HistoryNode> >& childEntries = historyNode->children(); 294 for (size_t i = 0; i < childEntries.size(); i++) 295 item->addChildItem(itemForExport(childEntries[i].get())); 296 return item; 297 } 298 299 PassRefPtr<HistoryItem> HistoryController::currentItemForExport() 300 { 301 if (!m_currentEntry) 302 return 0; 303 return itemForExport(m_currentEntry->rootHistoryNode()); 304 } 305 306 PassRefPtr<HistoryItem> HistoryController::previousItemForExport() 307 { 308 if (!m_previousEntry) 309 return 0; 310 return itemForExport(m_previousEntry->rootHistoryNode()); 311 } 312 313 PassRefPtr<HistoryItem> HistoryController::provisionalItemForExport() 314 { 315 if (!m_provisionalEntry) 316 return 0; 317 return itemForExport(m_provisionalEntry->rootHistoryNode()); 318 } 319 320 HistoryItem* HistoryController::itemForNewChildFrame(Frame* frame) const 321 { 322 return m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0; 323 } 324 325 void HistoryController::removeChildrenForRedirect(Frame* frame) 326 { 327 if (!m_provisionalEntry) 328 return; 329 if (HistoryNode* node = m_provisionalEntry->historyNodeForFrame(frame)) 330 node->removeChildren(); 331 } 332 333 void HistoryController::createNewBackForwardItem(Frame* targetFrame, HistoryItem* item, bool clipAtTarget) 334 { 335 RefPtr<HistoryItem> newItem = item; 336 if (!m_currentEntry) { 337 m_currentEntry = HistoryEntry::create(newItem.get()); 338 } else { 339 HistoryItem* oldItem = m_currentEntry->itemForFrame(targetFrame); 340 if (!clipAtTarget && oldItem) 341 newItem->setDocumentSequenceNumber(oldItem->documentSequenceNumber()); 342 m_previousEntry = m_currentEntry.release(); 343 m_currentEntry = m_previousEntry->cloneAndReplace(newItem.get(), clipAtTarget, targetFrame, m_page); 344 } 345 } 346 347 } // namespace WebCore 348