Add String version of visitedLinkHash() to properly handle 8-bit URL Strings.
[WebKit-https.git] / Source / WebCore / loader / HistoryController.cpp
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 "HistoryController.h"
33
34 #include "BackForwardController.h"
35 #include "CachedPage.h"
36 #include "Document.h"
37 #include "DocumentLoader.h"
38 #include "Frame.h"
39 #include "FrameLoader.h"
40 #include "FrameLoaderClient.h"
41 #include "FrameLoaderStateMachine.h"
42 #include "FrameTree.h"
43 #include "FrameView.h"
44 #include "HistoryItem.h"
45 #include "Logging.h"
46 #include "Page.h"
47 #include "PageCache.h"
48 #include "PageGroup.h"
49 #include "ScrollingCoordinator.h"
50 #include "Settings.h"
51 #include <wtf/text/CString.h>
52
53 #if USE(PLATFORM_STRATEGIES)
54 #include "PlatformStrategies.h"
55 #include "VisitedLinkStrategy.h"
56 #endif
57
58 namespace WebCore {
59
60 static inline void addVisitedLink(Page* page, const KURL& url)
61 {
62 #if USE(PLATFORM_STRATEGIES)
63     platformStrategies()->visitedLinkStrategy()->addVisitedLink(page, visitedLinkHash(url.string()));
64 #else
65     page->group().addVisitedLink(url);
66 #endif
67 }
68
69 HistoryController::HistoryController(Frame* frame)
70     : m_frame(frame)
71     , m_frameLoadComplete(true)
72     , m_defersLoading(false)
73 {
74 }
75
76 HistoryController::~HistoryController()
77 {
78 }
79
80 void HistoryController::saveScrollPositionAndViewStateToItem(HistoryItem* item)
81 {
82     if (!item || !m_frame->view())
83         return;
84
85     if (m_frame->document()->inPageCache())
86         item->setScrollPoint(m_frame->view()->cachedScrollPosition());
87     else
88         item->setScrollPoint(m_frame->view()->scrollPosition());
89
90     item->setPageScaleFactor(m_frame->frameScaleFactor());
91     
92     // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client.
93     m_frame->loader()->client()->saveViewStateToItem(item);
94 }
95
96 /*
97  There is a race condition between the layout and load completion that affects restoring the scroll position.
98  We try to restore the scroll position at both the first layout and upon load completion.
99  
100  1) If first layout happens before the load completes, we want to restore the scroll position then so that the
101  first time we draw the page is already scrolled to the right place, instead of starting at the top and later
102  jumping down.  It is possible that the old scroll position is past the part of the doc laid out so far, in
103  which case the restore silent fails and we will fix it in when we try to restore on doc completion.
104  2) If the layout happens after the load completes, the attempt to restore at load completion time silently
105  fails.  We then successfully restore it when the layout happens.
106 */
107 void HistoryController::restoreScrollPositionAndViewState()
108 {
109     if (!m_frame->loader()->stateMachine()->committedFirstRealDocumentLoad())
110         return;
111
112     ASSERT(m_currentItem);
113     
114     // FIXME: As the ASSERT attests, it seems we should always have a currentItem here.
115     // One counterexample is <rdar://problem/4917290>
116     // For now, to cover this issue in release builds, there is no technical harm to returning
117     // early and from a user standpoint - as in the above radar - the previous page load failed 
118     // so there *is* no scroll or view state to restore!
119     if (!m_currentItem)
120         return;
121     
122     // FIXME: It would be great to work out a way to put this code in WebCore instead of calling
123     // through to the client. It's currently used only for the PDF view on Mac.
124     m_frame->loader()->client()->restoreViewState();
125
126     // FIXME: There is some scrolling related work that needs to happen whenever a page goes into the
127     // page cache and similar work that needs to occur when it comes out. This is where we do the work
128     // that needs to happen when we exit, and the work that needs to happen when we enter is in
129     // Document::setIsInPageCache(bool). It would be nice if there was more symmetry in these spots.
130     // https://bugs.webkit.org/show_bug.cgi?id=98698
131     if (FrameView* view = m_frame->view()) {
132         Page* page = m_frame->page();
133         if (page && page->mainFrame() == m_frame) {
134             if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
135                 scrollingCoordinator->frameViewRootLayerDidChange(view);
136         }
137
138         if (!view->wasScrolledByUser()) {
139             if (page && page->mainFrame() == m_frame && m_currentItem->pageScaleFactor())
140                 page->setPageScaleFactor(m_currentItem->pageScaleFactor(), m_currentItem->scrollPoint());
141             else
142                 view->setScrollPosition(m_currentItem->scrollPoint());
143         }
144     }
145 }
146
147 void HistoryController::updateBackForwardListForFragmentScroll()
148 {
149     updateBackForwardListClippedAtTarget(false);
150 }
151
152 void HistoryController::saveDocumentState()
153 {
154     // FIXME: Reading this bit of FrameLoader state here is unfortunate.  I need to study
155     // this more to see if we can remove this dependency.
156     if (m_frame->loader()->stateMachine()->creatingInitialEmptyDocument())
157         return;
158
159     // For a standard page load, we will have a previous item set, which will be used to
160     // store the form state.  However, in some cases we will have no previous item, and
161     // the current item is the right place to save the state.  One example is when we
162     // detach a bunch of frames because we are navigating from a site with frames to
163     // another site.  Another is when saving the frame state of a frame that is not the
164     // target of the current navigation (if we even decide to save with that granularity).
165
166     // Because of previousItem's "masking" of currentItem for this purpose, it's important
167     // that we keep track of the end of a page transition with m_frameLoadComplete.  We
168     // leverage the checkLoadComplete recursion to achieve this goal.
169
170     HistoryItem* item = m_frameLoadComplete ? m_currentItem.get() : m_previousItem.get();
171     if (!item)
172         return;
173
174     Document* document = m_frame->document();
175     ASSERT(document);
176     
177     if (item->isCurrentDocument(document) && document->attached()) {
178         LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->uniqueName().string().utf8().data(), item);
179         item->setDocumentState(document->formElementsState());
180     }
181 }
182
183 // Walk the frame tree, telling all frames to save their form state into their current
184 // history item.
185 void HistoryController::saveDocumentAndScrollState()
186 {
187     for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame)) {
188         frame->loader()->history()->saveDocumentState();
189         frame->loader()->history()->saveScrollPositionAndViewStateToItem(frame->loader()->history()->currentItem());
190     }
191 }
192
193 static inline bool isAssociatedToRequestedHistoryItem(const HistoryItem* current, Frame* frame, const HistoryItem* requested)
194 {
195     if (requested == current)
196         return true;
197     if (requested)
198         return false;
199     while ((frame = frame->tree()->parent())) {
200         requested = frame->loader()->requestedHistoryItem();
201         if (!requested)
202             continue;
203         if (requested->isAncestorOf(current))
204             return true;
205     }
206     return false;
207 }
208
209 void HistoryController::restoreDocumentState()
210 {
211     Document* doc = m_frame->document();
212         
213     HistoryItem* itemToRestore = 0;
214     
215     switch (m_frame->loader()->loadType()) {
216         case FrameLoadTypeReload:
217         case FrameLoadTypeReloadFromOrigin:
218         case FrameLoadTypeSame:
219         case FrameLoadTypeReplace:
220             break;
221         case FrameLoadTypeBack:
222         case FrameLoadTypeForward:
223         case FrameLoadTypeIndexedBackForward:
224         case FrameLoadTypeRedirectWithLockedBackForwardList:
225         case FrameLoadTypeStandard:
226             itemToRestore = m_currentItem.get(); 
227     }
228     
229     if (!itemToRestore)
230         return;
231     if (isAssociatedToRequestedHistoryItem(itemToRestore, m_frame, m_frame->loader()->requestedHistoryItem()) && !m_frame->loader()->documentLoader()->isClientRedirect()) {
232         LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->uniqueName().string().utf8().data(), itemToRestore);
233         doc->setStateForNewFormElements(itemToRestore->documentState());
234     }
235 }
236
237 void HistoryController::invalidateCurrentItemCachedPage()
238 {
239     // When we are pre-commit, the currentItem is where the pageCache data resides    
240     CachedPage* cachedPage = pageCache()->get(currentItem());
241
242     // FIXME: This is a grotesque hack to fix <rdar://problem/4059059> Crash in RenderFlow::detach
243     // Somehow the PageState object is not properly updated, and is holding onto a stale document.
244     // Both Xcode and FileMaker see this crash, Safari does not.
245     
246     ASSERT(!cachedPage || cachedPage->document() == m_frame->document());
247     if (cachedPage && cachedPage->document() == m_frame->document()) {
248         cachedPage->document()->setInPageCache(false);
249         cachedPage->clear();
250     }
251     
252     if (cachedPage)
253         pageCache()->remove(currentItem());
254 }
255
256 bool HistoryController::shouldStopLoadingForHistoryItem(HistoryItem* targetItem) const
257 {
258     if (!m_currentItem)
259         return false;
260
261     // Don't abort the current load if we're navigating within the current document.
262     if (m_currentItem->shouldDoSameDocumentNavigationTo(targetItem))
263         return false;
264
265     return m_frame->loader()->client()->shouldStopLoadingForHistoryItem(targetItem);
266 }
267
268 // Main funnel for navigating to a previous location (back/forward, non-search snap-back)
269 // This includes recursion to handle loading into framesets properly
270 void HistoryController::goToItem(HistoryItem* targetItem, FrameLoadType type)
271 {
272     ASSERT(!m_frame->tree()->parent());
273     
274     // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
275     // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
276     // Ultimately, history item navigations should go through the policy delegate. That's covered in:
277     // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
278     Page* page = m_frame->page();
279     if (!page)
280         return;
281     if (!m_frame->loader()->client()->shouldGoToHistoryItem(targetItem))
282         return;
283     if (m_defersLoading) {
284         m_deferredItem = targetItem;
285         m_deferredFrameLoadType = type;
286         return;
287     }
288
289     // Set the BF cursor before commit, which lets the user quickly click back/forward again.
290     // - plus, it only makes sense for the top level of the operation through the frametree,
291     // as opposed to happening for some/one of the page commits that might happen soon
292     RefPtr<HistoryItem> currentItem = page->backForward()->currentItem();
293     page->backForward()->setCurrentItem(targetItem);
294     m_frame->loader()->client()->updateGlobalHistoryItemForPage();
295
296     // First set the provisional item of any frames that are not actually navigating.
297     // This must be done before trying to navigate the desired frame, because some
298     // navigations can commit immediately (such as about:blank).  We must be sure that
299     // all frames have provisional items set before the commit.
300     recursiveSetProvisionalItem(targetItem, currentItem.get(), type);
301     // Now that all other frames have provisional items, do the actual navigation.
302     recursiveGoToItem(targetItem, currentItem.get(), type);
303 }
304
305 void HistoryController::setDefersLoading(bool defer)
306 {
307     m_defersLoading = defer;
308     if (!defer && m_deferredItem) {
309         goToItem(m_deferredItem.get(), m_deferredFrameLoadType);
310         m_deferredItem = 0;
311     }
312 }
313
314 void HistoryController::updateForBackForwardNavigation()
315 {
316 #if !LOG_DISABLED
317     if (m_frame->loader()->documentLoader())
318         LOG(History, "WebCoreHistory: Updating History for back/forward navigation in frame %s", m_frame->loader()->documentLoader()->title().string().utf8().data());
319 #endif
320
321     // Must grab the current scroll position before disturbing it
322     if (!m_frameLoadComplete)
323         saveScrollPositionAndViewStateToItem(m_previousItem.get());
324
325     // When traversing history, we may end up redirecting to a different URL
326     // this time (e.g., due to cookies).  See http://webkit.org/b/49654.
327     updateCurrentItem();
328 }
329
330 void HistoryController::updateForReload()
331 {
332 #if !LOG_DISABLED
333     if (m_frame->loader()->documentLoader())
334         LOG(History, "WebCoreHistory: Updating History for reload in frame %s", m_frame->loader()->documentLoader()->title().string().utf8().data());
335 #endif
336
337     if (m_currentItem) {
338         pageCache()->remove(m_currentItem.get());
339     
340         if (m_frame->loader()->loadType() == FrameLoadTypeReload || m_frame->loader()->loadType() == FrameLoadTypeReloadFromOrigin)
341             saveScrollPositionAndViewStateToItem(m_currentItem.get());
342     }
343
344     // When reloading the page, we may end up redirecting to a different URL
345     // this time (e.g., due to cookies).  See http://webkit.org/b/4072.
346     updateCurrentItem();
347 }
348
349 // There are 3 things you might think of as "history", all of which are handled by these functions.
350 //
351 //     1) Back/forward: The m_currentItem is part of this mechanism.
352 //     2) Global history: Handled by the client.
353 //     3) Visited links: Handled by the PageGroup.
354
355 void HistoryController::updateForStandardLoad(HistoryUpdateType updateType)
356 {
357     LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", m_frame->loader()->documentLoader()->url().string().ascii().data());
358
359     FrameLoader* frameLoader = m_frame->loader();
360
361     Settings* settings = m_frame->settings();
362     bool needPrivacy = !settings || settings->privateBrowsingEnabled();
363     const KURL& historyURL = frameLoader->documentLoader()->urlForHistory();
364
365     if (!frameLoader->documentLoader()->isClientRedirect()) {
366         if (!historyURL.isEmpty()) {
367             if (updateType != UpdateAllExceptBackForwardList)
368                 updateBackForwardListClippedAtTarget(true);
369             if (!needPrivacy) {
370                 frameLoader->client()->updateGlobalHistory();
371                 frameLoader->documentLoader()->setDidCreateGlobalHistoryEntry(true);
372                 if (frameLoader->documentLoader()->unreachableURL().isEmpty())
373                     frameLoader->client()->updateGlobalHistoryRedirectLinks();
374             }
375
376             m_frame->loader()->client()->updateGlobalHistoryItemForPage();
377         }
378     } else {
379         // The client redirect replaces the current history item.
380         updateCurrentItem();
381     }
382
383     if (!historyURL.isEmpty() && !needPrivacy) {
384         if (Page* page = m_frame->page())
385             addVisitedLink(page, historyURL);
386
387         if (!frameLoader->documentLoader()->didCreateGlobalHistoryEntry() && frameLoader->documentLoader()->unreachableURL().isEmpty() && !m_frame->document()->url().isEmpty())
388             frameLoader->client()->updateGlobalHistoryRedirectLinks();
389     }
390 }
391
392 void HistoryController::updateForRedirectWithLockedBackForwardList()
393 {
394 #if !LOG_DISABLED
395     if (m_frame->loader()->documentLoader())
396         LOG(History, "WebCoreHistory: Updating History for redirect load in frame %s", m_frame->loader()->documentLoader()->title().string().utf8().data());
397 #endif
398     
399     Settings* settings = m_frame->settings();
400     bool needPrivacy = !settings || settings->privateBrowsingEnabled();
401     const KURL& historyURL = m_frame->loader()->documentLoader()->urlForHistory();
402
403     if (m_frame->loader()->documentLoader()->isClientRedirect()) {
404         if (!m_currentItem && !m_frame->tree()->parent()) {
405             if (!historyURL.isEmpty()) {
406                 updateBackForwardListClippedAtTarget(true);
407                 if (!needPrivacy) {
408                     m_frame->loader()->client()->updateGlobalHistory();
409                     m_frame->loader()->documentLoader()->setDidCreateGlobalHistoryEntry(true);
410                     if (m_frame->loader()->documentLoader()->unreachableURL().isEmpty())
411                         m_frame->loader()->client()->updateGlobalHistoryRedirectLinks();
412                 }
413
414                 m_frame->loader()->client()->updateGlobalHistoryItemForPage();
415             }
416         }
417         // The client redirect replaces the current history item.
418         updateCurrentItem();
419     } else {
420         Frame* parentFrame = m_frame->tree()->parent();
421         if (parentFrame && parentFrame->loader()->history()->m_currentItem)
422             parentFrame->loader()->history()->m_currentItem->setChildItem(createItem());
423     }
424
425     if (!historyURL.isEmpty() && !needPrivacy) {
426         if (Page* page = m_frame->page())
427             addVisitedLink(page, historyURL);
428
429         if (!m_frame->loader()->documentLoader()->didCreateGlobalHistoryEntry() && m_frame->loader()->documentLoader()->unreachableURL().isEmpty() && !m_frame->document()->url().isEmpty())
430             m_frame->loader()->client()->updateGlobalHistoryRedirectLinks();
431     }
432 }
433
434 void HistoryController::updateForClientRedirect()
435 {
436 #if !LOG_DISABLED
437     if (m_frame->loader()->documentLoader())
438         LOG(History, "WebCoreHistory: Updating History for client redirect in frame %s", m_frame->loader()->documentLoader()->title().string().utf8().data());
439 #endif
440
441     // Clear out form data so we don't try to restore it into the incoming page.  Must happen after
442     // webcore has closed the URL and saved away the form state.
443     if (m_currentItem) {
444         m_currentItem->clearDocumentState();
445         m_currentItem->clearScrollPoint();
446     }
447
448     Settings* settings = m_frame->settings();
449     bool needPrivacy = !settings || settings->privateBrowsingEnabled();
450     const KURL& historyURL = m_frame->loader()->documentLoader()->urlForHistory();
451
452     if (!historyURL.isEmpty() && !needPrivacy) {
453         if (Page* page = m_frame->page())
454             addVisitedLink(page, historyURL);
455     }
456 }
457
458 void HistoryController::updateForCommit()
459 {
460     FrameLoader* frameLoader = m_frame->loader();
461 #if !LOG_DISABLED
462     if (frameLoader->documentLoader())
463         LOG(History, "WebCoreHistory: Updating History for commit in frame %s", frameLoader->documentLoader()->title().string().utf8().data());
464 #endif
465     FrameLoadType type = frameLoader->loadType();
466     if (isBackForwardLoadType(type)
467         || isReplaceLoadTypeWithProvisionalItem(type)
468         || (isReloadTypeWithProvisionalItem(type) && !frameLoader->provisionalDocumentLoader()->unreachableURL().isEmpty())) {
469         // Once committed, we want to use current item for saving DocState, and
470         // the provisional item for restoring state.
471         // Note previousItem must be set before we close the URL, which will
472         // happen when the data source is made non-provisional below
473         m_frameLoadComplete = false;
474         m_previousItem = m_currentItem;
475         ASSERT(m_provisionalItem);
476         m_currentItem = m_provisionalItem;
477         m_provisionalItem = 0;
478
479         // Tell all other frames in the tree to commit their provisional items and
480         // restore their scroll position.  We'll avoid this frame (which has already
481         // committed) and its children (which will be replaced).
482         Page* page = m_frame->page();
483         ASSERT(page);
484         page->mainFrame()->loader()->history()->recursiveUpdateForCommit();
485     }
486 }
487
488 bool HistoryController::isReplaceLoadTypeWithProvisionalItem(FrameLoadType type)
489 {
490     // Going back to an error page in a subframe can trigger a FrameLoadTypeReplace
491     // while m_provisionalItem is set, so we need to commit it.
492     return type == FrameLoadTypeReplace && m_provisionalItem;
493 }
494
495 bool HistoryController::isReloadTypeWithProvisionalItem(FrameLoadType type)
496 {
497     return (type == FrameLoadTypeReload || type == FrameLoadTypeReloadFromOrigin) && m_provisionalItem;
498 }
499
500 void HistoryController::recursiveUpdateForCommit()
501 {
502     // The frame that navigated will now have a null provisional item.
503     // Ignore it and its children.
504     if (!m_provisionalItem)
505         return;
506
507     // For each frame that already had the content the item requested (based on
508     // (a matching URL and frame tree snapshot), just restore the scroll position.
509     // Save form state (works from currentItem, since m_frameLoadComplete is true)
510     if (m_currentItem && itemsAreClones(m_currentItem.get(), m_provisionalItem.get())) {
511         ASSERT(m_frameLoadComplete);
512         saveDocumentState();
513         saveScrollPositionAndViewStateToItem(m_currentItem.get());
514
515         if (FrameView* view = m_frame->view())
516             view->setWasScrolledByUser(false);
517
518         // Now commit the provisional item
519         m_frameLoadComplete = false;
520         m_previousItem = m_currentItem;
521         m_currentItem = m_provisionalItem;
522         m_provisionalItem = 0;
523
524         // Restore form state (works from currentItem)
525         restoreDocumentState();
526
527         // Restore the scroll position (we choose to do this rather than going back to the anchor point)
528         restoreScrollPositionAndViewState();
529     }
530
531     // Iterate over the rest of the tree
532     for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
533         child->loader()->history()->recursiveUpdateForCommit();
534 }
535
536 void HistoryController::updateForSameDocumentNavigation()
537 {
538     if (m_frame->document()->url().isEmpty())
539         return;
540
541     Settings* settings = m_frame->settings();
542     if (!settings || settings->privateBrowsingEnabled())
543         return;
544
545     Page* page = m_frame->page();
546     if (!page)
547         return;
548
549     addVisitedLink(page, m_frame->document()->url());
550     page->mainFrame()->loader()->history()->recursiveUpdateForSameDocumentNavigation();
551
552     if (m_currentItem) {
553         m_currentItem->setURL(m_frame->document()->url());
554         m_frame->loader()->client()->updateGlobalHistory();
555     }
556 }
557
558 void HistoryController::recursiveUpdateForSameDocumentNavigation()
559 {
560     // The frame that navigated will now have a null provisional item.
561     // Ignore it and its children.
562     if (!m_provisionalItem)
563         return;
564
565     // The provisional item may represent a different pending navigation.
566     // Don't commit it if it isn't a same document navigation.
567     if (m_currentItem && !m_currentItem->shouldDoSameDocumentNavigationTo(m_provisionalItem.get()))
568         return;
569
570     // Commit the provisional item.
571     m_frameLoadComplete = false;
572     m_previousItem = m_currentItem;
573     m_currentItem = m_provisionalItem;
574     m_provisionalItem = 0;
575
576     // Iterate over the rest of the tree.
577     for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
578         child->loader()->history()->recursiveUpdateForSameDocumentNavigation();
579 }
580
581 void HistoryController::updateForFrameLoadCompleted()
582 {
583     // Even if already complete, we might have set a previous item on a frame that
584     // didn't do any data loading on the past transaction. Make sure to track that
585     // the load is complete so that we use the current item instead.
586     m_frameLoadComplete = true;
587 }
588
589 void HistoryController::setCurrentItem(HistoryItem* item)
590 {
591     m_frameLoadComplete = false;
592     m_previousItem = m_currentItem;
593     m_currentItem = item;
594 }
595
596 void HistoryController::setCurrentItemTitle(const StringWithDirection& title)
597 {
598     if (m_currentItem)
599         // FIXME: make use of title.direction() as well.
600         m_currentItem->setTitle(title.string());
601 }
602
603 bool HistoryController::currentItemShouldBeReplaced() const
604 {
605     // From the HTML5 spec for location.assign():
606     //  "If the browsing context's session history contains only one Document,
607     //   and that was the about:blank Document created when the browsing context
608     //   was created, then the navigation must be done with replacement enabled."
609     return m_currentItem && !m_previousItem && equalIgnoringCase(m_currentItem->urlString(), blankURL());
610 }
611
612 void HistoryController::setProvisionalItem(HistoryItem* item)
613 {
614     m_provisionalItem = item;
615 }
616
617 void HistoryController::initializeItem(HistoryItem* item)
618 {
619     DocumentLoader* documentLoader = m_frame->loader()->documentLoader();
620     ASSERT(documentLoader);
621
622     KURL unreachableURL = documentLoader->unreachableURL();
623
624     KURL url;
625     KURL originalURL;
626
627     if (!unreachableURL.isEmpty()) {
628         url = unreachableURL;
629         originalURL = unreachableURL;
630     } else {
631         url = documentLoader->url();
632         originalURL = documentLoader->originalURL();
633     }
634
635     // Frames that have never successfully loaded any content
636     // may have no URL at all. Currently our history code can't
637     // deal with such things, so we nip that in the bud here.
638     // Later we may want to learn to live with nil for URL.
639     // See bug 3368236 and related bugs for more information.
640     if (url.isEmpty()) 
641         url = blankURL();
642     if (originalURL.isEmpty())
643         originalURL = blankURL();
644     
645     Frame* parentFrame = m_frame->tree()->parent();
646     String parent = parentFrame ? parentFrame->tree()->uniqueName() : "";
647     StringWithDirection title = documentLoader->title();
648
649     item->setURL(url);
650     item->setTarget(m_frame->tree()->uniqueName());
651     item->setParent(parent);
652     // FIXME: should store title directionality in history as well.
653     item->setTitle(title.string());
654     item->setOriginalURLString(originalURL.string());
655
656     if (!unreachableURL.isEmpty() || documentLoader->response().httpStatusCode() >= 400)
657         item->setLastVisitWasFailure(true);
658
659     // Save form state if this is a POST
660     item->setFormInfoFromRequest(documentLoader->request());
661 }
662
663 PassRefPtr<HistoryItem> HistoryController::createItem()
664 {
665     RefPtr<HistoryItem> item = HistoryItem::create();
666     initializeItem(item.get());
667     
668     // Set the item for which we will save document state
669     m_frameLoadComplete = false;
670     m_previousItem = m_currentItem;
671     m_currentItem = item;
672     
673     return item.release();
674 }
675
676 PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame* targetFrame, bool clipAtTarget)
677 {
678     RefPtr<HistoryItem> bfItem = createItem();
679     if (!m_frameLoadComplete)
680         saveScrollPositionAndViewStateToItem(m_previousItem.get());
681
682     if (!clipAtTarget || m_frame != targetFrame) {
683         // save frame state for items that aren't loading (khtml doesn't save those)
684         saveDocumentState();
685
686         // clipAtTarget is false for navigations within the same document, so
687         // we should copy the documentSequenceNumber over to the newly create
688         // item.  Non-target items are just clones, and they should therefore
689         // preserve the same itemSequenceNumber.
690         if (m_previousItem) {
691             if (m_frame != targetFrame)
692                 bfItem->setItemSequenceNumber(m_previousItem->itemSequenceNumber());
693             bfItem->setDocumentSequenceNumber(m_previousItem->documentSequenceNumber());
694         }
695
696         for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
697             FrameLoader* childLoader = child->loader();
698             bool hasChildLoaded = childLoader->frameHasLoaded();
699
700             // If the child is a frame corresponding to an <object> element that never loaded,
701             // we don't want to create a history item, because that causes fallback content
702             // to be ignored on reload.
703             
704             if (!(!hasChildLoaded && childLoader->isHostedByObjectElement()))
705                 bfItem->addChildItem(childLoader->history()->createItemTree(targetFrame, clipAtTarget));
706         }
707     }
708     // FIXME: Eliminate the isTargetItem flag in favor of itemSequenceNumber.
709     if (m_frame == targetFrame)
710         bfItem->setIsTargetItem(true);
711     return bfItem;
712 }
713
714 // The general idea here is to traverse the frame tree and the item tree in parallel,
715 // tracking whether each frame already has the content the item requests.  If there is
716 // a match, we set the provisional item and recurse.  Otherwise we will reload that
717 // frame and all its kids in recursiveGoToItem.
718 void HistoryController::recursiveSetProvisionalItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type)
719 {
720     ASSERT(item);
721
722     if (itemsAreClones(item, fromItem)) {
723         // Set provisional item, which will be committed in recursiveUpdateForCommit.
724         m_provisionalItem = item;
725
726         const HistoryItemVector& childItems = item->children();
727
728         int size = childItems.size();
729
730         for (int i = 0; i < size; ++i) {
731             String childFrameName = childItems[i]->target();
732             HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName);
733             ASSERT(fromChildItem);
734             Frame* childFrame = m_frame->tree()->child(childFrameName);
735             ASSERT(childFrame);
736             childFrame->loader()->history()->recursiveSetProvisionalItem(childItems[i].get(), fromChildItem, type);
737         }
738     }
739 }
740
741 // We now traverse the frame tree and item tree a second time, loading frames that
742 // do have the content the item requests.
743 void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type)
744 {
745     ASSERT(item);
746
747     if (itemsAreClones(item, fromItem)) {
748         // Just iterate over the rest, looking for frames to navigate.
749         const HistoryItemVector& childItems = item->children();
750
751         int size = childItems.size();
752         for (int i = 0; i < size; ++i) {
753             String childFrameName = childItems[i]->target();
754             HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName);
755             ASSERT(fromChildItem);
756             Frame* childFrame = m_frame->tree()->child(childFrameName);
757             ASSERT(childFrame);
758             childFrame->loader()->history()->recursiveGoToItem(childItems[i].get(), fromChildItem, type);
759         }
760     } else {
761         m_frame->loader()->loadItem(item, type);
762     }
763 }
764
765 bool HistoryController::itemsAreClones(HistoryItem* item1, HistoryItem* item2) const
766 {
767     // If the item we're going to is a clone of the item we're at, then we do
768     // not need to load it again.  The current frame tree and the frame tree
769     // snapshot in the item have to match.
770     // Note: Some clients treat a navigation to the current history item as
771     // a reload.  Thus, if item1 and item2 are the same, we need to create a
772     // new document and should not consider them clones.
773     // (See http://webkit.org/b/35532 for details.)
774     return item1
775         && item2
776         && item1 != item2
777         && item1->itemSequenceNumber() == item2->itemSequenceNumber()
778         && currentFramesMatchItem(item1)
779         && item2->hasSameFrames(item1);
780 }
781
782 // Helper method that determines whether the current frame tree matches given history item's.
783 bool HistoryController::currentFramesMatchItem(HistoryItem* item) const
784 {
785     if ((!m_frame->tree()->uniqueName().isEmpty() || !item->target().isEmpty()) && m_frame->tree()->uniqueName() != item->target())
786         return false;
787         
788     const HistoryItemVector& childItems = item->children();
789     if (childItems.size() != m_frame->tree()->childCount())
790         return false;
791     
792     unsigned size = childItems.size();
793     for (unsigned i = 0; i < size; ++i) {
794         if (!m_frame->tree()->child(childItems[i]->target()))
795             return false;
796     }
797     
798     return true;
799 }
800
801 void HistoryController::updateBackForwardListClippedAtTarget(bool doClip)
802 {
803     // In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.  
804     // The item that was the target of the user's navigation is designated as the "targetItem".  
805     // When this function is called with doClip=true we're able to create the whole tree except for the target's children, 
806     // which will be loaded in the future. That part of the tree will be filled out as the child loads are committed.
807
808     Page* page = m_frame->page();
809     if (!page)
810         return;
811
812     if (m_frame->loader()->documentLoader()->urlForHistory().isEmpty())
813         return;
814
815     Frame* mainFrame = page->mainFrame();
816     ASSERT(mainFrame);
817     FrameLoader* frameLoader = mainFrame->loader();
818
819     frameLoader->checkDidPerformFirstNavigation();
820
821     RefPtr<HistoryItem> topItem = frameLoader->history()->createItemTree(m_frame, doClip);
822     LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", topItem.get(), m_frame->loader()->documentLoader()->url().string().ascii().data());
823     page->backForward()->addItem(topItem.release());
824 }
825
826 void HistoryController::updateCurrentItem()
827 {
828     if (!m_currentItem)
829         return;
830
831     DocumentLoader* documentLoader = m_frame->loader()->documentLoader();
832
833     if (!documentLoader->unreachableURL().isEmpty())
834         return;
835
836     if (m_currentItem->url() != documentLoader->url()) {
837         // We ended up on a completely different URL this time, so the HistoryItem
838         // needs to be re-initialized.  Preserve the isTargetItem flag as it is a
839         // property of how this HistoryItem was originally created and is not
840         // dependent on the document.
841         bool isTargetItem = m_currentItem->isTargetItem();
842         m_currentItem->reset();
843         initializeItem(m_currentItem.get());
844         m_currentItem->setIsTargetItem(isTargetItem);
845     } else {
846         // Even if the final URL didn't change, the form data may have changed.
847         m_currentItem->setFormInfoFromRequest(documentLoader->request());
848     }
849 }
850
851 void HistoryController::pushState(PassRefPtr<SerializedScriptValue> stateObject, const String& title, const String& urlString)
852 {
853     if (!m_currentItem)
854         return;
855
856     Page* page = m_frame->page();
857     ASSERT(page);
858
859     // Get a HistoryItem tree for the current frame tree.
860     RefPtr<HistoryItem> topItem = page->mainFrame()->loader()->history()->createItemTree(m_frame, false);
861     
862     // Override data in the current item (created by createItemTree) to reflect
863     // the pushState() arguments.
864     m_currentItem->setTitle(title);
865     m_currentItem->setStateObject(stateObject);
866     m_currentItem->setURLString(urlString);
867
868     page->backForward()->addItem(topItem.release());
869
870     Settings* settings = m_frame->settings();
871     if (!settings || settings->privateBrowsingEnabled())
872         return;
873
874     addVisitedLink(page, KURL(ParsedURLString, urlString));
875     m_frame->loader()->client()->updateGlobalHistory();
876
877 }
878
879 void HistoryController::replaceState(PassRefPtr<SerializedScriptValue> stateObject, const String& title, const String& urlString)
880 {
881     if (!m_currentItem)
882         return;
883
884     if (!urlString.isEmpty())
885         m_currentItem->setURLString(urlString);
886     m_currentItem->setTitle(title);
887     m_currentItem->setStateObject(stateObject);
888     m_currentItem->setFormData(0);
889     m_currentItem->setFormContentType(String());
890
891     Settings* settings = m_frame->settings();
892     if (!settings || settings->privateBrowsingEnabled())
893         return;
894
895     ASSERT(m_frame->page());
896     addVisitedLink(m_frame->page(), KURL(ParsedURLString, urlString));
897     m_frame->loader()->client()->updateGlobalHistory();
898 }
899
900 } // namespace WebCore