/*
 * 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.
 */

#ifndef Page_h
#define Page_h

#include "FrameLoaderTypes.h"
#include "FindOptions.h"
#include "LayoutTypesInlineMethods.h"
#include "Pagination.h"
#include "PageVisibilityState.h"
#include "PlatformScreen.h"
#include "PlatformString.h"
#include "Region.h"
#include "SessionID.h"
#include "Supplementable.h"
#include "ViewportArguments.h"
#include <wtf/Forward.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
#include <wtf/Noncopyable.h>

#if OS(SOLARIS)
#include <sys/time.h> // For time_t structure.
#endif

#if PLATFORM(MAC)
#include "SchedulePair.h"
#endif

namespace JSC {
    class Debugger;
}

namespace WebCore {

class AlternativeTextClient;
class BackForwardController;
class BackForwardList;
class Chrome;
class ChromeClient;
#if ENABLE(CONTEXT_MENUS)
class ContextMenuClient;
class ContextMenuController;
#endif
class Document;
class DragCaretController;
class DragClient;
class DragController;
class EditorClient;
class FocusController;
class Frame;
class FrameLoaderClient;
class FrameSelection;
class HaltablePlugin;
class HistoryItem;
class InspectorClient;
class InspectorController;
class MediaCanStartListener;
class Node;
class PageConsole;
class PageGroup;
class PluginData;
class PointerLockController;
class ProgressTracker;
class Range;
class RenderObject;
class RenderTheme;
class VisibleSelection;
class ScrollableArea;
class ScrollingCoordinator;
class Settings;
class StorageNamespace;

typedef uint64_t LinkHash;

enum FindDirection { FindDirectionForward, FindDirectionBackward };

float deviceScaleFactor(Frame*);

class Page : public Supplementable<Page> {
	WTF_MAKE_NONCOPYABLE(Page); WTF_MAKE_FAST_ALLOCATED;
	friend class Settings;
public:
	static void scheduleForcedStyleRecalcForAllPages();
    static void jettisonSelectorQueryCacheInAllDocuments();
    static void jettisonStyleResolversInAllDocuments();
    static void invalidateDOMCookieCacheInAllDocuments();

	// It is up to the platform to ensure that non-null clients are provided where required.
	struct PageClients {
		WTF_MAKE_NONCOPYABLE(PageClients); WTF_MAKE_FAST_ALLOCATED;
	public:
		PageClients();
		~PageClients();

		AlternativeTextClient* alternativeTextClient;
		ChromeClient* chromeClient;
		FrameLoaderClient* loaderClientForMainFrame;
#if ENABLE(CONTEXT_MENUS)
		ContextMenuClient* contextMenuClient;
#endif
		EditorClient* editorClient;
		DragClient* dragClient;
		InspectorClient* inspectorClient;
		BackForwardList* backForwardClient;
	};

	explicit Page(PageClients&);
	~Page();

	uint64_t renderTreeSize() const;

	void setNeedsRecalcStyleInAllFrames();

	ALWAYS_INLINE RenderTheme* theme() const { return m_theme.get(); };

	ViewportArguments viewportArguments() const;

	static void refreshPlugins(bool reload);
	PluginData* pluginData() const;

	void setCanStartMedia(bool);
	ALWAYS_INLINE bool canStartMedia() const { return m_canStartMedia; }

	ALWAYS_INLINE EditorClient* editorClient() const { return m_editorClient; }

	void setMainFrame(PassRefPtr<Frame>);
	ALWAYS_INLINE Frame* mainFrame() const { return m_mainFrame.get(); }

	bool openedByDOM() const;
	void setOpenedByDOM();

	// DEPRECATED. Use backForward() instead of the following 6 functions.
	BackForwardList* backForwardList() const;
	bool goBack();
	bool goForward();
	bool canGoBackOrForward(int distance) const;
	void goBackOrForward(int distance);
	int getHistoryLength();

	void goToItem(HistoryItem*, FrameLoadType);

	void setGroupName(const String&);
	const String& groupName() const;

	ALWAYS_INLINE PageGroup& group() { if (!m_group) initGroup(); return *m_group; }
	ALWAYS_INLINE PageGroup* groupPtr() { return m_group; } // can return 0

	ALWAYS_INLINE void incrementFrameCount() { ++m_frameCount; }
	ALWAYS_INLINE void decrementFrameCount() { ASSERT(m_frameCount); --m_frameCount; }
	ALWAYS_INLINE int frameCount() const { checkFrameCountConsistency(); return m_frameCount; }

	ALWAYS_INLINE Chrome* chrome() const { return m_chrome.get(); }
	ALWAYS_INLINE DragCaretController* dragCaretController() const { return m_dragCaretController.get(); }
#if ENABLE(DRAG_SUPPORT)
	ALWAYS_INLINE DragController* dragController() const { return m_dragController.get(); }
#endif
	ALWAYS_INLINE FocusController* focusController() const { return m_focusController.get(); }
#if ENABLE(CONTEXT_MENUS)
	ALWAYS_INLINE ContextMenuController* contextMenuController() const { return m_contextMenuController.get(); }
#endif
#if ENABLE(INSPECTOR)
	ALWAYS_INLINE InspectorController* inspectorController() const { return m_inspectorController.get(); }
#endif
#if ENABLE(POINTER_LOCK)
	ALWAYS_INLINE PointerLockController* pointerLockController() const { return m_pointerLockController.get(); }
#endif

	ScrollingCoordinator* scrollingCoordinator();

	ALWAYS_INLINE Settings& settings() const { return *m_settings; }
	ALWAYS_INLINE ProgressTracker* progress() const { return m_progress.get(); }
	ALWAYS_INLINE BackForwardController* backForward() const { return m_backForwardController.get(); }

	enum ViewMode {
		ViewModeInvalid,
		ViewModeWindowed,
		ViewModeFloating,
		ViewModeFullscreen,
		ViewModeMaximized,
		ViewModeMinimized
	};
	static ViewMode stringToViewMode(const String&);

	ALWAYS_INLINE ViewMode viewMode() const { return m_viewMode; }
	void setViewMode(ViewMode);

	ALWAYS_INLINE void setTabKeyCyclesThroughElements(bool b) { m_tabKeyCyclesThroughElements = b; }
	ALWAYS_INLINE bool tabKeyCyclesThroughElements() const { return m_tabKeyCyclesThroughElements; }

	bool findString(const String&, FindOptions);
	// FIXME: Switch callers over to the FindOptions version and retire this one.
	bool findString(const String&, TextCaseSensitivity, FindDirection, bool shouldWrap);

	PassRefPtr<Range> rangeOfString(const String&, Range*, FindOptions);

	unsigned markAllMatchesForText(const String&, FindOptions, bool shouldHighlight, unsigned);
	// FIXME: Switch callers over to the FindOptions version and retire this one.
	unsigned markAllMatchesForText(const String&, TextCaseSensitivity, bool shouldHighlight, unsigned);
	void unmarkAllTextMatches();

#if PLATFORM(MAC)
	void addSchedulePair(PassRefPtr<SchedulePair>);
	void removeSchedulePair(PassRefPtr<SchedulePair>);
	SchedulePairHashSet* scheduledRunLoopPairs() { return m_scheduledRunLoopPairs.get(); }

	OwnPtr<SchedulePairHashSet> m_scheduledRunLoopPairs;
#endif

	const VisibleSelection& selection() const;

	void setDefersLoading(bool);
	ALWAYS_INLINE bool defersLoading() const { return m_defersLoading; }

	void clearUndoRedoOperations();

	bool inLowQualityImageInterpolationMode() const;
	void setInLowQualityImageInterpolationMode(bool = true);

	ALWAYS_INLINE float mediaVolume() const { return m_mediaVolume; }
	void setMediaVolume(float volume);

	void setPageScaleFactor(float scale, const IntPoint& origin);
	ALWAYS_INLINE float pageScaleFactor() const { return m_pageScaleFactor; }

	ALWAYS_INLINE float deviceScaleFactor() const { return m_deviceScaleFactor; }
	void setDeviceScaleFactor(float);

    // Page and FrameView both store a Pagination value. Page::pagination() is set only by API,
    // and FrameView::pagination() is set only by CSS. Page::pagination() will affect all
    // FrameViews in the page cache, but FrameView::pagination() only affects the current
    // FrameView.
	ALWAYS_INLINE const Pagination& pagination() const { return m_pagination; }
	void setPagination(const Pagination&);

	unsigned pageCount() const;

	// Notifications when the Page starts and stops being presented via a native window.
	void didMoveOnscreen();
	void willMoveOffscreen();
	ALWAYS_INLINE bool isOnscreen() const { return m_isOnscreen; }

	void windowScreenDidChange(PlatformDisplayID);

	void suspendScriptedAnimations();
	void resumeScriptedAnimations();
	ALWAYS_INLINE bool scriptedAnimationsSuspended() const { return m_scriptedAnimationsSuspended; }

	void userStyleSheetLocationChanged();
	const String& userStyleSheet() const;

	void dnsPrefetchingStateChanged();
	void privateBrowsingStateChanged();

	static void setDebuggerForAllPages(JSC::Debugger*);
	void setDebugger(JSC::Debugger*);
	ALWAYS_INLINE JSC::Debugger* debugger() const { return m_debugger; }

	static void removeAllVisitedLinks();

	static void allVisitedStateChanged(PageGroup*);
	static void visitedStateChanged(PageGroup*, LinkHash visitedHash);

	StorageNamespace* sessionStorage(bool optionalCreate = true);
	void setSessionStorage(PassRefPtr<StorageNamespace>);

	void setCustomHTMLTokenizerTimeDelay(double);
	ALWAYS_INLINE bool hasCustomHTMLTokenizerTimeDelay() const { return m_customHTMLTokenizerTimeDelay != -1; }
	ALWAYS_INLINE double customHTMLTokenizerTimeDelay() const { ASSERT(m_customHTMLTokenizerTimeDelay != -1); return m_customHTMLTokenizerTimeDelay; }

	void setCustomHTMLTokenizerChunkSize(int);
	ALWAYS_INLINE bool hasCustomHTMLTokenizerChunkSize() const { return m_customHTMLTokenizerChunkSize != -1; }
	ALWAYS_INLINE int customHTMLTokenizerChunkSize() const { ASSERT(m_customHTMLTokenizerChunkSize != -1); return m_customHTMLTokenizerChunkSize; }

	void setMemoryCacheClientCallsEnabled(bool);
	ALWAYS_INLINE bool areMemoryCacheClientCallsEnabled() const { return m_areMemoryCacheClientCallsEnabled; }

	void setJavaScriptURLsAreAllowed(bool);
	bool javaScriptURLsAreAllowed() const;

	// Don't allow more than a certain number of frames in a page.
	// This seems like a reasonable upper bound, and otherwise mutually
	// recursive frameset pages can quickly bring the program to its knees
	// with exponential growth in the number of frames.
	static const int maxNumberOfFrames = 1000;

	ALWAYS_INLINE void setEditable(bool isEditable) { m_isEditable = isEditable; }
	ALWAYS_INLINE bool isEditable() { return m_isEditable; }

#if ENABLE(PAGE_VISIBILITY_API)
	PageVisibilityState visibilityState() const;
	void setVisibilityState(PageVisibilityState, bool);
#endif
    void resumeAnimatingImages();

	ALWAYS_INLINE PlatformDisplayID displayID() const { return m_displayID; }

	bool isCountingRelevantRepaintedObjects() const;
	void setRelevantRepaintedObjectsCounterThreshold(uint64_t);
	void startCountingRelevantRepaintedObjects();
	void resetRelevantPaintedObjectCounter();
	void addRelevantRepaintedObject(RenderObject*, const LayoutRect& objectPaintRect);
	void addRelevantUnpaintedObject(RenderObject*, const LayoutRect& objectPaintRect);

	void suspendActiveDOMObjectsAndAnimations();
	void resumeActiveDOMObjectsAndAnimations();
#ifndef NDEBUG
	ALWAYS_INLINE void setIsPainting(bool painting) { m_isPainting = painting; }
	ALWAYS_INLINE bool isPainting() const { return m_isPainting; }
#endif

	ALWAYS_INLINE AlternativeTextClient* alternativeTextClient() const { return m_alternativeTextClient; }

    bool hasSeenPlugin(const String& serviceType) const;
    bool hasSeenAnyPlugin() const;
    void sawPlugin(const String& serviceType);
    void resetSeenPlugins();

    PageConsole* console() { return m_console.get(); }

    SessionID sessionID() const;
    void setSessionID(SessionID);
    bool isSessionIDSet() const { return m_sessionID.isValid(); }
    bool usesEphemeralSession() const { return m_sessionID.isEphemeral(); }

#if ENABLE(VIDEO_TRACK)
    void captionPreferencesChanged();
#endif

private:
	void initGroup();

#if ASSERT_DISABLED
	void checkFrameCountConsistency() const { }
#else
	void checkFrameCountConsistency() const;
#endif

	MediaCanStartListener* takeAnyMediaCanStartListener();

	void setMinimumTimerInterval(double);
	double minimumTimerInterval() const;

	OwnPtr<Chrome> m_chrome;
	OwnPtr<DragCaretController> m_dragCaretController;

#if ENABLE(DRAG_SUPPORT)
	OwnPtr<DragController> m_dragController;
#endif
	OwnPtr<FocusController> m_focusController;
#if ENABLE(CONTEXT_MENUS)
	OwnPtr<ContextMenuController> m_contextMenuController;
#endif
#if ENABLE(INSPECTOR)
	OwnPtr<InspectorController> m_inspectorController;
#endif
#if ENABLE(POINTER_LOCK)
	OwnPtr<PointerLockController> m_pointerLockController;
#endif
	RefPtr<ScrollingCoordinator> m_scrollingCoordinator;

	const RefPtr<Settings> m_settings;
	OwnPtr<ProgressTracker> m_progress;

	OwnPtr<BackForwardController> m_backForwardController;
	RefPtr<Frame> m_mainFrame;

	mutable RefPtr<PluginData> m_pluginData;

	RefPtr<RenderTheme> m_theme;

	EditorClient* m_editorClient;

	int m_frameCount;
	String m_groupName;
	bool m_openedByDOM;

	bool m_tabKeyCyclesThroughElements;
	bool m_defersLoading;
	unsigned m_defersLoadingCallCount;

	bool m_inLowQualityInterpolationMode;
	bool m_cookieEnabled;
	bool m_areMemoryCacheClientCallsEnabled;
	float m_mediaVolume;

	float m_pageScaleFactor;
	float m_deviceScaleFactor;

	Pagination m_pagination;

	bool m_javaScriptURLsAreAllowed;

	String m_userStyleSheetPath;
	mutable String m_userStyleSheet;
	mutable bool m_didLoadUserStyleSheet;
	mutable time_t m_userStyleSheetModificationTime;

	OwnPtr<PageGroup> m_singlePageGroup;
	PageGroup* m_group;

	JSC::Debugger* m_debugger;

	double m_customHTMLTokenizerTimeDelay;
	int m_customHTMLTokenizerChunkSize;

	bool m_canStartMedia;

	RefPtr<StorageNamespace> m_sessionStorage;

	ViewMode m_viewMode;

	double m_minimumTimerInterval;

	bool m_isEditable;
	bool m_isOnscreen;

#if ENABLE(PAGE_VISIBILITY_API)
	PageVisibilityState m_visibilityState;
#endif
	PlatformDisplayID m_displayID;

	HashSet<RenderObject*> m_relevantUnpaintedRenderObjects;
    Region m_topRelevantPaintedRegion;
    Region m_bottomRelevantPaintedRegion;
	Region m_relevantUnpaintedRegion;
	bool m_isCountingRelevantRepaintedObjects;
#ifndef NDEBUG
	bool m_isPainting;
#endif
	AlternativeTextClient* m_alternativeTextClient;

	bool m_scriptedAnimationsSuspended;
    OwnPtr<PageConsole> m_console;

    HashSet<String> m_seenPlugins;

    SessionID m_sessionID;
};

inline Page::PageClients::PageClients()
: alternativeTextClient(0)
, chromeClient(0)
, loaderClientForMainFrame(0)
#if ENABLE(CONTEXT_MENUS)
, contextMenuClient(0)
#endif
, editorClient(0)
, dragClient(0)
, inspectorClient(0)
, backForwardClient(0)
{
}

inline Page::PageClients::~PageClients()
{
}

} // namespace WebCore
    
#endif // Page_h
