| /* |
| * Copyright (C) 2012 Igalia S.L. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "WebKitPrintOperation.h" |
| |
| #include "WebKitPrintCustomWidgetPrivate.h" |
| #include "WebKitPrintOperationPrivate.h" |
| #include "WebKitPrivate.h" |
| #include "WebKitWebViewPrivate.h" |
| #include "WebPageProxy.h" |
| #include <WebCore/GtkUtilities.h> |
| #include <WebCore/NotImplemented.h> |
| #include <glib/gi18n-lib.h> |
| #include <wtf/glib/GRefPtr.h> |
| #include <wtf/glib/GUniquePtr.h> |
| #include <wtf/text/CString.h> |
| |
| #if HAVE(GTK_UNIX_PRINTING) |
| #include <gtk/gtkunixprint.h> |
| #endif |
| |
| using namespace WebKit; |
| |
| /** |
| * SECTION: WebKitPrintOperation |
| * @Short_description: Controls a print operation |
| * @Title: WebKitPrintOperation |
| * |
| * A #WebKitPrintOperation controls a print operation in WebKit. With |
| * a similar API to #GtkPrintOperation, it lets you set the print |
| * settings with webkit_print_operation_set_print_settings() or |
| * display the print dialog with webkit_print_operation_run_dialog(). |
| * |
| */ |
| |
| enum { |
| PROP_0, |
| |
| PROP_WEB_VIEW, |
| PROP_PRINT_SETTINGS, |
| PROP_PAGE_SETUP |
| }; |
| |
| enum { |
| FINISHED, |
| FAILED, |
| CREATE_CUSTOM_WIDGET, |
| |
| LAST_SIGNAL |
| }; |
| |
| struct _WebKitPrintOperationPrivate { |
| ~_WebKitPrintOperationPrivate() |
| { |
| if (webView) |
| g_object_remove_weak_pointer(G_OBJECT(webView), reinterpret_cast<void**>(&webView)); |
| } |
| |
| WebKitWebView* webView; |
| PrintInfo::PrintMode printMode; |
| |
| GRefPtr<GtkPrintSettings> printSettings; |
| GRefPtr<GtkPageSetup> pageSetup; |
| }; |
| |
| static guint signals[LAST_SIGNAL] = { 0, }; |
| |
| WEBKIT_DEFINE_TYPE(WebKitPrintOperation, webkit_print_operation, G_TYPE_OBJECT) |
| |
| static void webkitPrintOperationConstructed(GObject* object) |
| { |
| G_OBJECT_CLASS(webkit_print_operation_parent_class)->constructed(object); |
| |
| WebKitPrintOperationPrivate* priv = WEBKIT_PRINT_OPERATION(object)->priv; |
| g_object_add_weak_pointer(G_OBJECT(priv->webView), reinterpret_cast<void**>(&priv->webView)); |
| } |
| |
| static void webkitPrintOperationGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec) |
| { |
| WebKitPrintOperation* printOperation = WEBKIT_PRINT_OPERATION(object); |
| |
| switch (propId) { |
| case PROP_WEB_VIEW: |
| g_value_take_object(value, printOperation->priv->webView); |
| break; |
| case PROP_PRINT_SETTINGS: |
| g_value_set_object(value, printOperation->priv->printSettings.get()); |
| break; |
| case PROP_PAGE_SETUP: |
| g_value_set_object(value, printOperation->priv->pageSetup.get()); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec); |
| } |
| } |
| |
| static void webkitPrintOperationSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* paramSpec) |
| { |
| WebKitPrintOperation* printOperation = WEBKIT_PRINT_OPERATION(object); |
| |
| switch (propId) { |
| case PROP_WEB_VIEW: |
| printOperation->priv->webView = WEBKIT_WEB_VIEW(g_value_get_object(value)); |
| break; |
| case PROP_PRINT_SETTINGS: |
| webkit_print_operation_set_print_settings(printOperation, GTK_PRINT_SETTINGS(g_value_get_object(value))); |
| break; |
| case PROP_PAGE_SETUP: |
| webkit_print_operation_set_page_setup(printOperation, GTK_PAGE_SETUP(g_value_get_object(value))); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec); |
| } |
| } |
| |
| static gboolean webkitPrintOperationAccumulatorObjectHandled(GSignalInvocationHint*, GValue* returnValue, const GValue* handlerReturn, gpointer) |
| { |
| void* object = g_value_get_object(handlerReturn); |
| if (object) |
| g_value_set_object(returnValue, object); |
| |
| return !object; |
| } |
| |
| static void webkit_print_operation_class_init(WebKitPrintOperationClass* printOperationClass) |
| { |
| GObjectClass* gObjectClass = G_OBJECT_CLASS(printOperationClass); |
| gObjectClass->constructed = webkitPrintOperationConstructed; |
| gObjectClass->get_property = webkitPrintOperationGetProperty; |
| gObjectClass->set_property = webkitPrintOperationSetProperty; |
| |
| /** |
| * WebKitPrintOperation:web-view: |
| * |
| * The #WebKitWebView that will be printed. |
| */ |
| g_object_class_install_property(gObjectClass, |
| PROP_WEB_VIEW, |
| g_param_spec_object("web-view", |
| _("Web View"), |
| _("The web view that will be printed"), |
| WEBKIT_TYPE_WEB_VIEW, |
| static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY))); |
| |
| /** |
| * WebKitPrintOperation:print-settings: |
| * |
| * The initial #GtkPrintSettings for the print operation. |
| */ |
| g_object_class_install_property(gObjectClass, |
| PROP_PRINT_SETTINGS, |
| g_param_spec_object("print-settings", |
| _("Print Settings"), |
| _("The initial print settings for the print operation"), |
| GTK_TYPE_PRINT_SETTINGS, |
| WEBKIT_PARAM_READWRITE)); |
| /** |
| * WebKitPrintOperation:page-setup: |
| * |
| * The initial #GtkPageSetup for the print operation. |
| */ |
| g_object_class_install_property(gObjectClass, |
| PROP_PAGE_SETUP, |
| g_param_spec_object("page-setup", |
| _("Page Setup"), |
| _("The initial page setup for the print operation"), |
| GTK_TYPE_PAGE_SETUP, |
| WEBKIT_PARAM_READWRITE)); |
| |
| /** |
| * WebKitPrintOperation::finished: |
| * @print_operation: the #WebKitPrintOperation on which the signal was emitted |
| * |
| * Emitted when the print operation has finished doing everything |
| * required for printing. |
| */ |
| signals[FINISHED] = |
| g_signal_new("finished", |
| G_TYPE_FROM_CLASS(gObjectClass), |
| G_SIGNAL_RUN_LAST, |
| 0, 0, 0, |
| g_cclosure_marshal_VOID__VOID, |
| G_TYPE_NONE, 0); |
| |
| /** |
| * WebKitPrintOperation::failed: |
| * @print_operation: the #WebKitPrintOperation on which the signal was emitted |
| * @error: the #GError that was triggered |
| * |
| * Emitted when an error occurs while printing. The given @error, of the domain |
| * %WEBKIT_PRINT_ERROR, contains further details of the failure. |
| * The #WebKitPrintOperation::finished signal is emitted after this one. |
| */ |
| signals[FAILED] = |
| g_signal_new( |
| "failed", |
| G_TYPE_FROM_CLASS(gObjectClass), |
| G_SIGNAL_RUN_LAST, |
| 0, 0, 0, |
| g_cclosure_marshal_VOID__BOXED, |
| G_TYPE_NONE, 1, |
| G_TYPE_ERROR | G_SIGNAL_TYPE_STATIC_SCOPE); |
| |
| /** |
| * WebKitPrintOperation::create-custom-widget: |
| * @print_operation: the #WebKitPrintOperation on which the signal was emitted |
| * |
| * Emitted when displaying the print dialog with webkit_print_operation_run_dialog(). |
| * The returned #WebKitPrintCustomWidget will be added to the print dialog and |
| * it will be owned by the @print_operation. However, the object is guaranteed |
| * to be alive until the #WebKitPrintCustomWidget::apply is emitted. |
| * |
| * Returns: (transfer full): A #WebKitPrintCustomWidget that will be embedded in the dialog. |
| * |
| * Since: 2.16 |
| */ |
| signals[CREATE_CUSTOM_WIDGET] = |
| g_signal_new( |
| "create-custom-widget", |
| G_TYPE_FROM_CLASS(gObjectClass), |
| G_SIGNAL_RUN_LAST, |
| 0, |
| webkitPrintOperationAccumulatorObjectHandled, 0, |
| g_cclosure_marshal_generic, |
| WEBKIT_TYPE_PRINT_CUSTOM_WIDGET, 0); |
| } |
| |
| #if HAVE(GTK_UNIX_PRINTING) |
| static void notifySelectedPrinterCallback(GtkPrintUnixDialog* dialog, GParamSpec*, WebKitPrintCustomWidget* printCustomWidget) |
| { |
| webkitPrintCustomWidgetEmitUpdateCustomWidgetSignal(printCustomWidget, gtk_print_unix_dialog_get_page_setup(dialog), gtk_print_unix_dialog_get_settings(dialog)); |
| } |
| |
| static WebKitPrintOperationResponse webkitPrintOperationRunDialog(WebKitPrintOperation* printOperation, GtkWindow* parent) |
| { |
| GtkPrintUnixDialog* printDialog = GTK_PRINT_UNIX_DIALOG(gtk_print_unix_dialog_new(0, parent)); |
| gtk_print_unix_dialog_set_manual_capabilities(printDialog, static_cast<GtkPrintCapabilities>(GTK_PRINT_CAPABILITY_NUMBER_UP |
| | GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
| | GTK_PRINT_CAPABILITY_PAGE_SET |
| | GTK_PRINT_CAPABILITY_REVERSE |
| | GTK_PRINT_CAPABILITY_COPIES |
| | GTK_PRINT_CAPABILITY_COLLATE |
| | GTK_PRINT_CAPABILITY_SCALE)); |
| |
| WebKitPrintOperationPrivate* priv = printOperation->priv; |
| // Make sure the initial settings of the GtkPrintUnixDialog is a valid |
| // GtkPrintSettings object to work around a crash happening in the GTK+ |
| // file print backend. https://bugzilla.gnome.org/show_bug.cgi?id=703784. |
| if (!priv->printSettings) |
| priv->printSettings = adoptGRef(gtk_print_settings_new()); |
| gtk_print_unix_dialog_set_settings(printDialog, priv->printSettings.get()); |
| |
| if (priv->pageSetup) |
| gtk_print_unix_dialog_set_page_setup(printDialog, priv->pageSetup.get()); |
| |
| gtk_print_unix_dialog_set_embed_page_setup(printDialog, TRUE); |
| |
| GRefPtr<WebKitPrintCustomWidget> customWidget; |
| g_signal_emit(printOperation, signals[CREATE_CUSTOM_WIDGET], 0, &customWidget.outPtr()); |
| if (customWidget) { |
| const gchar* widgetTitle = webkit_print_custom_widget_get_title(customWidget.get()); |
| GtkWidget* widget = webkit_print_custom_widget_get_widget(customWidget.get()); |
| |
| g_signal_connect(printDialog, "notify::selected-printer", G_CALLBACK(notifySelectedPrinterCallback), customWidget.get()); |
| gtk_print_unix_dialog_add_custom_tab(printDialog, widget, gtk_label_new(widgetTitle)); |
| } |
| |
| WebKitPrintOperationResponse returnValue = WEBKIT_PRINT_OPERATION_RESPONSE_CANCEL; |
| if (gtk_dialog_run(GTK_DIALOG(printDialog)) == GTK_RESPONSE_OK) { |
| priv->printSettings = adoptGRef(gtk_print_unix_dialog_get_settings(printDialog)); |
| priv->pageSetup = gtk_print_unix_dialog_get_page_setup(printDialog); |
| returnValue = WEBKIT_PRINT_OPERATION_RESPONSE_PRINT; |
| if (customWidget) |
| webkitPrintCustomWidgetEmitCustomWidgetApplySignal(customWidget.get()); |
| } |
| |
| gtk_widget_destroy(GTK_WIDGET(printDialog)); |
| |
| return returnValue; |
| } |
| #else |
| // TODO: We need to add an implementation for Windows. |
| static WebKitPrintOperationResponse webkitPrintOperationRunDialog(WebKitPrintOperation*, GtkWindow*) |
| { |
| notImplemented(); |
| return WEBKIT_PRINT_OPERATION_RESPONSE_CANCEL; |
| } |
| #endif |
| |
| static void drawPagesForPrintingCompleted(API::Error* wkPrintError, WebKitPrintOperation* printOperation) |
| { |
| // When running synchronously WebPageProxy::printFrame() calls endPrinting(). |
| if (printOperation->priv->printMode == PrintInfo::PrintModeAsync && printOperation->priv->webView) |
| webkitWebViewGetPage(printOperation->priv->webView).endPrinting(); |
| |
| const WebCore::ResourceError& resourceError = wkPrintError ? wkPrintError->platformError() : WebCore::ResourceError(); |
| if (!resourceError.isNull()) { |
| GUniquePtr<GError> printError(g_error_new_literal(g_quark_from_string(resourceError.domain().utf8().data()), |
| toWebKitError(resourceError.errorCode()), resourceError.localizedDescription().utf8().data())); |
| g_signal_emit(printOperation, signals[FAILED], 0, printError.get()); |
| } |
| g_signal_emit(printOperation, signals[FINISHED], 0, NULL); |
| } |
| |
| static void webkitPrintOperationPrintPagesForFrame(WebKitPrintOperation* printOperation, WebFrameProxy* webFrame, GtkPrintSettings* printSettings, GtkPageSetup* pageSetup) |
| { |
| PrintInfo printInfo(printSettings, pageSetup, printOperation->priv->printMode); |
| auto& page = webkitWebViewGetPage(printOperation->priv->webView); |
| g_object_ref(printOperation); |
| page.drawPagesForPrinting(webFrame, printInfo, PrintFinishedCallback::create([printOperation](API::Error* printError, CallbackBase::Error) { |
| drawPagesForPrintingCompleted(printError, adoptGRef(printOperation).get()); |
| })); |
| } |
| |
| WebKitPrintOperationResponse webkitPrintOperationRunDialogForFrame(WebKitPrintOperation* printOperation, GtkWindow* parent, WebFrameProxy* webFrame) |
| { |
| WebKitPrintOperationPrivate* priv = printOperation->priv; |
| if (!parent) { |
| GtkWidget* toplevel = gtk_widget_get_toplevel(GTK_WIDGET(priv->webView)); |
| if (WebCore::widgetIsOnscreenToplevelWindow(toplevel)) |
| parent = GTK_WINDOW(toplevel); |
| } |
| |
| WebKitPrintOperationResponse response = webkitPrintOperationRunDialog(printOperation, parent); |
| if (response == WEBKIT_PRINT_OPERATION_RESPONSE_CANCEL) |
| return response; |
| |
| webkitPrintOperationPrintPagesForFrame(printOperation, webFrame, priv->printSettings.get(), priv->pageSetup.get()); |
| return response; |
| } |
| |
| void webkitPrintOperationSetPrintMode(WebKitPrintOperation* printOperation, PrintInfo::PrintMode printMode) |
| { |
| printOperation->priv->printMode = printMode; |
| } |
| |
| /** |
| * webkit_print_operation_new: |
| * @web_view: a #WebKitWebView |
| * |
| * Create a new #WebKitPrintOperation to print @web_view contents. |
| * |
| * Returns: (transfer full): a new #WebKitPrintOperation. |
| */ |
| WebKitPrintOperation* webkit_print_operation_new(WebKitWebView* webView) |
| { |
| g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), 0); |
| |
| return WEBKIT_PRINT_OPERATION(g_object_new(WEBKIT_TYPE_PRINT_OPERATION, "web-view", webView, NULL)); |
| } |
| |
| /** |
| * webkit_print_operation_get_print_settings: |
| * @print_operation: a #WebKitPrintOperation |
| * |
| * Return the current print settings of @print_operation. It returns %NULL until |
| * either webkit_print_operation_set_print_settings() or webkit_print_operation_run_dialog() |
| * have been called. |
| * |
| * Returns: (transfer none): the current #GtkPrintSettings of @print_operation. |
| */ |
| GtkPrintSettings* webkit_print_operation_get_print_settings(WebKitPrintOperation* printOperation) |
| { |
| g_return_val_if_fail(WEBKIT_IS_PRINT_OPERATION(printOperation), 0); |
| |
| return printOperation->priv->printSettings.get(); |
| } |
| |
| /** |
| * webkit_print_operation_set_print_settings: |
| * @print_operation: a #WebKitPrintOperation |
| * @print_settings: a #GtkPrintSettings to set |
| * |
| * Set the current print settings of @print_operation. Current print settings are used for |
| * the initial values of the print dialog when webkit_print_operation_run_dialog() is called. |
| */ |
| void webkit_print_operation_set_print_settings(WebKitPrintOperation* printOperation, GtkPrintSettings* printSettings) |
| { |
| g_return_if_fail(WEBKIT_IS_PRINT_OPERATION(printOperation)); |
| g_return_if_fail(GTK_IS_PRINT_SETTINGS(printSettings)); |
| |
| if (printOperation->priv->printSettings.get() == printSettings) |
| return; |
| |
| printOperation->priv->printSettings = printSettings; |
| g_object_notify(G_OBJECT(printOperation), "print-settings"); |
| } |
| |
| /** |
| * webkit_print_operation_get_page_setup: |
| * @print_operation: a #WebKitPrintOperation |
| * |
| * Return the current page setup of @print_operation. It returns %NULL until |
| * either webkit_print_operation_set_page_setup() or webkit_print_operation_run_dialog() |
| * have been called. |
| * |
| * Returns: (transfer none): the current #GtkPageSetup of @print_operation. |
| */ |
| GtkPageSetup* webkit_print_operation_get_page_setup(WebKitPrintOperation* printOperation) |
| { |
| g_return_val_if_fail(WEBKIT_IS_PRINT_OPERATION(printOperation), 0); |
| |
| return printOperation->priv->pageSetup.get(); |
| } |
| |
| /** |
| * webkit_print_operation_set_page_setup: |
| * @print_operation: a #WebKitPrintOperation |
| * @page_setup: a #GtkPageSetup to set |
| * |
| * Set the current page setup of @print_operation. Current page setup is used for the |
| * initial values of the print dialog when webkit_print_operation_run_dialog() is called. |
| */ |
| void webkit_print_operation_set_page_setup(WebKitPrintOperation* printOperation, GtkPageSetup* pageSetup) |
| { |
| g_return_if_fail(WEBKIT_IS_PRINT_OPERATION(printOperation)); |
| g_return_if_fail(GTK_IS_PAGE_SETUP(pageSetup)); |
| |
| if (printOperation->priv->pageSetup.get() == pageSetup) |
| return; |
| |
| printOperation->priv->pageSetup = pageSetup; |
| g_object_notify(G_OBJECT(printOperation), "page-setup"); |
| } |
| |
| /** |
| * webkit_print_operation_run_dialog: |
| * @print_operation: a #WebKitPrintOperation |
| * @parent: (allow-none): transient parent of the print dialog |
| * |
| * Run the print dialog and start printing using the options selected by |
| * the user. This method returns when the print dialog is closed. |
| * If the print dialog is cancelled %WEBKIT_PRINT_OPERATION_RESPONSE_CANCEL |
| * is returned. If the user clicks on the print button, %WEBKIT_PRINT_OPERATION_RESPONSE_PRINT |
| * is returned and the print operation starts. In this case, the #WebKitPrintOperation::finished |
| * signal is emitted when the operation finishes. If an error occurs while printing, the signal |
| * #WebKitPrintOperation::failed is emitted before #WebKitPrintOperation::finished. |
| * If the print dialog is not cancelled current print settings and page setup of @print_operation |
| * are updated with options selected by the user when Print button is pressed in print dialog. |
| * You can get the updated print settings and page setup by calling |
| * webkit_print_operation_get_print_settings() and webkit_print_operation_get_page_setup() |
| * after this method. |
| * |
| * Returns: the #WebKitPrintOperationResponse of the print dialog |
| */ |
| WebKitPrintOperationResponse webkit_print_operation_run_dialog(WebKitPrintOperation* printOperation, GtkWindow* parent) |
| { |
| g_return_val_if_fail(WEBKIT_IS_PRINT_OPERATION(printOperation), WEBKIT_PRINT_OPERATION_RESPONSE_CANCEL); |
| |
| auto& page = webkitWebViewGetPage(printOperation->priv->webView); |
| return webkitPrintOperationRunDialogForFrame(printOperation, parent, page.mainFrame()); |
| } |
| |
| /** |
| * webkit_print_operation_print: |
| * @print_operation: a #WebKitPrintOperation |
| * |
| * Start a print operation using current print settings and page setup |
| * without showing the print dialog. If either print settings or page setup |
| * are not set with webkit_print_operation_set_print_settings() and |
| * webkit_print_operation_set_page_setup(), the default options will be used |
| * and the print job will be sent to the default printer. |
| * The #WebKitPrintOperation::finished signal is emitted when the printing |
| * operation finishes. If an error occurs while printing the signal |
| * #WebKitPrintOperation::failed is emitted before #WebKitPrintOperation::finished. |
| */ |
| void webkit_print_operation_print(WebKitPrintOperation* printOperation) |
| { |
| g_return_if_fail(WEBKIT_IS_PRINT_OPERATION(printOperation)); |
| |
| WebKitPrintOperationPrivate* priv = printOperation->priv; |
| GRefPtr<GtkPrintSettings> printSettings = priv->printSettings ? priv->printSettings : adoptGRef(gtk_print_settings_new()); |
| GRefPtr<GtkPageSetup> pageSetup = priv->pageSetup ? priv->pageSetup : adoptGRef(gtk_page_setup_new()); |
| |
| auto& page = webkitWebViewGetPage(printOperation->priv->webView); |
| webkitPrintOperationPrintPagesForFrame(printOperation, page.mainFrame(), printSettings.get(), pageSetup.get()); |
| } |