Home | History | Annotate | Download | only in loader
      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