Home | History | Annotate | Download | only in page
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All Rights Reserved.
      3  * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "config.h"
     22 #include "Page.h"
     23 
     24 #include "BackForwardList.h"
     25 #include "Base64.h"
     26 #include "CSSStyleSelector.h"
     27 #include "Chrome.h"
     28 #include "ChromeClient.h"
     29 #include "ContextMenuClient.h"
     30 #include "ContextMenuController.h"
     31 #include "DOMWindow.h"
     32 #include "DragController.h"
     33 #include "ExceptionCode.h"
     34 #include "EditorClient.h"
     35 #include "EventNames.h"
     36 #include "Event.h"
     37 #include "FileSystem.h"
     38 #include "FocusController.h"
     39 #include "Frame.h"
     40 #include "FrameLoader.h"
     41 #include "FrameLoaderClient.h"
     42 #include "FrameTree.h"
     43 #include "FrameView.h"
     44 #include "HTMLElement.h"
     45 #include "HistoryItem.h"
     46 #include "InspectorController.h"
     47 #include "InspectorTimelineAgent.h"
     48 #include "Logging.h"
     49 #include "Navigator.h"
     50 #include "NetworkStateNotifier.h"
     51 #include "PageGroup.h"
     52 #include "PluginData.h"
     53 #include "PluginHalter.h"
     54 #include "ProgressTracker.h"
     55 #include "RenderWidget.h"
     56 #include "RenderTheme.h"
     57 #include "ScriptController.h"
     58 #include "SelectionController.h"
     59 #include "Settings.h"
     60 #include "StringHash.h"
     61 #include "TextResourceDecoder.h"
     62 #include "Widget.h"
     63 #include <wtf/HashMap.h>
     64 #include <wtf/RefCountedLeakCounter.h>
     65 #include <wtf/StdLibExtras.h>
     66 
     67 #if ENABLE(DOM_STORAGE)
     68 #include "StorageArea.h"
     69 #include "StorageNamespace.h"
     70 #endif
     71 
     72 #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC)
     73 #include "JavaScriptDebugServer.h"
     74 #endif
     75 
     76 #if ENABLE(WML)
     77 #include "WMLPageState.h"
     78 #endif
     79 
     80 #if ENABLE(CLIENT_BASED_GEOLOCATION)
     81 #include "GeolocationController.h"
     82 #endif
     83 
     84 #if PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED)
     85 #include "PackageNotifier.h"
     86 #endif
     87 
     88 namespace WebCore {
     89 
     90 static HashSet<Page*>* allPages;
     91 
     92 #ifndef NDEBUG
     93 static WTF::RefCountedLeakCounter pageCounter("Page");
     94 #endif
     95 
     96 static void networkStateChanged()
     97 {
     98     Vector<RefPtr<Frame> > frames;
     99 
    100     // Get all the frames of all the pages in all the page groups
    101     HashSet<Page*>::iterator end = allPages->end();
    102     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
    103         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
    104             frames.append(frame);
    105     }
    106 
    107     AtomicString eventName = networkStateNotifier().onLine() ? eventNames().onlineEvent : eventNames().offlineEvent;
    108     for (unsigned i = 0; i < frames.size(); i++)
    109         frames[i]->document()->dispatchWindowEvent(Event::create(eventName, false, false));
    110 }
    111 
    112 #if PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED)
    113 static void onPackageResultAvailable()
    114 {
    115     HashSet<Page*>::iterator end = allPages->end();
    116     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
    117         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
    118             frame->domWindow()->navigator()->onPackageResult();
    119     }
    120 }
    121 #endif
    122 
    123 Page::Page(ChromeClient* chromeClient, ContextMenuClient* contextMenuClient, EditorClient* editorClient, DragClient* dragClient, InspectorClient* inspectorClient, PluginHalterClient* pluginHalterClient, GeolocationControllerClient* geolocationControllerClient)
    124     : m_chrome(new Chrome(this, chromeClient))
    125     , m_dragCaretController(new SelectionController(0, true))
    126 #if ENABLE(DRAG_SUPPORT)
    127     , m_dragController(new DragController(this, dragClient))
    128 #endif
    129     , m_focusController(new FocusController(this))
    130 #if ENABLE(CONTEXT_MENUS)
    131     , m_contextMenuController(new ContextMenuController(this, contextMenuClient))
    132 #endif
    133 #if ENABLE(INSPECTOR)
    134     , m_inspectorController(new InspectorController(this, inspectorClient))
    135 #endif
    136 #if ENABLE(CLIENT_BASED_GEOLOCATION)
    137     , m_geolocationController(new GeolocationController(this, geolocationControllerClient))
    138 #endif
    139     , m_settings(new Settings(this))
    140     , m_progress(new ProgressTracker)
    141     , m_backForwardList(BackForwardList::create(this))
    142     , m_theme(RenderTheme::themeForPage(this))
    143     , m_editorClient(editorClient)
    144     , m_frameCount(0)
    145     , m_openedByDOM(false)
    146     , m_tabKeyCyclesThroughElements(true)
    147     , m_defersLoading(false)
    148     , m_inLowQualityInterpolationMode(false)
    149     , m_cookieEnabled(true)
    150     , m_areMemoryCacheClientCallsEnabled(true)
    151     , m_mediaVolume(1)
    152     , m_javaScriptURLsAreAllowed(true)
    153 #if ENABLE(INSPECTOR)
    154     , m_parentInspectorController(0)
    155 #endif
    156     , m_didLoadUserStyleSheet(false)
    157     , m_userStyleSheetModificationTime(0)
    158     , m_group(0)
    159     , m_debugger(0)
    160     , m_customHTMLTokenizerTimeDelay(-1)
    161     , m_customHTMLTokenizerChunkSize(-1)
    162     , m_canStartPlugins(true)
    163 {
    164 #if !ENABLE(CONTEXT_MENUS)
    165     UNUSED_PARAM(contextMenuClient);
    166 #endif
    167 #if !ENABLE(DRAG_SUPPORT)
    168     UNUSED_PARAM(dragClient);
    169 #endif
    170 #if !ENABLE(INSPECTOR)
    171     UNUSED_PARAM(inspectorClient);
    172 #endif
    173 #if !ENABLE(CLIENT_BASED_GEOLOCATION)
    174     UNUSED_PARAM(geolocationControllerClient);
    175 #endif
    176 
    177     if (!allPages) {
    178         allPages = new HashSet<Page*>;
    179 
    180         networkStateNotifier().setNetworkStateChangedFunction(networkStateChanged);
    181 #if PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED)
    182         packageNotifier().setOnResultAvailable(onPackageResultAvailable);
    183 #endif
    184     }
    185 
    186     ASSERT(!allPages->contains(this));
    187     allPages->add(this);
    188 
    189     if (pluginHalterClient) {
    190         m_pluginHalter.set(new PluginHalter(pluginHalterClient));
    191         m_pluginHalter->setPluginAllowedRunTime(m_settings->pluginAllowedRunTime());
    192     }
    193 
    194 #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC)
    195     JavaScriptDebugServer::shared().pageCreated(this);
    196 #endif
    197 
    198 #ifndef NDEBUG
    199     pageCounter.increment();
    200 #endif
    201 }
    202 
    203 Page::~Page()
    204 {
    205     m_mainFrame->setView(0);
    206     setGroupName(String());
    207     allPages->remove(this);
    208 
    209     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
    210         frame->pageDestroyed();
    211 
    212     m_editorClient->pageDestroyed();
    213     if (m_pluginData)
    214         m_pluginData->disconnectPage();
    215 
    216 #if ENABLE(INSPECTOR)
    217     if (m_parentInspectorController)
    218         m_parentInspectorController->pageDestroyed();
    219     m_inspectorController->inspectedPageDestroyed();
    220 #endif
    221 
    222     m_backForwardList->close();
    223 
    224 #ifndef NDEBUG
    225     pageCounter.decrement();
    226 
    227     // Cancel keepAlive timers, to ensure we release all Frames before exiting.
    228     // It's safe to do this because we prohibit closing a Page while JavaScript
    229     // is executing.
    230     Frame::cancelAllKeepAlive();
    231 #endif
    232 }
    233 
    234 void Page::setMainFrame(PassRefPtr<Frame> mainFrame)
    235 {
    236     ASSERT(!m_mainFrame); // Should only be called during initialization
    237     m_mainFrame = mainFrame;
    238 }
    239 
    240 bool Page::openedByDOM() const
    241 {
    242     return m_openedByDOM;
    243 }
    244 
    245 void Page::setOpenedByDOM()
    246 {
    247     m_openedByDOM = true;
    248 }
    249 
    250 BackForwardList* Page::backForwardList()
    251 {
    252     return m_backForwardList.get();
    253 }
    254 
    255 bool Page::goBack()
    256 {
    257     HistoryItem* item = m_backForwardList->backItem();
    258 
    259     if (item) {
    260         goToItem(item, FrameLoadTypeBack);
    261         return true;
    262     }
    263     return false;
    264 }
    265 
    266 bool Page::goForward()
    267 {
    268     HistoryItem* item = m_backForwardList->forwardItem();
    269 
    270     if (item) {
    271         goToItem(item, FrameLoadTypeForward);
    272         return true;
    273     }
    274     return false;
    275 }
    276 
    277 bool Page::canGoBackOrForward(int distance) const
    278 {
    279     if (distance == 0)
    280         return true;
    281     if (distance > 0 && distance <= m_backForwardList->forwardListCount())
    282         return true;
    283     if (distance < 0 && -distance <= m_backForwardList->backListCount())
    284         return true;
    285     return false;
    286 }
    287 
    288 void Page::goBackOrForward(int distance)
    289 {
    290     if (distance == 0)
    291         return;
    292 
    293     HistoryItem* item = m_backForwardList->itemAtIndex(distance);
    294     if (!item) {
    295         if (distance > 0) {
    296             int forwardListCount = m_backForwardList->forwardListCount();
    297             if (forwardListCount > 0)
    298                 item = m_backForwardList->itemAtIndex(forwardListCount);
    299         } else {
    300             int backListCount = m_backForwardList->backListCount();
    301             if (backListCount > 0)
    302                 item = m_backForwardList->itemAtIndex(-backListCount);
    303         }
    304     }
    305 
    306     ASSERT(item); // we should not reach this line with an empty back/forward list
    307     if (item)
    308         goToItem(item, FrameLoadTypeIndexedBackForward);
    309 }
    310 
    311 void Page::goToItem(HistoryItem* item, FrameLoadType type)
    312 {
    313     // Abort any current load unless we're navigating the current document to a new state object
    314     HistoryItem* currentItem = m_mainFrame->loader()->history()->currentItem();
    315     if (!item->stateObject() || !currentItem || item->documentSequenceNumber() != currentItem->documentSequenceNumber()) {
    316         // Define what to do with any open database connections. By default we stop them and terminate the database thread.
    317         DatabasePolicy databasePolicy = DatabasePolicyStop;
    318 
    319 #if ENABLE(DATABASE)
    320         // If we're navigating the history via a fragment on the same document, then we do not want to stop databases.
    321         const KURL& currentURL = m_mainFrame->loader()->url();
    322         const KURL& newURL = item->url();
    323 
    324         if (newURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(currentURL, newURL))
    325             databasePolicy = DatabasePolicyContinue;
    326 #endif
    327 
    328         m_mainFrame->loader()->stopAllLoaders(databasePolicy);
    329     }
    330 
    331     m_mainFrame->loader()->history()->goToItem(item, type);
    332 }
    333 
    334 int Page::getHistoryLength()
    335 {
    336     return m_backForwardList->backListCount() + 1 + m_backForwardList->forwardListCount();
    337 }
    338 
    339 void Page::setGlobalHistoryItem(HistoryItem* item)
    340 {
    341     m_globalHistoryItem = item;
    342 }
    343 
    344 void Page::setGroupName(const String& name)
    345 {
    346     if (m_group && !m_group->name().isEmpty()) {
    347         ASSERT(m_group != m_singlePageGroup.get());
    348         ASSERT(!m_singlePageGroup);
    349         m_group->removePage(this);
    350     }
    351 
    352     if (name.isEmpty())
    353         m_group = m_singlePageGroup.get();
    354     else {
    355         m_singlePageGroup.clear();
    356         m_group = PageGroup::pageGroup(name);
    357         m_group->addPage(this);
    358     }
    359 }
    360 
    361 const String& Page::groupName() const
    362 {
    363     DEFINE_STATIC_LOCAL(String, nullString, ());
    364     return m_group ? m_group->name() : nullString;
    365 }
    366 
    367 void Page::initGroup()
    368 {
    369     ASSERT(!m_singlePageGroup);
    370     ASSERT(!m_group);
    371     m_singlePageGroup.set(new PageGroup(this));
    372     m_group = m_singlePageGroup.get();
    373 }
    374 
    375 void Page::setNeedsReapplyStyles()
    376 {
    377     if (!allPages)
    378         return;
    379     HashSet<Page*>::iterator end = allPages->end();
    380     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
    381         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
    382             frame->setNeedsReapplyStyles();
    383 }
    384 
    385 void Page::refreshPlugins(bool reload)
    386 {
    387     if (!allPages)
    388         return;
    389 
    390     PluginData::refresh();
    391 
    392     Vector<RefPtr<Frame> > framesNeedingReload;
    393 
    394     HashSet<Page*>::iterator end = allPages->end();
    395     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
    396         Page* page = *it;
    397 
    398         // Clear out the page's plug-in data.
    399         if (page->m_pluginData) {
    400             page->m_pluginData->disconnectPage();
    401             page->m_pluginData = 0;
    402         }
    403 
    404         if (reload) {
    405             for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    406                 if (frame->loader()->containsPlugins())
    407                     framesNeedingReload.append(frame);
    408             }
    409         }
    410     }
    411 
    412     for (size_t i = 0; i < framesNeedingReload.size(); ++i)
    413         framesNeedingReload[i]->loader()->reload();
    414 }
    415 
    416 PluginData* Page::pluginData() const
    417 {
    418     if (!settings()->arePluginsEnabled())
    419         return 0;
    420     if (!m_pluginData)
    421         m_pluginData = PluginData::create(this);
    422     return m_pluginData.get();
    423 }
    424 
    425 void Page::addUnstartedPlugin(PluginView* view)
    426 {
    427     ASSERT(!m_canStartPlugins);
    428     m_unstartedPlugins.add(view);
    429 }
    430 
    431 void Page::removeUnstartedPlugin(PluginView* view)
    432 {
    433     ASSERT(!m_canStartPlugins);
    434     ASSERT(m_unstartedPlugins.contains(view));
    435     m_unstartedPlugins.remove(view);
    436 }
    437 
    438 static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag)
    439 {
    440     return forward
    441         ? curr->tree()->traverseNextWithWrap(wrapFlag)
    442         : curr->tree()->traversePreviousWithWrap(wrapFlag);
    443 }
    444 
    445 bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, FindDirection direction, bool shouldWrap)
    446 {
    447     if (target.isEmpty() || !mainFrame())
    448         return false;
    449 
    450     Frame* frame = focusController()->focusedOrMainFrame();
    451     Frame* startFrame = frame;
    452     do {
    453         if (frame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) {
    454             if (frame != startFrame)
    455                 startFrame->selection()->clear();
    456             focusController()->setFocusedFrame(frame);
    457             return true;
    458         }
    459         frame = incrementFrame(frame, direction == FindDirectionForward, shouldWrap);
    460     } while (frame && frame != startFrame);
    461 
    462     // Search contents of startFrame, on the other side of the selection that we did earlier.
    463     // We cheat a bit and just research with wrap on
    464     if (shouldWrap && !startFrame->selection()->isNone()) {
    465         bool found = startFrame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true);
    466         focusController()->setFocusedFrame(frame);
    467         return found;
    468     }
    469 
    470     return false;
    471 }
    472 
    473 unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit)
    474 {
    475     if (target.isEmpty() || !mainFrame())
    476         return 0;
    477 
    478     unsigned matches = 0;
    479 
    480     Frame* frame = mainFrame();
    481     do {
    482         frame->setMarkedTextMatchesAreHighlighted(shouldHighlight);
    483         matches += frame->markAllMatchesForText(target, caseSensitivity == TextCaseSensitive, (limit == 0) ? 0 : (limit - matches));
    484         frame = incrementFrame(frame, true, false);
    485     } while (frame);
    486 
    487     return matches;
    488 }
    489 
    490 void Page::unmarkAllTextMatches()
    491 {
    492     if (!mainFrame())
    493         return;
    494 
    495     Frame* frame = mainFrame();
    496     do {
    497         frame->document()->removeMarkers(DocumentMarker::TextMatch);
    498         frame = incrementFrame(frame, true, false);
    499     } while (frame);
    500 }
    501 
    502 const VisibleSelection& Page::selection() const
    503 {
    504     return focusController()->focusedOrMainFrame()->selection()->selection();
    505 }
    506 
    507 void Page::setDefersLoading(bool defers)
    508 {
    509     if (!m_settings->loadDeferringEnabled())
    510         return;
    511 
    512     if (defers == m_defersLoading)
    513         return;
    514 
    515     m_defersLoading = defers;
    516     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
    517         frame->loader()->setDefersLoading(defers);
    518 }
    519 
    520 void Page::clearUndoRedoOperations()
    521 {
    522     m_editorClient->clearUndoRedoOperations();
    523 }
    524 
    525 bool Page::inLowQualityImageInterpolationMode() const
    526 {
    527     return m_inLowQualityInterpolationMode;
    528 }
    529 
    530 void Page::setInLowQualityImageInterpolationMode(bool mode)
    531 {
    532     m_inLowQualityInterpolationMode = mode;
    533 }
    534 
    535 void Page::setMediaVolume(float volume)
    536 {
    537     if (volume < 0 || volume > 1)
    538         return;
    539 
    540     if (m_mediaVolume == volume)
    541         return;
    542 
    543     m_mediaVolume = volume;
    544     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    545         frame->document()->mediaVolumeDidChange();
    546     }
    547 }
    548 
    549 void Page::didMoveOnscreen()
    550 {
    551     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    552         if (frame->view())
    553             frame->view()->didMoveOnscreen();
    554     }
    555 }
    556 
    557 void Page::willMoveOffscreen()
    558 {
    559     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    560         if (frame->view())
    561             frame->view()->willMoveOffscreen();
    562     }
    563 }
    564 
    565 void Page::userStyleSheetLocationChanged()
    566 {
    567     // FIXME: Eventually we will move to a model of just being handed the sheet
    568     // text instead of loading the URL ourselves.
    569     KURL url = m_settings->userStyleSheetLocation();
    570     if (url.isLocalFile())
    571         m_userStyleSheetPath = url.fileSystemPath();
    572     else
    573         m_userStyleSheetPath = String();
    574 
    575     m_didLoadUserStyleSheet = false;
    576     m_userStyleSheet = String();
    577     m_userStyleSheetModificationTime = 0;
    578 
    579     // Data URLs with base64-encoded UTF-8 style sheets are common. We can process them
    580     // synchronously and avoid using a loader.
    581     if (url.protocolIs("data") && url.string().startsWith("data:text/css;charset=utf-8;base64,")) {
    582         m_didLoadUserStyleSheet = true;
    583 
    584         const unsigned prefixLength = 35;
    585         Vector<char> encodedData(url.string().length() - prefixLength);
    586         for (unsigned i = prefixLength; i < url.string().length(); ++i)
    587             encodedData[i - prefixLength] = static_cast<char>(url.string()[i]);
    588 
    589         Vector<char> styleSheetAsUTF8;
    590         if (base64Decode(encodedData, styleSheetAsUTF8))
    591             m_userStyleSheet = String::fromUTF8(styleSheetAsUTF8.data(), styleSheetAsUTF8.size());
    592     }
    593 
    594     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    595         if (frame->document())
    596             frame->document()->clearPageUserSheet();
    597     }
    598 }
    599 
    600 const String& Page::userStyleSheet() const
    601 {
    602     if (m_userStyleSheetPath.isEmpty())
    603         return m_userStyleSheet;
    604 
    605     time_t modTime;
    606     if (!getFileModificationTime(m_userStyleSheetPath, modTime)) {
    607         // The stylesheet either doesn't exist, was just deleted, or is
    608         // otherwise unreadable. If we've read the stylesheet before, we should
    609         // throw away that data now as it no longer represents what's on disk.
    610         m_userStyleSheet = String();
    611         return m_userStyleSheet;
    612     }
    613 
    614     // If the stylesheet hasn't changed since the last time we read it, we can
    615     // just return the old data.
    616     if (m_didLoadUserStyleSheet && modTime <= m_userStyleSheetModificationTime)
    617         return m_userStyleSheet;
    618 
    619     m_didLoadUserStyleSheet = true;
    620     m_userStyleSheet = String();
    621     m_userStyleSheetModificationTime = modTime;
    622 
    623     // FIXME: It would be better to load this asynchronously to avoid blocking
    624     // the process, but we will first need to create an asynchronous loading
    625     // mechanism that is not tied to a particular Frame. We will also have to
    626     // determine what our behavior should be before the stylesheet is loaded
    627     // and what should happen when it finishes loading, especially with respect
    628     // to when the load event fires, when Document::close is called, and when
    629     // layout/paint are allowed to happen.
    630     RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_userStyleSheetPath);
    631     if (!data)
    632         return m_userStyleSheet;
    633 
    634     RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/css");
    635     m_userStyleSheet = decoder->decode(data->data(), data->size());
    636     m_userStyleSheet += decoder->flush();
    637 
    638     return m_userStyleSheet;
    639 }
    640 
    641 void Page::removeAllVisitedLinks()
    642 {
    643     if (!allPages)
    644         return;
    645     HashSet<PageGroup*> groups;
    646     HashSet<Page*>::iterator pagesEnd = allPages->end();
    647     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
    648         if (PageGroup* group = (*it)->groupPtr())
    649             groups.add(group);
    650     }
    651     HashSet<PageGroup*>::iterator groupsEnd = groups.end();
    652     for (HashSet<PageGroup*>::iterator it = groups.begin(); it != groupsEnd; ++it)
    653         (*it)->removeVisitedLinks();
    654 }
    655 
    656 void Page::allVisitedStateChanged(PageGroup* group)
    657 {
    658     ASSERT(group);
    659     if (!allPages)
    660         return;
    661 
    662     HashSet<Page*>::iterator pagesEnd = allPages->end();
    663     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
    664         Page* page = *it;
    665         if (page->m_group != group)
    666             continue;
    667         for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) {
    668             if (CSSStyleSelector* styleSelector = frame->document()->styleSelector())
    669                 styleSelector->allVisitedStateChanged();
    670         }
    671     }
    672 }
    673 
    674 void Page::visitedStateChanged(PageGroup* group, LinkHash visitedLinkHash)
    675 {
    676     ASSERT(group);
    677     if (!allPages)
    678         return;
    679 
    680     HashSet<Page*>::iterator pagesEnd = allPages->end();
    681     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
    682         Page* page = *it;
    683         if (page->m_group != group)
    684             continue;
    685         for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) {
    686             if (CSSStyleSelector* styleSelector = frame->document()->styleSelector())
    687                 styleSelector->visitedStateChanged(visitedLinkHash);
    688         }
    689     }
    690 }
    691 
    692 void Page::setDebuggerForAllPages(JSC::Debugger* debugger)
    693 {
    694     ASSERT(allPages);
    695 
    696     HashSet<Page*>::iterator end = allPages->end();
    697     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
    698         (*it)->setDebugger(debugger);
    699 }
    700 
    701 void Page::setDebugger(JSC::Debugger* debugger)
    702 {
    703     if (m_debugger == debugger)
    704         return;
    705 
    706     m_debugger = debugger;
    707 
    708     for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree()->traverseNext())
    709         frame->script()->attachDebugger(m_debugger);
    710 }
    711 
    712 #if ENABLE(DOM_STORAGE)
    713 StorageNamespace* Page::sessionStorage(bool optionalCreate)
    714 {
    715     if (!m_sessionStorage && optionalCreate)
    716         m_sessionStorage = StorageNamespace::sessionStorageNamespace(this);
    717 
    718     return m_sessionStorage.get();
    719 }
    720 
    721 void Page::setSessionStorage(PassRefPtr<StorageNamespace> newStorage)
    722 {
    723     m_sessionStorage = newStorage;
    724 }
    725 #endif
    726 
    727 #if ENABLE(WML)
    728 WMLPageState* Page::wmlPageState()
    729 {
    730     if (!m_wmlPageState)
    731         m_wmlPageState.set(new WMLPageState(this));
    732     return m_wmlPageState.get();
    733 }
    734 #endif
    735 
    736 void Page::setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay)
    737 {
    738     if (customHTMLTokenizerTimeDelay < 0) {
    739         m_customHTMLTokenizerTimeDelay = -1;
    740         return;
    741     }
    742     m_customHTMLTokenizerTimeDelay = customHTMLTokenizerTimeDelay;
    743 }
    744 
    745 void Page::setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize)
    746 {
    747     if (customHTMLTokenizerChunkSize < 0) {
    748         m_customHTMLTokenizerChunkSize = -1;
    749         return;
    750     }
    751     m_customHTMLTokenizerChunkSize = customHTMLTokenizerChunkSize;
    752 }
    753 
    754 void Page::setMemoryCacheClientCallsEnabled(bool enabled)
    755 {
    756     if (m_areMemoryCacheClientCallsEnabled == enabled)
    757         return;
    758 
    759     m_areMemoryCacheClientCallsEnabled = enabled;
    760     if (!enabled)
    761         return;
    762 
    763     for (RefPtr<Frame> frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
    764         frame->loader()->tellClientAboutPastMemoryCacheLoads();
    765 }
    766 
    767 void Page::setJavaScriptURLsAreAllowed(bool areAllowed)
    768 {
    769     m_javaScriptURLsAreAllowed = areAllowed;
    770 }
    771 
    772 bool Page::javaScriptURLsAreAllowed() const
    773 {
    774     return m_javaScriptURLsAreAllowed;
    775 }
    776 
    777 #if ENABLE(INSPECTOR)
    778 InspectorTimelineAgent* Page::inspectorTimelineAgent() const
    779 {
    780     return m_inspectorController->timelineAgent();
    781 }
    782 #endif
    783 
    784 void Page::pluginAllowedRunTimeChanged()
    785 {
    786     if (m_pluginHalter)
    787         m_pluginHalter->setPluginAllowedRunTime(m_settings->pluginAllowedRunTime());
    788 }
    789 
    790 void Page::didStartPlugin(HaltablePlugin* obj)
    791 {
    792     if (m_pluginHalter)
    793         m_pluginHalter->didStartPlugin(obj);
    794 }
    795 
    796 void Page::didStopPlugin(HaltablePlugin* obj)
    797 {
    798     if (m_pluginHalter)
    799         m_pluginHalter->didStopPlugin(obj);
    800 }
    801 
    802 #if !ASSERT_DISABLED
    803 void Page::checkFrameCountConsistency() const
    804 {
    805     ASSERT(m_frameCount >= 0);
    806 
    807     int frameCount = 0;
    808     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
    809         ++frameCount;
    810 
    811     ASSERT(m_frameCount + 1 == frameCount);
    812 }
    813 #endif
    814 } // namespace WebCore
    815