summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/page/Page.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/page/Page.cpp')
-rw-r--r--Source/WebCore/page/Page.cpp965
1 files changed, 965 insertions, 0 deletions
diff --git a/Source/WebCore/page/Page.cpp b/Source/WebCore/page/Page.cpp
new file mode 100644
index 0000000..5b87051
--- /dev/null
+++ b/Source/WebCore/page/Page.cpp
@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "Page.h"
+
+#include "BackForwardController.h"
+#include "BackForwardList.h"
+#include "Base64.h"
+#include "CSSStyleSelector.h"
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "ContextMenuClient.h"
+#include "ContextMenuController.h"
+#include "DOMWindow.h"
+#include "DeviceMotionController.h"
+#include "DeviceOrientationController.h"
+#include "DocumentMarkerController.h"
+#include "DragController.h"
+#include "EditorClient.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "ExceptionCode.h"
+#include "FileSystem.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "HTMLElement.h"
+#include "HistoryItem.h"
+#include "InspectorController.h"
+#include "Logging.h"
+#include "MediaCanStartListener.h"
+#include "Navigator.h"
+#include "NetworkStateNotifier.h"
+#include "PageGroup.h"
+#include "PluginData.h"
+#include "PluginHalter.h"
+#include "PluginView.h"
+#include "PluginViewBase.h"
+#include "ProgressTracker.h"
+#include "RenderTheme.h"
+#include "RenderWidget.h"
+#include "RuntimeEnabledFeatures.h"
+#include "ScriptController.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+#include "SpeechInput.h"
+#include "SpeechInputClient.h"
+#include "TextResourceDecoder.h"
+#include "Widget.h"
+#include <wtf/HashMap.h>
+#include <wtf/RefCountedLeakCounter.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/StringHash.h>
+
+#if ENABLE(ACCELERATED_2D_CANVAS)
+#include "SharedGraphicsContext3D.h"
+#endif
+
+#if ENABLE(DOM_STORAGE)
+#include "StorageArea.h"
+#include "StorageNamespace.h"
+#endif
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+#include "ScriptDebugServer.h"
+#endif
+
+#if ENABLE(WML)
+#include "WMLPageState.h"
+#endif
+
+#if ENABLE(CLIENT_BASED_GEOLOCATION)
+#include "GeolocationController.h"
+#endif
+
+#if ENABLE(INSPECTOR) && ENABLE(OFFLINE_WEB_APPLICATIONS)
+#include "InspectorApplicationCacheAgent.h"
+#endif
+
+#if PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED)
+#include "PackageNotifier.h"
+#endif
+
+namespace WebCore {
+
+static HashSet<Page*>* allPages;
+
+#ifndef NDEBUG
+static WTF::RefCountedLeakCounter pageCounter("Page");
+#endif
+
+static void networkStateChanged()
+{
+ Vector<RefPtr<Frame> > frames;
+
+#if ENABLE(INSPECTOR) && ENABLE(OFFLINE_WEB_APPLICATIONS)
+ bool isNowOnline = networkStateNotifier().onLine();
+#endif
+
+ // Get all the frames of all the pages in all the page groups
+ HashSet<Page*>::iterator end = allPages->end();
+ for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
+ for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frames.append(frame);
+#if ENABLE(INSPECTOR) && ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (InspectorApplicationCacheAgent* applicationCacheAgent = (*it)->inspectorController()->applicationCacheAgent())
+ applicationCacheAgent->updateNetworkState(isNowOnline);
+#endif
+ }
+
+ AtomicString eventName = networkStateNotifier().onLine() ? eventNames().onlineEvent : eventNames().offlineEvent;
+ for (unsigned i = 0; i < frames.size(); i++)
+ frames[i]->document()->dispatchWindowEvent(Event::create(eventName, false, false));
+}
+
+#if PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED)
+static void onPackageResultAvailable()
+{
+ HashSet<Page*>::iterator end = allPages->end();
+ for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
+ for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->domWindow()->navigator()->onPackageResult();
+ }
+}
+#endif
+
+Page::Page(const PageClients& pageClients)
+ : m_chrome(adoptPtr(new Chrome(this, pageClients.chromeClient)))
+ , m_dragCaretController(adoptPtr(new SelectionController(0, true)))
+#if ENABLE(DRAG_SUPPORT)
+ , m_dragController(adoptPtr(new DragController(this, pageClients.dragClient)))
+#endif
+ , m_focusController(adoptPtr(new FocusController(this)))
+#if ENABLE(CONTEXT_MENUS)
+ , m_contextMenuController(adoptPtr(new ContextMenuController(this, pageClients.contextMenuClient)))
+#endif
+#if ENABLE(INSPECTOR)
+ , m_inspectorController(adoptPtr(new InspectorController(this, pageClients.inspectorClient)))
+#endif
+#if ENABLE(CLIENT_BASED_GEOLOCATION)
+ , m_geolocationController(adoptPtr(new GeolocationController(this, pageClients.geolocationClient)))
+#endif
+#if ENABLE(DEVICE_ORIENTATION)
+ , m_deviceMotionController(RuntimeEnabledFeatures::deviceMotionEnabled() ? new DeviceMotionController(pageClients.deviceMotionClient) : 0)
+ , m_deviceOrientationController(RuntimeEnabledFeatures::deviceOrientationEnabled() ? new DeviceOrientationController(this, pageClients.deviceOrientationClient) : 0)
+#endif
+#if ENABLE(INPUT_SPEECH)
+ , m_speechInputClient(pageClients.speechInputClient)
+#endif
+ , m_settings(adoptPtr(new Settings(this)))
+ , m_progress(adoptPtr(new ProgressTracker))
+ , m_backForwardController(adoptPtr(new BackForwardController(this, pageClients.backForwardClient)))
+ , m_theme(RenderTheme::themeForPage(this))
+ , m_editorClient(pageClients.editorClient)
+ , m_frameCount(0)
+ , m_openedByDOM(false)
+ , m_tabKeyCyclesThroughElements(true)
+ , m_defersLoading(false)
+ , m_inLowQualityInterpolationMode(false)
+ , m_cookieEnabled(true)
+ , m_areMemoryCacheClientCallsEnabled(true)
+ , m_mediaVolume(1)
+ , m_javaScriptURLsAreAllowed(true)
+ , m_didLoadUserStyleSheet(false)
+ , m_userStyleSheetModificationTime(0)
+ , m_group(0)
+ , m_debugger(0)
+ , m_customHTMLTokenizerTimeDelay(-1)
+ , m_customHTMLTokenizerChunkSize(-1)
+ , m_canStartMedia(true)
+ , m_viewMode(ViewModeWindowed)
+{
+ if (!allPages) {
+ allPages = new HashSet<Page*>;
+
+ networkStateNotifier().setNetworkStateChangedFunction(networkStateChanged);
+#if PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED)
+ packageNotifier().setOnResultAvailable(onPackageResultAvailable);
+#endif
+ }
+
+ ASSERT(!allPages->contains(this));
+ allPages->add(this);
+
+ if (pageClients.pluginHalterClient) {
+ m_pluginHalter.set(new PluginHalter(pageClients.pluginHalterClient));
+ m_pluginHalter->setPluginAllowedRunTime(m_settings->pluginAllowedRunTime());
+ }
+
+#if ENABLE(JAVASCRIPT_DEBUGGER)
+ ScriptDebugServer::shared().pageCreated(this);
+#endif
+
+#ifndef NDEBUG
+ pageCounter.increment();
+#endif
+}
+
+Page::~Page()
+{
+ m_mainFrame->setView(0);
+ setGroupName(String());
+ allPages->remove(this);
+
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->pageDestroyed();
+
+ m_editorClient->pageDestroyed();
+
+#if ENABLE(INSPECTOR)
+ m_inspectorController->inspectedPageDestroyed();
+#endif
+
+ backForward()->close();
+
+#ifndef NDEBUG
+ pageCounter.decrement();
+
+ // Cancel keepAlive timers, to ensure we release all Frames before exiting.
+ // It's safe to do this because we prohibit closing a Page while JavaScript
+ // is executing.
+ Frame::cancelAllKeepAlive();
+#endif
+}
+
+struct ViewModeInfo {
+ const char* name;
+ Page::ViewMode type;
+};
+static const int viewModeMapSize = 5;
+static ViewModeInfo viewModeMap[viewModeMapSize] = {
+ {"windowed", Page::ViewModeWindowed},
+ {"floating", Page::ViewModeFloating},
+ {"fullscreen", Page::ViewModeFullscreen},
+ {"maximized", Page::ViewModeMaximized},
+ {"minimized", Page::ViewModeMinimized}
+};
+
+Page::ViewMode Page::stringToViewMode(const String& text)
+{
+ for (int i = 0; i < viewModeMapSize; ++i) {
+ if (text == viewModeMap[i].name)
+ return viewModeMap[i].type;
+ }
+ return Page::ViewModeInvalid;
+}
+
+void Page::setViewMode(ViewMode viewMode)
+{
+ if (viewMode == m_viewMode || viewMode == ViewModeInvalid)
+ return;
+
+ m_viewMode = viewMode;
+
+ if (!m_mainFrame)
+ return;
+
+ if (m_mainFrame->view())
+ m_mainFrame->view()->forceLayout();
+
+ if (m_mainFrame->document())
+ m_mainFrame->document()->styleSelectorChanged(RecalcStyleImmediately);
+}
+
+void Page::setMainFrame(PassRefPtr<Frame> mainFrame)
+{
+ ASSERT(!m_mainFrame); // Should only be called during initialization
+ m_mainFrame = mainFrame;
+}
+
+bool Page::openedByDOM() const
+{
+ return m_openedByDOM;
+}
+
+void Page::setOpenedByDOM()
+{
+ m_openedByDOM = true;
+}
+
+BackForwardList* Page::backForwardList() const
+{
+ return m_backForwardController->client();
+}
+
+bool Page::goBack()
+{
+ HistoryItem* item = backForward()->backItem();
+
+ if (item) {
+ goToItem(item, FrameLoadTypeBack);
+ return true;
+ }
+ return false;
+}
+
+bool Page::goForward()
+{
+ HistoryItem* item = backForward()->forwardItem();
+
+ if (item) {
+ goToItem(item, FrameLoadTypeForward);
+ return true;
+ }
+ return false;
+}
+
+bool Page::canGoBackOrForward(int distance) const
+{
+ if (distance == 0)
+ return true;
+ if (distance > 0 && distance <= backForward()->forwardCount())
+ return true;
+ if (distance < 0 && -distance <= backForward()->backCount())
+ return true;
+ return false;
+}
+
+void Page::goBackOrForward(int distance)
+{
+ if (distance == 0)
+ return;
+
+ HistoryItem* item = backForward()->itemAtIndex(distance);
+ if (!item) {
+ if (distance > 0) {
+ if (int forwardCount = backForward()->forwardCount())
+ item = backForward()->itemAtIndex(forwardCount);
+ } else {
+ if (int backCount = backForward()->backCount())
+ item = backForward()->itemAtIndex(-backCount);
+ }
+ }
+
+ ASSERT(item);
+ if (!item)
+ return;
+
+ goToItem(item, FrameLoadTypeIndexedBackForward);
+}
+
+void Page::goToItem(HistoryItem* item, FrameLoadType type)
+{
+ if (defersLoading())
+ return;
+
+ // stopAllLoaders may end up running onload handlers, which could cause further history traversals that may lead to the passed in HistoryItem
+ // being deref()-ed. Make sure we can still use it with HistoryController::goToItem later.
+ RefPtr<HistoryItem> protector(item);
+
+ // Abort any current load unless we're navigating the current document to a new state object
+ HistoryItem* currentItem = m_mainFrame->loader()->history()->currentItem();
+ if (!item->stateObject() || !currentItem || item->documentSequenceNumber() != currentItem->documentSequenceNumber() || item == currentItem) {
+ // Define what to do with any open database connections. By default we stop them and terminate the database thread.
+ DatabasePolicy databasePolicy = DatabasePolicyStop;
+
+#if ENABLE(DATABASE)
+ // If we're navigating the history via a fragment on the same document, then we do not want to stop databases.
+ const KURL& currentURL = m_mainFrame->loader()->url();
+ const KURL& newURL = item->url();
+
+ if (newURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(currentURL, newURL))
+ databasePolicy = DatabasePolicyContinue;
+#endif
+
+ m_mainFrame->loader()->stopAllLoaders(databasePolicy);
+ }
+
+ m_mainFrame->loader()->history()->goToItem(item, type);
+}
+
+int Page::getHistoryLength()
+{
+ return backForward()->backCount() + 1 + backForward()->forwardCount();
+}
+
+void Page::setGlobalHistoryItem(HistoryItem* item)
+{
+ m_globalHistoryItem = item;
+}
+
+void Page::setGroupName(const String& name)
+{
+ if (m_group && !m_group->name().isEmpty()) {
+ ASSERT(m_group != m_singlePageGroup.get());
+ ASSERT(!m_singlePageGroup);
+ m_group->removePage(this);
+ }
+
+ if (name.isEmpty())
+ m_group = m_singlePageGroup.get();
+ else {
+ m_singlePageGroup.clear();
+ m_group = PageGroup::pageGroup(name);
+ m_group->addPage(this);
+ }
+}
+
+const String& Page::groupName() const
+{
+ DEFINE_STATIC_LOCAL(String, nullString, ());
+ return m_group ? m_group->name() : nullString;
+}
+
+void Page::initGroup()
+{
+ ASSERT(!m_singlePageGroup);
+ ASSERT(!m_group);
+ m_singlePageGroup.set(new PageGroup(this));
+ m_group = m_singlePageGroup.get();
+}
+
+void Page::scheduleForcedStyleRecalcForAllPages()
+{
+ if (!allPages)
+ return;
+ HashSet<Page*>::iterator end = allPages->end();
+ for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
+ for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->document()->scheduleForcedStyleRecalc();
+}
+
+void Page::updateViewportArguments()
+{
+ if (!mainFrame() || !mainFrame()->document() || mainFrame()->document()->viewportArguments() == m_viewportArguments)
+ return;
+
+ m_viewportArguments = mainFrame()->document()->viewportArguments();
+ chrome()->dispatchViewportDataDidChange(m_viewportArguments);
+}
+
+void Page::refreshPlugins(bool reload)
+{
+ if (!allPages)
+ return;
+
+ PluginData::refresh();
+
+ Vector<RefPtr<Frame> > framesNeedingReload;
+
+ HashSet<Page*>::iterator end = allPages->end();
+ for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
+ Page* page = *it;
+
+ // Clear out the page's plug-in data.
+ if (page->m_pluginData)
+ page->m_pluginData = 0;
+
+ if (!reload)
+ continue;
+
+ for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ if (frame->loader()->subframeLoader()->containsPlugins())
+ framesNeedingReload.append(frame);
+ }
+ }
+
+ for (size_t i = 0; i < framesNeedingReload.size(); ++i)
+ framesNeedingReload[i]->loader()->reload();
+}
+
+PluginData* Page::pluginData() const
+{
+ if (!mainFrame()->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin))
+ return 0;
+ if (!m_pluginData)
+ m_pluginData = PluginData::create(this);
+ return m_pluginData.get();
+}
+
+inline MediaCanStartListener* Page::takeAnyMediaCanStartListener()
+{
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ if (MediaCanStartListener* listener = frame->document()->takeAnyMediaCanStartListener())
+ return listener;
+ }
+ return 0;
+}
+
+void Page::setCanStartMedia(bool canStartMedia)
+{
+ if (m_canStartMedia == canStartMedia)
+ return;
+
+ m_canStartMedia = canStartMedia;
+
+ while (m_canStartMedia) {
+ MediaCanStartListener* listener = takeAnyMediaCanStartListener();
+ if (!listener)
+ break;
+ listener->mediaCanStart();
+ }
+}
+
+static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag)
+{
+ return forward
+ ? curr->tree()->traverseNextWithWrap(wrapFlag)
+ : curr->tree()->traversePreviousWithWrap(wrapFlag);
+}
+
+bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, FindDirection direction, bool shouldWrap)
+{
+ return findString(target, (caseSensitivity == TextCaseInsensitive ? CaseInsensitive : 0) | (direction == FindDirectionBackward ? Backwards : 0) | (shouldWrap ? WrapAround : 0));
+}
+
+bool Page::findString(const String& target, FindOptions options)
+{
+ if (target.isEmpty() || !mainFrame())
+ return false;
+
+ bool shouldWrap = options & WrapAround;
+ Frame* frame = focusController()->focusedOrMainFrame();
+ Frame* startFrame = frame;
+ do {
+ if (frame->editor()->findString(target, (options & ~WrapAround) | StartInSelection)) {
+ if (frame != startFrame)
+ startFrame->selection()->clear();
+ focusController()->setFocusedFrame(frame);
+ return true;
+ }
+ frame = incrementFrame(frame, !(options & Backwards), shouldWrap);
+ } while (frame && frame != startFrame);
+
+ // Search contents of startFrame, on the other side of the selection that we did earlier.
+ // We cheat a bit and just research with wrap on
+ if (shouldWrap && !startFrame->selection()->isNone()) {
+ bool found = startFrame->editor()->findString(target, options | WrapAround | StartInSelection);
+ focusController()->setFocusedFrame(frame);
+ return found;
+ }
+
+ return false;
+}
+
+unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit)
+{
+ return markAllMatchesForText(target, caseSensitivity == TextCaseInsensitive ? CaseInsensitive : 0, shouldHighlight, limit);
+}
+
+unsigned int Page::markAllMatchesForText(const String& target, FindOptions options, bool shouldHighlight, unsigned limit)
+{
+ if (target.isEmpty() || !mainFrame())
+ return 0;
+
+ unsigned matches = 0;
+
+ Frame* frame = mainFrame();
+ do {
+ frame->editor()->setMarkedTextMatchesAreHighlighted(shouldHighlight);
+ matches += frame->editor()->countMatchesForText(target, options, limit ? (limit - matches) : 0, true);
+ frame = incrementFrame(frame, true, false);
+ } while (frame);
+
+ return matches;
+}
+
+void Page::unmarkAllTextMatches()
+{
+ if (!mainFrame())
+ return;
+
+ Frame* frame = mainFrame();
+ do {
+ frame->document()->markers()->removeMarkers(DocumentMarker::TextMatch);
+ frame = incrementFrame(frame, true, false);
+ } while (frame);
+}
+
+const VisibleSelection& Page::selection() const
+{
+ return focusController()->focusedOrMainFrame()->selection()->selection();
+}
+
+void Page::setDefersLoading(bool defers)
+{
+ if (!m_settings->loadDeferringEnabled())
+ return;
+
+ if (defers == m_defersLoading)
+ return;
+
+ m_defersLoading = defers;
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->loader()->setDefersLoading(defers);
+}
+
+void Page::clearUndoRedoOperations()
+{
+ m_editorClient->clearUndoRedoOperations();
+}
+
+bool Page::inLowQualityImageInterpolationMode() const
+{
+ return m_inLowQualityInterpolationMode;
+}
+
+void Page::setInLowQualityImageInterpolationMode(bool mode)
+{
+ m_inLowQualityInterpolationMode = mode;
+}
+
+void Page::setMediaVolume(float volume)
+{
+ if (volume < 0 || volume > 1)
+ return;
+
+ if (m_mediaVolume == volume)
+ return;
+
+ m_mediaVolume = volume;
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ frame->document()->mediaVolumeDidChange();
+ }
+}
+
+void Page::didMoveOnscreen()
+{
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ if (frame->view())
+ frame->view()->didMoveOnscreen();
+ }
+}
+
+void Page::willMoveOffscreen()
+{
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ if (frame->view())
+ frame->view()->willMoveOffscreen();
+ }
+}
+
+void Page::userStyleSheetLocationChanged()
+{
+ // FIXME: Eventually we will move to a model of just being handed the sheet
+ // text instead of loading the URL ourselves.
+ KURL url = m_settings->userStyleSheetLocation();
+ if (url.isLocalFile())
+ m_userStyleSheetPath = url.fileSystemPath();
+ else
+ m_userStyleSheetPath = String();
+
+ m_didLoadUserStyleSheet = false;
+ m_userStyleSheet = String();
+ m_userStyleSheetModificationTime = 0;
+
+ // Data URLs with base64-encoded UTF-8 style sheets are common. We can process them
+ // synchronously and avoid using a loader.
+ if (url.protocolIsData() && url.string().startsWith("data:text/css;charset=utf-8;base64,")) {
+ m_didLoadUserStyleSheet = true;
+
+ Vector<char> styleSheetAsUTF8;
+ if (base64Decode(decodeURLEscapeSequences(url.string().substring(35)), styleSheetAsUTF8, IgnoreWhitespace))
+ m_userStyleSheet = String::fromUTF8(styleSheetAsUTF8.data(), styleSheetAsUTF8.size());
+ }
+
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ if (frame->document())
+ frame->document()->updatePageUserSheet();
+ }
+}
+
+const String& Page::userStyleSheet() const
+{
+ if (m_userStyleSheetPath.isEmpty())
+ return m_userStyleSheet;
+
+ time_t modTime;
+ if (!getFileModificationTime(m_userStyleSheetPath, modTime)) {
+ // The stylesheet either doesn't exist, was just deleted, or is
+ // otherwise unreadable. If we've read the stylesheet before, we should
+ // throw away that data now as it no longer represents what's on disk.
+ m_userStyleSheet = String();
+ return m_userStyleSheet;
+ }
+
+ // If the stylesheet hasn't changed since the last time we read it, we can
+ // just return the old data.
+ if (m_didLoadUserStyleSheet && modTime <= m_userStyleSheetModificationTime)
+ return m_userStyleSheet;
+
+ m_didLoadUserStyleSheet = true;
+ m_userStyleSheet = String();
+ m_userStyleSheetModificationTime = modTime;
+
+ // FIXME: It would be better to load this asynchronously to avoid blocking
+ // the process, but we will first need to create an asynchronous loading
+ // mechanism that is not tied to a particular Frame. We will also have to
+ // determine what our behavior should be before the stylesheet is loaded
+ // and what should happen when it finishes loading, especially with respect
+ // to when the load event fires, when Document::close is called, and when
+ // layout/paint are allowed to happen.
+ RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_userStyleSheetPath);
+ if (!data)
+ return m_userStyleSheet;
+
+ RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/css");
+ m_userStyleSheet = decoder->decode(data->data(), data->size());
+ m_userStyleSheet += decoder->flush();
+
+ return m_userStyleSheet;
+}
+
+void Page::removeAllVisitedLinks()
+{
+ if (!allPages)
+ return;
+ HashSet<PageGroup*> groups;
+ HashSet<Page*>::iterator pagesEnd = allPages->end();
+ for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
+ if (PageGroup* group = (*it)->groupPtr())
+ groups.add(group);
+ }
+ HashSet<PageGroup*>::iterator groupsEnd = groups.end();
+ for (HashSet<PageGroup*>::iterator it = groups.begin(); it != groupsEnd; ++it)
+ (*it)->removeVisitedLinks();
+}
+
+void Page::allVisitedStateChanged(PageGroup* group)
+{
+ ASSERT(group);
+ if (!allPages)
+ return;
+
+ HashSet<Page*>::iterator pagesEnd = allPages->end();
+ for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
+ Page* page = *it;
+ if (page->m_group != group)
+ continue;
+ for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) {
+ if (CSSStyleSelector* styleSelector = frame->document()->styleSelector())
+ styleSelector->allVisitedStateChanged();
+ }
+ }
+}
+
+void Page::visitedStateChanged(PageGroup* group, LinkHash visitedLinkHash)
+{
+ ASSERT(group);
+ if (!allPages)
+ return;
+
+ HashSet<Page*>::iterator pagesEnd = allPages->end();
+ for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
+ Page* page = *it;
+ if (page->m_group != group)
+ continue;
+ for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) {
+ if (CSSStyleSelector* styleSelector = frame->document()->styleSelector())
+ styleSelector->visitedStateChanged(visitedLinkHash);
+ }
+ }
+}
+
+void Page::setDebuggerForAllPages(JSC::Debugger* debugger)
+{
+ ASSERT(allPages);
+
+ HashSet<Page*>::iterator end = allPages->end();
+ for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
+ (*it)->setDebugger(debugger);
+}
+
+void Page::setDebugger(JSC::Debugger* debugger)
+{
+ if (m_debugger == debugger)
+ return;
+
+ m_debugger = debugger;
+
+ for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree()->traverseNext())
+ frame->script()->attachDebugger(m_debugger);
+}
+
+SharedGraphicsContext3D* Page::sharedGraphicsContext3D()
+{
+#if ENABLE(ACCELERATED_2D_CANVAS)
+ if (!m_sharedGraphicsContext3D)
+ m_sharedGraphicsContext3D = SharedGraphicsContext3D::create(chrome());
+
+ return m_sharedGraphicsContext3D.get();
+#else
+ return 0;
+#endif
+}
+
+#if ENABLE(DOM_STORAGE)
+StorageNamespace* Page::sessionStorage(bool optionalCreate)
+{
+ if (!m_sessionStorage && optionalCreate)
+ m_sessionStorage = StorageNamespace::sessionStorageNamespace(this, m_settings->sessionStorageQuota());
+
+ return m_sessionStorage.get();
+}
+
+void Page::setSessionStorage(PassRefPtr<StorageNamespace> newStorage)
+{
+ m_sessionStorage = newStorage;
+}
+#endif
+
+#if ENABLE(WML)
+WMLPageState* Page::wmlPageState()
+{
+ if (!m_wmlPageState)
+ m_wmlPageState.set(new WMLPageState(this));
+ return m_wmlPageState.get();
+}
+#endif
+
+void Page::setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay)
+{
+ if (customHTMLTokenizerTimeDelay < 0) {
+ m_customHTMLTokenizerTimeDelay = -1;
+ return;
+ }
+ m_customHTMLTokenizerTimeDelay = customHTMLTokenizerTimeDelay;
+}
+
+void Page::setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize)
+{
+ if (customHTMLTokenizerChunkSize < 0) {
+ m_customHTMLTokenizerChunkSize = -1;
+ return;
+ }
+ m_customHTMLTokenizerChunkSize = customHTMLTokenizerChunkSize;
+}
+
+void Page::setMemoryCacheClientCallsEnabled(bool enabled)
+{
+ if (m_areMemoryCacheClientCallsEnabled == enabled)
+ return;
+
+ m_areMemoryCacheClientCallsEnabled = enabled;
+ if (!enabled)
+ return;
+
+ for (RefPtr<Frame> frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->loader()->tellClientAboutPastMemoryCacheLoads();
+}
+
+void Page::setJavaScriptURLsAreAllowed(bool areAllowed)
+{
+ m_javaScriptURLsAreAllowed = areAllowed;
+}
+
+bool Page::javaScriptURLsAreAllowed() const
+{
+ return m_javaScriptURLsAreAllowed;
+}
+
+#if ENABLE(INPUT_SPEECH)
+SpeechInput* Page::speechInput()
+{
+ ASSERT(m_speechInputClient);
+ if (!m_speechInput.get())
+ m_speechInput.set(new SpeechInput(m_speechInputClient));
+ return m_speechInput.get();
+}
+#endif
+
+void Page::dnsPrefetchingStateChanged()
+{
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->document()->initDNSPrefetch();
+}
+
+void Page::privateBrowsingStateChanged()
+{
+ bool privateBrowsingEnabled = m_settings->privateBrowsingEnabled();
+
+ // Collect the PluginViews in to a vector to ensure that action the plug-in takes
+ // from below privateBrowsingStateChanged does not affect their lifetime.
+ Vector<RefPtr<PluginViewBase>, 32> pluginViewBases;
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ FrameView* view = frame->view();
+ if (!view)
+ return;
+
+ const HashSet<RefPtr<Widget> >* children = view->children();
+ ASSERT(children);
+
+ HashSet<RefPtr<Widget> >::const_iterator end = children->end();
+ for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(); it != end; ++it) {
+ Widget* widget = (*it).get();
+ if (widget->isPluginViewBase())
+ pluginViewBases.append(static_cast<PluginViewBase*>(widget));
+ }
+ }
+
+ for (size_t i = 0; i < pluginViewBases.size(); ++i)
+ pluginViewBases[i]->privateBrowsingStateChanged(privateBrowsingEnabled);
+}
+
+void Page::pluginAllowedRunTimeChanged()
+{
+ if (m_pluginHalter)
+ m_pluginHalter->setPluginAllowedRunTime(m_settings->pluginAllowedRunTime());
+}
+
+void Page::didStartPlugin(HaltablePlugin* obj)
+{
+ if (m_pluginHalter)
+ m_pluginHalter->didStartPlugin(obj);
+}
+
+void Page::didStopPlugin(HaltablePlugin* obj)
+{
+ if (m_pluginHalter)
+ m_pluginHalter->didStopPlugin(obj);
+}
+
+#if !ASSERT_DISABLED
+void Page::checkFrameCountConsistency() const
+{
+ ASSERT(m_frameCount >= 0);
+
+ int frameCount = 0;
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
+ ++frameCount;
+
+ ASSERT(m_frameCount + 1 == frameCount);
+}
+#endif
+
+Page::PageClients::PageClients()
+ : chromeClient(0)
+ , contextMenuClient(0)
+ , editorClient(0)
+ , dragClient(0)
+ , inspectorClient(0)
+ , pluginHalterClient(0)
+ , geolocationClient(0)
+ , deviceMotionClient(0)
+ , deviceOrientationClient(0)
+ , speechInputClient(0)
+{
+}
+
+Page::PageClients::~PageClients()
+{
+}
+
+} // namespace WebCore