/*
 * Copyright (c) 2010-2013 Stefan Bolte <portix@gmx.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <string.h>
#include <gdk/gdkkeysyms.h> 
#include <JavaScriptCore/JavaScript.h> 
#include "dwb.h"
#include "commands.h"
#include "util.h"
#include "download.h"
#include "session.h"
#include "view.h"
#include "html.h"
#include "plugins.h"
#include "local.h"
#include "soup.h"
#include "adblock.h"
#include "js.h"
#include "scripts.h"
#include "dom.h"

static void view_ssl_state(GList *);
static unsigned long s_click_time;
static guint s_sig_caret_button_release;
static guint s_sig_caret_motion;
static const char * dummy_icon[] = { "1 1 1 1 ", "- c NONE", "", };


/* CALLBACKS */
static gboolean view_caret_release_cb(WebKitWebView *web, GdkEventButton *e, WebKitDOMRange *other);
static gboolean view_caret_motion_cb(WebKitWebView *web, GdkEventButton *e, WebKitDOMRange *other);
static gboolean view_button_press_cb(WebKitWebView *, GdkEventButton *, GList *);
static gboolean view_close_web_view_cb(WebKitWebView *, GList *);
static gboolean view_console_message_cb(WebKitWebView *, char *, int , char *, GList *);
static WebKitWebView * view_create_web_view_cb(WebKitWebView *, WebKitWebFrame *, GList *);
static gboolean view_download_requested_cb(WebKitWebView *, WebKitDownload *, GList *);
static WebKitWebView * view_inspect_web_view_cb(WebKitWebInspector *, WebKitWebView *, GList *);
static void view_hovering_over_link_cb(WebKitWebView *, char *, char *, GList *);
static gboolean view_mime_type_policy_cb(WebKitWebView *, WebKitWebFrame *, WebKitNetworkRequest *, char *, WebKitWebPolicyDecision *, GList *);
static gboolean view_navigation_policy_cb(WebKitWebView *, WebKitWebFrame *, WebKitNetworkRequest *, WebKitWebNavigationAction *, WebKitWebPolicyDecision *, GList *);
static gboolean view_new_window_policy_cb(WebKitWebView *, WebKitWebFrame *, WebKitNetworkRequest *, WebKitWebNavigationAction *, WebKitWebPolicyDecision *, GList *);
//static void view_resource_request_cb(WebKitWebView *, WebKitWebFrame *, WebKitWebResource *, WebKitNetworkRequest *, WebKitNetworkResponse *, GList *);
static gboolean view_scroll_cb(GtkWidget *, GdkEventScroll *, GList *);
static gboolean view_value_changed_cb(GtkAdjustment *, GList *);
static void view_title_cb(WebKitWebView *, GParamSpec *, GList *);
static void view_uri_cb(WebKitWebView *, GParamSpec *, GList *);
static void view_load_status_cb(WebKitWebView *, GParamSpec *, GList *);
static gboolean view_tab_button_press_cb(GtkWidget *, GdkEventButton *, GList *);
static void view_frame_created_cb(WebKitWebView *wv, WebKitWebFrame *, GList *);

/* WEB_VIEW_CALL_BACKS {{{*/

/* view_main_frame_committed_cb {{{*/
#if 0
void
view_main_frame_committed_cb(WebKitWebFrame *frame, GList *gl) {
//  if (gl == dwb.state.fview && (dwb.state.mode == INSERT_MODE || dwb.state.mode == FIND_MODE)) {
//    dwb_change_mode(NORMAL_MODE, true);
//  }
}/*}}}*/
#endif

static void 
view_resource_request_cb(WebKitWebView *wv, WebKitWebFrame *frame, WebKitWebResource *resource, WebKitNetworkRequest *request, WebKitNetworkResponse *response, GList *gl) 
{
    if (EMIT_SCRIPT(RESOURCE))  
    {
        /**
         * Emitted before a resource is loaded
         * @event resource
         * @memberOf signals
         * @param {signals~onResource} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called before a resource is loaded
         * @callback signals~onResource
         * @param {WebKitWebView}  webview 
         *      The webview that contains the requesting frame
         * @param {WebKitWebFrame}  frame 
         *      The frame that requested the resource
         * @param {WebKitNetworkRequest}  request 
         *      The request, setting request.uri to "about:blank" will prevent
         *      the request from being loaded.
         * @param {WebKitNetworkResponse}  response 
         *      The response, note that response will always be null unless the
         *      request comes from a redirect
         * */
        ScriptSignal signal = {
            SCRIPTS_WV(gl), .objects = { G_OBJECT(frame), G_OBJECT(request), G_OBJECT(response) }, SCRIPTS_SIG_META(NULL, RESOURCE, 3) };
        scripts_emit(&signal);
    }
    if (dwb.state.do_not_track) 
    {
        SoupMessage *msg = webkit_network_request_get_message(request);
        if (msg != NULL) 
            soup_message_headers_append(msg->request_headers, "DNT", "1");
    }
}
#if WEBKIT_CHECK_VERSION(1, 8, 0) 
static void 
view_document_finished(WebKitWebView *wv, WebKitWebFrame *frame, GList *gl) 
{
    if (EMIT_SCRIPT(DOCUMENT_LOADED)) 
    {
        /**
         * Emitted when a document of a frame has been loaded. Use this
         * event to inject scripts that need a fully populated DOM-tree
         * @event documentLoaded
         * @memberOf signals
         * @param {signals~onDocumentLoaded} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when the document of a frame has been loaded.
         * @callback signals~onDocumentLoaded
         * @param {WebKitWebView}  webview 
         *      The webview that contains the frame
         * @param {WebKitWebFrame}  frame 
         *      The frame that emitted the signal
         * */
        ScriptSignal signal = { SCRIPTS_WV(gl), { G_OBJECT(frame) }, SCRIPTS_SIG_META(NULL, DOCUMENT_LOADED, 1) };
        scripts_emit(&signal);
    }
}
#endif

/* view_button_press_cb(WebKitWebView *web, GdkEventButton *button, GList *gl) {{{*/
static void 
view_disconnect_caret(WebKitWebView *web) 
{
    if (s_sig_caret_button_release > 0) 
    {
        g_signal_handler_disconnect(web, s_sig_caret_button_release);
        s_sig_caret_button_release = 0;
    }
    if (s_sig_caret_motion > 0) 
    {
        g_signal_handler_disconnect(web, s_sig_caret_motion);
        s_sig_caret_motion = 0;
    }
}
void 
view_link_select(WebKitWebView *web, GdkEventButton *e, WebKitDOMRange *other) 
{
    int start, end;

    WebKitDOMDocument *doc = webkit_web_view_get_dom_document(web);
    WebKitDOMRange *range = webkit_dom_document_caret_range_from_point(doc, e->x, e->y);
    WebKitDOMDOMSelection *selection = webkit_dom_dom_window_get_selection(webkit_dom_document_get_default_view(doc));

    start = webkit_dom_range_get_start_offset(other, NULL);
    end = webkit_dom_range_get_end_offset(range, NULL);

    WebKitDOMNode *node = webkit_dom_range_get_start_container(other, NULL);
    WebKitDOMNode *thisnode = webkit_dom_range_get_start_container(range, NULL);

    if (start < end) 
        webkit_dom_dom_selection_set_base_and_extent(selection, node, start, thisnode, end, NULL);
    else 
        webkit_dom_dom_selection_set_base_and_extent(selection, thisnode, end, node, start, NULL);
}
static gboolean
view_caret_motion_cb(WebKitWebView *web, GdkEventButton *e, WebKitDOMRange *other) 
{
    view_link_select(web, e, other);
    return false;
}
static gboolean
view_caret_release_cb(WebKitWebView *web, GdkEventButton *e, WebKitDOMRange *other) 
{
    view_link_select(web, e, other);
    view_disconnect_caret(web);
    return false;
}
static gboolean
view_button_press_cb(WebKitWebView *web, GdkEventButton *e, GList *gl) 
{
    gboolean ret = false;
    WebKitHitTestResultContext context;

    WebKitHitTestResult *result = webkit_web_view_get_hit_test_result(web, e);
    g_object_get(result, "context", &context, NULL);

    s_click_time = e->time;
    if (EMIT_SCRIPT(BUTTON_PRESS)) 
    {
        /**
         * Emitted when a mouse button was pressed
         *
         * @event buttonPress
         * @memberOf signals
         * @param {signals~onButtonPress} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when a mouse button was pressed on a webview
         * @callback signals~onButtonPress
         * @param {WebKitWebView}  webview 
         *      The webview that emitted the signal
         * @param {WebKitHitTestResult} result 
         *      A hit test result
         * @param {Object} event
         * @param {Number} event.button 
         *      The button that was pressed, usually a value between 1 and 5
         * @param {Modifier} event.state 
         *      A bitmask of {@link Enums and Flags.Modifier|Modifiers} pressed
         * @param {Number} event.time 
         *      The time in milliseconds when the button was pressed
         * @param {ClickType} event.type 
         *      A {@link Enums and Flags.ClickType|ClickType}
         * @param {Number} event.x 
         *      x-position relative to the window
         * @param {Number} event.xRoot
         *      x-position relative to the screen
         * @param {Number} event.y 
         *      y-position relative to the window
         * @param {Number} event.yRoot
         *      y-position relative to the screen
         *
         * @returns {Boolean}
         *      Return true to prevent the default action
         * */
        char *json = util_create_json(8, 
                UINTEGER, "time", e->time, UINTEGER,    "type", e->type, 
                DOUBLE,   "x", e->x, DOUBLE,            "y", e->y, 
                UINTEGER, "state", e->state, UINTEGER,  "button", e->button, 
                DOUBLE,   "xRoot", e->x_root, DOUBLE,   "yRoot", e->y_root);
        ScriptSignal signal = { SCRIPTS_WV(gl), { G_OBJECT(result) }, SCRIPTS_SIG_META(json, BUTTON_PRESS, 1) };
        ret = scripts_emit(&signal);
        g_free(json);
        if (ret) 
            goto clean;
    }

    if (gtk_widget_has_focus(dwb.gui.entry)) 
    {
        dwb_focus_scroll(gl);
        CLEAR_COMMAND_TEXT();
    }
    if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE) 
    {
        dwb_change_mode(INSERT_MODE);
    }
    else if (e->state & GDK_CONTROL_MASK && e->button == 1) 
    {
        WebKitDOMDocument *doc = webkit_web_view_get_dom_document(web);
        WebKitDOMRange *range = webkit_dom_document_caret_range_from_point(doc, e->x, e->y);
        WebKitDOMDOMSelection *selection = webkit_dom_dom_window_get_selection(webkit_dom_document_get_default_view(doc));
        webkit_dom_dom_selection_remove_all_ranges(selection);
        webkit_dom_dom_selection_add_range(selection, range);
        s_sig_caret_button_release = g_signal_connect(web, "button-release-event", G_CALLBACK(view_caret_release_cb), range);
        s_sig_caret_motion = g_signal_connect(web, "motion-notify-event", G_CALLBACK(view_caret_motion_cb), range);
    }
    else if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION && e->type == GDK_BUTTON_PRESS && e->state & GDK_BUTTON1_MASK) 
    {
        char *clipboard = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
        char *backup = clipboard;
        g_strstrip(clipboard);
        if (e->button == 3) 
        {
            dwb_load_uri(NULL, clipboard);
            ret = true;
        }
        else if (e->button == 2) 
        {
            view_add(clipboard, dwb.state.background_tabs);
            ret = true;
        }
        g_free(backup);
    }
    else if (e->button == 1 && e->type == GDK_BUTTON_PRESS && WEBVIEW(gl) != CURRENT_WEBVIEW()) 
    {
        dwb_focus_view(gl, "button_press");
    }
    else if (e->button == 3 && e->state & GDK_BUTTON1_MASK) 
        /* no popup if button 1 is presssed */
        ret = true;
    else if (e->button == 8) 
    {
        dwb_history_back();
        ret = true;
    }
    else if (e->button == 9) 
    {
        dwb_history_forward();
        ret = true;
    }
clean:
    g_object_unref(result);
    return ret;
}/*}}}*/

/* view_button_press_cb {{{*/
static gboolean
view_button_release_cb(WebKitWebView *web, GdkEventButton *e, GList *gl) 
{
    gboolean ret = false;
    char *uri =  NULL;
    WebKitHitTestResultContext context;

    WebKitHitTestResult *result = webkit_web_view_get_hit_test_result(web, e);
    g_object_get(result, "context", &context, NULL);

    if (EMIT_SCRIPT(BUTTON_RELEASE)) 
    {
        /**
         * Emitted when a mouse button was released
         *
         * @event buttonRelease
         * @memberOf signals
         * @param {signals~onButtonRelease} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when the load-status of a WebKitWebView changes
         * @callback signals~onButtonRelease
         * @param {WebKitWebView}  webview 
         *      The webview that emitted the signal
         * @param {WebKitHitTestResult} result
         *      The hit test result
         * @param {Object} event
         * @param {Number} event.button 
         *      The button that was pressed, usually a value between 1 and 5
         * @param {Modifier} event.state 
         *      A bitmask of {@link Enums and Flags.Modifier|Modifiers} pressed
         * @param {Number} event.time 
         *      The time in milliseconds when the button was pressed
         * @param {Number} event.x 
         *      x-position relative to the window
         * @param {Number} event.xRoot
         *      x-position relative to the screen
         * @param {Number} event.y 
         *      y-position relative to the window
         * @param {Number} event.yRoot
         *      y-position relative to the screen
         *
         * @returns {Boolean}
         *      Return true to prevent the default action
         * */
        char *json = util_create_json(7, 
                UINTEGER, "time", e->time, 
                DOUBLE,   "x", e->x, DOUBLE,            "y", e->y, 
                UINTEGER, "state", e->state, UINTEGER,  "button", e->button, 
                DOUBLE,   "xRoot", e->x_root, DOUBLE,   "yRoot", e->y_root);
        ScriptSignal signal = { SCRIPTS_WV(gl), { G_OBJECT(result) }, SCRIPTS_SIG_META(json, BUTTON_RELEASE, 1) };
        ret = scripts_emit(&signal);
        g_free(json);
        if (ret) 
            goto clean;
    }

    if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) 
    {
        if (e->button == 2 || (e->time - s_click_time < 200 && (e->button == 1 && e->state & GDK_CONTROL_MASK))) 
        {
            g_object_get(result, "link-uri", &uri, NULL);
            view_add(uri, dwb.state.background_tabs);
            view_disconnect_caret(web);
            ret = true;
        }
    }
clean:
    g_object_unref(result);
    return ret;
}/*}}}*/

/* view_close_web_view_cb(WebKitWebView *web, GList *gl) {{{*/
static gboolean 
view_close_web_view_cb(WebKitWebView *web, GList *gl) 
{
    view_remove(gl);
    return true;
}/*}}}*/

/* view_frame_committed_cb {{{*/
static void 
view_frame_committed_cb(WebKitWebFrame *frame, GList *gl) 
{
    if (EMIT_SCRIPT(FRAME_STATUS)) 
    {
        /**
         * Emitted when the load status of a frame changes
         *
         * @event frameStatus
         * @memberOf signals
         * @param {signals~onFrameStatus} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when the load-status of a WebKitWebFrame changes
         * @callback signals~onFrameStatus
         * @param {WebKitWebView}  webview 
         *      The webview that contains the frame
         * @param {WebKitWebFrame} frame 
         *      The frame that emitted the signal
         * */
        ScriptSignal signal = { SCRIPTS_WV(gl), 
            .objects = { G_OBJECT(frame) }, SCRIPTS_SIG_META(NULL, FRAME_STATUS, 1) };
        scripts_emit(&signal);
    }
}/*}}}*/

/* view_frame_created_cb {{{*/
static void 
view_frame_created_cb(WebKitWebView *wv, WebKitWebFrame *frame, GList *gl) 
{
    if (EMIT_SCRIPT(FRAME_CREATED)) 
    {
        /**
         * Emitted when a frame is created
         *
         * @event frameCreated
         * @memberOf signals
         * @param {signals~onFrameCreated} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when a WebKitWebFrame is created
         * @callback signals~onFrameCreated
         * @param {WebKitWebView}  webview 
         *      The webview that contains the frame
         * @param {WebKitWebFrame} frame 
         *      The created frame
         * */
        ScriptSignal signal = { SCRIPTS_WV(gl), .objects = { G_OBJECT(frame) }, SCRIPTS_SIG_META(NULL, FRAME_CREATED, 1) };
        scripts_emit(&signal);
    }
    g_signal_connect(frame, "notify::load-status", G_CALLBACK(view_frame_committed_cb), gl);
    VIEW(gl)->status->frames = g_slist_prepend(VIEW(gl)->status->frames, frame);
}/*}}}*/

/* view_console_message_cb(WebKitWebView *web, char *message, int line, char *sourceid, GList *gl) {{{*/
static gboolean 
view_console_message_cb(WebKitWebView *web, char *message, int line, char *sourceid, GList *gl) 
{
    return true;
}/*}}}*/

static gboolean 
new_window_cb(WebKitWebView *web, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *action,
    WebKitWebPolicyDecision *policy, GList *gl) 
{
    const char *uri = webkit_network_request_get_uri(request);
    dwb_change_mode(NORMAL_MODE, true);
    dwb_new_window(uri);
    gtk_widget_destroy(GTK_WIDGET(web));
    return true;
}

/* view_create_web_view_cb(WebKitWebView *, WebKitWebFrame *, GList *) {{{*/
static WebKitWebView * 
view_create_web_view_cb(WebKitWebView *web, WebKitWebFrame *frame, GList *gl) 
{
    if (dwb.misc.tabbed_browsing) 
    {
        GList *gl = view_add(NULL, dwb.state.background_tabs); 
        return WEBVIEW(gl);
    }
    else 
    {
        GtkWidget *wv = webkit_web_view_new();
        g_signal_connect(wv, "navigation-policy-decision-requested", G_CALLBACK(new_window_cb), gl);
        return WEBKIT_WEB_VIEW(wv);
    }
}/*}}}*/

/* view_download_requested_cb(WebKitWebView *, WebKitDownload *, GList *) {{{*/
static gboolean 
view_download_requested_cb(WebKitWebView *web, WebKitDownload *download, GList *gl) 
{
    if (EMIT_SCRIPT(DOWNLOAD)) 
    {
        /**
         * Emitted before a download starts
         *
         * @event download
         * @memberOf signals
         * @param {signals~onDownload} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called before a download starts, will be called before the
         * a path/application was chosen
         * @callback signals~onDownload
         * @param {WebKitWebView}  webview 
         *      The webview emitted the signal
         * @param {WebKitDownload} frame 
         *      The download
         * @param {Object} data 
         * @param {String} data.referer
         *      The referer
         * @param {String} data.mimeType
         *      The mime type of the download
         * */
        char *json = util_create_json(2, 
                CHAR, "referer", soup_get_header_from_request(webkit_download_get_network_request(download), "Referer"), 
                CHAR, "mimeType", dwb.state.mimetype_request);
        ScriptSignal signal = { SCRIPTS_WV(gl), .objects = { G_OBJECT(download) }, SCRIPTS_SIG_META(json, DOWNLOAD, 1) };
        SCRIPTS_EMIT_RETURN(signal, json, true);
    }
    download_get_path(gl, download);
    return true;
}/*}}}*/

gboolean 
view_delete_web_inspector(GtkWidget *widget, GdkEvent *event, WebKitWebInspector *inspector) 
{
    webkit_web_inspector_close(inspector);
    for (GList *gl = dwb.state.views; gl; gl=gl->next) 
    {
        if (VIEW(gl)->inspector_window == widget) 
        {
            VIEW(gl)->inspector_window = NULL; 
            break;
        }
    }
    gtk_widget_destroy(widget);

    return true;
}

/* view_inspect_web_view_cb(WebKitWebInspector *, WebKitWebView *, GList * *){{{*/
static WebKitWebView * 
view_inspect_web_view_cb(WebKitWebInspector *inspector, WebKitWebView *wv, GList *gl) 
{
    GtkWidget *window;
    if (dwb.gui.wid == 0) 
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    else
        window = gtk_plug_new(dwb.gui.wid);

    GtkWidget *webview = webkit_web_view_new();
    WebKitWebSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
    g_object_set(settings, "user-stylesheet-uri", GET_CHAR("user-stylesheet-uri"), NULL);
    gtk_window_set_title(GTK_WINDOW(window), "dwb-web-inspector");

    gtk_window_set_default_size(GTK_WINDOW(window), GET_INT("default-width"), GET_INT("default-height"));
    gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, NULL, GDK_HINT_MIN_SIZE);

    gtk_container_add(GTK_CONTAINER(window), webview);
    gtk_widget_show_all(window);
    VIEW(gl)->inspector_window = window;

    g_signal_connect(window, "delete-event", G_CALLBACK(view_delete_web_inspector), inspector);
    return WEBKIT_WEB_VIEW(webview);
}/*}}}*/

/* view_hovering_over_link_cb(WebKitWebView *, char *title, char *uri, GList *) {{{*/
static void 
view_hovering_over_link_cb(WebKitWebView *web, char *title, char *uri, GList *gl) 
{
    if (uri) 
    {
        VIEW(gl)->status->hover_uri = g_strdup(uri);
        dwb_set_status_bar_text(dwb.gui.urilabel, uri, &dwb.color.active_fg, NULL, false);
        if (! (dwb.state.bar_visible & BAR_VIS_STATUS)) 
        {
            WebKitDOMDocument *doc = webkit_web_view_get_dom_document(web);
            WebKitDOMElement *docelement = webkit_dom_document_get_document_element(doc);
            webkit_dom_node_append_child(WEBKIT_DOM_NODE(docelement), WEBKIT_DOM_NODE(VIEW(gl)->hover.element), NULL);
            webkit_dom_html_anchor_element_set_href(WEBKIT_DOM_HTML_ANCHOR_ELEMENT(VIEW(gl)->hover.anchor), uri);
            webkit_dom_html_element_set_inner_text(WEBKIT_DOM_HTML_ELEMENT(VIEW(gl)->hover.anchor), uri, NULL);
        }
    }
    else 
    {
        g_free(VIEW(gl)->status->hover_uri);
        VIEW(gl)->status->hover_uri = NULL;
        dwb_update_uri(gl, true);
        if (! (dwb.state.bar_visible & BAR_VIS_STATUS)) 
            dom_remove_from_parent(WEBKIT_DOM_NODE(VIEW(gl)->hover.element), NULL);
    }
}/*}}}*/


/* view_mime_type_policy_cb {{{*/
static gboolean 
view_mime_type_policy_cb(WebKitWebView *web, WebKitWebFrame *frame, WebKitNetworkRequest *request, char *mimetype, WebKitWebPolicyDecision *policy, GList *gl) 
{
    /* Prevents from segfault if proxy is set */
    if ( !mimetype || strlen(mimetype) == 0 )
        return false;

    if (EMIT_SCRIPT(MIME_TYPE)) 
    {
        /**
         * Emitted when a mime type decision is requested
         * @event mimeType
         * @memberOf signals
         * @param {signals~onMimeType} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when a mimetype decision has to be made
         * @callback signals~onMimeType
         * @param {WebKitWebView}  webview 
         *      The webview that contains the frame
         * @param {WebKitWebFrame}  frame 
         *      The frame that emitted the signal
         * @param {WebKitNetworkRequest}  request 
         *      The network request
         * @param {Object}  data 
         * @param {Boolean}  data.canShowMimeType
         *      Whether the webview can show the mimetype
         * @param {String}  data.mimeType
         *      The mime type
         *
         * @returns {Boolean}
         *      Return true to ignore the request
         * */
        char *json = util_create_json(2, CHAR, "mimeType", mimetype, BOOLEAN, "canShowMimeType", webkit_web_view_can_show_mime_type(web, mimetype));
        ScriptSignal signal = { SCRIPTS_WV(gl), { G_OBJECT(frame), G_OBJECT(request) }, SCRIPTS_SIG_META(json, MIME_TYPE, 2) };
        if (scripts_emit(&signal)) 
        {
            webkit_web_policy_decision_ignore(policy);
            g_free(json);
            return true;
        }
        g_free(json);
    }

    if (!webkit_web_view_can_show_mime_type(web, mimetype) ||  dwb.state.nv & OPEN_DOWNLOAD) 
    {
        dwb.state.mimetype_request = g_strdup(mimetype);
        webkit_web_policy_decision_download(policy);
        return true;
    }
    return  false;
}/*}}}*/

/* view_navigation_policy_cb {{{*/
static gboolean 
view_navigation_policy_cb(WebKitWebView *web, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *action,
    WebKitWebPolicyDecision *policy, GList *gl) 
{

    const char *uri = webkit_network_request_get_uri(request);
    gboolean ret = false;
    WebKitWebNavigationReason reason = webkit_web_navigation_action_get_reason(action);

    if (EMIT_SCRIPT(NAVIGATION)) 
    {
        /**
         * Emitted before a new site or a new frame is loaded
         * @event navigation
         * @memberOf signals
         * @param {signals~onNavigation} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called before a new navigation starts
         * @callback signals~onNavigation
         * @param {WebKitWebView}  webview 
         *      The webview that contains the frame, to check if a new site will
         *      be loaded check if {@linkcode webview.mainFrame == frame}
         * @param {WebKitWebFrame}  frame 
         *      The frame that emitted the signal
         * @param {WebKitNetworkRequest}  request 
         *      The network request
         * @param {WebKitWebNavigationAction}  action 
         *      A navigation action that started the request
         *
         * @returns {Boolean}
         *      Return true to stop the emission and prevent the site from being
         *      loaded
         * */
        ScriptSignal signal = { SCRIPTS_WV(gl), { G_OBJECT(frame), G_OBJECT(request), G_OBJECT(action) }, SCRIPTS_SIG_META(NULL, NAVIGATION, 3) };
        if (scripts_emit(&signal)) 
        {
            webkit_web_policy_decision_ignore(policy);
            return true;
        }
    }

    if (!g_str_has_prefix(uri, "http:") && !g_str_has_prefix(uri, "https:") &&
            !g_str_has_prefix(uri, "about:") && !g_str_has_prefix(uri, "dwb:") &&
            !g_str_has_prefix(uri, "file:")) 
    {
        if (dwb_scheme_handler(gl, request) == STATUS_OK)  
        {
            webkit_web_policy_decision_ignore(policy);
            return true;
        }
    }
    if (VIEW(gl)->status->deferred) 
    {
        if (gl != dwb.state.fview)
        {
            const char *stripped;

            VIEW(gl)->status->deferred_uri = g_strdup(uri);

            stripped = strstr(uri, "://");

            dwb_tab_label_set_text(gl, stripped ? stripped + 3 : uri);
            webkit_web_policy_decision_ignore(policy);
            return true;
        }
        else 
            VIEW(gl)->status->deferred = false;
    }


    /* Check if tab is locked */
    if (LP_LOCKED_URI(VIEW(gl))) 
    {
        const char *initial_uri = webkit_web_view_get_uri(web);
        if (g_strcmp0(uri, initial_uri)) 
        {
            dwb_set_error_message(dwb.state.fview, "Locked to domain %s, request aborted.", initial_uri);
            webkit_web_policy_decision_ignore(policy);
            return true;
        }

    }
    else if (LP_LOCKED_DOMAIN(VIEW(gl))) 
    {
        const char *host, *initial_host;
        WebKitWebFrame *mainframe = webkit_web_view_get_main_frame(web);
        WebKitWebDataSource *datasource = webkit_web_frame_get_data_source(mainframe);
        WebKitNetworkRequest *initial_request = webkit_web_data_source_get_request(datasource);

        initial_host = dwb_soup_get_host_from_request(initial_request);
        host         = dwb_soup_get_host_from_request(request);

        if (g_strcmp0(initial_host, host)) 
        {
            dwb_set_error_message(dwb.state.fview, "Locked to domain %s, request aborted.", initial_host);
            webkit_web_policy_decision_ignore(policy);
            return true;
        }
    }
    if (g_str_has_prefix(uri, "dwb:")) 
    {
        if (!html_load(gl, uri)) 
            fprintf(stderr, "Error loadings %s, maybe some files are missing.\n", uri);
        
        webkit_web_policy_decision_ignore(policy);
        return true;
    }
    if (webkit_web_navigation_action_get_button(action) == 2) 
    {
        gboolean background = dwb.state.nv & OPEN_BACKGROUND;
        dwb.state.nv = OPEN_NORMAL;
        webkit_web_policy_decision_ignore(policy);
        view_add(uri, background);
        return true;
    }
    /* In single mode (without tabs), this seems to be the only way of creating a
     * new window
     */
    if (dwb.state.nv & OPEN_NEW_WINDOW) 
    {
        if ((dwb.state.nv & OPEN_VIA_HINTS) || (dwb.state.nv & OPEN_EXPLICIT && reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)) 
        {
            dwb_change_mode(NORMAL_MODE, true);
            dwb_new_window(uri);
            webkit_web_policy_decision_ignore(policy);
            return true;
        }
    }
    GError *error = NULL;

    switch (reason) 
    {
        /* special handler for filesystem browsing */
        case WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED:
        case WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD:
        case WEBKIT_WEB_NAVIGATION_REASON_RELOAD:
            if (local_check_directory(gl, uri, reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED,  &error)) 
            {
                if (error != NULL) 
                {
                    dwb_set_error_message(gl, error->message);
                    g_clear_error(&error);
                }
                webkit_web_policy_decision_ignore(policy);
                return true;
            }
            dwb_soup_clean();
            break;
        case WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED: 
            if (dwb.state.mode == SEARCH_FIELD_MODE) 
            {
                webkit_web_policy_decision_ignore(policy);
                char *hint_search_submit = GET_CHAR("searchengine-submit-pattern");
                if (hint_search_submit == NULL) 
                    hint_search_submit = HINT_SEARCH_SUBMIT;
                
                char *tmp_uri = g_uri_unescape_string(uri, NULL);
                char *search_engine = dwb.state.form_name && !g_strrstr(tmp_uri, hint_search_submit) 
                    ? g_strdup_printf("%s?%s=%s", tmp_uri, dwb.state.form_name, hint_search_submit) 
                    : g_strdup(tmp_uri);
                dwb_save_searchengine(search_engine);
                webkit_web_policy_decision_ignore(policy);
                return true;
            }
            break;
        case WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED: 
            if (!dwb_confirm(dwb.state.fview, "Resubmit form [y/n]?")) 
            {
                webkit_web_policy_decision_ignore(policy);
                return true;
            }
            break;

        default: break;

    }
    if (!ret && frame == webkit_web_view_get_main_frame(web))
    {
        if (VIEW(gl)->js_base != NULL) 
        {
            JSValueUnprotect(JS_CONTEXT_REF(gl), VIEW(gl)->js_base);
            VIEW(gl)->js_base = NULL;
        }
    }
    return ret;
}/*}}}*/

/* view_new_window_policy_cb {{{*/
static gboolean 
view_new_window_policy_cb(WebKitWebView *web, WebKitWebFrame *frame,
    WebKitNetworkRequest *request, WebKitWebNavigationAction *action,
    WebKitWebPolicyDecision *decision, GList *gl) 
{
  return false;
}/*}}}*/

/* view_create_plugin_widget_cb {{{*/
static GtkWidget * 
view_create_plugin_widget_cb(WebKitWebView *web, char *mime_type, char *uri, GHashTable *param, GList *gl) 
{
    VIEW(gl)->plugins->status |= PLUGIN_STATUS_HAS_PLUGIN;
#if 0
    GHashTableIter iter;
    g_hash_table_iter_init(&iter, param);
    char *key, *value;
    while(g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer*)&value)) {
        printf("%s %s\n", key, value);
    }
#endif
    return NULL;
}/*}}}*/

/* view_scroll_cb(GtkWidget *w, GdkEventScroll * GList *) {{{*/
static gboolean
view_scroll_cb(GtkWidget *w, GdkEventScroll *e, GList *gl) 
{
    dwb_update_status_text(gl, NULL);
    if (GET_BOOL("enable-frame-flattening")) 
    {
        Arg a = { .n = e->direction };
        commands_scroll(NULL, &a);
        return true;
    }
    return false;
}/*}}}*/

/* view_scroll_cb(GtkWidget *w, GdkEventScroll * GList *) {{{*/
static gboolean
view_scroll_tab_cb(GtkWidget *w, GdkEventScroll *e, GList *gl) 
{
    const char *event;
    int pos = -1;
    if (e->direction == GDK_SCROLL_DOWN)
    {
        pos = util_modulo(g_list_position(dwb.state.views, dwb.state.fview) + 1, g_list_length(dwb.state.views));
        event = "focus_next";
    }
    else if (e->direction == GDK_SCROLL_UP)
    {
        pos = util_modulo(g_list_position(dwb.state.views, dwb.state.fview) - 1, g_list_length(dwb.state.views));
        event = "focus_prev";
    }
    if (pos != -1)
    {
        dwb_focus_view(g_list_nth(dwb.state.views, pos), event);
    }
    return false;
}/*}}}*/

/* view_value_changed_cb(GtkAdjustment *a, GList *gl) {{{ */
static gboolean
view_value_changed_cb(GtkAdjustment *a, GList *gl) 
{
    dwb_update_status_text(gl, a);
    return false;
}/* }}} */

/* view_icon_loaded(GtkAdjustment *a, GList *gl) {{{ */
void 
view_icon_loaded(WebKitWebView *web, char *icon_uri, GList *gl) 
{
    view_set_favicon(gl, true);
}/* }}} */

/* view_title_cb {{{*/
static void 
view_title_cb(WebKitWebView *web, GParamSpec *pspec, GList *gl) 
{
    dwb_update_status(gl, NULL);
}/*}}}*/

/* view_title_cb {{{*/
static void 
view_uri_cb(WebKitWebView *web, GParamSpec *pspec, GList *gl) 
{
    dwb_update_uri(gl, true);
}/*}}}*/

/* view_progress_cb {{{*/
static void 
view_progress_cb(WebKitWebView *web, GParamSpec *pspec, GList *gl) 
{
    View *v = gl->data;
    v->status->progress = webkit_web_view_get_progress(web) * 100;
    if (v->status->progress == 100) 
        v->status->progress = 0;
    
    dwb_update_status(gl, NULL);
}/*}}}*/

/* view_popup_activate_cb {{{*/
static void
view_popup_activate_cb(GtkMenuItem *menu, GList *gl) 
{
    GtkAction *a = NULL;
    const char *name;
    /* 
     * context-menu-action-2000       open link
     * context-menu-action-1          open link in window
     * context-menu-action-2          download linked file
     * context-menu-action-3          copy link location
     * context-menu-action-13         reload
     * context-menu-action-10         back
     * context-menu-action-11         forward
     * context-menu-action-12         stop
     * 
     * */

    a = gtk_activatable_get_related_action(GTK_ACTIVATABLE(menu));
    if (a == NULL) 
        return;
    name  = gtk_action_get_name(a);
    if (!g_strcmp0(name, "context-menu-action-3"))  /* copy link location */
    { 
        GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
        gtk_clipboard_set_text(clipboard, VIEW(gl)->status->hover_uri, -1);
    }
}/*}}}*/

void on_destroy(GtkWidget *item)
{
    puts("destroy");
}
/* view_populate_popup_cb {{{*/
static void 
view_populate_popup_cb(WebKitWebView *web, GtkWidget *menu, GList *gl) 
{
    if (EMIT_SCRIPT(CONTEXT_MENU))
    {
        ScriptSignal signal = { SCRIPTS_WV(gl), .objects = { G_OBJECT(menu) }, 
            SCRIPTS_SIG_META(NULL, CONTEXT_MENU, 1) };
        scripts_emit(&signal);
    }
    GList *items = gtk_container_get_children(GTK_CONTAINER(menu));
    for (GList *l = items; l; l=l->next) 
        g_signal_connect(l->data, "activate", G_CALLBACK(view_popup_activate_cb), gl);

    g_list_free(items);
}/*}}}*/

/* view_load_status_cb {{{*/
/* window-object-cleared is emmited in receivedFirstData which emits load-status
 * commited, so we don't connect to window-object-cleared but to
 * load_status_after instead */
static void 
view_load_status_after_cb(WebKitWebView *web, GParamSpec *pspec, GList *gl) 
{
    WebKitLoadStatus status = webkit_web_view_get_load_status(web);
    if (status == WEBKIT_LOAD_COMMITTED) 
    {
        VIEW(gl)->js_base = js_create_object(webkit_web_view_get_main_frame(web), dwb.misc.hints);
        js_call_as_function(webkit_web_view_get_main_frame(web), VIEW(gl)->js_base, "init", dwb.misc.hint_style, kJSTypeObject, NULL);
    }
}/*}}}*/
/* view_load_status_cb {{{*/

static void 
view_load_status_cb(WebKitWebView *web, GParamSpec *pspec, GList *gl) 
{
    WebKitLoadStatus status = webkit_web_view_get_load_status(web);
    View *v = VIEW(gl);
    char *host =  NULL;
    const char *uri = webkit_web_view_get_uri(web);


    switch (status) 
    {
        case WEBKIT_LOAD_PROVISIONAL: 
            dwb_clean_load_begin(gl);
            g_slist_free(v->status->frames);
            v->status->frames = NULL;
            v->status->frames = g_slist_prepend(v->status->frames, webkit_web_view_get_main_frame(WEBVIEW(gl)));
            break;
        case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT: 
            /* This is more or less a dummy call, to compile the script and speed up
             * execution time 
             * */
            js_call_as_function(webkit_web_view_get_main_frame(web), v->js_base, "createStyleSheet", NULL, kJSTypeUndefined, NULL);
            break;
        case WEBKIT_LOAD_COMMITTED: 
            if (v->status->scripts & SCRIPTS_ALLOWED_TEMPORARY) 
            {
                g_object_set(webkit_web_view_get_settings(web), "enable-scripts", false, NULL);
                v->status->scripts &= ~SCRIPTS_ALLOWED_TEMPORARY;
            }
            if (v->plugins->status & PLUGIN_STATUS_ENABLED) 
                plugins_connect(gl);
            if (VIEW(gl)->status->scripts & SCRIPTS_BLOCKED 
                    && (((host = dwb_get_host(web)) 
                            && (g_list_find_custom(dwb.fc.pers_scripts, host, (GCompareFunc)g_strcmp0) || g_list_find_custom(dwb.fc.pers_scripts, uri, (GCompareFunc)g_strcmp0) 
                                || g_list_find_custom(dwb.fc.tmp_scripts, host, (GCompareFunc)g_strcmp0) || g_list_find_custom(dwb.fc.tmp_scripts, uri, (GCompareFunc)g_strcmp0)))
                        ||  g_str_has_prefix(uri, "dwb:") || !g_strcmp0(uri, "Error"))) 
            {
                g_object_set(webkit_web_view_get_settings(web), "enable-scripts", true, NULL);
                v->status->scripts |= SCRIPTS_ALLOWED_TEMPORARY;
            }
            if (v->plugins->status & PLUGIN_STATUS_ENABLED 
                    && ( (host != NULL || (host = dwb_get_host(web))) 
                        && (g_list_find_custom(dwb.fc.pers_plugins, host, (GCompareFunc)g_strcmp0) || g_list_find_custom(dwb.fc.pers_plugins, uri, (GCompareFunc)g_strcmp0)
                            || g_list_find_custom(dwb.fc.tmp_plugins, host, (GCompareFunc)g_strcmp0) || g_list_find_custom(dwb.fc.tmp_plugins, uri, (GCompareFunc)g_strcmp0) )
                       )) 
            {
                plugins_disconnect(gl);
            }
            /**
             * Emitted when the load has just been commited, no data has been loaded
             * when this signal is emitted. This is the preferred signal for
             * injected scripts that do not manipulate the DOM.
             *
             * @event loadCommitted
             * @memberOf signals
             * @param {signals~onLoadCommitted} callback
             *      Callback function that will be called when the signal is emitted
             * */
            /**
             * Callback called when the load of a site has just been committed.
             * @callback signals~onLoadCommitted
             * @param {WebKitWebView}  webview 
             *      The webview that emitted the signal
             * */
            if (EMIT_SCRIPT(LOAD_COMMITTED)) 
            {
                ScriptSignal signal = { SCRIPTS_WV(gl), SCRIPTS_SIG_META(NULL, LOAD_COMMITTED, 0) };
                scripts_emit(&signal);
            }
            view_ssl_state(gl);
            g_free(host);
            break;
        case WEBKIT_LOAD_FINISHED:
            dwb_update_status(gl, NULL);
            /* TODO sqlite */
            if (!dwb.misc.private_browsing 
                    && g_strcmp0(uri, "about:blank")
                    && !g_str_has_prefix(uri, "dwb:") 
                    && (dwb_prepend_navigation(gl, &dwb.fc.history) == STATUS_OK)
                    && dwb.misc.sync_interval <= 0) 
            {
                util_file_add_navigation(dwb.files[FILES_HISTORY], dwb.fc.history->data, false, dwb.misc.history_length);
            }
            if (dwb.state.auto_insert_mode) 
                dwb_check_auto_insert(gl);

            /**
             * Emitted when the site has completely loaded.
             *
             * @event loadFinished
             * @memberOf signals
             * @param {signals~onLoadFinished} callback
             *      Callback function that will be called when the signal is emitted
             * */
            /**
             * Callback called when a site has completely loaded
             * @callback signals~onLoadFinished
             * @param {WebKitWebView}  webview 
             *      The webview that emitted the signal
             * */
            if (EMIT_SCRIPT(LOAD_FINISHED)) 
            {
                ScriptSignal signal = { SCRIPTS_WV(gl), SCRIPTS_SIG_META(NULL, LOAD_FINISHED, 0) };
                scripts_emit(&signal);
            }
            break;
        case WEBKIT_LOAD_FAILED: 
            break;
        default:
            break;
    }
    if (EMIT_SCRIPT(LOAD_STATUS)) 
    {
        /**
         * Emitted whenever the loadstatus changes
         *
         * @event loadStatus
         * @memberOf signals
         * @param {signals~onLoadStatus} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when the load-status of a WebKitWebView changes
         * @callback signals~onLoadStatus
         * @param {WebKitWebView}  webview 
         *      The webview that emitted the signal
         * */
        ScriptSignal signal = { SCRIPTS_WV(gl), SCRIPTS_SIG_META(NULL, LOAD_STATUS, 0) };
        scripts_emit(&signal);
    }
}/*}}}*/

/* view_load_error_cb {{{*/
static gboolean 
view_load_error_cb(WebKitWebView *web, WebKitWebFrame *frame, char *uri, GError *weberror, GList *gl) 
{
    if (weberror->code == WEBKIT_NETWORK_ERROR_CANCELLED 
            || weberror->code == WEBKIT_PLUGIN_ERROR_WILL_HANDLE_LOAD 
            || weberror->code == WEBKIT_POLICY_ERROR_FRAME_LOAD_INTERRUPTED_BY_POLICY_CHANGE) 
        return false;

    char *errorfile = util_get_data_file(ERROR_FILE, "html");
    if (errorfile == NULL) 
        return false;

    char *site, *search;
    char *content = util_get_file_content(errorfile, NULL);
    char *tmp = g_strdup(uri);
    char *res = tmp;
    if ( (tmp = strstr(tmp, "://")) != NULL) 
        tmp += 3;
    else 
        tmp = res;

    if (g_str_has_suffix(tmp, "/")) 
        tmp[strlen(tmp)-1] = '\0';

    char *icon = dwb_get_stock_item_base64_encoded(GTK_STOCK_DIALOG_ERROR);
    if ((search = dwb_get_searchengine(tmp)) != NULL)  
        site = g_strdup_printf(content, icon != NULL ? icon : "", uri, weberror->message, "visible", search);
    else 
        site = g_strdup_printf(content, icon != NULL ? icon : "", uri, weberror->message, "hidden", "");

    webkit_web_frame_load_alternate_string(frame, site, uri, uri);

    g_free(site);
    g_free(content);
    g_free(res);
    g_free(icon);
    g_free(search);
    return true;
}/*}}}*/

static gboolean 
view_motion_notify_cb(WebKitWebView *web, GdkEventButton *e, GList *gl) 
{
    if (EMIT_SCRIPT(MOUSE_MOVE)) {
        /**
         * Emitted when the mouse was moved
         *
         * @event mouseMove
         * @memberOf signals
         * @param {signals~onMouseMove} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when the mouse was moved
         * @callback signals~onMouseMove
         * @param {WebKitWebView}  webview 
         *      The webview that emitted the signal
         * @param {Object} event
         * @param {Number} event.button 
         *      The button that was pressed, usually a value between 1 and 5
         * @param {Modifier} event.state 
         *      A bitmask of {@link Enums and Flags.Modifier|Modifiers} pressed
         * @param {Number} event.time 
         *      The time in milliseconds when the button was pressed
         * @param {Number} event.x 
         *      x-position relative to the window
         * @param {Number} event.xRoot
         *      x-position relative to the screen
         * @param {Number} event.y 
         *      y-position relative to the window
         * @param {Number} event.yRoot
         *      y-position relative to the screen
         *
         * @returns {Boolean}
         *      Return true to prevent the movement
         * */
        char *json = util_create_json(7, 
                UINTEGER, "time", e->time, 
                DOUBLE,   "x", e->x, DOUBLE,            "y", e->y, 
                UINTEGER, "state", e->state, UINTEGER,  "button", e->button, 
                DOUBLE,   "xRoot", e->x_root, DOUBLE,   "yRoot", e->y_root);
        ScriptSignal signal = { SCRIPTS_WV(gl), SCRIPTS_SIG_META(json, MOUSE_MOVE, 0) };
        SCRIPTS_EMIT_RETURN(signal, json, true);
    }
    return false;
}

/* Entry */

/* dwb_entry_activate_cb (GtkWidget *entry) {{{*/
/*}}}*/

/* view_tab_button_press_cb(GtkWidget, GdkEventButton* , GList * ){{{*/
static gboolean
view_tab_button_press_cb(GtkWidget *tabevent, GdkEventButton *e, GList *gl) 
{
    if (EMIT_SCRIPT(TAB_BUTTON_PRESS)) 
    {
        /**
         * Emitted when the mouse was pressed on a tab label
         *
         * @event tabButtonPress
         * @memberOf signals
         * @param {signals~onTabButtonPress} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when a button was pressed on a tablabel
         * @callback signals~onTabButtonPress
         * @param {WebKitWebView}  webview 
         *      The webview that emitted the signal
         * @param {GtkEventBox} tabwidget
         *      The tabwidget
         * @param {Object} event
         * @param {Number} event.button 
         *      The button that was pressed, usually a value between 1 and 5
         * @param {Modifier} event.state 
         *      A bitmask of {@link Enums and Flags.Modifier|Modifiers} pressed
         * @param {Number} event.time 
         *      The time in milliseconds when the button was pressed
         * @param {ClickType} event.type 
         *      A {@link Enums and Flags.ClickType|ClickType}
         * @param {Number} event.x 
         *      x-position relative to the window
         * @param {Number} event.xRoot
         *      x-position relative to the screen
         * @param {Number} event.y 
         *      y-position relative to the window
         * @param {Number} event.yRoot
         *      y-position relative to the screen
         *
         * @returns {Boolean}
         *      Return true to prevent the default action
         * */
        char *json = util_create_json(8, 
                UINTEGER, "time", e->time, UINTEGER,    "type", e->type, 
                DOUBLE,   "x", e->x, DOUBLE,            "y", e->y, 
                UINTEGER, "state", e->state, UINTEGER,  "button", e->button, 
                DOUBLE,   "xRoot", e->x_root, DOUBLE,   "yRoot", e->y_root);
        ScriptSignal signal = { SCRIPTS_WV(gl), { G_OBJECT(tabevent) }, SCRIPTS_SIG_META(json, TAB_BUTTON_PRESS, 1) };
        SCRIPTS_EMIT_RETURN(signal, json, true);
    }
    if (e->button == 1 && e->type == GDK_BUTTON_PRESS) 
    {
        dwb_focus_view(gl, "tab_button_press");
        return true;
    }
    else if (e->button == 3 && e->type == GDK_BUTTON_PRESS) 
    {
        view_remove(gl);
        return true;
    }
    return false;
}/*}}}*/

/* DWB_WEB_VIEW {{{*/

void
view_set_favicon(GList *gl, gboolean web) 
{
    GdkPixbuf *pb = NULL, *old;
    GdkPixbuf *new = NULL;
    if ( (old = gtk_image_get_pixbuf(GTK_IMAGE(VIEW(gl)->tabicon))) ) 
        g_object_unref(old);

    if (web) 
    {
        pb = webkit_web_view_get_icon_pixbuf(WEBVIEW(gl));
        if (pb) 
        {
            new = gdk_pixbuf_scale_simple(pb, dwb.misc.bar_height, dwb.misc.bar_height, GDK_INTERP_BILINEAR);
            g_object_unref(pb);
        }
    }
    gtk_image_set_from_pixbuf(GTK_IMAGE(VIEW(gl)->tabicon), new);
}

/* view_modify_style(GList *gl, GdkColor *fg, GdkColor *bg, GdkColor *tabfg, GdkColor *tabbg, PangoFontDescription *fd, int fontsize) {{{*/
void 
view_modify_style(GList  *gl, DwbColor *tabfg, DwbColor *tabbg, PangoFontDescription *fd) 
{
    View *v = VIEW(gl);
    DWB_WIDGET_OVERRIDE_BACKGROUND(v->tabevent, GTK_STATE_NORMAL, tabbg);
    DWB_WIDGET_OVERRIDE_COLOR(v->tablabel, GTK_STATE_NORMAL, tabfg);
    DWB_WIDGET_OVERRIDE_FONT(v->tablabel, fd);
} /*}}}*/

/* view_set_active_style (GList *) {{{*/
void 
view_set_active_style(GList *gl) 
{
    view_modify_style(gl, &dwb.color.tab_active_fg, &dwb.color.tab_active_bg, dwb.font.fd_active);
}/*}}}*/

/* view_set_normal_style {{{*/
void 
view_set_normal_style(GList *gl) 
{
    if (g_list_position(dwb.state.views, gl) % 2 == 0)
        view_modify_style(gl, &dwb.color.tab_normal_fg1, &dwb.color.tab_normal_bg1, dwb.font.fd_inactive);
    else
        view_modify_style(gl, &dwb.color.tab_normal_fg2, &dwb.color.tab_normal_bg2, dwb.font.fd_inactive);
}/*}}}*/

/* view_init_settings {{{*/
static void 
view_init_settings(GList *gl) 
{
    View *v = gl->data;
    v->settings = webkit_web_settings_copy(dwb.state.web_settings);
    webkit_web_view_set_settings(WEBKIT_WEB_VIEW(v->web), v->settings);
    GList *l;
    for (l = g_hash_table_get_values(dwb.settings); l; l=l->next) 
    {
        WebSettings *s = l->data;
        if (s->apply & SETTING_PER_VIEW && s->func != NULL) 
            s->func(gl, s);
    }
    if (l != NULL) 
        g_list_free(l);
}/*}}}*/

static gboolean
view_key_ignore_cb(WebKitWebView *wv, GdkEventKey *e, GList *gl)
{
    // passthrough none
    if (dwb.misc.passthrough == PASSTHROUGH_NONE)
        return !(dwb.state.mode & INSERT_MODE);
    // passthrough all 
    else if (dwb.misc.passthrough == PASSTHROUGH_ALL)
        return false;

    if (e->state == GDK_CONTROL_MASK)
    {
        switch (e->keyval)
        {
            case GDK_KEY_c: 
            case GDK_KEY_x: 
            case GDK_KEY_v: 
            case GDK_KEY_a: 
                return false;
        }
    }
    else if (e->state == GDK_SHIFT_MASK)
    {
        switch (e->keyval)
        {
            case GDK_KEY_ISO_Left_Tab: 
            case GDK_KEY_space: 
                return false;
        }
    }
    else 
    {
        switch (e->keyval)
        {
            case GDK_KEY_Tab: 
            case GDK_KEY_Return: 
            case GDK_KEY_Escape: 
            case GDK_KEY_Up: 
            case GDK_KEY_Down: 
            case GDK_KEY_Left: 
            case GDK_KEY_Right: 
            case GDK_KEY_Page_Up: 
            case GDK_KEY_Page_Down: 
            case GDK_KEY_space: 
                return false;
        }
    }
    return !(dwb.state.mode & INSERT_MODE);
}

/* view_init_signals(View *v) {{{*/
static void
view_init_signals(GList *gl) 
{
    View *v = gl->data;
    GtkAdjustment *a = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(v->scroll));
    g_signal_connect(v->web, "key-press-event", G_CALLBACK(view_key_ignore_cb), gl);
    g_signal_connect(v->web, "key-release-event", G_CALLBACK(view_key_ignore_cb), gl);
    v->status->signals[SIG_BUTTON_PRESS]          = g_signal_connect(v->web, "button-press-event",                    G_CALLBACK(view_button_press_cb), gl);
    v->status->signals[SIG_BUTTON_RELEASE]        = g_signal_connect(v->web, "button-release-event",                  G_CALLBACK(view_button_release_cb), gl);
    v->status->signals[SIG_CLOSE_WEB_VIEW]        = g_signal_connect(v->web, "close-web-view",                        G_CALLBACK(view_close_web_view_cb), gl);
    v->status->signals[SIG_CONSOLE_MESSAGE]       = g_signal_connect(v->web, "console-message",                       G_CALLBACK(view_console_message_cb), gl);
    v->status->signals[SIG_CREATE_WEB_VIEW]       = g_signal_connect(v->web, "create-web-view",                       G_CALLBACK(view_create_web_view_cb), gl);
    v->status->signals[SIG_DOWNLOAD_REQUESTED]    = g_signal_connect(v->web, "download-requested",                    G_CALLBACK(view_download_requested_cb), gl);
    v->status->signals[SIG_HOVERING_OVER_LINK]    = g_signal_connect(v->web, "hovering-over-link",                    G_CALLBACK(view_hovering_over_link_cb), gl);
    v->status->signals[SIG_MIME_TYPE]             = g_signal_connect(v->web, "mime-type-policy-decision-requested",   G_CALLBACK(view_mime_type_policy_cb), gl);
    v->status->signals[SIG_NAVIGATION]            = g_signal_connect(v->web, "navigation-policy-decision-requested",  G_CALLBACK(view_navigation_policy_cb), gl);
    v->status->signals[SIG_NEW_WINDOW]            = g_signal_connect(v->web, "new-window-policy-decision-requested",  G_CALLBACK(view_new_window_policy_cb), gl);
    v->status->signals[SIG_RESOURCE_REQUEST]      = g_signal_connect(v->web, "resource-request-starting",             G_CALLBACK(view_resource_request_cb), gl);
#if WEBKIT_CHECK_VERSION(1, 8, 0)
    v->status->signals[SIG_DOCUMENT_FINISHED]     = g_signal_connect(v->web, "document-load-finished",             G_CALLBACK(view_document_finished), gl);
#endif
    v->status->signals[SIG_CREATE_PLUGIN_WIDGET]  = g_signal_connect(v->web, "create-plugin-widget",                  G_CALLBACK(view_create_plugin_widget_cb), gl);
    v->status->signals[SIG_FRAME_CREATED]         = g_signal_connect(v->web, "frame-created",                         G_CALLBACK(view_frame_created_cb), gl);

    v->status->signals[SIG_LOAD_STATUS]           = g_signal_connect(v->web, "notify::load-status",                   G_CALLBACK(view_load_status_cb), gl);
    v->status->signals[SIG_LOAD_ERROR]            = g_signal_connect(v->web, "load-error",                            G_CALLBACK(view_load_error_cb), gl);
    v->status->signals[SIG_LOAD_STATUS_AFTER]     = g_signal_connect(v->web, "notify::load-status",   G_CALLBACK(view_load_status_after_cb), gl);
    v->status->signals[SIG_POPULATE_POPUP]        = g_signal_connect(v->web, "populate-popup",                        G_CALLBACK(view_populate_popup_cb), gl);
    v->status->signals[SIG_PROGRESS]              = g_signal_connect(v->web, "notify::progress",               G_CALLBACK(view_progress_cb), gl);
    v->status->signals[SIG_TITLE]                 = g_signal_connect(v->web, "notify::title",                         G_CALLBACK(view_title_cb), gl);
    v->status->signals[SIG_URI]                   = g_signal_connect(v->web, "notify::uri",                           G_CALLBACK(view_uri_cb), gl);
    v->status->signals[SIG_SCROLL]                = g_signal_connect(v->web, "scroll-event",                          G_CALLBACK(view_scroll_cb), gl);
    v->status->signals[SIG_SCROLL_TAB]            = g_signal_connect(v->tabevent, "scroll-event",                          G_CALLBACK(view_scroll_tab_cb), gl);
    v->status->signals[SIG_VALUE_CHANGED]         = g_signal_connect(a,      "value-changed",                         G_CALLBACK(view_value_changed_cb), gl);
    if (GET_BOOL("enable-favicon")) 
        v->status->signals[SIG_ICON_LOADED]           = g_signal_connect(v->web, "icon-loaded",                           G_CALLBACK(view_icon_loaded), gl);

    /* v->status->signals[SIG_ENTRY_ACTIVATE]        = g_signal_connect(v->entry, "activate",                            G_CALLBACK(view_entry_activate_cb), gl); */

    v->status->signals[SIG_MOTION_NOTIFY] = g_signal_connect(v->web, "motion-notify-event", G_CALLBACK(view_motion_notify_cb), gl);
    v->status->signals[SIG_TAB_BUTTON_PRESS]      = g_signal_connect(v->tabevent, "button-press-event",               G_CALLBACK(view_tab_button_press_cb), gl);
    //WebKitWebFrame *frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(v->web));
    //v->status->signals[SIG_MAIN_FRAME_COMMITTED]  = g_signal_connect(frame, "load-committed", G_CALLBACK(view_main_frame_committed_cb), gl);

    /* WebInspector */
    WebKitWebInspector *inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(v->web));
    g_signal_connect(inspector, "inspect-web-view", G_CALLBACK(view_inspect_web_view_cb), gl);
} /*}}}*/

/* view_create_web_view(View *v)         return: GList * {{{*/
static View * 
view_create_web_view() 
{
    View *v = g_malloc(sizeof(View));

    ViewStatus *status = dwb_malloc(sizeof(ViewStatus));
    status->search_string = NULL;
    status->downloads = NULL;
    status->hover_uri = NULL;
#ifdef WITH_LIBSOUP_2_38
    status->request_uri = NULL;
#endif
    status->progress = 0;
    status->allowed_plugins = NULL;
    status->exc_style = NULL;
    status->styles = NULL;
    status->lockprotect = 0;
    status->frames = NULL;
    status->group = 0;
    status->deferred = GET_BOOL("load-on-focus");
    status->deferred_uri = NULL;

    v->js_base = NULL;
    v->inspector_window = NULL;
    v->plugins = plugins_new();
    for (int i=0; i<SIG_LAST; i++) 
        status->signals[i] = 0;
    v->status = status;

    v->web = webkit_web_view_new();

    /* Srolling */
    v->scroll = gtk_scrolled_window_new(NULL, NULL);
    gtk_container_add(GTK_CONTAINER(v->scroll), v->web);

#if !_HAS_GTK3
    if (! GET_BOOL("scrollbars")) 
    {
        WebKitWebFrame *frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(v->web));
        g_signal_connect(frame, "scrollbars-policy-changed", G_CALLBACK(gtk_true), NULL);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(v->scroll), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
    }
#endif


    /* Tabbar */
    v->tabevent = gtk_event_box_new();
#if _HAS_GTK3
    v->tabbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
#else 
    v->tabbox = gtk_hbox_new(false, 1);
#endif

    v->tablabel = gtk_label_new(NULL);

    gtk_label_set_use_markup(GTK_LABEL(v->tablabel), true);
    gtk_label_set_width_chars(GTK_LABEL(v->tablabel), 1);
    gtk_misc_set_alignment(GTK_MISC(v->tablabel), 0.0, 0.5);
    gtk_label_set_ellipsize(GTK_LABEL(v->tablabel), PANGO_ELLIPSIZE_END);

    gtk_box_pack_end(GTK_BOX(v->tabbox), v->tablabel, true, true, 3);

    GdkPixbuf *pb = gdk_pixbuf_new_from_xpm_data(dummy_icon);
    v->tabicon = gtk_image_new_from_pixbuf(pb);
    gtk_box_pack_end(GTK_BOX(v->tabbox), v->tabicon, false, false, 0);

    GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
    int padding = GET_INT("bars-padding");
    gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), padding, padding, padding, padding);
    gtk_container_add(GTK_CONTAINER(alignment), v->tabbox);
    gtk_container_add(GTK_CONTAINER(v->tabevent), alignment);

    DWB_WIDGET_OVERRIDE_FONT(v->tablabel, dwb.font.fd_inactive);

    gtk_widget_set_can_focus(v->scroll, false);
    gtk_widget_show_all(v->scroll);
    gtk_widget_show_all(v->tabevent);

    WebKitDOMDocument *doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(v->web));
    v->hover.element = webkit_dom_document_create_element(doc, "div", NULL);
    v->hover.anchor = webkit_dom_document_create_element(doc, "a", NULL);
    char *font = GET_CHAR("font-hidden-statusbar");
    char *bgcolor = GET_CHAR("background-color");
    char *fgcolor = GET_CHAR("foreground-color");
    gchar *style = g_strdup_printf(
            "bottom:0px;right:0px;position:fixed;z-index:1000;\
            text-overflow:ellipsis;white-space:nowrap;overflow:hidden;max-width:100%%;\
            border-left:1px solid #555;\
            border-top:1px solid #555;\
            padding-left:2px;\
            border-radius:5px 0px 0px 0px;letter-spacing:0px;background:%s;color:%s;font:%s", 
            bgcolor, 
            fgcolor, 
            font);
    webkit_dom_element_set_attribute(v->hover.element, "style", style, NULL);
    g_free(style);
    webkit_dom_element_set_attribute(v->hover.anchor, "style", "text-decoration:none;color:inherit;", NULL);
    webkit_dom_node_append_child(WEBKIT_DOM_NODE(v->hover.element), WEBKIT_DOM_NODE(v->hover.anchor), NULL);
    webkit_dom_html_element_set_id(WEBKIT_DOM_HTML_ELEMENT(v->hover.element), "dwb_hover_element");

    v->status_element = webkit_dom_document_create_element(doc, "div", NULL);
    style = g_strdup_printf(
            "bottom:0px;left:0px;position:fixed;z-index:1000;\
            border-right:1px solid #555;\
            border-top:1px solid #555;\
            padding-right:2px;\
            border-radius:0px 5px 0px 0px;letter-spacing:0px;background:%s;color:%s;font:%s;", 
            bgcolor, 
            fgcolor, 
            font);
    webkit_dom_element_set_attribute(v->status_element, "style", style, NULL);
    g_free(style);
    return v;
} /*}}}*/

/* view_clear_tab {{{*/
void 
view_clear_tab(GList *gl) 
{
    WebKitWebBackForwardList *bf_list = webkit_web_view_get_back_forward_list(WEBVIEW(gl));
    webkit_web_back_forward_list_clear(bf_list);
    dwb_load_uri(gl, "about:blank");
}/*}}}*/

void
view_clean(GList *gl) 
{
    View *v = VIEW(gl);
    /* Inspector */
    if (v->inspector_window != NULL) 
    {
        gtk_widget_destroy(v->inspector_window);
        v->inspector_window = NULL;
    }

    /* Favicon */ 
    GdkPixbuf *pb;
    if ( (pb = gtk_image_get_pixbuf(GTK_IMAGE(v->tabicon))) ) 
        g_object_unref(pb);

    gtk_widget_destroy(v->tabicon);
    gtk_widget_destroy(v->tablabel);
    gtk_widget_destroy(v->tabbox);
    gtk_widget_destroy(v->tabevent);

    plugins_free(v->plugins);
    g_slist_free(v->status->allowed_plugins);

    scripts_remove_tab(v->script_wv);

    if (v->status->exc_style) 
        g_object_unref(v->status->exc_style);
    
    if (v->status->styles) 
    {
        for (GSList *l = v->status->styles; l; l=l->next) 
            g_object_unref(l->data);
        g_slist_free(v->status->styles);
    }

    g_object_unref(v->hover.anchor);
    g_object_unref(v->hover.element);
    g_object_unref(v->status_element);

    g_object_unref(v->settings);
    /* Destroy widget */
    gtk_widget_destroy(v->web);
    gtk_widget_destroy(v->scroll);

    FREE0(v->status->deferred_uri);
    FREE0(v->status->hover_uri);
#ifdef WITH_LIBSOUP_2_38
    FREE0(v->status->request_uri);
#endif
    FREE0(v->status);

    FREE0(v);
}
/* view_remove (void) {{{*/
DwbStatus 
view_remove(GList *gl) 
{
    if (!dwb.state.views->next) 
    {
        if (dwb.misc.clt_policy == CLT_POLICY_CLOSE) 
        {
            view_clear_tab(dwb.state.fview);
            dwb_end(0);
            return STATUS_END;
        }
        if (dwb.misc.clt_policy == CLT_POLICY_CLEAR) 
            view_clear_tab(dwb.state.fview);
        return STATUS_OK;
    }
    if (dwb.state.nummod >= 0) 
    {
        gl = g_list_nth(dwb.state.views, dwb.state.nummod - 1);
        if (gl == NULL)
            return STATUS_OK;
    }
    else if (gl == NULL) 
        gl = dwb.state.fview;

    View *v = gl->data;
    int position = g_list_position(dwb.state.views, gl);
    /* Check for protected tab */
    if (LP_PROTECTED(v) && !dwb_confirm(dwb.state.fview, "Really close tab %d [y/n]?", position + 1) ) 
    {
        CLEAR_COMMAND_TEXT();
        return STATUS_OK;
    }
    /* Get new focused tab */
    GList *new_fview = dwb.state.fview;
    if (gl == dwb.state.fview) 
    {
        if (dwb.state.last_tab > position) 
            dwb.state.last_tab--;
        if (dwb.misc.tab_position & TAB_CLOSE_POSITION_RIGHTMOST) 
        {
            if (dwb.state.fview->next) 
                new_fview = g_list_last(dwb.state.views);
            else 
                new_fview = g_list_last(dwb.state.views)->prev;
            
        }
        else if (dwb.misc.tab_position & TAB_CLOSE_POSITION_RIGHT) 
        {
            if ( !(new_fview = dwb.state.fview->next) ) 
                new_fview = g_list_last(dwb.state.views)->prev;
        }
        else if (dwb.misc.tab_position & TAB_CLOSE_POSITION_LEFTMOST) 
        {
            if (dwb.state.fview == dwb.state.views) 
                new_fview = dwb.state.views->next;
            else 
                new_fview = dwb.state.views;
        }
        else 
        { 
            if ( !(new_fview = dwb.state.fview->prev) ) 
                new_fview = g_list_first(dwb.state.views)->next;
        }
        if (dwb.state.bar_visible & BAR_VIS_TOP && !gtk_widget_get_visible(dwb.gui.topbox)) 
            gtk_widget_show_all(dwb.gui.topbox);
    }
    else if (dwb.state.nummod - 1 == position) 
    {
        dwb.state.last_tab = -1;
    }

    /* Get History for the undo list */
    WebKitWebBackForwardList *bflist = webkit_web_view_get_back_forward_list(WEBKIT_WEB_VIEW(v->web));
    if ( bflist != NULL ) 
    {
        GList *store = NULL;

        for (int i = -webkit_web_back_forward_list_get_back_length(bflist); i<=0; i++) 
        {
            WebKitWebHistoryItem *item = webkit_web_back_forward_list_get_nth_item(bflist, i);
            Navigation *n = dwb_navigation_from_webkit_history_item(item);
            if (n) 
                store = g_list_append(store, n);
        }
        dwb.state.undo_list = g_list_prepend(dwb.state.undo_list, store);
    }

    dwb_focus_view(new_fview, "close_tab");
    view_clean(gl);

    dwb_source_remove();

    dwb.state.views = g_list_delete_link(dwb.state.views, gl);
    if (!dwb.state.views->next && !dwb.misc.show_single_tab)
        gtk_widget_hide(dwb.gui.topbox);

    gtk_widget_show(CURRENT_VIEW()->scroll);

    dwb_update_layout();
    dwb_update_status_text(dwb.state.fview, NULL);
    CLEAR_COMMAND_TEXT();

    return STATUS_OK;
}/*}}}*/

/* view_ssl_state (GList *gl) {{{*/
static void
view_ssl_state(GList *gl) 
{
    View *v = VIEW(gl);
    SslState ssl = SSL_NONE;

    const char *uri = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(v->web));
    if (uri && g_str_has_prefix(uri, "https")) 
    {
        SoupMessage *msg = dwb_soup_get_message(MAIN_FRAME_CAST(v->web));
        if (msg) 
        {
            ssl = soup_message_get_flags(msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED 
                ? SSL_TRUSTED 
                : SSL_UNTRUSTED;
        }
    }
    v->status->ssl = ssl;
    dwb_update_uri(gl, true);
}/*}}}*/


/* view_add(Arg *arg)               return: View *{{{*/
GList *  
view_add(const char *uri, gboolean background) 
{
    GList *ret = NULL;

    if (!dwb.misc.tabbed_browsing && dwb.state.views) 
    {
        dwb_new_window(uri);
        return NULL;
    }
    View *v = view_create_web_view();
    gtk_box_pack_end(GTK_BOX(dwb.gui.topbox), v->tabevent, true, true, 0);
    int length = g_list_length(dwb.state.views);
    if (dwb.state.fview) 
    {
        int p;
        if (dwb.misc.tab_position & TAB_POSITION_RIGHTMOST) 
            p = length;
        else if (dwb.misc.tab_position & TAB_POSITION_LEFT) 
            p = g_list_position(dwb.state.views, dwb.state.fview);
        else if (dwb.misc.tab_position & TAB_POSITION_LEFTMOST) 
            p = 0;
        else 
            p = g_list_position(dwb.state.views, dwb.state.fview) + 1;

        gtk_box_reorder_child(GTK_BOX(dwb.gui.topbox), v->tabevent, length - p);
        gtk_box_insert(GTK_BOX(dwb.gui.mainbox), v->scroll, true, true, 0, p, GTK_PACK_START);
        dwb.state.views = g_list_insert(dwb.state.views, v, p);
        ret = g_list_nth(dwb.state.views, p);
        scripts_create_tab(ret);

        if (background) 
        {
            view_set_normal_style(ret);
            gtk_widget_hide(v->scroll);
        }
        else 
        {
            dwb_focus_view(ret, "new_tab");
        }
    }
    else 
    {
        gtk_box_pack_start(GTK_BOX(dwb.gui.mainbox), v->scroll, true, true, 0);
        dwb.state.views = g_list_prepend(dwb.state.views, v);
        ret = dwb.state.views;
        scripts_create_tab(ret);
        dwb_focus(ret);
    }
    if (!dwb.misc.show_single_tab) 
    {
        if (length == 0) 
            gtk_widget_hide(dwb.gui.topbox);
        else if (length == 1 && (dwb.state.bar_visible & BAR_VIS_TOP)) 
            gtk_widget_show(dwb.gui.topbox);
    }
    if (EMIT_SCRIPT(CREATE_TAB)) 
    {
        /**
         * Emitted when a tab is created
         * @event createTab
         * @memberOf signals
         * @param {signals~onCreateTab} callback
         *      Callback function that will be called when the signal is emitted
         * */
        /**
         * Callback called when a tab is created
         * @callback signals~onCreateTab
         * @param {WebKitWebView}  webview 
         *      The webview that was created
         * @param {Object} data
         * @param {Object} data.uri 
         *      The uri that will be loaded or <i>null</i> if an empty tab or
         *      the startpage will be loaded
         * */
        char *json = util_create_json(1, CHAR, "uri", uri);
        ScriptSignal signal = { VIEW(ret)->script_wv, SCRIPTS_SIG_META(json, CREATE_TAB, 0) };
        scripts_emit(&signal);
        g_free(json);
    }


    view_init_signals(ret);
    view_init_settings(ret);
    if (GET_BOOL("adblocker"))
        adblock_connect(ret);

    dwb_update_layout();

    if (uri != NULL) 
        dwb_load_uri(ret, uri);

    return ret;
} /*}}}*/

/*}}}*/
