| /* |
| * 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; |
| }; |
| |
| static GHashTable *userMediaPermissionGrantedOrigins; |
| struct _BrowserTabClass { |
| GtkBoxClass parent; |
| }; |
| |
| G_DEFINE_TYPE(BrowserTab, browser_tab, GTK_TYPE_BOX) |
| |
| typedef struct { |
| WebKitPermissionRequest *request; |
| gchar *origin; |
| } PermissionRequestData; |
| |
| static PermissionRequestData *permissionRequestDataNew(WebKitPermissionRequest *request, gchar *origin) |
| { |
| PermissionRequestData *data = g_malloc0(sizeof(PermissionRequestData)); |
| |
| data->request = g_object_ref(request); |
| data->origin = origin; |
| |
| return data; |
| } |
| |
| static void permissionRequestDataFree(PermissionRequestData *data) |
| { |
| g_clear_object(&data->request); |
| g_clear_pointer(&data->origin, g_free); |
| g_free(data); |
| } |
| |
| static gchar *getWebViewOrigin(WebKitWebView *webView) |
| { |
| WebKitSecurityOrigin *origin = webkit_security_origin_new_for_uri(webkit_web_view_get_uri(webView)); |
| gchar *originStr = webkit_security_origin_to_string(origin); |
| webkit_security_origin_unref(origin); |
| |
| return originStr; |
| } |
| |
| 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, PermissionRequestData *requestData) |
| { |
| switch (response) { |
| case GTK_RESPONSE_YES: |
| if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(requestData->request)) |
| g_hash_table_add(userMediaPermissionGrantedOrigins, g_strdup(requestData->origin)); |
| |
| webkit_permission_request_allow(requestData->request); |
| break; |
| default: |
| if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(requestData->request)) |
| g_hash_table_remove(userMediaPermissionGrantedOrigins, requestData->origin); |
| |
| webkit_permission_request_deny(requestData->request); |
| break; |
| } |
| |
| gtk_widget_destroy(dialog); |
| g_clear_pointer(&requestData, permissionRequestDataFree); |
| } |
| |
| 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 if (WEBKIT_IS_DEVICE_INFO_PERMISSION_REQUEST(request)) { |
| char* origin = getWebViewOrigin(webView); |
| if (g_hash_table_contains(userMediaPermissionGrantedOrigins, origin)) { |
| webkit_permission_request_allow(request); |
| g_free(origin); |
| return TRUE; |
| } |
| g_free(origin); |
| return FALSE; |
| } else { |
| g_print("%s request not handled\n", G_OBJECT_TYPE_NAME(request)); |
| return FALSE; |
| } |
| |
| GtkWidget *dialog = createInfoBarQuestionMessage(title, text); |
| g_free(text); |
| g_signal_connect(dialog, "response", G_CALLBACK(permissionRequestDialogResponse), permissionRequestDataNew(request, |
| getWebViewOrigin(webView))); |
| |
| 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; |
| |
| if (!userMediaPermissionGrantedOrigins) |
| userMediaPermissionGrantedOrigins = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
| |
| 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); |
| } |
| } |
| |
| void browser_tab_set_background_color(BrowserTab *tab, GdkRGBA *rgba) |
| { |
| g_return_if_fail(BROWSER_IS_TAB(tab)); |
| g_return_if_fail(rgba); |
| |
| GdkRGBA viewRGBA; |
| webkit_web_view_get_background_color(tab->webView, &viewRGBA); |
| if (gdk_rgba_equal(rgba, &viewRGBA)) |
| return; |
| |
| webkit_web_view_set_background_color(tab->webView, rgba); |
| } |