summaryrefslogtreecommitdiff
path: root/Tools/MiniBrowser
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/MiniBrowser')
-rw-r--r--Tools/MiniBrowser/MBToolbarItem.h27
-rw-r--r--Tools/MiniBrowser/MiniBrowserWebProcessPlugIn.h34
-rw-r--r--Tools/MiniBrowser/gtk/BrowserDownloadsBar.c1
-rw-r--r--Tools/MiniBrowser/gtk/BrowserSearchBar.c12
-rw-r--r--Tools/MiniBrowser/gtk/BrowserSettingsDialog.c55
-rw-r--r--Tools/MiniBrowser/gtk/BrowserTab.c552
-rw-r--r--Tools/MiniBrowser/gtk/BrowserTab.h60
-rw-r--r--Tools/MiniBrowser/gtk/BrowserWindow.c898
-rw-r--r--Tools/MiniBrowser/gtk/BrowserWindow.h11
-rw-r--r--Tools/MiniBrowser/gtk/CMakeLists.txt64
-rw-r--r--Tools/MiniBrowser/gtk/GNUmakefile.am75
-rw-r--r--Tools/MiniBrowser/gtk/main.c307
12 files changed, 1740 insertions, 356 deletions
diff --git a/Tools/MiniBrowser/MBToolbarItem.h b/Tools/MiniBrowser/MBToolbarItem.h
new file mode 100644
index 000000000..9971d4c10
--- /dev/null
+++ b/Tools/MiniBrowser/MBToolbarItem.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+@interface MBToolbarItem : NSToolbarItem
+@end
diff --git a/Tools/MiniBrowser/MiniBrowserWebProcessPlugIn.h b/Tools/MiniBrowser/MiniBrowserWebProcessPlugIn.h
new file mode 100644
index 000000000..ff16bf5dc
--- /dev/null
+++ b/Tools/MiniBrowser/MiniBrowserWebProcessPlugIn.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <WebKit/WKWebProcessPlugIn.h>
+
+#if WK_API_ENABLED
+
+@interface MiniBrowserWebProcessPlugIn : NSObject <WKWebProcessPlugIn>
+
+@end
+
+#endif // WK_API_ENABLED
diff --git a/Tools/MiniBrowser/gtk/BrowserDownloadsBar.c b/Tools/MiniBrowser/gtk/BrowserDownloadsBar.c
index 5f0e620e5..b0ec3ce14 100644
--- a/Tools/MiniBrowser/gtk/BrowserDownloadsBar.c
+++ b/Tools/MiniBrowser/gtk/BrowserDownloadsBar.c
@@ -23,6 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "cmakeconfig.h"
#include "BrowserDownloadsBar.h"
#include <glib/gi18n.h>
diff --git a/Tools/MiniBrowser/gtk/BrowserSearchBar.c b/Tools/MiniBrowser/gtk/BrowserSearchBar.c
index 7585b564d..22930c4c5 100644
--- a/Tools/MiniBrowser/gtk/BrowserSearchBar.c
+++ b/Tools/MiniBrowser/gtk/BrowserSearchBar.c
@@ -23,9 +23,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "cmakeconfig.h"
#include "BrowserSearchBar.h"
-
static const char *searchEntryFailedStyle = "GtkEntry#searchEntry {background-color: #ff6666;}";
struct _BrowserSearchBar {
@@ -63,7 +63,7 @@ static void doSearch(BrowserSearchBar *searchBar)
if (!gtk_entry_get_icon_stock(entry, GTK_ENTRY_ICON_SECONDARY))
gtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
- WebKitFindOptions options = WEBKIT_FIND_OPTIONS_NONE;
+ WebKitFindOptions options = WEBKIT_FIND_OPTIONS_WRAP_AROUND;
if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(searchBar->caseCheckButton)))
options |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE;
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(searchBar->begginigWordCheckButton)))
@@ -274,9 +274,13 @@ void browser_search_bar_open(BrowserSearchBar *searchBar)
{
g_return_if_fail(BROWSER_IS_SEARCH_BAR(searchBar));
+ GtkEntry *entry = GTK_ENTRY(searchBar->entry);
+
gtk_widget_show(GTK_WIDGET(searchBar));
- gtk_widget_grab_focus(GTK_WIDGET(searchBar->entry));
- gtk_editable_select_region(GTK_EDITABLE(searchBar->entry), 0, -1);
+ gtk_widget_grab_focus(GTK_WIDGET(entry));
+ gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
+ if (gtk_entry_get_text_length(entry))
+ doSearch(searchBar);
}
void browser_search_bar_close(BrowserSearchBar *searchBar)
diff --git a/Tools/MiniBrowser/gtk/BrowserSettingsDialog.c b/Tools/MiniBrowser/gtk/BrowserSettingsDialog.c
index 1e4dcd804..511b3db04 100644
--- a/Tools/MiniBrowser/gtk/BrowserSettingsDialog.c
+++ b/Tools/MiniBrowser/gtk/BrowserSettingsDialog.c
@@ -55,6 +55,34 @@ struct _BrowserSettingsDialogClass {
G_DEFINE_TYPE(BrowserSettingsDialog, browser_settings_dialog, GTK_TYPE_DIALOG)
+static const char *hardwareAccelerationPolicyToString(WebKitHardwareAccelerationPolicy policy)
+{
+ switch (policy) {
+ case WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS:
+ return "always";
+ case WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER:
+ return "never";
+ case WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND:
+ return "ondemand";
+ }
+
+ g_assert_not_reached();
+ return "ondemand";
+}
+
+static int stringToHardwareAccelerationPolicy(const char *policy)
+{
+ if (!g_ascii_strcasecmp(policy, "always"))
+ return WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS;
+ if (!g_ascii_strcasecmp(policy, "never"))
+ return WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER;
+ if (!g_ascii_strcasecmp(policy, "ondemand"))
+ return WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND;
+
+ g_warning("Invalid value %s for hardware-acceleration-policy setting valid values are always, never and ondemand", policy);
+ return -1;
+}
+
static void cellRendererChanged(GtkCellRenderer *renderer, const char *path, const GValue *value, BrowserSettingsDialog *dialog)
{
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(dialog->settingsList));
@@ -62,12 +90,21 @@ static void cellRendererChanged(GtkCellRenderer *renderer, const char *path, con
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, treePath);
+ gboolean updateTreeStore = TRUE;
char *name;
gtk_tree_model_get(model, &iter, SETTINGS_LIST_COLUMN_NAME, &name, -1);
- g_object_set_property(G_OBJECT(dialog->settings), name, value);
+ if (!g_strcmp0(name, "hardware-acceleration-policy")) {
+ int policy = stringToHardwareAccelerationPolicy(g_value_get_string(value));
+ if (policy != -1)
+ webkit_settings_set_hardware_acceleration_policy(dialog->settings, policy);
+ else
+ updateTreeStore = FALSE;
+ } else
+ g_object_set_property(G_OBJECT(dialog->settings), name, value);
g_free(name);
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, SETTINGS_LIST_COLUMN_VALUE, value, -1);
+ if (updateTreeStore)
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, SETTINGS_LIST_COLUMN_VALUE, value, -1);
gtk_tree_path_free(treePath);
}
@@ -134,10 +171,19 @@ static void browserSettingsDialogConstructed(GObject *object)
GParamSpec *property = properties[i];
const char *name = g_param_spec_get_name(property);
const char *nick = g_param_spec_get_nick(property);
+ char *blurb = g_markup_escape_text(g_param_spec_get_blurb(property), -1);
GValue value = { 0, { { 0 } } };
- g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(property));
- g_object_get_property(G_OBJECT(settings), name, &value);
+ if (!g_strcmp0(name, "hardware-acceleration-policy")) {
+ g_value_init(&value, G_TYPE_STRING);
+ g_value_set_string(&value, hardwareAccelerationPolicyToString(webkit_settings_get_hardware_acceleration_policy(settings)));
+ char *extendedBlutb = g_strdup_printf("%s (always, never or ondemand)", blurb);
+ g_free(blurb);
+ blurb = extendedBlutb;
+ } else {
+ g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(property));
+ g_object_get_property(G_OBJECT(settings), name, &value);
+ }
GtkAdjustment *adjustment = NULL;
if (G_PARAM_SPEC_VALUE_TYPE(property) == G_TYPE_UINT) {
@@ -146,7 +192,6 @@ static void browserSettingsDialogConstructed(GObject *object)
uIntProperty->maximum, 1, 1, 1);
}
- char *blurb = g_markup_escape_text(g_param_spec_get_blurb(property), -1);
GtkTreeIter iter;
gtk_list_store_append(model, &iter);
gtk_list_store_set(model, &iter,
diff --git a/Tools/MiniBrowser/gtk/BrowserTab.c b/Tools/MiniBrowser/gtk/BrowserTab.c
new file mode 100644
index 000000000..a6b2ea42e
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserTab.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H && defined(BUILDING_WITH_CMAKE)
+#include "cmakeconfig.h"
+#endif
+#include "BrowserTab.h"
+
+#include "BrowserSearchBar.h"
+#include "BrowserWindow.h"
+#include <string.h>
+
+enum {
+ PROP_0,
+
+ PROP_VIEW
+};
+
+struct _BrowserTab {
+ GtkBox parent;
+
+ WebKitWebView *webView;
+ BrowserSearchBar *searchBar;
+ GtkWidget *statusLabel;
+ gboolean wasSearchingWhenEnteredFullscreen;
+ gboolean inspectorIsVisible;
+ GtkWidget *fullScreenMessageLabel;
+ guint fullScreenMessageLabelId;
+
+ /* Tab Title */
+ GtkWidget *titleBox;
+ GtkWidget *titleLabel;
+ GtkWidget *titleSpinner;
+ GtkWidget *titleCloseButton;
+};
+
+struct _BrowserTabClass {
+ GtkBoxClass parent;
+};
+
+G_DEFINE_TYPE(BrowserTab, browser_tab, GTK_TYPE_BOX)
+
+static void titleChanged(WebKitWebView *webView, GParamSpec *pspec, BrowserTab *tab)
+{
+ const char *title = webkit_web_view_get_title(webView);
+ if (title && *title)
+ gtk_label_set_text(GTK_LABEL(tab->titleLabel), title);
+}
+
+static void isLoadingChanged(WebKitWebView *webView, GParamSpec *paramSpec, BrowserTab *tab)
+{
+ if (webkit_web_view_is_loading(webView)) {
+ gtk_spinner_start(GTK_SPINNER(tab->titleSpinner));
+ gtk_widget_show(tab->titleSpinner);
+ } else {
+ gtk_spinner_stop(GTK_SPINNER(tab->titleSpinner));
+ gtk_widget_hide(tab->titleSpinner);
+ }
+}
+
+static gboolean decidePolicy(WebKitWebView *webView, WebKitPolicyDecision *decision, WebKitPolicyDecisionType decisionType, BrowserTab *tab)
+{
+ if (decisionType != WEBKIT_POLICY_DECISION_TYPE_RESPONSE)
+ return FALSE;
+
+ WebKitResponsePolicyDecision *responseDecision = WEBKIT_RESPONSE_POLICY_DECISION(decision);
+ if (webkit_response_policy_decision_is_mime_type_supported(responseDecision))
+ return FALSE;
+
+ WebKitWebResource *mainResource = webkit_web_view_get_main_resource(webView);
+ WebKitURIRequest *request = webkit_response_policy_decision_get_request(responseDecision);
+ const char *requestURI = webkit_uri_request_get_uri(request);
+ if (g_strcmp0(webkit_web_resource_get_uri(mainResource), requestURI))
+ return FALSE;
+
+ webkit_policy_decision_download(decision);
+ return TRUE;
+}
+
+static void removeChildIfInfoBar(GtkWidget *child, GtkContainer *tab)
+{
+ if (GTK_IS_INFO_BAR(child))
+ gtk_container_remove(tab, child);
+}
+
+static void loadChanged(WebKitWebView *webView, WebKitLoadEvent loadEvent, BrowserTab *tab)
+{
+ if (loadEvent != WEBKIT_LOAD_STARTED)
+ return;
+
+ gtk_container_foreach(GTK_CONTAINER(tab), (GtkCallback)removeChildIfInfoBar, tab);
+}
+
+static GtkWidget *createInfoBarQuestionMessage(const char *title, const char *text)
+{
+ GtkWidget *dialog = gtk_info_bar_new_with_buttons("No", GTK_RESPONSE_NO, "Yes", GTK_RESPONSE_YES, NULL);
+ gtk_info_bar_set_message_type(GTK_INFO_BAR(dialog), GTK_MESSAGE_QUESTION);
+
+ GtkWidget *contentBox = gtk_info_bar_get_content_area(GTK_INFO_BAR(dialog));
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(contentBox), GTK_ORIENTATION_VERTICAL);
+ gtk_box_set_spacing(GTK_BOX(contentBox), 0);
+
+ GtkWidget *label = gtk_label_new(NULL);
+ gchar *markup = g_strdup_printf("<span size='xx-large' weight='bold'>%s</span>", title);
+ gtk_label_set_markup(GTK_LABEL(label), markup);
+ g_free(markup);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_label_set_selectable(GTK_LABEL(label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);
+ gtk_box_pack_start(GTK_BOX(contentBox), label, FALSE, FALSE, 2);
+ gtk_widget_show(label);
+
+ label = gtk_label_new(text);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_label_set_selectable(GTK_LABEL(label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);
+ gtk_box_pack_start(GTK_BOX(contentBox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ return dialog;
+}
+
+static void tlsErrorsDialogResponse(GtkWidget *dialog, gint response, BrowserTab *tab)
+{
+ if (response == GTK_RESPONSE_YES) {
+ const char *failingURI = (const char *)g_object_get_data(G_OBJECT(dialog), "failingURI");
+ GTlsCertificate *certificate = (GTlsCertificate *)g_object_get_data(G_OBJECT(dialog), "certificate");
+ SoupURI *uri = soup_uri_new(failingURI);
+ webkit_web_context_allow_tls_certificate_for_host(webkit_web_view_get_context(tab->webView), certificate, uri->host);
+ soup_uri_free(uri);
+ webkit_web_view_load_uri(tab->webView, failingURI);
+ }
+ gtk_widget_destroy(dialog);
+}
+
+static gboolean loadFailedWithTLSerrors(WebKitWebView *webView, const char *failingURI, GTlsCertificate *certificate, GTlsCertificateFlags errors, BrowserTab *tab)
+{
+ gchar *text = g_strdup_printf("Failed to load %s: Do you want to continue ignoring the TLS errors?", failingURI);
+ GtkWidget *dialog = createInfoBarQuestionMessage("Invalid TLS Certificate", text);
+ g_free(text);
+ g_object_set_data_full(G_OBJECT(dialog), "failingURI", g_strdup(failingURI), g_free);
+ g_object_set_data_full(G_OBJECT(dialog), "certificate", g_object_ref(certificate), g_object_unref);
+
+ g_signal_connect(dialog, "response", G_CALLBACK(tlsErrorsDialogResponse), tab);
+
+ gtk_box_pack_start(GTK_BOX(tab), dialog, FALSE, FALSE, 0);
+ gtk_box_reorder_child(GTK_BOX(tab), dialog, 0);
+ gtk_widget_show(dialog);
+
+ return TRUE;
+}
+
+static void permissionRequestDialogResponse(GtkWidget *dialog, gint response, WebKitPermissionRequest *request)
+{
+ switch (response) {
+ case GTK_RESPONSE_YES:
+ webkit_permission_request_allow(request);
+ break;
+ default:
+ webkit_permission_request_deny(request);
+ break;
+ }
+
+ gtk_widget_destroy(dialog);
+ g_object_unref(request);
+}
+
+static gboolean decidePermissionRequest(WebKitWebView *webView, WebKitPermissionRequest *request, BrowserTab *tab)
+{
+ const gchar *title = NULL;
+ gchar *text = NULL;
+
+ if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(request)) {
+ title = "Geolocation request";
+ text = g_strdup("Allow geolocation request?");
+ } else if (WEBKIT_IS_NOTIFICATION_PERMISSION_REQUEST(request)) {
+ title = "Notification request";
+ text = g_strdup("Allow notifications request?");
+ } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request)) {
+ title = "UserMedia request";
+ gboolean is_for_audio_device = webkit_user_media_permission_is_for_audio_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
+ gboolean is_for_video_device = webkit_user_media_permission_is_for_video_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request));
+ const char *mediaType = NULL;
+ if (is_for_audio_device) {
+ if (is_for_video_device)
+ mediaType = "audio/video";
+ else
+ mediaType = "audio";
+ } else if (is_for_video_device)
+ mediaType = "video";
+ text = g_strdup_printf("Allow access to %s device?", mediaType);
+ } else if (WEBKIT_IS_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request)) {
+ title = "Media plugin missing request";
+ text = g_strdup_printf("The media backend was unable to find a plugin to play the requested media:\n%s.\nAllow to search and install the missing plugin?",
+ webkit_install_missing_media_plugins_permission_request_get_description(WEBKIT_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request)));
+ } else
+ return FALSE;
+
+ GtkWidget *dialog = createInfoBarQuestionMessage(title, text);
+ g_free(text);
+ g_signal_connect(dialog, "response", G_CALLBACK(permissionRequestDialogResponse), g_object_ref(request));
+
+ gtk_box_pack_start(GTK_BOX(tab), dialog, FALSE, FALSE, 0);
+ gtk_box_reorder_child(GTK_BOX(tab), dialog, 0);
+ gtk_widget_show(dialog);
+
+ return TRUE;
+}
+
+#if GTK_CHECK_VERSION(3, 12, 0)
+static void colorChooserRGBAChanged(GtkColorChooser *colorChooser, GParamSpec *paramSpec, WebKitColorChooserRequest *request)
+{
+ GdkRGBA rgba;
+ gtk_color_chooser_get_rgba(colorChooser, &rgba);
+ webkit_color_chooser_request_set_rgba(request, &rgba);
+}
+
+static void popoverColorClosed(GtkWidget *popover, WebKitColorChooserRequest *request)
+{
+ webkit_color_chooser_request_finish(request);
+}
+
+static void colorChooserRequestFinished(WebKitColorChooserRequest *request, GtkWidget *popover)
+{
+ g_object_unref(request);
+ gtk_widget_destroy(popover);
+}
+
+static gboolean runColorChooserCallback(WebKitWebView *webView, WebKitColorChooserRequest *request, BrowserTab *tab)
+{
+ GtkWidget *popover = gtk_popover_new(GTK_WIDGET(webView));
+
+ GdkRectangle rectangle;
+ webkit_color_chooser_request_get_element_rectangle(request, &rectangle);
+ gtk_popover_set_pointing_to(GTK_POPOVER(popover), &rectangle);
+
+ GtkWidget *colorChooser = gtk_color_chooser_widget_new();
+ GdkRGBA rgba;
+ webkit_color_chooser_request_get_rgba(request, &rgba);
+ gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(colorChooser), &rgba);
+ g_signal_connect(colorChooser, "notify::rgba", G_CALLBACK(colorChooserRGBAChanged), request);
+ gtk_container_add(GTK_CONTAINER(popover), colorChooser);
+ gtk_widget_show(colorChooser);
+
+ g_object_ref(request);
+ g_signal_connect_object(popover, "hide", G_CALLBACK(popoverColorClosed), request, 0);
+ g_signal_connect_object(request, "finished", G_CALLBACK(colorChooserRequestFinished), popover, 0);
+
+ gtk_widget_show(popover);
+
+ return TRUE;
+}
+#endif /* GTK_CHECK_VERSION(3, 12, 0) */
+
+static gboolean inspectorOpenedInWindow(WebKitWebInspector *inspector, BrowserTab *tab)
+{
+ tab->inspectorIsVisible = TRUE;
+ return FALSE;
+}
+
+static gboolean inspectorClosed(WebKitWebInspector *inspector, BrowserTab *tab)
+{
+ tab->inspectorIsVisible = FALSE;
+ return FALSE;
+}
+
+static void browserTabSetProperty(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
+{
+ BrowserTab *tab = BROWSER_TAB(object);
+
+ switch (propId) {
+ case PROP_VIEW:
+ tab->webView = g_value_get_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ }
+}
+
+static void browserTabFinalize(GObject *gObject)
+{
+ BrowserTab *tab = BROWSER_TAB(gObject);
+
+ if (tab->fullScreenMessageLabelId)
+ g_source_remove(tab->fullScreenMessageLabelId);
+
+ G_OBJECT_CLASS(browser_tab_parent_class)->finalize(gObject);
+}
+
+static void browser_tab_init(BrowserTab *tab)
+{
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(tab), GTK_ORIENTATION_VERTICAL);
+}
+
+static void browserTabConstructed(GObject *gObject)
+{
+ BrowserTab *tab = BROWSER_TAB(gObject);
+
+ G_OBJECT_CLASS(browser_tab_parent_class)->constructed(gObject);
+
+ tab->searchBar = BROWSER_SEARCH_BAR(browser_search_bar_new(tab->webView));
+ gtk_box_pack_start(GTK_BOX(tab), GTK_WIDGET(tab->searchBar), FALSE, FALSE, 0);
+
+ GtkWidget *overlay = gtk_overlay_new();
+ gtk_box_pack_start(GTK_BOX(tab), overlay, TRUE, TRUE, 0);
+ gtk_widget_show(overlay);
+
+ tab->statusLabel = gtk_label_new(NULL);
+ gtk_widget_set_halign(tab->statusLabel, GTK_ALIGN_START);
+ gtk_widget_set_valign(tab->statusLabel, GTK_ALIGN_END);
+ gtk_widget_set_margin_left(tab->statusLabel, 1);
+ gtk_widget_set_margin_right(tab->statusLabel, 1);
+ gtk_widget_set_margin_top(tab->statusLabel, 1);
+ gtk_widget_set_margin_bottom(tab->statusLabel, 1);
+ gtk_overlay_add_overlay(GTK_OVERLAY(overlay), tab->statusLabel);
+
+ tab->fullScreenMessageLabel = gtk_label_new(NULL);
+ gtk_widget_set_halign(tab->fullScreenMessageLabel, GTK_ALIGN_CENTER);
+ gtk_widget_set_valign(tab->fullScreenMessageLabel, GTK_ALIGN_CENTER);
+ gtk_widget_set_no_show_all(tab->fullScreenMessageLabel, TRUE);
+ gtk_overlay_add_overlay(GTK_OVERLAY(overlay), tab->fullScreenMessageLabel);
+
+ gtk_container_add(GTK_CONTAINER(overlay), GTK_WIDGET(tab->webView));
+ gtk_widget_show(GTK_WIDGET(tab->webView));
+
+ tab->titleBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
+
+ GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_widget_set_halign(hbox, GTK_ALIGN_CENTER);
+
+ tab->titleSpinner = gtk_spinner_new();
+ gtk_box_pack_start(GTK_BOX(hbox), tab->titleSpinner, FALSE, FALSE, 0);
+
+ tab->titleLabel = gtk_label_new(NULL);
+ gtk_label_set_ellipsize(GTK_LABEL(tab->titleLabel), PANGO_ELLIPSIZE_END);
+ gtk_label_set_single_line_mode(GTK_LABEL(tab->titleLabel), TRUE);
+ gtk_misc_set_padding(GTK_MISC(tab->titleLabel), 0, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), tab->titleLabel, FALSE, FALSE, 0);
+ gtk_widget_show(tab->titleLabel);
+
+ gtk_box_pack_start(GTK_BOX(tab->titleBox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show(hbox);
+
+ tab->titleCloseButton = gtk_button_new();
+ g_signal_connect_swapped(tab->titleCloseButton, "clicked", G_CALLBACK(gtk_widget_destroy), tab);
+ gtk_button_set_relief(GTK_BUTTON(tab->titleCloseButton), GTK_RELIEF_NONE);
+ gtk_button_set_focus_on_click(GTK_BUTTON(tab->titleCloseButton), FALSE);
+
+ GtkWidget *image = gtk_image_new_from_icon_name("window-close-symbolic", GTK_ICON_SIZE_MENU);
+ gtk_container_add(GTK_CONTAINER(tab->titleCloseButton), image);
+ gtk_widget_show(image);
+
+ gtk_box_pack_start(GTK_BOX(tab->titleBox), tab->titleCloseButton, FALSE, FALSE, 0);
+ gtk_widget_show(tab->titleCloseButton);
+
+ g_signal_connect(tab->webView, "notify::title", G_CALLBACK(titleChanged), tab);
+ g_signal_connect(tab->webView, "notify::is-loading", G_CALLBACK(isLoadingChanged), tab);
+ g_signal_connect(tab->webView, "decide-policy", G_CALLBACK(decidePolicy), tab);
+ g_signal_connect(tab->webView, "load-changed", G_CALLBACK(loadChanged), tab);
+ g_signal_connect(tab->webView, "load-failed-with-tls-errors", G_CALLBACK(loadFailedWithTLSerrors), tab);
+ g_signal_connect(tab->webView, "permission-request", G_CALLBACK(decidePermissionRequest), tab);
+#if GTK_CHECK_VERSION(3, 12, 0)
+ g_signal_connect(tab->webView, "run-color-chooser", G_CALLBACK(runColorChooserCallback), tab);
+#endif
+
+ WebKitWebInspector *inspector = webkit_web_view_get_inspector(tab->webView);
+ g_signal_connect(inspector, "open-window", G_CALLBACK(inspectorOpenedInWindow), tab);
+ g_signal_connect(inspector, "closed", G_CALLBACK(inspectorClosed), tab);
+
+ if (webkit_web_view_is_editable(tab->webView))
+ webkit_web_view_load_html(tab->webView, "<html></html>", "file:///");
+}
+
+static void browser_tab_class_init(BrowserTabClass *klass)
+{
+ GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
+ gobjectClass->constructed = browserTabConstructed;
+ gobjectClass->set_property = browserTabSetProperty;
+ gobjectClass->finalize = browserTabFinalize;
+
+ g_object_class_install_property(
+ gobjectClass,
+ PROP_VIEW,
+ g_param_spec_object(
+ "view",
+ "View",
+ "The web view of this tab",
+ WEBKIT_TYPE_WEB_VIEW,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static char *getInternalURI(const char *uri)
+{
+ /* Internally we use minibrowser-about: as about: prefix is ignored by WebKit. */
+ if (g_str_has_prefix(uri, "about:") && !g_str_equal(uri, "about:blank"))
+ return g_strconcat(BROWSER_ABOUT_SCHEME, uri + strlen ("about"), NULL);
+
+ return g_strdup(uri);
+}
+
+/* Public API. */
+GtkWidget *browser_tab_new(WebKitWebView *view)
+{
+ g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(view), NULL);
+
+ return GTK_WIDGET(g_object_new(BROWSER_TYPE_TAB, "view", view, NULL));
+}
+
+WebKitWebView *browser_tab_get_web_view(BrowserTab *tab)
+{
+ g_return_val_if_fail(BROWSER_IS_TAB(tab), NULL);
+
+ return tab->webView;
+}
+
+void browser_tab_load_uri(BrowserTab *tab, const char *uri)
+{
+ g_return_if_fail(BROWSER_IS_TAB(tab));
+ g_return_if_fail(uri);
+
+ if (!g_str_has_prefix(uri, "javascript:")) {
+ char *internalURI = getInternalURI(uri);
+ webkit_web_view_load_uri(tab->webView, internalURI);
+ g_free(internalURI);
+ return;
+ }
+
+ webkit_web_view_run_javascript(tab->webView, strstr(uri, "javascript:"), NULL, NULL, NULL);
+}
+
+GtkWidget *browser_tab_get_title_widget(BrowserTab *tab)
+{
+ g_return_val_if_fail(BROWSER_IS_TAB(tab), NULL);
+
+ return tab->titleBox;
+}
+
+void browser_tab_set_status_text(BrowserTab *tab, const char *text)
+{
+ g_return_if_fail(BROWSER_IS_TAB(tab));
+
+ gtk_label_set_text(GTK_LABEL(tab->statusLabel), text);
+ gtk_widget_set_visible(tab->statusLabel, !!text);
+}
+
+void browser_tab_toggle_inspector(BrowserTab *tab)
+{
+ g_return_if_fail(BROWSER_IS_TAB(tab));
+
+ WebKitWebInspector *inspector = webkit_web_view_get_inspector(tab->webView);
+ if (!tab->inspectorIsVisible) {
+ webkit_web_inspector_show(inspector);
+ tab->inspectorIsVisible = TRUE;
+ } else
+ webkit_web_inspector_close(inspector);
+}
+
+void browser_tab_start_search(BrowserTab *tab)
+{
+ g_return_if_fail(BROWSER_IS_TAB(tab));
+
+ if (!gtk_widget_get_visible(GTK_WIDGET(tab->searchBar)))
+ browser_search_bar_open(tab->searchBar);
+}
+
+void browser_tab_stop_search(BrowserTab *tab)
+{
+ g_return_if_fail(BROWSER_IS_TAB(tab));
+
+ if (gtk_widget_get_visible(GTK_WIDGET(tab->searchBar)))
+ browser_search_bar_close(tab->searchBar);
+}
+
+void browser_tab_add_accelerators(BrowserTab *tab, GtkAccelGroup *accelGroup)
+{
+ g_return_if_fail(BROWSER_IS_TAB(tab));
+ g_return_if_fail(GTK_IS_ACCEL_GROUP(accelGroup));
+
+ browser_search_bar_add_accelerators(tab->searchBar, accelGroup);
+}
+
+static gboolean fullScreenMessageTimeoutCallback(BrowserTab *tab)
+{
+ gtk_widget_hide(tab->fullScreenMessageLabel);
+ tab->fullScreenMessageLabelId = 0;
+ return FALSE;
+}
+
+void browser_tab_enter_fullscreen(BrowserTab *tab)
+{
+ g_return_if_fail(BROWSER_IS_TAB(tab));
+
+ const gchar *titleOrURI = webkit_web_view_get_title(tab->webView);
+ if (!titleOrURI || !titleOrURI[0])
+ titleOrURI = webkit_web_view_get_uri(tab->webView);
+
+ gchar *message = g_strdup_printf("%s is now full screen. Press ESC or f to exit.", titleOrURI);
+ gtk_label_set_text(GTK_LABEL(tab->fullScreenMessageLabel), message);
+ g_free(message);
+
+ gtk_widget_show(tab->fullScreenMessageLabel);
+
+ tab->fullScreenMessageLabelId = g_timeout_add_seconds(2, (GSourceFunc)fullScreenMessageTimeoutCallback, tab);
+ g_source_set_name_by_id(tab->fullScreenMessageLabelId, "[WebKit] fullScreenMessageTimeoutCallback");
+
+ tab->wasSearchingWhenEnteredFullscreen = gtk_widget_get_visible(GTK_WIDGET(tab->searchBar));
+ browser_tab_stop_search(tab);
+}
+
+void browser_tab_leave_fullscreen(BrowserTab *tab)
+{
+ g_return_if_fail(BROWSER_IS_TAB(tab));
+
+ if (tab->fullScreenMessageLabelId) {
+ g_source_remove(tab->fullScreenMessageLabelId);
+ tab->fullScreenMessageLabelId = 0;
+ }
+
+ gtk_widget_hide(tab->fullScreenMessageLabel);
+
+ if (tab->wasSearchingWhenEnteredFullscreen) {
+ /* Opening the search bar steals the focus. Usually, we want
+ * this but not when coming back from fullscreen.
+ */
+ GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(tab)));
+ GtkWidget *focusWidget = gtk_window_get_focus(window);
+ browser_tab_start_search(tab);
+ gtk_window_set_focus(window, focusWidget);
+ }
+}
diff --git a/Tools/MiniBrowser/gtk/BrowserTab.h b/Tools/MiniBrowser/gtk/BrowserTab.h
new file mode 100644
index 000000000..9d5727591
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/BrowserTab.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BrowserTab_h
+#define BrowserTab_h
+
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+G_BEGIN_DECLS
+
+#define BROWSER_TYPE_TAB (browser_tab_get_type())
+#define BROWSER_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BROWSER_TYPE_TAB, BrowserTab))
+#define BROWSER_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BROWSER_TYPE_TAB, BrowserTabClass))
+#define BROWSER_IS_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_TYPE_TAB))
+#define BROWSER_IS_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_TYPE_TAB))
+#define BROWSER_TAB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BROWSER_TYPE_TAB, BrowserTabClass))
+
+typedef struct _BrowserTab BrowserTab;
+typedef struct _BrowserTabClass BrowserTabClass;
+
+GType browser_tab_get_type(void);
+
+GtkWidget* browser_tab_new(WebKitWebView*);
+WebKitWebView* browser_tab_get_web_view(BrowserTab*);
+void browser_tab_load_uri(BrowserTab*, const char* uri);
+GtkWidget *browser_tab_get_title_widget(BrowserTab*);
+void browser_tab_set_status_text(BrowserTab*, const char* text);
+void browser_tab_toggle_inspector(BrowserTab*);
+void browser_tab_start_search(BrowserTab*);
+void browser_tab_stop_search(BrowserTab*);
+void browser_tab_add_accelerators(BrowserTab*, GtkAccelGroup*);
+void browser_tab_enter_fullscreen(BrowserTab*);
+void browser_tab_leave_fullscreen(BrowserTab*);
+
+G_END_DECLS
+
+#endif
diff --git a/Tools/MiniBrowser/gtk/BrowserWindow.c b/Tools/MiniBrowser/gtk/BrowserWindow.c
index b8a0d056c..b80157fa2 100644
--- a/Tools/MiniBrowser/gtk/BrowserWindow.c
+++ b/Tools/MiniBrowser/gtk/BrowserWindow.c
@@ -25,23 +25,23 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H && defined(BUILDING_WITH_CMAKE)
+#include "cmakeconfig.h"
+#endif
#include "BrowserWindow.h"
#include "BrowserDownloadsBar.h"
#include "BrowserSearchBar.h"
#include "BrowserSettingsDialog.h"
+#include "BrowserTab.h"
#include <gdk/gdkkeysyms.h>
#include <string.h>
-enum {
- PROP_0,
-
- PROP_VIEW
-};
-
struct _BrowserWindow {
GtkWindow parent;
+ WebKitWebContext *webContext;
+
GtkAccelGroup *accelGroup;
GtkWidget *mainBox;
GtkWidget *toolbar;
@@ -50,18 +50,21 @@ struct _BrowserWindow {
GtkWidget *forwardItem;
GtkWidget *zoomInItem;
GtkWidget *zoomOutItem;
- GtkWidget *statusLabel;
+ GtkWidget *boldItem;
+ GtkWidget *italicItem;
+ GtkWidget *underlineItem;
+ GtkWidget *strikethroughItem;
GtkWidget *settingsDialog;
- WebKitWebView *webView;
+ GtkWidget *notebook;
+ BrowserTab *activeTab;
GtkWidget *downloadsBar;
- BrowserSearchBar *searchBar;
gboolean searchBarVisible;
+ gboolean fullScreenIsEnabled;
GdkPixbuf *favicon;
GtkWidget *reloadOrStopButton;
- GtkWidget *fullScreenMessageLabel;
GtkWindow *parentWindow;
- guint fullScreenMessageLabelId;
guint resetEntryProgressTimeoutId;
+ gchar *sessionFile;
};
struct _BrowserWindowClass {
@@ -69,36 +72,26 @@ struct _BrowserWindowClass {
};
static const char *defaultWindowTitle = "WebKitGTK+ MiniBrowser";
-static const char *miniBrowserAboutScheme = "minibrowser-about";
static const gdouble minimumZoomLevel = 0.5;
static const gdouble maximumZoomLevel = 3;
+static const gdouble defaultZoomLevel = 1;
static const gdouble zoomStep = 1.2;
static gint windowCount = 0;
G_DEFINE_TYPE(BrowserWindow, browser_window, GTK_TYPE_WINDOW)
-static char *getInternalURI(const char *uri)
-{
- // Internally we use minibrowser-about: as about: prefix is ignored by WebKit.
- if (g_str_has_prefix(uri, "about:") && !g_str_equal(uri, "about:blank"))
- return g_strconcat(miniBrowserAboutScheme, uri + strlen ("about"), NULL);
-
- return g_strdup(uri);
-}
-
static char *getExternalURI(const char *uri)
{
- // From the user point of view we support about: prefix.
- if (g_str_has_prefix(uri, miniBrowserAboutScheme))
- return g_strconcat("about", uri + strlen(miniBrowserAboutScheme), NULL);
+ /* From the user point of view we support about: prefix. */
+ if (uri && g_str_has_prefix(uri, BROWSER_ABOUT_SCHEME))
+ return g_strconcat("about", uri + strlen(BROWSER_ABOUT_SCHEME), NULL);
return g_strdup(uri);
}
static void browserWindowSetStatusText(BrowserWindow *window, const char *text)
{
- gtk_label_set_text(GTK_LABEL(window->statusLabel), text);
- gtk_widget_set_visible(window->statusLabel, !!text);
+ browser_tab_set_status_text(window->activeTab, text);
}
static void resetStatusText(GtkWidget *widget, BrowserWindow *window)
@@ -113,20 +106,23 @@ static void activateUriEntryCallback(BrowserWindow *window)
static void reloadOrStopCallback(BrowserWindow *window)
{
- if (webkit_web_view_is_loading(window->webView))
- webkit_web_view_stop_loading(window->webView);
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ if (webkit_web_view_is_loading(webView))
+ webkit_web_view_stop_loading(webView);
else
- webkit_web_view_reload(window->webView);
+ webkit_web_view_reload(webView);
}
static void goBackCallback(BrowserWindow *window)
{
- webkit_web_view_go_back(window->webView);
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_go_back(webView);
}
static void goForwardCallback(BrowserWindow *window)
{
- webkit_web_view_go_forward(window->webView);
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_go_forward(webView);
}
static void settingsCallback(BrowserWindow *window)
@@ -136,7 +132,8 @@ static void settingsCallback(BrowserWindow *window)
return;
}
- window->settingsDialog = browser_settings_dialog_new(webkit_web_view_get_settings(window->webView));
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ window->settingsDialog = browser_settings_dialog_new(webkit_web_view_get_settings(webView));
gtk_window_set_transient_for(GTK_WINDOW(window->settingsDialog), GTK_WINDOW(window));
g_object_add_weak_pointer(G_OBJECT(window->settingsDialog), (gpointer *)&window->settingsDialog);
gtk_widget_show(window->settingsDialog);
@@ -145,14 +142,23 @@ static void settingsCallback(BrowserWindow *window)
static void webViewURIChanged(WebKitWebView *webView, GParamSpec *pspec, BrowserWindow *window)
{
char *externalURI = getExternalURI(webkit_web_view_get_uri(webView));
- gtk_entry_set_text(GTK_ENTRY(window->uriEntry), externalURI);
- g_free(externalURI);
+ if (externalURI) {
+ gtk_entry_set_text(GTK_ENTRY(window->uriEntry), externalURI);
+ g_free(externalURI);
+ } else
+ gtk_entry_set_text(GTK_ENTRY(window->uriEntry), "");
}
static void webViewTitleChanged(WebKitWebView *webView, GParamSpec *pspec, BrowserWindow *window)
{
const char *title = webkit_web_view_get_title(webView);
- gtk_window_set_title(GTK_WINDOW(window), title ? title : defaultWindowTitle);
+ if (!title)
+ title = defaultWindowTitle;
+ char *privateTitle = NULL;
+ if (webkit_web_view_is_ephemeral(webView))
+ privateTitle = g_strdup_printf("[Private] %s", title);
+ gtk_window_set_title(GTK_WINDOW(window), privateTitle ? privateTitle : title);
+ g_free(privateTitle);
}
static gboolean resetEntryProgress(BrowserWindow *window)
@@ -196,7 +202,8 @@ static void browserWindowHistoryItemActivated(BrowserWindow *window, GtkAction *
if (!item)
return;
- webkit_web_view_go_to_back_forward_list_item(window->webView, item);
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_go_to_back_forward_list_item(webView, item);
}
static GtkWidget *browserWindowCreateBackForwardMenu(BrowserWindow *window, GList *list)
@@ -230,8 +237,9 @@ static GtkWidget *browserWindowCreateBackForwardMenu(BrowserWindow *window, GLis
static void browserWindowUpdateNavigationActions(BrowserWindow *window, WebKitBackForwardList *backForwadlist)
{
- gtk_widget_set_sensitive(window->backItem, webkit_web_view_can_go_back(window->webView));
- gtk_widget_set_sensitive(window->forwardItem, webkit_web_view_can_go_forward(window->webView));
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ gtk_widget_set_sensitive(window->backItem, webkit_web_view_can_go_back(webView));
+ gtk_widget_set_sensitive(window->forwardItem, webkit_web_view_can_go_forward(webView));
GList *list = g_list_reverse(webkit_back_forward_list_get_back_list_with_limit(backForwadlist, 10));
gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(window->backItem),
@@ -249,24 +257,22 @@ static void backForwadlistChanged(WebKitBackForwardList *backForwadlist, WebKitB
browserWindowUpdateNavigationActions(window, backForwadlist);
}
-static void geolocationRequestDialogCallback(GtkDialog *dialog, gint response, WebKitPermissionRequest *request)
+static void webViewClose(WebKitWebView *webView, BrowserWindow *window)
{
- switch (response) {
- case GTK_RESPONSE_YES:
- webkit_permission_request_allow(request);
- break;
- default:
- webkit_permission_request_deny(request);
- break;
+ int tabsCount = gtk_notebook_get_n_pages(GTK_NOTEBOOK(window->notebook));
+ if (tabsCount == 1) {
+ gtk_widget_destroy(GTK_WIDGET(window));
+ return;
}
- gtk_widget_destroy(GTK_WIDGET(dialog));
- g_object_unref(request);
-}
-
-static void webViewClose(WebKitWebView *webView, BrowserWindow *window)
-{
- gtk_widget_destroy(GTK_WIDGET(window));
+ int i;
+ for (i = 0; i < tabsCount; ++i) {
+ BrowserTab *tab = (BrowserTab *)gtk_notebook_get_nth_page(GTK_NOTEBOOK(window->notebook), i);
+ if (browser_tab_get_web_view(tab) == webView) {
+ gtk_widget_destroy(GTK_WIDGET(tab));
+ return;
+ }
+ }
}
static void webViewRunAsModal(WebKitWebView *webView, BrowserWindow *window)
@@ -297,65 +303,33 @@ static void webViewReadyToShow(WebKitWebView *webView, BrowserWindow *window)
gtk_widget_show(GTK_WIDGET(window));
}
-static gboolean fullScreenMessageTimeoutCallback(BrowserWindow *window)
+static GtkWidget *webViewCreate(WebKitWebView *webView, WebKitNavigationAction *navigation, BrowserWindow *window)
{
- gtk_widget_hide(window->fullScreenMessageLabel);
- window->fullScreenMessageLabelId = 0;
- return FALSE;
+ WebKitWebView *newWebView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(webView));
+ webkit_web_view_set_settings(newWebView, webkit_web_view_get_settings(webView));
+
+ GtkWidget *newWindow = browser_window_new(GTK_WINDOW(window), window->webContext);
+ browser_window_append_view(BROWSER_WINDOW(newWindow), newWebView);
+ g_signal_connect(newWebView, "ready-to-show", G_CALLBACK(webViewReadyToShow), newWindow);
+ g_signal_connect(newWebView, "run-as-modal", G_CALLBACK(webViewRunAsModal), newWindow);
+ g_signal_connect(newWebView, "close", G_CALLBACK(webViewClose), newWindow);
+ return GTK_WIDGET(newWebView);
}
static gboolean webViewEnterFullScreen(WebKitWebView *webView, BrowserWindow *window)
{
- gchar *titleOrURI = g_strdup(webkit_web_view_get_title(window->webView));
- if (!titleOrURI)
- titleOrURI = getExternalURI(webkit_web_view_get_uri(window->webView));
- gchar *message = g_strdup_printf("%s is now full screen. Press ESC or f to exit.", titleOrURI);
- gtk_label_set_text(GTK_LABEL(window->fullScreenMessageLabel), message);
- g_free(titleOrURI);
- g_free(message);
-
- gtk_widget_show(window->fullScreenMessageLabel);
-
- window->fullScreenMessageLabelId = g_timeout_add_seconds(2, (GSourceFunc)fullScreenMessageTimeoutCallback, window);
- g_source_set_name_by_id(window->fullScreenMessageLabelId, "[WebKit] fullScreenMessageTimeoutCallback");
gtk_widget_hide(window->toolbar);
- window->searchBarVisible = gtk_widget_get_visible(GTK_WIDGET(window->searchBar));
- browser_search_bar_close(window->searchBar);
-
+ browser_tab_enter_fullscreen(window->activeTab);
return FALSE;
}
static gboolean webViewLeaveFullScreen(WebKitWebView *webView, BrowserWindow *window)
{
- if (window->fullScreenMessageLabelId) {
- g_source_remove(window->fullScreenMessageLabelId);
- window->fullScreenMessageLabelId = 0;
- }
- gtk_widget_hide(window->fullScreenMessageLabel);
+ browser_tab_leave_fullscreen(window->activeTab);
gtk_widget_show(window->toolbar);
- if (window->searchBarVisible) {
- // Opening the search bar steals the focus. Usually, we want
- // this but not when coming back from fullscreen.
- GtkWidget *focusWidget = gtk_window_get_focus(GTK_WINDOW(window));
- browser_search_bar_open(window->searchBar);
- gtk_window_set_focus(GTK_WINDOW(window), focusWidget);
- }
-
return FALSE;
}
-static GtkWidget *webViewCreate(WebKitWebView *webView, BrowserWindow *window)
-{
- WebKitWebView *newWebView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(webView));
- webkit_web_view_set_settings(newWebView, webkit_web_view_get_settings(webView));
-
- GtkWidget *newWindow = browser_window_new(newWebView, GTK_WINDOW(window));
- g_signal_connect(newWebView, "ready-to-show", G_CALLBACK(webViewReadyToShow), newWindow);
- g_signal_connect(newWebView, "run-as-modal", G_CALLBACK(webViewRunAsModal), newWindow);
- g_signal_connect(newWebView, "close", G_CALLBACK(webViewClose), newWindow);
- return GTK_WIDGET(newWebView);
-}
-
static gboolean webViewLoadFailed(WebKitWebView *webView, WebKitLoadEvent loadEvent, const char *failingURI, GError *error, BrowserWindow *window)
{
gtk_entry_set_progress_fraction(GTK_ENTRY(window->uriEntry), 0.);
@@ -364,57 +338,28 @@ static gboolean webViewLoadFailed(WebKitWebView *webView, WebKitLoadEvent loadEv
static gboolean webViewDecidePolicy(WebKitWebView *webView, WebKitPolicyDecision *decision, WebKitPolicyDecisionType decisionType, BrowserWindow *window)
{
- switch (decisionType) {
- case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: {
- WebKitNavigationPolicyDecision *navigationDecision = WEBKIT_NAVIGATION_POLICY_DECISION(decision);
- if (webkit_navigation_policy_decision_get_navigation_type(navigationDecision) != WEBKIT_NAVIGATION_TYPE_LINK_CLICKED
- || webkit_navigation_policy_decision_get_mouse_button(navigationDecision) != GDK_BUTTON_MIDDLE)
- return FALSE;
-
- // Opening a new window if link clicked with the middle button.
- WebKitWebView *newWebView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(webkit_web_view_get_context(webView)));
- GtkWidget *newWindow = browser_window_new(newWebView, GTK_WINDOW(window));
- webkit_web_view_load_request(newWebView, webkit_navigation_policy_decision_get_request(navigationDecision));
- gtk_widget_show(newWindow);
-
- webkit_policy_decision_ignore(decision);
- return TRUE;
- }
- case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: {
- WebKitResponsePolicyDecision *responseDecision = WEBKIT_RESPONSE_POLICY_DECISION(decision);
- if (webkit_response_policy_decision_is_mime_type_supported(responseDecision))
- return FALSE;
-
- WebKitWebResource *mainResource = webkit_web_view_get_main_resource(webView);
- WebKitURIRequest *request = webkit_response_policy_decision_get_request(responseDecision);
- const char *requestURI = webkit_uri_request_get_uri(request);
- if (g_strcmp0(webkit_web_resource_get_uri(mainResource), requestURI))
- return FALSE;
-
- webkit_policy_decision_download(decision);
- return TRUE;
- }
- case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
- default:
+ if (decisionType != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION)
return FALSE;
- }
-}
-static gboolean webViewDecidePermissionRequest(WebKitWebView *webView, WebKitPermissionRequest *request, BrowserWindow *window)
-{
- if (!WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(request))
+ WebKitNavigationAction *navigationAction = webkit_navigation_policy_decision_get_navigation_action(WEBKIT_NAVIGATION_POLICY_DECISION(decision));
+ if (webkit_navigation_action_get_navigation_type(navigationAction) != WEBKIT_NAVIGATION_TYPE_LINK_CLICKED
+ || webkit_navigation_action_get_mouse_button(navigationAction) != GDK_BUTTON_MIDDLE)
return FALSE;
- GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_YES_NO,
- "Geolocation request");
+ /* Multiple tabs are not allowed in editor mode. */
+ if (webkit_web_view_is_editable(webView))
+ return FALSE;
- gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "Allow geolocation request?");
- g_signal_connect(dialog, "response", G_CALLBACK(geolocationRequestDialogCallback), g_object_ref(request));
- gtk_widget_show(dialog);
+ /* Opening a new tab if link clicked with the middle button. */
+ WebKitWebView *newWebView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "web-context", webkit_web_view_get_context(webView),
+ "settings", webkit_web_view_get_settings(webView),
+ "user-content-manager", webkit_web_view_get_user_content_manager(webView),
+ NULL));
+ browser_window_append_view(window, newWebView);
+ webkit_web_view_load_request(newWebView, webkit_navigation_action_get_request(navigationAction));
+ webkit_policy_decision_ignore(decision);
return TRUE;
}
@@ -429,16 +374,53 @@ static void webViewMouseTargetChanged(WebKitWebView *webView, WebKitHitTestResul
static gboolean browserWindowCanZoomIn(BrowserWindow *window)
{
- gdouble zoomLevel = webkit_web_view_get_zoom_level(window->webView) * zoomStep;
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ gdouble zoomLevel = webkit_web_view_get_zoom_level(webView) * zoomStep;
return zoomLevel < maximumZoomLevel;
}
static gboolean browserWindowCanZoomOut(BrowserWindow *window)
{
- gdouble zoomLevel = webkit_web_view_get_zoom_level(window->webView) / zoomStep;
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ gdouble zoomLevel = webkit_web_view_get_zoom_level(webView) / zoomStep;
return zoomLevel > minimumZoomLevel;
}
+static gboolean browserWindowZoomIn(BrowserWindow *window)
+{
+ if (browserWindowCanZoomIn(window)) {
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ gdouble zoomLevel = webkit_web_view_get_zoom_level(webView) * zoomStep;
+ webkit_web_view_set_zoom_level(webView, zoomLevel);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean browserWindowZoomOut(BrowserWindow *window)
+{
+ if (browserWindowCanZoomOut(window)) {
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ gdouble zoomLevel = webkit_web_view_get_zoom_level(webView) / zoomStep;
+ webkit_web_view_set_zoom_level(webView, zoomLevel);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean scrollEventCallback(WebKitWebView *webView, const GdkEventScroll *event, BrowserWindow *window)
+{
+ GdkModifierType mod = gtk_accelerator_get_default_mod_mask();
+
+ if ((event->state & mod) != GDK_CONTROL_MASK)
+ return FALSE;
+
+ if (event->delta_y < 0)
+ return browserWindowZoomIn(window);
+
+ return browserWindowZoomOut(window);
+}
+
static void browserWindowUpdateZoomActions(BrowserWindow *window)
{
gtk_widget_set_sensitive(window->zoomInItem, browserWindowCanZoomIn(window));
@@ -459,10 +441,10 @@ static void updateUriEntryIcon(BrowserWindow *window)
gtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_NEW);
}
-static void faviconChanged(GObject *object, GParamSpec *paramSpec, BrowserWindow *window)
+static void faviconChanged(WebKitWebView *webView, GParamSpec *paramSpec, BrowserWindow *window)
{
GdkPixbuf *favicon = NULL;
- cairo_surface_t *surface = webkit_web_view_get_favicon(window->webView);
+ cairo_surface_t *surface = webkit_web_view_get_favicon(webView);
if (surface) {
int width = cairo_image_surface_get_width(surface);
@@ -477,32 +459,194 @@ static void faviconChanged(GObject *object, GParamSpec *paramSpec, BrowserWindow
updateUriEntryIcon(window);
}
-static void webViewIsLoadingChanged(GObject *object, GParamSpec *paramSpec, BrowserWindow *window)
+static void webViewIsLoadingChanged(WebKitWebView *webView, GParamSpec *paramSpec, BrowserWindow *window)
{
- gboolean isLoading = webkit_web_view_is_loading(window->webView);
+ gboolean isLoading = webkit_web_view_is_loading(webView);
gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(window->reloadOrStopButton), isLoading ? GTK_STOCK_STOP : GTK_STOCK_REFRESH);
}
static void zoomInCallback(BrowserWindow *window)
{
- gdouble zoomLevel = webkit_web_view_get_zoom_level(window->webView) * zoomStep;
- webkit_web_view_set_zoom_level(window->webView, zoomLevel);
+ browserWindowZoomIn(window);
}
static void zoomOutCallback(BrowserWindow *window)
{
- gdouble zoomLevel = webkit_web_view_get_zoom_level(window->webView) / zoomStep;
- webkit_web_view_set_zoom_level(window->webView, zoomLevel);
+ browserWindowZoomOut(window);
+}
+
+static void defaultZoomCallback(BrowserWindow *window)
+{
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_set_zoom_level(webView, defaultZoomLevel);
}
static void searchCallback(BrowserWindow *window)
{
- browser_search_bar_open(window->searchBar);
+ browser_tab_start_search(window->activeTab);
+}
+
+static void newTabCallback(BrowserWindow *window)
+{
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ if (webkit_web_view_is_editable(webView))
+ return;
+
+ browser_window_append_view(window, WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "web-context", webkit_web_view_get_context(webView),
+ "settings", webkit_web_view_get_settings(webView),
+ "user-content-manager", webkit_web_view_get_user_content_manager(webView),
+ NULL)));
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), -1);
+}
+
+static void toggleWebInspector(BrowserWindow *window)
+{
+ browser_tab_toggle_inspector(window->activeTab);
+}
+
+static void openPrivateWindow(BrowserWindow *window)
+{
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ WebKitWebView *newWebView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "web-context", webkit_web_view_get_context(webView),
+ "settings", webkit_web_view_get_settings(webView),
+ "user-content-manager", webkit_web_view_get_user_content_manager(webView),
+ "is-ephemeral", TRUE,
+ NULL));
+ GtkWidget *newWindow = browser_window_new(GTK_WINDOW(window), window->webContext);
+ browser_window_append_view(BROWSER_WINDOW(newWindow), newWebView);
+ gtk_widget_show(GTK_WIDGET(newWindow));
+}
+
+static void reloadPage(BrowserWindow *window)
+{
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_reload(webView);
+}
+
+static void reloadPageIgnoringCache(BrowserWindow *window)
+{
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_reload_bypass_cache(webView);
+}
+
+static void stopPageLoad(BrowserWindow *window)
+{
+ browser_tab_stop_search(window->activeTab);
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ if (webkit_web_view_is_loading(webView))
+ webkit_web_view_stop_loading(webView);
+}
+
+static void loadHomePage(BrowserWindow *window)
+{
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_load_uri(webView, BROWSER_DEFAULT_URL);
+}
+
+static gboolean toggleFullScreen(BrowserWindow *window, gpointer user_data)
+{
+ if (!window->fullScreenIsEnabled) {
+ gtk_window_fullscreen(GTK_WINDOW(window));
+ gtk_widget_hide(window->toolbar);
+ window->fullScreenIsEnabled = TRUE;
+ } else {
+ gtk_window_unfullscreen(GTK_WINDOW(window));
+ gtk_widget_show(window->toolbar);
+ window->fullScreenIsEnabled = FALSE;
+ }
+ return TRUE;
+}
+
+static void webKitPrintOperationFailedCallback(WebKitPrintOperation *printOperation, GError *error)
+{
+ g_warning("Print failed: '%s'", error->message);
+}
+
+static gboolean printPage(BrowserWindow *window, gpointer user_data)
+{
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ WebKitPrintOperation *printOperation = webkit_print_operation_new(webView);
+
+ g_signal_connect(printOperation, "failed", G_CALLBACK(webKitPrintOperationFailedCallback), NULL);
+ webkit_print_operation_run_dialog(printOperation, GTK_WINDOW(window));
+ g_object_unref(printOperation);
+
+ return TRUE;
+}
+
+static void editingCommandCallback(GtkWidget *widget, BrowserWindow *window)
+{
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_execute_editing_command(webView, gtk_widget_get_name(widget));
+}
+
+static void insertImageCommandCallback(GtkWidget *widget, BrowserWindow *window)
+{
+ GtkWidget *fileChooser = gtk_file_chooser_dialog_new("Insert Image", GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_OPEN,
+ "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, NULL);
+
+ GtkFileFilter *filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, "Images");
+ gtk_file_filter_add_pixbuf_formats(filter);
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fileChooser), filter);
+
+ if (gtk_dialog_run(GTK_DIALOG(fileChooser)) == GTK_RESPONSE_ACCEPT) {
+ char *uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(fileChooser));
+ if (uri) {
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_execute_editing_command_with_argument(webView, WEBKIT_EDITING_COMMAND_INSERT_IMAGE, uri);
+ g_free(uri);
+ }
+ }
+
+ gtk_widget_destroy(fileChooser);
+}
+
+static void insertLinkCommandCallback(GtkWidget *widget, BrowserWindow *window)
+{
+ GtkWidget *dialog = gtk_dialog_new_with_buttons("Insert Link", GTK_WINDOW(window), GTK_DIALOG_MODAL, "Insert", GTK_RESPONSE_ACCEPT, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+ GtkWidget *entry = gtk_entry_new();
+ gtk_entry_set_placeholder_text(GTK_ENTRY(entry), "URL");
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), entry);
+ gtk_widget_show(entry);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ const char *url = gtk_entry_get_text(GTK_ENTRY(entry));
+ if (url && *url) {
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_execute_editing_command_with_argument(webView, WEBKIT_EDITING_COMMAND_CREATE_LINK, url);
+ }
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static void browserWindowEditingCommandToggleButtonSetActive(BrowserWindow *window, GtkWidget *button, gboolean active)
+{
+ g_signal_handlers_block_by_func(button, G_CALLBACK(editingCommandCallback), window);
+ gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button), active);
+ g_signal_handlers_unblock_by_func(button, G_CALLBACK(editingCommandCallback), window);
+}
+
+static void typingAttributesChanged(WebKitEditorState *editorState, GParamSpec *spec, BrowserWindow *window)
+{
+ unsigned typingAttributes = webkit_editor_state_get_typing_attributes(editorState);
+ browserWindowEditingCommandToggleButtonSetActive(window, window->boldItem, typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD);
+ browserWindowEditingCommandToggleButtonSetActive(window, window->italicItem, typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC);
+ browserWindowEditingCommandToggleButtonSetActive(window, window->underlineItem, typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE);
+ browserWindowEditingCommandToggleButtonSetActive(window, window->strikethroughItem, typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH);
}
static void browserWindowFinalize(GObject *gObject)
{
BrowserWindow *window = BROWSER_WINDOW(gObject);
+
+ g_signal_handlers_disconnect_matched(window->webContext, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, window);
+
if (window->favicon) {
g_object_unref(window->favicon);
window->favicon = NULL;
@@ -513,42 +657,207 @@ static void browserWindowFinalize(GObject *gObject)
window->accelGroup = NULL;
}
- if (window->fullScreenMessageLabelId)
- g_source_remove(window->fullScreenMessageLabelId);
-
if (window->resetEntryProgressTimeoutId)
g_source_remove(window->resetEntryProgressTimeoutId);
+ g_free(window->sessionFile);
+
G_OBJECT_CLASS(browser_window_parent_class)->finalize(gObject);
if (g_atomic_int_dec_and_test(&windowCount))
gtk_main_quit();
}
-static void browserWindowGetProperty(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
+static void browserWindowSetupEditorToolbar(BrowserWindow *window)
{
- BrowserWindow *window = BROWSER_WINDOW(object);
+ GtkWidget *toolbar = gtk_toolbar_new();
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL);
+ gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
- switch (propId) {
- case PROP_VIEW:
- g_value_set_object(value, browser_window_get_view(window));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
- }
+ GtkToolItem *item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_BOLD);
+ window->boldItem = GTK_WIDGET(item);
+ gtk_widget_set_name(GTK_WIDGET(item), "Bold");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_ITALIC);
+ window->italicItem = GTK_WIDGET(item);
+ gtk_widget_set_name(GTK_WIDGET(item), "Italic");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_UNDERLINE);
+ window->underlineItem = GTK_WIDGET(item);
+ gtk_widget_set_name(GTK_WIDGET(item), "Underline");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_STRIKETHROUGH);
+ gtk_widget_set_name(GTK_WIDGET(item), "Strikethrough");
+ window->strikethroughItem = GTK_WIDGET(item);
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_CUT);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_CUT);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_COPY);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_COPY);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_PASTE);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_PASTE);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_UNDO);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_UNDO);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_REDO);
+ gtk_widget_set_name(GTK_WIDGET(item), WEBKIT_EDITING_COMMAND_REDO);
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_radio_tool_button_new_from_stock(NULL, GTK_STOCK_JUSTIFY_LEFT);
+ GSList *justifyRadioGroup = gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(item));
+ gtk_widget_set_name(GTK_WIDGET(item), "JustifyLeft");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_radio_tool_button_new_from_stock(justifyRadioGroup, GTK_STOCK_JUSTIFY_CENTER);
+ justifyRadioGroup = gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(item));
+ gtk_widget_set_name(GTK_WIDGET(item), "JustifyCenter");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_radio_tool_button_new_from_stock(justifyRadioGroup, GTK_STOCK_JUSTIFY_RIGHT);
+ gtk_widget_set_name(GTK_WIDGET(item), "JustifyRight");
+ g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_INDENT);
+ gtk_widget_set_name(GTK_WIDGET(item), "Indent");
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_UNINDENT);
+ gtk_widget_set_name(GTK_WIDGET(item), "Outdent");
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(editingCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new(NULL, NULL);
+ gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "insert-image");
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(insertImageCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new(NULL, NULL);
+ gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "insert-link");
+ g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(insertLinkCommandCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ gtk_box_pack_start(GTK_BOX(window->mainBox), toolbar, FALSE, FALSE, 0);
+ gtk_box_reorder_child(GTK_BOX(window->mainBox), toolbar, 1);
+ gtk_widget_show(toolbar);
}
-static void browserWindowSetProperty(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
+static void browserWindowSwitchTab(GtkNotebook *notebook, BrowserTab *tab, guint tabIndex, BrowserWindow *window)
{
- BrowserWindow* window = BROWSER_WINDOW(object);
+ if (window->activeTab == tab)
+ return;
- switch (propId) {
- case PROP_VIEW:
- window->webView = g_value_get_object(value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ if (window->activeTab) {
+ browser_tab_set_status_text(window->activeTab, NULL);
+ g_clear_object(&window->favicon);
+
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ g_signal_handlers_disconnect_by_data(webView, window);
+
+ /* We always want close to be connected even for not active tabs */
+ g_signal_connect(webView, "close", G_CALLBACK(webViewClose), window);
+
+ WebKitBackForwardList *backForwadlist = webkit_web_view_get_back_forward_list(webView);
+ g_signal_handlers_disconnect_by_data(backForwadlist, window);
+ }
+
+ window->activeTab = tab;
+
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ if (webkit_web_view_is_editable(webView)) {
+ browserWindowSetupEditorToolbar(window);
+ g_signal_connect(webkit_web_view_get_editor_state(webView), "notify::typing-attributes", G_CALLBACK(typingAttributesChanged), window);
}
+ webViewURIChanged(webView, NULL, window);
+ webViewTitleChanged(webView, NULL, window);
+ webViewIsLoadingChanged(webView, NULL, window);
+ faviconChanged(webView, NULL, window);
+ browserWindowUpdateZoomActions(window);
+ if (webkit_web_view_is_loading(webView))
+ webViewLoadProgressChanged(webView, NULL, window);
+
+ g_signal_connect(webView, "notify::uri", G_CALLBACK(webViewURIChanged), window);
+ g_signal_connect(webView, "notify::estimated-load-progress", G_CALLBACK(webViewLoadProgressChanged), window);
+ g_signal_connect(webView, "notify::title", G_CALLBACK(webViewTitleChanged), window);
+ g_signal_connect(webView, "notify::is-loading", G_CALLBACK(webViewIsLoadingChanged), window);
+ g_signal_connect(webView, "create", G_CALLBACK(webViewCreate), window);
+ g_signal_connect(webView, "close", G_CALLBACK(webViewClose), window);
+ g_signal_connect(webView, "load-failed", G_CALLBACK(webViewLoadFailed), window);
+ g_signal_connect(webView, "decide-policy", G_CALLBACK(webViewDecidePolicy), window);
+ g_signal_connect(webView, "mouse-target-changed", G_CALLBACK(webViewMouseTargetChanged), window);
+ g_signal_connect(webView, "notify::zoom-level", G_CALLBACK(webViewZoomLevelChanged), window);
+ g_signal_connect(webView, "notify::favicon", G_CALLBACK(faviconChanged), window);
+ g_signal_connect(webView, "enter-fullscreen", G_CALLBACK(webViewEnterFullScreen), window);
+ g_signal_connect(webView, "leave-fullscreen", G_CALLBACK(webViewLeaveFullScreen), window);
+ g_signal_connect(webView, "scroll-event", G_CALLBACK(scrollEventCallback), window);
+
+ WebKitBackForwardList *backForwadlist = webkit_web_view_get_back_forward_list(webView);
+ browserWindowUpdateNavigationActions(window, backForwadlist);
+ g_signal_connect(backForwadlist, "changed", G_CALLBACK(backForwadlistChanged), window);
+}
+
+static void browserWindowTabAddedOrRemoved(GtkNotebook *notebook, BrowserTab *tab, guint tabIndex, BrowserWindow *window)
+{
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(window->notebook), gtk_notebook_get_n_pages(notebook) > 1);
}
static void browser_window_init(BrowserWindow *window)
@@ -567,6 +876,64 @@ static void browser_window_init(BrowserWindow *window)
window->accelGroup = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), window->accelGroup);
+ /* Global accelerators */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_I, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(toggleWebInspector), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F12, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(toggleWebInspector), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_P, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(openPrivateWindow), window, NULL));
+
+ /* Reload page */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F5, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(reloadPage), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_R, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(reloadPage), window, NULL));
+
+ /* Reload page ignoring cache */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F5, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(reloadPageIgnoringCache), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_R, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(reloadPageIgnoringCache), window, NULL));
+
+ /* Stop page load */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F6, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(stopPageLoad), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_Escape, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(stopPageLoad), window, NULL));
+
+ /* Load home page */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_Home, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(loadHomePage), window, NULL));
+
+ /* Zoom in, zoom out and default zoom*/
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_equal, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(zoomInCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_KP_Add, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(zoomInCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_minus, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(zoomOutCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_KP_Subtract, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(zoomOutCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_0, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(defaultZoomCallback), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_KP_0, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(defaultZoomCallback), window, NULL));
+
+ /* Toggle fullscreen */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_F11, 0, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(toggleFullScreen), window, NULL));
+
+ /* Quit */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_Q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(gtk_widget_destroy), window, NULL));
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_W, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(gtk_widget_destroy), window, NULL));
+
+ /* Print */
+ gtk_accel_group_connect(window->accelGroup, GDK_KEY_P, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
+ g_cclosure_new_swap(G_CALLBACK(printPage), window, NULL));
+
GtkWidget *toolbar = gtk_toolbar_new();
window->toolbar = toolbar;
gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL);
@@ -609,6 +976,18 @@ static void browser_window_init(BrowserWindow *window)
gtk_widget_add_accelerator(GTK_WIDGET(item), "clicked", window->accelGroup, GDK_KEY_F, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
gtk_widget_show(GTK_WIDGET(item));
+ item = gtk_tool_button_new_from_stock(GTK_STOCK_HOME);
+ g_signal_connect_swapped(item, "clicked", G_CALLBACK(loadHomePage), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_add_accelerator(GTK_WIDGET(item), "clicked", window->accelGroup, GDK_KEY_Home, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
+ gtk_widget_show(GTK_WIDGET(item));
+
+ item = gtk_tool_button_new(gtk_image_new_from_icon_name("tab-new", GTK_ICON_SIZE_SMALL_TOOLBAR), NULL);
+ g_signal_connect_swapped(item, "clicked", G_CALLBACK(newTabCallback), window);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+ gtk_widget_add_accelerator(GTK_WIDGET(item), "clicked", window->accelGroup, GDK_KEY_T, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
+ gtk_widget_show_all(GTK_WIDGET(item));
+
item = gtk_tool_item_new();
gtk_tool_item_set_expand(item, TRUE);
gtk_container_add(GTK_CONTAINER(item), window->uriEntry);
@@ -628,60 +1007,44 @@ static void browser_window_init(BrowserWindow *window)
gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
gtk_widget_show(toolbar);
+ window->notebook = gtk_notebook_new();
+ g_signal_connect(window->notebook, "switch-page", G_CALLBACK(browserWindowSwitchTab), window);
+ g_signal_connect(window->notebook, "page-added", G_CALLBACK(browserWindowTabAddedOrRemoved), window);
+ g_signal_connect(window->notebook, "page-removed", G_CALLBACK(browserWindowTabAddedOrRemoved), window);
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(window->notebook), FALSE);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(window->notebook), FALSE);
+ gtk_box_pack_start(GTK_BOX(window->mainBox), window->notebook, TRUE, TRUE, 0);
+ gtk_widget_show(window->notebook);
+
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show(vbox);
}
static void browserWindowConstructed(GObject *gObject)
{
- BrowserWindow *window = BROWSER_WINDOW(gObject);
-
- browserWindowUpdateZoomActions(window);
-
- g_signal_connect(window->webView, "notify::uri", G_CALLBACK(webViewURIChanged), window);
- g_signal_connect(window->webView, "notify::estimated-load-progress", G_CALLBACK(webViewLoadProgressChanged), window);
- g_signal_connect(window->webView, "notify::title", G_CALLBACK(webViewTitleChanged), window);
- g_signal_connect(window->webView, "create", G_CALLBACK(webViewCreate), window);
- g_signal_connect(window->webView, "load-failed", G_CALLBACK(webViewLoadFailed), window);
- g_signal_connect(window->webView, "decide-policy", G_CALLBACK(webViewDecidePolicy), window);
- g_signal_connect(window->webView, "permission-request", G_CALLBACK(webViewDecidePermissionRequest), window);
- g_signal_connect(window->webView, "mouse-target-changed", G_CALLBACK(webViewMouseTargetChanged), window);
- g_signal_connect(window->webView, "notify::zoom-level", G_CALLBACK(webViewZoomLevelChanged), window);
- g_signal_connect(window->webView, "notify::favicon", G_CALLBACK(faviconChanged), window);
- g_signal_connect(window->webView, "enter-fullscreen", G_CALLBACK(webViewEnterFullScreen), window);
- g_signal_connect(window->webView, "leave-fullscreen", G_CALLBACK(webViewLeaveFullScreen), window);
- g_signal_connect(window->webView, "notify::is-loading", G_CALLBACK(webViewIsLoadingChanged), window);
-
- g_signal_connect(webkit_web_view_get_context(window->webView), "download-started", G_CALLBACK(downloadStarted), window);
-
- window->searchBar = BROWSER_SEARCH_BAR(browser_search_bar_new(window->webView));
- browser_search_bar_add_accelerators(window->searchBar, window->accelGroup);
- gtk_box_pack_start(GTK_BOX(window->mainBox), GTK_WIDGET(window->searchBar), FALSE, FALSE, 0);
-
- WebKitBackForwardList *backForwadlist = webkit_web_view_get_back_forward_list(window->webView);
- g_signal_connect(backForwadlist, "changed", G_CALLBACK(backForwadlistChanged), window);
-
- GtkWidget *overlay = gtk_overlay_new();
- gtk_box_pack_start(GTK_BOX(window->mainBox), overlay, TRUE, TRUE, 0);
- gtk_widget_show(overlay);
+ G_OBJECT_CLASS(browser_window_parent_class)->constructed(gObject);
+}
- window->statusLabel = gtk_label_new(NULL);
- gtk_widget_set_halign(window->statusLabel, GTK_ALIGN_START);
- gtk_widget_set_valign(window->statusLabel, GTK_ALIGN_END);
- gtk_widget_set_margin_left(window->statusLabel, 1);
- gtk_widget_set_margin_right(window->statusLabel, 1);
- gtk_widget_set_margin_top(window->statusLabel, 1);
- gtk_widget_set_margin_bottom(window->statusLabel, 1);
- gtk_overlay_add_overlay(GTK_OVERLAY(overlay), window->statusLabel);
+static void browserWindowSaveSession(BrowserWindow *window)
+{
+ if (!window->sessionFile)
+ return;
- gtk_container_add(GTK_CONTAINER(overlay), GTK_WIDGET(window->webView));
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ WebKitWebViewSessionState *state = webkit_web_view_get_session_state(webView);
+ GBytes *bytes = webkit_web_view_session_state_serialize(state);
+ webkit_web_view_session_state_unref(state);
+ g_file_set_contents(window->sessionFile, g_bytes_get_data(bytes, NULL), g_bytes_get_size(bytes), NULL);
+ g_bytes_unref(bytes);
+}
- window->fullScreenMessageLabel = gtk_label_new(NULL);
- gtk_widget_set_halign(window->fullScreenMessageLabel, GTK_ALIGN_CENTER);
- gtk_widget_set_valign(window->fullScreenMessageLabel, GTK_ALIGN_CENTER);
- gtk_widget_set_no_show_all(window->fullScreenMessageLabel, TRUE);
- gtk_overlay_add_overlay(GTK_OVERLAY(overlay), window->fullScreenMessageLabel);
- gtk_widget_show(GTK_WIDGET(window->webView));
+static gboolean browserWindowDeleteEvent(GtkWidget *widget, GdkEventAny* event)
+{
+ BrowserWindow *window = BROWSER_WINDOW(widget);
+ browserWindowSaveSession(window);
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ webkit_web_view_try_close(webView);
+ return TRUE;
}
static void browser_window_class_init(BrowserWindowClass *klass)
@@ -689,27 +1052,22 @@ static void browser_window_class_init(BrowserWindowClass *klass)
GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
gobjectClass->constructed = browserWindowConstructed;
- gobjectClass->get_property = browserWindowGetProperty;
- gobjectClass->set_property = browserWindowSetProperty;
gobjectClass->finalize = browserWindowFinalize;
- g_object_class_install_property(gobjectClass,
- PROP_VIEW,
- g_param_spec_object("view",
- "View",
- "The web view of this window",
- WEBKIT_TYPE_WEB_VIEW,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ GtkWidgetClass *widgetClass = GTK_WIDGET_CLASS(klass);
+ widgetClass->delete_event = browserWindowDeleteEvent;
}
-// Public API.
-GtkWidget *browser_window_new(WebKitWebView *view, GtkWindow *parent)
+/* Public API. */
+GtkWidget *browser_window_new(GtkWindow *parent, WebKitWebContext *webContext)
{
- g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(view), 0);
+ g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(webContext), NULL);
BrowserWindow *window = BROWSER_WINDOW(g_object_new(BROWSER_TYPE_WINDOW,
- "type", GTK_WINDOW_TOPLEVEL, "view", view, NULL));
+ "type", GTK_WINDOW_TOPLEVEL, NULL));
+ window->webContext = webContext;
+ g_signal_connect(window->webContext, "download-started", G_CALLBACK(downloadStarted), window);
if (parent) {
window->parentWindow = parent;
g_object_add_weak_pointer(G_OBJECT(parent), (gpointer *)&window->parentWindow);
@@ -718,11 +1076,28 @@ GtkWidget *browser_window_new(WebKitWebView *view, GtkWindow *parent)
return GTK_WIDGET(window);
}
-WebKitWebView *browser_window_get_view(BrowserWindow *window)
+WebKitWebContext *browser_window_get_web_context(BrowserWindow *window)
+{
+ g_return_val_if_fail(BROWSER_IS_WINDOW(window), NULL);
+
+ return window->webContext;
+}
+
+void browser_window_append_view(BrowserWindow *window, WebKitWebView *webView)
{
- g_return_val_if_fail(BROWSER_IS_WINDOW(window), 0);
+ g_return_if_fail(BROWSER_IS_WINDOW(window));
+ g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
- return window->webView;
+ if (window->activeTab && webkit_web_view_is_editable(browser_tab_get_web_view(window->activeTab))) {
+ g_warning("Only one tab is allowed in editable mode");
+ return;
+ }
+
+ GtkWidget *tab = browser_tab_new(webView);
+ browser_tab_add_accelerators(BROWSER_TAB(tab), window->accelGroup);
+ gtk_notebook_append_page(GTK_NOTEBOOK(window->notebook), tab, browser_tab_get_title_widget(BROWSER_TAB(tab)));
+ gtk_container_child_set(GTK_CONTAINER(window->notebook), tab, "tab-expand", TRUE, NULL);
+ gtk_widget_show(tab);
}
void browser_window_load_uri(BrowserWindow *window, const char *uri)
@@ -730,12 +1105,55 @@ void browser_window_load_uri(BrowserWindow *window, const char *uri)
g_return_if_fail(BROWSER_IS_WINDOW(window));
g_return_if_fail(uri);
- if (!g_str_has_prefix(uri, "javascript:")) {
- char *internalURI = getInternalURI(uri);
- webkit_web_view_load_uri(window->webView, internalURI);
- g_free(internalURI);
- return;
+ browser_tab_load_uri(window->activeTab, uri);
+}
+
+void browser_window_load_session(BrowserWindow *window, const char *sessionFile)
+{
+ g_return_if_fail(BROWSER_IS_WINDOW(window));
+ g_return_if_fail(sessionFile);
+
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ window->sessionFile = g_strdup(sessionFile);
+ gchar *data = NULL;
+ gsize dataLength;
+ if (g_file_get_contents(sessionFile, &data, &dataLength, NULL)) {
+ GBytes *bytes = g_bytes_new_take(data, dataLength);
+ WebKitWebViewSessionState *state = webkit_web_view_session_state_new(bytes);
+ g_bytes_unref(bytes);
+
+ if (state) {
+ webkit_web_view_restore_session_state(webView, state);
+ webkit_web_view_session_state_unref(state);
+ }
}
- webkit_web_view_run_javascript(window->webView, strstr(uri, "javascript:"), NULL, NULL, NULL);
+ WebKitBackForwardList *bfList = webkit_web_view_get_back_forward_list(webView);
+ WebKitBackForwardListItem *item = webkit_back_forward_list_get_current_item(bfList);
+ if (item)
+ webkit_web_view_go_to_back_forward_list_item(webView, item);
+ else
+ webkit_web_view_load_uri(webView, BROWSER_DEFAULT_URL);
+
+}
+
+void browser_window_set_background_color(BrowserWindow *window, GdkRGBA *rgba)
+{
+ g_return_if_fail(BROWSER_IS_WINDOW(window));
+ g_return_if_fail(rgba);
+
+ WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+ GdkRGBA viewRGBA;
+ webkit_web_view_get_background_color(webView, &viewRGBA);
+ if (gdk_rgba_equal(rgba, &viewRGBA))
+ return;
+
+ GdkVisual *rgbaVisual = gdk_screen_get_rgba_visual(gtk_window_get_screen(GTK_WINDOW(window)));
+ if (!rgbaVisual)
+ return;
+
+ gtk_widget_set_visual(GTK_WIDGET(window), rgbaVisual);
+ gtk_widget_set_app_paintable(GTK_WIDGET(window), TRUE);
+
+ webkit_web_view_set_background_color(webView, rgba);
}
diff --git a/Tools/MiniBrowser/gtk/BrowserWindow.h b/Tools/MiniBrowser/gtk/BrowserWindow.h
index 66675462f..15abd0612 100644
--- a/Tools/MiniBrowser/gtk/BrowserWindow.h
+++ b/Tools/MiniBrowser/gtk/BrowserWindow.h
@@ -37,15 +37,20 @@ G_BEGIN_DECLS
#define BROWSER_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BROWSER_TYPE_WINDOW))
#define BROWSER_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BROWSER_TYPE_WINDOW))
#define BROWSER_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BROWSER_TYPE_WINDOW, BrowserWindowClass))
+#define BROWSER_DEFAULT_URL "http://www.webkitgtk.org/"
+#define BROWSER_ABOUT_SCHEME "minibrowser-about"
typedef struct _BrowserWindow BrowserWindow;
typedef struct _BrowserWindowClass BrowserWindowClass;
GType browser_window_get_type(void);
-GtkWidget* browser_window_new(WebKitWebView*, GtkWindow*);
-WebKitWebView* browser_window_get_view(BrowserWindow*);
-void browser_window_load_uri(BrowserWindow *, const char *uri);
+GtkWidget* browser_window_new(GtkWindow*, WebKitWebContext*);
+WebKitWebContext* browser_window_get_web_context(BrowserWindow*);
+void browser_window_append_view(BrowserWindow*, WebKitWebView*);
+void browser_window_load_uri(BrowserWindow*, const char *uri);
+void browser_window_load_session(BrowserWindow *, const char *sessionFile);
+void browser_window_set_background_color(BrowserWindow*, GdkRGBA*);
G_END_DECLS
diff --git a/Tools/MiniBrowser/gtk/CMakeLists.txt b/Tools/MiniBrowser/gtk/CMakeLists.txt
new file mode 100644
index 000000000..e38bc6dc1
--- /dev/null
+++ b/Tools/MiniBrowser/gtk/CMakeLists.txt
@@ -0,0 +1,64 @@
+set(MINIBROWSER_DIR "${TOOLS_DIR}/MiniBrowser/gtk")
+set(DERIVED_SOURCES_MINIBROWSER_DIR "${CMAKE_BINARY_DIR}/DerivedSources/MiniBrowser")
+
+file(MAKE_DIRECTORY ${DERIVED_SOURCES_MINIBROWSER_DIR})
+
+set(MiniBrowser_SOURCES
+ ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.c
+ ${MINIBROWSER_DIR}/BrowserCellRendererVariant.c
+ ${MINIBROWSER_DIR}/BrowserCellRendererVariant.h
+ ${MINIBROWSER_DIR}/BrowserDownloadsBar.c
+ ${MINIBROWSER_DIR}/BrowserDownloadsBar.h
+ ${MINIBROWSER_DIR}/BrowserSearchBar.c
+ ${MINIBROWSER_DIR}/BrowserSearchBar.h
+ ${MINIBROWSER_DIR}/BrowserSettingsDialog.c
+ ${MINIBROWSER_DIR}/BrowserSettingsDialog.h
+ ${MINIBROWSER_DIR}/BrowserTab.c
+ ${MINIBROWSER_DIR}/BrowserTab.h
+ ${MINIBROWSER_DIR}/BrowserWindow.c
+ ${MINIBROWSER_DIR}/BrowserWindow.h
+ ${MINIBROWSER_DIR}/main.c
+)
+
+set(MiniBrowser_INCLUDE_DIRECTORIES
+ ${DERIVED_SOURCES_MINIBROWSER_DIR}
+ ${DERIVED_SOURCES_WEBKIT2GTK_DIR}
+ ${FORWARDING_HEADERS_WEBKIT2GTK_DIR}
+ ${FORWARDING_HEADERS_DIR}
+ ${CMAKE_SOURCE_DIR}/Source
+)
+
+set(MiniBrowser_SYSTEM_INCLUDE_DIRECTORIES
+ ${GTK3_INCLUDE_DIRS}
+ ${GLIB_INCLUDE_DIRS}
+ ${LIBSOUP_INCLUDE_DIRS}
+)
+
+set(MiniBrowser_LIBRARIES
+ ${JavaScriptCore_LIBRARY_NAME}
+ WebKit2
+ ${GTK3_LIBRARIES}
+ ${GLIB_LIBRARIES}
+ ${LIBSOUP_LIBRARIES}
+)
+
+add_custom_command(
+ OUTPUT ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.c
+ ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.h
+ MAIN_DEPENDENCY ${MINIBROWSER_DIR}/browser-marshal.list
+ COMMAND glib-genmarshal --prefix=browser_marshal ${MINIBROWSER_DIR}/browser-marshal.list --body > ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.c
+ COMMAND glib-genmarshal --prefix=browser_marshal ${MINIBROWSER_DIR}/browser-marshal.list --header > ${DERIVED_SOURCES_MINIBROWSER_DIR}/BrowserMarshal.h
+ VERBATIM)
+
+if (DEVELOPER_MODE)
+ add_definitions(-DWEBKIT_INJECTED_BUNDLE_PATH="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+endif ()
+
+add_definitions(-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_6)
+
+include_directories(${MiniBrowser_INCLUDE_DIRECTORIES})
+include_directories(SYSTEM ${MiniBrowser_SYSTEM_INCLUDE_DIRECTORIES})
+add_executable(MiniBrowser ${MiniBrowser_SOURCES})
+target_link_libraries(MiniBrowser ${MiniBrowser_LIBRARIES})
+
+install(TARGETS MiniBrowser DESTINATION "${LIBEXEC_INSTALL_DIR}")
diff --git a/Tools/MiniBrowser/gtk/GNUmakefile.am b/Tools/MiniBrowser/gtk/GNUmakefile.am
deleted file mode 100644
index 8bdbdd33e..000000000
--- a/Tools/MiniBrowser/gtk/GNUmakefile.am
+++ /dev/null
@@ -1,75 +0,0 @@
-if ENABLE_WEBKIT2
-noinst_PROGRAMS += \
- Programs/MiniBrowser
-endif
-
-Programs_MiniBrowser_CPPFLAGS = \
- -I$(srcdir)/Source \
- -I$(top_builddir)/DerivedSources/WebKit2 \
- -I$(top_builddir)/DerivedSources/WebKit2/webkit2gtk \
- -I$(top_builddir)/DerivedSources/WebKit2/webkit2gtk/include \
- -DWEBKIT_EXEC_PATH=\"${shell pwd}/$(top_builddir)/Programs/\" \
- -DWEBKIT_INJECTED_BUNDLE_PATH=\"${shell pwd}/$(top_builddir)/.libs\" \
- -DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_6 \
- $(global_cppflags) \
- $(javascriptcore_cppflags) \
- $(FREETYPE_CFLAGS) \
- $(GLIB_CFLAGS) \
- $(GTK_CFLAGS) \
- $(LIBSOUP_CFLAGS)
-
-Programs_MiniBrowser_SOURCES = \
- Tools/MiniBrowser/gtk/BrowserCellRendererVariant.h \
- Tools/MiniBrowser/gtk/BrowserCellRendererVariant.c \
- Tools/MiniBrowser/gtk/BrowserDownloadsBar.h \
- Tools/MiniBrowser/gtk/BrowserDownloadsBar.c \
- Tools/MiniBrowser/gtk/BrowserSearchBar.h \
- Tools/MiniBrowser/gtk/BrowserSearchBar.c \
- Tools/MiniBrowser/gtk/BrowserSettingsDialog.h \
- Tools/MiniBrowser/gtk/BrowserSettingsDialog.c \
- Tools/MiniBrowser/gtk/BrowserWindow.h \
- Tools/MiniBrowser/gtk/BrowserWindow.c \
- Tools/MiniBrowser/gtk/main.c
-
-minibrowser_built_sources += \
- DerivedSources/WebKit2/BrowserMarshal.h \
- DerivedSources/WebKit2/BrowserMarshal.c
-nodist_Programs_MiniBrowser_SOURCES = \
- $(minibrowser_built_sources)
-
-Programs_MiniBrowser_LDADD = \
- libwebkit2gtk-@WEBKITGTK_API_MAJOR_VERSION@.@WEBKITGTK_API_MINOR_VERSION@.la \
- $(FREETYPE_LIBS) \
- $(GLIB_LIBS) \
- $(GTK_LIBS) \
- $(LIBSOUP_LIBS)
-
-Programs_MiniBrowser_LDFLAGS = \
- -no-install
-
-CLEANFILES += \
- $(top_builddir)/Programs/MiniBrowser \
- $(minibrowser_built_sources) \
- $(top_builddir)/stamp-mini-browser-marshal.h \
- $(top_builddir)/stamp-mini-browser-marshal.c
-
-BUILT_SOURCES += $(minibrowser_built_sources)
-
-minibrowser_marshal_list = $(srcdir)/Tools/MiniBrowser/gtk/browser-marshal.list
-
-$(GENSOURCES_WEBKIT2)/BrowserMarshal.h: stamp-mini-browser-marshal.h
- @true
-$(GENSOURCES_WEBKIT2)/BrowserMarshal.c: stamp-mini-browser-marshal.c
- @true
-
-stamp-mini-browser-marshal.c: $(minibrowser_marshal_list)
- $(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=browser_marshal $(minibrowser_marshal_list) --body > $(GENSOURCES_WEBKIT2)/BrowserMarshal.c && \
- echo timestamp > $(@F)
-
-stamp-mini-browser-marshal.h: $(minibrowser_marshal_list)
- $(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=browser_marshal $(minibrowser_marshal_list) --header > $(GENSOURCES_WEBKIT2)/BrowserMarshal.h && \
- echo timestamp > $(@F)
-
-EXTRA_DIST += \
- $(srcdir)/Tools/MiniBrowser/gtk/browser-marshal.list
-
diff --git a/Tools/MiniBrowser/gtk/main.c b/Tools/MiniBrowser/gtk/main.c
index e383fda83..e5e5618cd 100644
--- a/Tools/MiniBrowser/gtk/main.c
+++ b/Tools/MiniBrowser/gtk/main.c
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,7 +25,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "cmakeconfig.h"
+
#include "BrowserWindow.h"
+#include <JavaScriptCore/JavaScript.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <string.h>
@@ -34,7 +37,11 @@
#define MINI_BROWSER_ERROR (miniBrowserErrorQuark())
static const gchar **uriArguments = NULL;
-static const char *miniBrowserAboutScheme = "minibrowser-about";
+static GdkRGBA *backgroundColor;
+static gboolean editorMode;
+static const char *sessionFile;
+static char *geometry;
+static gboolean privateMode;
typedef enum {
MINI_BROWSER_ERROR_INVALID_ABOUT_PATH
@@ -54,24 +61,40 @@ static gchar *argumentToURL(const char *filename)
return fileURL;
}
-static void createBrowserWindow(const gchar *uri, WebKitSettings *webkitSettings)
+static WebKitWebView *createBrowserTab(BrowserWindow *window, WebKitSettings *webkitSettings, WebKitUserContentManager *userContentManager)
{
- GtkWidget *webView = webkit_web_view_new();
- GtkWidget *mainWindow = browser_window_new(WEBKIT_WEB_VIEW(webView), NULL);
- gchar *url = argumentToURL(uri);
+ WebKitWebView *webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "web-context", browser_window_get_web_context(window),
+ "settings", webkitSettings,
+ "user-content-manager", userContentManager,
+ NULL));
+
+ if (editorMode)
+ webkit_web_view_set_editable(webView, TRUE);
- if (webkitSettings)
- webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView), webkitSettings);
+ browser_window_append_view(window, webView);
+ return webView;
+}
- browser_window_load_uri(BROWSER_WINDOW(mainWindow), url);
- g_free(url);
+static gboolean parseBackgroundColor(const char *optionName, const char *value, gpointer data, GError **error)
+{
+ GdkRGBA rgba;
+ if (gdk_rgba_parse(&rgba, value)) {
+ backgroundColor = gdk_rgba_copy(&rgba);
+ return TRUE;
+ }
- gtk_widget_grab_focus(webView);
- gtk_widget_show(mainWindow);
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Failed to parse '%s' as RGBA color", value);
+ return FALSE;
}
static const GOptionEntry commandLineOptions[] =
{
+ { "bg-color", 0, 0, G_OPTION_ARG_CALLBACK, parseBackgroundColor, "Background color", NULL },
+ { "editor-mode", 'e', 0, G_OPTION_ARG_NONE, &editorMode, "Run in editor mode", NULL },
+ { "session-file", 's', 0, G_OPTION_ARG_FILENAME, &sessionFile, "Session file", "FILE" },
+ { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry, "Set the size and position of the window (WIDTHxHEIGHT+X+Y)", "GEOMETRY" },
+ { "private", 'p', 0, G_OPTION_ARG_NONE, &privateMode, "Run in private browsing mode", NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, 0, "[URL…]" },
{ 0, 0, 0, 0, 0, 0, 0 }
};
@@ -208,8 +231,196 @@ static gboolean addSettingsGroupToContext(GOptionContext *context, WebKitSetting
return TRUE;
}
-static void
-aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData)
+typedef struct {
+ WebKitURISchemeRequest *request;
+ GList *dataList;
+ GHashTable *dataMap;
+} AboutDataRequest;
+
+static GHashTable *aboutDataRequestMap;
+
+static void aboutDataRequestFree(AboutDataRequest *request)
+{
+ if (!request)
+ return;
+
+ g_list_free_full(request->dataList, g_object_unref);
+
+ if (request->request)
+ g_object_unref(request->request);
+ if (request->dataMap)
+ g_hash_table_destroy(request->dataMap);
+
+ g_slice_free(AboutDataRequest, request);
+}
+
+static AboutDataRequest* aboutDataRequestNew(WebKitURISchemeRequest *uriRequest)
+{
+ if (!aboutDataRequestMap)
+ aboutDataRequestMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)aboutDataRequestFree);
+
+ AboutDataRequest *request = g_slice_new0(AboutDataRequest);
+ request->request = g_object_ref(uriRequest);
+ g_hash_table_insert(aboutDataRequestMap, GUINT_TO_POINTER(webkit_web_view_get_page_id(webkit_uri_scheme_request_get_web_view(request->request))), request);
+
+ return request;
+}
+
+static AboutDataRequest *aboutDataRequestForView(guint64 pageID)
+{
+ return aboutDataRequestMap ? g_hash_table_lookup(aboutDataRequestMap, GUINT_TO_POINTER(pageID)) : NULL;
+}
+
+static void websiteDataRemovedCallback(WebKitWebsiteDataManager *manager, GAsyncResult *result, AboutDataRequest *dataRequest)
+{
+ if (webkit_website_data_manager_remove_finish(manager, result, NULL))
+ webkit_web_view_reload(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+}
+
+static void websiteDataClearedCallback(WebKitWebsiteDataManager *manager, GAsyncResult *result, AboutDataRequest *dataRequest)
+{
+ if (webkit_website_data_manager_clear_finish(manager, result, NULL))
+ webkit_web_view_reload(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+}
+
+static void aboutDataScriptMessageReceivedCallback(WebKitUserContentManager *userContentManager, WebKitJavascriptResult *message, WebKitWebContext *webContext)
+{
+ JSValueRef jsValue = webkit_javascript_result_get_value(message);
+ JSStringRef jsString = JSValueToStringCopy(webkit_javascript_result_get_global_context(message), jsValue, NULL);
+ size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
+ if (!maxSize) {
+ JSStringRelease(jsString);
+ return;
+ }
+ char *messageString = g_malloc(maxSize);
+ JSStringGetUTF8CString(jsString, messageString, maxSize);
+ JSStringRelease(jsString);
+
+ char **tokens = g_strsplit(messageString, ":", 3);
+ g_free(messageString);
+
+ unsigned tokenCount = g_strv_length(tokens);
+ if (!tokens || tokenCount < 2) {
+ g_strfreev(tokens);
+ return;
+ }
+
+ guint64 pageID = g_ascii_strtoull(tokens[0], NULL, 10);
+ AboutDataRequest *dataRequest = aboutDataRequestForView(pageID);
+ if (!dataRequest) {
+ g_strfreev(tokens);
+ return;
+ }
+
+ WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webContext);
+ guint64 types = g_ascii_strtoull(tokens[1], NULL, 10);
+ if (tokenCount == 2)
+ webkit_website_data_manager_clear(manager, types, 0, NULL, (GAsyncReadyCallback)websiteDataClearedCallback, dataRequest);
+ else {
+ guint64 domainID = g_ascii_strtoull(tokens[2], NULL, 10);
+ GList *dataList = g_hash_table_lookup(dataRequest->dataMap, GUINT_TO_POINTER(types));
+ WebKitWebsiteData *data = g_list_nth_data(dataList, domainID);
+ if (data) {
+ GList dataList = { data, NULL, NULL };
+ webkit_website_data_manager_remove(manager, types, &dataList, NULL, (GAsyncReadyCallback)websiteDataRemovedCallback, dataRequest);
+ }
+ }
+ g_strfreev(tokens);
+}
+
+static void domainListFree(GList *domains)
+{
+ g_list_free_full(domains, (GDestroyNotify)webkit_website_data_unref);
+}
+
+static void aboutDataFillTable(GString *result, AboutDataRequest *dataRequest, GList* dataList, const char *title, WebKitWebsiteDataTypes types, const char *dataPath, guint64 pageID)
+{
+ guint64 totalDataSize = 0;
+ GList *domains = NULL;
+ GList *l;
+ for (l = dataList; l; l = g_list_next(l)) {
+ WebKitWebsiteData *data = (WebKitWebsiteData *)l->data;
+
+ if (webkit_website_data_get_types(data) & types) {
+ domains = g_list_prepend(domains, webkit_website_data_ref(data));
+ totalDataSize += webkit_website_data_get_size(data, types);
+ }
+ }
+ if (!domains)
+ return;
+
+ if (!dataRequest->dataMap)
+ dataRequest->dataMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)domainListFree);
+ g_hash_table_insert(dataRequest->dataMap, GUINT_TO_POINTER(types), domains);
+
+ if (totalDataSize) {
+ char *totalDataSizeStr = g_format_size(totalDataSize);
+ g_string_append_printf(result, "<h1>%s (%s)</h1>\n<table>\n", title, totalDataSizeStr);
+ g_free(totalDataSizeStr);
+ } else
+ g_string_append_printf(result, "<h1>%s</h1>\n<table>\n", title);
+ if (dataPath)
+ g_string_append_printf(result, "<tr><td colspan=\"2\">Path: %s</td></tr>\n", dataPath);
+
+ unsigned index;
+ for (l = domains, index = 0; l; l = g_list_next(l), index++) {
+ WebKitWebsiteData *data = (WebKitWebsiteData *)l->data;
+ const char *displayName = webkit_website_data_get_name(data);
+ guint64 dataSize = webkit_website_data_get_size(data, types);
+ if (dataSize) {
+ char *dataSizeStr = g_format_size(dataSize);
+ g_string_append_printf(result, "<tr><td>%s (%s)</td>", displayName, dataSizeStr);
+ g_free(dataSizeStr);
+ } else
+ g_string_append_printf(result, "<tr><td>%s</td>", displayName);
+ g_string_append_printf(result, "<td><input type=\"button\" value=\"Remove\" onclick=\"removeData('%"G_GUINT64_FORMAT":%u:%u');\"></td></tr>\n",
+ pageID, types, index);
+ }
+ g_string_append_printf(result, "<tr><td><input type=\"button\" value=\"Clear all\" onclick=\"clearData('%"G_GUINT64_FORMAT":%u');\"></td></tr></table>\n",
+ pageID, types);
+}
+
+static void gotWebsiteDataCallback(WebKitWebsiteDataManager *manager, GAsyncResult *asyncResult, AboutDataRequest *dataRequest)
+{
+ GList *dataList = webkit_website_data_manager_fetch_finish(manager, asyncResult, NULL);
+
+ GString *result = g_string_new(
+ "<html><head>"
+ "<script>"
+ " function removeData(domain) {"
+ " window.webkit.messageHandlers.aboutData.postMessage(domain);"
+ " }"
+ " function clearData(dataType) {"
+ " window.webkit.messageHandlers.aboutData.postMessage(dataType);"
+ " }"
+ "</script></head><body>\n");
+
+ guint64 pageID = webkit_web_view_get_page_id(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+ aboutDataFillTable(result, dataRequest, dataList, "Cookies", WEBKIT_WEBSITE_DATA_COOKIES, NULL, pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Memory Cache", WEBKIT_WEBSITE_DATA_MEMORY_CACHE, NULL, pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Disk Cache", WEBKIT_WEBSITE_DATA_DISK_CACHE, webkit_website_data_manager_get_disk_cache_directory(manager), pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Session Storage", WEBKIT_WEBSITE_DATA_SESSION_STORAGE, NULL, pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Local Storage", WEBKIT_WEBSITE_DATA_LOCAL_STORAGE, webkit_website_data_manager_get_local_storage_directory(manager), pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "WebSQL Databases", WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES, webkit_website_data_manager_get_websql_directory(manager), pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "IndexedDB Databases", WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES, webkit_website_data_manager_get_indexeddb_directory(manager), pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Plugins Data", WEBKIT_WEBSITE_DATA_PLUGIN_DATA, NULL, pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Offline Web Applications Cache", WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE, webkit_website_data_manager_get_offline_application_cache_directory(manager), pageID);
+
+ result = g_string_append(result, "</body></html>");
+ gsize streamLength = result->len;
+ GInputStream *stream = g_memory_input_stream_new_from_data(g_string_free(result, FALSE), streamLength, g_free);
+ webkit_uri_scheme_request_finish(dataRequest->request, stream, streamLength, "text/html");
+ g_list_free_full(dataList, (GDestroyNotify)webkit_website_data_unref);
+}
+
+static void aboutDataHandleRequest(WebKitURISchemeRequest *request, WebKitWebContext *webContext)
+{
+ AboutDataRequest *dataRequest = aboutDataRequestNew(request);
+ WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webContext);
+ webkit_website_data_manager_fetch(manager, WEBKIT_WEBSITE_DATA_ALL, NULL, (GAsyncReadyCallback)gotWebsiteDataCallback, dataRequest);
+}
+
+static void aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, WebKitWebContext *webContext)
{
GInputStream *stream;
gsize streamLength;
@@ -228,7 +439,9 @@ aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData
webkit_uri_scheme_request_finish(request, stream, streamLength, "text/html");
g_object_unref(stream);
- } else {
+ } else if (!g_strcmp0(path, "data"))
+ aboutDataHandleRequest(request, webContext);
+ else {
error = g_error_new(MINI_BROWSER_ERROR, MINI_BROWSER_ERROR_INVALID_ABOUT_PATH, "Invalid about:%s page.", path);
webkit_uri_scheme_request_finish_error(request, error);
g_error_free(error);
@@ -238,12 +451,9 @@ aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
-
- const gchar *multiprocess = g_getenv("MINIBROWSER_MULTIPROCESS");
- if (multiprocess && *multiprocess) {
- webkit_web_context_set_process_model(webkit_web_context_get_default(),
- WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
- }
+#if ENABLE_DEVELOPER_MODE
+ g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
+#endif
GOptionContext *context = g_option_context_new(NULL);
g_option_context_add_main_entries(context, commandLineOptions, 0);
@@ -252,6 +462,7 @@ int main(int argc, char *argv[])
WebKitSettings *webkitSettings = webkit_settings_new();
webkit_settings_set_enable_developer_extras(webkitSettings, TRUE);
webkit_settings_set_enable_webgl(webkitSettings, TRUE);
+ webkit_settings_set_enable_media_stream(webkitSettings, TRUE);
if (!addSettingsGroupToContext(context, webkitSettings))
g_clear_object(&webkitSettings);
@@ -265,24 +476,62 @@ int main(int argc, char *argv[])
}
g_option_context_free (context);
- g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
+ WebKitWebContext *webContext = privateMode ? webkit_web_context_new_ephemeral() : webkit_web_context_get_default();
+
+ const gchar *singleprocess = g_getenv("MINIBROWSER_SINGLEPROCESS");
+ webkit_web_context_set_process_model(webContext, (singleprocess && *singleprocess) ?
+ WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS : WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
// Enable the favicon database, by specifying the default directory.
- webkit_web_context_set_favicon_database_directory(webkit_web_context_get_default(), NULL);
+ webkit_web_context_set_favicon_database_directory(webContext, NULL);
+
+ webkit_web_context_register_uri_scheme(webContext, BROWSER_ABOUT_SCHEME, (WebKitURISchemeRequestCallback)aboutURISchemeRequestCallback, webContext, NULL);
+
+ WebKitUserContentManager *userContentManager = webkit_user_content_manager_new();
+ webkit_user_content_manager_register_script_message_handler(userContentManager, "aboutData");
+ g_signal_connect(userContentManager, "script-message-received::aboutData", G_CALLBACK(aboutDataScriptMessageReceivedCallback), webContext);
- webkit_web_context_register_uri_scheme(webkit_web_context_get_default(), miniBrowserAboutScheme, aboutURISchemeRequestCallback, NULL, NULL);
+ BrowserWindow *mainWindow = BROWSER_WINDOW(browser_window_new(NULL, webContext));
+ if (geometry)
+ gtk_window_parse_geometry(GTK_WINDOW(mainWindow), geometry);
+ GtkWidget *firstTab = NULL;
if (uriArguments) {
int i;
- for (i = 0; uriArguments[i]; i++)
- createBrowserWindow(uriArguments[i], webkitSettings);
- } else
- createBrowserWindow("http://www.webkitgtk.org/", webkitSettings);
+ for (i = 0; uriArguments[i]; i++) {
+ WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager);
+ if (!i)
+ firstTab = GTK_WIDGET(webView);
+ gchar *url = argumentToURL(uriArguments[i]);
+ webkit_web_view_load_uri(webView, url);
+ g_free(url);
+ }
+ } else {
+ WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager);
+ firstTab = GTK_WIDGET(webView);
+
+ if (backgroundColor)
+ browser_window_set_background_color(mainWindow, backgroundColor);
+
+ if (!editorMode) {
+ if (sessionFile)
+ browser_window_load_session(mainWindow, sessionFile);
+ else
+ webkit_web_view_load_uri(webView, BROWSER_DEFAULT_URL);
+ }
+ }
+
+ gtk_widget_grab_focus(firstTab);
+ gtk_widget_show(GTK_WIDGET(mainWindow));
g_clear_object(&webkitSettings);
+ g_clear_object(&userContentManager);
gtk_main();
+ if (privateMode)
+ g_object_unref(webContext);
+
return 0;
}