Index: third_party/WebKit/WebCore/page/Page.cpp |
=================================================================== |
--- third_party/WebKit/WebCore/page/Page.cpp (revision 9383) |
+++ third_party/WebKit/WebCore/page/Page.cpp (working copy) |
@@ -1,648 +1,648 @@ |
-/* |
- * Copyright (C) 2006, 2007, 2008 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 "Chrome.h" |
-#include "ChromeClient.h" |
-#include "ContextMenuClient.h" |
-#include "ContextMenuController.h" |
-#include "CSSStyleSelector.h" |
-#include "EditorClient.h" |
-#include "DOMWindow.h" |
-#include "DragController.h" |
-#include "EventNames.h" |
-#include "FileSystem.h" |
-#include "FocusController.h" |
-#include "Frame.h" |
-#include "FrameLoader.h" |
-#include "FrameTree.h" |
-#include "FrameView.h" |
-#include "HistoryItem.h" |
-#include "InspectorController.h" |
-#include "Logging.h" |
-#include "NetworkStateNotifier.h" |
-#include "Navigator.h" |
-#include "PageGroup.h" |
-#include "PluginData.h" |
-#include "ProgressTracker.h" |
-#include "RenderWidget.h" |
-#include "SelectionController.h" |
-#include "Settings.h" |
-#include "StringHash.h" |
-#include "TextResourceDecoder.h" |
-#include "Widget.h" |
-#include "ScriptController.h" |
-#include <wtf/HashMap.h> |
-#include <wtf/RefCountedLeakCounter.h> |
-#include <wtf/StdLibExtras.h> |
- |
-#if ENABLE(DOM_STORAGE) |
-#include "LocalStorage.h" |
-#include "SessionStorage.h" |
-#include "StorageArea.h" |
-#endif |
- |
-#if ENABLE(JAVASCRIPT_DEBUGGER) |
-#include "JavaScriptDebugServer.h" |
-#endif |
- |
-#if ENABLE(WML) |
-#include "WMLPageState.h" |
-#endif |
- |
-namespace WebCore { |
- |
-static HashSet<Page*>* allPages; |
- |
-#ifndef NDEBUG |
-static WTF::RefCountedLeakCounter pageCounter("Page"); |
-#endif |
- |
-static void networkStateChanged() |
-{ |
- Vector<RefPtr<Frame> > frames; |
- |
- // 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); |
- } |
- |
- AtomicString eventName = networkStateNotifier().onLine() ? eventNames().onlineEvent : eventNames().offlineEvent; |
- |
- for (unsigned i = 0; i < frames.size(); i++) { |
- Document* document = frames[i]->document(); |
- |
- if (!document) |
- continue; |
- |
- // If the document does not have a body the event should be dispatched to the document |
- Node* eventTarget = document->body(); |
- if (!eventTarget) |
- eventTarget = document; |
- |
- eventTarget->dispatchEventForType(eventName, false, false); |
- } |
-} |
- |
-Page::Page(ChromeClient* chromeClient, ContextMenuClient* contextMenuClient, EditorClient* editorClient, DragClient* dragClient, InspectorClient* inspectorClient) |
- : m_chrome(new Chrome(this, chromeClient)) |
- , m_dragCaretController(new SelectionController(0, true)) |
- , m_dragController(new DragController(this, dragClient)) |
- , m_focusController(new FocusController(this)) |
- , m_contextMenuController(new ContextMenuController(this, contextMenuClient)) |
- , m_inspectorController(new InspectorController(this, inspectorClient)) |
- , m_settings(new Settings(this)) |
- , m_progress(new ProgressTracker) |
- , m_backForwardList(BackForwardList::create(this)) |
- , m_editorClient(editorClient) |
- , m_frameCount(0) |
- , m_tabKeyCyclesThroughElements(true) |
- , m_defersLoading(false) |
- , m_inLowQualityInterpolationMode(false) |
- , m_cookieEnabled(true) |
- , m_areMemoryCacheClientCallsEnabled(true) |
- , m_mediaVolume(1) |
- , m_parentInspectorController(0) |
- , m_didLoadUserStyleSheet(false) |
- , m_userStyleSheetModificationTime(0) |
- , m_group(0) |
- , m_debugger(0) |
- , m_pendingUnloadEventCount(0) |
- , m_pendingBeforeUnloadEventCount(0) |
- , m_customHTMLTokenizerTimeDelay(-1) |
- , m_customHTMLTokenizerChunkSize(-1) |
-{ |
- if (!allPages) { |
- allPages = new HashSet<Page*>; |
- |
- networkStateNotifier().setNetworkStateChangedFunction(networkStateChanged); |
- } |
- |
- ASSERT(!allPages->contains(this)); |
- allPages->add(this); |
- |
-#if ENABLE(JAVASCRIPT_DEBUGGER) |
- JavaScriptDebugServer::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()) { |
- if (frame->document()) |
- frame->document()->documentWillBecomeInactive(); |
- frame->pageDestroyed(); |
- } |
- m_editorClient->pageDestroyed(); |
- if (m_parentInspectorController) |
- m_parentInspectorController->pageDestroyed(); |
- m_inspectorController->inspectedPageDestroyed(); |
- |
- m_backForwardList->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 |
-} |
- |
-void Page::setMainFrame(PassRefPtr<Frame> mainFrame) |
-{ |
- ASSERT(!m_mainFrame); // Should only be called during initialization |
- m_mainFrame = mainFrame; |
-} |
- |
-BackForwardList* Page::backForwardList() |
-{ |
- return m_backForwardList.get(); |
-} |
- |
-bool Page::goBack() |
-{ |
- HistoryItem* item = m_backForwardList->backItem(); |
- |
- if (item) { |
- goToItem(item, FrameLoadTypeBack); |
- return true; |
- } |
- return false; |
-} |
- |
-bool Page::goForward() |
-{ |
- HistoryItem* item = m_backForwardList->forwardItem(); |
- |
- if (item) { |
- goToItem(item, FrameLoadTypeForward); |
- return true; |
- } |
- return false; |
-} |
- |
-void Page::goToItem(HistoryItem* item, FrameLoadType type) |
-{ |
- // Abort any current load if we're going to a history item |
- m_mainFrame->loader()->stopAllLoaders(); |
- m_mainFrame->loader()->goToItem(item, type); |
-} |
- |
-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 = 0; |
- 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::setNeedsReapplyStyles() |
-{ |
- 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->setNeedsReapplyStyles(); |
-} |
- |
-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) { |
- (*it)->m_pluginData = 0; |
- |
- if (reload) { |
- for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) { |
- if (frame->loader()->containsPlugins()) |
- framesNeedingReload.append(frame); |
- } |
- } |
- } |
- |
- for (size_t i = 0; i < framesNeedingReload.size(); ++i) |
- framesNeedingReload[i]->loader()->reload(); |
-} |
- |
-PluginData* Page::pluginData() const |
-{ |
- if (!settings()->arePluginsEnabled()) |
- return 0; |
- if (!m_pluginData) |
- m_pluginData = PluginData::create(this); |
- return m_pluginData.get(); |
-} |
- |
-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) |
-{ |
- if (target.isEmpty() || !mainFrame()) |
- return false; |
- |
- Frame* frame = focusController()->focusedOrMainFrame(); |
- Frame* startFrame = frame; |
- do { |
- if (frame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) { |
- if (frame != startFrame) |
- startFrame->selection()->clear(); |
- focusController()->setFocusedFrame(frame); |
- return true; |
- } |
- frame = incrementFrame(frame, direction == FindDirectionForward, 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->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true); |
- focusController()->setFocusedFrame(frame); |
- return found; |
- } |
- |
- return false; |
-} |
- |
-unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit) |
-{ |
- if (target.isEmpty() || !mainFrame()) |
- return 0; |
- |
- unsigned matches = 0; |
- |
- Frame* frame = mainFrame(); |
- do { |
- frame->setMarkedTextMatchesAreHighlighted(shouldHighlight); |
- matches += frame->markAllMatchesForText(target, caseSensitivity == TextCaseSensitive, (limit == 0) ? 0 : (limit - matches)); |
- frame = incrementFrame(frame, true, false); |
- } while (frame); |
- |
- return matches; |
-} |
- |
-void Page::unmarkAllTextMatches() |
-{ |
- if (!mainFrame()) |
- return; |
- |
- Frame* frame = mainFrame(); |
- do { |
- if (Document* document = frame->document()) |
- document->removeMarkers(DocumentMarker::TextMatch); |
- frame = incrementFrame(frame, true, false); |
- } while (frame); |
-} |
- |
-const Selection& Page::selection() const |
-{ |
- return focusController()->focusedOrMainFrame()->selection()->selection(); |
-} |
- |
-void Page::setDefersLoading(bool defers) |
-{ |
- 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()) { |
- if (frame->document()) |
- 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() |
-{ |
-#if !FRAME_LOADS_USER_STYLESHEET |
- // FIXME: We should provide a way to load other types of URLs than just |
- // file: (e.g., http:, data:). |
- if (m_settings->userStyleSheetLocation().isLocalFile()) |
- m_userStyleSheetPath = m_settings->userStyleSheetLocation().fileSystemPath(); |
- else |
- m_userStyleSheetPath = String(); |
- |
- m_didLoadUserStyleSheet = false; |
- m_userStyleSheet = String(); |
- m_userStyleSheetModificationTime = 0; |
-#endif |
-} |
- |
-const String& Page::userStyleSheet() const |
-{ |
- if (m_userStyleSheetPath.isEmpty()) { |
- ASSERT(m_userStyleSheet.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; |
- |
- m_userStyleSheet = TextResourceDecoder::create("text/css")->decode(data->data(), data->size()); |
- |
- 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); |
- ASSERT(allPages); |
- 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); |
- ASSERT(allPages); |
- 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); |
-} |
- |
-#if ENABLE(DOM_STORAGE) |
-SessionStorage* Page::sessionStorage(bool optionalCreate) |
-{ |
- if (!m_sessionStorage && optionalCreate) |
- m_sessionStorage = SessionStorage::create(this); |
- |
- return m_sessionStorage.get(); |
-} |
- |
-void Page::setSessionStorage(PassRefPtr<SessionStorage> newStorage) |
-{ |
- ASSERT(newStorage->page() == this); |
- m_sessionStorage = newStorage; |
-} |
-#endif |
- |
-unsigned Page::pendingUnloadEventCount() |
-{ |
- return m_pendingUnloadEventCount; |
-} |
- |
-void Page::changePendingUnloadEventCount(int delta) |
-{ |
- if (!delta) |
- return; |
- ASSERT( (delta + (int)m_pendingUnloadEventCount) >= 0 ); |
- |
- if (m_pendingUnloadEventCount == 0) |
- m_chrome->disableSuddenTermination(); |
- else if ((m_pendingUnloadEventCount + delta) == 0) |
- m_chrome->enableSuddenTermination(); |
- |
- m_pendingUnloadEventCount += delta; |
- return; |
-} |
- |
-unsigned Page::pendingBeforeUnloadEventCount() |
-{ |
- return m_pendingBeforeUnloadEventCount; |
-} |
- |
-void Page::changePendingBeforeUnloadEventCount(int delta) |
-{ |
- if (!delta) |
- return; |
- ASSERT( (delta + (int)m_pendingBeforeUnloadEventCount) >= 0 ); |
- |
- if (m_pendingBeforeUnloadEventCount == 0) |
- m_chrome->disableSuddenTermination(); |
- else if ((m_pendingBeforeUnloadEventCount + delta) == 0) |
- m_chrome->enableSuddenTermination(); |
- |
- m_pendingBeforeUnloadEventCount += delta; |
- return; |
-} |
- |
-#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(); |
-} |
- |
-} // namespace WebCore |
+/* |
+ * Copyright (C) 2006, 2007, 2008 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 "Chrome.h" |
+#include "ChromeClient.h" |
+#include "ContextMenuClient.h" |
+#include "ContextMenuController.h" |
+#include "CSSStyleSelector.h" |
+#include "EditorClient.h" |
+#include "DOMWindow.h" |
+#include "DragController.h" |
+#include "EventNames.h" |
+#include "FileSystem.h" |
+#include "FocusController.h" |
+#include "Frame.h" |
+#include "FrameLoader.h" |
+#include "FrameTree.h" |
+#include "FrameView.h" |
+#include "HistoryItem.h" |
+#include "InspectorController.h" |
+#include "Logging.h" |
+#include "NetworkStateNotifier.h" |
+#include "Navigator.h" |
+#include "PageGroup.h" |
+#include "PluginData.h" |
+#include "ProgressTracker.h" |
+#include "RenderWidget.h" |
+#include "SelectionController.h" |
+#include "Settings.h" |
+#include "StringHash.h" |
+#include "TextResourceDecoder.h" |
+#include "Widget.h" |
+#include "ScriptController.h" |
+#include <wtf/HashMap.h> |
+#include <wtf/RefCountedLeakCounter.h> |
+#include <wtf/StdLibExtras.h> |
+ |
+#if ENABLE(DOM_STORAGE) |
+#include "LocalStorage.h" |
+#include "SessionStorage.h" |
+#include "StorageArea.h" |
+#endif |
+ |
+#if ENABLE(JAVASCRIPT_DEBUGGER) |
+#include "JavaScriptDebugServer.h" |
+#endif |
+ |
+#if ENABLE(WML) |
+#include "WMLPageState.h" |
+#endif |
+ |
+namespace WebCore { |
+ |
+static HashSet<Page*>* allPages; |
+ |
+#ifndef NDEBUG |
+static WTF::RefCountedLeakCounter pageCounter("Page"); |
+#endif |
+ |
+static void networkStateChanged() |
+{ |
+ Vector<RefPtr<Frame> > frames; |
+ |
+ // 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); |
+ } |
+ |
+ AtomicString eventName = networkStateNotifier().onLine() ? eventNames().onlineEvent : eventNames().offlineEvent; |
+ |
+ for (unsigned i = 0; i < frames.size(); i++) { |
+ Document* document = frames[i]->document(); |
+ |
+ if (!document) |
+ continue; |
+ |
+ // If the document does not have a body the event should be dispatched to the document |
+ EventTargetNode* eventTarget = document->body(); |
+ if (!eventTarget) |
+ eventTarget = document; |
+ |
+ eventTarget->dispatchEventForType(eventName, false, false); |
+ } |
+} |
+ |
+Page::Page(ChromeClient* chromeClient, ContextMenuClient* contextMenuClient, EditorClient* editorClient, DragClient* dragClient, InspectorClient* inspectorClient) |
+ : m_chrome(new Chrome(this, chromeClient)) |
+ , m_dragCaretController(new SelectionController(0, true)) |
+ , m_dragController(new DragController(this, dragClient)) |
+ , m_focusController(new FocusController(this)) |
+ , m_contextMenuController(new ContextMenuController(this, contextMenuClient)) |
+ , m_inspectorController(new InspectorController(this, inspectorClient)) |
+ , m_settings(new Settings(this)) |
+ , m_progress(new ProgressTracker) |
+ , m_backForwardList(BackForwardList::create(this)) |
+ , m_editorClient(editorClient) |
+ , m_frameCount(0) |
+ , m_tabKeyCyclesThroughElements(true) |
+ , m_defersLoading(false) |
+ , m_inLowQualityInterpolationMode(false) |
+ , m_cookieEnabled(true) |
+ , m_areMemoryCacheClientCallsEnabled(true) |
+ , m_mediaVolume(1) |
+ , m_parentInspectorController(0) |
+ , m_didLoadUserStyleSheet(false) |
+ , m_userStyleSheetModificationTime(0) |
+ , m_group(0) |
+ , m_debugger(0) |
+ , m_pendingUnloadEventCount(0) |
+ , m_pendingBeforeUnloadEventCount(0) |
+ , m_customHTMLTokenizerTimeDelay(-1) |
+ , m_customHTMLTokenizerChunkSize(-1) |
+{ |
+ if (!allPages) { |
+ allPages = new HashSet<Page*>; |
+ |
+ networkStateNotifier().setNetworkStateChangedFunction(networkStateChanged); |
+ } |
+ |
+ ASSERT(!allPages->contains(this)); |
+ allPages->add(this); |
+ |
+#if ENABLE(JAVASCRIPT_DEBUGGER) |
+ JavaScriptDebugServer::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()) { |
+ if (frame->document()) |
+ frame->document()->documentWillBecomeInactive(); |
+ frame->pageDestroyed(); |
+ } |
+ m_editorClient->pageDestroyed(); |
+ if (m_parentInspectorController) |
+ m_parentInspectorController->pageDestroyed(); |
+ m_inspectorController->inspectedPageDestroyed(); |
+ |
+ m_backForwardList->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 |
+} |
+ |
+void Page::setMainFrame(PassRefPtr<Frame> mainFrame) |
+{ |
+ ASSERT(!m_mainFrame); // Should only be called during initialization |
+ m_mainFrame = mainFrame; |
+} |
+ |
+BackForwardList* Page::backForwardList() |
+{ |
+ return m_backForwardList.get(); |
+} |
+ |
+bool Page::goBack() |
+{ |
+ HistoryItem* item = m_backForwardList->backItem(); |
+ |
+ if (item) { |
+ goToItem(item, FrameLoadTypeBack); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool Page::goForward() |
+{ |
+ HistoryItem* item = m_backForwardList->forwardItem(); |
+ |
+ if (item) { |
+ goToItem(item, FrameLoadTypeForward); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+void Page::goToItem(HistoryItem* item, FrameLoadType type) |
+{ |
+ // Abort any current load if we're going to a history item |
+ m_mainFrame->loader()->stopAllLoaders(); |
+ m_mainFrame->loader()->goToItem(item, type); |
+} |
+ |
+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 = 0; |
+ 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::setNeedsReapplyStyles() |
+{ |
+ 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->setNeedsReapplyStyles(); |
+} |
+ |
+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) { |
+ (*it)->m_pluginData = 0; |
+ |
+ if (reload) { |
+ for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) { |
+ if (frame->loader()->containsPlugins()) |
+ framesNeedingReload.append(frame); |
+ } |
+ } |
+ } |
+ |
+ for (size_t i = 0; i < framesNeedingReload.size(); ++i) |
+ framesNeedingReload[i]->loader()->reload(); |
+} |
+ |
+PluginData* Page::pluginData() const |
+{ |
+ if (!settings()->arePluginsEnabled()) |
+ return 0; |
+ if (!m_pluginData) |
+ m_pluginData = PluginData::create(this); |
+ return m_pluginData.get(); |
+} |
+ |
+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) |
+{ |
+ if (target.isEmpty() || !mainFrame()) |
+ return false; |
+ |
+ Frame* frame = focusController()->focusedOrMainFrame(); |
+ Frame* startFrame = frame; |
+ do { |
+ if (frame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) { |
+ if (frame != startFrame) |
+ startFrame->selection()->clear(); |
+ focusController()->setFocusedFrame(frame); |
+ return true; |
+ } |
+ frame = incrementFrame(frame, direction == FindDirectionForward, 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->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true); |
+ focusController()->setFocusedFrame(frame); |
+ return found; |
+ } |
+ |
+ return false; |
+} |
+ |
+unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit) |
+{ |
+ if (target.isEmpty() || !mainFrame()) |
+ return 0; |
+ |
+ unsigned matches = 0; |
+ |
+ Frame* frame = mainFrame(); |
+ do { |
+ frame->setMarkedTextMatchesAreHighlighted(shouldHighlight); |
+ matches += frame->markAllMatchesForText(target, caseSensitivity == TextCaseSensitive, (limit == 0) ? 0 : (limit - matches)); |
+ frame = incrementFrame(frame, true, false); |
+ } while (frame); |
+ |
+ return matches; |
+} |
+ |
+void Page::unmarkAllTextMatches() |
+{ |
+ if (!mainFrame()) |
+ return; |
+ |
+ Frame* frame = mainFrame(); |
+ do { |
+ if (Document* document = frame->document()) |
+ document->removeMarkers(DocumentMarker::TextMatch); |
+ frame = incrementFrame(frame, true, false); |
+ } while (frame); |
+} |
+ |
+const Selection& Page::selection() const |
+{ |
+ return focusController()->focusedOrMainFrame()->selection()->selection(); |
+} |
+ |
+void Page::setDefersLoading(bool defers) |
+{ |
+ 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()) { |
+ if (frame->document()) |
+ 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() |
+{ |
+#if !FRAME_LOADS_USER_STYLESHEET |
+ // FIXME: We should provide a way to load other types of URLs than just |
+ // file: (e.g., http:, data:). |
+ if (m_settings->userStyleSheetLocation().isLocalFile()) |
+ m_userStyleSheetPath = m_settings->userStyleSheetLocation().fileSystemPath(); |
+ else |
+ m_userStyleSheetPath = String(); |
+ |
+ m_didLoadUserStyleSheet = false; |
+ m_userStyleSheet = String(); |
+ m_userStyleSheetModificationTime = 0; |
+#endif |
+} |
+ |
+const String& Page::userStyleSheet() const |
+{ |
+ if (m_userStyleSheetPath.isEmpty()) { |
+ ASSERT(m_userStyleSheet.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; |
+ |
+ m_userStyleSheet = TextResourceDecoder::create("text/css")->decode(data->data(), data->size()); |
+ |
+ 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); |
+ ASSERT(allPages); |
+ 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); |
+ ASSERT(allPages); |
+ 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); |
+} |
+ |
+#if ENABLE(DOM_STORAGE) |
+SessionStorage* Page::sessionStorage(bool optionalCreate) |
+{ |
+ if (!m_sessionStorage && optionalCreate) |
+ m_sessionStorage = SessionStorage::create(this); |
+ |
+ return m_sessionStorage.get(); |
+} |
+ |
+void Page::setSessionStorage(PassRefPtr<SessionStorage> newStorage) |
+{ |
+ ASSERT(newStorage->page() == this); |
+ m_sessionStorage = newStorage; |
+} |
+#endif |
+ |
+unsigned Page::pendingUnloadEventCount() |
+{ |
+ return m_pendingUnloadEventCount; |
+} |
+ |
+void Page::changePendingUnloadEventCount(int delta) |
+{ |
+ if (!delta) |
+ return; |
+ ASSERT( (delta + (int)m_pendingUnloadEventCount) >= 0 ); |
+ |
+ if (m_pendingUnloadEventCount == 0) |
+ m_chrome->disableSuddenTermination(); |
+ else if ((m_pendingUnloadEventCount + delta) == 0) |
+ m_chrome->enableSuddenTermination(); |
+ |
+ m_pendingUnloadEventCount += delta; |
+ return; |
+} |
+ |
+unsigned Page::pendingBeforeUnloadEventCount() |
+{ |
+ return m_pendingBeforeUnloadEventCount; |
+} |
+ |
+void Page::changePendingBeforeUnloadEventCount(int delta) |
+{ |
+ if (!delta) |
+ return; |
+ ASSERT( (delta + (int)m_pendingBeforeUnloadEventCount) >= 0 ); |
+ |
+ if (m_pendingBeforeUnloadEventCount == 0) |
+ m_chrome->disableSuddenTermination(); |
+ else if ((m_pendingBeforeUnloadEventCount + delta) == 0) |
+ m_chrome->enableSuddenTermination(); |
+ |
+ m_pendingBeforeUnloadEventCount += delta; |
+ return; |
+} |
+ |
+#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(); |
+} |
+ |
+} // namespace WebCore |