/*
 * Copyright (C) 2007, 2008 Holger Hans Peter Freyther
 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
 * Copyright (C) 2007 Apple Inc.
 * Copyright (C) 2008 Christian Dywan <christian@imendio.com>
 * Copyright (C) 2008 Collabora Ltd.
 * Copyright (C) 2008 Nuanti Ltd.
 * Copyright (C) 2009 Jan Alonzo <jmalonzo@gmail.com>
 * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
 * Copyright (C) 2010 Collabora Ltd. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "webkitgwebframe.h"

#include "AXObjectCache.h"
#include "AnimationController.h"
#include "DocumentLoader.h"
#include "DocumentLoaderGObject.h"
#include "FrameLoader.h"
#include "FrameLoaderClientGObject.h"
#include "FrameTree.h"
#include "FrameView.h"
#include "GCController.h"
#include "GraphicsContext.h"
#include "HTMLFrameOwnerElement.h"
#include "InspectorController.h"
#include "JSDOMBinding.h"
#include "JSDOMWindow.h"
#include "JSElement.h"
#include "JSLock.h"
#include "PrintContext.h"
#include "RenderListItem.h"
#include "RenderTreeAsText.h"
#include "RenderView.h"
#include "ScriptController.h"
#include "SubstituteData.h"
#include "SVGDocumentExtensions.h"
#include "webkitenumtypes.h"
#include "webkitglobals.h"
#include "webkitglobalsprivate.h"
#include "webkitgwebframeprivate.h"
#include "webkitiwebview.h"
#include "webkitmarshal.h"
#include "webkitnetworkrequestprivate.h"
#include "webkitnetworkresponseprivate.h"
#include "webkitsecurityoriginprivate.h"
#include "webkitwebpageprivate.h"
#include <JavaScriptCore/APICast.h>
#include <cairo/cairo-pdf.h>
#include <glib/gi18n-lib.h>
#include <wtf/text/CString.h>

#if ENABLE(SVG)
#include "SVGSMILElement.h"
#endif

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

/**
 * SECTION:webkitwebframe
 * @short_description: The content of a #WebKitWebView
 *
 * A #WebKitWebView contains a main #WebKitWebFrame. A #WebKitWebFrame
 * contains the content of one URI. The URI and name of the frame can
 * be retrieved, the load status and progress can be observed using the
 * signals and can be controlled using the methods of the #WebKitWebFrame.
 * A #WebKitWebFrame can have any number of children and one child can
 * be found by using #webkit_web_frame_find_frame.
 *
 * <informalexample><programlisting>
 * /<!-- -->* Get the frame from the #WebKitWebView *<!-- -->/
 * WebKitWebFrame *frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW(my_view));
 * g_print("The URI of this frame is '%s'", webkit_web_frame_get_uri (frame));
 * </programlisting></informalexample>
 */

using namespace WebKit;
using namespace WebCore;
using namespace std;

enum {
    CLEARED,
    LOAD_COMMITTED,
    LOAD_DONE,
    TITLE_CHANGED,
    HOVERING_OVER_LINK,
    CONTENTS_SIZE_CHANGED,
    SCROLL_OFFSET_CHANGED,
    LAST_SIGNAL
};

enum {
    PROP_0,

    PROP_NAME,
    PROP_TITLE,
    PROP_URI,
    PROP_LOAD_STATUS,
    PROP_ZOOM_LEVEL,
    PROP_TEXT_ZOOM_LEVEL,
    PROP_CONTENTS_WIDTH,
    PROP_CONTENTS_HEIGHT,
    PROP_X_SCROLL_OFFSET,
    PROP_Y_SCROLL_OFFSET,
};

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

G_DEFINE_TYPE(WebKitWebFrame, webkit_web_frame, G_TYPE_OBJECT)

static void webkit_web_frame_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
{
    WebKitWebFrame* frame = WEBKIT_WEB_FRAME(object);

    switch(prop_id) {
    case PROP_NAME:
        g_value_set_string(value, webkit_web_frame_get_name(frame));
        break;
    case PROP_TITLE:
        g_value_set_string(value, webkit_web_frame_get_title(frame));
        break;
    case PROP_URI:
        g_value_set_string(value, webkit_web_frame_get_uri(frame));
        break;
    case PROP_LOAD_STATUS:
        g_value_set_enum(value, webkit_web_frame_get_load_status(frame));
        break;
    case PROP_ZOOM_LEVEL:
        g_value_set_float(value, webkit_web_frame_get_zoom_level(frame));
        break;
    case PROP_TEXT_ZOOM_LEVEL:
        g_value_set_float(value, webkit_web_frame_get_text_zoom_level(frame));
        break;
    case PROP_CONTENTS_WIDTH:
        g_value_set_int(value, webkit_web_frame_get_contents_width(frame));
        break;
    case PROP_CONTENTS_HEIGHT:
        g_value_set_int(value, webkit_web_frame_get_contents_height(frame));
        break;
    case PROP_X_SCROLL_OFFSET:
        g_value_set_int(value, webkit_web_frame_get_x_scroll_offset(frame));
        break;
    case PROP_Y_SCROLL_OFFSET:
        g_value_set_int(value, webkit_web_frame_get_y_scroll_offset(frame));
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    }
}

static void webkit_web_frame_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec *pspec)
{
    WebKitWebFrame* frame = WEBKIT_WEB_FRAME(object);

    switch (prop_id) {
    case PROP_ZOOM_LEVEL:
        webkit_web_frame_set_zoom_level(frame, g_value_get_float(value));
        break;
    case PROP_TEXT_ZOOM_LEVEL:
        webkit_web_frame_set_text_zoom_level(frame, g_value_get_float(value));
        break;
    case PROP_X_SCROLL_OFFSET:
        webkit_web_frame_set_x_scroll_offset(frame, g_value_get_int(value));
        break;
    case PROP_Y_SCROLL_OFFSET:
        webkit_web_frame_set_y_scroll_offset(frame, g_value_get_int(value));
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

// Called from the FrameLoaderClient when it is destroyed. Normally
// the unref in the FrameLoaderClient is destroying this object as
// well but due reference counting a user might have added a reference...
void webkit_web_frame_core_frame_gone(WebKitWebFrame* frame)
{
    ASSERT(WEBKIT_IS_WEB_FRAME(frame));
    frame->priv->coreFrame = 0;
}

static WebKitWebDataSource* webkit_web_frame_get_data_source_from_core_loader(WebCore::DocumentLoader* loader)
{
    return loader ? static_cast<WebKit::DocumentLoader*>(loader)->dataSource() : NULL;
}

static void webkit_web_frame_finalize(GObject* object)
{
    WebKitWebFrame* frame = WEBKIT_WEB_FRAME(object);
    WebKitWebFramePrivate* priv = frame->priv;

    if (priv->coreFrame) {
        priv->coreFrame->loader()->cancelAndClear();
        priv->coreFrame = 0;
    }

    g_free(priv->name);
    g_free(priv->title);
    g_free(priv->uri);

    G_OBJECT_CLASS(webkit_web_frame_parent_class)->finalize(object);
}

static void webkit_web_frame_class_init(WebKitWebFrameClass* frameClass)
{
    webkitInit();

    /*
     * signals
     */
    webkit_web_frame_signals[CLEARED] = g_signal_new("cleared",
            G_TYPE_FROM_CLASS(frameClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__VOID,
            G_TYPE_NONE, 0);

    /**
     * WebKitWebFrame::load-done
     * @web_frame: the object on which the signal is emitted
     *
     * Emitted when frame loading is done.
     *
     * Deprecated: Use the "load-status" property instead.
     */
    webkit_web_frame_signals[LOAD_COMMITTED] = g_signal_new("load-committed",
            G_TYPE_FROM_CLASS(frameClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__VOID,
            G_TYPE_NONE, 0);

    /**
     * WebKitWebFrame::load-done
     * @web_frame: the object on which the signal is emitted
     *
     * Emitted when frame loading is done.
     *
     * Deprecated: Use the "load-status" property instead, and/or
     * WebKitWebView::load-error to be notified of load errors
     */
    webkit_web_frame_signals[LOAD_DONE] = g_signal_new("load-done",
            G_TYPE_FROM_CLASS(frameClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__BOOLEAN,
            G_TYPE_NONE, 1,
            G_TYPE_BOOLEAN);

    /**
     * WebKitWebFrame::title-changed:
     * @frame: the object on which the signal is emitted
     * @title: the new title
     *
     * When a #WebKitWebFrame changes the document title this signal is emitted.
     *
     * Deprecated: 1.1.18: Use "notify::title" instead.
     */
    webkit_web_frame_signals[TITLE_CHANGED] = g_signal_new("title-changed",
            G_TYPE_FROM_CLASS(frameClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            webkit_marshal_VOID__STRING,
            G_TYPE_NONE, 1,
            G_TYPE_STRING);

    webkit_web_frame_signals[HOVERING_OVER_LINK] = g_signal_new("hovering-over-link",
            G_TYPE_FROM_CLASS(frameClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            webkit_marshal_VOID__STRING_STRING,
            G_TYPE_NONE, 2,
            G_TYPE_STRING, G_TYPE_STRING);

    /**
     * WebKitWebFrame::contents-size-changed:
     * @frame: the object on which the signal is emitted
     * @width: The new width
     * @height: The new height
     *
     * FIXME: Should we notify on the width and height propertied instead?
     */
    webkit_web_frame_signals[CONTENTS_SIZE_CHANGED] = g_signal_new("contents-size-changed",
            G_TYPE_FROM_CLASS(frameClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            webkit_marshal_VOID__UINT_UINT,
            G_TYPE_NONE, 2,
            G_TYPE_UINT, G_TYPE_UINT);

    /**
     * WebKitWebFrame::scroll-offset-changed:
     * @frame: the object on which the signal is emitted
     *
     * FIXME: This should be a notification on the two scroll properties, but
     * rigth now the engine only signals changes without saying in which
     * direction.
     */
    webkit_web_frame_signals[SCROLL_OFFSET_CHANGED] = g_signal_new("scroll-offset-changed",
            G_TYPE_FROM_CLASS(frameClass),
            (GSignalFlags)G_SIGNAL_RUN_LAST,
            0,
            NULL,
            NULL,
            g_cclosure_marshal_VOID__VOID,
            G_TYPE_NONE, 0);

    /*
     * implementations of virtual methods
     */
    GObjectClass* objectClass = G_OBJECT_CLASS(frameClass);
    objectClass->finalize = webkit_web_frame_finalize;
    objectClass->get_property = webkit_web_frame_get_property;
    objectClass->set_property = webkit_web_frame_set_property;

    /*
     * properties
     */
    g_object_class_install_property(objectClass, PROP_NAME,
                                    g_param_spec_string("name",
                                                        _("Name"),
                                                        _("The name of the frame"),
                                                        NULL,
                                                        WEBKIT_PARAM_READABLE));

    g_object_class_install_property(objectClass, PROP_TITLE,
                                    g_param_spec_string("title",
                                                        _("Title"),
                                                        _("The document title of the frame"),
                                                        NULL,
                                                        WEBKIT_PARAM_READABLE));

    g_object_class_install_property(objectClass, PROP_URI,
                                    g_param_spec_string("uri",
                                                        _("URI"),
                                                        _("The current URI of the contents displayed by the frame"),
                                                        NULL,
                                                        WEBKIT_PARAM_READABLE));

    /**
    * WebKitWebFrame:load-status:
    *
    * Determines the current status of the load.
    *
    * Since: 1.1.7
    */
    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));

    /**
     * WebKitWebFrame: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));

    /**
     * WebKitWebFrame:text-zoom-level:
     *
     * The level of zoom of the text.
     */
    g_object_class_install_property(objectClass, PROP_ZOOM_LEVEL,
                                    g_param_spec_float("text-zoom-level",
                                                       _("Text Zoom level"),
                                                       _("The level of zoom of the text"),
                                                       G_MINFLOAT,
                                                       G_MAXFLOAT,
                                                       1.0f,
                                                       WEBKIT_PARAM_READWRITE));

    /**
     * WebKitWebFrame:contents-width:
     *
     * The width of the frame's contents.
     */
    g_object_class_install_property(objectClass, PROP_CONTENTS_WIDTH,
                                    g_param_spec_int("contents-width",
                                                     _("Contents Width"),
                                                     _("The width of the frame's contents"),
                                                     G_MININT,
                                                     G_MAXINT,
                                                     0,
                                                     WEBKIT_PARAM_READABLE));

    /**
     * WebKitWebFrame:contents-height:
     *
     * The height of the frame's contents.
     */
    g_object_class_install_property(objectClass, PROP_CONTENTS_WIDTH,
                                    g_param_spec_int("contents-height",
                                                     _("Contents Height"),
                                                     _("The height of the frame's contents"),
                                                     G_MININT,
                                                     G_MAXINT,
                                                     0,
                                                     WEBKIT_PARAM_READABLE));

    /**
     * WebKitWebFrame:x-scroll-offset:
     *
     * The scroll offset of the x axis.
     */
    g_object_class_install_property(objectClass, PROP_X_SCROLL_OFFSET,
                                    g_param_spec_int("x-scroll-offset",
                                                     _("X Scroll Offset"),
                                                     _("The scroll offset of the x axis"),
                                                     G_MININT,
                                                     G_MAXINT,
                                                     0,
                                                     WEBKIT_PARAM_READWRITE));

    /**
     * WebKitWebFrame:y-scroll-offset:
     *
     * The scroll offset of the y axis.
     */
    g_object_class_install_property(objectClass, PROP_Y_SCROLL_OFFSET,
                                    g_param_spec_int("y-scroll-offset",
                                                     _("Y Scroll Offset"),
                                                     _("The scroll offset of the y axis"),
                                                     G_MININT,
                                                     G_MAXINT,
                                                     0,
                                                     WEBKIT_PARAM_READWRITE));

    g_type_class_add_private(frameClass, sizeof(WebKitWebFramePrivate));
}

static void webkit_web_frame_init(WebKitWebFrame* frame)
{
    WebKitWebFramePrivate* priv = WEBKIT_WEB_FRAME_GET_PRIVATE(frame);

    // TODO: Move constructor code here.
    frame->priv = priv;
}

/**
 * webkit_web_frame_get_title:
 * @frame: a #WebKitWebFrame
 *
 * Returns the @frame's document title
 *
 * Return value: the title of @frame
 */
const gchar* webkit_web_frame_get_title(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    WebKitWebFramePrivate* priv = frame->priv;
    return priv->title;
}

/**
 * webkit_web_frame_get_uri:
 * @frame: a #WebKitWebFrame
 *
 * Returns the current URI of the contents displayed by the @frame
 *
 * Return value: the URI of @frame
 */
const gchar* webkit_web_frame_get_uri(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    WebKitWebFramePrivate* priv = frame->priv;
    return priv->uri;
}

/**
 * webkit_web_frame_get_name:
 * @frame: a #WebKitWebFrame
 *
 * Returns the @frame's name
 *
 * Return value: the name of @frame
 */
const gchar* webkit_web_frame_get_name(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    WebKitWebFramePrivate* priv = frame->priv;

    if (priv->name)
        return priv->name;

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return "";

    String string = coreFrame->tree()->uniqueName();
    priv->name = g_strdup(string.utf8().data());
    return priv->name;
}

/**
 * webkit_web_frame_get_parent:
 * @frame: a #WebKitWebFrame
 *
 * Returns the @frame's parent frame, or %NULL if it has none.
 *
 * Return value: (transfer none): the parent #WebKitWebFrame or %NULL in case there is none
 */
WebKitWebFrame* webkit_web_frame_get_parent(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return NULL;

    return kit(coreFrame->tree()->parent());
}

/**
 * webkit_web_frame_load_uri:
 * @frame: a #WebKitWebFrame
 * @uri: an URI string
 *
 * Requests loading of the specified URI string.
 *
 * Since: 1.1.1
 */
void webkit_web_frame_load_uri(WebKitWebFrame* frame, const gchar* uri)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));
    g_return_if_fail(uri);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return;

    coreFrame->loader()->load(FrameLoadRequest(coreFrame, ResourceRequest(KURL(KURL(), String::fromUTF8(uri)))));
}

static void webkit_web_frame_load_data(WebKitWebFrame* frame, const gchar* content, const gchar* mimeType, const gchar* encoding, const gchar* baseURL, const gchar* unreachableURL)
{
    Frame* coreFrame = core(frame);
    ASSERT(coreFrame);

    KURL baseKURL = baseURL ? KURL(KURL(), String::fromUTF8(baseURL)) : blankURL();

    ResourceRequest request(baseKURL);

    RefPtr<SharedBuffer> sharedBuffer = SharedBuffer::create(content, strlen(content));
    SubstituteData substituteData(sharedBuffer.release(),
                                  mimeType ? String::fromUTF8(mimeType) : String::fromUTF8("text/html"),
                                  encoding ? String::fromUTF8(encoding) : String::fromUTF8("UTF-8"),
                                  KURL(KURL(), String::fromUTF8(unreachableURL)),
                                  KURL(KURL(), String::fromUTF8(unreachableURL)));

    coreFrame->loader()->load(FrameLoadRequest(coreFrame, request, substituteData));
}

/**
 * webkit_web_frame_load_string:
 * @frame: a #WebKitWebFrame
 * @content: an URI string
 * @mime_type: the MIME type, or %NULL
 * @encoding: the encoding, or %NULL
 * @base_uri: the base URI for relative locations
 *
 * Requests loading of the given @content with the specified @mime_type,
 * @encoding and @base_uri.
 *
 * If @mime_type is %NULL, "text/html" is assumed.
 *
 * If @encoding is %NULL, "UTF-8" is assumed.
 *
 * Since: 1.1.1
 */
void webkit_web_frame_load_string(WebKitWebFrame* frame, const gchar* content, const gchar* contentMimeType, const gchar* contentEncoding, const gchar* baseUri)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));
    g_return_if_fail(content);

    webkit_web_frame_load_data(frame, content, contentMimeType, contentEncoding, baseUri, NULL);
}

/**
 * webkit_web_frame_load_alternate_string:
 * @frame: a #WebKitWebFrame
 * @content: the alternate content to display as the main page of the @frame
 * @base_url: the base URI for relative locations
 * @unreachable_url: the URL for the alternate page content
 *
 * Request loading of an alternate content for a URL that is unreachable.
 * Using this method will preserve the back-forward list. The URI passed in
 * @base_url has to be an absolute URI.
 *
 * Since: 1.1.6
 */
void webkit_web_frame_load_alternate_string(WebKitWebFrame* frame, const gchar* content, const gchar* baseURL, const gchar* unreachableURL)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));
    g_return_if_fail(content);

    webkit_web_frame_load_data(frame, content, NULL, NULL, baseURL, unreachableURL);
}

/**
 * webkit_web_frame_load_request:
 * @frame: a #WebKitWebFrame
 * @request: a #WebKitNetworkRequest
 *
 * Connects to a given URI by initiating an 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_frame_stop_loading() to
 * stop the load. This function is typically invoked on the main frame.
 */
void webkit_web_frame_load_request(WebKitWebFrame* frame, WebKitNetworkRequest* request)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));
    g_return_if_fail(WEBKIT_IS_NETWORK_REQUEST(request));

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return;

    coreFrame->loader()->load(FrameLoadRequest(coreFrame->document()->securityOrigin(), core(request)));
}

/**
 * webkit_web_frame_stop_loading:
 * @frame: a #WebKitWebFrame
 *
 * Stops any pending loads on @frame's data source, and those of its children.
 */
void webkit_web_frame_stop_loading(WebKitWebFrame* frame)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return;

    coreFrame->loader()->stopAllLoaders();
}

/**
 * webkit_web_frame_reload:
 * @frame: a #WebKitWebFrame
 *
 * Reloads the initial request.
 */
void webkit_web_frame_reload(WebKitWebFrame* frame)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return;

    coreFrame->loader()->reload();
}

/**
 * webkit_web_frame_find_frame:
 * @frame: a #WebKitWebFrame
 * @name: the name of the frame to be found
 *
 * For pre-defined names, returns @frame if @name is "_self" or "_current",
 * returns @frame's parent frame if @name is "_parent", and returns the main
 * frame if @name is "_top". Also returns @frame if it is the main frame and
 * @name is either "_parent" or "_top". For other names, this function returns
 * the first frame that matches @name. This function searches @frame and its
 * descendents first, then @frame's parent and its children moving up the
 * hierarchy until a match is found. If no match is found in @frame's
 * hierarchy, this function will search for a matching frame in other main
 * frame hierarchies. Returns %NULL if no match is found.
 *
 * Return value: (transfer none): the found #WebKitWebFrame or %NULL in case none is found
 */
WebKitWebFrame* webkit_web_frame_find_frame(WebKitWebFrame* frame, const gchar* name)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
    g_return_val_if_fail(name, NULL);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return NULL;

    String nameString = String::fromUTF8(name);
    return kit(coreFrame->tree()->find(AtomicString(nameString)));
}

/**
 * webkit_web_frame_get_global_context:
 * @frame: a #WebKitWebFrame
 *
 * Gets the global JavaScript execution context. Use this function to bridge
 * between the WebKit and JavaScriptCore APIs.
 *
 * Return value: (transfer none): the global JavaScript context
 */
JSGlobalContextRef webkit_web_frame_get_global_context(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return NULL;

    return toGlobalRef(coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec());
}

/**
 * webkit_web_frame_get_data_source:
 * @frame: a #WebKitWebFrame
 *
 * Returns the committed data source.
 *
 * Return value: (transfer none): the committed #WebKitWebDataSource.
 *
 * Since: 1.1.14
 */
WebKitWebDataSource* webkit_web_frame_get_data_source(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    Frame* coreFrame = core(frame);
    return webkit_web_frame_get_data_source_from_core_loader(coreFrame->loader()->documentLoader());
}

/**
 * webkit_web_frame_get_provisional_data_source:
 * @frame: a #WebKitWebFrame
 *
 * You use the webkit_web_frame_load_request method to initiate a request that
 * creates a provisional data source. The provisional data source will
 * transition to a committed data source once any data has been received. Use
 * webkit_web_frame_get_data_source to get the committed data source.
 *
 * Return value: (transfer none): the provisional #WebKitWebDataSource or %NULL if a load
 * request is not in progress.
 *
 * Since: 1.1.14
 */
WebKitWebDataSource* webkit_web_frame_get_provisional_data_source(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    Frame* coreFrame = core(frame);
    return webkit_web_frame_get_data_source_from_core_loader(coreFrame->loader()->provisionalDocumentLoader());
}

/**
 * webkit_web_frame_get_children:
 * @frame: a #WebKitWebFrame
 *
 * Return value: child frames of @frame
 */
GSList* webkit_web_frame_get_children(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return NULL;

    GSList* children = NULL;
    for (Frame* child = coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
        FrameLoader* loader = child->loader();
        WebKit::FrameLoaderClient* client = static_cast<WebKit::FrameLoaderClient*>(loader->client());
        if (client)
          children = g_slist_append(children, client->webFrame());
    }

    return children;
}

/**
 * webkit_web_frame_get_inner_text:
 * @frame: a #WebKitWebFrame
 *
 * Return value: inner text of @frame
 */
gchar* webkit_web_frame_get_inner_text(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return g_strdup("");

    FrameView* view = coreFrame->view();

    if (view && view->layoutPending())
        view->layout();

    Element* documentElement = coreFrame->document()->documentElement();
    String string =  documentElement->innerText();
    return g_strdup(string.utf8().data());
}

/**
 * webkit_web_frame_dump_render_tree:
 * @frame: a #WebKitWebFrame
 *
 * Return value: Non-recursive render tree dump of @frame
 */
gchar* webkit_web_frame_dump_render_tree(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return g_strdup("");

    FrameView* view = coreFrame->view();

    if (view && view->layoutPending())
        view->layout();

    String string = externalRepresentation(coreFrame);
    return g_strdup(string.utf8().data());
}

/**
 * webkit_web_frame_counter_value_for_element_by_id:
 * @frame: a #WebKitWebFrame
 * @id: an element ID string
 *
 * Return value: The counter value of element @id in @frame
 */
gchar* webkit_web_frame_counter_value_for_element_by_id(WebKitWebFrame* frame, const gchar* id)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return 0;

    Element* coreElement = coreFrame->document()->getElementById(AtomicString(id));
    if (!coreElement)
        return 0;
    String counterValue = counterValueForElement(coreElement);
    return g_strdup(counterValue.utf8().data());
}

/**
 * webkit_web_frame_page_number_for_element_by_id
 * @frame: a #WebKitWebFrame
 * @id: an element ID string
 * @pageWidth: width of a page
 * @pageHeight: height of a page
 *
 * Return value: The number of page where the specified element will be put
 */
int webkit_web_frame_page_number_for_element_by_id(WebKitWebFrame* frame, const gchar* id, float pageWidth, float pageHeight)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), -1);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return -1;

    Element* coreElement = coreFrame->document()->getElementById(AtomicString(id));
    if (!coreElement)
        return -1;
    return PrintContext::pageNumberForElement(coreElement, FloatSize(pageWidth, pageHeight));
}

/**
 * webkit_web_frame_number_of_pages
 * @frame: a #WebKitWebFrame
 * @pageWidth: width of a page
 * @pageHeight: height of a page
 *
 * Return value: The number of pages to be printed.
 */
int webkit_web_frame_number_of_pages(WebKitWebFrame* frame, float pageWidth, float pageHeight)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), -1);

    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return -1;

    return PrintContext::numberOfPages(coreFrame, FloatSize(pageWidth, pageHeight));
}

/**
 * webkit_web_frame_get_pending_unload_event_count:
 * @frame: a #WebKitWebFrame
 *
 * Return value: number of pending unload events
 */
guint webkit_web_frame_get_pending_unload_event_count(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), 0);

    return core(frame)->document()->domWindow()->pendingUnloadEventListeners();
}

bool webkit_web_frame_pause_animation(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element)
{
    ASSERT(core(frame));
    Element* coreElement = core(frame)->document()->getElementById(AtomicString(element));
    if (!coreElement || !coreElement->renderer())
        return false;
    return core(frame)->animation()->pauseAnimationAtTime(coreElement->renderer(), AtomicString(name), time);
}

bool webkit_web_frame_pause_transition(WebKitWebFrame* frame, const gchar* name, double time, const gchar* element)
{
    ASSERT(core(frame));
    Element* coreElement = core(frame)->document()->getElementById(AtomicString(element));
    if (!coreElement || !coreElement->renderer())
        return false;
    return core(frame)->animation()->pauseTransitionAtTime(coreElement->renderer(), AtomicString(name), time);
}

gchar* webkit_web_frame_marker_text_for_list_item(WebKitWebFrame* frame, JSContextRef context, JSValueRef nodeObject)
{
    JSC::ExecState* exec = toJS(context);
    Element* element = toElement(toJS(exec, nodeObject));
    if (!element)
        return 0;

    return g_strdup(markerTextForListItem(element).utf8().data());
}

gchar* webkit_web_frame_get_response_mime_type(WebKitWebFrame* frame)
{
    Frame* coreFrame = core(frame);
    WebCore::DocumentLoader* docLoader = coreFrame->loader()->documentLoader();
    String mimeType = docLoader->responseMIMEType();
    return g_strdup(mimeType.utf8().data());
}

/**
 * webkit_web_frame_get_load_status:
 * @frame: a #WebKitWebView
 *
 * Determines the current status of the load.
 *
 * Return value: a #WebKitLoadStatus object
 *
 * Since: 1.1.7
 */
WebKitLoadStatus webkit_web_frame_get_load_status(WebKitWebFrame* frame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), WEBKIT_LOAD_FINISHED);

    WebKitWebFramePrivate* priv = frame->priv;
    return priv->loadStatus;
}

void webkit_web_frame_clear_main_frame_name(WebKitWebFrame* frame)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));

    core(frame)->tree()->clearName();
}

void webkit_gc_collect_javascript_objects()
{
    gcController().garbageCollectNow();
}

void webkit_gc_collect_javascript_objects_on_alternate_thread(gboolean waitUntilDone)
{
    gcController().garbageCollectOnAlternateThreadForDebugging(waitUntilDone);
}

/**
 * webkit_web_frame_get_security_origin:
 * @frame: a #WebKitWebFrame
 *
 * Returns the @frame's security origin.
 *
 * Return value: (transfer none): the security origin of @frame
 *
 * Since: 1.1.14
 */
WebKitSecurityOrigin* webkit_web_frame_get_security_origin(WebKitWebFrame* frame)
{
    WebKitWebFramePrivate* priv = frame->priv;
    if (!priv->coreFrame || !priv->coreFrame->document() || !priv->coreFrame->document()->securityOrigin())
        return NULL;

    if (priv->origin && priv->origin->priv->coreOrigin.get() == priv->coreFrame->document()->securityOrigin())
        return priv->origin;

    if (priv->origin)
        g_object_unref(priv->origin);

    priv->origin = kit(priv->coreFrame->document()->securityOrigin());
    return priv->origin;
}

void webkit_web_frame_layout(WebKitWebFrame* frame)
{
    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return;

    FrameView* view = coreFrame->view();
    if (!view)
        return;

    view->layout();
}

/**
 * webkit_web_frame_get_network_response:
 * @frame: a #WebKitWebFrame
 *
 * Returns a #WebKitNetworkResponse object representing the response
 * that was given to the request for the given frame, or NULL if the
 * frame was not created by a load. You must unref the object when you
 * are done with it.
 *
 * Return value: (transfer full): a #WebKitNetworkResponse object
 *
 * Since: 1.1.18
 */
WebKitNetworkResponse* webkit_web_frame_get_network_response(WebKitWebFrame* frame)
{
    Frame* coreFrame = core(frame);
    if (!coreFrame)
        return NULL;

    WebCore::DocumentLoader* loader = coreFrame->loader()->activeDocumentLoader();
    if (!loader)
        return NULL;

    return kitNew(loader->response());
}

gboolean webkit_web_frame_get_contents_size(WebKitWebFrame* webFrame,
                                            int* width, int* height)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), FALSE);

    FrameView* coreView = core(webFrame)->view();
    if (!coreView)
        return FALSE;

    if (width)
        *width = coreView->contentsWidth();
    if (height)
        *height = coreView->contentsHeight();

    return TRUE;
}

int webkit_web_frame_get_contents_width(WebKitWebFrame* webFrame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), -1);

    FrameView* coreView = core(webFrame)->view();
    if (!coreView)
        return -1;

    return coreView->contentsWidth();
}

int webkit_web_frame_get_contents_height(WebKitWebFrame* webFrame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), -1);

    FrameView* coreView = core(webFrame)->view();
    if (!coreView)
        return -1;

    return coreView->contentsHeight();
}

/**
 * webkit_web_frame_get_scroll_offset:
 * @webFrame: a #WebKitWebFrame instance
 * @x: the location where the value for the x position should be stored or %NULL
 * @y: the location where the value for the y position should be stored or %NULL
 *
 * Obtains the current scroll offset the the given @webFrame.
 *
 * Return value: %TRUE if the position was correctly obtained, %FALSE
 * if the frame does not have a view currently.
 */
gboolean webkit_web_frame_get_scroll_offset(WebKitWebFrame* webFrame,
                                            int* x, int* y)
{
   g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), FALSE);

    FrameView* coreView = core(webFrame)->view();
    if (!coreView)
        return FALSE;

    if (x) {
#if USE(TILED_BACKING_STORE)
        if (core(webFrame) && core(webFrame)->tiledBackingStore())
            *x = webFrame->priv->xOffset;
        else
#endif
            *x = coreView->scrollX();
    }

    if (y) {
#if USE(TILED_BACKING_STORE)
        if (core(webFrame) && core(webFrame)->tiledBackingStore())
            *y = webFrame->priv->yOffset;
        else
#endif
            *y = coreView->scrollY();
    }

    return TRUE;
}

/**
 * webkit_web_frame_get_x_scroll_offset:
 * @webFrame: a #WebKitWebFrame instance
 *
 * Obtains the current scroll offset of the x axis for the the given
 * @webFrame.
 *
 * Return value: the x scroll offset or -1 if no view exists for the
 * frame.
 */
int webkit_web_frame_get_x_scroll_offset(WebKitWebFrame* webFrame)
{
   g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), -1);

    FrameView* coreView = core(webFrame)->view();
    if (!coreView)
        return -1;

#if USE(TILED_BACKING_STORE)
    Frame* frame = core(webFrame);
    if (frame && frame->tiledBackingStore())
        return webFrame->priv->xOffset;
#endif

    return coreView->scrollX();
}

/**
 * webkit_web_frame_get_y_scroll_offset:
 * @webFrame: a #WebKitWebFrame instance
 *
 * Obtains the current scroll offset of the y axis for the the given
 * @webFrame.
 *
 * Return value: the y scroll offset or -1 if no view exists for the
 * frame.
 */
int webkit_web_frame_get_y_scroll_offset(WebKitWebFrame* webFrame)
{
   g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), -1);

    FrameView* coreView = core(webFrame)->view();
    if (!coreView)
        return -1;

#if USE(TILED_BACKING_STORE)
    Frame* frame = core(webFrame);
    if (frame && frame->tiledBackingStore())
        return webFrame->priv->yOffset;
#endif

    return coreView->scrollY();
}

/**
 * webkit_web_frame_set_scroll_offset:
 * @webFrame: a #WebKitWebFrame instance
 * @x: the x position to where the view should be scrolled
 * @y: the y position to where the view should be scrolled
 *
 * Sets the scroll offset of the given frame.
 */
void webkit_web_frame_set_scroll_offset(WebKitWebFrame* webFrame,
                                        int x, int y)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

    FrameView* coreView = core(webFrame)->view();
    if (!coreView)
        return;

    WebKitWebPage* webPage = webkit_web_frame_get_page(webFrame);

#if USE(TILED_BACKING_STORE)
    Frame* frame = core(webFrame);
    if (frame && frame->tiledBackingStore()) {
        webFrame->priv->xOffset = x;
        webFrame->priv->yOffset = y;

        int width, height;
        webkit_web_page_get_viewport_size(webPage, &width, &height);
        IntRect fixedVisibleContentRect(x, y, width, height);

        // Let WebCore know that we're in a different part of the page,
        // for programmatic scrolling.
        // This also emits the scroll event so that DOM event listeners are notified.
        coreView->setFixedVisibleContentRect(fixedVisibleContentRect);

        frame->tiledBackingStore()->coverWithTilesIfNeeded();

        // Let plugins and other child widgets know as well.
        coreView->frameRectsChanged();
    }
#endif

    if (!coreView->delegatesScrolling()) {
        coreView->setScrollPosition(IntPoint(x, y));
#if USE(ACCELERATED_COMPOSITING)
        webkit_web_page_set_scroll_layers(webPage, x, y);
#endif
    }
}

/**
 * webkit_web_frame_set_x_scroll_offset:
 * @webFrame: a #WebKitWebFrame instance
 * @offset: the x position to where the view should be scrolled
 *
 * Sets the x scroll offset of the given frame.
 */
void webkit_web_frame_set_x_scroll_offset(WebKitWebFrame* webFrame, int offset)
{
    int y;
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

    webkit_web_frame_get_scroll_offset(webFrame, 0, &y);
    webkit_web_frame_set_scroll_offset(webFrame, offset, y);
}

/**
 * webkit_web_frame_set_y_scroll_offset:
 * @webFrame: a #WebKitWebFrame instance
 * @offset: the y position to where the view should be scrolled
 *
 * Sets the y scroll offset of the given frame.
 */
void webkit_web_frame_set_y_scroll_offset(WebKitWebFrame* webFrame, int offset)
{
    int x;
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

    webkit_web_frame_get_scroll_offset(webFrame, &x, 0);
    webkit_web_frame_set_scroll_offset(webFrame, x, offset);
}

/**
 * webkit_web_frame_scroll:
 * @webFrame: a #WebKitWebFrame instance
 * @x: how many pixels the view should be scrolled in the x axis
 * @y: how many pixels the view should be scrolled in the y axis
 *
 * Shifts the visible area by scrolling the contents of the given
 * frame by the provided amount.
 */
void webkit_web_frame_scroll(WebKitWebFrame* webFrame, int x, int y)
{
    int currX = 0, currY = 0;
    if (webkit_web_frame_get_scroll_offset(webFrame, &currX, &currY))
        webkit_web_frame_set_scroll_offset(webFrame, currX+x, currY+y);
}

/**
 * webkit_web_frame_get_zoom_level:
 * @webFrame: a #WebKitWebFrame
 *
 * Returns the zoom level of @webFrame, i.e. the factor by which elements in
 * the frame are scaled with respect to their original size.
 *
 * Return value: the zoom level of @web_frame
 */
gfloat webkit_web_frame_get_zoom_level(WebKitWebFrame* webFrame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), 1.0f);

    return core(webFrame)->pageZoomFactor();
}

/**
 * webkit_web_frame_set_zoom_level:
 * @webFrame: a #WebKitWebFrame
 * @zoomLevel: the new zoom level
 *
 * Sets the zoom level of @webFrame, i.e. the factor by which elements in
 * the frame are scaled with respect to their original size.
 */
void webkit_web_frame_set_zoom_level(WebKitWebFrame* webFrame, gfloat zoomLevel)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

    // Invalidate the whole view - this will be necessary anyway, and
    // by doing it here we guarantee the areas that may remain outside
    // the content if the zoom level is reduced are correctly
    // repainted.
    if (webFrame->priv->webPage && webkit_web_page_get_main_frame(webFrame->priv->webPage) == webFrame)
        webkit_iweb_view_invalidate(getViewFromFrame(webFrame));

    Frame* frame = core(webFrame);

    frame->setPageZoomFactor(zoomLevel);

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

/**
 * webkit_web_frame_zoom_in:
 * @webFrame: a #WebKitWebFrame
 *
 * Increases the zoom level of @webFrame. The current zoom
 * level is incremented by the value of the "zoom-step"
 * property of the #WebKitWebSettings associated with @web_frame.
 */
void webkit_web_frame_zoom_in(WebKitWebFrame* webFrame)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

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

    webkit_web_frame_set_zoom_level(webFrame, webkit_web_frame_get_zoom_level(webFrame) + zoomMultiplierRatio);
}

/**
 * webkit_web_frame_zoom_out:
 * @webFrame: a #WebKitWebFrame
 *
 * Decreases the zoom level of @webFrame. The current zoom
 * level is decremented by the value of the "zoom-step"
 * property of the #WebKitWebSettings associated with @web_frame.
 */
void webkit_web_frame_zoom_out(WebKitWebFrame* webFrame)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

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

    webkit_web_frame_set_zoom_level(webFrame, webkit_web_frame_get_zoom_level(webFrame) - zoomMultiplierRatio);
}

/**
 * webkit_web_frame_get_text_zoom_level:
 * @webFrame: a #WebKitWebFrame
 *
 * Returns the text zoom level of @webFrame, i.e. the factor by which
 * text in the frame is scaled with respect to its original size.
 *
 * Return value: the text zoom level of @web_frame
 */
gfloat webkit_web_frame_get_text_zoom_level(WebKitWebFrame* webFrame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), 1.0f);

    return core(webFrame)->textZoomFactor();
}

/**
 * webkit_web_frame_set_text_zoom_level:
 * @webFrame: a #WebKitWebFrame
 * @zoomLevel: the new zoom level
 *
 * Sets the text zoom level of @webFrame, i.e. the factor by which
 * text in the frame is scaled with respect to its original size.
 */
void webkit_web_frame_set_text_zoom_level(WebKitWebFrame* webFrame, gfloat zoomLevel)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

    core(webFrame)->setTextZoomFactor(zoomLevel);

    g_object_notify(G_OBJECT(webFrame), "text-zoom-level");
}

#if USE(TILED_BACKING_STORE)
static void renderFromTiledBackingStore(WebKitWebFrame* webFrame, GraphicsContext* context, IntRect rect)
{
    Frame* frame = core(webFrame);
    ASSERT(frame->tiledBackingStore());
    if (!frame->view() || !frame->contentRenderer())
        return;

    int scrollX = webkit_web_frame_get_x_scroll_offset(webFrame);
    int scrollY = webkit_web_frame_get_y_scroll_offset(webFrame);

    context->translate(-scrollX, -scrollY);
    rect.move(scrollX, scrollY);
    frame->tiledBackingStore()->paint(context, rect);
}
#endif

void webkit_web_frame_render(WebKitWebFrame *webFrame, cairo_t *cr,
                             gint x_offset, gint y_offset,
                             gint width, gint height)
{
    Frame* coreFrame = core(webFrame);
    if (!coreFrame || !coreFrame->contentRenderer() || !coreFrame->view())
        return;

    GraphicsContext ctx(cr);

    coreFrame->view()->updateLayoutAndStyleIfNeededRecursive();

#if 0 // FIXME: How do we handle transparency?
    if (priv->transparent)
        ctx.clearRect(rect);
#endif

    IntRect rect(x_offset, y_offset, width, height);
    ctx.save();
    ctx.clip(rect);

#if USE(TILED_BACKING_STORE)
        if (coreFrame->tiledBackingStore())
            renderFromTiledBackingStore(webFrame, &ctx, rect);
        else
            coreFrame->view()->paint(&ctx, rect);
#else
        coreFrame->view()->paint(&ctx, rect);
#endif

    ctx.restore();

    ctx.save();
    coreFrame->page()->inspectorController()->drawHighlight(ctx);
    ctx.restore();
}

WebKitWebPage* webkit_web_frame_get_page(WebKitWebFrame* webFrame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), 0);
    if (webFrame->priv)
        return webFrame->priv->webPage;
    return 0;
}

/* Private functions. */
WebKitIWebView* getViewFromFrame(WebKitWebFrame* frame)
{
    WebKitWebFramePrivate* priv = frame->priv;
    WebKitWebPage* webPage  = priv->webPage;
    if (webPage)
        return webPage->priv->view;
    return 0;
}

/**
 * webkit_web_frame_save_as_pdf:
 * @frame: a #WebKitWebFrame
 * @file_name: a path to which the file will be written
 * @width: width of the page
 * @height: height of the page
 *
 * Prints the given #WebKitWebFrame to a pdf
 *
 */
WEBKIT_API void
webkit_web_frame_save_as_pdf    (WebKitWebFrame* frame,
                                 const char* file_name,
                                 gfloat width,
                                 gfloat height)

{
    g_return_if_fail( WEBKIT_IS_WEB_FRAME(frame) );

    Frame* coreFrame = core(frame);
    PrintContext printContext(coreFrame);
    FloatRect printRect = FloatRect(0, 0, width, height);

    // TODO: Margin adjustments and header/footer support
    float headerHeight = 0;
    float footerHeight = 0;
    float pageHeight; // height of the page adjusted by margins
    printContext.computePageRects(printRect, headerHeight, footerHeight, 1.0, pageHeight);
    cairo_surface_t * surface = cairo_pdf_surface_create(file_name, width, height);
    cairo_t *cr = cairo_create(surface);
    GraphicsContext graphicsContext(cr);

    coreFrame->view()->updateLayoutAndStyleIfNeededRecursive();
    for(guint pageNumber=0;pageNumber < printContext.pageCount();pageNumber++) {
        printContext.spoolPage(graphicsContext, pageNumber, width);
        cairo_show_page(cr);
    }

    cairo_surface_destroy(surface);
    cairo_destroy(cr);
}

/**
 * webkit_web_frame_set_contents_scale:
 * @webFrame: a #WebKitWebFrame
 * @scale: the scale for the frame contents
 *
 * Sets the scale factor to be applied to the frame contents.
 */
void
webkit_web_frame_set_contents_scale(WebKitWebFrame* webFrame, gfloat scale)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

    Frame* coreFrame = core(webFrame);
    coreFrame->tiledBackingStore()->setContentsScale(scale);
}

/**
 * webkit_web_frame_get_contents_scale:
 * @webFrame: a #WebKitWebFrame
 *
 * Gets the scale factor applied to the frame contents.
 *
 * Returns: the scale applied to the frame contents
 */
gfloat
webkit_web_frame_get_contents_scale(WebKitWebFrame* webFrame)
{
    g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(webFrame), 1.0);

    Frame* coreFrame = core(webFrame);
    if (coreFrame->tiledBackingStore())
        return coreFrame->tiledBackingStore()->contentsScale();
    else
        return 1.0;
}

/**
 * webkit_web_frame_set_contents_frozen:
 * @webFrame: a #WebKitWebFrame
 * @freeze: whether to freeze frame contents
 *
 * Freeze frame contents preventing them from getting relayouted after
 * size or scale changes.
 */
void
webkit_web_frame_set_contents_frozen(WebKitWebFrame* webFrame, gboolean freeze)
{
    g_return_if_fail(WEBKIT_IS_WEB_FRAME(webFrame));

    Frame* coreFrame = core(webFrame);
    coreFrame->tiledBackingStore()->setContentsFrozen(freeze);
}

namespace WebKit {
WebCore::Frame* core(WebKitWebFrame* frame)
{
    if (!frame)
        return 0;

    WebKitWebFramePrivate* priv = frame->priv;
    return priv ? priv->coreFrame : 0;
}

WebKitWebFrame* kit(WebCore::Frame* coreFrame)
{
    if (!coreFrame)
        return 0;

    ASSERT(coreFrame->loader());
    WebKit::FrameLoaderClient* client = static_cast<WebKit::FrameLoaderClient*>(coreFrame->loader()->client());
    return client ? client->webFrame() : 0;
}
}
