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 ENABLE(INSPECTOR)
    214     if (m_parentInspectorController)
    215         m_parentInspectorController->pageDestroyed();
    216     m_inspectorController->inspectedPageDestroyed();
    217 #endif
    218 
    219     m_backForwardList->close();
    220 
    221 #ifndef NDEBUG
    222     pageCounter.decrement();
    223 
    224     // Cancel keepAlive timers, to ensure we release all Frames before exiting.
    225     // It's safe to do this because we prohibit closing a Page while JavaScript
    226     // is executing.
    227     Frame::cancelAllKeepAlive();
    228 #endif
    229 }
    230 
    231 void Page::setMainFrame(PassRefPtr<Frame> mainFrame)
    232 {
    233     ASSERT(!m_mainFrame); // Should only be called during initialization
    234     m_mainFrame = mainFrame;
    235 }
    236 
    237 bool Page::openedByDOM() const
    238 {
    239     return m_openedByDOM;
    240 }
    241 
    242 void Page::setOpenedByDOM()
    243 {
    244     m_openedByDOM = true;
    245 }
    246 
    247 BackForwardList* Page::backForwardList()
    248 {
    249     return m_backForwardList.get();
    250 }
    251 
    252 bool Page::goBack()
    253 {
    254     HistoryItem* item = m_backForwardList->backItem();
    255 
    256     if (item) {
    257         goToItem(item, FrameLoadTypeBack);
    258         return true;
    259     }
    260     return false;
    261 }
    262 
    263 bool Page::goForward()
    264 {
    265     HistoryItem* item = m_backForwardList->forwardItem();
    266 
    267     if (item) {
    268         goToItem(item, FrameLoadTypeForward);
    269         return true;
    270     }
    271     return false;
    272 }
    273 
    274 bool Page::canGoBackOrForward(int distance) const
    275 {
    276     if (distance == 0)
    277         return true;
    278     if (distance > 0 && distance <= m_backForwardList->forwardListCount())
    279         return true;
    280     if (distance < 0 && -distance <= m_backForwardList->backListCount())
    281         return true;
    282     return false;
    283 }
    284 
    285 void Page::goBackOrForward(int distance)
    286 {
    287     if (distance == 0)
    288         return;
    289 
    290     HistoryItem* item = m_backForwardList->itemAtIndex(distance);
    291     if (!item) {
    292         if (distance > 0) {
    293             int forwardListCount = m_backForwardList->forwardListCount();
    294             if (forwardListCount > 0)
    295                 item = m_backForwardList->itemAtIndex(forwardListCount);
    296         } else {
    297             int backListCount = m_backForwardList->backListCount();
    298             if (backListCount > 0)
    299                 item = m_backForwardList->itemAtIndex(-backListCount);
    300         }
    301     }
    302 
    303     ASSERT(item); // we should not reach this line with an empty back/forward list
    304     if (item)
    305         goToItem(item, FrameLoadTypeIndexedBackForward);
    306 }
    307 
    308 void Page::goToItem(HistoryItem* item, FrameLoadType type)
    309 {
    310     // Abort any current load unless we're navigating the current document to a new state object
    311     HistoryItem* currentItem = m_mainFrame->loader()->history()->currentItem();
    312     if (!item->stateObject() || !currentItem || item->documentSequenceNumber() != currentItem->documentSequenceNumber()) {
    313         // Define what to do with any open database connections. By default we stop them and terminate the database thread.
    314         DatabasePolicy databasePolicy = DatabasePolicyStop;
    315 
    316 #if ENABLE(DATABASE)
    317         // If we're navigating the history via a fragment on the same document, then we do not want to stop databases.
    318         const KURL& currentURL = m_mainFrame->loader()->url();
    319         const KURL& newURL = item->url();
    320 
    321         if (newURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(currentURL, newURL))
    322             databasePolicy = DatabasePolicyContinue;
    323 #endif
    324 
    325         m_mainFrame->loader()->stopAllLoaders(databasePolicy);
    326     }
    327 
    328     m_mainFrame->loader()->history()->goToItem(item, type);
    329 }
    330 
    331 int Page::getHistoryLength()
    332 {
    333     return m_backForwardList->backListCount() + 1 + m_backForwardList->forwardListCount();
    334 }
    335 
    336 void Page::setGlobalHistoryItem(HistoryItem* item)
    337 {
    338     m_globalHistoryItem = item;
    339 }
    340 
    341 void Page::setGroupName(const String& name)
    342 {
    343     if (m_group && !m_group->name().isEmpty()) {
    344         ASSERT(m_group != m_singlePageGroup.get());
    345         ASSERT(!m_singlePageGroup);
    346         m_group->removePage(this);
    347     }
    348 
    349     if (name.isEmpty())
    350         m_group = m_singlePageGroup.get();
    351     else {
    352         m_singlePageGroup.clear();
    353         m_group = PageGroup::pageGroup(name);
    354         m_group->addPage(this);
    355     }
    356 }
    357 
    358 const String& Page::groupName() const
    359 {
    360     DEFINE_STATIC_LOCAL(String, nullString, ());
    361     return m_group ? m_group->name() : nullString;
    362 }
    363 
    364 void Page::initGroup()
    365 {
    366     ASSERT(!m_singlePageGroup);
    367     ASSERT(!m_group);
    368     m_singlePageGroup.set(new PageGroup(this));
    369     m_group = m_singlePageGroup.get();
    370 }
    371 
    372 void Page::setNeedsReapplyStyles()
    373 {
    374     if (!allPages)
    375         return;
    376     HashSet<Page*>::iterator end = allPages->end();
    377     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
    378         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
    379             frame->setNeedsReapplyStyles();
    380 }
    381 
    382 void Page::refreshPlugins(bool reload)
    383 {
    384     if (!allPages)
    385         return;
    386 
    387     PluginData::refresh();
    388 
    389     Vector<RefPtr<Frame> > framesNeedingReload;
    390 
    391     HashSet<Page*>::iterator end = allPages->end();
    392     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
    393         (*it)->m_pluginData = 0;
    394 
    395         if (reload) {
    396             for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    397                 if (frame->loader()->containsPlugins())
    398                     framesNeedingReload.append(frame);
    399             }
    400         }
    401     }
    402 
    403     for (size_t i = 0; i < framesNeedingReload.size(); ++i)
    404         framesNeedingReload[i]->loader()->reload();
    405 }
    406 
    407 PluginData* Page::pluginData() const
    408 {
    409     if (!settings()->arePluginsEnabled())
    410         return 0;
    411     if (!m_pluginData)
    412         m_pluginData = PluginData::create(this);
    413     return m_pluginData.get();
    414 }
    415 
    416 void Page::addUnstartedPlugin(PluginView* view)
    417 {
    418     ASSERT(!m_canStartPlugins);
    419     m_unstartedPlugins.add(view);
    420 }
    421 
    422 void Page::removeUnstartedPlugin(PluginView* view)
    423 {
    424     ASSERT(!m_canStartPlugins);
    425     ASSERT(m_unstartedPlugins.contains(view));
    426     m_unstartedPlugins.remove(view);
    427 }
    428 
    429 static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag)
    430 {
    431     return forward
    432         ? curr->tree()->traverseNextWithWrap(wrapFlag)
    433         : curr->tree()->traversePreviousWithWrap(wrapFlag);
    434 }
    435 
    436 bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, FindDirection direction, bool shouldWrap)
    437 {
    438     if (target.isEmpty() || !mainFrame())
    439         return false;
    440 
    441     Frame* frame = focusController()->focusedOrMainFrame();
    442     Frame* startFrame = frame;
    443     do {
    444         if (frame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) {
    445             if (frame != startFrame)
    446                 startFrame->selection()->clear();
    447             focusController()->setFocusedFrame(frame);
    448             return true;
    449         }
    450         frame = incrementFrame(frame, direction == FindDirectionForward, shouldWrap);
    451     } while (frame && frame != startFrame);
    452 
    453     // Search contents of startFrame, on the other side of the selection that we did earlier.
    454     // We cheat a bit and just research with wrap on
    455     if (shouldWrap && !startFrame->selection()->isNone()) {
    456         bool found = startFrame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true);
    457         focusController()->setFocusedFrame(frame);
    458         return found;
    459     }
    460 
    461     return false;
    462 }
    463 
    464 unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit)
    465 {
    466     if (target.isEmpty() || !mainFrame())
    467         return 0;
    468 
    469     unsigned matches = 0;
    470 
    471     Frame* frame = mainFrame();
    472     do {
    473         frame->setMarkedTextMatchesAreHighlighted(shouldHighlight);
    474         matches += frame->markAllMatchesForText(target, caseSensitivity == TextCaseSensitive, (limit == 0) ? 0 : (limit - matches));
    475         frame = incrementFrame(frame, true, false);
    476     } while (frame);
    477 
    478     return matches;
    479 }
    480 
    481 void Page::unmarkAllTextMatches()
    482 {
    483     if (!mainFrame())
    484         return;
    485 
    486     Frame* frame = mainFrame();
    487     do {
    488         frame->document()->removeMarkers(DocumentMarker::TextMatch);
    489         frame = incrementFrame(frame, true, false);
    490     } while (frame);
    491 }
    492 
    493 const VisibleSelection& Page::selection() const
    494 {
    495     return focusController()->focusedOrMainFrame()->selection()->selection();
    496 }
    497 
    498 void Page::setDefersLoading(bool defers)
    499 {
    500     if (!m_settings->loadDeferringEnabled())
    501         return;
    502 
    503     if (defers == m_defersLoading)
    504         return;
    505 
    506     m_defersLoading = defers;
    507     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
    508         frame->loader()->setDefersLoading(defers);
    509 }
    510 
    511 void Page::clearUndoRedoOperations()
    512 {
    513     m_editorClient->clearUndoRedoOperations();
    514 }
    515 
    516 bool Page::inLowQualityImageInterpolationMode() const
    517 {
    518     return m_inLowQualityInterpolationMode;
    519 }
    520 
    521 void Page::setInLowQualityImageInterpolationMode(bool mode)
    522 {
    523     m_inLowQualityInterpolationMode = mode;
    524 }
    525 
    526 void Page::setMediaVolume(float volume)
    527 {
    528     if (volume < 0 || volume > 1)
    529         return;
    530 
    531     if (m_mediaVolume == volume)
    532         return;
    533 
    534     m_mediaVolume = volume;
    535     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    536         frame->document()->mediaVolumeDidChange();
    537     }
    538 }
    539 
    540 void Page::didMoveOnscreen()
    541 {
    542     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    543         if (frame->view())
    544             frame->view()->didMoveOnscreen();
    545     }
    546 }
    547 
    548 void Page::willMoveOffscreen()
    549 {
    550     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    551         if (frame->view())
    552             frame->view()->willMoveOffscreen();
    553     }
    554 }
    555 
    556 void Page::userStyleSheetLocationChanged()
    557 {
    558     // FIXME: Eventually we will move to a model of just being handed the sheet
    559     // text instead of loading the URL ourselves.
    560     KURL url = m_settings->userStyleSheetLocation();
    561     if (url.isLocalFile())
    562         m_userStyleSheetPath = url.fileSystemPath();
    563     else
    564         m_userStyleSheetPath = String();
    565 
    566     m_didLoadUserStyleSheet = false;
    567     m_userStyleSheet = String();
    568     m_userStyleSheetModificationTime = 0;
    569 
    570     // Data URLs with base64-encoded UTF-8 style sheets are common. We can process them
    571     // synchronously and avoid using a loader.
    572     if (url.protocolIs("data") && url.string().startsWith("data:text/css;charset=utf-8;base64,")) {
    573         m_didLoadUserStyleSheet = true;
    574 
    575         const unsigned prefixLength = 35;
    576         Vector<char> encodedData(url.string().length() - prefixLength);
    577         for (unsigned i = prefixLength; i < url.string().length(); ++i)
    578             encodedData[i - prefixLength] = static_cast<char>(url.string()[i]);
    579 
    580         Vector<char> styleSheetAsUTF8;
    581         if (base64Decode(encodedData, styleSheetAsUTF8))
    582             m_userStyleSheet = String::fromUTF8(styleSheetAsUTF8.data(), styleSheetAsUTF8.size());
    583     }
    584 
    585     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
    586         if (frame->document())
    587             frame->document()->clearPageUserSheet();
    588     }
    589 }
    590 
    591 const String& Page::userStyleSheet() const
    592 {
    593     if (m_userStyleSheetPath.isEmpty())
    594         return m_userStyleSheet;
    595 
    596     time_t modTime;
    597     if (!getFileModificationTime(m_userStyleSheetPath, modTime)) {
    598         // The stylesheet either doesn't exist, was just deleted, or is
    599         // otherwise unreadable. If we've read the stylesheet before, we should
    600         // throw away that data now as it no longer represents what's on disk.
    601         m_userStyleSheet = String();
    602         return m_userStyleSheet;
    603     }
    604 
    605     // If the stylesheet hasn't changed since the last time we read it, we can
    606     // just return the old data.
    607     if (m_didLoadUserStyleSheet && modTime <= m_userStyleSheetModificationTime)
    608         return m_userStyleSheet;
    609 
    610     m_didLoadUserStyleSheet = true;
    611     m_userStyleSheet = String();
    612     m_userStyleSheetModificationTime = modTime;
    613 
    614     // FIXME: It would be better to load this asynchronously to avoid blocking
    615     // the process, but we will first need to create an asynchronous loading
    616     // mechanism that is not tied to a particular Frame. We will also have to
    617     // determine what our behavior should be before the stylesheet is loaded
    618     // and what should happen when it finishes loading, especially with respect
    619     // to when the load event fires, when Document::close is called, and when
    620     // layout/paint are allowed to happen.
    621     RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_userStyleSheetPath);
    622     if (!data)
    623         return m_userStyleSheet;
    624 
    625     RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/css");
    626     m_userStyleSheet = decoder->decode(data->data(), data->size());
    627     m_userStyleSheet += decoder->flush();
    628 
    629     return m_userStyleSheet;
    630 }
    631 
    632 void Page::removeAllVisitedLinks()
    633 {
    634     if (!allPages)
    635         return;
    636     HashSet<PageGroup*> groups;
    637     HashSet<Page*>::iterator pagesEnd = allPages->end();
    638     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
    639         if (PageGroup* group = (*it)->groupPtr())
    640             groups.add(group);
    641     }
    642     HashSet<PageGroup*>::iterator groupsEnd = groups.end();
    643     for (HashSet<PageGroup*>::iterator it = groups.begin(); it != groupsEnd; ++it)
    644         (*it)->removeVisitedLinks();
    645 }
    646 
    647 void Page::allVisitedStateChanged(PageGroup* group)
    648 {
    649     ASSERT(group);
    650     if (!allPages)
    651         return;
    652 
    653     HashSet<Page*>::iterator pagesEnd = allPages->end();
    654     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
    655         Page* page = *it;
    656         if (page->m_group != group)
    657             continue;
    658         for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) {
    659             if (CSSStyleSelector* styleSelector = frame->document()->styleSelector())
    660                 styleSelector->allVisitedStateChanged();
    661         }
    662     }
    663 }
    664 
    665 void Page::visitedStateChanged(PageGroup* group, LinkHash visitedLinkHash)
    666 {
    667     ASSERT(group);
    668     if (!allPages)
    669         return;
    670 
    671     HashSet<Page*>::iterator pagesEnd = allPages->end();
    672     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
    673         Page* page = *it;
    674         if (page->m_group != group)
    675             continue;
    676         for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) {
    677             if (CSSStyleSelector* styleSelector = frame->document()->styleSelector())
    678                 styleSelector->visitedStateChanged(visitedLinkHash);
    679         }
    680     }
    681 }
    682 
    683 void Page::setDebuggerForAllPages(JSC::Debugger* debugger)
    684 {
    685     ASSERT(allPages);
    686 
    687     HashSet<Page*>::iterator end = allPages->end();
    688     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
    689         (*it)->setDebugger(debugger);
    690 }
    691 
    692 void Page::setDebugger(JSC::Debugger* debugger)
    693 {
    694     if (m_debugger == debugger)
    695         return;
    696 
    697     m_debugger = debugger;
    698 
    699     for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree()->traverseNext())
    700         frame->script()->attachDebugger(m_debugger);
    701 }
    702 
    703 #if ENABLE(DOM_STORAGE)
    704 StorageNamespace* Page::sessionStorage(bool optionalCreate)
    705 {
    706     if (!m_sessionStorage && optionalCreate)
    707         m_sessionStorage = StorageNamespace::sessionStorageNamespace(this);
    708 
    709     return m_sessionStorage.get();
    710 }
    711 
    712 void Page::setSessionStorage(PassRefPtr<StorageNamespace> newStorage)
    713 {
    714     m_sessionStorage = newStorage;
    715 }
    716 #endif
    717 
    718 #if ENABLE(WML)
    719 WMLPageState* Page::wmlPageState()
    720 {
    721     if (!m_wmlPageState)
    722         m_wmlPageState.set(new WMLPageState(this));
    723     return m_wmlPageState.get();
    724 }
    725 #endif
    726 
    727 void Page::setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay)
    728 {
    729     if (customHTMLTokenizerTimeDelay < 0) {
    730         m_customHTMLTokenizerTimeDelay = -1;
    731         return;
    732     }
    733     m_customHTMLTokenizerTimeDelay = customHTMLTokenizerTimeDelay;
    734 }
    735 
    736 void Page::setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize)
    737 {
    738     if (customHTMLTokenizerChunkSize < 0) {
    739         m_customHTMLTokenizerChunkSize = -1;
    740         return;
    741     }
    742     m_customHTMLTokenizerChunkSize = customHTMLTokenizerChunkSize;
    743 }
    744 
    745 void Page::setMemoryCacheClientCallsEnabled(bool enabled)
    746 {
    747     if (m_areMemoryCacheClientCallsEnabled == enabled)
    748         return;
    749 
    750     m_areMemoryCacheClientCallsEnabled = enabled;
    751     if (!enabled)
    752         return;
    753 
    754     for (RefPtr<Frame> frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
    755         frame->loader()->tellClientAboutPastMemoryCacheLoads();
    756 }
    757 
    758 void Page::setJavaScriptURLsAreAllowed(bool areAllowed)
    759 {
    760     m_javaScriptURLsAreAllowed = areAllowed;
    761 }
    762 
    763 bool Page::javaScriptURLsAreAllowed() const
    764 {
    765     return m_javaScriptURLsAreAllowed;
    766 }
    767 
    768 #if ENABLE(INSPECTOR)
    769 InspectorTimelineAgent* Page::inspectorTimelineAgent() const
    770 {
    771     return m_inspectorController->timelineAgent();
    772 }
    773 #endif
    774 
    775 void Page::pluginAllowedRunTimeChanged()
    776 {
    777     if (m_pluginHalter)
    778         m_pluginHalter->setPluginAllowedRunTime(m_settings->pluginAllowedRunTime());
    779 }
    780 
    781 void Page::didStartPlugin(HaltablePlugin* obj)
    782 {
    783     if (m_pluginHalter)
    784         m_pluginHalter->didStartPlugin(obj);
    785 }
    786 
    787 void Page::didStopPlugin(HaltablePlugin* obj)
    788 {
    789     if (m_pluginHalter)
    790         m_pluginHalter->didStopPlugin(obj);
    791 }
    792 
    793 #if !ASSERT_DISABLED
    794 void Page::checkFrameCountConsistency() const
    795 {
    796     ASSERT(m_frameCount >= 0);
    797 
    798     int frameCount = 0;
    799     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
    800         ++frameCount;
    801 
    802     ASSERT(m_frameCount + 1 == frameCount);
    803 }
    804 #endif
    805 } // namespace WebCore
    806