/*
 *  Copyright (C) 2007, 2008 Holger Hans Peter Freyther
 *  Copyright (C) 2007, 2008, 2009 Christian Dywan <christian@imendio.com>
 *  Copyright (C) 2007 Xan Lopez <xan@gnome.org>
 *  Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
 *  Copyright (C) 2008 Jan Alonzo <jmalonzo@unpluggable.com>
 *  Copyright (C) 2008 Gustavo Noronha Silva <gns@gnome.org>
 *  Copyright (C) 2008 Nuanti Ltd.
 *  Copyright (C) 2008, 2009, 2010 Collabora Ltd.
 *  Copyright (C) 2009, 2010 Igalia S.L.
 *  Copyright (C) 2009 Movial Creative Technologies Inc.
 *  Copyright (C) 2009 Bobby Powers
 *  Copyright (C) 2010 Collabora Ltd.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "config.h"
#include "webkitwebpage.h"

#if ENABLE(NETSCAPE_PLUGIN_API)
// FIXME: Putting this in order breaks including X.
#include "npruntime_priv.h"
#endif

// FIXME: Needs to be imported before WebCore::Font gets defined
#include "FrameLoaderClientGObject.h"
#undef Status
#undef None

#include "CachedImage.h"
#include "CachedResourceLoader.h"
#include "Chrome.h"
#include "ChromeClientGObject.h"
#include "CachedResourceLoader.h"
#include "DatabaseManager.h"
#include "DocumentLoader.h"
#include "DragClientGObject.h"
#include "EditorClientClutter.h"
#include "FocusController.h"
#include "Frame.h"
#include "GeolocationClientGtk.h"
#include "GeolocationClientMock.h"
#include "GeolocationController.h"
#include <wtf/gobject/GRefPtr.h>
#include "HitTestResult.h"
#include "IconDatabase.h"
#include "InspectorClientClutter.h"
#include "Page.h"
#include "ProgressTracker.h"
#include "RenderLayer.h"
#include "RenderView.h"
#include "ScriptValue.h"
#include "Settings.h"
#include "UtilClutter.h"
#include "webkitdom/WebKitDOMDocumentPrivate.h"
#include "webkitgeolocationpolicydecision.h"
#include "webkitgobjectflavourprivate.h"
#include "webkitgwebframeprivate.h"
#include "webkithittestresultprivate.h"
#include "webkitmarshal.h"
#include "webkitnetworkresponse.h"
#include "webkitwebbackforwardlist.h"
#include "webkitwebbackforwardlistprivate.h"
#include "webkitglobalsprivate.h"
#include "webkitwebhistoryitemprivate.h"
#include "webkitwebinspector.h"
#include "webkitwebinspectorprivate.h"
#include "webkitwebpageprivate.h"
#include "webkitwebpolicydecision.h"
#include "webkitwebresource.h"
#include "webkitwebsettings.h"
#include "webkitwebwindowfeatures.h"
#include <glib/gi18n-lib.h>

#if PLATFORM(CLUTTER)
#include "DumpRenderTreeSupportClutter.h"
#endif

#if USE(ACCELERATED_COMPOSITING)
#include "clutter/GraphicsLayerActor.h"
#endif

#if USE(TILED_BACKING_STORE)
#include "TiledBackingStore.h"
#endif

using namespace WebKit;
using namespace WebCore;

enum {
    PROP_0,

    PROP_ICON_URI,
    PROP_LOAD_STATUS,
    PROP_PROGRESS,
    PROP_SETTINGS,
    PROP_TITLE,
    PROP_URI,
    PROP_WEB_INSPECTOR,
    PROP_WEB_VIEW,
    PROP_WINDOW_FEATURES,
    PROP_ZOOM_LEVEL,
    PROP_USE_FIXED_LAYOUT,
    PROP_FIXED_LAYOUT_WIDTH,
    PROP_FIXED_LAYOUT_HEIGHT,
};

enum {
    CREATE_WINDOW,
    WEB_PAGE_READY,
    DOWNLOAD_REQUESTED,
    INSPECTION_REQUESTED,
    FRAME_CREATED,
    TITLE_CHANGED,
    HOVERING_OVER_LINK,
    STATUS_BAR_TEXT_CHANGED,
    ICON_LOADED,
    CONSOLE_MESSAGE,
    SCRIPT_ALERT,
    SCRIPT_CONFIRM,
    SCRIPT_PROMPT,
    MIME_TYPE_POLICY_DECISION_REQUESTED,
    NAVIGATION_POLICY_DECISION_REQUESTED,
    NEW_WINDOW_POLICY_DECISION_REQUESTED,
    ONLOAD_EVENT,
    DATABASE_QUOTA_EXCEEDED,
    RESOURCE_REQUEST_STARTING,
    DOCUMENT_LOAD_FINISHED,
    GEOLOCATION_POLICY_DECISION_REQUESTED,
    GEOLOCATION_POLICY_DECISION_CANCELLED,
    WEB_LOAD_ERROR,
    WEB_LOAD_FINISHED,
    WEB_LOAD_STARTED,
    WINDOW_OBJECT_CLEARED,
    SHOULD_INTERRUPT_SCRIPT,
    INPUT_METHOD_STATE_SET,
    MICROFOCUS_CHANGED,
    LAST_SIGNAL
};

const int zoomAreaMargin = 10;

static guint webkit_web_page_signals[LAST_SIGNAL] = { 0, };

G_DEFINE_TYPE(WebKitWebPage, webkit_web_page, G_TYPE_OBJECT)

static void webkit_web_page_set_window_features(WebKitWebPage* webPage, WebKitWebWindowFeatures* webWindowFeatures);

// FIXME: move this to webkitgeneralprivate, and share with GTK+!
static gboolean webkit_signal_accumulator_object_handled(GSignalInvocationHint* ihint, GValue* returnAccu, const GValue* handlerReturn, gpointer dummy)
{
    gpointer newWebPage = g_value_get_object(handlerReturn);
    g_value_set_object(returnAccu, newWebPage);

    // Continue if we don't have a newWebPage
    return !newWebPage;
}

// FIXME: move this to webkitgeneralprivate, and share with GTK+?
static void updateSettings(WebKitWebPage* webPage)
{
    WebKitWebPagePrivate* priv = webPage->priv;
    WebKitWebSettings* webSettings = priv->webSettings.get();
    Settings* settings = core(webPage)->settings();

    gchar* defaultEncoding, *cursiveFontFamily, *defaultFontFamily, *fantasyFontFamily, *monospaceFontFamily, *sansSerifFontFamily, *serifFontFamily, *userStylesheetUri;
    gboolean autoLoadImages, autoShrinkImages, printBackgrounds,
        enableScripts, enablePlugins, enableDeveloperExtras, resizableTextAreas,
        enablePrivateBrowsing, enableCaretBrowsing, enableHTML5Database, enableHTML5LocalStorage,
        enableXSSAuditor, enableSpatialNavigation, enableFrameFlattening, javascriptCanOpenWindows,
        javaScriptCanAccessClipboard, enableOfflineWebAppCache,
        enableUniversalAccessFromFileURI, enableFileAccessFromFileURI,
        enableDOMPaste, tabKeyCyclesThroughElements,
        enableSiteSpecificQuirks, usePageCache, enableJavaApplet, enableHyperlinkAuditing,
        enableDebuggingBorders, enableMediaPreload;

    WebKitEditingBehavior editingBehavior;

    g_object_get(webSettings,
                 "default-encoding", &defaultEncoding,
                 "cursive-font-family", &cursiveFontFamily,
                 "default-font-family", &defaultFontFamily,
                 "fantasy-font-family", &fantasyFontFamily,
                 "monospace-font-family", &monospaceFontFamily,
                 "sans-serif-font-family", &sansSerifFontFamily,
                 "serif-font-family", &serifFontFamily,
                 "auto-load-images", &autoLoadImages,
                 "auto-shrink-images", &autoShrinkImages,
                 "print-backgrounds", &printBackgrounds,
                 "enable-scripts", &enableScripts,
                 "enable-plugins", &enablePlugins,
                 "resizable-text-areas", &resizableTextAreas,
                 "user-stylesheet-uri", &userStylesheetUri,
                 "enable-media-preload", &enableMediaPreload,
                 "enable-developer-extras", &enableDeveloperExtras,
                 "enable-private-browsing", &enablePrivateBrowsing,
                 "enable-caret-browsing", &enableCaretBrowsing,
                 "enable-html5-database", &enableHTML5Database,
                 "enable-html5-local-storage", &enableHTML5LocalStorage,
                 "enable-xss-auditor", &enableXSSAuditor,
                 "enable-spatial-navigation", &enableSpatialNavigation,
                 "enable-frame-flattening", &enableFrameFlattening,
                 "javascript-can-open-windows-automatically", &javascriptCanOpenWindows,
                 "javascript-can-access-clipboard", &javaScriptCanAccessClipboard,
                 "enable-offline-web-application-cache", &enableOfflineWebAppCache,
                 "editing-behavior", &editingBehavior,
                 "enable-universal-access-from-file-uris", &enableUniversalAccessFromFileURI,
                 "enable-file-access-from-file-uris", &enableFileAccessFromFileURI,
                 "enable-dom-paste", &enableDOMPaste,
                 "tab-key-cycles-through-elements", &tabKeyCyclesThroughElements,
                 "enable-site-specific-quirks", &enableSiteSpecificQuirks,
                 "enable-page-cache", &usePageCache,
                 "enable-java-applet", &enableJavaApplet,
                 "enable-hyperlink-auditing", &enableHyperlinkAuditing,
                 "enable-debugging-borders", &enableDebuggingBorders,
                 NULL);

    settings->setDefaultTextEncodingName(defaultEncoding);
    settings->setCursiveFontFamily(cursiveFontFamily);
    settings->setStandardFontFamily(defaultFontFamily);
    settings->setFantasyFontFamily(fantasyFontFamily);
    settings->setFixedFontFamily(monospaceFontFamily);
    settings->setSansSerifFontFamily(sansSerifFontFamily);
    settings->setSerifFontFamily(serifFontFamily);
    settings->setLoadsImagesAutomatically(autoLoadImages);
    settings->setShrinksStandaloneImagesToFit(autoShrinkImages);
    settings->setShouldPrintBackgrounds(printBackgrounds);
    settings->setScriptEnabled(enableScripts);
    settings->setPluginsEnabled(enablePlugins);
    settings->setTextAreasAreResizable(resizableTextAreas);
    settings->setUserStyleSheetLocation(KURL(KURL(), userStylesheetUri));
    settings->setDeveloperExtrasEnabled(enableDeveloperExtras);
    settings->setPrivateBrowsingEnabled(enablePrivateBrowsing);
    settings->setCaretBrowsingEnabled(enableCaretBrowsing);
#if ENABLE(SQL_DATABASE)
    DatabaseManager::manager().setIsAvailable(enableHTML5Database);
#endif
    settings->setLocalStorageEnabled(enableHTML5LocalStorage);
    settings->setXSSAuditorEnabled(enableXSSAuditor);
    settings->setSpatialNavigationEnabled(enableSpatialNavigation);
    settings->setFrameFlatteningEnabled(enableFrameFlattening);
    settings->setJavaScriptCanOpenWindowsAutomatically(javascriptCanOpenWindows);
    settings->setJavaScriptCanAccessClipboard(javaScriptCanAccessClipboard);
    settings->setOfflineWebApplicationCacheEnabled(enableOfflineWebAppCache);
    settings->setEditingBehaviorType(static_cast<WebCore::EditingBehaviorType>(editingBehavior));
    settings->setAllowUniversalAccessFromFileURLs(enableUniversalAccessFromFileURI);
    settings->setAllowFileAccessFromFileURLs(enableFileAccessFromFileURI);
    settings->setDOMPasteAllowed(enableDOMPaste);
    settings->setNeedsSiteSpecificQuirks(enableSiteSpecificQuirks);
    settings->setUsesPageCache(usePageCache);
    settings->setJavaEnabled(enableJavaApplet);
    settings->setHyperlinkAuditingEnabled(enableHyperlinkAuditing);
    settings->setShowDebugBorders(enableDebuggingBorders);
    settings->setMediaPreloadEnabled(enableMediaPreload);

    Page* page = core(webPage);
    if (page)
        page->setTabKeyCyclesThroughElements(tabKeyCyclesThroughElements);

    g_free(defaultEncoding);
    g_free(cursiveFontFamily);
    g_free(defaultFontFamily);
    g_free(fantasyFontFamily);
    g_free(monospaceFontFamily);
    g_free(sansSerifFontFamily);
    g_free(serifFontFamily);
    g_free(userStylesheetUri);
}

AtkObject* webkit_web_page_get_accessible(WebKitWebPage* page)
{
    if (!core(page))
        return 0;

    AXObjectCache::enableAccessibility();

    Frame* coreFrame = core(page)->mainFrame();
    if (!coreFrame)
        return 0;

    Document* doc = coreFrame->document();
    if (!doc)
        return 0;

    AccessibilityObject* rootAccessible = doc->axObjectCache()->rootObject();
    if (!rootAccessible)
        return 0;

    // We need to return the root accessibility object's first child
    // to get to the actual ATK Object associated with the web view.
    // See https://bugs.webkit.org/show_bug.cgi?id=51932
    AtkObject* axRoot = rootAccessible->wrapper();
    if (!axRoot || !ATK_IS_OBJECT(axRoot))
        return 0;

    AtkObject* axWebView = atk_object_ref_accessible_child(ATK_OBJECT(axRoot), 0);
    if (!axWebView || !ATK_IS_OBJECT(axWebView))
        return 0;

    // We don't want the extra reference returned by ref_accessible_child.
    g_object_unref(axWebView);
    return axWebView;
}

static gdouble webPageGetDPI(WebKitWebPage* webPage)
{
    WebKitWebPagePrivate* priv = webPage->priv;
    WebKitWebSettings* webSettings = priv->webSettings.get();
    gboolean enforce96DPI;
    g_object_get(webSettings, "enforce-96-dpi", &enforce96DPI, NULL);
    if (enforce96DPI)
        return 96.0;

    return webkit_gobject_flavour_get_dpi();
}

static inline gint pixelsFromSize(WebKitWebPage* webPage, gint size)
{
    gdouble DPI = webPageGetDPI(webPage);
    return size / 72.0 * DPI;
}

static void settingsNotifyCallback(WebKitWebSettings* webSettings, GParamSpec* pspec, WebKitWebPage* webPage)
{
    Settings* settings = core(webPage)->settings();

    const gchar* name = g_intern_string(pspec->name);
    GValue value = { 0, { { 0 } } };
    g_value_init(&value, pspec->value_type);
    g_object_get_property(G_OBJECT(webSettings), name, &value);

    if (name == g_intern_string("default-encoding"))
        settings->setDefaultTextEncodingName(g_value_get_string(&value));
    else if (name == g_intern_string("cursive-font-family"))
        settings->setCursiveFontFamily(g_value_get_string(&value));
    else if (name == g_intern_string("default-font-family"))
        settings->setStandardFontFamily(g_value_get_string(&value));
    else if (name == g_intern_string("fantasy-font-family"))
        settings->setFantasyFontFamily(g_value_get_string(&value));
    else if (name == g_intern_string("monospace-font-family"))
        settings->setFixedFontFamily(g_value_get_string(&value));
    else if (name == g_intern_string("sans-serif-font-family"))
        settings->setSansSerifFontFamily(g_value_get_string(&value));
    else if (name == g_intern_string("serif-font-family"))
        settings->setSerifFontFamily(g_value_get_string(&value));
    else if (name == g_intern_string("default-font-size"))
        settings->setDefaultFontSize(pixelsFromSize(webPage, g_value_get_int(&value)));
    else if (name == g_intern_string("default-monospace-font-size"))
        settings->setDefaultFixedFontSize(pixelsFromSize(webPage, g_value_get_int(&value)));
    else if (name == g_intern_string("minimum-font-size"))
        settings->setMinimumFontSize(pixelsFromSize(webPage, g_value_get_int(&value)));
    else if (name == g_intern_string("minimum-logical-font-size"))
        settings->setMinimumLogicalFontSize(pixelsFromSize(webPage, g_value_get_int(&value)));
    // FIXME
    // else if (name == g_intern_string("enforce-96-dpi"))
    //     webkit_web_page_screen_changed(GTK_WIDGET(webPage), NULL);
    else if (name == g_intern_string("auto-load-images"))
        settings->setLoadsImagesAutomatically(g_value_get_boolean(&value));
    else if (name == g_intern_string("auto-shrink-images"))
        settings->setShrinksStandaloneImagesToFit(g_value_get_boolean(&value));
    else if (name == g_intern_string("print-backgrounds"))
        settings->setShouldPrintBackgrounds(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-scripts"))
        settings->setScriptEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-plugins"))
        settings->setPluginsEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("resizable-text-areas"))
        settings->setTextAreasAreResizable(g_value_get_boolean(&value));
    else if (name == g_intern_string("user-stylesheet-uri"))
        settings->setUserStyleSheetLocation(KURL(KURL(), g_value_get_string(&value)));
    else if (name == g_intern_string("enable-developer-extras"))
        settings->setDeveloperExtrasEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-private-browsing"))
        settings->setPrivateBrowsingEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-caret-browsing"))
        settings->setCaretBrowsingEnabled(g_value_get_boolean(&value));
#if ENABLE(SQL_DATABASE)
    else if (name == g_intern_string("enable-html5-database")) {
        DatabaseManager::manager().setIsAvailable(g_value_get_boolean(&value));
    }
#endif
    else if (name == g_intern_string("enable-html5-local-storage"))
        settings->setLocalStorageEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-xss-auditor"))
        settings->setXSSAuditorEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-spatial-navigation"))
        settings->setSpatialNavigationEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-frame-flattening"))
        settings->setFrameFlatteningEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("javascript-can-open-windows-automatically"))
        settings->setJavaScriptCanOpenWindowsAutomatically(g_value_get_boolean(&value));
    else if (name == g_intern_string("javascript-can-access-clipboard"))
        settings->setJavaScriptCanAccessClipboard(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-offline-web-application-cache"))
        settings->setOfflineWebApplicationCacheEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("editing-behavior"))
        settings->setEditingBehaviorType(static_cast<WebCore::EditingBehaviorType>(g_value_get_enum(&value)));
    else if (name == g_intern_string("enable-universal-access-from-file-uris"))
        settings->setAllowUniversalAccessFromFileURLs(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-file-access-from-file-uris"))
        settings->setAllowFileAccessFromFileURLs(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-dom-paste"))
        settings->setDOMPasteAllowed(g_value_get_boolean(&value));
    else if (name == g_intern_string("tab-key-cycles-through-elements")) {
        Page* page = core(webPage);
        if (page)
            page->setTabKeyCyclesThroughElements(g_value_get_boolean(&value));
    } else if (name == g_intern_string("enable-site-specific-quirks"))
        settings->setNeedsSiteSpecificQuirks(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-page-cache"))
        settings->setUsesPageCache(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-java-applet"))
        settings->setJavaEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-hyperlink-auditing"))
        settings->setHyperlinkAuditingEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-tiled-backing-store")) {
#if USE(TILED_BACKING_STORE)
        bool enabled = g_value_get_boolean(&value);
        settings->setTiledBackingStoreEnabled(enabled);

        Page* page = core(webPage);
        if (page && page->mainFrame()) {
            Frame* frame = page->mainFrame();

            frame->setTiledBackingStoreEnabled(enabled);

            if (frame->view())
                frame->view()->setDelegatesScrolling(enabled);

            if (frame->tiledBackingStore()) {
                frame->tiledBackingStore()->setTileSize(IntSize(512, 512));

                IntRect fixedVisibleContentRect(frame->view()->fixedVisibleContentRect());

                int width, height;
                webkit_web_page_get_viewport_size(webPage, &width, &height);
                fixedVisibleContentRect.setSize(IntSize(width, height));

                frame->view()->setFixedVisibleContentRect(fixedVisibleContentRect);
            }
        }
#endif
    } else if (name == g_intern_string("enable-debugging-borders"))
        settings->setShowDebugBorders(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-webgl"))
        settings->setWebGLEnabled(g_value_get_boolean(&value));
    else if (name == g_intern_string("enable-accelerated-2d-canvas"))
        settings->setAccelerated2dCanvasEnabled(g_value_get_boolean(&value));
    else if (!g_object_class_find_property(G_OBJECT_GET_CLASS(webSettings), name))
        g_warning("Unexpected setting '%s'", name);
    g_value_unset(&value);
}

static void webkit_web_page_dispose(GObject* object)
{
    WebKitWebPage* webPage = WEBKIT_WEB_PAGE(object);
    WebKitWebPagePrivate* priv = webPage->priv;

    priv->disposing = TRUE;

    priv->backForwardList.clear();

    if (priv->corePage) {
        Frame* frame = core(webPage)->mainFrame();

        if (FrameLoader* loader = frame->loader()) {
            loader->stopForUserCancel();
            loader->detachFromParent();
        }

        delete priv->corePage;
        priv->corePage = 0;
    }

    if (priv->webSettings) {
        g_signal_handlers_disconnect_by_func(priv->webSettings.get(), (gpointer)settingsNotifyCallback, webPage);
        priv->webSettings.clear();
    }

    priv->webWindowFeatures.clear();

    priv->mainResource.clear();
    priv->subResources.clear();

    priv->webInspector.clear();

    webkit_web_page_set_root_layer(webPage, NULL);

    G_OBJECT_CLASS(webkit_web_page_parent_class)->dispose(object);
}

static void webkit_web_page_finalize(GObject* object)
{
    WEBKIT_WEB_PAGE(object)->priv->~WebKitWebPagePrivate();
    G_OBJECT_CLASS(webkit_web_page_parent_class)->finalize(object);
}

static void webkit_web_page_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
{
    WebKitWebPage* webPage = WEBKIT_WEB_PAGE(object);

    switch (prop_id) {
    case PROP_TITLE:
        g_value_set_string(value, webkit_web_page_get_title(webPage));
        break;
    case PROP_URI:
        g_value_set_string(value, webkit_web_page_get_uri(webPage));
        break;
    case PROP_ZOOM_LEVEL:
        g_value_set_float(value, webkit_web_page_get_zoom_level(webPage));
        break;
    case PROP_SETTINGS:
        g_value_set_object(value, webkit_web_page_get_settings(webPage));
        break;
    case PROP_WEB_INSPECTOR:
        g_value_set_object(value, webkit_web_page_get_inspector(webPage));
        break;
    case PROP_LOAD_STATUS:
        g_value_set_enum(value, webkit_web_page_get_load_status(webPage));
        break;
    case PROP_PROGRESS:
        g_value_set_double(value, webkit_web_page_get_progress(webPage));
        break;
    case PROP_WINDOW_FEATURES:
        g_value_set_object(value, webkit_web_page_get_window_features(webPage));
        break;
    case PROP_WEB_VIEW:
        g_value_set_object(value, webkit_web_page_get_view(webPage));
        break;
    case PROP_ICON_URI:
        g_value_set_string(value, webkit_web_page_get_icon_uri(webPage));
        break;
    case PROP_USE_FIXED_LAYOUT:
        g_value_set_boolean(value, webkit_web_page_get_use_fixed_layout(webPage));
        break;
    case PROP_FIXED_LAYOUT_WIDTH:
        g_value_set_uint(value, webkit_web_page_get_fixed_layout_width(webPage));
        break;
    case PROP_FIXED_LAYOUT_HEIGHT:
        g_value_set_uint(value, webkit_web_page_get_fixed_layout_height(webPage));
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

static void webkit_web_page_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec *pspec)
{
    WebKitWebPage* webPage = WEBKIT_WEB_PAGE(object);

    switch (prop_id) {
    case PROP_ZOOM_LEVEL:
        webkit_web_page_set_zoom_level(webPage, g_value_get_float(value));
        break;
    case PROP_SETTINGS:
        webkit_web_page_set_settings(webPage, WEBKIT_WEB_SETTINGS(g_value_get_object(value)));
        break;
    case PROP_WINDOW_FEATURES:
        webkit_web_page_set_window_features(webPage, WEBKIT_WEB_WINDOW_FEATURES(g_value_get_object(value)));
        break;
    case PROP_WEB_VIEW:
        webPage->priv->view = WEBKIT_IWEB_VIEW(g_value_get_object(value));
        break;
    case PROP_USE_FIXED_LAYOUT:
        webkit_web_page_set_use_fixed_layout(webPage, g_value_get_boolean(value));
        break;
    case PROP_FIXED_LAYOUT_WIDTH:
        webkit_web_page_set_fixed_layout_width(webPage, g_value_get_uint(value));
        break;
    case PROP_FIXED_LAYOUT_HEIGHT:
        webkit_web_page_set_fixed_layout_height(webPage, g_value_get_uint(value));
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

static GObject *
webkit_web_page_constructor (GType                  gtype,
                             guint                  n_properties,
                             GObjectConstructParam *properties)
{
    GObject *obj = G_OBJECT_CLASS (webkit_web_page_parent_class)->constructor (gtype, n_properties, properties);
    WebKitWebPage* webPage = WEBKIT_WEB_PAGE (obj);
    WebKitWebPagePrivate* priv = webPage->priv;

    priv->disposing = FALSE;

    Page::PageClients pageClients;
    pageClients.chromeClient = new WebKit::ChromeClient(webPage);
#if ENABLE(CONTEXT_MENUS)
    pageClients.contextMenuClient = new WebKit::ContextMenuClient(webPage);
#endif
    pageClients.editorClient = new WebKit::EditorClient(webPage);
    pageClients.dragClient = WebKit::DragClientGObject::create(webPage);
    pageClients.inspectorClient = new WebKit::InspectorClient(webPage);

    priv->corePage = new Page(pageClients);

#if ENABLE(GEOLOCATION)
    if (DumpRenderTreeSupportClutter::dumpRenderTreeModeEnabled()) {
        GeolocationClientMock* mock = new GeolocationClientMock();
        WebCore::provideGeolocationTo(priv->corePage, mock);
        mock->setController(GeolocationController::from(priv->corePage));
    } else
        WebCore::provideGeolocationTo(priv->corePage, new WebKit::GeolocationClient(webPage));
#endif

    // The main frame.
    WebKitWebFrame* frame = WEBKIT_WEB_FRAME(g_object_new(WEBKIT_TYPE_WEB_FRAME, NULL));
    WebKitWebFramePrivate* framePriv = frame->priv;

    framePriv->webPage = webPage;
    WebKit::FrameLoaderClient* client = new WebKit::FrameLoaderClient(frame);
    framePriv->coreFrame = Frame::create(priv->corePage, 0, client).get();
    framePriv->coreFrame->init();
    framePriv->origin = NULL;

    priv->mainFrame = frame;

    // Resources.
    priv->subResources = adoptGRef(g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref));

    // Back/Forward list.
    priv->backForwardList = adoptGRef(webkit_web_back_forward_list_new_with_web_page(webPage));

    // Inspector.
    priv->webInspector = adoptGRef(WEBKIT_WEB_INSPECTOR(g_object_new(WEBKIT_TYPE_WEB_INSPECTOR, NULL)));
    webkit_web_inspector_set_inspector_client(priv->webInspector.get(), priv->corePage);

    // TODO: wire all settings.
    priv->webSettings = adoptGRef(WEBKIT_WEB_SETTINGS(g_object_new(WEBKIT_TYPE_WEB_SETTINGS, NULL)));

    WebKitWebSettings* webSettings = priv->webSettings.get();
    Settings* settings = priv->corePage->settings();
    gdouble DPI = webPageGetDPI(webPage);

    guint defaultFontSize, defaultMonospaceFontSize, minimumFontSize, minimumLogicalFontSize;

    g_object_get(webSettings,
                 "default-font-size", &defaultFontSize,
                 "default-monospace-font-size", &defaultMonospaceFontSize,
                 "minimum-font-size", &minimumFontSize,
                 "minimum-logical-font-size", &minimumLogicalFontSize,
                 NULL);

    settings->setDefaultFontSize(defaultFontSize / 72.0 * DPI);
    settings->setDefaultFixedFontSize(defaultMonospaceFontSize / 72.0 * DPI);
    settings->setMinimumFontSize(minimumFontSize / 72.0 * DPI);
    settings->setMinimumLogicalFontSize(minimumLogicalFontSize / 72.0 * DPI);

    settings->setAcceleratedCompositingForFixedPositionEnabled(true);

    updateSettings(webPage);
    g_signal_connect(priv->webSettings.get(), "notify", G_CALLBACK(settingsNotifyCallback), webPage);

    // Window features.
    priv->webWindowFeatures = adoptGRef(webkit_web_window_features_new());

#if USE(ACCELERATED_COMPOSITING)
    priv->rootLayer = NULL;
#endif

    return obj;
}

static gboolean objectCreationHandled(GSignalInvocationHint* ihint, GValue* returnAccu, const GValue* handlerReturn, gpointer dummy)
{
    gboolean continueEmission = TRUE;
    gpointer newObject = g_value_get_object(handlerReturn);
    g_value_set_object(returnAccu, newObject);

    if (newObject)
        continueEmission = FALSE;

    return continueEmission;
}

static void webkit_web_page_class_init(WebKitWebPageClass* webPageClass)
{
    GObjectClass* objectClass = G_OBJECT_CLASS(webPageClass);
    objectClass->dispose = webkit_web_page_dispose;
    objectClass->finalize = webkit_web_page_finalize;
    objectClass->constructor = webkit_web_page_constructor;
    objectClass->get_property = webkit_web_page_get_property;
    objectClass->set_property = webkit_web_page_set_property;

    /**
     * WebKitWebPage::create-window:
     * @web_page: the object on which the signal is emitted
     * @frame: the #WebKitWebFrame
     *
     * Emitted when the creation of a new window is requested.
     * If this signal is handled the signal handler should return the
     * newly created #WebKitWebPage.
     *
     * The new #WebKitWebPage should not be displayed to the user
     * until the #WebKitWebPage::web-page-ready signal is emitted.
     *
     * The signal handlers should not try to deal with the reference count for
     * the new #WebKitWebPage. The page to which the page is added will
     * handle that.
     *
     * Return value: (transfer full): a newly allocated #WebKitWebPage, or %NULL
     */
    webkit_web_page_signals[CREATE_WINDOW] = g_signal_new("create-window",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            webkit_signal_accumulator_object_handled,
            NULL,
            webkit_marshal_OBJECT__OBJECT,
            WEBKIT_TYPE_WEB_PAGE , 1,
            WEBKIT_TYPE_WEB_FRAME);

    /**
     * WebKitWebPage::web-page-ready:
     * @web_page: the object on which the signal is emitted
     *
     * Emitted after #WebKitWebPage::create-window when the new #WebKitWebPage
     * should be displayed to the user. When this signal is emitted
     * all the information about how the window should look, including
     * size, position, whether the location, status and scroll bars
     * should be displayed, is already set on the
     * #WebKitWebWindowFeatures object contained by the #WebKitWebPage.
     *
     * Notice that some of that information may change during the life
     * time of the window, so you may want to connect to the ::notify
     * signal of the #WebKitWebWindowFeatures object to handle those.
     *
     * Return value: %TRUE to stop handlers from being invoked for the event or
     * %FALSE to propagate the event furter
     */
    webkit_web_page_signals[WEB_PAGE_READY] = g_signal_new("web-page-ready",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__VOID,
            G_TYPE_BOOLEAN, 0);

    /**
     * WebKitWebPage::download-requested:
     * @web_page: the object on which the signal is emitted
     * @download: a #WebKitDownload object that lets you control the
     * download process
     *
     * A new Download is being requested. By default, if the signal is
     * not handled, the download is cancelled. If you handle the download
     * and call webkit_download_set_destination_uri(), it will be
     * started for you. If you need to set the destination asynchronously
     * you are responsible for starting or cancelling it yourself.
     *
     * If you intend to handle downloads yourself rather than using
     * the #WebKitDownload helper object you must handle this signal,
     * and return %FALSE.
     *
     * Also, keep in mind that the default policy for WebKitGTK+ is to
     * ignore files with a MIME type that it does not know how to
     * handle, which means this signal won't be emitted in the default
     * setup. One way to trigger downloads is to connect to
     * WebKitWebPage::mime-type-policy-decision-requested and call
     * webkit_web_policy_decision_download() on the
     * #WebKitWebPolicyDecision in the parameter list for the kind of
     * files you want your application to download (a common solution
     * is to download anything that WebKit can't handle, which you can
     * figure out by using webkit_web_page_can_show_mime_type()).
     *
     * Return value: TRUE if the download should be performed, %FALSE to
     * cancel it
     */
    webkit_web_page_signals[DOWNLOAD_REQUESTED] = g_signal_new("download-requested",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__OBJECT,
            G_TYPE_BOOLEAN, 1,
            G_TYPE_OBJECT);

    webkit_web_page_signals[INSPECTION_REQUESTED] = g_signal_new("inspection-requested",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            objectCreationHandled,
            NULL,
            webkit_marshal_OBJECT__OBJECT,
            WEBKIT_TYPE_WEB_PAGE, 1,
            WEBKIT_TYPE_WEB_INSPECTOR);

    /**
     * WebKitWebPage::web-load-error
     * @web_page: the object on which the signal is emitted
     * @web_frame: the #WebKitWebFrame
     * @uri: the URI that triggered the error
     * @web_error: the #GError that was triggered
     *
     * An error occurred while loading. By default, if the signal is not
     * handled, the @web_page will display a stock error page. You need to
     * handle the signal if you want to provide your own error page.
     *
     * Return value: %TRUE to stop other handlers from being invoked for the
     * event. %FALSE to propagate the event further.
     */
    webkit_web_page_signals[WEB_LOAD_ERROR] = g_signal_new("web-load-error",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST),
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__OBJECT_STRING_POINTER,
            G_TYPE_BOOLEAN, 3,
            WEBKIT_TYPE_WEB_FRAME,
            G_TYPE_STRING,
            G_TYPE_POINTER);

    /**
     * WebKitWebPage::web-load-started:
     * @web_page: the object on which the signal is emitted
     * @frame: the frame going to do the load
     *
     * When a #WebKitWebFrame begins to load this signal is emitted.
     */
    webkit_web_page_signals[WEB_LOAD_STARTED] = g_signal_new("web-load-started",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_TYPE_WEB_FRAME);

    /**
     * WebKitWebPage::web-load-finished:
     * @web_page: the #WebKitWebPage
     * @frame: the #WebKitWebFrame
     */
    webkit_web_page_signals[WEB_LOAD_FINISHED] = g_signal_new("web-load-finished",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_TYPE_WEB_FRAME);

    /**
     * WebKitWebPage::window-object-cleared:
     * @web_page: the object on which the signal is emitted
     * @frame: the #WebKitWebFrame to which @window_object belongs
     * @context: the #JSGlobalContextRef holding the global object and other
     * execution state; equivalent to the return value of
     * webkit_web_frame_get_global_context(@frame)
     * @window_object: the #JSObjectRef representing the frame's JavaScript
     * window object
     *
     * Emitted when the JavaScript window object in a #WebKitWebFrame has been
     * cleared in preparation for a new load. This is the preferred place to
     * set custom properties on the window object using the JavaScriptCore API.
     */
    webkit_web_page_signals[WINDOW_OBJECT_CLEARED] = g_signal_new("window-object-cleared",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            webkit_marshal_VOID__OBJECT_POINTER_POINTER,
            G_TYPE_NONE, 3,
            WEBKIT_TYPE_WEB_FRAME,
            G_TYPE_POINTER,
            G_TYPE_POINTER);

    /**
     * WebKitWebPage::onload-event:
     * @web_page: the object on which the signal is emitted
     * @frame: the frame
     *
     * When a #WebKitWebFrame receives an onload event this signal is emitted.
     */
    webkit_web_page_signals[ONLOAD_EVENT] = g_signal_new("onload-event",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_TYPE_WEB_FRAME);

    /**
     * WebKitWebPage::title-changed:
     * @web_page: the object on which the signal is emitted
     * @frame: the main frame
     * @title: the new title
     *
     * When a #WebKitWebFrame changes the document title this signal is emitted.
     *
     * Deprecated: 1.1.4: Use "notify::title" instead.
     */
    webkit_web_page_signals[TITLE_CHANGED] = g_signal_new("title-changed",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            webkit_marshal_VOID__OBJECT_STRING,
            G_TYPE_NONE, 2,
            WEBKIT_TYPE_WEB_FRAME,
            G_TYPE_STRING);

    /**
     * WebKitWebPage::hovering-over-link:
     * @web_page: the object on which the signal is emitted
     * @title: the link's title
     * @uri: the URI the link points to
     *
     * When the cursor is over a link, this signal is emitted.
     */
    webkit_web_page_signals[HOVERING_OVER_LINK] = g_signal_new("hovering-over-link",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            webkit_marshal_VOID__STRING_STRING,
            G_TYPE_NONE, 2,
            G_TYPE_STRING,
            G_TYPE_STRING);

    /*
     * WebKitWebPage::frame-created
     * @webPage: the object which received the signal
     * @webFrame: the #WebKitWebFrame which was just created.
     *
     * Emitted when a #WebKitWebPage has created a new frame. This signal will
     * be emitted for all sub-frames created during page load. It will not be
     * emitted for the main frame, which originates in the WebKitWebPage constructor
     * and may be accessed at any time using webkit_web_page_get_main_frame.
     */
    webkit_web_page_signals[FRAME_CREATED] = g_signal_new("frame-created",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL, NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_TYPE_WEB_FRAME);

    /**
     * WebKitWebPage::new-window-policy-decision-requested:
     * @web_page: the object on which the signal is emitted
     * @frame: the #WebKitWebFrame that required the navigation
     * @request: a #WebKitNetworkRequest
     * @navigation_action: a #WebKitWebNavigationAction
     * @policy_decision: a #WebKitWebPolicyDecision
     *
     * Emitted when @frame requests opening a new window. With this
     * signal the browser can use the context of the request to decide
     * about the new window. If the request is not handled the default
     * behavior is to allow opening the new window to load the URI,
     * which will cause a create-window signal emission where the
     * browser handles the new window action but without information
     * of the context that caused the navigation. The following
     * navigation-policy-decision-requested emissions will load the
     * page after the creation of the new window just with the
     * information of this new navigation context, without any
     * information about the action that made this new window to be
     * opened.
     *
     * Notice that if you return TRUE, meaning that you handled the
     * signal, you are expected to have decided what to do, by calling
     * webkit_web_policy_decision_ignore(),
     * webkit_web_policy_decision_use(), or
     * webkit_web_policy_decision_download() on the @policy_decision
     * object.
     *
     * Return value: %TRUE if a decision was made, %FALSE to have the
     * default behavior apply
     */
    webkit_web_page_signals[NEW_WINDOW_POLICY_DECISION_REQUESTED] =
        g_signal_new("new-window-policy-decision-requested",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__OBJECT_OBJECT_OBJECT_OBJECT,
            G_TYPE_BOOLEAN, 4,
            WEBKIT_TYPE_WEB_FRAME,
            WEBKIT_TYPE_NETWORK_REQUEST,
            WEBKIT_TYPE_WEB_NAVIGATION_ACTION,
            WEBKIT_TYPE_WEB_POLICY_DECISION);

    /**
     * WebKitWebPage::navigation-policy-decision-requested:
     * @web_page: the object on which the signal is emitted
     * @frame: the #WebKitWebFrame that required the navigation
     * @request: a #WebKitNetworkRequest
     * @navigation_action: a #WebKitWebNavigationAction
     * @policy_decision: a #WebKitWebPolicyDecision
     *
     * Emitted when @frame requests a navigation to another page.
     * If this signal is not handled, the default behavior is to allow the
     * navigation.
     *
     * Notice that if you return TRUE, meaning that you handled the
     * signal, you are expected to have decided what to do, by calling
     * webkit_web_policy_decision_ignore(),
     * webkit_web_policy_decision_use(), or
     * webkit_web_policy_decision_download() on the @policy_decision
     * object.
     *
     * Return value: %TRUE if a decision was made, %FALSE to have the
     * default behavior apply
     */
    webkit_web_page_signals[NAVIGATION_POLICY_DECISION_REQUESTED] = g_signal_new("navigation-policy-decision-requested",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__OBJECT_OBJECT_OBJECT_OBJECT,
            G_TYPE_BOOLEAN, 4,
            WEBKIT_TYPE_WEB_FRAME,
            WEBKIT_TYPE_NETWORK_REQUEST,
            WEBKIT_TYPE_WEB_NAVIGATION_ACTION,
            WEBKIT_TYPE_WEB_POLICY_DECISION);

    /**
     * WebKitWebPage::console-message:
     * @webPage: the object on which the signal is emitted
     * @message: the message text
     * @line: the line where the error occured
     * @source_id: the source id
     *
     * A JavaScript console message was created.
     *
     * Return value: %TRUE to stop other handlers from being invoked for the
     * event. %FALSE to propagate the event further.
     */
    webkit_web_page_signals[CONSOLE_MESSAGE] = g_signal_new("console-message",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__STRING_INT_STRING,
            G_TYPE_BOOLEAN, 3,
            G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);

    /**
     * WebKitWebPage::script-alert:
     * @webPage: the object on which the signal is emitted
     * @frame: the relevant frame
     * @message: the message text
     *
     * A JavaScript alert dialog was created.
     *
     * Return value: %TRUE to stop other handlers from being invoked for the
     * event. %FALSE to propagate the event further.
     */
    webkit_web_page_signals[SCRIPT_ALERT] = g_signal_new("script-alert",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__OBJECT_STRING,
            G_TYPE_BOOLEAN, 2,
            WEBKIT_TYPE_WEB_FRAME, G_TYPE_STRING);

    /**
     * WebKitWebPage::script-confirm:
     * @webPage: the object on which the signal is emitted
     * @frame: the relevant frame
     * @message: the message text
     * @confirmed: whether the dialog has been confirmed
     *
     * A JavaScript confirm dialog was created, providing Yes and No buttons.
     *
     * Return value: %TRUE to stop other handlers from being invoked for the
     * event. %FALSE to propagate the event further.
     */
    webkit_web_page_signals[SCRIPT_CONFIRM] = g_signal_new("script-confirm",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__OBJECT_STRING_POINTER,
            G_TYPE_BOOLEAN, 3,
            WEBKIT_TYPE_WEB_FRAME, G_TYPE_STRING, G_TYPE_POINTER);

    /**
     * WebKitWebPage::script-prompt:
     * @webPage: the object on which the signal is emitted
     * @frame: the relevant frame
     * @message: the message text
     * @default: the default value
     * @text: To be filled with the return value or NULL if the dialog was cancelled.
     *
     * A JavaScript prompt dialog was created, providing an entry to input text.
     *
     * Return value: %TRUE to stop other handlers from being invoked for the
     * event. %FALSE to propagate the event further.
     */
    webkit_web_page_signals[SCRIPT_PROMPT] = g_signal_new("script-prompt",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__OBJECT_STRING_STRING_STRING,
            G_TYPE_BOOLEAN, 4,
            WEBKIT_TYPE_WEB_FRAME, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);

    /**
     * WebKitWebPage::mime-type-policy-decision-requested:
     * @web_page: the object on which the signal is emitted
     * @frame: the #WebKitWebFrame that required the policy decision
     * @request: a WebKitNetworkRequest
     * @mimetype: the MIME type attempted to load
     * @policy_decision: a #WebKitWebPolicyDecision
     *
     * Decide whether or not to display the given MIME type.  If this
     * signal is not handled, the default behavior is to show the
     * content of the requested URI if WebKit can show this MIME
     * type and the content disposition is not a download; if WebKit
     * is not able to show the MIME type nothing happens.
     *
     * Notice that if you return TRUE, meaning that you handled the
     * signal, you are expected to be aware of the "Content-Disposition"
     * header. A value of "attachment" usually indicates a download
     * regardless of the MIME type, see also
     * soup_message_headers_get_content_disposition(). And you must call
     * webkit_web_policy_decision_ignore(),
     * webkit_web_policy_decision_use(), or
     * webkit_web_policy_decision_download() on the @policy_decision
     * object.
     *
     * Return value: %TRUE if a decision was made, %FALSE to have the
     * default behavior apply
     */
    webkit_web_page_signals[MIME_TYPE_POLICY_DECISION_REQUESTED] = g_signal_new("mime-type-policy-decision-requested",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__OBJECT_OBJECT_STRING_OBJECT,
            G_TYPE_BOOLEAN, 4,
            WEBKIT_TYPE_WEB_FRAME,
            WEBKIT_TYPE_NETWORK_REQUEST,
            G_TYPE_STRING,
            WEBKIT_TYPE_WEB_POLICY_DECISION);

    /**
     * WebKitWebPage::database-quota-exceeded
     * @web_page: the object which received the signal
     * @frame: the relevant frame
     * @database: the #WebKitWebDatabase which exceeded the quota of its #WebKitSecurityOrigin
     *
     * The #WebKitWebPage::database-quota-exceeded signal will be emitted when
     * a Web Database exceeds the quota of its security origin. This signal
     * may be used to increase the size of the quota before the originating
     * operation fails.
     */
    webkit_web_page_signals[DATABASE_QUOTA_EXCEEDED] = g_signal_new("database-quota-exceeded",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL, NULL,
            webkit_marshal_VOID__OBJECT_OBJECT,
            G_TYPE_NONE, 2,
            G_TYPE_OBJECT, G_TYPE_OBJECT);

    /**
     * WebKitWebPage::resource-request-starting:
     * @web_page: the object which received the signal
     * @web_frame: the #WebKitWebFrame whose load dispatched this request
     * @web_resource: an empty #WebKitWebResource object
     * @request: the #WebKitNetworkRequest that will be dispatched
     * @response: the #WebKitNetworkResponse representing the redirect
     * response, if any
     *
     * Emitted when a request is about to be sent. You can modify the
     * request while handling this signal. You can set the URI in the
     * #WebKitNetworkRequest object itself, and add/remove/replace
     * headers using the #SoupMessage object it carries, if it is
     * present. See webkit_network_request_get_message(). Setting the
     * request URI to "about:blank" will effectively cause the request
     * to load nothing, and can be used to disable the loading of
     * specific resources.
     *
     * Notice that information about an eventual redirect is available
     * in @response's #SoupMessage, not in the #SoupMessage carried by
     * the @request. If @response is %NULL, then this is not a
     * redirected request.
     *
     * The #WebKitWebResource object will be the same throughout all
     * the lifetime of the resource, but the contents may change from
     * inbetween signal emissions.
     */
    webkit_web_page_signals[RESOURCE_REQUEST_STARTING] = g_signal_new("resource-request-starting",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL, NULL,
            webkit_marshal_VOID__OBJECT_OBJECT_OBJECT_OBJECT,
            G_TYPE_NONE, 4,
            WEBKIT_TYPE_WEB_FRAME,
            WEBKIT_TYPE_WEB_RESOURCE,
            WEBKIT_TYPE_NETWORK_REQUEST,
            WEBKIT_TYPE_NETWORK_RESPONSE);

    /**
     * WebKitWebPage::geolocation-policy-decision-requested:
     * @webPage: the object on which the signal is emitted
     * @frame: the frame that requests permission
     * @policy_decision: a WebKitGeolocationPolicyDecision
     *
     * This signal is emitted when a @frame wants to obtain the user's
     * location. The decision can be made asynchronously, but you must
     * call g_object_ref() on the @policy_decision, and return %TRUE if
     * you are going to handle the request. To actually make the
     * decision you need to call webkit_geolocation_policy_allow() or
     * webkit_geolocation_policy_deny() on @policy_decision.
     */
    webkit_web_page_signals[GEOLOCATION_POLICY_DECISION_REQUESTED] = g_signal_new("geolocation-policy-decision-requested",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST),
            0,
            NULL, NULL,
            webkit_marshal_BOOLEAN__OBJECT_OBJECT,
            G_TYPE_BOOLEAN, 2,
            WEBKIT_TYPE_WEB_FRAME,
            WEBKIT_TYPE_GEOLOCATION_POLICY_DECISION);

    /**
     * WebKitWebPage::geolocation-policy-decision-cancelled:
     * @webPage: the object on which the signal is emitted
     * @frame: the frame that cancels geolocation request.
     *
     * When a @frame wants to cancel geolocation permission it had requested
     * before.
     */
    webkit_web_page_signals[GEOLOCATION_POLICY_DECISION_CANCELLED] = g_signal_new("geolocation-policy-decision-cancelled",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST),
            0,
            NULL, NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_TYPE_WEB_FRAME);

    /**
     * WebKitWebPage::document-load-finished
     * @web_page: the object which received the signal
     * @web_frame: the #WebKitWebFrame whose load dispatched this request
     *
     * Emitted when the DOM document object load is finished for the
     * given frame.
     */
    webkit_web_page_signals[DOCUMENT_LOAD_FINISHED] = g_signal_new("document-load-finished",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            0,
            NULL, NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_TYPE_WEB_FRAME);

    webkit_web_page_signals[STATUS_BAR_TEXT_CHANGED] = g_signal_new("status-bar-text-changed",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__STRING,
            G_TYPE_NONE, 1,
            G_TYPE_STRING);

    /**
     * WebKitWebPage::icon-loaded:
     * @web_page: the object on which the signal is emitted
     * @icon_uri: the URI for the icon
     *
     * This signal is emitted when the main frame has got a favicon.
     */
    webkit_web_page_signals[ICON_LOADED] = g_signal_new("icon-loaded",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__STRING,
            G_TYPE_NONE, 1,
            G_TYPE_STRING);

    /**
     * WebKitWebPage::input-method-state-set:
     * @web_page: the object on which the signal is emitted
     * @element: the focused WebKitDOMElement
     * @active: %TRUE if editing
     *
     * This signal is emitted when an editable element has been focused.
     */
    webkit_web_page_signals[INPUT_METHOD_STATE_SET] = g_signal_new("input-method-state-set",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            webkit_marshal_VOID__OBJECT_BOOLEAN,
            G_TYPE_NONE, 2,
            WEBKIT_TYPE_DOM_NODE,
            G_TYPE_BOOLEAN);

    /**
     * WebKitWebPage::should-interrupt-script:
     * @web_page: the object on which the signal is emitted
     *
     * Notification that JavaScript code may be stuck or is using up too much
     * time. Return %TRUE if execution should be interrupted, %FALSE
     * otherwise.
     *
     * Return value: %TRUE if execution should be interrupted, %FALSE otherwise
     */
    webkit_web_page_signals[SHOULD_INTERRUPT_SCRIPT] = g_signal_new("should-interrupt-script",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            g_signal_accumulator_true_handled,
            NULL,
            webkit_marshal_BOOLEAN__VOID,
            G_TYPE_BOOLEAN, 0);

    /**
     * WebKitWebPage::microfocus-changed:
     * @web_page: the object on which the signal is emitted
     * @element: the focused WebKitDOMElement, or NULL
     *
     * This signal is emitted when the focus in the web page changes to another
     * element.
     */
    webkit_web_page_signals[MICROFOCUS_CHANGED] = g_signal_new("microfocus-changed",
            G_TYPE_FROM_CLASS(webPageClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            WEBKIT_TYPE_DOM_NODE);

    /**
     * WebKitWebPage:title:
     *
     * Returns: the @web_page's document title.
     */
    g_object_class_install_property(objectClass, PROP_TITLE,
                                    g_param_spec_string("title",
                                                        _("Title"),
                                                        _("Returns the @web_page's document title"),
                                                        NULL,
                                                        WEBKIT_PARAM_READABLE));

    /**
     * WebKitWebPage:uri:
     *
     * Returns: the current URI of the contents displayed by the @web_page.
     */
    g_object_class_install_property(objectClass, PROP_URI,
                                    g_param_spec_string("uri",
                                                        _("URI"),
                                                        _("Returns the current URI of the contents displayed by the @web_page"),
                                                        NULL,
                                                        WEBKIT_PARAM_READABLE));

    /**
     * WebKitWebPage:icon-uri:
     *
     * The URI for the favicon for the #WebKitWebPage.
     */
    g_object_class_install_property(objectClass, PROP_ICON_URI,
                                    g_param_spec_string("icon-uri",
                                                        _("Icon URI"),
                                                        _("The URI for the favicon for the #WebKitWebPage."),
                                                        NULL,
                                                        WEBKIT_PARAM_READABLE));
    /**
     * WebKitWebPage:zoom-level:
     *
     * The level of zoom of the content.
     */
    g_object_class_install_property(objectClass, PROP_ZOOM_LEVEL,
                                    g_param_spec_float("zoom-level",
                                                       _("Zoom level"),
                                                       _("The level of zoom of the content"),
                                                       G_MINFLOAT,
                                                       G_MAXFLOAT,
                                                       1.0f,
                                                       WEBKIT_PARAM_READWRITE));

    /**
     * WebKitWebPage:web-inspector:
     *
     * The associated WebKitWebInspector instance.
     */
    g_object_class_install_property(objectClass, PROP_WEB_INSPECTOR,
                                    g_param_spec_object("web-inspector",
                                                        _("Web Inspector"),
                                                        _("The associated WebKitWebInspector instance"),
                                                        WEBKIT_TYPE_WEB_INSPECTOR,
                                                        WEBKIT_PARAM_READABLE));

    /**
     * WebKitWebPage:web-view:
     *
     * The associated WebKitIWebView instance.
     */
    g_object_class_install_property(objectClass, PROP_WEB_VIEW,
                                    g_param_spec_object("web-view",
                                                        _("Web View"),
                                                        _("The associated WebKitIWebView instance"),
                                                        WEBKIT_TYPE_IWEB_VIEW,
                                                        (GParamFlags)(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));

    /**
     * WebKitWebPage:load-status:
     *
     * Determines the current status of the load.
     *
     * Connect to "notify::load-status" to monitor loading.
     */
    g_object_class_install_property(objectClass, PROP_LOAD_STATUS,
                                    g_param_spec_enum("load-status",
                                                      "Load Status",
                                                      "Determines the current status of the load",
                                                      WEBKIT_TYPE_LOAD_STATUS,
                                                      WEBKIT_LOAD_FINISHED,
                                                      WEBKIT_PARAM_READABLE));

    /**
     * WebKitWebPage:progress:
     *
     * Determines the current progress of the load.
     */
    g_object_class_install_property(objectClass, PROP_PROGRESS,
                                    g_param_spec_double("progress",
                                                        "Progress",
                                                        "Determines the current progress of the load",
                                                        0.0, 1.0, 1.0,
                                                        WEBKIT_PARAM_READABLE));

    /**
     * WebKitWebPage:window-features:
     *
     * An associated WebKitWebWindowFeatures instance.
     */
    g_object_class_install_property(objectClass, PROP_WINDOW_FEATURES,
                                    g_param_spec_object("window-features",
                                                        "Window Features",
                                                        "An associated WebKitWebWindowFeatures instance",
                                                        WEBKIT_TYPE_WEB_WINDOW_FEATURES,
                                                        WEBKIT_PARAM_READWRITE));

    /**
     * WebKitWebPage:use-fixed-layout:
     *
     * An associated WebKitWebWindowFeatures instance.
     */
    g_object_class_install_property(objectClass, PROP_USE_FIXED_LAYOUT,
                                    g_param_spec_boolean("use-fixed-layout",
                                                        "Use Fixed Layout",
                                                        "Whether the fixed layout mode should be used.",
                                                        FALSE,
                                                        WEBKIT_PARAM_READWRITE));

    /**
     * WebKitWebPage:fixed-layout-width:
     *
     * An associated WebKitWebWindowFeatures instance.
     */
    g_object_class_install_property(objectClass, PROP_FIXED_LAYOUT_WIDTH,
                                    g_param_spec_uint("fixed-layout-width",
                                                      "Fixed Layout Width",
                                                      "The width for the fixed layout",
                                                      0,
                                                      G_MAXUINT,
                                                      800,
                                                      WEBKIT_PARAM_READWRITE));

    /**
     * WebKitWebPage:fixed-layout-height:
     *
     * An associated WebKitWebWindowFeatures instance.
     */
    g_object_class_install_property(objectClass, PROP_FIXED_LAYOUT_HEIGHT,
                                    g_param_spec_uint("fixed-layout-height",
                                                      "Fixed Layout Height",
                                                      "The height for the fixed layout",
                                                      0,
                                                      G_MAXUINT,
                                                      600,
                                                      WEBKIT_PARAM_READWRITE));

    g_object_class_install_property(objectClass, PROP_SETTINGS,
                                    g_param_spec_object("settings",
                                                        _("Settings"),
                                                        _("An associated WebKitWebSettings instance"),
                                                        WEBKIT_TYPE_WEB_SETTINGS,
                                                        WEBKIT_PARAM_READWRITE));

    g_type_class_add_private(webPageClass, sizeof(WebKitWebPagePrivate));
}

static void webkit_web_page_init(WebKitWebPage* webPage)
{
    WebKitWebPagePrivate* priv = WEBKIT_WEB_PAGE_GET_PRIVATE(webPage);
    webPage->priv = priv;
    new (priv) WebKitWebPagePrivate();

    priv->fixedLayoutWidth = 800;
    priv->fixedLayoutHeight = 600;
}

WebKitWebPage* webkit_web_page_new(WebKitIWebView* view)
{
    return WEBKIT_WEB_PAGE(g_object_new(WEBKIT_TYPE_WEB_PAGE, "web-view", view, 0));
}

WebKitIWebView* webkit_web_page_get_view(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), 0);
    return web_page->priv->view;
}

void webkit_web_page_get_viewport_size(WebKitWebPage* webPage,
                                       int* width, int* height)
{
    guint setWidth, setHeight;
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    WebKitWebFrame* frame = webPage->priv->mainFrame;
    if (frame && core(frame)->view()) {
        IntRect rect = core(frame)->view()->frameRect();
        setWidth = rect.width();
        setHeight = rect.height();
    } else {
        setWidth = webPage->priv->viewportWidth;
        setHeight = webPage->priv->viewportHeight;
    }

    if (width)
        *width = setWidth;
    if (height)
        *height = setHeight;
}

void webkit_web_page_set_viewport_size(WebKitWebPage* webPage,
                                       const int width,
                                       const int height)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    webPage->priv->viewportWidth = width;
    webPage->priv->viewportHeight = height;

    Frame* frame = core(webPage->priv->mainFrame);
    if (!frame->view())
        return;

    IntSize current = frame->view()->frameRect().size();
    if (current.width() == width && current.height() == height)
        return;

    frame->view()->resize(width, height);

#if USE(TILED_BACKING_STORE)
    if (frame->tiledBackingStore()) {
        IntRect fixedVisibleContentRect(frame->view()->fixedVisibleContentRect());
        fixedVisibleContentRect.setSize(IntSize(width, height));
        frame->view()->setFixedVisibleContentRect(fixedVisibleContentRect);
    }
#endif
}

WebKitWebFrame*
webkit_web_page_get_main_frame(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), 0);
    return web_page->priv->mainFrame;
}

/**
 * webkit_web_page_reload:
 * @web_page: a #WebKitWebPage
 *
 * Reloads the @web_page.
 */
void webkit_web_page_reload(WebKitWebPage* webPage)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    core(webPage)->mainFrame()->loader()->reload();
}

/**
 * webkit_web_page_reload_bypass_cache:
 * @web_page: a #WebKitWebPage
 *
 * Reloads the @web_page without using any cached data.
 */
void webkit_web_page_reload_bypass_cache(WebKitWebPage* webPage)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    core(webPage)->mainFrame()->loader()->reload(true);
}

/**
 * webkit_web_page_load_uri:
 * @web_page: a #WebKitWebPage
 * @uri: an URI string
 *
 * Requests loading of the specified URI string.
 */
void webkit_web_page_load_uri(WebKitWebPage* web_page, const gchar* uri)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(web_page));
    g_return_if_fail(uri);

    WebKitWebFrame* frame = web_page->priv->mainFrame;
    webkit_web_frame_load_uri(frame, uri);
}

/**
 * webkit_web_page_load_string:
 * @webPage: a #WebKitWebPage
 * @content: an URI string
 * @mimeType: the MIME type, or %NULL
 * @encoding: the encoding, or %NULL
 * @baseUri: the base URI for relative locations
 *
 * Requests loading of the given @content with the specified @mimeType,
 * @encoding and @baseUri.
 *
 * If @mimeType is %NULL, "text/html" is assumed.
 *
 * If @encoding is %NULL, "UTF-8" is assumed.
 */
void webkit_web_page_load_string(WebKitWebPage* webPage, const gchar* content, const gchar* mimeType, const gchar* encoding, const gchar* baseUri)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));
    g_return_if_fail(content);

    WebKitWebFrame* frame = webPage->priv->mainFrame;
    webkit_web_frame_load_string(frame, content, mimeType, encoding, baseUri);
}

/**
 * webkit_web_page_load_request:
 * @web_page: a #WebKitWebPage
 * @request: a #WebKitNetworkRequest
 *
 * Requests loading of the specified asynchronous client request.
 *
 * Creates a provisional data source that will transition to a committed data
 * source once any data has been received. Use webkit_web_page_stop_loading() to
 * stop the load.
 */
void webkit_web_page_load_request(WebKitWebPage* webPage, WebKitNetworkRequest* request)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));
    g_return_if_fail(WEBKIT_IS_NETWORK_REQUEST(request));

    WebKitWebFrame* frame = webPage->priv->mainFrame;
    webkit_web_frame_load_request(frame, request);
}

/**
 * webkit_web_page_search_text:
 * @web_page: a #WebKitWebPage
 * @text: a string to look for
 * @forward: whether to find forward or not
 * @case_sensitive: whether to respect the case of text
 * @wrap: whether to continue looking at the beginning after reaching the end
 *
 * Looks for a specified string inside #web_page.
 *
 * Return value: %TRUE on success or %FALSE on failure
 */
gboolean webkit_web_page_search_text(WebKitWebPage* webPage, const gchar* string, gboolean caseSensitive, gboolean forward, gboolean shouldWrap)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), FALSE);
    g_return_val_if_fail(string, FALSE);

    TextCaseSensitivity caseSensitivity = caseSensitive ? TextCaseSensitive : TextCaseInsensitive;
    FindDirection direction = forward ? FindDirectionForward : FindDirectionBackward;

    return core(webPage)->findString(String::fromUTF8(string), caseSensitivity, direction, shouldWrap);
}

/**
 * webkit_web_page_mark_text_matches:
 * @web_page: a #WebKitWebPage
 * @string: a string to look for
 * @case_sensitive: whether to respect the case of text
 * @limit: the maximum number of strings to look for or 0 for all
 *
 * Attempts to highlight all occurances of #string inside #web_page.
 *
 * Return value: the number of strings highlighted
 */
guint webkit_web_page_mark_text_matches(WebKitWebPage* webPage, const gchar* string, gboolean caseSensitive, guint limit)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
    g_return_val_if_fail(string, 0);

    TextCaseSensitivity caseSensitivity = caseSensitive ? TextCaseSensitive : TextCaseInsensitive;

    return core(webPage)->markAllMatchesForText(String::fromUTF8(string), caseSensitivity, false, limit);
}

/**
 * webkit_web_page_set_highlight_text_matches:
 * @web_page: a #WebKitWebPage
 * @highlight: whether to highlight text matches
 *
 * Highlights text matches previously marked by webkit_web_page_mark_text_matches.
 */
void webkit_web_page_set_highlight_text_matches(WebKitWebPage* webPage, gboolean shouldHighlight)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    Frame *frame = core(webPage)->mainFrame();
    do {
        frame->editor()->setMarkedTextMatchesAreHighlighted(shouldHighlight);
        frame = frame->tree()->traverseNextWithWrap(false);
    } while (frame);
}

/**
 * webkit_web_page_unmark_text_matches:
 * @web_page: a #WebKitWebPage
 *
 * Removes highlighting previously set by webkit_web_page_mark_text_matches.
 */
void webkit_web_page_unmark_text_matches(WebKitWebPage* webPage)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    return core(webPage)->unmarkAllTextMatches();
}

/**
 * webkit_web_page_set_settings:
 * @web_page: a #WebKitWebPage
 * @settings: (transfer none): the #WebKitWebSettings to be set
 *
 * Replaces the #WebKitWebSettings instance that is currently attached
 * to @web_page with @settings. The reference held by the @web_page on
 * the old #WebKitWebSettings instance is dropped, and the reference
 * count of @settings is increased.
 *
 * The settings are automatically applied to @web_page.
 */
void webkit_web_page_set_settings(WebKitWebPage* web_page, WebKitWebSettings* webSettings)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(web_page));
    g_return_if_fail(WEBKIT_IS_WEB_SETTINGS(webSettings));

    WebKitWebPagePrivate* priv = web_page->priv;
    // FIXME: updateSettings and settingsNotifyCallback could both use a better name.
    g_signal_handlers_disconnect_by_func(priv->webSettings.get(), (gpointer)settingsNotifyCallback, web_page);
    priv->webSettings = webSettings;
    updateSettings(web_page);
    g_signal_connect(webSettings, "notify", G_CALLBACK(settingsNotifyCallback), web_page);
    g_object_notify(G_OBJECT(web_page), "settings");
}

/**
 * webkit_web_page_get_settings:
 * @web_page: a #WebKitWebPage
 *
 * Obtains the #WebKitWebSettings associated with the
 * #WebKitWebPage. The #WebKitWebPage always has an associated
 * instance of #WebKitWebSettings. The reference that is returned by
 * this call is owned by the #WebKitWebPage. You may need to increase
 * its reference count if you intend to keep it alive for longer than
 * the #WebKitWebPage.
 *
 * Return value: (transfer none): the #WebKitWebSettings instance
 */
WebKitWebSettings* webkit_web_page_get_settings(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), 0);
    return web_page->priv->webSettings.get();
}

/**
 * webkit_web_page_get_inspector:
 * @web_page: a #WebKitWebPage
 *
 * Obtains the #WebKitWebInspector associated with the
 * #WebKitWebPage. Every #WebKitWebPage object has a
 * #WebKitWebInspector object attached to it as soon as it is created,
 * so this function will only return NULL if the argument is not a
 * valid #WebKitWebPage.
 *
 * Return value: (transfer none): the #WebKitWebInspector instance.
 */
WebKitWebInspector* webkit_web_page_get_inspector(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), 0);
    return web_page->priv->webInspector.get();
}

/**
 * webkit_web_page_get_load_status:
 * @web_page: a #WebKitWebPage
 *
 * Determines the current status of the load.
 *
 * Return value: the #WebKitLoadStatus.
 */
WebKitLoadStatus webkit_web_page_get_load_status(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), WEBKIT_LOAD_FINISHED);

    WebKitWebPagePrivate* priv = web_page->priv;
    return priv->loadStatus;
}

/**
 * webkit_web_page_get_progress:
 * @web_page: a #WebKitWebPage
 *
 * Determines the current progress of the load.
 *
 * FIXME: Update below comment.
 * Return value: %TRUE or %FALSE.
 */
gdouble webkit_web_page_get_progress(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), 1.0);

    return core(web_page)->progress()->estimatedProgress();
}

static void webkit_web_page_set_window_features(WebKitWebPage* webPage,
                                                WebKitWebWindowFeatures* webWindowFeatures)
{
    if (!webWindowFeatures)
      return;
    if (webkit_web_window_features_equal(webPage->priv->webWindowFeatures.get(), webWindowFeatures))
      return;
    webPage->priv->webWindowFeatures = webWindowFeatures;
}

/**
 * webkit_web_page_get_window_features:
 * @webPage: a #WebKitWebPage
 *
 * Return the instance of #WebKitWebWindowFeatures held by the given
 * #WebKitWebPage.
 *
 * Return value: (transfer none): the #WebKitWebWindowFeatures
 */
WebKitWebWindowFeatures* webkit_web_page_get_window_features(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
    return webPage->priv->webWindowFeatures.get();
}

/**
 * webkit_web_page_go_back:
 * @web_page: a #WebKitWebPage
 *
 * Loads the previous history item.
 */
void webkit_web_page_go_back(WebKitWebPage* web_page)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(web_page));

    core(web_page)->goBack();
}

/**
 * webkit_web_page_go_back_or_forward:
 * @web_page: a #WebKitWebPage
 * @steps: the number of steps
 *
 * Loads the history item that is the number of @steps away from the current
 * item. Negative values represent steps backward while positive values
 * represent steps forward.
 */
void webkit_web_page_go_back_or_forward(WebKitWebPage* web_page, gint steps)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(web_page));

    core(web_page)->goBackOrForward(steps);
}

/**
 * webkit_web_page_go_forward:
 * @web_page: a #WebKitWebPage
 *
 * Loads the next history item.
 */
void webkit_web_page_go_forward(WebKitWebPage* web_page)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(web_page));

    core(web_page)->goForward();
}

/**
 * webkit_web_page_can_go_back:
 * @web_page: a #WebKitWebPage
 *
 * Determines whether #web_page has a previous history item.
 *
 * Return value: %TRUE if able to move back, %FALSE otherwise
 */
gboolean webkit_web_page_can_go_back(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), FALSE);

    if (!core(web_page) || !core(web_page)->backForwardList()->backItem())
        return FALSE;

    return TRUE;
}

/**
 * webkit_web_page_can_go_back_or_forward:
 * @web_page: a #WebKitWebPage
 * @steps: the number of steps
 *
 * Determines whether #web_page has a history item of @steps. Negative values
 * represent steps backward while positive values represent steps forward.
 *
 * Return value: %TRUE if able to move back or forward the given number of
 * steps, %FALSE otherwise
 */
gboolean webkit_web_page_can_go_back_or_forward(WebKitWebPage* web_page, gint steps)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), FALSE);

    return core(web_page)->canGoBackOrForward(steps);
}

/**
 * webkit_web_page_can_go_forward:
 * @web_page: a #WebKitWebPage
 *
 * Determines whether #web_page has a next history item.
 *
 * Return value: %TRUE if able to move forward, %FALSE otherwise
 */
gboolean webkit_web_page_can_go_forward(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), FALSE);

    Page* page = core(web_page);

    if (!page)
        return FALSE;

    if (!page->backForwardList()->forwardItem())
        return FALSE;

    return TRUE;
}

/**
 * webkit_web_page_set_maintains_back_forward_list:
 * @web_page: a #WebKitWebPage
 * @flag: to tell the page to maintain a back or forward list
 *
 * Set the page to maintain a back or forward list of history items.
 */
void webkit_web_page_set_maintains_back_forward_list(WebKitWebPage* web_page, gboolean flag)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(web_page));

    static_cast<BackForwardListImpl*>(core(web_page)->backForwardList())->setEnabled(flag);
}

/**
 * webkit_web_page_get_back_forward_list:
 * @web_page: a #WebKitWebPage
 *
 * Obtains the #WebKitWebBackForwardList associated with the given #WebKitWebPage. The
 * #WebKitWebBackForwardList is owned by the #WebKitWebPage.
 *
 * Return value: (transfer none): the #WebKitWebBackForwardList
 */
WebKitWebBackForwardList* webkit_web_page_get_back_forward_list(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), 0);
    if (!core(web_page) || !static_cast<BackForwardListImpl*>(core(web_page)->backForwardList())->enabled())
        return 0;
    return web_page->priv->backForwardList.get();
}

/**
 * webkit_web_page_go_to_back_forward_item:
 * @web_page: a #WebKitWebPage
 * @item: a #WebKitWebHistoryItem*
 *
 * Go to the specified #WebKitWebHistoryItem
 *
 * Return value: %TRUE if loading of item is successful, %FALSE if not
 */
gboolean webkit_web_page_go_to_back_forward_item(WebKitWebPage* web_page, WebKitWebHistoryItem* item)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), FALSE);
    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(item), FALSE);

    WebKitWebBackForwardList* backForwardList = webkit_web_page_get_back_forward_list(web_page);
    if (!webkit_web_back_forward_list_contains_item(backForwardList, item))
        return FALSE;

    core(web_page)->goToItem(core(item), FrameLoadTypeIndexedBackForward);
    return TRUE;
}

/**
 * webkit_web_page_get_title:
 * @web_page: a #WebKitWebPage
 *
 * Return the @web_page's document title
 *
 * Return value: the title of @web_page
 */
const gchar* webkit_web_page_get_title(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), NULL);

    WebKitWebPagePrivate* priv = webPage->priv;
    return priv->mainFrame->priv->title;
}

/**
 * webkit_web_page_get_uri:
 * @web_page: a #WebKitWebPage
 *
 * Return the current URI of the contents displayed by the @web_page
 *
 * Return value: the URI of @web_page
 */
const gchar* webkit_web_page_get_uri(WebKitWebPage* web_page)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(web_page), NULL);

    WebKitWebPagePrivate* priv = web_page->priv;
    return priv->mainFrame->priv->uri;
}

/**
 * webkit_web_page_stop_loading:
 * @web_page: a #WebKitWebPage
 *
 * Stops any ongoing load in the @web_page.
 **/
void webkit_web_page_stop_loading(WebKitWebPage* web_page)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(web_page));

    Frame* frame = core(web_page)->mainFrame();

    if (FrameLoader* loader = frame->loader())
        loader->stopForUserCancel();
}

/**
 * webkit_web_page_get_icon_uri:
 * @web_page: the #WebKitWebPage object
 *
 * Obtains the URI for the favicon for the given #WebKitWebPage, or
 * %NULL if there is none.
 *
 * Return value: the URI for the favicon, or %NULL
 */
const gchar* webkit_web_page_get_icon_uri(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
    String iconURL = iconDatabase().synchronousIconURLForPageURL(core(webPage)->mainFrame()->document()->url().string());
    webPage->priv->iconURI = iconURL.utf8();
    return webPage->priv->iconURI.data();
}

/**
 * webkit_web_page_get_dom_document:
 * @webPage: a #WebKitWebPage
 *
 * Returns: (transfer none): the #WebKitDOMDocument currently loaded in the @webPage
 **/
WebKitDOMDocument*
webkit_web_page_get_dom_document(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);

    Frame* coreFrame = core(webPage)->mainFrame();
    if (!coreFrame)
        return 0;

    Document* doc = coreFrame->document();
    if (!doc)
        return 0;

    return static_cast<WebKitDOMDocument*>(kit(doc));
}

/* From EventHandler.cpp */
static IntPoint documentPointForWindowPoint(WebKitWebPage* webPage, const IntPoint& windowPoint)
{
    Frame* frame = core(webPage)->focusController()->focusedOrMainFrame();
    FrameView* view = frame->view();
    ClutterActor* actor = CLUTTER_ACTOR(webPage->priv->view);
    IntPoint point;

    UtilClutter::applyInverseChildTransform(actor,
                                            windowPoint.x(),
                                            windowPoint.y(),
                                            point);

    // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
    // Historically the code would just crash; this is clearly no worse than that.
    return view ? view->windowToContents(point) : point;
}

/**
 * webkit_web_page_hit_test:
 * @webPage: a #WebKitWebPage
 * @x: the X coordinate of the test, relative to the viewport
 * @y: the Y coordinate of the test, relative to the viewport
 *
 * Does a 'hit test' in the coordinates specified by @x and @y to figure
 * out context information about that position in the @webPage.
 *
 * Returns: (transfer none): a newly created #WebKitHitTestResult with the context of the
 * specified position.
 **/
WebKitHitTestResult* webkit_web_page_hit_test(WebKitWebPage* webPage, int x, int y)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), NULL);

    Frame* frame = core(webPage)->focusController()->focusedOrMainFrame();

    HitTestRequest request(HitTestRequest::Active);
    HitTestResult result(documentPointForWindowPoint(webPage, IntPoint(x, y)));
    frame->document()->renderView()->layer()->hitTest(request, result);

    return kit(result);
}

/**
 * webkit_web_page_hit_test_rectangle:
 * @webPage: a #WebKitWebPage
 * @x: the X coordinate of the test, relative to the viewport
 * @y: the Y coordinate of the test, relative to the viewport
 * @topPadding: the number of pixels to consider above the hit test point
 * @bottomPadding: the number of pixels to consider bellow the hit test point
 * @leftPadding: the number of pixels to consider to the left the hit test point
 * @rightPadding: the number of pixels to consider to the right the hit test point
 *
 * Does a 'hit test' in the coordinates specified by @x and @y and the
 * surrounding area provided by the padding arguments to figure out
 * context information about that position in the @webPage.
 *
 * Returns: a newly created #GList with #WebKitHitTestResult objects
 * as the elements with the context of the specified position.
 **/
WebKitHitTestResult* webkit_web_page_hit_test_rectangle(WebKitWebPage* webPage, int x, int y, guint topPadding, guint bottomPadding, guint leftPadding, guint rightPadding)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), NULL);

    Frame* frame = core(webPage)->focusController()->focusedOrMainFrame();

    HitTestRequest request(HitTestRequest::Active);
    HitTestResult result(documentPointForWindowPoint(webPage, IntPoint(x, y)), topPadding, rightPadding, bottomPadding, leftPadding);
    frame->document()->renderView()->layer()->hitTest(request, result);

    return kit(result);
}

/**
 * webkit_web_page_get_zoom_level:
 * @web_page: a #WebKitWebPage
 *
 * Return the zoom level of @web_page, i.e. the factor by which elements in
 * the page are scaled with respect to their original size.
 *
 * Return value: the zoom level of @web_page
 */
gfloat webkit_web_page_get_zoom_level(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 1.0f);

    return webkit_web_frame_get_zoom_level(webPage->priv->mainFrame);
}

/**
 * webkit_web_page_set_zoom_level:
 * @web_page: a #WebKitWebPage
 * @zoom_level: the new zoom level
 *
 * Sets the zoom level of @web_page, i.e. the factor by which elements in
 * the page are scaled with respect to their original size.
 */
void webkit_web_page_set_zoom_level(WebKitWebPage* webPage, gfloat zoomLevel)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    webkit_web_frame_set_zoom_level(webPage->priv->mainFrame, zoomLevel);

    g_object_notify(G_OBJECT(webPage), "zoom-level");
}

/**
 * webkit_web_page_zoom_in:
 * @web_page: a #WebKitWebPage
 *
 * Increases the zoom level of @web_page. The current zoom
 * level is incremented by the value of the "zoom-step"
 * property of the #WebKitWebSettings associated with @web_page.
 */
void webkit_web_page_zoom_in(WebKitWebPage* webPage)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    WebKitWebPagePrivate* priv = webPage->priv;
    gfloat zoomMultiplierRatio;
    g_object_get(priv->webSettings.get(), "zoom-step", &zoomMultiplierRatio, NULL);

    webkit_web_page_set_zoom_level(webPage, webkit_web_page_get_zoom_level(webPage) + zoomMultiplierRatio);
}

/**
 * webkit_web_page_zoom_out:
 * @web_page: a #WebKitWebPage
 *
 * Decreases the zoom level of @web_page. The current zoom
 * level is decremented by the value of the "zoom-step"
 * property of the #WebKitWebSettings associated with @web_page.
 */
void webkit_web_page_zoom_out(WebKitWebPage* webPage)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    WebKitWebPagePrivate* priv = webPage->priv;
    gfloat zoomMultiplierRatio;
    g_object_get(priv->webSettings.get(), "zoom-step", &zoomMultiplierRatio, NULL);

    webkit_web_page_set_zoom_level(webPage, webkit_web_page_get_zoom_level(webPage) - zoomMultiplierRatio);
}

/**
 * webkit_web_page_get_focused_frame:
 * @webPage: a #WebKitWebPage
 *
 * Return the frame that has focus or an active text selection.
 *
 * Return value: (transfer none): The focused #WebKitWebFrame or %NULL if no frame is focused
 */
WebKitWebFrame* webkit_web_page_get_focused_frame(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), NULL);

    Frame* focusedFrame = core(webPage)->focusController()->focusedFrame();
    return kit(focusedFrame);
}

/**
 * webkit_web_page_execute_script:
 * @webPage: the #WebKitWebPage
 * @script: a string containing the script to be executed
 *
 * This is a convenience to run very simple scripts in the context of
 * the main frame of a given page. If your needs are more complex you
 * should use the JavaScripCore API or the DOM bindings instead.
 */
void webkit_web_page_execute_script(WebKitWebPage* webPage, const gchar* script)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));
    g_return_if_fail(script);

    core(webPage)->mainFrame()->script()->executeScript(String::fromUTF8(script), true);
}

/**
 * webkit_web_page_get_editable:
 * @web_page: a #WebKitWebPage
 *
 * Return whether the user is allowed to edit the document.
 *
 * Returns %TRUE if @web_page allows the user to edit the HTML document, %FALSE if
 * it doesn't. You can change @web_page's document programmatically regardless of
 * this setting using the DOM bindings or JavaScriptCore.
 *
 * Return value: a #gboolean indicating the editable state
 */
gboolean webkit_web_page_get_editable(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), FALSE);

    WebKitWebPagePrivate* priv = webPage->priv;

    return priv->editable;
}

/**
 * webkit_web_page_set_editable:
 * @web_page: a #WebKitWebPage
 * @flag: a #gboolean indicating the editable state
 *
 * Sets whether @web_page allows the user to edit its HTML document.
 *
 * If @flag is %TRUE, @web_page allows the user to edit the document. If @flag is
 * %FALSE, an element in @web_page's document can only be edited if the
 * CONTENTEDITABLE attribute has been set on the element or one of its parent
 * elements. You can change @web_page's document programmatically regardless of
 * this setting. By default a #WebKitWebPage is not editable.

 * Normally, an HTML document is not editable unless the elements within the
 * document are editable. This function provides a low-level way to make the
 * contents of a #WebKitWebPage editable without altering the document or DOM
 * structure.
 */
void webkit_web_page_set_editable(WebKitWebPage* webPage, gboolean flag)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    WebKitWebPagePrivate* priv = webPage->priv;

    Frame* frame = core(webPage)->mainFrame();
    g_return_if_fail(frame);

    // TODO: What happens when the frame is replaced?
    flag = flag != FALSE;
    if (flag == priv->editable)
        return;

    priv->editable = flag;

    if (flag)
        frame->editor()->applyEditingStyleToBodyElement();

    g_object_notify(G_OBJECT(webPage), "editable");
}

/**
 * webkit_web_page_can_show_mime_type:
 * @web_page: a #WebKitWebPage
 * @mime_type: a MIME type
 *
 * This functions returns whether or not a MIME type can be displayed using this page.
 *
 * Return value: a #gboolean indicating if the MIME type can be displayed
 **/

gboolean webkit_web_page_can_show_mime_type(WebKitWebPage* webPage, const gchar* mimeType)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), FALSE);

    Frame* frame = core(webkit_web_page_get_main_frame(webPage));
    return frame->loader()->client()->canShowMIMEType(String::fromUTF8(mimeType));
}

/**
 * webkit_web_page_get_encoding:
 * @webPage: a #WebKitWebPage
 *
 * Return the default encoding of the #WebKitWebPage.
 *
 * Return value: the default encoding
 */
const gchar* webkit_web_page_get_encoding(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), NULL);
    String encoding = core(webPage)->mainFrame()->document()->encoding();
    if (encoding.isEmpty())
        return 0;
    webPage->priv->encoding = encoding.utf8();
    return webPage->priv->encoding.data();
}

/**
 * webkit_web_page_set_custom_encoding:
 * @webPage: a #WebKitWebPage
 * @encoding: the new encoding, or %NULL to restore the default encoding
 *
 * Sets the current #WebKitWebPage encoding, without modifying the default one,
 * and reloads the page.
 */
void webkit_web_page_set_custom_encoding(WebKitWebPage* webPage, const char* encoding)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    core(webPage)->mainFrame()->loader()->reloadWithOverrideEncoding(String::fromUTF8(encoding));
}

/**
 * webkit_web_page_get_custom_encoding:
 * @webPage: a #WebKitWebPage
 *
 * Returns the current encoding of the #WebKitWebPage, not the default-encoding
 * of WebKitWebSettings.
 *
 * Return value: a string containing the current custom encoding for @webPage, or %NULL if there's none set.
 */
const char* webkit_web_page_get_custom_encoding(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), NULL);
    String overrideEncoding = core(webPage)->mainFrame()->loader()->documentLoader()->overrideEncoding();
    if (overrideEncoding.isEmpty())
        return 0;
    webPage->priv->customEncoding = overrideEncoding.utf8();
    return webPage->priv->customEncoding.data();
}

/**
 * webkit_web_page_set_page_mode:
 * @web_page: the #WebKitWebPage that will have its page mode set
 * @mode: the %WebKitWebPagePageMode to be set
 *
 * Sets the page-mode property of the #WebKitWebPage. Check the
 * property's documentation for more information.
 */
void webkit_web_page_set_page_mode(WebKitWebPage* webPage, WebKitWebPagePageMode mode)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    Page* page = core(webPage);

    switch (mode) {
    case WEBKIT_WEB_PAGE_PAGE_MODE_FLOATING:
        page->setViewMode(Page::ViewModeFloating);
        break;
    case WEBKIT_WEB_PAGE_PAGE_MODE_FULLSCREEN:
        page->setViewMode(Page::ViewModeFullscreen);
        break;
    case WEBKIT_WEB_PAGE_PAGE_MODE_MAXIMIZED:
        page->setViewMode(Page::ViewModeMaximized);
        break;
    case WEBKIT_WEB_PAGE_PAGE_MODE_MINIMIZED:
        page->setViewMode(Page::ViewModeMinimized);
        break;
    default:
        page->setViewMode(Page::ViewModeWindowed);
        break;
    }
}

/**
 * webkit_web_page_get_page_mode:
 * @web_page: the #WebKitWebPage to obtain the page mode from
 *
 * Gets the value of the page-mode property of the
 * #WebKitWebPage. Check the property's documentation for more
 * information.
 *
 * Return value: the %WebKitWebPagePageMode currently set for the
 * #WebKitWebPage.
 */
WebKitWebPagePageMode webkit_web_page_get_page_mode(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), WEBKIT_WEB_PAGE_PAGE_MODE_WINDOWED);

    Page* page = core(webPage);
    Page::ViewMode mode = page->viewMode();

    if (mode == Page::ViewModeFloating)
        return WEBKIT_WEB_PAGE_PAGE_MODE_FLOATING;

    if (mode == Page::ViewModeFullscreen)
        return WEBKIT_WEB_PAGE_PAGE_MODE_FULLSCREEN;

    if (mode == Page::ViewModeMaximized)
        return WEBKIT_WEB_PAGE_PAGE_MODE_MAXIMIZED;

    if (mode == Page::ViewModeMinimized)
        return WEBKIT_WEB_PAGE_PAGE_MODE_MINIMIZED;

    return WEBKIT_WEB_PAGE_PAGE_MODE_WINDOWED;
}

gboolean webkit_web_page_get_use_fixed_layout(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), FALSE);
    return webPage->priv->useFixedLayout;
}

void webkit_web_page_set_use_fixed_layout(WebKitWebPage* webPage, gboolean used)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));
    webPage->priv->useFixedLayout = used;

    Frame* frame = core(webPage)->mainFrame();
    if (!frame || !frame->view())
        return;

    frame->view()->setUseFixedLayout(static_cast<bool>(used));
}

guint webkit_web_page_get_fixed_layout_width(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);

    return webPage->priv->fixedLayoutWidth;
}

void webkit_web_page_set_fixed_layout_width(WebKitWebPage* webPage, guint width)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));
    webPage->priv->fixedLayoutWidth = width;

    Frame* frame = core(webPage)->mainFrame();
    if (!frame || !frame->view())
        return;

    frame->view()->setFixedLayoutSize(IntSize(static_cast<int>(width), static_cast<int>(webPage->priv->fixedLayoutHeight)));
}

guint webkit_web_page_get_fixed_layout_height(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);

    return webPage->priv->fixedLayoutHeight;
}

void webkit_web_page_set_fixed_layout_height(WebKitWebPage* webPage, guint height)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));
    webPage->priv->fixedLayoutHeight = height;

    Frame* frame = core(webPage)->mainFrame();
    if (!frame || !frame->view())
        return;

    frame->view()->setFixedLayoutSize(IntSize(static_cast<int>(webPage->priv->fixedLayoutWidth), static_cast<int>(height)));
}

void webkit_web_page_request_exit_fullscreen_media(WebKitWebPage* page)
{
    g_message("%s: needs reimplementation!", __func__);
}

/**
 * webkit_web_page_load_image:
 * @webPage: a #WebKitWebPage
 * @url: url of the image to load
 *
 * Starts loading the image at @url, if it hadn't been loaded already.
 */
void webkit_web_page_load_image(WebKitWebPage* webPage, const gchar *url)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    Frame* frame = core(webPage)->mainFrame();
    CachedResourceLoader *loader = frame->document()->cachedResourceLoader();

    CachedImage* resource = static_cast<CachedImage*>(loader->cachedResource(url));
    if (resource && resource->stillNeedsLoad()) {
        resource->setLoading(true);
        resource->load(loader, ResourceLoaderOptions(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, DoNotAskClientForCrossOriginCredentials, DoSecurityCheck));
    }
}

#if USE(ACCELERATED_COMPOSITING)
void webkit_web_page_set_root_layer(WebKitWebPage* webPage, GraphicsLayerClutter* graphicsLayer)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    if (webPage->priv->rootLayer)
        clutter_actor_remove_child(CLUTTER_ACTOR(webPage->priv->view), webPage->priv->rootLayer->platformLayer());

    webPage->priv->rootLayer = graphicsLayer;

    if (!graphicsLayer)
        return;

    clutter_actor_set_parent(CLUTTER_ACTOR(graphicsLayer->platformLayer()), CLUTTER_ACTOR(webPage->priv->view));

    // Tell the root layer of any scroll offset we already have, only if
    // scrolling has not been delegated to the webview
    Frame* mainFrame = core(webPage)->mainFrame();
    if (mainFrame && mainFrame->view() && !mainFrame->view()->delegatesScrolling()) {
        int x, y;
        webkit_web_frame_get_scroll_offset(webPage->priv->mainFrame, &x, &y);
        webkit_web_page_set_scroll_layers(webPage, x, y);
    }
}

GraphicsLayerClutter* webkit_web_page_get_root_layer(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), NULL);

    return webPage->priv->rootLayer;
}

ClutterActor* webkit_web_page_get_root_layer_actor(WebKitWebPage* webPage)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), NULL);

    if (!webPage->priv->rootLayer)
        return 0;

    return webPage->priv->rootLayer->platformLayer();
}

void webkit_web_page_set_scroll_layers(WebKitWebPage* webPage, gint x, gint y)
{
    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));

    if (webPage->priv->rootLayer)
        webPage->priv->rootLayer->setScrollPosition(FloatPoint(x, y));
}

#endif

static void get_absolute_offset_for_element(WebKitDOMElement* element, int *x, int *y)
{
    WebKitDOMElement* offset_parent;
    glong offset_top, offset_left;

    g_object_get(element,
                 "offset-left", &offset_left,
                 "offset-top", &offset_top,
                 "offset-parent", &offset_parent,
                 NULL);

    *x = offset_left;
    *y = offset_top;

    if (!offset_parent)
        return;

    /* If there's a parent, we keep going. */
    int parent_x, parent_y;
    get_absolute_offset_for_element(offset_parent, &parent_x, &parent_y);

    *x += parent_x;
    *y += parent_y;
}

/**
 * webkit_web_page_get_zoom_target
 * @webPage: a #WebKitWebPage
 * @x: the horizontal coordinate of the event
 * @y: the vertical coordinate of the event
 * @zoomCenterX: (out): return location for the X coordinate of the zoomed area center
 * @zoomCenterY: (out): return location for the Y coordinate of the zoomed area center
 * @zoomFactor: (out): return location for the final zoom factor
 * @callback: (allow-none): the #ChooseElementToZoomCallback, or %NULL
 * @userData: (allow-none): user data to pass to @callback, or %NULL
 *
 * Compute the zoom center and factor for a contextual zoom requested at the
 * point defined by the @x and @y arguments.
 *
 * The @callback (if any) will be called with a list of the candidate nodes to
 * zoom at. If the callback returns a node, it will be chosen to be zoomed at,
 * otherwise the first node will be chosen.
 *
 * Return value: %FALSE if no suitable node was found
 */
bool webkit_web_page_get_zoom_target(WebKitWebPage* webPage, float x, float y,
                                     int *zoomCenterX, int *zoomCenterY,
                                     double *zoomFactor,
                                     ChooseElementToZoomCallback callback,
                                     void* userData)
{
    WebKitHitTestResult* result;
    WebKitDOMNode* node;
    WebKitDOMElement* element;
    int viewportWidth, viewportHeight;
    long elementWidth;
    GSList* candidates = NULL;

    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), false);

    result = webkit_web_page_hit_test(webPage, x, y);
    g_object_get(result, "inner-node", &node, NULL);

    /* Use the immediate element that seems to make sense. */
    element = webkit_dom_node_get_parent_element(node);
    if (!element)
        element = WEBKIT_DOM_ELEMENT(node);

    while (true) {
        WebKitDOMElement* tmp;
        char* tagName = webkit_dom_element_get_tag_name(element);

        tmp = webkit_dom_node_get_parent_element(WEBKIT_DOM_NODE(element));

        if (g_strcmp0("P", tagName) == 0 ||
            g_strcmp0("DIV", tagName) == 0 ||
            g_strcmp0("TD", tagName) == 0 ||
            g_strcmp0("IMG", tagName) == 0 ||
            g_strcmp0("H1", tagName) == 0 ||
            g_strcmp0("H2", tagName) == 0 ||
            g_strcmp0("H3", tagName) == 0 ||
            g_strcmp0("H4", tagName) == 0 ||
            g_strcmp0("H5", tagName) == 0 ||
            g_strcmp0("H6", tagName) == 0) {

            candidates = g_slist_append(candidates, element);
        }

        /* We'll use what we have */
        if (!tmp)
            break;

        element = tmp;
    }

    g_object_unref(result);

    if (candidates == NULL)
        return false;

    if (callback != NULL)
        element = callback(candidates, userData);
    else
        element = NULL;

    if (element == NULL)
        element = WEBKIT_DOM_ELEMENT(candidates->data);

    g_slist_free(candidates);

    /* Then the scroll to focus the element */
    get_absolute_offset_for_element(element, zoomCenterX, zoomCenterY);

    /* Do not scroll too close to the node, for better readability */
    int margin = MIN(*zoomCenterX, zoomAreaMargin);

    *zoomCenterX = *zoomCenterX - margin;
    *zoomCenterY = MAX(*zoomCenterY - margin, 0);

    webkit_web_page_get_viewport_size(webPage, &viewportWidth, &viewportHeight);
    elementWidth = webkit_dom_element_get_offset_width(element);
    elementWidth += margin * 2;
    *zoomFactor = viewportWidth / (double) elementWidth;

    return true;
}

namespace WebKit {
WebCore::Page* core(WebKitWebPage* webPage)
{
    if (!webPage)
        return 0;

    WebKitWebPagePrivate* priv = webPage->priv;
    return priv ? priv->corePage : 0;
}

WebKitWebPage* kit(WebCore::Page* corePage)
{
    if (!corePage)
        return 0;

    ASSERT(corePage->chrome());
    WebKit::ChromeClient* client = static_cast<WebKit::ChromeClient*>(corePage->chrome()->client());
    return client ? client->webPage() : 0;
}

}
