diff options
Diffstat (limited to 'embed/xulrunner/components/ContentHandler.cpp')
-rw-r--r-- | embed/xulrunner/components/ContentHandler.cpp | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/embed/xulrunner/components/ContentHandler.cpp b/embed/xulrunner/components/ContentHandler.cpp new file mode 100644 index 000000000..051f5e077 --- /dev/null +++ b/embed/xulrunner/components/ContentHandler.cpp @@ -0,0 +1,480 @@ +/* + * Copyright © 2001 Philip Langdale + * Copyright © 2003 Marco Pesenti Gritti + * Copyright © 2003 Xan Lopez + * Copyright © 2004 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Id$ + */ + +#include <xpcom-config.h> +#include "config.h" + +#include <glib/gi18n.h> +#include <gtk/gtkbutton.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtkimage.h> +#include <gtk/gtkmain.h> +#include <gtk/gtkmessagedialog.h> +#include <gtk/gtkstock.h> +#include <libgnomevfs/gnome-vfs-mime.h> +#include <libgnomevfs/gnome-vfs-utils.h> + +#include <nsStringGlue.h> + +#include <nsCExternalHandlerService.h> +#include <nsComponentManagerUtils.h> +#include <nsIDOMWindow.h> +#include <nsIInterfaceRequestorUtils.h> +#include <nsILocalFile.h> +#include <nsIMIMEInfo.h> +#include <nsIURL.h> +#include <nsMemory.h> +#include <nsNetError.h> +#include <nsServiceManagerUtils.h> + +#include "eel-gconf-extensions.h" +#include "ephy-debug.h" +#include "ephy-embed-shell.h" +#include "ephy-embed-single.h" +#include "ephy-file-chooser.h" +#include "ephy-file-helpers.h" +#include "ephy-gui.h" +#include "ephy-prefs.h" +#include "ephy-stock-icons.h" + +#include "AutoJSContextStack.h" +#include "AutoWindowModalState.h" +#include "EphyUtils.h" +#include "MozDownload.h" + +#include "ContentHandler.h" + +/* FIXME: we don't generally have a timestamp for the user action which initiated this + * content handler. + */ +GContentHandler::GContentHandler() +: mUserTime(0) +{ + LOG ("GContentHandler ctor (%p)", this); +} + +GContentHandler::~GContentHandler() +{ + LOG ("GContentHandler dtor (%p)", this); +} + +NS_IMPL_ISUPPORTS1(GContentHandler, nsIHelperAppLauncherDialog) + +/* void show (in nsIHelperAppLauncher aLauncher, in nsISupports aContext, in unsigned long aReason); */ +NS_IMETHODIMP +GContentHandler::Show (nsIHelperAppLauncher *aLauncher, + nsISupports *aContext, + PRUint32 aReason) +{ + nsresult rv; + EphyEmbedSingle *single; + gboolean handled = FALSE; + + /* FIXME: handle aForced / aReason argument in some way? */ + + mContext = aContext; + + /* Check for a potential veto */ + nsCOMPtr<nsIDOMWindow> window (do_GetInterface (aContext)); + GtkWidget *embed = EphyUtils::FindEmbed (window); + if (EPHY_IS_EMBED (embed)) + { + if (g_object_get_data (G_OBJECT (embed), "content-handler-deny")) + { + return NS_OK; + } + } + + mLauncher = aLauncher; + rv = Init (); + NS_ENSURE_SUCCESS (rv, rv); + + single = EPHY_EMBED_SINGLE (ephy_embed_shell_get_embed_single (embed_shell)); + g_signal_emit_by_name (single, "handle_content", mMimeType.get(), + mUrl.get(), &handled); + + if (!handled) + { + MIMEInitiateAction (); + } + else + { + mLauncher->Cancel (NS_BINDING_ABORTED); + } + + return NS_OK; +} + +/* nsILocalFile promptForSaveToFile (in nsISupports aWindowContext, in wstring aDefaultFile, in wstring aSuggestedFileExtension); */ +NS_IMETHODIMP GContentHandler::PromptForSaveToFile( + nsIHelperAppLauncher *aLauncher, + nsISupports *aWindowContext, + const PRUnichar *aDefaultFile, + const PRUnichar *aSuggestedFileExtension, + nsILocalFile **_retval) +{ + EphyFileChooser *dialog; + int response; + char *filename = NULL; + nsCString defaultFile; + + NS_UTF16ToCString (nsString (aDefaultFile), + NS_CSTRING_ENCODING_UTF8, defaultFile); + + if (mAction != CONTENT_ACTION_SAVEAS) + { + return BuildDownloadPath (defaultFile.get(), _retval); + } + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + nsCOMPtr<nsIDOMWindow> parentDOMWindow (do_GetInterface (aWindowContext)); + GtkWidget *parentWindow = GTK_WIDGET (EphyUtils::FindGtkParent (parentDOMWindow)); + + AutoWindowModalState modalState (parentDOMWindow); + + dialog = ephy_file_chooser_new (_("Save"), parentWindow, + GTK_FILE_CHOOSER_ACTION_SAVE, + CONF_STATE_SAVE_DIR, + EPHY_FILE_FILTER_ALL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), defaultFile.get()); + + if (parentWindow) + { + gtk_window_group_add_window (ephy_gui_ensure_window_group (GTK_WINDOW (parentWindow)), + GTK_WINDOW (dialog)); + } + + /* FIXME: this will only be the real user time if we came from ::Show */ + ephy_gui_window_update_user_time (GTK_WIDGET (dialog), (guint32) mUserTime); + + /* FIXME: modal -- mozilla sucks! */ + do + { + g_free (filename); + response = gtk_dialog_run (GTK_DIALOG (dialog)); + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + } while (response == GTK_RESPONSE_ACCEPT + && !ephy_gui_check_location_writable (GTK_WIDGET (dialog), filename)); + + if (response == GTK_RESPONSE_ACCEPT) + { + nsCOMPtr <nsILocalFile> destFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); + NS_ENSURE_TRUE (destFile, NS_ERROR_FAILURE); + + destFile->InitWithNativePath (nsCString (filename)); + g_free (filename); + + NS_IF_ADDREF (*_retval = destFile); + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + return NS_OK; + } + else + { + gtk_widget_destroy (GTK_WIDGET (dialog)); + g_free (filename); + + return NS_ERROR_FAILURE; + } +} + +NS_METHOD GContentHandler::Init () +{ + nsresult rv; + + NS_ENSURE_TRUE (mLauncher, NS_ERROR_FAILURE); + + nsCOMPtr<nsIMIMEInfo> MIMEInfo; + mLauncher->GetMIMEInfo (getter_AddRefs(MIMEInfo)); + NS_ENSURE_TRUE (MIMEInfo, NS_ERROR_FAILURE); + + rv = MIMEInfo->GetMIMEType (mMimeType); + + nsCOMPtr<nsIURI> uri; + mLauncher->GetSource (getter_AddRefs(uri)); + NS_ENSURE_TRUE (uri, NS_ERROR_FAILURE); + + uri->GetSpec (mUrl); + + return NS_OK; +} + +static void +response_cb (GtkWidget *dialog, + int response, + GContentHandler *self) +{ + gtk_widget_destroy (dialog); + + if (response > 0) + { + self->mAction = (ContentAction) response; + } + else + { + self->mAction = CONTENT_ACTION_NONE; + } + + self->MIMEDoAction (); +} + +static void +release_cb (GContentHandler *data) +{ + NS_RELEASE (data); +} + +NS_METHOD GContentHandler::MIMEConfirmAction () +{ + GtkWidget *dialog, *button, *image; + const char *action_label; + const char *mime_description; + nsCString file_name; + + nsCOMPtr<nsIDOMWindow> parentDOMWindow = do_GetInterface (mContext); + GtkWindow *parentWindow = GTK_WINDOW (EphyUtils::FindGtkParent(parentDOMWindow)); + + action_label = (mAction == CONTENT_ACTION_OPEN) || + (mAction == CONTENT_ACTION_OPEN_TMP) ? + GTK_STOCK_OPEN : STOCK_DOWNLOAD; + + mime_description = gnome_vfs_mime_get_description (mMimeType.get()); + if (mime_description == NULL) + { + /* Translators: The text before the "|" is context to help you decide on + * the correct translation. You MUST OMIT it in the translated string. */ + mime_description = Q_("File Type:|Unknown"); + } + + /* We have one tiny, minor issue, the filename can be empty (""), + is that severe enough to be completely fixed ? */ + nsString suggested; + + mLauncher->GetSuggestedFileName (suggested); + NS_UTF16ToCString (suggested, + NS_CSTRING_ENCODING_UTF8, file_name); + + if (mPermission != EPHY_MIME_PERMISSION_SAFE && mHelperApp) + { + dialog = gtk_message_dialog_new + (parentWindow, GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, + _("Download this potentially unsafe file?")); + + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (dialog), + /* translators: First %s is the file type description, + Second %s is the file name */ + _("File Type: “%s”.\n\nIt is unsafe to open “%s” as " + "it could potentially damage your documents or " + "invade your privacy. You can download it instead."), + mime_description, file_name.get()); + } + else if (mAction == CONTENT_ACTION_OPEN_TMP && mHelperApp) + { + dialog = gtk_message_dialog_new + (parentWindow, GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, + _("Open this file?")); + + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (dialog), + /* translators: First %s is the file type description, + Second %s is the file name, + Third %s is the application used to open the file */ + _("File Type: “%s”.\n\nYou can open “%s” using “%s” or save it."), + mime_description, file_name.get(), mHelperApp->name); + } + else + { + dialog = gtk_message_dialog_new + (parentWindow, GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, + _("Download this file?")); + + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (dialog), + /* translators: First %s is the file type description, + Second %s is the file name */ + _("File Type: “%s”.\n\nYou have no application able to open “%s”. " + "You can download it instead."), + mime_description, file_name.get()); + } + + button = gtk_button_new_with_label (_("_Save As...")); + image = gtk_image_new_from_stock (GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (button), image); + /* don't show the image! see bug #307818 */ + gtk_widget_show (button); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, CONTENT_ACTION_SAVEAS); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (dialog), + action_label, mAction); + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + int defaultResponse = mAction == CONTENT_ACTION_NONE + ? (int) GTK_RESPONSE_CANCEL + : (int) mAction; + gtk_dialog_set_default_response (GTK_DIALOG (dialog), defaultResponse); + + NS_ADDREF_THIS(); + g_signal_connect_data (dialog, "response", + G_CALLBACK (response_cb), this, + (GClosureNotify) release_cb, (GConnectFlags) 0); + + /* FIXME: should find a way to get the user time of the user action which + * initiated this content handler + */ + gtk_window_present (GTK_WINDOW (dialog)); + + return NS_OK; +} + +NS_METHOD GContentHandler::MIMEInitiateAction (void) +{ + gboolean auto_downloads; + + if (eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_SAVE_TO_DISK)) return NS_OK; + + auto_downloads = eel_gconf_get_boolean (CONF_AUTO_DOWNLOADS); + + mHelperApp = gnome_vfs_mime_get_default_application (mMimeType.get()); + mPermission = ephy_file_check_mime (mMimeType.get()); + + /* HACK! Check that this 'helper application' isn't Epiphany itself, + * see bug #310023. + */ + if (mHelperApp) + { + const char *id = gnome_vfs_mime_application_get_desktop_id (mHelperApp); + + /* FIXME! menu editing can make this check fail!!!! */ + if (id && strcmp (id, "epiphany.desktop") == 0) + { + mHelperApp = nsnull; + } + } + + if (auto_downloads) + { + mAction = CONTENT_ACTION_OPEN; + } + else + { + mAction = CONTENT_ACTION_OPEN_TMP; + } + + if (!mHelperApp || mPermission != EPHY_MIME_PERMISSION_SAFE) + { + mAction = CONTENT_ACTION_DOWNLOAD; + } + + if (!auto_downloads || mAction == CONTENT_ACTION_DOWNLOAD) + { + MIMEConfirmAction (); + } + else + { + MIMEDoAction (); + } + + return NS_OK; +} + +NS_METHOD GContentHandler::MIMEDoAction (void) +{ + /* This is okay, since we either clicked on a button, or we get 0 */ + mUserTime = gtk_get_current_event_time (); + + nsCOMPtr<nsIMIMEInfo> mimeInfo; + mLauncher->GetMIMEInfo(getter_AddRefs(mimeInfo)); + NS_ENSURE_TRUE (mimeInfo, NS_ERROR_FAILURE); + + char *info = NULL; + + if (mAction == CONTENT_ACTION_OPEN) + { + g_return_val_if_fail (mHelperApp, NS_ERROR_FAILURE); + + const char *id; + id = gnome_vfs_mime_application_get_desktop_id (mHelperApp); + + /* The current time is fine here as the user has just clicked + * a button (it is used as the time for the application opening) + */ + info = g_strdup_printf ("gnome-default:%d:%s", gtk_get_current_event_time(), id); + } + else if (mAction == CONTENT_ACTION_DOWNLOAD) + { + info = g_strdup_printf ("gnome-browse-to-file:%d", gtk_get_current_event_time()); + } + + /* See http://bugzilla.gnome.org/show_bug.cgi?id=456945 */ + // FIXME convert this properly to 1.9 +#ifdef HAVE_GECKO_1_9 + if (info != NULL) + { + nsString desc; + NS_CStringToUTF16 (nsCString (info), + NS_CSTRING_ENCODING_UTF8, desc); + g_free (info); + + /* HACK we use the application description to ask + MozDownload to open the file when download + is finished */ + mimeInfo->SetApplicationDescription (desc); + } + else + { + mimeInfo->SetApplicationDescription (nsString ()); + } +#endif /* HAVE_GECKO_1_9 */ + + if (mAction == CONTENT_ACTION_OPEN) + { + mLauncher->SaveToDisk (nsnull, PR_FALSE); + } + else if (mAction == CONTENT_ACTION_OPEN_TMP) + { + mLauncher->LaunchWithApplication (nsnull, PR_FALSE); + } + else if (mAction == CONTENT_ACTION_NONE) + { + mLauncher->Cancel (NS_BINDING_ABORTED); + } + else + { + mLauncher->SaveToDisk (nsnull, PR_FALSE); + } + + return NS_OK; +} |