diff options
Diffstat (limited to 'embed/xulrunner/components')
37 files changed, 9074 insertions, 2 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; +} diff --git a/embed/xulrunner/components/ContentHandler.h b/embed/xulrunner/components/ContentHandler.h new file mode 100644 index 000000000..113e29070 --- /dev/null +++ b/embed/xulrunner/components/ContentHandler.h @@ -0,0 +1,81 @@ +/* + * Copyright © 2000-2003 Marco Pesenti Gritti, + * Copyright © 2003 Xan Lopez + * + * 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$ + */ + +#ifndef CONTENT_HANDLER_H +#define CONTENT_HANDLER_H + +#include <libgnomevfs/gnome-vfs-mime-handlers.h> + +#include <nsCOMPtr.h> +#include <nsIFile.h> +#include <nsIHelperAppLauncherDialog.h> +#include <nsIURI.h> + +#include "ephy-file-helpers.h" + + +typedef enum +{ + CONTENT_ACTION_OPEN = 1, + CONTENT_ACTION_OPEN_TMP, + CONTENT_ACTION_DOWNLOAD, + CONTENT_ACTION_SAVEAS, + CONTENT_ACTION_NONE +} ContentAction; + +#define G_CONTENTHANDLER_CID \ +{ /* 16072c4a-23a6-4996-9beb-9335c06bbeae */ \ + 0x16072c4a, \ + 0x23a6, \ + 0x4996, \ + {0x9b, 0xeb, 0x93, 0x35, 0xc0, 0x6b, 0xbe, 0xae} \ +} + +class GContentHandler : public nsIHelperAppLauncherDialog +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIHELPERAPPLAUNCHERDIALOG + + GContentHandler(); + virtual ~GContentHandler(); + + NS_METHOD MIMEDoAction (); + ContentAction mAction; + private: + + NS_METHOD Init (); + + NS_METHOD MIMEInitiateAction (); + NS_METHOD MIMEConfirmAction (); + + nsCOMPtr<nsIHelperAppLauncher> mLauncher; + nsCOMPtr<nsISupports> mContext; + + GnomeVFSMimeApplication *mHelperApp; + EphyMimePermission mPermission; + + nsCString mUrl; + nsCString mMimeType; + PRUint32 mUserTime; +}; + +#endif /* CONTENT_HANDLER_H */ diff --git a/embed/xulrunner/components/EphyAboutModule.cpp b/embed/xulrunner/components/EphyAboutModule.cpp new file mode 100644 index 000000000..78ce93bec --- /dev/null +++ b/embed/xulrunner/components/EphyAboutModule.cpp @@ -0,0 +1,724 @@ +/* + * Copyright © 2001 Matt Aubury, Philip Langdale + * Copyright © 2004 Crispin Flowerday + * Copyright © 2005 Adam Hooper + * Copyright © 2005, 2007 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 <string.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <nsStringGlue.h> + +#include <nsAutoPtr.h> +#include <nsCOMPtr.h> +#include <nsIChannel.h> +#include <nsIInputStreamChannel.h> +#include <nsIInputStream.h> +#include <nsIIOService.h> +#include <nsINetUtil.h> +#include <nsIOutputStream.h> +#include <nsIScriptSecurityManager.h> +#include <nsIStorageStream.h> +#include <nsIURI.h> +#include <nsNetCID.h> +#include <nsNetUtil.h> +#include <nsServiceManagerUtils.h> + +#include "ephy-debug.h" + +#include "EphyRedirectChannel.h" + +#include "EphyAboutModule.h" + +EphyAboutModule::EphyAboutModule() +{ + LOG ("EphyAboutModule ctor [%p]\n", this); +} + +EphyAboutModule::~EphyAboutModule() +{ + LOG ("EphyAboutModule dtor [%p]\n", this); +} + +NS_IMPL_ISUPPORTS1 (EphyAboutModule, nsIAboutModule) + +/* nsIChannel newChannel (in nsIURI aURI); */ +NS_IMETHODIMP +EphyAboutModule::NewChannel(nsIURI *aURI, + nsIChannel **_retval) +{ + NS_ENSURE_ARG(aURI); + + nsCAutoString path; + aURI->GetPath (path); + + if (strncmp (path.get(), "neterror?", strlen ("neterror?")) == 0) + { + return CreateErrorPage (aURI, _retval); + } + + if (strncmp (path.get (), "recover?", strlen ("recover?")) == 0) + { + return CreateRecoverPage (aURI, _retval); + } + + if (strcmp (path.get (), "epiphany") == 0) + { + return Redirect (nsDependentCString ("file://" SHARE_DIR "/epiphany.xhtml"), _retval); + } + + return NS_ERROR_ILLEGAL_VALUE; +} + +/* unsigned long getURIFlags(in nsIURI aURI); */ +NS_IMETHODIMP +EphyAboutModule::GetURIFlags (nsIURI *aURI, + PRUint32 *_result) +{ + *_result = 0; + return NS_OK; +} + +/* private functions */ + +nsresult +EphyAboutModule::Redirect(const nsACString &aURL, + nsIChannel **_retval) +{ + *_retval = nsnull; + + nsresult rv; + nsCOMPtr<nsIURI> uri; + rv = NS_NewURI (getter_AddRefs (uri), aURL); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIChannel> tempChannel; + rv = NS_NewChannel (getter_AddRefs (tempChannel), uri); + NS_ENSURE_SUCCESS (rv, rv); + + tempChannel->SetOriginalURI (uri); + + nsCOMPtr<nsIScriptSecurityManager> securityManager = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIPrincipal> principal; + rv = securityManager->GetCodebasePrincipal(uri, getter_AddRefs(principal)); + NS_ENSURE_SUCCESS (rv, rv); + + rv = tempChannel->SetOwner(principal); + NS_ENSURE_SUCCESS (rv, rv); + + tempChannel.swap (*_retval); + + return NS_OK; +} + +nsresult +EphyAboutModule::ParseURL(const char *aURL, + nsACString &aCode, + nsACString &aRawOriginURL, + nsACString &aOriginURL, + nsACString &aOriginCharset, + nsACString &aTitle) +{ + /* The page URL is of the form "about:neterror?e=<errorcode>&u=<URL>&c=<charset>&d=<description>" */ + const char *query = strstr (aURL, "?"); + if (!query) return NS_ERROR_FAILURE; + + /* skip the '?' */ + ++query; + + char **params = g_strsplit (query, "&", -1); + if (!params) return NS_ERROR_FAILURE; + + nsCOMPtr<nsINetUtil> netUtil (do_GetService (NS_NETUTIL_CONTRACTID)); + if (!netUtil) return NS_ERROR_FAILURE; + + for (PRUint32 i = 0; params[i] != NULL; ++i) + { + char *param = params[i]; + + if (strlen (param) <= 2) continue; + + switch (param[0]) + { + case 'e': + netUtil->UnescapeString (nsDependentCString (param + 2), 0, aCode); + break; + case 'u': + aRawOriginURL.Assign (param + 2); + netUtil->UnescapeString (nsDependentCString (param + 2), 0, aOriginURL); + break; + case 'c': + netUtil->UnescapeString (nsDependentCString (param + 2), 0, aOriginCharset); + break; + /* The next one is not used in neterror but recover: */ + case 't': + netUtil->UnescapeString (nsDependentCString (param + 2), 0, aTitle); + break; + case 'd': + /* we don't need mozilla's description parameter */ + default: + break; + } + } + + g_strfreev (params); + + return NS_OK; +} + +nsresult +EphyAboutModule::GetErrorMessage(nsIURI *aURI, + const char *aError, + char **aStockIcon, + char **aTitle, + char **aPrimary, + char **aSecondary, + char **aTertiary, + char **aLinkIntro) +{ + *aStockIcon = GTK_STOCK_DIALOG_ERROR; + + if (strcmp (aError, "protocolNotFound") == 0) + { + nsCAutoString scheme; + aURI->GetScheme (scheme); + + /* Translators: %s is the name of a protocol, like "http" etc. */ + *aTitle = g_strdup_printf (_("“%s” Protocol is not Supported"), scheme.get()); + /* Translators: %s is the name of a protocol, like "http" etc. */ + *aPrimary = g_strdup_printf (_("“%s” protocol is not supported."), scheme.get()); + /* FIXME: get the list of supported protocols from necko */ + *aSecondary = g_strdup (_("Supported protocols are “http”, “https”, “ftp”, “file”, “smb” " + "and “sftp”.")); + } + else if (strcmp (aError, "fileNotFound") == 0) + { + nsCAutoString path; + aURI->GetPath (path); + + /* Translators: %s is the path and filename, for example "/home/user/test.html" */ + *aTitle = g_markup_printf_escaped (_("File “%s” not Found"), path.get()); + /* Translators: %s is the path and filename, for example "/home/user/test.html" */ + *aPrimary = g_markup_printf_escaped (_("File “%s” not found."), path.get()); + *aSecondary = g_strdup (_("Check the location of the file and try again.")); + } + else if (strcmp (aError, "dnsNotFound") == 0) + { + nsCAutoString host; + aURI->GetHost (host); + + /* Translators: %s is the hostname, like "www.example.com" */ + *aTitle = g_markup_printf_escaped (_("“%s” Could not be Found"), + host.get()); + /* Translators: %s is the hostname, like "www.example.com" */ + *aPrimary = g_markup_printf_escaped (_("“%s” could not be found."), + host.get()); + *aSecondary = g_strdup (_("Check that you are connected to the internet, and " + "that the address is correct.")); + *aLinkIntro = _("If this page used to exist, you may find an archived version:"); + } + else if (strcmp (aError, "connectionFailure") == 0) + { + nsCAutoString host; + aURI->GetHost (host); + + /* Translators: %s is the hostname, like "www.example.com" */ + *aTitle = g_markup_printf_escaped + (_("“%s” Refused the Connection"), + host.get()); + /* Translators: %s is the hostname, like "www.example.com" */ + *aPrimary = g_markup_printf_escaped + (_("“%s” refused the connection."), + host.get()); + + /* FIXME what about 127.0.0.* ? */ + if (strcmp (host.get(), "localhost") == 0) + { + PRInt32 port; + aURI->GetPort (&port); + + *aSecondary = g_strdup (_("Likely causes of the problem are")); + + /* Try to get the service name attached to that port */ + if (port != -1) + { + struct servent *serv; + + if ((serv = getservbyport (htons (port), NULL)) != NULL) + { + *aTertiary = g_markup_printf_escaped ( + _("<ul>" + "<li>the service ""%s"" isn't started.</li>" + "Try to start it using the Services Configuration Tool in " + "System > Control Center, or</ul>" + "<ul><li>the port number %d is wrong.</li>" + "</ul>"), + serv->s_name, port); + } + else + { + *aTertiary = g_markup_printf_escaped ( + _("<ul>" + "<li>some service isn't started, or</li>" + "<li>the port number %d is wrong.</li>" + "</ul>"), + port); + } + } + else + { + *aTertiary = _("<ul>" + "<li>some service isn't started, or</li>" + "<li>you got the port number wrong.</li>" + "</ul>"); + } + } + else + { + *aSecondary = g_strdup (_("The server may be busy or you may have a " + "network connection problem. Try again later.")); + *aLinkIntro = _("There may be an old version of the page you wanted:"); + } + } + else if (strcmp (aError, "netInterrupt") == 0) + { + nsCAutoString host; + aURI->GetHost (host); + + /* Translators: %s is the hostname, like "www.example.com" */ + *aTitle = g_markup_printf_escaped + (_("“%s” Interrupted the Connection"), + host.get()); + /* Translators: %s is the hostname, like "www.example.com" */ + *aPrimary = g_markup_printf_escaped + (_("“%s” interrupted the connection."), + host.get()); + *aSecondary = g_strdup (_("The server may be busy or you may have a " + "network connection problem. Try again later.")); + *aLinkIntro = _("There may be an old version of the page you wanted:"); + } + else if (strcmp (aError, "netTimeout") == 0) + { + nsCAutoString host; + aURI->GetHost (host); + + /* Translators: %s is the hostname, like "www.example.com" */ + *aTitle = g_markup_printf_escaped + (_("“%s” is not Responding"), + host.get()); + /* Translators: %s is the hostname, like "www.example.com" */ + *aPrimary = g_markup_printf_escaped + (_("“%s” is not responding."), + host.get()); + *aSecondary = g_strdup (_("The connection was lost because the " + "server took too long to respond.")); + *aTertiary = _("The server may be busy or you may have a network " + "connection problem. Try again later."); + *aLinkIntro = _("There may be an old version of the page you wanted:"); + } + else if (strcmp (aError, "malformedURI") == 0) + { + *aTitle = g_strdup (_("Invalid Address")); + *aPrimary = g_strdup (_("Invalid address.")); + *aSecondary = g_strdup (_("The address you entered is not valid.")); + } + else if (strcmp (aError, "redirectLoop") == 0) + { + nsCAutoString host; + aURI->GetHost (host); + + /* Translators: %s is the hostname, like "www.example.com" */ + *aTitle = g_markup_printf_escaped + (_("“%s” Redirected Too Many Times"), + host.get()); + /* Translators: %s is the hostname, like "www.example.com" */ + *aPrimary = g_strdup (_("This page cannot load because of a problem with the Web site.")); + + *aSecondary = g_markup_printf_escaped + (_("The server “%s” is redirecting in a way that will never complete."), + host.get()); + *aLinkIntro = _("There may be an old version of the page you wanted:"); + } + else if (strcmp (aError, "unknownSocketType") == 0) + { + nsCAutoString host; + aURI->GetHost (host); + + /* Translators: %s is the hostname, like "www.example.com" */ + *aTitle = g_markup_printf_escaped + (_("“%s” Requires an Encrypted Connection"), + host.get()); + /* Translators: %s is the hostname, like "www.example.com" */ + *aPrimary = g_markup_printf_escaped + (_("“%s” requires an encrypted connection."), + host.get()); + *aSecondary = g_strdup (_("The document could not be loaded because " + "encryption support is not installed.")); + } + else if (strcmp (aError, "netReset") == 0) + { + nsCAutoString host; + aURI->GetHost (host); + + /* Translators: %s is the hostname, like "www.example.com" */ + *aTitle = g_markup_printf_escaped + (_("“%s” Dropped the Connection"), + host.get()); + /* Translators: %s is the hostname, like "www.example.com" */ + *aPrimary = g_markup_printf_escaped + (_("“%s” dropped the connection."), + host.get()); + *aSecondary = g_strdup (_("The server dropped the connection " + "before any data could be read.")); + *aTertiary = _("The server may be busy or you may have a " + "network connection problem. Try again later."); + *aLinkIntro = _("There may be an old version of the page you wanted:"); + } + else if (strcmp (aError, "netOffline") == 0) + { + /* Error is a bit too strong here */ + *aStockIcon = GTK_STOCK_DIALOG_INFO; + + *aTitle = g_strdup (_("Cannot Load Document Whilst Working Offline")); + *aPrimary = g_strdup (_("Cannot load document whilst working offline.")); + *aSecondary = g_strdup (_("To view this document, disable “Work Offline” and try again.")); + } + else if (strcmp (aError, "deniedPortAccess") == 0) + { + nsCAutoString host; + aURI->GetHost (host); + + PRInt32 port = -1; + aURI->GetPort (&port); + + /* Translators: %s is the hostname, like "www.example.com" */ + *aTitle = g_markup_printf_escaped + (_("“%s” Denied Access to Port “%d”"), + host.get(), port > 0 ? port : 80); + /* Translators: %s is the hostname, like "www.example.com" */ + *aPrimary = g_markup_printf_escaped + (_("“%s” denied access to port “%d”."), + host.get(), port > 0 ? port : 80); + *aSecondary = g_strdup (_("The server dropped the connection " + "before any data could be read.")); + *aTertiary = _("The server may be busy or you may have a " + "network connection problem. Try again later."); + *aLinkIntro = _("There may be an old version of the page you wanted:"); + } + else if (strcmp (aError, "proxyResolveFailure") == 0 || + strcmp (aError, "proxyConnectFailure") == 0) + { + *aTitle = g_strdup (_("Could not Connect to Proxy Server")); + *aPrimary = g_strdup (_("Could not connect to proxy server.")); + *aSecondary = g_strdup (_("Check your proxy server settings. " + "If the connection still fails, there may be " + "a problem with your proxy server or your " + "network connection.")); + } + /* This was introduced in gecko 1.9 */ + else if (strcmp (aError, "contentEncodingError") == 0) + { + *aTitle = g_strdup (_("Could not Display Content")); + *aPrimary = g_strdup (_("Could not display content.")); + *aSecondary = g_strdup (_("The page uses an unsupported or invalid form of compression.")); + } + else + { + return NS_ERROR_ILLEGAL_VALUE; + } + + return NS_OK; +} + +nsresult +EphyAboutModule::CreateErrorPage(nsIURI *aErrorURI, + nsIChannel **_retval) +{ + *_retval = nsnull; + + /* First parse the arguments */ + nsresult rv; + nsCAutoString spec; + rv = aErrorURI->GetSpec (spec); + NS_ENSURE_TRUE (NS_SUCCEEDED (rv), rv); + + nsCAutoString error, rawurl, url, charset, dummy; + rv = ParseURL (spec.get (), error, rawurl, url, charset, dummy); + if (NS_FAILED (rv)) return rv; + if (error.IsEmpty () || rawurl.IsEmpty () || url.IsEmpty()) return NS_ERROR_FAILURE; + + nsCOMPtr<nsIURI> uri; + rv = NS_NewURI (getter_AddRefs (uri), url, charset.get()); + /* FIXME can uri be NULL if the original url was invalid? */ + NS_ENSURE_SUCCESS (rv, rv); + + char *stock_id = nsnull, *title = nsnull, *primary = nsnull, + *secondary = nsnull, *tertiary = nsnull, *linkintro = nsnull; + rv = GetErrorMessage (uri, error.get(), &stock_id, &title, &primary, + &secondary, &tertiary, &linkintro); + + /* we don't know about this error code. + * FIXME: We'd like to forward to mozilla's about:neterror handler, + * but I don't know how to. So just redirect to the same page that + * mozilla's handler redirects to. + */ + if (rv == NS_ERROR_ILLEGAL_VALUE) + { + nsCAutoString newurl(spec); + + /* remove "about:neterror" part and insert mozilla's error page url */ + newurl.Cut(0, strlen ("about:neterror")); + newurl.Insert("chrome://global/content/netError.xhtml", 0); + + return Redirect (newurl, _retval); + } + NS_ENSURE_SUCCESS (rv, rv); + NS_ENSURE_TRUE (primary && secondary, NS_ERROR_FAILURE); + + nsCOMPtr<nsIChannel> channel; + rv = WritePage (aErrorURI, uri, aErrorURI, rawurl, title, stock_id, primary, secondary, tertiary, linkintro, getter_AddRefs (channel)); + NS_ENSURE_SUCCESS (rv, rv); + + g_free (title); + g_free (primary); + g_free (secondary); + + channel.swap (*_retval); + + return NS_OK; +} + +nsresult +EphyAboutModule::CreateRecoverPage(nsIURI *aRecoverURI, + nsIChannel **_retval) +{ + *_retval = nsnull; + + /* First parse the arguments */ + nsresult rv; + nsCAutoString spec; + rv = aRecoverURI->GetSpec (spec); + NS_ENSURE_TRUE (NS_SUCCEEDED (rv), rv); + + nsCAutoString error, rawurl, url, charset, title; + rv = ParseURL (spec.get (), error, rawurl, url, charset, title); + if (NS_FAILED (rv)) return rv; + if (rawurl.IsEmpty () || url.IsEmpty()) return NS_ERROR_FAILURE; + + nsCOMPtr<nsIURI> uri; + rv = NS_NewURI(getter_AddRefs (uri), url, charset.get()); + NS_ENSURE_SUCCESS (rv, rv); + + char *secondary = g_markup_printf_escaped + (_("The page “%s” in this tab was not fully loaded yet when " + "the web browser crashed; it could have caused the crash."), + url.get()); + + nsCOMPtr<nsIChannel> channel; + rv = WritePage (aRecoverURI, uri, uri, rawurl, title.get(), + GTK_STOCK_DIALOG_INFO, title.get() /* as primary */, + secondary, nsnull, nsnull, getter_AddRefs (channel)); + NS_ENSURE_SUCCESS (rv, rv); + + nsRefPtr<EphyRedirectChannel> redirectChannel (new EphyRedirectChannel (channel)); + if (!redirectChannel) return NS_ERROR_OUT_OF_MEMORY; + + g_free (secondary); + + NS_ADDREF(*_retval = redirectChannel); + + return NS_OK; +} + +nsresult +EphyAboutModule::WritePage(nsIURI *aOriginalURI, + nsIURI *aURI, + nsIURI *aChannelURI, + const nsACString &aRawURL, + const char *aTitle, + const char *aStockIcon, + const char *aPrimary, + const char *aSecondary, + const char *aTertiary, + const char *aLinkIntro, + nsIChannel **_retval) +{ + *_retval = nsnull; + + nsresult rv; + nsCOMPtr<nsIStorageStream> storageStream; + rv = NS_NewStorageStream (16384, (PRUint32) -1, getter_AddRefs (storageStream)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIOutputStream> stream; + rv = storageStream->GetOutputStream (0, getter_AddRefs (stream)); + NS_ENSURE_SUCCESS (rv, rv); + + char *language = g_strdup (pango_language_to_string (gtk_get_default_language ())); + g_strdelimit (language, "_-@", '\0'); + + Write (stream, + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\""); + Write (stream, language); + Write (stream, + "\" xml:lang=\""); + Write (stream, language); + Write (stream, + "\">\n" + "<head>\n" + "<title>"); + Write (stream, aTitle); + /* no favicon for now, it would pollute the favicon cache */ + /* "<link rel=\"icon\" type=\"image/png\" href=\"moz-icon://stock/gtk-dialog-error?size=16\" />\n" */ + Write (stream, + "</title>\n" + "<style type=\"text/css\">\n" + "div#body {\n" + "top: 12px;\n" + "right: 12px;\n" + "bottom: 12px;\n" + "left: 12px;\n" + "overflow: auto;\n" + + "background: -moz-dialog url('moz-icon://stock/"); + Write (stream, aStockIcon); + Write (stream, + "?size=dialog') no-repeat 12px 12px;\n" + "color: -moz-dialogtext;\n" + "font: message-box;\n" + "border: 1px solid -moz-dialogtext;\n" + + "padding: 12px 12px 12px 72px;\n" + "}\n" + + "h1 {\n" + "margin: 0;\n" + "font-size: 1.2em;\n" + "}\n" + "</style>\n" + "</head>\n" + "<body dir=\""); + Write (stream, + gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL ? "rtl" : "ltr"); + Write (stream, + "\">\n" + "<div id=\"body\">" + "<h1>"); + Write (stream, aPrimary); + Write (stream, + "</h1>\n"); + if (aSecondary) + { + Write (stream, "<p>"); + Write (stream, aSecondary); + if (aTertiary) + { + Write (stream, " "); + Write (stream, aTertiary); + } + Write (stream, "</p>\n"); + } + + PRBool isHttp = PR_FALSE, isHttps = PR_FALSE; + aURI->SchemeIs ("http", &isHttp); + aURI->SchemeIs ("https", &isHttps); + if (aLinkIntro && (isHttp || isHttps)) + { + nsCString raw(aRawURL); + + Write (stream, "<p>"); + Write (stream, aLinkIntro); + Write (stream, "<ul>\n"); + Write (stream, "<li><a href=\"http://www.google.com/search?q=cache:"); + Write (stream, raw.get()); + Write (stream, "\">"); + /* Translators: The text before the "|" is context to help you decide on + * the correct translation. You MUST OMIT it in the translated string. */ + Write (stream, Q_("You may find an old version:|in the Google Cache")); + Write (stream, "</a></li>\n"); + + Write (stream, "<li><a href=\"http://web.archive.org/web/*/"); + Write (stream, raw.get()); + Write (stream, "\">"); + /* Translators: The text before the "|" is context to help you decide on + * the correct translation. You MUST OMIT it in the translated string. */ + Write (stream, Q_("You may find an old version:|in the Internet Archive")); + Write (stream, "</a></li>\n" + "</ul>\n" + "</p>"); + } + + Write (stream, + "</div>\n" + "</body>\n" + "</html>\n"); + + g_free (language); + + /* finish the rendering */ + nsCOMPtr<nsIInputStream> inputStream; + rv = storageStream->NewInputStream (0, getter_AddRefs (inputStream)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIChannel> channel; + rv = NS_NewInputStreamChannel (getter_AddRefs (channel), + aChannelURI, + inputStream, + NS_LITERAL_CSTRING ("application/xhtml+xml"), + NS_LITERAL_CSTRING ("utf-8")); + NS_ENSURE_SUCCESS (rv, rv); + + rv = channel->SetOriginalURI (aOriginalURI); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIScriptSecurityManager> securityManager + (do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIPrincipal> principal; + rv = securityManager->GetCodebasePrincipal (aOriginalURI, getter_AddRefs (principal)); + NS_ENSURE_SUCCESS (rv, rv); + + rv = channel->SetOwner(principal); + NS_ENSURE_SUCCESS (rv, rv); + + channel.swap (*_retval); + + return NS_OK; +} + +nsresult +EphyAboutModule::Write(nsIOutputStream *aStream, + const char *aText) +{ + PRUint32 bytesWritten; + return aStream->Write (aText, strlen (aText), &bytesWritten); +} diff --git a/embed/xulrunner/components/EphyAboutModule.h b/embed/xulrunner/components/EphyAboutModule.h new file mode 100644 index 000000000..21a269c49 --- /dev/null +++ b/embed/xulrunner/components/EphyAboutModule.h @@ -0,0 +1,65 @@ +/* + * Copyright © 2001 Matt Aubury, Philip Langdale + * Copyright © 2004 Crispin Flowerday + * Copyright © 2005 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$ + */ + +#ifndef EPHY_ABOUT_MODULE_H +#define EPHY_ABOUT_MODULE_H + +#include <nsIAboutModule.h> + +/* a9aea13e-21de-4be8-a07e-a05f11658c55 */ +#define EPHY_ABOUT_MODULE_CID \ +{ 0xa9aea13e, 0x21de, 0x4be8, \ + { 0xa0, 0x7e, 0xa0, 0x5f, 0x11, 0x65, 0x8c, 0x55 } } + +#define EPHY_ABOUT_NETERROR_CONTRACTID NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror" +#define EPHY_ABOUT_NETERROR_CLASSNAME "Epiphany about:neterror module" + +#define EPHY_ABOUT_EPIPHANY_CONTRACTID NS_ABOUT_MODULE_CONTRACTID_PREFIX "epiphany" +#define EPHY_ABOUT_EPIPHANY_CLASSNAME "Epiphany about:epiphany module" +#define EPHY_ABOUT_RECOVER_CONTRACTID NS_ABOUT_MODULE_CONTRACTID_PREFIX "recover" +#define EPHY_ABOUT_RECOVER_CLASSNAME "Epiphany about:recover module" + +class nsIChannel; +class nsIOutputStream; +class nsIInputStreamChannel; +class nsIURI; + +class EphyAboutModule : public nsIAboutModule +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIABOUTMODULE + + EphyAboutModule(); + virtual ~EphyAboutModule(); + + private: + nsresult Redirect(const nsACString&, nsIChannel**); + nsresult ParseURL(const char*, nsACString&, nsACString&, nsACString&, nsACString&, nsACString&); + nsresult GetErrorMessage(nsIURI*, const char*, char**, char**, char**, char**, char**, char**); + nsresult CreateErrorPage(nsIURI*, nsIChannel**); + nsresult CreateRecoverPage(nsIURI*, nsIChannel**); + nsresult WritePage(nsIURI*, nsIURI*, nsIURI*, const nsACString&, const char*, const char*, const char*, const char*, const char*, const char*, nsIChannel**); + nsresult Write(nsIOutputStream*, const char*); +}; + +#endif /* EPHY_ABOUT_MODULE_H */ diff --git a/embed/xulrunner/components/EphyContentPolicy.cpp b/embed/xulrunner/components/EphyContentPolicy.cpp new file mode 100644 index 000000000..7f8943687 --- /dev/null +++ b/embed/xulrunner/components/EphyContentPolicy.cpp @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright © 2003 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 <nsStringGlue.h> + +#include <nsCOMPtr.h> +#include <nsICategoryManager.h> +#include <nsIDOMAbstractView.h> +#include <nsIDOMDocument.h> +#include <nsIDOMDocumentView.h> +#include <nsIDOMNode.h> +#include <nsIDOMWindow.h> +#include <nsIURI.h> +#include <nsServiceManagerUtils.h> +#include <nsXPCOMCID.h> + +#include "eel-gconf-extensions.h" +#include "ephy-adblock-manager.h" +#include "ephy-debug.h" +#include "ephy-embed-shell.h" +#include "ephy-embed-single.h" + +#include "EphyUtils.h" + +#include "EphyContentPolicy.h" + +#define CONF_LOCKDOWN_DISABLE_UNSAFE_PROTOCOLS "/apps/epiphany/lockdown/disable_unsafe_protocols" +#define CONF_LOCKDOWN_ADDITIONAL_SAFE_PROTOCOLS "/apps/epiphany/lockdown/additional_safe_protocols" + +NS_IMPL_ISUPPORTS1(EphyContentPolicy, nsIContentPolicy) + +EphyContentPolicy::EphyContentPolicy() +{ + LOG ("EphyContentPolicy ctor (%p)", this); + + mLocked = eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_UNSAFE_PROTOCOLS); + + mSafeProtocols = eel_gconf_get_string_list (CONF_LOCKDOWN_ADDITIONAL_SAFE_PROTOCOLS); +} + +EphyContentPolicy::~EphyContentPolicy() +{ + LOG ("EphyContentPolicy dtor (%p)", this); + + g_slist_foreach (mSafeProtocols, (GFunc) g_free, NULL); + g_slist_free (mSafeProtocols); +} + +GtkWidget * +EphyContentPolicy::GetEmbedFromContext (nsISupports *aContext) +{ + /* + * aContext is either an nsIDOMWindow, an nsIDOMNode, or NULL. If it's + * an nsIDOMNode, we need the nsIDOMWindow to get the EphyEmbed. + */ + if (aContext == NULL) return NULL; + + nsCOMPtr<nsIDOMWindow> window; + + nsCOMPtr<nsIDOMNode> node (do_QueryInterface (aContext)); + if (node != NULL) + { + nsCOMPtr<nsIDOMDocument> domDocument; + + node->GetOwnerDocument (getter_AddRefs (domDocument)); + if (domDocument == NULL) return NULL; /* resource://... */ + + nsCOMPtr<nsIDOMDocumentView> docView = + do_QueryInterface (domDocument); + NS_ENSURE_TRUE (docView, NULL); + + nsCOMPtr<nsIDOMAbstractView> view; + + docView->GetDefaultView (getter_AddRefs (view)); + + window = do_QueryInterface (view); + } + else + { + window = do_QueryInterface (aContext); + } + NS_ENSURE_TRUE (window, NULL); + + GtkWidget *embed = EphyUtils::FindEmbed (window); + if (!EPHY_IS_EMBED (embed)) return NULL; + + return embed; +} + +NS_IMETHODIMP +EphyContentPolicy::ShouldLoad(PRUint32 aContentType, + nsIURI *aContentLocation, + nsIURI *aRequestingLocation, + nsISupports *aContext, + const nsACString &aMimeTypeGuess, + nsISupports *aExtra, + PRInt16 *aDecision) +{ + NS_ENSURE_ARG (aContentLocation); + NS_ENSURE_ARG_POINTER (aDecision); + + *aDecision = nsIContentPolicy::ACCEPT; + + /* We have to always allow these, else forms and scrollbars break */ + PRBool isChrome = PR_FALSE, isResource = PR_FALSE; + aContentLocation->SchemeIs ("chrome", &isChrome); + aContentLocation->SchemeIs ("resource", &isResource); + if (isChrome || isResource) return NS_OK; + + PRBool isHttps = PR_FALSE; + aContentLocation->SchemeIs ("https", &isHttps); + if (isHttps) return NS_OK; + + /* is this url allowed ? */ + nsCString contentSpec; + aContentLocation->GetSpec (contentSpec); + + EphyAdBlockManager *adblock_manager = + EPHY_ADBLOCK_MANAGER (ephy_embed_shell_get_adblock_manager (embed_shell)); + + static PRBool kBlockType[nsIContentPolicy::TYPE_REFRESH + 1] = { + PR_FALSE /* unused/unknown, don't block */, + PR_TRUE /* TYPE_OTHER */, + PR_TRUE /* TYPE_SCRIPT */, + PR_TRUE /* TYPE_IMAGE */, + PR_FALSE /* TYPE_STYLESHEET */, + PR_TRUE /* TYPE_OBJECT */, + PR_FALSE /* TYPE_DOCUMENT */, + PR_TRUE /* TYPE_SUBDOCUMENT */, + PR_TRUE /* TYPE_REFRESH */ + }; + + if (kBlockType[aContentType < G_N_ELEMENTS (kBlockType) ? aContentType : 0]) + { + GtkWidget *embed = GetEmbedFromContext (aContext); + + if (embed && + !ephy_adblock_manager_should_load (adblock_manager, + EPHY_EMBED (embed), + contentSpec.get (), + AdUriCheckType (aContentType))) + { + *aDecision = nsIContentPolicy::REJECT_REQUEST; + + g_signal_emit_by_name (embed, + "content-blocked", + contentSpec.get ()); + return NS_OK; + } + } + + PRBool isHttp = PR_FALSE; + aContentLocation->SchemeIs ("http", &isHttp); + if (isHttp) return NS_OK; + + if (strcmp (contentSpec.get(), "about:blank") == 0) return NS_OK; + + nsCString contentScheme; + aContentLocation->GetScheme (contentScheme); + + /* first general lockdown check */ + if (mLocked && + !g_slist_find_custom (mSafeProtocols, contentScheme.get(), (GCompareFunc) strcmp)) + { + *aDecision = nsIContentPolicy::REJECT_REQUEST; + } + + return NS_OK; +} + +NS_IMETHODIMP +EphyContentPolicy::ShouldProcess(PRUint32 aContentType, + nsIURI *aContentLocation, + nsIURI *aRequestingLocation, + nsISupports *aContext, + const nsACString &aMimeType, + nsISupports *aExtra, + PRInt16 *aDecision) +{ + *aDecision = nsIContentPolicy::ACCEPT; + return NS_OK; +} + + +/* static */ NS_METHOD +EphyContentPolicy::Register (nsIComponentManager* aComponentManager, + nsIFile* aPath, + const char* aRegistryLocation, + const char* aComponentType, + const nsModuleComponentInfo* aInfo) +{ + nsresult rv; + nsCOMPtr<nsICategoryManager> catMan (do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + rv = catMan->AddCategoryEntry ("content-policy", + EPHY_CONTENT_POLICY_CONTRACTID, + EPHY_CONTENT_POLICY_CONTRACTID, + PR_FALSE /* don't persist */, + PR_TRUE /* replace */, + nsnull); + NS_ENSURE_SUCCESS (rv, rv); + + return rv; +} + +/* static */ NS_METHOD +EphyContentPolicy::Unregister (nsIComponentManager* aComponentManager, + nsIFile* aPath, + const char* aRegistryLocation, + const nsModuleComponentInfo* aInfo) +{ + nsresult rv; + nsCOMPtr<nsICategoryManager> catMan (do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + rv = catMan->DeleteCategoryEntry ("content-policy", + EPHY_CONTENT_POLICY_CONTRACTID, + PR_FALSE /* don't persist */); + NS_ENSURE_SUCCESS (rv, rv); + + return rv; +} diff --git a/embed/xulrunner/components/EphyContentPolicy.h b/embed/xulrunner/components/EphyContentPolicy.h new file mode 100644 index 000000000..0bc68ced7 --- /dev/null +++ b/embed/xulrunner/components/EphyContentPolicy.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright © 2003 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$ + */ + +#ifndef EPHY_CONTENT_POLICY_H +#define EPHY_CONTENT_POLICY_H + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtkwidget.h> + +#include <nsIContentPolicy.h> + +#include "ephy-embed.h" + +class nsIComponentManager; +class nsIFile; +struct nsModuleComponentInfo; + +#define EPHY_CONTENT_POLICY_CONTRACTID "@gnome.org/projects/epiphany/epiphany-content-policy;1" +#define EPHY_CONTENT_POLICY_CLASSNAME "Epiphany Content Policy Class" + +#define EPHY_CONTENT_POLICY_CID \ +{ /* 6bb60b15-b7bd-4023-a19e-ab691bc3fb43 */ \ + 0x6bb60b15, \ + 0xb7bd, \ + 0x4023, \ + { 0xa1, 0x9e, 0xab, 0x69, 0x1b, 0xc3, 0xfb, 0x43 } \ +} + +class EphyContentPolicy : public nsIContentPolicy +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSICONTENTPOLICY + + EphyContentPolicy(); + + static NS_METHOD Register (nsIComponentManager* aComponentManager, + nsIFile* aPath, + const char* aRegistryLocation, + const char* aComponentType, + const nsModuleComponentInfo* aInfo); + + static NS_METHOD Unregister (nsIComponentManager* aComponentManager, + nsIFile* aPath, + const char* aRegistryLocation, + const nsModuleComponentInfo* aInfo); + + private: + ~EphyContentPolicy(); + + static GtkWidget *GetEmbedFromContext (nsISupports *aContext); + + gboolean mLocked; + GSList *mSafeProtocols; +}; + +#endif diff --git a/embed/xulrunner/components/EphyRedirectChannel.cpp b/embed/xulrunner/components/EphyRedirectChannel.cpp new file mode 100644 index 000000000..af6dd2f91 --- /dev/null +++ b/embed/xulrunner/components/EphyRedirectChannel.cpp @@ -0,0 +1,32 @@ +/* + * Copyright © 2005 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 "EphyRedirectChannel.h" + +NS_IMPL_ISUPPORTS2 (EphyWrappedChannel, nsIRequest, nsIChannel) + +NS_IMETHODIMP +EphyRedirectChannel::SetLoadFlags(nsLoadFlags aFlags) +{ + return mChannel->SetLoadFlags (aFlags | LOAD_REPLACE); +} diff --git a/embed/xulrunner/components/EphyRedirectChannel.h b/embed/xulrunner/components/EphyRedirectChannel.h new file mode 100644 index 000000000..bcec1f1e5 --- /dev/null +++ b/embed/xulrunner/components/EphyRedirectChannel.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2005 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$ + */ + +#ifndef EPHY_REDIRECT_CHANNEL_H +#define EPHY_REDIRECT_CHANNEL_H + +#include <nsCOMPtr.h> +#include <nsIChannel.h> + +class EphyWrappedChannel : public nsIChannel +{ + public: + NS_DECL_ISUPPORTS + NS_FORWARD_NSIREQUEST (mChannel->) + NS_FORWARD_NSICHANNEL (mChannel->) + + EphyWrappedChannel (nsIChannel *aChannel) : mChannel (aChannel) { } + virtual ~EphyWrappedChannel () { } + + protected: + nsCOMPtr<nsIChannel> mChannel; +}; + +class EphyRedirectChannel : public EphyWrappedChannel +{ + public: + EphyRedirectChannel (nsIChannel *aChannel) : EphyWrappedChannel (aChannel) { } + virtual ~EphyRedirectChannel () { } + + NS_IMETHOD SetLoadFlags (nsLoadFlags aFlags); +}; + +#endif /* !EPHY_REDIRECT_CHANNEL_H */ diff --git a/embed/xulrunner/components/EphySidebar.cpp b/embed/xulrunner/components/EphySidebar.cpp new file mode 100644 index 000000000..1225e2d9a --- /dev/null +++ b/embed/xulrunner/components/EphySidebar.cpp @@ -0,0 +1,161 @@ +/* + * Copyright © 2002 Philip Langdale + * Copyright © 2004 Crispin Flowerday + * + * 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 <nsStringGlue.h> + +#include <nsICategoryManager.h> +#include <nsIClassInfoImpl.h> +#include <nsIScriptNameSpaceManager.h> +#include <nsMemory.h> +#include <nsServiceManagerUtils.h> +#include <nsXPCOMCID.h> + +#include "ephy-debug.h" +#include "ephy-embed-shell.h" +#include "ephy-embed-single.h" + +#include "EphySidebar.h" + +NS_IMPL_ISUPPORTS1_CI(EphySidebar, nsISidebar) + +EphySidebar::EphySidebar() +{ +} + +EphySidebar::~EphySidebar() +{ +} + +/* void addPanel (in wstring aTitle, in string aContentURL, in string aCustomizeURL); */ +NS_IMETHODIMP +EphySidebar::AddPanel (const PRUnichar *aTitle, + const char *aContentURL, + const char *aCustomizeURL) +{ + NS_ENSURE_ARG (aTitle); + NS_ENSURE_ARG (aContentURL); + + nsCString title; + EphyEmbedSingle *single; + + /* FIXME: length-limit string */ + NS_UTF16ToCString (nsDependentString(aTitle), + NS_CSTRING_ENCODING_UTF8, title); + + LOG ("Adding sidebar, url=%s title=%s", aContentURL, title.get()); + + single = EPHY_EMBED_SINGLE (ephy_embed_shell_get_embed_single (embed_shell)); + + gboolean result = FALSE; + g_signal_emit_by_name (single, "add-sidebar", + aContentURL, title.get(), &result); + + return NS_OK; +} + +/* void addPersistentPanel (in wstring aTitle, in string aContentURL, in string aCustomizeURL); */ +NS_IMETHODIMP +EphySidebar::AddPersistentPanel (const PRUnichar *aTitle, + const char *aContentURL, + const char *aCustomizeURL) +{ + return AddPanel (aTitle, aContentURL, aCustomizeURL); +} + +/* void addSearchEngine (in string engineURL, in string iconURL, in wstring suggestedTitle, in wst +ring suggestedCategory); */ +NS_IMETHODIMP +EphySidebar::AddSearchEngine (const char *aEngineURL, + const char *aIconURL, + const PRUnichar *aSuggestedTitle, + const PRUnichar *aSuggestedCategory) +{ + NS_ENSURE_ARG (aSuggestedTitle); + NS_ENSURE_ARG (aIconURL); + NS_ENSURE_ARG (aEngineURL); + + nsCString title; + EphyEmbedSingle *single; + + /* FIXME: length-limit string */ + NS_UTF16ToCString (nsDependentString(aSuggestedTitle), + NS_CSTRING_ENCODING_UTF8, title); + + LOG ("Adding search engine, engineurl=%s iconurl=%s title=%s", aEngineURL, aIconURL, title.get()); + + single = EPHY_EMBED_SINGLE (ephy_embed_shell_get_embed_single (embed_shell)); + + gboolean result = FALSE; + g_signal_emit_by_name (single, "add-search-engine", + aEngineURL, aIconURL, title.get(), &result); + + return NS_OK; +} + +/* void addMicrosummaryGenerator (in string generatorURL); */ +NS_IMETHODIMP +EphySidebar::AddMicrosummaryGenerator (const char *generatorURL) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* static */ NS_METHOD +EphySidebar::Register (nsIComponentManager* aComponentManager, + nsIFile* aPath, + const char* aRegistryLocation, + const char* aComponentType, + const nsModuleComponentInfo* aInfo) +{ + nsresult rv; + nsCOMPtr<nsICategoryManager> catMan (do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + rv = catMan->AddCategoryEntry (JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY, + "sidebar", + NS_SIDEBAR_CONTRACTID, + PR_FALSE /* don't persist */, + PR_TRUE /* replace */, + nsnull); + NS_ENSURE_SUCCESS (rv, rv); + + return rv; +} + +/* static */ NS_METHOD +EphySidebar::Unregister (nsIComponentManager* aComponentManager, + nsIFile* aPath, + const char* aRegistryLocation, + const nsModuleComponentInfo* aInfo) +{ + nsresult rv; + nsCOMPtr<nsICategoryManager> catMan (do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + rv = catMan->DeleteCategoryEntry (JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY, + "sidebar", + PR_FALSE /* don't persist */); + NS_ENSURE_SUCCESS (rv, rv); + + return rv; +} diff --git a/embed/xulrunner/components/EphySidebar.h b/embed/xulrunner/components/EphySidebar.h new file mode 100644 index 000000000..37efa0bf8 --- /dev/null +++ b/embed/xulrunner/components/EphySidebar.h @@ -0,0 +1,66 @@ +/* + * Copyright © 2002 Philip Langdale + * Copyright © 2004 Crispin Flowerday + * + * 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$ + */ + +#ifndef EPHY_SIDEBAR_H +#define EPHY_SIDEBAR_H + +#include <nsISidebar.h> + +class nsIComponentManager; +class nsIFile; +struct nsModuleComponentInfo; + +#define EPHY_SIDEBAR_CLASSNAME \ + "Epiphany's Sidebar Implementation" + +#define EPHY_SIDEBAR_CID \ +{ /* {50f13159-f9b9-44b3-b18e-6ee5d85a202a} */ \ + 0x50f13159, \ + 0xf9b9, \ + 0x44b3, \ + {0xb1, 0x8e, 0x6e, 0xe5, 0xd8, 0x5a, 0x20, 0x2a} \ +} + +class EphySidebar : public nsISidebar +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSISIDEBAR + + EphySidebar(); + + static NS_METHOD Register (nsIComponentManager* aComponentManager, + nsIFile* aPath, + const char* aRegistryLocation, + const char* aComponentType, + const nsModuleComponentInfo* aInfo); + + static NS_METHOD Unregister (nsIComponentManager* aComponentManager, + nsIFile* aPath, + const char* aRegistryLocation, + const nsModuleComponentInfo* aInfo); + + private: + ~EphySidebar(); +}; + +#endif /* ! EPHY_SIDEBAR_H */ + diff --git a/embed/xulrunner/components/FilePicker.cpp b/embed/xulrunner/components/FilePicker.cpp new file mode 100644 index 000000000..e452dd0a7 --- /dev/null +++ b/embed/xulrunner/components/FilePicker.cpp @@ -0,0 +1,492 @@ +/* + * Copyright © 2001 Philip Langdale + * Copyright © 2003, 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/gconvert.h> +#include <glib/gi18n.h> +#include <gtk/gtkfilefilter.h> +#include <gtk/gtkmessagedialog.h> +#include <gtk/gtkstock.h> + +#include <nsStringGlue.h> + +#include <nsCOMPtr.h> +#include <nsIDOMWindow.h> +#include <nsIFileURL.h> +#include <nsILocalFile.h> +#include <nsIPromptService.h> +#include <nsIServiceManager.h> +#include <nsIURI.h> +#include <nsNetCID.h> + +#include "ephy-debug.h" +#include "ephy-gui.h" +#include "ephy-prefs.h" + +#include "AutoJSContextStack.h" +#include "AutoWindowModalState.h" +#include "EphyUtils.h" + +#include "FilePicker.h" + +NS_IMPL_ISUPPORTS1(GFilePicker, nsIFilePicker) + +GFilePicker::GFilePicker() +: mDialog(nsnull) +, mMode(nsIFilePicker::modeOpen) +{ + LOG ("GFilePicker ctor (%p)", this); +} + +GFilePicker::~GFilePicker() +{ + LOG ("GFilePicker dtor (%p)", this); + + if (mDialog) + { + g_object_remove_weak_pointer (G_OBJECT (mDialog), (gpointer *) &mDialog); + gtk_widget_destroy (GTK_WIDGET (mDialog)); + } +} + +/* void init (in nsIDOMWindow parent, in AString title, in short mode); */ +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ +NS_IMETHODIMP GFilePicker::Init(nsIDOMWindow *parent, const nsAString& title, PRInt16 mode) +#else +NS_IMETHODIMP GFilePicker::Init(nsIDOMWindowInternal *parent, const PRUnichar *title, PRInt16 mode) +#endif +{ + LOG ("GFilePicker::Init"); + + mParent = do_QueryInterface (parent); + + GtkWidget *gtkparent = EphyUtils::FindGtkParent (parent); +#if defined(MOZ_NSIFILEPICKER_NSASTRING_) + NS_ENSURE_TRUE (gtkparent, NS_ERROR_FAILURE); +#endif + + nsCString cTitle; +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ + NS_UTF16ToCString (title, NS_CSTRING_ENCODING_UTF8, cTitle); +#else + NS_UTF16ToCString (nsString(title), NS_CSTRING_ENCODING_UTF8, cTitle); +#endif + + mMode = mode; + + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; + switch (mode) + { + case nsIFilePicker::modeGetFolder: + action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + break; + case nsIFilePicker::modeOpenMultiple: + case nsIFilePicker::modeOpen: + action = GTK_FILE_CHOOSER_ACTION_OPEN; + break; + case nsIFilePicker::modeSave: + action = GTK_FILE_CHOOSER_ACTION_SAVE; + break; + default: + g_assert_not_reached (); + break; + } + + mDialog = ephy_file_chooser_new (cTitle.get(), gtkparent, action, + CONF_STATE_UPLOAD_DIR, + EPHY_FILE_FILTER_NONE); + + if (parent) + { + gtk_window_group_add_window (ephy_gui_ensure_window_group (GTK_WINDOW (gtkparent)), + GTK_WINDOW (mDialog)); + } + + if (mode == nsIFilePicker::modeOpenMultiple) + { + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (mDialog), TRUE); + } + if (mMode == nsIFilePicker::modeSave) + { + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (mDialog), TRUE); + } + + g_object_add_weak_pointer (G_OBJECT (mDialog), (gpointer *) &mDialog); + + return NS_OK; +} + +/* void appendFilters (in long filterMask); */ +NS_IMETHODIMP GFilePicker::AppendFilters(PRInt32 filterMask) +{ + NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE); + + LOG ("GFilePicker::AppendFilters mask=%d", filterMask); + + // http://lxr.mozilla.org/seamonkey/source/xpfe/components/filepicker/res/locale/en-US/filepicker.properties + // http://lxr.mozilla.org/seamonkey/source/xpfe/components/filepicker/src/nsFilePicker.js line 131 ff + + if (filterMask & nsIFilePicker::filterAll) + { + ephy_file_chooser_add_pattern_filter (mDialog, _("All files"), + "*", (char*) NULL); + } + if (filterMask & nsIFilePicker::filterHTML) + { + ephy_file_chooser_add_mime_filter (mDialog, _("Web pages"), + "text/html", + "application/xhtml+xml", + "text/xml", + (char *) NULL); + } + if (filterMask & nsIFilePicker::filterText) + { + ephy_file_chooser_add_pattern_filter (mDialog, _("Text files"), + "*.txt", "*.text", NULL); + } + if (filterMask & nsIFilePicker::filterImages) + { + ephy_file_chooser_add_mime_filter (mDialog, _("Images"), + "image/png", + "image/jpeg", + "image/gif", + (char *) NULL); + } + if (filterMask & nsIFilePicker::filterXML) + { + ephy_file_chooser_add_pattern_filter (mDialog, _("XML files"), + "*.xml", (char *) NULL); + } + if (filterMask & nsIFilePicker::filterXUL) + { + ephy_file_chooser_add_pattern_filter (mDialog, _("XUL files"), + "*.xul", (char *) NULL); + } + + return NS_OK; +} + +/* void appendFilter (in AString title, in AString filter); */ +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ +NS_IMETHODIMP GFilePicker::AppendFilter(const nsAString& title, const nsAString& filter) +#else +NS_IMETHODIMP GFilePicker::AppendFilter(const PRUnichar *title, const PRUnichar *filter) +#endif +{ + NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE); + + LOG ("GFilePicker::AppendFilter"); + +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ + if (!filter.Length()) return NS_ERROR_FAILURE; +#else + if (!filter) return NS_ERROR_FAILURE; +#endif + + nsCString pattern; +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ + NS_UTF16ToCString (filter, NS_CSTRING_ENCODING_UTF8, pattern); +#else + NS_UTF16ToCString (nsString(filter), NS_CSTRING_ENCODING_UTF8, pattern); +#endif + + char **patterns; + patterns = g_strsplit (pattern.get(), ";", -1); + if (!patterns) return NS_ERROR_FAILURE; + + GtkFileFilter *filth; + filth = gtk_file_filter_new (); + + for (int i = 0; patterns[i] != NULL; i++) + { + gtk_file_filter_add_pattern (filth, g_strstrip (patterns[i])); + } + + nsCString cTitle; +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ + NS_UTF16ToCString (title, NS_CSTRING_ENCODING_UTF8, cTitle); +#else + NS_UTF16ToCString (nsString(title), NS_CSTRING_ENCODING_UTF8, cTitle); +#endif + + gtk_file_filter_set_name (filth, cTitle.get()); + + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (mDialog), filth); + + g_strfreev (patterns); + + return NS_OK; +} + +/* attribute AString defaultString; */ +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ +NS_IMETHODIMP GFilePicker::GetDefaultString(nsAString& aDefaultString) +#else +NS_IMETHODIMP GFilePicker::GetDefaultString(PRUnichar **aDefaultString) +#endif +{ + NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE); + + LOG ("GFilePicker::GetDefaultString"); + +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ + aDefaultString = mDefaultString; +#else + *aDefaultString = NS_StringCloneData (mDefaultString); +#endif + + return NS_OK; +} + +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ +NS_IMETHODIMP GFilePicker::SetDefaultString(const nsAString& aDefaultString) +#else +NS_IMETHODIMP GFilePicker::SetDefaultString(const PRUnichar *aDefaultString) +#endif +{ + NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE); + + mDefaultString.Assign (aDefaultString); + + if (mMode == nsIFilePicker::modeSave) + { + nsCString defaultString; + NS_UTF16ToCString (mDefaultString, NS_CSTRING_ENCODING_UTF8, + defaultString); + + LOG ("GFilePicker::SetDefaultString %s", defaultString.get()); + + if (!defaultString.Length()) return NS_ERROR_FAILURE; + + /* set_current_name takes UTF-8, not a filename */ + gtk_file_chooser_set_current_name + (GTK_FILE_CHOOSER (mDialog), defaultString.get()); + } + + return NS_OK; +} + +/* attribute AString defaultExtension; */ +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ +NS_IMETHODIMP GFilePicker::GetDefaultExtension(nsAString& aDefaultExtension) +#else +NS_IMETHODIMP GFilePicker::GetDefaultExtension(PRUnichar **aDefaultExtension) +#endif +{ + LOG ("GFilePicker::GetDefaultExtension"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +#ifdef MOZ_NSIFILEPICKER_NSASTRING_ +NS_IMETHODIMP GFilePicker::SetDefaultExtension(const nsAString& aDefaultExtension) +#else +NS_IMETHODIMP GFilePicker::SetDefaultExtension(const PRUnichar *aDefaultExtension) +#endif +{ + LOG ("GFilePicker::SetDefaultExtension"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute long filterIndex; */ +NS_IMETHODIMP GFilePicker::GetFilterIndex(PRInt32 *aFilterIndex) +{ + LOG ("GFilePicker::GetFilterIndex"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP GFilePicker::SetFilterIndex(PRInt32 aFilterIndex) +{ + LOG ("GFilePicker::SetFilterIndex index=%d", aFilterIndex); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute nsILocalFile displayDirectory; */ +NS_IMETHODIMP GFilePicker::GetDisplayDirectory(nsILocalFile **aDisplayDirectory) +{ + NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE); + + LOG ("GFilePicker::GetDisplayDirectory"); + + char *dir = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (mDialog)); + + if (dir != NULL) + { + nsCOMPtr<nsILocalFile> file = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID); + file->InitWithNativePath (nsCString (dir)); + NS_IF_ADDREF (*aDisplayDirectory = file); + + g_free (dir); + } + + return NS_OK; +} + +NS_IMETHODIMP GFilePicker::SetDisplayDirectory(nsILocalFile *aDisplayDirectory) +{ + NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE); + + nsCString dir; + aDisplayDirectory->GetNativePath (dir); + + LOG ("GFilePicker::SetDisplayDirectory to %s", dir.get()); + + if (mDefaultString.Length() && mMode != nsIFilePicker::modeSave) + { + nsCString defaultString; + NS_UTF16ToCString (mDefaultString, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, + defaultString); + + char *filename = g_build_filename (dir.get(), defaultString.get(), (char *) NULL); + LOG ("Setting filename to %s", filename); + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (mDialog), filename); + g_free (filename); + } + else + { + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (mDialog), + dir.get()); + } + + return NS_OK; +} + +/* readonly attribute nsILocalFile file; */ +NS_IMETHODIMP GFilePicker::GetFile(nsILocalFile **aFile) +{ + NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE); + + char *filename; + + LOG ("GFilePicker::GetFile"); + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (mDialog)); + + if (filename != NULL) + { + nsCOMPtr<nsILocalFile> file = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID); + file->InitWithNativePath (nsCString (filename)); + NS_IF_ADDREF (*aFile = file); + + g_free (filename); + } + + return NS_OK; +} + +/* readonly attribute nsIFileURL fileURL; */ +NS_IMETHODIMP GFilePicker::GetFileURL(nsIFileURL **aFileURL) +{ + NS_ENSURE_TRUE (mDialog, NS_ERROR_FAILURE); + + LOG ("GFilePicker::GetFileURL"); + + nsCOMPtr<nsILocalFile> file; + GetFile (getter_AddRefs(file)); + NS_ENSURE_TRUE (file, NS_ERROR_FAILURE); + + nsCOMPtr<nsIFileURL> fileURL = do_CreateInstance (NS_STANDARDURL_CONTRACTID); + fileURL->SetFile(file); + NS_IF_ADDREF(*aFileURL = fileURL); + + return NS_OK; +} + +/* readonly attribute nsISimpleEnumerator files; */ +NS_IMETHODIMP GFilePicker::GetFiles(nsISimpleEnumerator * *aFiles) +{ + // Not sure if we need to implement it at all, it's used nowhere + // in mozilla, but I guess a javascript might call it? + + LOG ("GFilePicker::GetFiles"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* short show (); */ +NS_IMETHODIMP GFilePicker::Show(PRInt16 *_retval) +{ + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + AutoWindowModalState (mParent); + mParent = nsnull; + + LOG ("GFilePicker::Show"); + + gtk_window_set_modal (GTK_WINDOW (mDialog), TRUE); + gtk_window_set_destroy_with_parent (GTK_WINDOW (mDialog), FALSE); + + /* If there's just the "ALL" filter, it's no use showing the filters! */ + GSList *filters = gtk_file_chooser_list_filters (GTK_FILE_CHOOSER (mDialog)); + if (g_slist_length (filters) == 1) + { + GtkFileFilter *filter = GTK_FILE_FILTER (filters->data); + const char *name = gtk_file_filter_get_name (filter); + + if (!name || strcmp (name, _("All files")) == 0) + { + gtk_file_chooser_remove_filter (GTK_FILE_CHOOSER (mDialog), + filter); + } + } + g_slist_free (filters); + + gtk_widget_show (GTK_WIDGET (mDialog)); + + int response; + char *filename = NULL; + + do + { + response = gtk_dialog_run (GTK_DIALOG (mDialog)); + + g_free (filename); + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (mDialog)); + + LOG ("GFilePicker::Show response=%d, filename=%s", response, filename); + } + while (response == GTK_RESPONSE_ACCEPT && + mMode == nsIFilePicker::modeSave && + !ephy_gui_check_location_writable (GTK_WIDGET (mDialog), filename)); + + gtk_widget_hide (GTK_WIDGET (mDialog)); + + if (response == GTK_RESPONSE_ACCEPT && filename != NULL) + { + *_retval = nsIFilePicker::returnOK; + } + else + { + *_retval = nsIFilePicker::returnCancel; + } + + g_free (filename); + + return NS_OK; +} diff --git a/embed/xulrunner/components/FilePicker.h b/embed/xulrunner/components/FilePicker.h new file mode 100644 index 000000000..fa63c56b1 --- /dev/null +++ b/embed/xulrunner/components/FilePicker.h @@ -0,0 +1,62 @@ +/* + * Copyright © 2001 Philip Langdale + * Copyright © 2003 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$ + */ + +#ifndef EPHY_FILEPICKER_H +#define EPHY_FILEPICKER_H + +#include <nsIFilePicker.h> + +class nsIDOMWindow; + +#include "ephy-file-chooser.h" + +#define G_FILEPICKER_CID \ +{ /* 3636dc79-0b42-4bad-8a3f-ae15d3671d17 */ \ + 0x3636dc79, \ + 0x0b42, \ + 0x4bad, \ + {0x8a, 0x3f, 0xae, 0x15, 0xd3, 0x67, 0x1d, 0x17} \ +} + +#define G_FILEPICKER_CONTRACTID "@mozilla.org/filepicker;1" +#define G_FILEPICKER_CLASSNAME "Epiphany File Picker Implementation" + +class nsIFactory; + +extern nsresult NS_NewFilePickerFactory(nsIFactory** aFactory); + +class GFilePicker : public nsIFilePicker +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIFILEPICKER + + GFilePicker(); + virtual ~GFilePicker(); + +private: + EphyFileChooser *mDialog; + PRInt16 mMode; + nsString mDefaultString; + nsCOMPtr<nsIDOMWindow> mParent; +}; + +#endif diff --git a/embed/xulrunner/components/GeckoCookiePromptService.cpp b/embed/xulrunner/components/GeckoCookiePromptService.cpp new file mode 100644 index 000000000..4fc7c21a6 --- /dev/null +++ b/embed/xulrunner/components/GeckoCookiePromptService.cpp @@ -0,0 +1,159 @@ +/* + * Copyright © 2003 Tommi Komulainen <tommi.komulainen@iki.fi> + * Copyright © 2004, 2007 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Id$ + */ + +#include <xpcom-config.h> +#include "config.h" + +#include <glib/gi18n.h> + +#include <gtk/gtkbox.h> +#include <gtk/gtkcheckbutton.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtkmessagedialog.h> +#include <gtk/gtkstock.h> +#include <gtk/gtkwindow.h> + +#include <nsStringGlue.h> + +#include "ephy-debug.h" +#include "ephy-gui.h" + +#include "AutoJSContextStack.h" +#include "AutoWindowModalState.h" +#include "EphyUtils.h" + +#include "GeckoCookiePromptService.h" + +NS_IMPL_ISUPPORTS1 (GeckoCookiePromptService, nsICookiePromptService) + +GeckoCookiePromptService::GeckoCookiePromptService() +{ + LOG ("GeckoCookiePromptService ctor [%p]", this); +} + +GeckoCookiePromptService::~GeckoCookiePromptService() +{ + LOG ("GeckoCookiePromptService dtor [%p]", this); +} + +/* boolean cookieDialog (in nsIDOMWindow parent, in nsICookie cookie, in ACString hostname, in long cookiesFromHost, in boolean changingCookie, inout boolean checkValue); */ +NS_IMETHODIMP +GeckoCookiePromptService::CookieDialog (nsIDOMWindow *aParent, + nsICookie *aCookie, + const nsACString &aHostname, + PRInt32 aCookiesFromHost, + PRBool aChangingCookie, + PRBool *_checkValue, + PRBool *_retval) +{ + NS_ENSURE_ARG (aParent); + NS_ENSURE_ARG (aCookie); + NS_ENSURE_ARG_POINTER (_checkValue); + NS_ENSURE_ARG_POINTER (_retval); + + // TODO short-circuit and accept session cookies as per preference + // TODO until mozilla starts supporting it natively? + + GtkWidget *parent = EphyUtils::FindGtkParent (aParent); + NS_ENSURE_TRUE(parent, NS_ERROR_INVALID_POINTER); + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) { + return rv; + } + + AutoWindowModalState modalState (aParent); + + nsCString host(aHostname); + + GtkWidget *dialog = gtk_message_dialog_new + (GTK_WINDOW (parent), + GTK_DIALOG_MODAL /* FIXME mozilla sucks! */, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("Accept cookie from %s?"), + host.get()); + GtkWindow *window = GTK_WINDOW (dialog); + GtkDialog *gdialog = GTK_DIALOG (dialog); + GtkMessageDialog *message_dialog = GTK_MESSAGE_DIALOG (dialog); + + gtk_window_set_icon_name (window, "web-browser"); + gtk_window_set_title (window, _("Accept Cookie?")); + + if (aChangingCookie) { + gtk_message_dialog_format_secondary_text + (message_dialog, + _("The site wants to modify an existing cookie.")); + } else if (aCookiesFromHost == 0) { + gtk_message_dialog_format_secondary_text + (message_dialog, + _("The site wants to set a cookie.")); + } else if (aCookiesFromHost == 1) { + gtk_message_dialog_format_secondary_text + (message_dialog, + _("The site wants to set a second cookie.")); + } else { + char *num_text = g_strdup_printf + (ngettext ("You already have %d cookie from this site.", + "You already have %d cookies from this site.", + aCookiesFromHost), + aCookiesFromHost); + + gtk_message_dialog_format_secondary_text + (message_dialog, + "The site %s wants to set another cookie. %s", + host.get(), num_text); + g_free (num_text); + } + + GtkWidget *checkbutton; + checkbutton = gtk_check_button_new_with_mnemonic + (_("Apply this _decision to all cookies from this site")); + gtk_widget_show (checkbutton); + gtk_box_pack_start (GTK_BOX (ephy_gui_message_dialog_get_content_box (dialog)), + checkbutton, FALSE, FALSE, 0); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), *_checkValue); + + gtk_dialog_add_button (gdialog, + _("_Reject"), GTK_RESPONSE_REJECT); + gtk_dialog_add_button (gdialog, + _("_Accept"), GTK_RESPONSE_ACCEPT); + gtk_dialog_set_default_response (gdialog, GTK_RESPONSE_ACCEPT); + + int response = gtk_dialog_run (gdialog); + + if (response == GTK_RESPONSE_ACCEPT || response == GTK_RESPONSE_REJECT) { + *_retval = (response == GTK_RESPONSE_ACCEPT); + *_checkValue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton)); + } else { + /* if the dialog was closed, but no button was pressed, + * consider it as 'Reject' but ignore the checkbutton + */ + *_retval = PR_FALSE; + *_checkValue = PR_FALSE; + } + + gtk_widget_destroy (dialog); + + return NS_OK; +} diff --git a/embed/xulrunner/components/GeckoCookiePromptService.h b/embed/xulrunner/components/GeckoCookiePromptService.h new file mode 100644 index 000000000..7092a2982 --- /dev/null +++ b/embed/xulrunner/components/GeckoCookiePromptService.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003 Tommi Komulainen <tommi.komulainen@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Id$ + */ + +#ifndef COOKIEPROMPTSERVICE_H +#define COOKIEPROMPTSERVICE_H + +#include <nsICookiePromptService.h> + +/* 50766a18-0b34-41d9-8f6c-4612200e6556 */ +#define EPHY_COOKIEPROMPTSERVICE_CID \ + { 0x50766a18, 0x0b34, 0x41d9, { 0x8f, 0x6c, 0x46, 0x12, 0x20, 0x0e, 0x65, 0x56 } } + +#define EPHY_COOKIEPROMPTSERVICE_CLASSNAME "Epiphany Cookie Prompt Service" +#define EPHY_COOKIEPROMPTSERVICE_CONTRACTID "@mozilla.org/embedcomp/cookieprompt-service;1" + +class GeckoCookiePromptService : public nsICookiePromptService { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSICOOKIEPROMPTSERVICE + + GeckoCookiePromptService(); + + private: + ~GeckoCookiePromptService(); +}; + +#endif /* COOKIEPROMPTSERVICE_H */ diff --git a/embed/xulrunner/components/GeckoFormSigningDialog.cpp b/embed/xulrunner/components/GeckoFormSigningDialog.cpp new file mode 100644 index 000000000..55b9d0d37 --- /dev/null +++ b/embed/xulrunner/components/GeckoFormSigningDialog.cpp @@ -0,0 +1,158 @@ +/* + * Copyright © 2006 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <glade/glade-xml.h> + +#include <nsStringGlue.h> + +#include <nsCOMPtr.h> +#include <nsIDOMWindow.h> +#include <nsIInterfaceRequestor.h> +#include <nsIInterfaceRequestorUtils.h> + +#include "eel-gconf-extensions.h" +#include "ephy-debug.h" +#include "ephy-embed-shell.h" +#include "ephy-file-helpers.h" +#include "ephy-prefs.h" + +#include "AutoJSContextStack.h" +#include "AutoWindowModalState.h" +#include "EphyUtils.h" + +#include "GeckoFormSigningDialog.h" + +#define LITERAL(s) reinterpret_cast<const nsAString::char_type*>(NS_L(s)) + +NS_IMPL_ISUPPORTS1 (GeckoFormSigningDialog, + nsIFormSigningDialog) + +GeckoFormSigningDialog::GeckoFormSigningDialog() +{ + LOG ("GeckoFormSigningDialog ctor [%p]", this); +} + +GeckoFormSigningDialog::~GeckoFormSigningDialog() +{ + LOG ("GeckoFormSigningDialog dtor [%p]", this); +} + +/* nsIFormSigningDialog implementation */ + +/* boolean confirmSignText (in nsIInterfaceRequestor ctxt, + in AString host, + in AString signText, + [array, size_is (count)] in wstring certNickList, + [array, size_is (count)] in wstring certDetailsList, + in PRUint32 count, + out PRInt32 selectedIndex, + out AString password); */ +NS_IMETHODIMP +GeckoFormSigningDialog::ConfirmSignText (nsIInterfaceRequestor *ctx, + const nsAString & host, + const nsAString & signText, + const PRUnichar **certNickList, + const PRUnichar **certDetailsList, + PRUint32 count, + PRInt32 *selectedIndex, + nsAString &_password, + PRBool *_cancelled) +{ + /* FIXME: limit |signText| to a sensitlbe length (maybe 100k)? */ + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (ctx)); + if (!parent) { + parent = EphyJSUtils::GetDOMWindowFromCallContext (); + g_print ("Fallback window %p\n", (void*)parent.get()); + } + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + AutoWindowModalState modalState (parent); + + GladeXML *gxml = glade_xml_new (ephy_file ("form-signing-dialog.glade"), + "form_signing_dialog", NULL); + g_return_val_if_fail (gxml, NS_ERROR_FAILURE); + + GtkWidget *dialog = glade_xml_get_widget (gxml, "form_signing_dialog"); + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (gparent)); + + GtkLabel *primary_label = GTK_LABEL (glade_xml_get_widget (gxml, "primary_label")); + char *primary = g_strdup_printf (_("The web site “%s” requests that you sign the following text:"), + NS_ConvertUTF16toUTF8 (host).get ()); + gtk_label_set_text (primary_label, primary); + g_free (primary); + + GtkTextView *textview = GTK_TEXT_VIEW (glade_xml_get_widget (gxml, "textview")); + NS_ConvertUTF16toUTF8 text (signText); + gtk_text_buffer_set_text (gtk_text_view_get_buffer (textview), + text.get (), text.Length ()); + + GtkTable *table = GTK_TABLE (glade_xml_get_widget (gxml, "table")); + GtkComboBox *combo = GTK_COMBO_BOX (gtk_combo_box_new_text ()); + for (PRUint32 i = 0; i < count; ++i) { + gtk_combo_box_append_text (combo, NS_ConvertUTF16toUTF8 (certNickList[i]).get ()); + } + + gtk_combo_box_set_active (combo, 0); + gtk_table_attach (table, GTK_WIDGET (combo), 1, 2, 0, 1, + GtkAttachOptions (0), GtkAttachOptions (0), 0, 0); + gtk_widget_show (GTK_WIDGET (combo)); + + /* FIXME: Add "View Certificate" button */ + + GtkEntry *password_entry = GTK_ENTRY (glade_xml_get_widget (gxml, "password_entry")); + + GtkWidget *button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Sign text"), + GTK_RESPONSE_ACCEPT); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT); + gtk_widget_grab_focus (button); + + /* FIXME: make Sign insensitive for some time (proportional to text length, with maximum?) */ + + g_object_unref (gxml); + + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + + *_cancelled = response != GTK_RESPONSE_ACCEPT; + + if (response == GTK_RESPONSE_ACCEPT) { + _password = NS_ConvertUTF8toUTF16 (gtk_entry_get_text (password_entry)); + *selectedIndex = gtk_combo_box_get_active (combo); + } + + gtk_widget_destroy (dialog); + + return NS_OK; +} diff --git a/embed/xulrunner/components/GeckoFormSigningDialog.h b/embed/xulrunner/components/GeckoFormSigningDialog.h new file mode 100644 index 000000000..a4438e9d1 --- /dev/null +++ b/embed/xulrunner/components/GeckoFormSigningDialog.h @@ -0,0 +1,42 @@ +/* + * Copyright © 2006 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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$ + */ + +#ifndef GECKO_FORMSIGNINGDIALOGS_H +#define GECKO_FORMSIGNINGDIALOGS_H + +#include <nsIFormSigningDialog.h> + +/* 4e42a43e-fbc5-40cc-bcbb-8cbc4e2101eb */ +#define GECKO_FORMSIGNINGDIALOGS_CID \ +{ 0x4e42a43e, 0xfbc5, 0x40cc, { 0xbc, 0xbb, 0x8c, 0xbc, 0x4e, 0x21, 0x01, 0xeb } } + +#define GECKO_FORMSIGNINGDIALOGS_CLASSNAME "Gecko Form Signing Dialogs" + +class GeckoFormSigningDialog : public nsIFormSigningDialog +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIFORMSIGNINGDIALOG + + GeckoFormSigningDialog(); + ~GeckoFormSigningDialog(); +}; + +#endif /* GECKO_FORMSIGNINGDIALOGS_H */ diff --git a/embed/xulrunner/components/GeckoPrintService.cpp b/embed/xulrunner/components/GeckoPrintService.cpp new file mode 100644 index 000000000..b1d34a960 --- /dev/null +++ b/embed/xulrunner/components/GeckoPrintService.cpp @@ -0,0 +1,713 @@ +/* + * Copyright © 2006, 2007 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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.h> +#include <glib/gi18n.h> +#include <gtk/gtkcheckbutton.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkmessagedialog.h> +#include <gtk/gtkprintunixdialog.h> +#include <gtk/gtkstock.h> +#include <gtk/gtkwindow.h> +#include <glade/glade-xml.h> + +#include <nsStringGlue.h> + +#include <nsCOMPtr.h> +#include <nsIDOMWindow.h> +#include <nsIDOMWindowInternal.h> + +#include "eel-gconf-extensions.h" +#include "ephy-debug.h" +#include "ephy-embed-shell.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 "GeckoPrintSession.h" + +#include "GeckoPrintService.h" + +/* Some printing keys */ + +#define CONF_PRINT_BG_COLORS "/apps/epiphany/dialogs/print_background_colors" +#define CONF_PRINT_BG_IMAGES "/apps/epiphany/dialogs/print_background_images" +#define CONF_PRINT_COLOR "/apps/epiphany/dialogs/print_color" +#define CONF_PRINT_DATE "/apps/epiphany/dialogs/print_date" +#define CONF_PRINT_PAGE_NUMBERS "/apps/epiphany/dialogs/print_page_numbers" +#define CONF_PRINT_PAGE_TITLE "/apps/epiphany/dialogs/print_page_title" +#define CONF_PRINT_PAGE_URL "/apps/epiphany/dialogs/print_page_url" + +#define LITERAL(s) reinterpret_cast<const nsAString::char_type*>(NS_L(s)) + +/* From nsIDeviceContext.h */ +#define NS_ERROR_GFX_PRINTER_BASE (1) /* adjustable :-) */ +#define NS_ERROR_GFX_PRINTER_ACCESS_DENIED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GFX,NS_ERROR_GFX_PRINTER_BASE+5) +#define NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GFX,NS_ERROR_GFX_PRINTER_BASE+4) + +NS_IMPL_ISUPPORTS1 (GeckoPrintService, + nsIPrintingPromptService) + +GeckoPrintService::GeckoPrintService() +{ + LOG ("GeckoPrintService ctor [%p]", this); +} + +GeckoPrintService::~GeckoPrintService() +{ + LOG ("GeckoPrintService dtor [%p]", this); +} + +/* nsIPrintingPromptService implementation */ + +/* void showPrintDialog (in nsIDOMWindow parent, + in nsIWebBrowserPrint webBrowserPrint, + in nsIPrintSettings printSettings); */ +NS_IMETHODIMP +GeckoPrintService::ShowPrintDialog (nsIDOMWindow *aParent, + nsIWebBrowserPrint *aWebBrowserPrint, + nsIPrintSettings *aSettings) +{ + /* Locked down? */ + if (eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_PRINTING)) { + return NS_ERROR_GFX_PRINTER_ACCESS_DENIED; + } + + GeckoPrintSession *session = GeckoPrintSession::FromSettings (aSettings); + NS_ENSURE_TRUE (session, NS_ERROR_INVALID_POINTER); + + /* Print settings changes disallowed, just translate the settings */ + if (eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_PRINT_SETUP)) { + return PrintUnattended (aParent, aSettings); + } + + /* Not locked down, show the dialogue */ + + nsresult rv; +#if 0 + PRBool haveSelection = PR_FALSE; + rv = aSettings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, &haveSelection); + NS_ENSURE_SUCCESS (rv, rv); +#endif + + PRInt16 frameUI = nsIPrintSettings::kFrameEnableAll; + rv = aSettings->GetHowToEnableFrameUI (&frameUI); + NS_ENSURE_SUCCESS (rv, rv); + + GtkWidget *parent = EphyUtils::FindGtkParent (aParent); + NS_ENSURE_TRUE(parent, NS_ERROR_INVALID_POINTER); + + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) { + return rv; + } + + AutoWindowModalState modalState (aParent); + + EphyEmbedShell *shell = ephy_embed_shell_get_default (); + + GladeXML *xml = glade_xml_new (ephy_file ("print.glade"), + "print_dialog_custom_tab", NULL); + if (!xml) { + return NS_ERROR_FAILURE; + } + + /* Build the custom tab */ + GtkWidget *custom_tab = glade_xml_get_widget (xml, "custom_tab_container"); + ephy_gui_connect_checkbutton_to_gconf (glade_xml_get_widget (xml, "print_bg_colors_checkbutton"), CONF_PRINT_BG_COLORS); + ephy_gui_connect_checkbutton_to_gconf (glade_xml_get_widget (xml, "print_bg_images_checkbutton"), CONF_PRINT_BG_IMAGES); + ephy_gui_connect_checkbutton_to_gconf (glade_xml_get_widget (xml, "print_date_checkbutton"), CONF_PRINT_DATE); + ephy_gui_connect_checkbutton_to_gconf (glade_xml_get_widget (xml, "print_page_numbers_checkbutton"), CONF_PRINT_PAGE_NUMBERS); + ephy_gui_connect_checkbutton_to_gconf (glade_xml_get_widget (xml, "print_page_title_checkbutton"), CONF_PRINT_PAGE_TITLE); + ephy_gui_connect_checkbutton_to_gconf (glade_xml_get_widget (xml, "print_page_url_checkbutton"), CONF_PRINT_PAGE_URL); + + GtkWidget *frame_box = glade_xml_get_widget (xml, "frame_box"); + GtkWidget *print_frames_normal = glade_xml_get_widget (xml, "print_frames_normal"); + GtkWidget *print_frames_selected = glade_xml_get_widget (xml, "print_frames_selected"); + GtkWidget *print_frames_separately = glade_xml_get_widget (xml, "print_frames_separately"); + + /* FIXME: store/load from pref */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (print_frames_normal), TRUE); + + if (frameUI == nsIPrintSettings::kFrameEnableAll) { + /* Allow all frame options */ + gtk_widget_set_sensitive (frame_box, TRUE); + } else if (frameUI == nsIPrintSettings::kFrameEnableAsIsAndEach) { + /* Allow all except "selected frame" */ + gtk_widget_set_sensitive (frame_box, TRUE); + gtk_widget_set_sensitive (print_frames_selected, FALSE); + /* Preselect this one, since the default above only prints _one page_ ! */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (print_frames_separately), TRUE); + } + + /* FIXME: this sucks! find some way to do all of this async! */ + GtkWidget *dialog = gtk_print_unix_dialog_new (NULL /* FIXME title */, + GTK_WINDOW (parent)); + GtkPrintUnixDialog *print_dialog = GTK_PRINT_UNIX_DIALOG (dialog); + + GtkPrintCapabilities capabilities = + GtkPrintCapabilities (GTK_PRINT_CAPABILITY_PAGE_SET | + GTK_PRINT_CAPABILITY_COPIES | + GTK_PRINT_CAPABILITY_COLLATE | + GTK_PRINT_CAPABILITY_REVERSE | + GTK_PRINT_CAPABILITY_SCALE | + GTK_PRINT_CAPABILITY_GENERATE_PS | + GTK_PRINT_CAPABILITY_GENERATE_PDF); + gtk_print_unix_dialog_set_manual_capabilities (print_dialog, capabilities); + + gtk_print_unix_dialog_set_page_setup (print_dialog, + ephy_embed_shell_get_page_setup (shell)); + gtk_print_unix_dialog_set_settings (print_dialog, + ephy_embed_shell_get_print_settings (shell)); + + /* Remove custom tab from its dummy window and put it in the print dialogue */ + g_object_ref_sink (custom_tab); + gtk_container_remove (GTK_CONTAINER (custom_tab->parent), custom_tab); + gtk_print_unix_dialog_add_custom_tab (print_dialog, custom_tab, + gtk_label_new (_("Options"))); /* FIXME better name! */ + g_object_unref (custom_tab); + g_object_unref (xml); + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_hide (dialog); + + GtkPrinter *printer = gtk_print_unix_dialog_get_selected_printer (print_dialog); + + if (response != GTK_RESPONSE_OK || !printer) { + gtk_widget_destroy (dialog); + + return NS_ERROR_ABORT; + } + + PRInt16 printFrames = nsIPrintSettings::kNoFrames; + if (frameUI != nsIPrintSettings::kFrameEnableNone) { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (print_frames_normal))) { + printFrames = nsIPrintSettings::kFramesAsIs; + } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (print_frames_selected))) { + printFrames = nsIPrintSettings::kSelectedFrame; + } if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (print_frames_separately))) { + printFrames = nsIPrintSettings::kEachFrameSep; + } + } + + GtkPageSetup *pageSetup = gtk_print_unix_dialog_get_page_setup (print_dialog); /* no reference owned */ + ephy_embed_shell_set_page_setup (shell, pageSetup); + + GtkPrintSettings *settings = gtk_print_unix_dialog_get_settings (print_dialog); + ephy_embed_shell_set_print_settings (shell, settings); + + /* We copy the setup and settings so we can modify them to unset + * options handled by gecko. + */ + GtkPageSetup *pageSetupCopy = gtk_page_setup_copy (pageSetup); + pageSetup = pageSetupCopy; + + GtkPrintSettings *settingsCopy = gtk_print_settings_copy (settings); + g_object_unref (settings); + settings = settingsCopy; + + rv = session->SetSettings (aSettings, settings, pageSetup, printer); + + /* Now translate the settings to nsIPrintSettings */ + if (NS_SUCCEEDED (rv)) { + nsCString sourceFile; + session->GetSourceFile (sourceFile); + if (!sourceFile.IsEmpty ()) { + rv = TranslateSettings (settings, pageSetup, printer, sourceFile, printFrames, PR_TRUE, aSettings); + } else { + rv = NS_ERROR_FAILURE; + } + } + + gtk_widget_destroy (dialog); + + g_object_unref (settings); + g_object_unref (pageSetup); + + return rv; +} + +/* void showProgress (in nsIDOMWindow parent, + in nsIWebBrowserPrint webBrowserPrint, + in nsIPrintSettings printSettings, + in nsIObserver openDialogObserver, + in boolean isForPrinting, + out nsIWebProgressListener webProgressListener, + out nsIPrintProgressParams printProgressParams, + out boolean notifyOnOpen); */ +NS_IMETHODIMP +GeckoPrintService::ShowProgress (nsIDOMWindow *aParent, + nsIWebBrowserPrint *aWebBrowserPrint, + nsIPrintSettings *aPrintSettings, + nsIObserver *aOpenDialogObserver, + PRBool aIsForPrinting, + nsIWebProgressListener **_webProgressListener, + nsIPrintProgressParams **_printProgressParams, + PRBool *_notifyOnOpen) +{ + /* Print preview */ + if (!aIsForPrinting) { + return NS_OK; + } + + nsresult rv; + nsCOMPtr<nsIDOMWindowInternal> domWin (do_QueryInterface (aParent, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIPrintSession> session; + rv = aPrintSettings->GetPrintSession (getter_AddRefs (session)); + NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && session, nsnull); + + nsCOMPtr<nsIPrintProgress> progress (do_QueryInterface (session, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + /* Our print session implements those interfaces */ + rv = CallQueryInterface (session, _webProgressListener); + rv |= CallQueryInterface (session, _printProgressParams); + NS_ENSURE_SUCCESS (rv, rv); + + /* Setting this to PR_FALSE will make gecko immediately start printing + * when we return from this function. + * If we set this to PR_TRUE, we need to call aOpenDialogObserver::Observe + * (topic, subject and data don't matter) when we're ready for printing. + */ + *_notifyOnOpen = PR_FALSE; + + return progress->OpenProgressDialog (domWin, nsnull, nsnull, aOpenDialogObserver, _notifyOnOpen); +} + +/* void showPageSetup (in nsIDOMWindow parent, + in nsIPrintSettings printSettings, + in nsIObserver aObs); */ +NS_IMETHODIMP GeckoPrintService::ShowPageSetup (nsIDOMWindow *aParent, + nsIPrintSettings *aPrintSettings, + nsIObserver *aObserver) +{ + /* This function is never called from gecko code */ +#if 0 + /* Locked down? */ + if (eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_PRINTING) || + eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_PRINT_SETUP)) { + return NS_ERROR_ABORT; + } + + GtkWidget *parent = EphyUtils::FindGtkParent (aParent); + NS_ENSURE_TRUE(parent, NS_ERROR_INVALID_POINTER); + + AutoJSContextStack stack; + nsresult rv = stack.Init (); + if (NS_FAILED (rv)) { + return rv; + } + + AutoWindowModalState modalState (aParent); + + EphyEmbedShell *shell = ephy_embed_shell_get_default (); + GtkPageSetup *new_setup = + gtk_print_run_page_setup_dialog (GTK_WINDOW (parent), + ephy_embed_shell_get_page_setup (shell), + ephy_embed_shell_get_print_settings (shell)); + if (new_setup) { + ephy_embed_shell_set_page_setup (shell, new_setup); + g_object_unref (new_setup); + } + + /* FIXME do we need to notify aObserver somehow? */ + return NS_OK; +#endif + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void showPrinterProperties (in nsIDOMWindow parent, + in wstring printerName, + in nsIPrintSettings printSettings); */ +NS_IMETHODIMP +GeckoPrintService::ShowPrinterProperties (nsIDOMWindow *aParent, + const PRUnichar *aPrinterName, + nsIPrintSettings *aPrintSettings) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* Private methods */ + +#if 0 +typedef struct +{ + GMainLoop *mainLoop; + GtkPrinter *mPrinter; + guint timeout; + int response; + guint cancelled : 1; +} FindPrinterData; + +static void +FreeFindPrinterData (FindPrinterData *data) +{ + if (data->printer) { + g_object_unref (data->printer); + } +} + +static void +DialogResponseCallback (GtkWidget *aDialog, + int aResponse, + FindPrinterData *data) +{ + data->response = aResponse; + g_main_loop_quit (data->mainloop); +} + +static void +TimeoutCallback (FindPrinterData *data) +{ + data->cancelled = TRUE; + g_main_loop_quit (data->mainLoop); + data->mainLoop = NULL; + return FALSE; +} + +static gboolean +PrinterEnumerateCallback (GtkPrinter *aPrinter, + FindPrinterData *data) +{ + if (data->cancelled) + return TRUE; + + if ((data->printerName && + strcmp (data->printerName, gtk_printer_get_name (aPrinter)) == 0) || + (!data->printerName && + gtk_printer_is_default (aPrinter))) { + data->printer = g_object_ref (aPrinter); + return TRUE; + } + + return FALSE; +} +#endif + +nsresult +GeckoPrintService::PrintUnattended (nsIDOMWindow *aParent, + nsIPrintSettings *aPrintSettings) +{ + return NS_ERROR_NOT_IMPLEMENTED; +#if 0 + GtkWidget *parent = EphyUtils::FindGtkParent (aParent); + NS_ENSURE_TRUE(parent, NS_ERROR_INVALID_POINTER); + + PRBool isCalledFromScript = EphyJSUtils::IsCalledFromScript (); + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) { + return rv; + } + + AutoWindowModalState modalState (aParent); + + EphyEmbedShell *shell = ephy_embed_shell_get_default (); + GtkPrintSettings *settings = ephy_embed_shell_get_print_settings (shell); + NS_ENSURE_TRUE (settings, NS_ERROR_FAILURE); + + const char *printer = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_PRINTER); +#if 0 + if (!printer || !printer[0]) { + return NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND; + } +#endif + /* We need to find the printer, so we need to run a mainloop. + * If called from a script, give the user a way to cancel the print; + * otherwise we'll just show a generic progress message. + */ + GtkWidget *dialog; + if (isCalledFromScript) { + dialog = gtk_message_dialog_new (GTK_WINDOW (parent), + GtkDialogFlags (0), + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + "%s", _("Print this page?")); + gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_PRINT, + GTK_RESPONSE_ACCEPT); + } else { + dialog = gtk_message_dialog_new (GTK_WINDOW (parent), + GtkDialogFlags (0), + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + "%s", _("Preparing to print")); + } + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + + FindPrinterData *data = g_new0 (PrinterData, 1); + data->dialog = dialog; + + g_signal_connect (dialog, "response", + G_CALLBACK (DialogResponseCallback), data); + + /* Don't run forever */ + data->timeoutId = g_timeout_add (PRINTER_ENUMERATE_TIMEOUT, + (GSourceFunc) EnumerateTimoutCallback, + data); + /* Enumerate printers until we find our printer */ + gtk_enumerate_printers ((GtkPrinterFunc) PrinterEnumerateCallback, + data, + (GDestroyNotify) EnumerateDestroyCallback, FALSE); + + /* Now run the mainloop */ + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + Printer + gtk_widget_destroy (dialog); + + if (response != GTK_RESPONSE_ACCEPT) { + return NS_ERROR_ABORT; + } + + nsCString sourceFile; + session->GetSourceFile (sourceFile); + if (!sourceFile.IsEmpty ()) { + rv = TranslateSettings (settings, + ephy_embed_shell_get_page_setup (shell), + sourceFile, PR_TRUE, aSettings); + } else { + rv = NS_ERROR_FAILURE; + } + + return rv; + } +#endif /* if 0 */ +} + +/* Static methods */ + +/* static */ nsresult +GeckoPrintService::TranslateSettings (GtkPrintSettings *aGtkSettings, + GtkPageSetup *aPageSetup, + GtkPrinter *aPrinter, + const nsACString &aSourceFile, + PRInt16 aPrintFrames, + PRBool aIsForPrinting, + nsIPrintSettings *aSettings) +{ + NS_ENSURE_ARG (aPrinter); + NS_ENSURE_ARG (aGtkSettings); + NS_ENSURE_ARG (aPageSetup); + +#if 0 + /* Locked down? */ + if (gtk_print_settings_get_print_to_file (aGtkSettings) && + eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_SAVE_TO_DISK)) { + return NS_ERROR_GFX_PRINTER_ACCESS_DENIED; + } +#endif + + GtkPrintCapabilities capabilities = gtk_printer_get_capabilities (aPrinter); + + /* Initialisation */ + aSettings->SetIsInitializedFromPrinter (PR_FALSE); /* FIXME: PR_TRUE? */ + aSettings->SetIsInitializedFromPrefs (PR_FALSE); /* FIXME: PR_TRUE? */ + aSettings->SetPrintSilent (PR_FALSE); + aSettings->SetShowPrintProgress (PR_TRUE); + + /* We always print PS to a file and then hand that off to gtk-print */ + aSettings->SetPrinterName (LITERAL ("PostScript/default")); + + if (aIsForPrinting) { + aSettings->SetPrintToFile (PR_TRUE); + + nsString sourceFile; + NS_CStringToUTF16 (aSourceFile, + NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, + sourceFile); + + aSettings->SetToFileName (sourceFile.get ()); + } else { + /* Otherwise mozilla will create the file nevertheless and + * fail since we haven't set a name! + */ + aSettings->SetPrintToFile (PR_FALSE); + } + + /* This is the time between printing each page, in ms. + * It 'gives the user more time to press cancel' ! + * We don't want any of this nonsense, so set this to a low value, + * just enough to update the print dialogue. + */ + aSettings->SetPrintPageDelay (50); + + if (aIsForPrinting) { + NS_ENSURE_TRUE (aPrinter, NS_ERROR_FAILURE); + + const char *format = gtk_print_settings_get (aGtkSettings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT); + if (!format) + format = "ps"; + + if (strcmp (format, "pdf") == 0 && + gtk_printer_accepts_pdf (aPrinter)) { + aSettings->SetOutputFormat (nsIPrintSettings::kOutputFormatPDF); + } else if (strcmp (format, "ps") == 0 && + gtk_printer_accepts_ps (aPrinter)) { + aSettings->SetOutputFormat (nsIPrintSettings::kOutputFormatPS); + } else { + g_warning ("Output format '%s' specified, but printer '%s' does not support it!", + format, gtk_printer_get_name (aPrinter)); + return NS_ERROR_FAILURE; + } + + int n_copies = gtk_print_settings_get_n_copies (aGtkSettings); + if (n_copies <= 0) + return NS_ERROR_FAILURE; + if (capabilities & GTK_PRINT_CAPABILITY_COPIES) { + aSettings->SetNumCopies (1); + } else { + /* We have to copy them ourself */ + aSettings->SetNumCopies (n_copies); + gtk_print_settings_set_n_copies (aGtkSettings, 1); + } + + gboolean reverse = gtk_print_settings_get_reverse (aGtkSettings); + if (capabilities & GTK_PRINT_CAPABILITY_REVERSE) { + aSettings->SetPrintReversed (PR_FALSE); + } else { + aSettings->SetPrintReversed (reverse); + gtk_print_settings_set_reverse (aGtkSettings, FALSE); + } + + GtkPageSet pageSet = gtk_print_settings_get_page_set (aGtkSettings); + aSettings->SetPrintOptions (nsIPrintSettings::kPrintEvenPages, + pageSet != GTK_PAGE_SET_ODD); + aSettings->SetPrintOptions (nsIPrintSettings::kPrintEvenPages, + pageSet != GTK_PAGE_SET_EVEN); + + GtkPrintPages printPages = gtk_print_settings_get_print_pages (aGtkSettings); + switch (printPages) { + case GTK_PRINT_PAGES_RANGES: { + int numRanges = 0; + GtkPageRange *pageRanges = gtk_print_settings_get_page_ranges (aGtkSettings, &numRanges); + if (numRanges > 0) { + /* FIXME: We can only support one range, ignore more ranges or raise error? */ + aSettings->SetPrintRange (nsIPrintSettings::kRangeSpecifiedPageRange); + /* Gecko page numbers start at 1, while gtk page numbers start at 0 */ + aSettings->SetStartPageRange (pageRanges[0].start + 1); + aSettings->SetEndPageRange (pageRanges[0].end + 1); + + g_free (pageRanges); + break; + } + /* Fall-through to PAGES_ALL */ + } + case GTK_PRINT_PAGES_CURRENT: + /* not supported, fall through */ + case GTK_PRINT_PAGES_ALL: + aSettings->SetPrintRange (nsIPrintSettings::kRangeAllPages); + break; + /* FIXME: we need some custom ranges here, "Selection" and "Focused Frame" */ + } + } else { + aSettings->SetPrintOptions (nsIPrintSettings::kPrintEvenPages, PR_TRUE); + aSettings->SetPrintOptions (nsIPrintSettings::kPrintEvenPages, PR_TRUE); + aSettings->SetPrintReversed (PR_FALSE); + aSettings->SetPrintRange (nsIPrintSettings::kRangeAllPages); + } + + /* And clear those in the settings, so the printer doesn't try to apply them too */ + gtk_print_settings_set_print_pages (aGtkSettings, GTK_PRINT_PAGES_ALL); + gtk_print_settings_set_page_ranges (aGtkSettings, NULL, 0); + gtk_print_settings_set_page_set (aGtkSettings, GTK_PAGE_SET_ALL); + + switch (gtk_print_settings_get_orientation (aGtkSettings)) { + case GTK_PAGE_ORIENTATION_PORTRAIT: + case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT: /* not supported */ + aSettings->SetOrientation (nsIPrintSettings::kPortraitOrientation); + break; + case GTK_PAGE_ORIENTATION_LANDSCAPE: + case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE: /* not supported */ + aSettings->SetOrientation (nsIPrintSettings::kLandscapeOrientation); + break; + } + + aSettings->SetPrintInColor (gtk_print_settings_get_use_color (aGtkSettings)); + + aSettings->SetPaperSizeUnit(nsIPrintSettings::kPaperSizeMillimeters); + + GtkPaperSize *paperSize = gtk_page_setup_get_paper_size (aPageSetup); + if (!paperSize) { + return NS_ERROR_FAILURE; + } + + aSettings->SetPaperSizeType (nsIPrintSettings::kPaperSizeDefined); + aSettings->SetPaperWidth (gtk_paper_size_get_width (paperSize, GTK_UNIT_MM)); + aSettings->SetPaperHeight (gtk_paper_size_get_height (paperSize, GTK_UNIT_MM)); + aSettings->SetPaperName (NS_ConvertUTF8toUTF16 (gtk_paper_size_get_name (paperSize)).get ()); + + /* Sucky mozilla wants margins in inch! */ + aSettings->SetMarginTop (gtk_page_setup_get_top_margin (aPageSetup, GTK_UNIT_INCH)); + aSettings->SetMarginBottom (gtk_page_setup_get_bottom_margin (aPageSetup, GTK_UNIT_INCH)); + aSettings->SetMarginLeft (gtk_page_setup_get_left_margin (aPageSetup, GTK_UNIT_INCH)); + aSettings->SetMarginRight (gtk_page_setup_get_right_margin (aPageSetup, GTK_UNIT_INCH)); + + aSettings->SetHeaderStrLeft (eel_gconf_get_boolean (CONF_PRINT_PAGE_TITLE) ? LITERAL ("&T") : LITERAL ("")); + aSettings->SetHeaderStrCenter (LITERAL ("")); + aSettings->SetHeaderStrRight (eel_gconf_get_boolean (CONF_PRINT_PAGE_URL) ? LITERAL ("&U") : LITERAL ("")); + aSettings->SetFooterStrLeft (eel_gconf_get_boolean (CONF_PRINT_PAGE_NUMBERS) ? LITERAL ("&PT") : LITERAL ("")); + aSettings->SetFooterStrCenter (LITERAL ("")); + aSettings->SetFooterStrRight (eel_gconf_get_boolean (CONF_PRINT_DATE) ? LITERAL ("&D") : LITERAL ("")); + + aSettings->SetPrintFrameType (aPrintFrames); + aSettings->SetPrintFrameTypeUsage (nsIPrintSettings::kUseSettingWhenPossible); + + /* FIXME: only if GTK_PRINT_CAPABILITY_SCALE is not set? */ + aSettings->SetScaling (gtk_print_settings_get_scale (aGtkSettings) / 100.0); + gtk_print_settings_set_scale (aGtkSettings, 1.0); + + aSettings->SetShrinkToFit (PR_FALSE); /* FIXME setting */ + + aSettings->SetPrintBGColors (eel_gconf_get_boolean (CONF_PRINT_BG_COLORS) != FALSE); + aSettings->SetPrintBGImages (eel_gconf_get_boolean (CONF_PRINT_BG_IMAGES) != FALSE); + + /* aSettings->SetPlexName (LITERAL ("default")); */ + /* aSettings->SetColorspace (LITERAL ("default")); */ + /* aSettings->SetResolutionName (LITERAL ("default")); */ + /* aSettings->SetDownloadFonts (PR_TRUE); */ + + /* Unset those setting that we can handle, so they don't get applied + * again for the print job. + */ + /* gtk_print_settings_set_collate (aGtkSettings, FALSE); not yet */ + /* FIXME: Unset the orientation for the print job? */ + /* gtk_print_settings_set_orientation (aGtkSettings, GTK_PAGE_ORIENTATION_PORTRAIT); */ + /* FIXME: unset output format -> "ps" ? */ + + return NS_OK; +} diff --git a/embed/xulrunner/components/GeckoPrintService.h b/embed/xulrunner/components/GeckoPrintService.h new file mode 100644 index 000000000..c2e21cf3e --- /dev/null +++ b/embed/xulrunner/components/GeckoPrintService.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2006 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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$ + */ + +#ifndef GECKO_PRINT_SERVICE_H +#define GECKO_PRINT_SERVICE_H + +#include <gtk/gtkpagesetup.h> +#include <gtk/gtkprintsettings.h> +#include <gtk/gtkprinter.h> + +#include <nsIPrintingPromptService.h> + +class nsIPrintSettings; + +/* 6a71ff30-7f4d-4d91-b71a-d5c9764b34be */ +#define GECKO_PRINT_SERVICE_IID \ +{ 0x6a71ff30, 0x7f4d, 0x4d91, \ + { 0xb7, 0x1a, 0xd5, 0xc9, 0x76, 0x4b, 0x34, 0xbe } } + +#define GECKO_PRINT_SERVICE_CLASSNAME "Gecko Print Service" + +class GeckoPrintService : public nsIPrintingPromptService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRINTINGPROMPTSERVICE + + GeckoPrintService(); + virtual ~GeckoPrintService(); + + static nsresult TranslateSettings (GtkPrintSettings*, GtkPageSetup *, GtkPrinter *, const nsACString&, PRInt16, PRBool, nsIPrintSettings*); + +private: + nsresult PrintUnattended (nsIDOMWindow *, nsIPrintSettings *); +}; + +#endif /* GECKO_PRINT_SERVICE_H */ diff --git a/embed/xulrunner/components/GeckoPrintSession.cpp b/embed/xulrunner/components/GeckoPrintSession.cpp new file mode 100644 index 000000000..e0c3b89e9 --- /dev/null +++ b/embed/xulrunner/components/GeckoPrintSession.cpp @@ -0,0 +1,629 @@ +/* + * Copyright © 2006 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 <unistd.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <nsStringGlue.h> + +#include <nsIDOMWindow.h> +#include <nsIDOMWindowInternal.h> +#include <nsIPrintSettings.h> + +#include "ephy-debug.h" +#include "ephy-embed-shell.h" +#include "ephy-file-helpers.h" + +#include "EphyUtils.h" + +#include "GeckoPrintSession.h" + +#define MAX_STRING_LENGTH 512 + +GeckoPrintSession::GeckoPrintSession () +: mSettings(NULL) +, mPageSetup(NULL) +, mPrinter(NULL) +, mJob(NULL) +, mProgressDialog(NULL) +, mTitleLabel(NULL) +, mProgressBar(NULL) +, mStartPrintIdleID(0) +, mSourceFileIsTemp(PR_FALSE) +, mDone(PR_FALSE) +, mCancelled(PR_FALSE) +{ + LOG ("GeckoPrintSession ctor [%p]", (void*) this); + + /* FIXME: connect to "prepare-close" ? */ + g_object_ref (ephy_embed_shell_get_default ()); +} + +GeckoPrintSession::~GeckoPrintSession () +{ + LOG ("GeckoPrintSession dtor [%p]", (void*) this); + + NS_ASSERTION (mStartPrintIdleID == 0, "Impossible"); + + if (!mDone && !mCancelled) { + Cancel (); + } + DestroyJob (); + + if (mSettings) { + g_object_unref (mSettings); + } + if (mPageSetup) { + g_object_unref (mPageSetup); + } + if (mPrinter) { + g_object_unref (mPrinter); + } + if (mProgressDialog) { + gtk_widget_destroy (mProgressDialog); + } + if (mSourceFileIsTemp) { + unlink (mSourceFile.get ()); + } + + g_object_unref (ephy_embed_shell_get_default ()); +} + +void +GeckoPrintSession::GetSourceFile (nsACString &aSource) +{ + aSource.Assign (mSourceFile); +} + +nsresult +GeckoPrintSession::SetSettings (nsIPrintSettings *aPrintSettings, + GtkPrintSettings *aSettings, + GtkPageSetup *aPageSetup, + GtkPrinter *aPrinter) +{ + NS_ASSERTION (!mPrintSettings && !mSettings && !mPageSetup && !mPrinter, "Already have settings!"); + + NS_ENSURE_ARG (aPrintSettings); + NS_ENSURE_ARG (aSettings); + + mPrintSettings = aPrintSettings; + mSettings = (GtkPrintSettings *) g_object_ref (aSettings); + + NS_ENSURE_ARG (aPageSetup); + NS_ENSURE_ARG (aPrinter); + + mPageSetup = (GtkPageSetup *) g_object_ref (aPageSetup); + mPrinter = (GtkPrinter *) g_object_ref (aPrinter); + +#if 0 + /* Compute the source file name */ + if (gtk_print_settings_get_print_to_file (mSettings)) { + /* FIXME: support gnome-VFS uris here! */ + const char *fileURI = gtk_print_settings_get (aSettings, "export-uri"); + NS_ENSURE_TRUE (fileURI, NS_ERROR_FAILURE); + + char *fileName = g_filename_from_uri (fileURI, NULL, NULL); + NS_ENSURE_TRUE (fileURI, NS_ERROR_FAILURE); + + mSourceFile.Assign (fileName); + g_free (fileName); + } else +#endif + { + char *base, *tmpName; + + /* FIXME: use pure glib here (g_mkstemp)! */ + base = g_build_filename (ephy_file_tmp_dir (), "print-XXXXXX", (const char *) NULL); + tmpName = ephy_file_tmp_filename (base, "ps"); + g_free (base); + + NS_ENSURE_TRUE (tmpName, NS_ERROR_FAILURE); + mSourceFile.Assign (tmpName); + g_free (tmpName); + + mSourceFileIsTemp = PR_TRUE; + } + + return NS_OK; +} + +/* static methods */ + +/* static */ GeckoPrintSession * +GeckoPrintSession::FromSettings (nsIPrintSettings *aSettings) +{ + nsresult rv; + nsCOMPtr<nsIPrintSession> session; + rv = aSettings->GetPrintSession (getter_AddRefs (session)); + NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && session, nsnull); + + /* this is ok since the caller holds a ref to the settings which hold a ref to the session */ + nsIPrintSession *sessionPtr = session.get(); + return static_cast<GeckoPrintSession*>(sessionPtr); +} + +/* static functions */ + +static void +ReleaseSession (GeckoPrintSession *aSession) +{ + NS_RELEASE (aSession); +} + +static gboolean +ProgressDeleteCallback (GtkDialog *aDialog) +{ + gtk_dialog_response (aDialog, GTK_RESPONSE_DELETE_EVENT); + return TRUE; +} + +static void +ProgressResponseCallback (GtkDialog *aDialog, + int aResponse, + GeckoPrintSession *aSession) +{ + aSession->Cancel (); +} + +static gboolean +StartPrintIdleCallback (GeckoPrintSession *aSession) +{ + aSession->StartPrinting (); + + return FALSE; +} + +static void +JobStatusChangedCallback (GtkPrintJob *aJob, + GeckoPrintSession *aSession) +{ + aSession->JobStatusChanged (); +} + +static void +JobCompletedCallback (GtkPrintJob *aJob, + GeckoPrintSession *aSession, + GError *aError) +{ + aSession->JobDone (); + + if (aError) { + aSession->JobError (aError->message); + } +} + +/* Private methods */ + +void +GeckoPrintSession::SetProgress (PRInt32 aCurrent, + PRInt32 aMaximum) +{ + NS_ENSURE_TRUE (mProgressDialog, ); + + if (mCancelled) return; + + /* Mozilla is weird */ + if (aCurrent > aMaximum || (aCurrent == 100 && aMaximum == 100)) return; + + double fraction = 0.0; + if (aMaximum > 0 && aCurrent >= 0) { + char *text = g_strdup_printf (_("Page %d of %d"), aCurrent, aMaximum); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (mProgressBar), text); + g_free (text); + + fraction = (double) aCurrent / (double) aMaximum; + } + + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (mProgressBar), CLAMP (fraction, 0.0, 1.0)); +} + +void +GeckoPrintSession::SetProgressText (const char *aText) +{ + NS_ENSURE_TRUE (mProgressDialog, ); + + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (mProgressBar), aText); +} + +void +GeckoPrintSession::Cancel () +{ + SetProcessCanceledByUser (PR_TRUE); + + if (mProgressDialog) { + gtk_dialog_set_response_sensitive (GTK_DIALOG (mProgressDialog), + GTK_RESPONSE_CANCEL, FALSE); + + SetProgress (0, 0); + SetProgressText (_("Cancelling print")); /* FIXME text! */ + } + + if (mJob) { + /* FIXME: There's no way to cancel mJob! Bug #339323 */ + } +} + +void +GeckoPrintSession::StartPrinting () +{ + mStartPrintIdleID = 0; + + GError *error = NULL; + +#if 0 + /* FIXME: this could also be a print job to a file which was + * printed to a temp file and now needs to be uploaded to its + * final location with gnome-vfs. + */ + if (gtk_print_settings_get_print_to_file (mSettings)) return; +#endif + + NS_ENSURE_TRUE (mSettings && mPageSetup && mPrinter, ); + + mJob = gtk_print_job_new (mTitle.get (), + mPrinter, + mSettings, + mPageSetup); + if (!gtk_print_job_set_source_file (mJob, mSourceFile.get (), &error)) { + /* FIXME: error dialogue! */ + g_warning ("Couldn't set print job source: %s", error->message); + g_error_free (error); + + g_object_unref (mJob); + mJob = NULL; + + return; + } + + g_signal_connect (mJob, "status-changed", + G_CALLBACK (JobStatusChangedCallback), this); + + /* Keep us alive until the job is done! */ + NS_ADDREF_THIS (); + gtk_print_job_send (mJob, + (GtkPrintJobCompleteFunc) JobCompletedCallback, + this, + (GDestroyNotify) ReleaseSession); +} + +void +GeckoPrintSession::JobStatusChanged () +{ + NS_ENSURE_TRUE (mProgressDialog, ); + + LOG ("print session %p status changed %d\n", this, gtk_print_job_get_status (mJob)); + + /* FIXME: are any other status codes relevant info for the user? */ + if (gtk_print_job_get_status (mJob) == GTK_PRINT_STATUS_SENDING_DATA) { + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (mProgressBar), 0.75); + /* FIXME text! */ + SetProgressText (_("Spooling...")); + } +} + +void +GeckoPrintSession::JobError (const char *aErrorMessage) +{ + LOG ("print job error: %s", aErrorMessage); + + /* FIXME better text */ + GtkWidget *dialog = gtk_message_dialog_new (NULL, + GtkDialogFlags (0), + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Print error")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", aErrorMessage); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); +} + +void +GeckoPrintSession::JobDone () +{ + NS_ENSURE_TRUE (mProgressDialog, ); + + mDone = PR_TRUE; + + gtk_widget_hide (mProgressDialog); + + DestroyJob (); +} + +void +GeckoPrintSession::DestroyJob () +{ + if (!mJob) return; + + g_signal_handlers_disconnect_by_func (mJob, (void*) JobStatusChangedCallback, this); + g_object_unref (mJob); + mJob = NULL; +} + +void +GeckoPrintSession::LaunchJobOnIdle () +{ + NS_ASSERTION (!mStartPrintIdleID, "Already started printing!"); + + /* Don't send the job to the printer if the user cancelled the print */ + if (mCancelled) return; + + /* Keep us alive until the idle handler runs! */ + NS_ADDREF_THIS (); + mStartPrintIdleID = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) StartPrintIdleCallback, + this, + (GDestroyNotify) ReleaseSession); +} + +/* XPCOM interfaces */ + +NS_IMPL_THREADSAFE_ISUPPORTS5 (GeckoPrintSession, + nsIPrintSession, + nsIWebProgressListener, + nsIPrintProgress, + nsIPrintProgressParams, + nsISupportsWeakReference) + +/* nsIPrintSession implementation */ + +/* nsIWebProgressListener implementation */ + +/* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long aStateFlags, in nsresult aStatus); */ +NS_IMETHODIMP +GeckoPrintSession::OnStateChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRUint32 aStateFlags, + nsresult aStatus) +{ + if (NS_SUCCEEDED (aStatus) && + aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) { + if (aStateFlags & nsIWebProgressListener::STATE_START) { + /* Printing starts now */ + SetProgress (0, 0); + } else if ((aStateFlags & nsIWebProgressListener::STATE_STOP)) { + /* Printing done, upload to printer */ + LaunchJobOnIdle (); + } + } + + return NS_OK; +} + +/* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */ +NS_IMETHODIMP +GeckoPrintSession::OnProgressChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt32 aCurSelfProgress, + PRInt32 aMaxSelfProgress, + PRInt32 aCurTotalProgress, + PRInt32 aMaxTotalProgress) +{ + SetProgress (aCurTotalProgress, aMaxTotalProgress); + + return NS_OK; +} + +/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI aLocation); */ +NS_IMETHODIMP +GeckoPrintSession::OnLocationChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsIURI *aLocation) +{ + NS_ASSERTION (0, "OnLocationChange reached!"); + return NS_OK; +} + +/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */ +NS_IMETHODIMP +GeckoPrintSession::OnStatusChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aMessage) +{ + NS_ASSERTION (0, "OnStatusChange reached!"); + return NS_OK; +} + +/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long aState); */ +NS_IMETHODIMP +GeckoPrintSession::OnSecurityChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRUint32 aState) +{ + NS_ASSERTION (0, "OnSecurityChange reached!"); + return NS_OK; +} + +/* nsIPrintProgress implementation */ + +/* void openProgressDialog (in nsIDOMWindowInternal parent, in string dialogURL, in nsISupports parameters, in nsIObserver openDialogObserver, out boolean notifyOnOpen); */ +NS_IMETHODIMP +GeckoPrintSession::OpenProgressDialog (nsIDOMWindowInternal *aParent, + const char *aDialogURL, + nsISupports *aParameters, + nsIObserver *aOpenDialogObserver, + PRBool *_notifyOnOpen) +{ + NS_ENSURE_STATE (!mProgressDialog); + + nsCOMPtr<nsIDOMWindow> domWindow (do_QueryInterface (aParent)); + GtkWidget *parent = EphyUtils::FindGtkParent (domWindow); + + GtkWidget *vbox, *hbox, *image; + + mProgressDialog = gtk_dialog_new (); + GtkDialog *dialog = GTK_DIALOG (mProgressDialog); + + gtk_dialog_add_button (dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + g_signal_connect (dialog, "delete-event", + G_CALLBACK (ProgressDeleteCallback), NULL); + g_signal_connect (dialog, "response", + G_CALLBACK (ProgressResponseCallback), this); + + /* FIXME do we need transient? initially on top should suffice */ + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (parent)); + + gtk_dialog_set_has_separator (dialog, FALSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (dialog->vbox), 14); /* 2 * 5 + 14 = 24 */ + gtk_box_set_spacing (GTK_BOX (dialog->action_area), 5); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + gtk_container_add (GTK_CONTAINER (dialog->vbox), hbox); + + image = gtk_image_new_from_stock (GTK_STOCK_PRINT, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + + mTitleLabel = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (mTitleLabel), TRUE); + gtk_misc_set_alignment (GTK_MISC (mTitleLabel), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (vbox), mTitleLabel, FALSE, FALSE, 0); + + mProgressBar = gtk_progress_bar_new (); + gtk_box_pack_start (GTK_BOX (vbox), mProgressBar, FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + gtk_window_present (GTK_WINDOW (dialog)); + + *_notifyOnOpen = PR_FALSE; + + return NS_OK; +} + +/* void closeProgressDialog (in boolean forceClose); */ +NS_IMETHODIMP +GeckoPrintSession::CloseProgressDialog (PRBool forceClose) +{ + return NS_OK; +} + +/* void registerListener (in nsIWebProgressListener listener); */ +NS_IMETHODIMP +GeckoPrintSession::RegisterListener (nsIWebProgressListener *listener) +{ + return NS_OK; +} + +/* void unregisterListener (in nsIWebProgressListener listener); */ +NS_IMETHODIMP +GeckoPrintSession::UnregisterListener (nsIWebProgressListener *listener) +{ + return NS_OK; +} + +/* void doneIniting (); */ +NS_IMETHODIMP +GeckoPrintSession::DoneIniting() +{ + return NS_OK; +} + +/* nsIPrompt getPrompter (); */ +NS_IMETHODIMP +GeckoPrintSession::GetPrompter (nsIPrompt **_retval) +{ + g_return_val_if_reached (NS_ERROR_NOT_IMPLEMENTED); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute boolean processCanceledByUser; */ +NS_IMETHODIMP +GeckoPrintSession::GetProcessCanceledByUser (PRBool *aProcessCanceledByUser) +{ + *aProcessCanceledByUser = mCancelled; + return NS_OK; +} + +NS_IMETHODIMP +GeckoPrintSession::SetProcessCanceledByUser (PRBool aProcessCanceledByUser) +{ + mCancelled = aProcessCanceledByUser; + if (mPrintSettings) { + mPrintSettings->SetIsCancelled (aProcessCanceledByUser); + } + + return NS_OK; +} + +/* nsIPrintProgressParams implementation */ + +/* attribute wstring docTitle; */ +NS_IMETHODIMP +GeckoPrintSession::GetDocTitle (PRUnichar * *aDocTitle) +{ + g_return_val_if_reached (NS_ERROR_NOT_IMPLEMENTED); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +GeckoPrintSession::SetDocTitle (const PRUnichar * aDocTitle) +{ + NS_ENSURE_STATE (mProgressDialog); + + char *converted = EphyUtils::ConvertUTF16toUTF8 (aDocTitle, MAX_STRING_LENGTH); + if (converted) { + mTitle.Assign (converted); + + char *title = g_strdup_printf (_("Printing “%s”"), converted); + gtk_window_set_title (GTK_WINDOW (mProgressDialog), title); + gtk_label_set_text (GTK_LABEL (mTitleLabel), title); + g_free (converted); + g_free (title); + } + return NS_OK; +} + +/* attribute wstring docURL; */ +NS_IMETHODIMP +GeckoPrintSession::GetDocURL (PRUnichar * *aDocURL) +{ + g_return_val_if_reached (NS_ERROR_NOT_IMPLEMENTED); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +GeckoPrintSession::SetDocURL (const PRUnichar * aDocURL) +{ +#if 0 + NS_ENSURE_STATE (mJob); + + char *converted = EphyUtils::ConvertUTF16toUTF8 (aDocTitle, MAX_STRING_LENGTH); + if (converted) { + g_free (converted); + } +#endif + return NS_OK; +} diff --git a/embed/xulrunner/components/GeckoPrintSession.h b/embed/xulrunner/components/GeckoPrintSession.h new file mode 100644 index 000000000..027f01e23 --- /dev/null +++ b/embed/xulrunner/components/GeckoPrintSession.h @@ -0,0 +1,94 @@ +/* + * Copyright © 2006 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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$ + */ + +#ifndef GECKO_PRINT_SESSION_H +#define GECKO_PRINT_SESSION_H + +#include <nsCOMPtr.h> +#include <nsIPrintSession.h> +#include <nsIWebProgressListener.h> +#include <nsIPrintProgress.h> +#include <nsIPrintProgressParams.h> +#include <nsWeakReference.h> + +#include <gtk/gtkwidget.h> +#include <gtk/gtkprintjob.h> +#include <gtk/gtkprinter.h> +#include <gtk/gtkprintjob.h> + +class nsIPrintSettings; +class nsIDOMWindow; + +/* 0940c973-97e7-476f-a612-4ed9473a0b36 */ +#define GECKO_PRINT_SESSION_IID \ +{ 0x0940c973, 0x97e7, 0x476f, \ + { 0xa6, 0x12, 0x4e, 0xd9, 0x47, 0x3a, 0x0b, 0x36 } } + +#define GECKO_PRINT_SESSION_CLASSNAME "Gecko Print Session" + +class GeckoPrintSession : public nsIPrintSession, + public nsIPrintProgress, + public nsIPrintProgressParams, + public nsSupportsWeakReference +{ + public: + GeckoPrintSession(); + virtual ~GeckoPrintSession(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIPRINTSESSION + NS_DECL_NSIWEBPROGRESSLISTENER + NS_DECL_NSIPRINTPROGRESS + NS_DECL_NSIPRINTPROGRESSPARAMS + + nsresult SetSettings (nsIPrintSettings *, GtkPrintSettings*, GtkPageSetup*, GtkPrinter*); + void GetSourceFile (nsACString&); + + static GeckoPrintSession *FromSettings (nsIPrintSettings *); + + void Cancel (); + void StartPrinting (); + void JobStatusChanged (); + void JobDone (); + void JobError (const char *); + + private: + nsCOMPtr<nsIPrintSettings> mPrintSettings; + GtkPrintSettings *mSettings; + GtkPageSetup *mPageSetup; + GtkPrinter *mPrinter; + GtkPrintJob *mJob; + GtkWidget *mProgressDialog; + GtkWidget *mTitleLabel; + GtkWidget *mProgressBar; + nsCString mSourceFile; + nsCString mTitle; + guint mStartPrintIdleID; + PRPackedBool mSourceFileIsTemp; + PRPackedBool mDone; + PRPackedBool mCancelled; + + void SetProgress (PRInt32, PRInt32); + void SetProgressText (const char *); + void LaunchJobOnIdle (); + void DestroyJob (); +}; + +#endif /* GECKO_PRINT_SESSION_H */ diff --git a/embed/xulrunner/components/GeckoSpellCheckEngine.cpp b/embed/xulrunner/components/GeckoSpellCheckEngine.cpp new file mode 100644 index 000000000..0b1257b41 --- /dev/null +++ b/embed/xulrunner/components/GeckoSpellCheckEngine.cpp @@ -0,0 +1,200 @@ +/* + * Copyright © 2006 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 <stdio.h> +#include <stdlib.h> + +#include <nsStringGlue.h> + +#include <mozIPersonalDictionary.h> +#include <nsMemory.h> + +#include "ephy-debug.h" + +#include "GeckoSpellCheckEngine.h" + +GeckoSpellCheckEngine::GeckoSpellCheckEngine () +{ + LOG ("GeckoSpellCheckEngine ctor [%p]", (void*) this); + mSpeller = ephy_spell_check_get_default (); +} + +GeckoSpellCheckEngine::~GeckoSpellCheckEngine () +{ + LOG ("GeckoSpellCheckEngine dtor [%p]", (void*) this); + g_object_unref (mSpeller); +} + +NS_IMPL_ISUPPORTS1 (GeckoSpellCheckEngine, + mozISpellCheckingEngine) + +/* nsISpellCheckEngine implementation */ + +/* attribute wstring dictionary; */ +NS_IMETHODIMP GeckoSpellCheckEngine::GetDictionary (PRUnichar * *aDictionary) +{ + /* Gets the identifier of the current dictionary */ + char *code = ephy_spell_check_get_language (mSpeller); + if (!code) { + return NS_ERROR_FAILURE; + } + + *aDictionary = ToNewUnicode (NS_ConvertUTF8toUTF16 (code)); + g_free (code); + + return NS_OK; +} + +NS_IMETHODIMP GeckoSpellCheckEngine::SetDictionary (const PRUnichar * aDictionary) +{ + return NS_OK; +} + +/* readonly attribute wstring language; */ +NS_IMETHODIMP GeckoSpellCheckEngine::GetLanguage (PRUnichar * *aLanguage) +{ + /* Gets the identifier of the current dictionary */ + char *code = ephy_spell_check_get_language (mSpeller); + if (!code) { + return NS_ERROR_FAILURE; + } + + *aLanguage = ToNewUnicode (NS_ConvertUTF8toUTF16 (code)); + g_free (code); + + return NS_OK; +} + +/* readonly attribute boolean providesPersonalDictionary; */ +NS_IMETHODIMP GeckoSpellCheckEngine::GetProvidesPersonalDictionary (PRBool *aProvidesPersonalDictionary) +{ + *aProvidesPersonalDictionary = PR_FALSE; + return NS_OK; +} + +/* readonly attribute boolean providesWordUtils; */ +NS_IMETHODIMP GeckoSpellCheckEngine::GetProvidesWordUtils (PRBool *aProvidesWordUtils) +{ + *aProvidesWordUtils = PR_FALSE; + return NS_OK; +} + +/* readonly attribute wstring name; */ +NS_IMETHODIMP GeckoSpellCheckEngine::GetName (PRUnichar * *aName) +{ + /* It's fine to leave this unimplemented */ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* readonly attribute wstring copyright; */ +NS_IMETHODIMP GeckoSpellCheckEngine::GetCopyright (PRUnichar * *aCopyright) +{ + /* It's fine to leave this unimplemented */ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute mozIPersonalDictionary personalDictionary; */ +NS_IMETHODIMP GeckoSpellCheckEngine::GetPersonalDictionary (mozIPersonalDictionary * *aPersonalDictionary) +{ + NS_IF_ADDREF (*aPersonalDictionary = mPersonalDictionary); + return NS_OK; +} + +NS_IMETHODIMP GeckoSpellCheckEngine::SetPersonalDictionary (mozIPersonalDictionary * aPersonalDictionary) +{ + mPersonalDictionary = aPersonalDictionary; + return NS_OK; +} + +/* void getDictionaryList ([array, size_is (count)] out wstring dictionaries, out PRUint32 count); */ +NS_IMETHODIMP +GeckoSpellCheckEngine::GetDictionaryList (PRUnichar ***_dictionaries, + PRUint32 *_count) +{ + *_count = 1; + *_dictionaries = (PRUnichar **)nsMemory::Alloc(sizeof(PRUnichar *)); // only one entry + *_dictionaries[0] = ToNewUnicode (NS_LITERAL_STRING ("en")); + return NS_OK; +} + +/* boolean check (in wstring word); */ +NS_IMETHODIMP GeckoSpellCheckEngine::Check (const PRUnichar *word, + PRBool *_retval) +{ + NS_ENSURE_STATE (mSpeller); + NS_ENSURE_ARG (word); + + NS_ConvertUTF16toUTF8 converted (word); + + gboolean correct = FALSE; + if (!ephy_spell_check_check_word (mSpeller, + converted.get (), + converted.Length (), + &correct)) + return NS_ERROR_FAILURE; + + *_retval = correct != FALSE; + + return NS_OK; +} + +/* void suggest (in wstring word, [array, size_is (count)] out wstring suggestions, out PRUint32 count); */ +NS_IMETHODIMP GeckoSpellCheckEngine::Suggest (const PRUnichar *word, + PRUnichar ***_suggestions, + PRUint32 *_count) +{ +#if 0 + NS_ENSURE_STATE (mSpeller); + NS_ENSURE_ARG (word); + + NS_ConvertUTF16toUTF8 converted (word); + + gsize count; + char **suggestions = ephy_spell_check_get_suggestions (mSpeller, + converted.get (), + converted.Length (), + &count); + + *_count = count; + *_suggestions = nsnull; + + PRUnichar **array = nsnull; + if (count > 0) { + NS_ASSERTION (suggestions, "Count > 0 but suggestions are NULL?"); + array = (PRUnichar **) nsMemory::Alloc (count * sizeof (PRUnichar *)); + if (array) { + *_suggestions = array; + + for (gsize i = 0; i < count; ++i) { + NS_ConvertUTF8toUTF16 sugg (suggestions[i]); + array[i] = ToNewUnicode (sugg); + } + } + + ephy_spell_check_free_suggestions (mSpeller, suggestions); + } + + return array ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +#endif + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/embed/xulrunner/components/GeckoSpellCheckEngine.h b/embed/xulrunner/components/GeckoSpellCheckEngine.h new file mode 100644 index 000000000..b70f6c970 --- /dev/null +++ b/embed/xulrunner/components/GeckoSpellCheckEngine.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2006 Christian Persch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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$ + */ + +#ifndef GECKO_SPELL_CHECK_ENGINE_H +#define GECKO_SPELL_CHECK_ENGINE_H + +#include <nsCOMPtr.h> +#include <mozISpellCheckingEngine.h> + +#include "ephy-spell-check.h" + +class mozIPersonalDictionary; + +/* 26948b8b-d136-4a78-a9c5-3a145812b649 */ +#define GECKO_SPELL_CHECK_ENGINE_IID \ +{ 0x26948b8b, 0xd136, 0x4a78, { 0xa9, 0xc5, 0x3a, 0x14, 0x58, 0x12, 0xb6, 0x49 } } + +#define GECKO_SPELL_CHECK_ENGINE_CONTRACTID "@mozilla.org/spellchecker/myspell;1" +#define GECKO_SPELL_CHECK_ENGINE_CLASSNAME "Gecko Print Settings" + +class GeckoSpellCheckEngine : public mozISpellCheckingEngine +{ + public: + GeckoSpellCheckEngine(); + virtual ~GeckoSpellCheckEngine(); + + NS_DECL_ISUPPORTS + NS_DECL_MOZISPELLCHECKINGENGINE + + private: + nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary; + EphySpellCheck *mSpeller; +}; + +#endif /* GECKO_SPELL_CHECK_ENGINE_H */ diff --git a/embed/xulrunner/components/GlobalHistory.cpp b/embed/xulrunner/components/GlobalHistory.cpp new file mode 100644 index 000000000..a2fd79960 --- /dev/null +++ b/embed/xulrunner/components/GlobalHistory.cpp @@ -0,0 +1,221 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright © 2001, 2004 Philip Langdale + * 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 <nsStringGlue.h> + +#include <nsIURI.h> + +#include "ephy-embed-shell.h" + +#include "GlobalHistory.h" + + +#define MAX_TITLE_LENGTH 2048 +#define MAX_URL_LENGTH 16384 + +#ifdef HAVE_NSIGLOBALHISTORY3_H +NS_IMPL_ISUPPORTS2 (MozGlobalHistory, nsIGlobalHistory2, nsIGlobalHistory3) +#else +NS_IMPL_ISUPPORTS1 (MozGlobalHistory, nsIGlobalHistory2) +#endif /* HAVE_NSIGLOBALHISTORY3_H */ + +MozGlobalHistory::MozGlobalHistory () +{ + mGlobalHistory = EPHY_HISTORY (ephy_embed_shell_get_global_history (embed_shell)); + + mHistoryListener = new EphyHistoryListener (); + mHistoryListener->Init (mGlobalHistory); +} + +MozGlobalHistory::~MozGlobalHistory () +{ +} + +/* void addURI (in nsIURI aURI, in boolean aRedirect, in boolean aToplevel, in nsIURI aReferrer); */ +NS_IMETHODIMP MozGlobalHistory::AddURI(nsIURI *aURI, + PRBool aRedirect, + PRBool aToplevel, + nsIURI *aReferrer) +{ + nsresult rv; + + NS_ENSURE_ARG (aURI); + + // filter out unwanted URIs such as chrome: etc + // The model is really if we don't know differently then add which basically + // means we are suppose to try all the things we know not to allow in and + // then if we don't bail go on and allow it in. But here lets compare + // against the most common case we know to allow in and go on and say yes + // to it. + + PRBool isHTTP = PR_FALSE, isHTTPS = PR_FALSE; + rv = aURI->SchemeIs("http", &isHTTP); + rv |= aURI->SchemeIs("https", &isHTTPS); + NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE); + + if (!isHTTP && !isHTTPS) + { + static const char *schemes[] = { "javascript", + "data", + "about", + "chrome", + "resource", + "view-source" }; + + for (PRUint32 i = 0; i < G_N_ELEMENTS (schemes); ++i) + { + PRBool result = PR_FALSE; + if (NS_SUCCEEDED (aURI->SchemeIs (schemes[i], &result)) && result) + { + return NS_OK; + } + } + } + + nsCString spec; + rv = aURI->GetSpec(spec); + NS_ENSURE_TRUE (NS_SUCCEEDED(rv) && spec.Length(), rv); + + if (spec.Length () > MAX_URL_LENGTH) return NS_OK; + + ephy_history_add_page (mGlobalHistory, spec.get(), aRedirect, aToplevel); + + return NS_OK; +} + +/* boolean isVisited (in nsIURI aURI); */ +NS_IMETHODIMP MozGlobalHistory::IsVisited(nsIURI *aURI, + PRBool *_retval) +{ + NS_ENSURE_ARG (aURI); + + *_retval = PR_FALSE; + + nsCString spec; + aURI->GetSpec(spec); + + if (spec.Length () > MAX_URL_LENGTH) return NS_OK; + + *_retval = ephy_history_is_page_visited (mGlobalHistory, spec.get()); + + return NS_OK; +} + +/* void setPageTitle (in nsIURI aURI, in AString aTitle); */ +NS_IMETHODIMP MozGlobalHistory::SetPageTitle(nsIURI *aURI, + const nsAString & aTitle) +{ + NS_ENSURE_ARG (aURI); + + nsCString spec; + aURI->GetSpec(spec); + + if (spec.Length () > MAX_URL_LENGTH) return NS_OK; + + nsString uTitle (aTitle); + + /* This depends on the assumption that + * typeof(PRUnichar) == typeof (gunichar2) == uint16, + * which should be pretty safe. + */ + glong n_read = 0, n_written = 0; + char *converted = g_utf16_to_utf8 ((gunichar2*) uTitle.get(), MAX_TITLE_LENGTH, + &n_read, &n_written, NULL); + /* FIXME loop from the end while !g_unichar_isspace (char)? */ + if (converted == NULL) return NS_OK; + + ephy_history_set_page_title (mGlobalHistory, spec.get(), converted); + + g_free (converted); + + return NS_OK; +} + +#ifdef HAVE_NSIGLOBALHISTORY3_H + +/* unsigned long getURIGeckoFlags(in nsIURI aURI); */ +NS_IMETHODIMP +MozGlobalHistory::GetURIGeckoFlags(nsIURI *aURI, + PRUint32* aFlags) +{ + *aFlags = 0; + + nsCString spec; + aURI->GetSpec(spec); + + if (spec.Length () > MAX_URL_LENGTH) return NS_OK; + + EphyNode *page = ephy_history_get_page (mGlobalHistory, spec.get()); + + GValue value = { 0, }; + if (page != NULL && + ephy_node_get_property (page, EPHY_NODE_PAGE_PROP_GECKO_FLAGS, &value)) + { + *aFlags = (PRUint32) (gulong) g_value_get_long (&value); + g_value_unset (&value); + + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + +/* void setURIGeckoFlags(in nsIURI aURI, in unsigned long aFlags); */ +NS_IMETHODIMP +MozGlobalHistory::SetURIGeckoFlags(nsIURI *aURI, + PRUint32 aFlags) +{ + nsCString spec; + aURI->GetSpec(spec); + + if (spec.Length () > MAX_URL_LENGTH) return NS_OK; + + EphyNode *page = ephy_history_get_page (mGlobalHistory, spec.get()); + if (page != NULL) + { + ephy_node_set_property_long (page, + EPHY_NODE_PAGE_PROP_GECKO_FLAGS, + aFlags); + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + +/* void addDocumentRedirect (in nsIChannel + aOldChannel, + in nsIChannel aNewChannel, + in PRInt32 aFlags, + in boolean aTopLevel); */ +NS_IMETHODIMP +MozGlobalHistory::AddDocumentRedirect(nsIChannel *aOldChannel, + nsIChannel *aNewChannel, + PRInt32 aFlags, + PRBool aTopLevel) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +#endif /* HAVE_NSIGLOBALHISTORY3_H */ diff --git a/embed/xulrunner/components/GlobalHistory.h b/embed/xulrunner/components/GlobalHistory.h new file mode 100644 index 000000000..8397874a0 --- /dev/null +++ b/embed/xulrunner/components/GlobalHistory.h @@ -0,0 +1,68 @@ +/* + * Copyright © 2001, 2004 Philip Langdale + * 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$ + */ + +#ifndef EPHY_GLOBAL_HISTORY_H +#define EPHY_GLOBAL_HISTORY_H + +#ifdef HAVE_NSIGLOBALHISTORY3_H +#include <nsIGlobalHistory3.h> +#else +#include <nsIGlobalHistory2.h> +#endif /* HAVE_NSIGLOBALHISTORY3_H */ + +#include <nsAutoPtr.h> +#include <nsCOMPtr.h> + +#include "ephy-history.h" + +#include "EphyHistoryListener.h" + +#define EPHY_GLOBALHISTORY_CLASSNAME "Epiphany Global History Implementation" + +#define EPHY_GLOBALHISTORY_CID \ +{ 0xbe0c42c1, \ + 0x39d4, \ + 0x4271, \ + { 0xb7, 0x9e, 0xf7, 0xaa, 0x49, 0xeb, 0x6a, 0x15} \ +} + +#ifdef HAVE_NSIGLOBALHISTORY3_H +class MozGlobalHistory: public nsIGlobalHistory3 +#else +class MozGlobalHistory: public nsIGlobalHistory2 +#endif /* HAVE_NSIGLOBALHISTORY3_H */ +{ + public: + MozGlobalHistory (); + virtual ~MozGlobalHistory(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIGLOBALHISTORY2 +#ifdef HAVE_NSIGLOBALHISTORY3_H + NS_DECL_NSIGLOBALHISTORY3 +#endif /* HAVE_NSIGLOBALHISTORY3_H */ + + private: + EphyHistory *mGlobalHistory; + nsRefPtr<EphyHistoryListener> mHistoryListener; +}; + +#endif /* EPHY_GLOBAL_HISTORY_H */ diff --git a/embed/xulrunner/components/GtkNSSClientAuthDialogs.cpp b/embed/xulrunner/components/GtkNSSClientAuthDialogs.cpp new file mode 100644 index 000000000..12b74573f --- /dev/null +++ b/embed/xulrunner/components/GtkNSSClientAuthDialogs.cpp @@ -0,0 +1,284 @@ +/* + * GtkNSSClientAuthDialogs.cpp + * + * Copyright © 2003 Crispin Flowerday <gnome@flowerday.cx> + * + * 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/gtkcelllayout.h> +#include <gtk/gtkcellrenderer.h> +#include <gtk/gtkcellrenderertext.h> +#include <gtk/gtkcombobox.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtkexpander.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtkimage.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkliststore.h> +#include <gtk/gtkmenuitem.h> +#include <gtk/gtkoptionmenu.h> +#include <gtk/gtkprogressbar.h> +#include <gtk/gtkscrolledwindow.h> +#include <gtk/gtkstock.h> +#include <gtk/gtktextbuffer.h> +#include <gtk/gtktextview.h> +#include <gtk/gtktogglebutton.h> +#include <gtk/gtktreemodel.h> +#include <gtk/gtkvbox.h> + +#include <nsStringGlue.h> + +#include <nsIDOMWindow.h> +#include <nsIInterfaceRequestor.h> +#include <nsIInterfaceRequestorUtils.h> +#include <nsIServiceManager.h> + +#include "ephy-debug.h" +#include "ephy-gui.h" +#include "ephy-state.h" +#include "ephy-stock-icons.h" + +#include "AutoJSContextStack.h" +#include "AutoWindowModalState.h" +#include "EphyUtils.h" + +#include "GtkNSSClientAuthDialogs.h" + +GtkNSSClientAuthDialogs::GtkNSSClientAuthDialogs() +{ + LOG ("GtkNSSClientAuthDialogs ctor (%p)", this); +} + + +GtkNSSClientAuthDialogs::~GtkNSSClientAuthDialogs() +{ + LOG ("GtkNSSClientAuthDialogs dtor (%p)", this); +} + +NS_IMPL_THREADSAFE_ISUPPORTS1 (GtkNSSClientAuthDialogs, + nsIClientAuthDialogs) + +/** + * Indent a widget according the HIG + * + * @returns: The new indented widget + */ +static GtkWidget* +higgy_indent_widget (GtkWidget *widget) +{ + GtkWidget *hbox; + GtkWidget *label; + + hbox = gtk_hbox_new (FALSE, 6); + + label = gtk_label_new (""); + gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, TRUE, 6); + gtk_widget_show (label); + + gtk_box_pack_start (GTK_BOX(hbox), widget, TRUE, TRUE, 0); + + return hbox; +} + +static void +combo_changed_cb (GtkComboBox *combo, GtkTextView *textview) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTextBuffer *buffer; + int index; + + model = gtk_combo_box_get_model (combo); + index = gtk_combo_box_get_active (combo); + buffer = gtk_text_view_get_buffer (textview); + + if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index)) + { + char *text; + + gtk_tree_model_get (model, &iter, 1, &text, -1); + + gtk_text_buffer_set_text (buffer, text, -1); + + g_free (text); + } + else + { + gtk_text_buffer_set_text (buffer, "", -1); + } +} + +NS_IMETHODIMP +GtkNSSClientAuthDialogs::ChooseCertificate (nsIInterfaceRequestor *ctx, + const PRUnichar *cn, + const PRUnichar *organization, + const PRUnichar *issuer, + const PRUnichar **certNickList, + const PRUnichar **certDetailsList, + PRUint32 count, PRInt32 *selectedIndex, + PRBool *canceled) +{ + GtkWidget *dialog, *label, *vbox, *textview; + GtkWidget *details, *expander, *hbox, *image; + GtkWidget *combo; + GtkListStore *store; + GtkTreeIter iter; + GtkCellRenderer *renderer; + char *msg, *markup_text; + PRUint32 i; + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (ctx)); + GtkWindow *gparent = GTK_WINDOW (EphyUtils::FindGtkParent (parent)); + + AutoWindowModalState modalState (parent); + + dialog = gtk_dialog_new_with_buttons ("", + GTK_WINDOW (gparent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + _("_Select Certificate"), + GTK_RESPONSE_OK, + (char *) NULL); + + if (gparent) + { + gtk_window_group_add_window (ephy_gui_ensure_window_group (gparent), + GTK_WINDOW (dialog)); + } + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX(GTK_DIALOG (dialog)->vbox), 14); /* 24 = 2 * 5 + 14 */ + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, + GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + gtk_widget_show (image); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + label = gtk_label_new (NULL); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + 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, 0.0); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + msg = g_markup_printf_escaped (_("Choose a certificate to present as identification to “%s”."), + NS_ConvertUTF16toUTF8 (cn).get()); + markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s", + _("Select a certificate to identify yourself."), + msg); + gtk_label_set_markup (GTK_LABEL (label), markup_text); + g_free (msg); + g_free (markup_text); + + /* Create and populate the combo */ + store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + for (i = 0; i < count; i++) + { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, NS_ConvertUTF16toUTF8 (certNickList[i]).get(), + 1, NS_ConvertUTF16toUTF8 (certDetailsList[i]).get(), + -1); + } + + combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + g_object_unref (store); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, + "text", 0, + (char *) NULL); + + gtk_widget_show (combo); + gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, TRUE, 0); + + expander = gtk_expander_new_with_mnemonic (_("Certificate _Details")); + ephy_state_add_expander (GTK_WIDGET (expander), "client-auth-dialog-expander", FALSE); + + gtk_widget_show (expander); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), expander, FALSE, FALSE, 0); + + /* Create the text box */ + textview = gtk_text_view_new (); + gtk_text_view_set_editable (GTK_TEXT_VIEW (textview), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (textview), FALSE); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (textview), GTK_WRAP_WORD); + gtk_widget_set_size_request (GTK_WIDGET (textview), -1, 100); + gtk_widget_show (textview); + + details = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (details), GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (details), GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER (details), textview); + gtk_widget_show (details); + + details = higgy_indent_widget (details); + gtk_container_set_border_width (GTK_CONTAINER (details), 5); + gtk_widget_show (details); + + gtk_container_add (GTK_CONTAINER (expander), details); + + g_signal_connect (G_OBJECT (combo), "changed", + G_CALLBACK (combo_changed_cb), + textview); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); + + /* run the dialog */ + int res = gtk_dialog_run (GTK_DIALOG (dialog)); + if (res == GTK_RESPONSE_OK) + { + *canceled = PR_FALSE; + *selectedIndex = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); + } + else + { + *canceled = PR_TRUE; + } + + gtk_widget_destroy (dialog); + return NS_OK; +} diff --git a/embed/xulrunner/components/GtkNSSClientAuthDialogs.h b/embed/xulrunner/components/GtkNSSClientAuthDialogs.h new file mode 100644 index 000000000..0aa582700 --- /dev/null +++ b/embed/xulrunner/components/GtkNSSClientAuthDialogs.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2003 Crispin Flowerday <gnome@flowerday.cx> + * + * 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$ + */ + +#ifndef GTKNSSCLIENTAUTHDIALOGS_H +#define GTKNSSCLIENTAUTHDIALOGS_H 1 + +#include <nsIClientAuthDialogs.h> + +// 55b3837e-dbde-4c24-9247-f328e3012485 +#define GTK_NSSCLIENTAUTHDIALOGS_CID \ + {0x55b3837e, 0xdbde, 0x4c24, {0x92, 0x47, 0xf3, 0x28, 0xe3, 0x01, 0x24, 0x85}} + +#define GTK_NSSCLIENTAUTHDIALOGS_CLASSNAME "Gtk NSS Client Auth Dialogs" + +class GtkNSSClientAuthDialogs +: public nsIClientAuthDialogs +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSICLIENTAUTHDIALOGS + + GtkNSSClientAuthDialogs(); + virtual ~GtkNSSClientAuthDialogs(); +}; + + +#endif /* GTKNSSCLIENTAUTHDIALOGS_H */ diff --git a/embed/xulrunner/components/GtkNSSDialogs.cpp b/embed/xulrunner/components/GtkNSSDialogs.cpp new file mode 100644 index 000000000..51581f8db --- /dev/null +++ b/embed/xulrunner/components/GtkNSSDialogs.cpp @@ -0,0 +1,1695 @@ +/* + * Copyright © 2003 Crispin Flowerday <gnome@flowerday.cx> + * Copyright © 2006 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$ + */ + +/* + * This file provides Gtk implementations of the mozilla Certificate dialogs + * such as the ones displayed when connecting to a site with a self-signed + * or expired certificate. + */ + +#include <xpcom-config.h> +#include "config.h" + +#include <time.h> + +#include <glib/gi18n.h> +#include <gtk/gtkalignment.h> +#include <gtk/gtkbutton.h> +#include <gtk/gtkcellrenderertext.h> +#include <gtk/gtkcheckbutton.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtkeditable.h> +#include <gtk/gtkentry.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtkimage.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkmessagedialog.h> +#include <gtk/gtkprogressbar.h> +#include <gtk/gtksizegroup.h> +#include <gtk/gtkstock.h> +#include <gtk/gtktable.h> +#include <gtk/gtktextbuffer.h> +#include <gtk/gtktextview.h> +#include <gtk/gtktogglebutton.h> +#include <gtk/gtktreeselection.h> +#include <gtk/gtktreestore.h> +#include <gtk/gtktreeview.h> +#include <gtk/gtkvbox.h> +#include <gtk/gtkcombobox.h> +#include <gconf/gconf-client.h> +#include <glade/glade-xml.h> + +#include <nsStringGlue.h> + +#include <nsCOMPtr.h> +#include <nsIArray.h> +#include <nsIASN1Object.h> +#include <nsIASN1Sequence.h> +#include <nsICRLInfo.h> +#include <nsIDOMWindow.h> +#include <nsIInterfaceRequestor.h> +#include <nsIInterfaceRequestorUtils.h> +#include <nsIMutableArray.h> +#include <nsIPKCS11ModuleDB.h> +#include <nsIPKCS11Slot.h> +#include <nsIPK11Token.h> +#include <nsIPK11TokenDB.h> +#include <nsIServiceManager.h> +#include <nsISimpleEnumerator.h> +#include <nsIX509CertDB.h> +#include <nsIX509Cert.h> +#include <nsIX509CertValidity.h> +#include <nsMemory.h> +#include <nsServiceManagerUtils.h> + +#ifdef HAVE_NSIMUTABLEARRAY_H +#include <nsIMutableArray.h> +#endif + +#include "ephy-file-helpers.h" +#include "ephy-gui.h" +#include "ephy-password-dialog.h" +#include "ephy-stock-icons.h" + +#include "AutoJSContextStack.h" +#include "AutoWindowModalState.h" +#include "EphyUtils.h" + +#include "GtkNSSDialogs.h" + +NS_DEFINE_CID (kX509CertCID, NS_IX509CERT_IID); +NS_DEFINE_CID (kASN1ObjectCID, NS_IASN1OBJECT_IID); + +enum +{ + NSSDIALOG_RESPONSE_VIEW_CERT = 10 +}; + +GtkNSSDialogs::GtkNSSDialogs () +{ +} + +GtkNSSDialogs::~GtkNSSDialogs () +{ +} + +NS_IMPL_THREADSAFE_ISUPPORTS5 (GtkNSSDialogs, + nsICertificateDialogs, + nsIBadCertListener, + nsITokenPasswordDialogs, + nsITokenDialogs, + nsIDOMCryptoDialogs) + +/* There's also nsICertPickDialogs which is implemented in mozilla + * but has no callers. So we don't implement it. + * Same for nsIUserCertPicker which is only used in mailnews. + */ + +/** + * Call the mozilla service to display a certificate + */ +static void +view_certificate (nsIInterfaceRequestor *ctx, nsIX509Cert *cert) +{ + nsresult rv; + nsCOMPtr<nsICertificateDialogs> certDialogs = + do_GetService (NS_CERTIFICATEDIALOGS_CONTRACTID, &rv); + NS_ENSURE_SUCCESS (rv, ); + + certDialogs->ViewCert (ctx, cert); +} + +/** + * Indent a widget according the HIG + * + * @returns: The new indented widget + */ +static GtkWidget* +higgy_indent_widget (GtkWidget *widget) +{ + GtkWidget *hbox; + GtkWidget *label; + + hbox = gtk_hbox_new (FALSE, 6); + + label = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, TRUE, 6); + gtk_widget_show (label); + + gtk_box_pack_start (GTK_BOX(hbox), widget, TRUE, TRUE, 0); + + return hbox; +} + +/** + * Setup up a dialog with the correct HIG'gy spacings, adding the content_widget + */ +static void +higgy_setup_dialog (GtkDialog *dialog, const gchar *stock_icon, + GtkWidget **content_label, + GtkWidget **content_vbox) +{ + GtkWidget *hbox, *label, *image, *vbox; + + g_return_if_fail (GTK_IS_DIALOG (dialog)); + g_return_if_fail (content_label); + + gtk_dialog_set_has_separator (dialog, FALSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); + + image = gtk_image_new_from_stock (stock_icon, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + + label = gtk_label_new (NULL); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + 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, 0.0); + + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + gtk_widget_show (image); + gtk_widget_show (vbox); + gtk_widget_show (hbox); + gtk_widget_show (label); + + /* Set up the spacing for the dialog internal widgets */ + gtk_box_set_spacing (GTK_BOX(dialog->vbox), 14); /* 24 = 2 * 5 + 14 */ + + *content_label = label; + if (content_vbox) + { + *content_vbox = vbox; + } +} + + +/** + * Display a dialog box, showing 'View Certificate', 'Cancel', + * and 'Accept' buttons. Optionally a checkbox can be shown, + * or the text can be NULL to avoid it being displayed + * + * @returns: GTK_RESPONSE_ACCEPT if the user clicked Accept + */ +static gint +display_cert_warning_box (nsIInterfaceRequestor *ctx, + nsIX509Cert *cert, + const char *markup_text, + const char *checkbox_text, + gboolean *checkbox_value, + const char *affirmative_text) +{ + GtkWidget *dialog, *label, *checkbox, *vbox, *button; + int res; + + g_return_val_if_fail (markup_text, GTK_RESPONSE_CANCEL); + g_return_val_if_fail (!checkbox_text || checkbox_value, GTK_RESPONSE_CANCEL); + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + /* NOTE: Due to a mozilla bug [https://bugzilla.mozilla.org/show_bug.cgi?id=306288], + * we will always end up without a parent! + */ + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (ctx)); + GtkWindow *gparent = GTK_WINDOW (EphyUtils::FindGtkParent (parent)); + + AutoWindowModalState modalState (parent); + + dialog = gtk_dialog_new_with_buttons ("", gparent, + GTK_DIALOG_DESTROY_WITH_PARENT, + (char *) NULL); + if (gparent) + { + gtk_window_group_add_window (ephy_gui_ensure_window_group (gparent), + GTK_WINDOW (dialog)); + } + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + higgy_setup_dialog (GTK_DIALOG (dialog), + GTK_STOCK_DIALOG_WARNING, &label, &vbox); + + /* Add the buttons */ + gtk_dialog_add_button (GTK_DIALOG (dialog), _("_View Certificate"), + NSSDIALOG_RESPONSE_VIEW_CERT); + + gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + + if (affirmative_text == NULL) + { + affirmative_text = _("_Accept"); + } + + button = gtk_dialog_add_button (GTK_DIALOG (dialog), + affirmative_text, + GTK_RESPONSE_ACCEPT); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + + if (checkbox_text) + { + checkbox = gtk_check_button_new_with_mnemonic (checkbox_text); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), + *checkbox_value); + + gtk_box_pack_start (GTK_BOX (vbox), checkbox, TRUE, TRUE, 0); + } + else + { + checkbox = 0; + } + + /* We don't want focus on the checkbox */ + gtk_widget_grab_focus (button); + + gtk_label_set_markup (GTK_LABEL (label), markup_text); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + gtk_widget_show_all (dialog); + + while (1) + { + res = gtk_dialog_run (GTK_DIALOG (dialog)); + if (res == NSSDIALOG_RESPONSE_VIEW_CERT) + { + view_certificate (ctx, cert); + continue; + } + + break; + } + + if (res == GTK_RESPONSE_ACCEPT && checkbox) + { + *checkbox_value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox)); + } + + gtk_widget_destroy (dialog); + return res; +} + + +/* Helper functions */ + +nsresult +GtkNSSDialogs::GetTokenAndSlotFromName (const PRUnichar *aName, + nsIPK11Token **aToken, + nsIPKCS11Slot **aSlot) +{ + nsresult rv = NS_ERROR_FAILURE; + *aToken = nsnull; + *aSlot = nsnull; + + nsCOMPtr<nsIPK11TokenDB> tokenDB = do_GetService("@mozilla.org/security/pk11tokendb;1"); + nsCOMPtr<nsIPKCS11ModuleDB> pkcs11DB = do_GetService("@mozilla.org/security/pkcs11moduledb;1"); + if (!tokenDB || !pkcs11DB) return rv; + + rv = tokenDB->FindTokenByName (aName, aToken); + NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && *aToken, rv); + + pkcs11DB->FindSlotByName (aName, aSlot); + + NS_ENSURE_TRUE (*aSlot, NS_ERROR_FAILURE); + +#ifdef GNOME_ENABLE_DEBUG + /* Dump some info about this token */ + nsIPK11Token *token = *aToken; + PRUnichar *tName, *tLabel, *tManID, *tHWVersion, *tFWVersion, *tSN; + PRInt32 minPwdLen; + PRBool needsInit, isHW, needsLogin, isFriendly; + + token->GetTokenName(&tName); + token->GetTokenLabel(&tLabel); + token->GetTokenManID(&tManID); + token->GetTokenHWVersion(&tHWVersion); + token->GetTokenFWVersion(&tFWVersion); + token->GetTokenSerialNumber(&tSN); + token->GetMinimumPasswordLength(&minPwdLen); + token->GetNeedsUserInit(&needsInit); + token->IsHardwareToken(&isHW); + token->NeedsLogin(&needsLogin); + token->IsFriendly(&isFriendly); + + g_print ("Token '%s' has \nName: %s\nLabel: %s\nManID: %s\nHWversion: %s\nFWVersion: %s\nSN: %s\n" + "MinPwdLen: %d\nNeedsUserInit: %d\nIsHWToken: %d\nNeedsLogin: %d\nIsFriendly: %d\n\n", + NS_ConvertUTF16toUTF8(aName).get(), + + NS_ConvertUTF16toUTF8(tName).get(), + NS_ConvertUTF16toUTF8(tLabel).get(), + NS_ConvertUTF16toUTF8(tManID).get(), + NS_ConvertUTF16toUTF8(tHWVersion).get(), + NS_ConvertUTF16toUTF8(tFWVersion).get(), + NS_ConvertUTF16toUTF8(tSN).get(), + minPwdLen, + needsInit, + isHW, + needsLogin, + isFriendly); + + nsIPKCS11Slot *slot = *aSlot; + PRUnichar*slDesc; + slot->GetDesc(&slDesc); + g_print ("Slot description: %s\n", NS_ConvertUTF16toUTF8 (slDesc).get()); +#endif + + return NS_OK; +} + +/* nsICertificateDialogs */ + +NS_IMETHODIMP +GtkNSSDialogs::ConfirmMismatchDomain (nsIInterfaceRequestor *ctx, + const nsACString &targetURL, + nsIX509Cert *cert, PRBool *_retval) +{ + char *first, *second, *msg; + int res; + + nsString commonName; + cert->GetCommonName (commonName); + + NS_ConvertUTF16toUTF8 cCommonName (commonName); + + nsCString cTargetUrl (targetURL); + + first = g_markup_printf_escaped (_("The site “%s” returned security information for " + "“%s”. It is possible that someone is intercepting " + "your communication to obtain your confidential " + "information."), + cTargetUrl.get(), cCommonName.get()); + + second = g_markup_printf_escaped (_("You should only accept the security information if you " + "trust “%s” and “%s”."), + cTargetUrl.get(), cCommonName.get()); + + msg = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n\n%s", + _("Accept incorrect security information?"), + first, second); + + res = display_cert_warning_box (ctx, cert, msg, NULL, NULL, NULL); + + g_free (second); + g_free (first); + g_free (msg); + + *_retval = (res == GTK_RESPONSE_ACCEPT); + return NS_OK; +} + + +NS_IMETHODIMP +GtkNSSDialogs::ConfirmUnknownIssuer (nsIInterfaceRequestor *ctx, + nsIX509Cert *cert, PRInt16 *outAddType, + PRBool *_retval) +{ + gboolean accept_perm = FALSE; + char *secondary, *tertiary, *msg; + int res; + + nsString commonName; + cert->GetCommonName (commonName); + + NS_ConvertUTF16toUTF8 cCommonName (commonName); + + secondary = g_markup_printf_escaped + (_("It was not possible to automatically trust “%s”. " + "It is possible that someone is intercepting your " + "communication to obtain your confidential information."), + cCommonName.get()); + + tertiary = g_markup_printf_escaped + (_("You should only connect to the site if you are certain " + "you are connected to “%s”."), + cCommonName.get()); + + msg = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n\n%s", + _("Connect to untrusted site?"), + secondary, tertiary); + + res = display_cert_warning_box (ctx, cert, msg, + _("_Trust this security information from now on"), + &accept_perm, _("Co_nnect")); + g_free (tertiary); + g_free (secondary); + g_free (msg); + + if (res != GTK_RESPONSE_ACCEPT) + { + *_retval = PR_FALSE; + *outAddType = UNINIT_ADD_FLAG; + } + else + { + if (accept_perm) + { + *_retval = PR_TRUE; + *outAddType = ADD_TRUSTED_PERMANENTLY; + } + else + { + *_retval = PR_TRUE; + *outAddType = ADD_TRUSTED_FOR_SESSION; + } + } + + return NS_OK; +} + + +/* boolean confirmCertExpired (in nsIInterfaceRequestor socketInfo, + in nsIX509Cert cert); */ +NS_IMETHODIMP +GtkNSSDialogs::ConfirmCertExpired (nsIInterfaceRequestor *ctx, + nsIX509Cert *cert, PRBool *_retval) +{ + nsresult rv; + PRTime now = PR_Now(); + PRTime notAfter, notBefore, timeToUse; + PRInt64 normalizedTime; + time_t t; + struct tm tm; + char formattedDate[128]; + char *fdate; + const char *primary, *text; + char *secondary, *msg; + + *_retval = PR_FALSE; + + nsCOMPtr<nsIX509CertValidity> validity; + rv = cert->GetValidity (getter_AddRefs(validity)); + if (NS_FAILED(rv)) return rv; + + rv = validity->GetNotAfter (¬After); + if (NS_FAILED(rv)) return rv; + + rv = validity->GetNotBefore (¬Before); + if (NS_FAILED(rv)) return rv; + + if (LL_CMP(now, >, notAfter)) + { + primary = _("Accept expired security information?"); + /* Translators: first %s is a hostname, second %s is a time/date */ + text = _("The security information for “%s” " + "expired on %s."); + timeToUse = notAfter; + } + else + { + primary = _("Accept not yet valid security information?"); + /* Translators: first %s is a hostname, second %s is a time/date */ + text = _("The security information for “%s” isn't valid until %s."); + timeToUse = notBefore; + } + + nsString commonName; + cert->GetCommonName (commonName); + + NS_ConvertUTF16toUTF8 cCommonName (commonName); + + LL_DIV (normalizedTime, timeToUse, PR_USEC_PER_SEC); + LL_L2UI (t, normalizedTime); + /* To translators: this a time format that is used while displaying the + * expiry or start date of an SSL certificate, for the format see + * strftime(3) */ + strftime (formattedDate, sizeof(formattedDate), _("%a %d %b %Y"), + localtime_r (&t, &tm)); + /* FIXME! this isn't actually correct, LC_CTIME codeset could be different than locale codeset! */ + fdate = g_locale_to_utf8 (formattedDate, -1, NULL, NULL, NULL); + + secondary = g_markup_printf_escaped (text, cCommonName.get(), fdate); + + msg = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n\n%s", + primary, secondary, + _("You should ensure that your computer's time is correct.")); + + int res = display_cert_warning_box (ctx, cert, msg, NULL, NULL, NULL); + + g_free (fdate); + g_free (msg); + g_free (secondary); + + *_retval = (res == GTK_RESPONSE_ACCEPT); + + return NS_OK; +} + +/* void notifyCrlNextupdate (in nsIInterfaceRequestor socketInfo, + in AUTF8String targetURL, + in nsIX509Cert cert); */ +NS_IMETHODIMP +GtkNSSDialogs::NotifyCrlNextupdate (nsIInterfaceRequestor *ctx, + const nsACString & targetURL, + nsIX509Cert *cert) +{ + nsCOMPtr<nsIDOMWindow> parent = do_GetInterface (ctx); + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + nsCString cTargetUrl (targetURL); + + nsString commonName; + cert->GetCommonName (commonName); + + GtkWidget *dialog = gtk_message_dialog_new + (GTK_WINDOW (gparent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot establish connection to “%s”"), + cTargetUrl.get ()); + + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (dialog), + _("The certificate revocation list (CRL) from “%s” " + "needs to be updated.\n\n" + "Please ask your system administrator for assistance."), + NS_ConvertUTF16toUTF8 (commonName).get ()); + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + g_signal_connect (dialog, "response", + (GCallback) gtk_widget_destroy, NULL); + + gtk_widget_show_all (dialog); + return NS_OK; +} + +NS_IMETHODIMP +GtkNSSDialogs::ConfirmDownloadCACert(nsIInterfaceRequestor *ctx, + nsIX509Cert *cert, + PRUint32 *_trust, + PRBool *_retval) +{ + GtkWidget *dialog, *label; + char *msg, *primary; + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (ctx)); + GtkWindow *gparent = GTK_WINDOW (EphyUtils::FindGtkParent (parent)); + + AutoWindowModalState modalState (parent); + + dialog = gtk_dialog_new_with_buttons (_("Trust new Certificate Authority?"), gparent, + GTK_DIALOG_DESTROY_WITH_PARENT, + _("_View Certificate"), + NSSDIALOG_RESPONSE_VIEW_CERT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("_Trust CA"), GTK_RESPONSE_ACCEPT, + (char *) NULL); + + if (gparent) + { + gtk_window_group_add_window (ephy_gui_ensure_window_group (gparent), + GTK_WINDOW (dialog)); + } + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + higgy_setup_dialog (GTK_DIALOG (dialog), GTK_STOCK_DIALOG_WARNING, + &label, NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + + nsString commonName; + cert->GetCommonName (commonName); + + NS_ConvertUTF16toUTF8 cCommonName (commonName); + + primary = g_markup_printf_escaped (_("Trust new Certificate Authority “%s” to identify web sites?"), + cCommonName.get()); + + msg = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s", + primary, + _("Before trusting a Certificate Authority (CA) you should " + "verify the certificate is authentic.")); + gtk_label_set_markup (GTK_LABEL (label), msg); + g_free (primary); + g_free (msg); + + gtk_widget_show_all (dialog); + int ret; + + while (1) + { + ret = gtk_dialog_run (GTK_DIALOG (dialog)); + if (ret == NSSDIALOG_RESPONSE_VIEW_CERT) + { + view_certificate (ctx, cert); + continue; + } + + break; + } + + if (ret != GTK_RESPONSE_ACCEPT) + { + *_retval = PR_FALSE; + } + else + { + if (ret == GTK_RESPONSE_ACCEPT) + { + *_trust |= nsIX509CertDB::TRUSTED_SSL; + } + else + { + *_trust = nsIX509CertDB::UNTRUSTED; + } + + *_retval = PR_TRUE; + } + gtk_widget_destroy (dialog); + + return NS_OK; +} + + +NS_IMETHODIMP +GtkNSSDialogs::NotifyCACertExists (nsIInterfaceRequestor *ctx) +{ + GtkWidget *dialog, *label; + char * msg; + + nsCOMPtr<nsIDOMWindow> parent = do_GetInterface (ctx); + GtkWindow *gparent = GTK_WINDOW (EphyUtils::FindGtkParent (parent)); + + dialog = gtk_dialog_new_with_buttons ("", gparent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, + GTK_RESPONSE_OK, + (char *) NULL); + + if (gparent) + { + gtk_window_group_add_window (ephy_gui_ensure_window_group (gparent), + GTK_WINDOW (dialog)); + } + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + higgy_setup_dialog (GTK_DIALOG (dialog), GTK_STOCK_DIALOG_ERROR, + &label, NULL); + + msg = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s", + _("Certificate already exists."), + _("The certificate has already been imported.")); + gtk_label_set_markup (GTK_LABEL (label), msg); + g_free (msg); + + g_signal_connect (G_OBJECT (dialog), + "response", + (GCallback)gtk_widget_destroy, NULL); + + gtk_widget_show_all (dialog); + return NS_OK; +} + +/* FIXME: This interface sucks! There is way to know the name of the certificate! */ +NS_IMETHODIMP +GtkNSSDialogs::SetPKCS12FilePassword(nsIInterfaceRequestor *ctx, + nsAString &_password, + PRBool *_retval) +{ + GtkWidget *dialog; + char *msg; + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (ctx)); + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + AutoWindowModalState modalState (parent); + + dialog = ephy_password_dialog_new (gparent, + _("Select Password"), + EphyPasswordDialogFlags(EPHY_PASSWORD_DIALOG_FLAGS_SHOW_NEW_PASSWORD | + EPHY_PASSWORD_DIALOG_FLAGS_SHOW_QUALITY_METER)); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + + /* FIXME: set accept button text to (_("_Back Up Certificate") ? + * That's not actually correct, since this function is also called from other places! + */ + + msg = g_markup_printf_escaped (_("Select a password to protect this certificate")); + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), msg); + g_free (msg); + + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_hide (dialog); + + if (response == GTK_RESPONSE_ACCEPT) + { + const char *text = ephy_password_dialog_get_new_password (EPHY_PASSWORD_DIALOG (dialog)); + g_return_val_if_fail (text != NULL, NS_ERROR_FAILURE); + NS_CStringToUTF16 (nsDependentCString (text), + NS_CSTRING_ENCODING_UTF8, _password); + } + + *_retval = response == GTK_RESPONSE_ACCEPT; + + gtk_widget_destroy (dialog); + + return NS_OK; +} + +NS_IMETHODIMP +GtkNSSDialogs::GetPKCS12FilePassword(nsIInterfaceRequestor *ctx, + nsAString &_password, + PRBool *_retval) +{ + g_print ("GtkNSSDialogs::GetPKCS12FilePassword\n"); + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (ctx)); + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + AutoWindowModalState modalState (parent); + + GtkWidget *dialog = ephy_password_dialog_new + (gparent, + "", + EphyPasswordDialogFlags (EPHY_PASSWORD_DIALOG_FLAGS_SHOW_PASSWORD)); + EphyPasswordDialog *password_dialog = EPHY_PASSWORD_DIALOG (dialog); + /* FIXME: set accept button text to _("I_mport Certificate") ? */ + + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + + /* FIXME: mozilla sucks, no way to get the name of the certificate / cert file! */ + char *msg = g_markup_printf_escaped (_("Enter the password for this certificate")); + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), msg); + g_free (msg); + + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response == GTK_RESPONSE_ACCEPT) + { + const char *pwd = ephy_password_dialog_get_password (password_dialog); + NS_CStringToUTF16 (nsDependentCString (pwd), + NS_CSTRING_ENCODING_UTF8, _password); + } + + *_retval = response == GTK_RESPONSE_ACCEPT; + + gtk_widget_destroy (dialog); + + return NS_OK; +} + + +static void +set_table_row (GtkWidget *table, + int& row, + const char *title, + const char *text) +{ + GtkWidget *header, *label; + char *bold; + + if (text == NULL || text[0] == 0) return; + + bold = g_markup_printf_escaped ("<b>%s</b>", title); + header = gtk_label_new (bold); + g_free (bold); + + gtk_label_set_use_markup (GTK_LABEL (header), TRUE); + gtk_misc_set_alignment (GTK_MISC (header), 0, 0); + gtk_widget_show (header); + gtk_table_attach (GTK_TABLE (table), header, 0, 1, row, row+1, + GTK_FILL, GTK_FILL, 0, 0); + + label = gtk_label_new (text); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + gtk_label_set_selectable (GTK_LABEL (label), TRUE); + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); + gtk_label_set_max_width_chars (GTK_LABEL (label), 48); + gtk_widget_show (label); + gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2, row, row+1); + + row++; +} + +NS_IMETHODIMP +GtkNSSDialogs::CrlImportStatusDialog(nsIInterfaceRequestor *ctx, nsICRLInfo *crl) +{ + + GtkWidget *dialog, *label, *table, *vbox; + nsresult rv; + char *msg; + + nsCOMPtr<nsIDOMWindow> parent = do_GetInterface (ctx); + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + dialog = gtk_dialog_new_with_buttons ("", + GTK_WINDOW (gparent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_OK, + (char *) NULL); + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + gtk_window_set_title (GTK_WINDOW (dialog), _("Certificate Revocation List Imported")); + + /* Needed because gparent == NULL always because of mozilla sucks */ + gtk_window_set_skip_pager_hint (GTK_WINDOW (dialog), TRUE); + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE); + + higgy_setup_dialog (GTK_DIALOG (dialog), GTK_STOCK_DIALOG_INFO, + &label, &vbox); + + msg = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>", + _("Certificate Revocation List (CRL) successfully imported")); + gtk_label_set_markup (GTK_LABEL (label), msg); + g_free (msg); + + table = gtk_table_new (2, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_table_set_col_spacings (GTK_TABLE (table), 12); + + nsString org, orgUnit, nextUpdate; + rv = crl->GetOrganization (org); + if (NS_FAILED(rv)) return rv; + + rv = crl->GetOrganizationalUnit (orgUnit); + if (NS_FAILED(rv)) return rv; + + rv = crl->GetNextUpdateLocale (nextUpdate); + if (NS_FAILED(rv)) return rv; + + int row = 0; + set_table_row (table, row, _("Organization:"), NS_ConvertUTF16toUTF8 (org).get ()); + + set_table_row (table, row, _("Unit:"), NS_ConvertUTF16toUTF8 (orgUnit).get ()); + + set_table_row (table, row, _("Next Update:"), NS_ConvertUTF16toUTF8 (nextUpdate).get ()); + + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + gtk_widget_show_all (dialog); + g_signal_connect (G_OBJECT (dialog), + "response", + (GCallback)gtk_widget_destroy, NULL); + + gtk_widget_show_all (dialog); + return NS_OK; +} + +/** + * Help function to fill in the labels on the General tab + */ +static void +set_label_cert_attribute (GladeXML* gxml, const char* label_id, nsAString &value) +{ + GtkWidget *label; + label = glade_xml_get_widget (gxml, label_id); + + g_return_if_fail (GTK_IS_LABEL (label)); + + if (!value.Length()) { + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + char *msg = g_strdup_printf ("<i><%s></i>", + _("Not part of certificate")); + gtk_label_set_markup (GTK_LABEL (label), msg); + g_free (msg); + } + else + { + gtk_label_set_use_markup (GTK_LABEL (label), FALSE); + gtk_label_set_text (GTK_LABEL (label), NS_ConvertUTF16toUTF8 (value).get()); + } +} + + +/** + * Do that actual filling in of the certificate tree + */ +static gboolean +fill_cert_chain_tree (GtkTreeView *treeview, nsIArray *certChain) +{ + nsresult rv; + GtkTreeModel * model = gtk_tree_view_get_model (treeview); + + GtkTreeIter parent; + PRUint32 numCerts; + rv = certChain->GetLength (&numCerts); + if (NS_FAILED(rv) || numCerts < 1) return FALSE; + + for (int i = (int)numCerts-1 ; i >= 0; i--) + { + nsCOMPtr<nsIX509Cert> nsCert; + rv = certChain->QueryElementAt (i, kX509CertCID, + getter_AddRefs(nsCert)); + if (NS_FAILED(rv)) return FALSE; + + GtkTreeIter iter; + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, + (i == (int)numCerts-1) ? NULL : &parent); + + nsString value; + rv = nsCert->GetCommonName (value); + if (NS_FAILED(rv)) return FALSE; + + NS_ConvertUTF16toUTF8 cValue (value); + + nsIX509Cert *nsCertP = nsCert; + if (value.Length()) + { + gtk_tree_store_set (GTK_TREE_STORE(model), &iter, + 0, cValue.get(), + 1, nsCertP, + -1); + } + else + { + char * title; + rv = nsCert->GetWindowTitle (&title); + if (NS_FAILED(rv)) return FALSE; + + gtk_tree_store_set (GTK_TREE_STORE(model), + &iter, 0, title, 1, nsCertP, -1); + nsMemory::Free (title); + } + parent = iter; + } + gtk_tree_view_expand_all (GTK_TREE_VIEW (treeview)); + + /* And select the last entry, and scroll the view so it's visible */ + GtkTreeSelection *select = gtk_tree_view_get_selection (treeview); + GtkTreePath *path = gtk_tree_model_get_path (model, &parent); + gtk_tree_selection_select_path (select, path); + gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.0); + gtk_tree_path_free (path); + + return TRUE; +} + +/** + * Add an ASN object to the treeview, recursing if the object was a + * sequence + */ +static void +add_asn1_object_to_tree(GtkTreeModel *model, nsIASN1Object *object, GtkTreeIter *parent) +{ + nsString dispNameU; + object->GetDisplayName(dispNameU); + + GtkTreeIter iter; + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent); + + gtk_tree_store_set (GTK_TREE_STORE(model), &iter, + 0, NS_ConvertUTF16toUTF8 (dispNameU).get(), + 1, object, + -1); + + nsCOMPtr<nsIASN1Sequence> sequence(do_QueryInterface(object)); + if (!sequence) return; + + nsCOMPtr<nsIMutableArray> asn1Objects; + sequence->GetASN1Objects(getter_AddRefs(asn1Objects)); + + PRUint32 numObjects; + asn1Objects->GetLength(&numObjects); + if (!asn1Objects) return; + + for (PRUint32 i = 0; i < numObjects ; i++) + { + nsCOMPtr<nsIASN1Object> currObject; + asn1Objects->QueryElementAt (i, kASN1ObjectCID, + getter_AddRefs (currObject)); + add_asn1_object_to_tree (model, currObject, &iter); + } +} + + +/** + * Update the "Certificate Fields" treeview when a different cert + * is selected in the hierarchy text view + */ +static void +cert_chain_tree_view_selection_changed_cb (GtkTreeSelection *selection, + GtkWidget* tree_view) +{ + GtkTreeIter iter; + nsIX509Cert *nsCert; + nsresult rv; + GtkTreeModel * model; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, 1, &nsCert, -1); + + nsCOMPtr<nsIASN1Object> object; + rv = nsCert->GetASN1Structure (getter_AddRefs(object)); + if (NS_FAILED(rv)) return; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); + gtk_tree_store_clear (GTK_TREE_STORE (model)); + add_asn1_object_to_tree (model, object, NULL); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + } +} + +/** + * When the "Certificate Field" treeview is changed, update the + * text_view to display the value of the currently selected field + */ +static void +field_tree_view_selection_changed_cb (GtkTreeSelection *selection, + GtkWidget* text_view) +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkTextBuffer * text_buffer = + gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + nsIASN1Object *object; + + gtk_tree_model_get (model, &iter, 1, &object, -1); + + nsString dispValU; + object->GetDisplayValue(dispValU); + + gtk_text_buffer_set_text (text_buffer, NS_ConvertUTF16toUTF8 (dispValU).get(), -1); + } + else + { + gtk_text_buffer_set_text (text_buffer, "", 0); + } +} + +/** + * Setup the various treeviews, the textview, and fill the treeviews + */ +static gboolean +setup_view_cert_tree (GtkWidget *dialog, GladeXML*gxml, nsIArray *certChain) +{ + GtkCellRenderer *renderer; + GtkWidget *chain_tree_view, *field_tree_view, *text_view; + PangoFontDescription *monospace_font_desc; + GConfClient *conf_client; + char *monospace_font; + + chain_tree_view = glade_xml_get_widget (gxml, "treeview_cert_chain"); + field_tree_view = glade_xml_get_widget (gxml, "treeview_cert_info"); + text_view = glade_xml_get_widget (gxml, "textview_field_value"); + + /* Setup the certificate chain view */ + GtkTreeStore *store = gtk_tree_store_new (2, + G_TYPE_STRING, + G_TYPE_POINTER); + gtk_tree_view_set_model (GTK_TREE_VIEW (chain_tree_view), GTK_TREE_MODEL (store)); + g_object_unref (store); + + + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (chain_tree_view), + 0, "Certificate", + renderer, + "text", 0, + (char *) NULL); + + GtkTreeSelection *select = gtk_tree_view_get_selection (GTK_TREE_VIEW (chain_tree_view)); + gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE); + + g_signal_connect (G_OBJECT (select), "changed", + G_CALLBACK (cert_chain_tree_view_selection_changed_cb), + field_tree_view); + + /* Setup the certificate field view */ + store = gtk_tree_store_new (2, + G_TYPE_STRING, + G_TYPE_POINTER); + gtk_tree_view_set_model (GTK_TREE_VIEW (field_tree_view), GTK_TREE_MODEL (store)); + g_object_unref (store); + + + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (field_tree_view), + 0, "Certificate Field", + renderer, + "text", 0, + (char *) NULL); + + select = gtk_tree_view_get_selection (GTK_TREE_VIEW (field_tree_view)); + gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE); + + g_signal_connect (G_OBJECT (select), "changed", + G_CALLBACK (field_tree_view_selection_changed_cb), + text_view); + + /* Get the text_view displaying a propertional font + * + * Pick up the monospace font from desktop preferences */ + conf_client = gconf_client_get_default (); + monospace_font = gconf_client_get_string (conf_client, + "/desktop/gnome/interface/monospace_font_name", NULL); + if (monospace_font) + { + monospace_font_desc = pango_font_description_from_string (monospace_font); + gtk_widget_modify_font (text_view, monospace_font_desc); + pango_font_description_free (monospace_font_desc); + } + g_object_unref (conf_client); + + /* And fill the certificate chain tree */ + return fill_cert_chain_tree (GTK_TREE_VIEW (chain_tree_view), certChain); +} + +/* void viewCert (in nsIX509Cert cert); */ +NS_IMETHODIMP +GtkNSSDialogs::ViewCert(nsIInterfaceRequestor *ctx, + nsIX509Cert *cert) +{ + GtkWidget *dialog, *widget; + GladeXML *gxml; + nsString value; + PRUint32 verifystate, count; + PRUnichar ** usage; + GtkSizeGroup * sizegroup; + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + gxml = glade_xml_new (ephy_file ("certificate-dialogs.glade"), + "viewcert_dialog", NULL); + g_return_val_if_fail (gxml != NULL, NS_ERROR_FAILURE); + + dialog = glade_xml_get_widget (gxml, "viewcert_dialog"); + g_return_val_if_fail (dialog != NULL, NS_ERROR_FAILURE); + + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (ctx)); + GtkWindow *gparent = GTK_WINDOW (EphyUtils::FindGtkParent (parent)); + + AutoWindowModalState modalState (parent); + + if (gparent) + { + gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(gparent)); + gtk_window_group_add_window (ephy_gui_ensure_window_group (GTK_WINDOW (gparent)), + GTK_WINDOW (dialog)); + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + } + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + gtk_window_set_title (GTK_WINDOW (dialog), _("Certificate Properties")); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); + + /* Set up the GtkSizeGroup so that the columns line up */ + sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + widget = glade_xml_get_widget (gxml, "label_size1"); + gtk_size_group_add_widget (sizegroup, widget); + widget = glade_xml_get_widget (gxml, "label_size2"); + gtk_size_group_add_widget (sizegroup, widget); + widget = glade_xml_get_widget (gxml, "label_size3"); + gtk_size_group_add_widget (sizegroup, widget); + widget = glade_xml_get_widget (gxml, "label_size4"); + gtk_size_group_add_widget (sizegroup, widget); + g_object_unref (sizegroup); + + rv = cert->GetUsagesArray (FALSE, &verifystate, &count, &usage); + if (NS_FAILED(rv)) return rv; + + const char * text; + switch (verifystate) + { + case nsIX509Cert::VERIFIED_OK: + text = _("This certificate has been verified for the following uses:"); + break; + case nsIX509Cert::CERT_REVOKED: + text = _("Could not verify this certificate because it has been revoked."); + break; + case nsIX509Cert::CERT_EXPIRED: + text = _("Could not verify this certificate because it has expired."); + break; + case nsIX509Cert::CERT_NOT_TRUSTED: + text = _("Could not verify this certificate because it is not trusted."); + break; + case nsIX509Cert::ISSUER_NOT_TRUSTED: + text = _("Could not verify this certificate because the issuer is not trusted."); + break; + case nsIX509Cert::ISSUER_UNKNOWN: + text = _("Could not verify this certificate because the issuer is unknown."); + break; + case nsIX509Cert::INVALID_CA: + text = _("Could not verify this certificate because the CA certificate is invalid."); + break; + case nsIX509Cert::NOT_VERIFIED_UNKNOWN: + case nsIX509Cert::USAGE_NOT_ALLOWED: + default: + text = _("Could not verify this certificate for unknown reasons."); + } + + char *vmsg = g_strdup_printf ("<b>%s</b>", text); + widget = glade_xml_get_widget (gxml, "label_verify_text"); + g_return_val_if_fail (GTK_IS_LABEL (widget), NS_ERROR_FAILURE); + gtk_label_set_markup (GTK_LABEL (widget), vmsg); + g_free (vmsg); + + if (count > 0) + { + GtkWidget *vbox = gtk_vbox_new (FALSE, 3); + GtkWidget *indent; + for (PRUint32 i = 0 ; i < count ; i++) + { + GtkWidget *label = gtk_label_new (NS_ConvertUTF16toUTF8 (usage[i]).get()); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + nsMemory::Free (usage[i]); + } + nsMemory::Free (usage); + indent = higgy_indent_widget (vbox); + widget = glade_xml_get_widget (gxml, "vbox_validity"); + g_return_val_if_fail (GTK_IS_BOX (widget), NS_ERROR_FAILURE); + + gtk_box_pack_start (GTK_BOX (widget), indent, FALSE, FALSE, 0); + } + + cert->GetCommonName (value); + set_label_cert_attribute (gxml, "label_cn", value); + + cert->GetOrganization (value); + set_label_cert_attribute (gxml, "label_o", value); + + cert->GetOrganizationalUnit (value); + set_label_cert_attribute (gxml, "label_ou", value); + + cert->GetSerialNumber (value); + set_label_cert_attribute (gxml, "label_serial", value); + + rv = cert->GetIssuerCommonName (value); + if (NS_FAILED(rv)) return rv; + set_label_cert_attribute (gxml, "label_issuer_cn", value); + + cert->GetIssuerOrganization (value); + set_label_cert_attribute (gxml, "label_issuer_o", value); + + cert->GetIssuerOrganizationUnit (value); + set_label_cert_attribute (gxml, "label_issuer_ou", value); + + nsCOMPtr<nsIX509CertValidity> validity; + rv = cert->GetValidity (getter_AddRefs(validity)); + if (NS_FAILED(rv)) return rv; + + rv = validity->GetNotAfterLocalDay (value); + if (NS_FAILED(rv)) return rv; + set_label_cert_attribute (gxml, "label_notafter", value); + + rv = validity->GetNotBeforeLocalDay (value); + if (NS_FAILED(rv)) return rv; + set_label_cert_attribute (gxml, "label_notbefore", value); + + cert->GetSha1Fingerprint (value); + set_label_cert_attribute (gxml, "label_sha_print", value); + + cert->GetMd5Fingerprint (value); + set_label_cert_attribute (gxml, "label_md5_print", value); + + /* Hold a reference to each certificate in the chain while the + * dialog is displayed, this holds the reference for the ASN + * objects as well */ + + nsCOMPtr<nsIArray> certChain; + rv = cert->GetChain (getter_AddRefs(certChain)); + if (NS_FAILED(rv)) return rv; + + gboolean ret = setup_view_cert_tree (dialog, gxml, certChain); + if (ret == FALSE) return NS_ERROR_FAILURE; + + g_object_unref (gxml); + + gtk_widget_show_all (dialog); + + int res; + while (1) + { + res = gtk_dialog_run (GTK_DIALOG (dialog)); + if (res == GTK_RESPONSE_HELP) + { + ephy_gui_help (GTK_WINDOW (dialog), "epiphany", "using-certificate-viewer"); + continue; + } + break; + } + + gtk_widget_destroy (dialog); + return NS_OK; +} + +/* nsITokenPasswordDialogs */ + +/* NOTE: This interface totally sucks, see https://bugzilla.mozilla.org/show_bug.cgi?id=306993 */ + +/* void setPassword (in nsIInterfaceRequestor ctx, in wstring tokenName, out boolean canceled); */ +NS_IMETHODIMP +GtkNSSDialogs::SetPassword(nsIInterfaceRequestor *aCtx, + const PRUnichar *aTokenName, + PRBool *aCancelled) +{ + NS_ENSURE_ARG_POINTER(aCancelled); + + nsresult rv; + nsCOMPtr<nsIPK11Token> token; + nsCOMPtr<nsIPKCS11Slot> slot; + rv = GetTokenAndSlotFromName (aTokenName, getter_AddRefs (token), + getter_AddRefs (slot)); + NS_ENSURE_SUCCESS (rv, rv); + NS_ENSURE_TRUE (token && slot, NS_ERROR_FAILURE); + + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + PRUint32 status = nsIPKCS11Slot::SLOT_UNINITIALIZED; + slot->GetStatus (&status); + + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (aCtx)); + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + AutoWindowModalState modalState (parent); + + EphyPasswordDialogFlags flags = + EphyPasswordDialogFlags (EPHY_PASSWORD_DIALOG_FLAGS_SHOW_NEW_PASSWORD | + EPHY_PASSWORD_DIALOG_FLAGS_SHOW_QUALITY_METER); + if (status != nsIPKCS11Slot::SLOT_UNINITIALIZED) + flags = EphyPasswordDialogFlags (flags | EPHY_PASSWORD_DIALOG_FLAGS_SHOW_PASSWORD); + + GtkWidget *dialog = ephy_password_dialog_new + (gparent, + _("Change Token Password"), + flags); + EphyPasswordDialog *password_dialog = EPHY_PASSWORD_DIALOG (dialog); + + char *message; + if (status == nsIPKCS11Slot::SLOT_UNINITIALIZED) { + message = g_markup_printf_escaped (_("Choose a password for the “%s” token"), + NS_ConvertUTF16toUTF8 (aTokenName).get ()); + } else { + message = g_markup_printf_escaped (_("Change the password for the “%s” token"), + NS_ConvertUTF16toUTF8 (aTokenName).get ()); + } + + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), + message); + g_free (message); + + int response; + nsString oldPassword; + PRBool pwdOk, needsLogin; + do { + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (status != nsIPKCS11Slot::SLOT_UNINITIALIZED) + { + const char *pwd = ephy_password_dialog_get_password (password_dialog); + oldPassword = NS_ConvertUTF8toUTF16 (pwd); + } + } while (response == GTK_RESPONSE_OK && + status != nsIPKCS11Slot::SLOT_UNINITIALIZED && + NS_SUCCEEDED (token->NeedsLogin (&needsLogin)) && needsLogin && + NS_SUCCEEDED (token->CheckPassword (oldPassword.get (), &pwdOk) && + !pwdOk)); + + if (response == GTK_RESPONSE_ACCEPT) + { + const char *pwd = ephy_password_dialog_get_new_password (password_dialog); + + NS_ConvertUTF8toUTF16 newPassword (pwd); + + if (status == nsIPKCS11Slot::SLOT_UNINITIALIZED) + { + rv = token->InitPassword (newPassword.get ()); + } + else + { + rv = token->ChangePassword (oldPassword.get (), + newPassword.get ()); + } + } + else + { + rv = NS_OK; + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + *aCancelled = response != GTK_RESPONSE_ACCEPT; + + return rv; +} + +/* void getPassword (in nsIInterfaceRequestor ctx, in wstring tokenName, out wstring password, out boolean canceled); */ +NS_IMETHODIMP +GtkNSSDialogs::GetPassword(nsIInterfaceRequestor *aCtx, + const PRUnichar *aTokenName, + PRUnichar **aPassword, + PRBool *aCancelled) +{ + NS_ENSURE_ARG_POINTER(aCancelled); + + nsresult rv; + nsCOMPtr<nsIPK11Token> token; + nsCOMPtr<nsIPKCS11Slot> slot; + rv = GetTokenAndSlotFromName (aTokenName, getter_AddRefs (token), + getter_AddRefs (slot)); + NS_ENSURE_SUCCESS (rv, rv); + NS_ENSURE_TRUE (token && slot, NS_ERROR_FAILURE); + + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (aCtx)); + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + AutoWindowModalState modalState (parent); + + EphyPasswordDialogFlags flags = + EphyPasswordDialogFlags (EPHY_PASSWORD_DIALOG_FLAGS_SHOW_PASSWORD); + + GtkWidget *dialog = ephy_password_dialog_new + (gparent, + _("Get Token Password"), /* FIXME */ + flags); + EphyPasswordDialog *password_dialog = EPHY_PASSWORD_DIALOG (dialog); + + /* Translators: A "token" is something that enables the user to authenticate himself or + * prove his credentials. This can be either a hardware device (e.g. a smart-card), or + * a data file (e.g. a cryptographic certificate). + */ + char *message = g_markup_printf_escaped (_("Please enter the password for the “%s” token"), + NS_ConvertUTF16toUTF8 (aTokenName).get ()); + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), + message); + g_free (message); + + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response == GTK_RESPONSE_ACCEPT) + { + const char *pwd = ephy_password_dialog_get_password (password_dialog); + *aPassword = NS_StringCloneData (NS_ConvertUTF8toUTF16 (pwd)); + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + *aCancelled = response != GTK_RESPONSE_ACCEPT; + + return NS_OK; +} + +/* nsITokenDialogs */ + +static void +SelectionChangedCallback (GtkComboBox *combo, + GtkDialog *dialog) +{ + int active = gtk_combo_box_get_active (combo); + gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_ACCEPT, active >= 0); +} + +/* void ChooseToken (in nsIInterfaceRequestor ctx, + [array, size_is (count)] in wstring tokenNameList, + in unsigned long count, + out wstring tokenName, + out boolean canceled); */ +NS_IMETHODIMP +GtkNSSDialogs::ChooseToken (nsIInterfaceRequestor *aContext, + const PRUnichar **tokenNameList, + PRUint32 count, + PRUnichar **_tokenName, + PRBool *_cancelled) +{ + NS_ENSURE_ARG (tokenNameList); + NS_ENSURE_ARG (count); + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + /* Didn't you know it? MOZILLA SUCKS! ChooseToken is always called with |aContext| == NULL! See + * http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/manager/ssl/src/nsKeygenHandler.cpp&rev=1.39&mark=346#346 + * Need to investigate if we it's always called directly from code called from JS, in which case we + * can use EphyJSUtils::GetDOMWindowFromCallContext. + */ + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (aContext)); + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + AutoWindowModalState modalState (parent); + + GtkWidget *dialog = gtk_message_dialog_new + (GTK_WINDOW (gparent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_OTHER, + GTK_BUTTONS_CANCEL, + _("Please select a token:")); + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + GtkWidget *combo = gtk_combo_box_new_text (); + for (PRUint32 i = 0; i < count; ++i) { + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), + NS_ConvertUTF16toUTF8 (tokenNameList[i]).get ()); + } + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), -1); + g_signal_connect (combo, "changed", + G_CALLBACK (SelectionChangedCallback), dialog); + + /* FIXME: View Cert button? */ + + GtkWidget *vbox = GTK_MESSAGE_DIALOG (dialog)->label->parent; + gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Select"), + GTK_RESPONSE_ACCEPT); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT); + + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + int selected = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); + + gtk_widget_destroy (dialog); + + *_cancelled = response != GTK_RESPONSE_ACCEPT; + + if (response == GTK_RESPONSE_ACCEPT) { + NS_ENSURE_TRUE (selected >= 0 && selected < (int) count, NS_ERROR_FAILURE); + *_tokenName = NS_StringCloneData (nsDependentString (tokenNameList[selected])); + } + + return NS_OK; +} + +/* nsIDOMCryptoDialogs */ + +/* Note: this interface sucks! See https://bugzilla.mozilla.org/show_bug.cgi?id=341914 */ + +/* boolean ConfirmKeyEscrow (in nsIX509Cert escrowAuthority); */ +NS_IMETHODIMP +GtkNSSDialogs::ConfirmKeyEscrow (nsIX509Cert *aEscrowAuthority, + PRBool *_retval) +{ + NS_ENSURE_ARG (aEscrowAuthority); + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + +#if 0 + nsCOMPtr<nsIDOMWindow> parent (do_GetInterface (aCtx)); +#endif + nsCOMPtr<nsIDOMWindow> parent (EphyJSUtils::GetDOMWindowFromCallContext ()); + GtkWidget *gparent = EphyUtils::FindGtkParent (parent); + + AutoWindowModalState modalState (parent); + + /* FIXME: is that guaranteed to be non-empty? */ + nsString commonName; + aEscrowAuthority->GetCommonName (commonName); + + GtkWidget *dialog = gtk_message_dialog_new + (GTK_WINDOW (gparent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING /* QUESTION really but it's also a strong warnings... */, + GTK_BUTTONS_NONE, + _("Escrow the secret key?")); + + /* FIXME: If I understand the documentation of generateCRMFRequest + * correctly, key escrow is never used for signing keys (if it were, + * we'd have to warn that the cert authority can forge your signature + * too). + */ + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (dialog), + _("The certificate authority “%s” requests that you give it a copy " + "of the newly generated secret key.\n\n" + "This will enable the certificate authority read any " + "communications encrypted with this key " + "without your knowledge or consent.\n\n" + "It is strongly recommended not to allow it."), + NS_ConvertUTF16toUTF8 (commonName).get ()); + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + GtkWidget *button = gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Reject"), + GTK_RESPONSE_REJECT); + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Allow"), + GTK_RESPONSE_ACCEPT); + /* FIXME: View Cert button? */ + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT); + gtk_widget_grab_focus (button); + + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + *_retval = response == GTK_RESPONSE_ACCEPT; + + return NS_OK; +} diff --git a/embed/xulrunner/components/GtkNSSDialogs.h b/embed/xulrunner/components/GtkNSSDialogs.h new file mode 100644 index 000000000..2edd1ffbe --- /dev/null +++ b/embed/xulrunner/components/GtkNSSDialogs.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2003 Crispin Flowerday <gnome@flowerday.cx> + * Copyright © 2006 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$ + */ + +#ifndef GTKNSSDIALOGS_H +#define GTKNSSDIALOGS_H 1 + +#include <nsIBadCertListener.h> +#include <nsICertificateDialogs.h> +#include <nsITokenPasswordDialogs.h> +#include <nsITokenDialogs.h> +#include <nsIDOMCryptoDialogs.h> + +class nsIPK11Token; +class nsIPKCS11Slot; + +/* 7a50a10d-9425-4e12-84b1-5822edacd8ce */ +#define GTK_NSSDIALOGS_CID \ + {0x7a50a10d, 0x9425, 0x4e12, {0x84, 0xb1, 0x58, 0x22, 0xed, 0xac, 0xd8, 0xce}} + +#define GTK_NSSDIALOGS_CLASSNAME "Gtk NSS Dialogs" + +class GtkNSSDialogs : public nsIBadCertListener, + public nsICertificateDialogs, + public nsITokenPasswordDialogs, + public nsITokenDialogs, + public nsIDOMCryptoDialogs +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIBADCERTLISTENER + NS_DECL_NSICERTIFICATEDIALOGS + NS_DECL_NSITOKENPASSWORDDIALOGS + NS_DECL_NSITOKENDIALOGS + NS_DECL_NSIDOMCRYPTODIALOGS + + GtkNSSDialogs(); + virtual ~GtkNSSDialogs(); + + private: + nsresult GetTokenAndSlotFromName(const PRUnichar*, nsIPK11Token**, nsIPKCS11Slot**); +}; + +#endif /* GTKNSSDIALOGS_H */ diff --git a/embed/xulrunner/components/GtkNSSKeyPairDialogs.cpp b/embed/xulrunner/components/GtkNSSKeyPairDialogs.cpp new file mode 100644 index 000000000..ac3afdfde --- /dev/null +++ b/embed/xulrunner/components/GtkNSSKeyPairDialogs.cpp @@ -0,0 +1,216 @@ +/* + * GtkNSSKeyPairDialogs.cpp + * + * Copyright © 2003 Crispin Flowerday <gnome@flowerday.cx> + * + * 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$ + */ + +/* + * This file provides Gtk implementations of the mozilla Generating Key Pair + * dialogs. + */ + +#include <xpcom-config.h> +#include "config.h" + +#include <glib/gi18n.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtkimage.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkmain.h> +#include <gtk/gtkprogressbar.h> +#include <gtk/gtkstock.h> +#include <gtk/gtkvbox.h> + +#include <nsIDOMWindow.h> +#include <nsIInterfaceRequestor.h> +#include <nsIInterfaceRequestorUtils.h> +#include <nsIKeygenThread.h> +#include <nsIObserver.h> +#include <nsIServiceManager.h> + +#include "ephy-debug.h" +#include "ephy-gui.h" +#include "ephy-stock-icons.h" + +#include "AutoJSContextStack.h" +#include "AutoWindowModalState.h" +#include "EphyUtils.h" + +#include "GtkNSSKeyPairDialogs.h" + +GtkNSSKeyPairDialogs::GtkNSSKeyPairDialogs () +{ + LOG ("GtkNSSKeyPairDialogs ctor (%p)", this); +} + +GtkNSSKeyPairDialogs::~GtkNSSKeyPairDialogs () +{ + LOG ("GtkNSSKeyPairDialogs dtor (%p)", this); +} + +NS_IMPL_THREADSAFE_ISUPPORTS1 (GtkNSSKeyPairDialogs, + nsIGeneratingKeypairInfoDialogs) + +class KeyPairObserver : public nsIObserver +{ +public: + NS_DECL_NSIOBSERVER + NS_DECL_ISUPPORTS + + KeyPairObserver() : close_called (FALSE) {}; + virtual ~KeyPairObserver() {}; + + gboolean close_called; +}; + +NS_IMPL_ISUPPORTS1 (KeyPairObserver, nsIObserver); + +NS_IMETHODIMP KeyPairObserver::Observe (nsISupports *aSubject, const char *aTopic, + const PRUnichar *aData) +{ + close_called = TRUE; + return NS_OK; +} + +/* ------------------------------------------------------------ */ +static void +begin_busy (GtkWidget *widget) +{ + static GdkCursor *cursor = NULL; + + if (cursor == NULL) cursor = gdk_cursor_new (GDK_WATCH); + + if (!GTK_WIDGET_REALIZED (widget)) gtk_widget_realize (GTK_WIDGET(widget)); + + gdk_window_set_cursor (GTK_WIDGET (widget)->window, cursor); + + /* Eek! FIXME: AutoJSContextStack! */ + while (gtk_events_pending ()) gtk_main_iteration (); +} + +static void +end_busy (GtkWidget *widget) +{ + gdk_window_set_cursor (GTK_WIDGET(widget)->window, NULL); +} + + +struct KeyPairInfo +{ + GtkWidget *progress; + GtkWidget *dialog; + KeyPairObserver *helper; +}; + + +static gboolean +generating_timeout_cb (KeyPairInfo *info) +{ + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (info->progress)); + + if (info->helper->close_called) + { + gtk_dialog_response (GTK_DIALOG (info->dialog), GTK_RESPONSE_OK); + } + return TRUE; +} + + +/* void displayGeneratingKeypairInfo (in nsIInterfaceRequestor ctx, + in nsIKeygenThread runnable); */ +NS_IMETHODIMP +GtkNSSKeyPairDialogs::DisplayGeneratingKeypairInfo (nsIInterfaceRequestor *ctx, + nsIKeygenThread *runnable) +{ + GtkWidget *dialog, *progress, *label, *vbox; + gint timeout_id; + + nsresult rv; + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return rv; + + nsCOMPtr<nsIDOMWindow> parent = do_GetInterface (ctx); + GtkWindow *gparent = GTK_WINDOW (EphyUtils::FindGtkParent (parent)); + + AutoWindowModalState modalState (parent); + + dialog = gtk_dialog_new_with_buttons ("", gparent, + GTK_DIALOG_DESTROY_WITH_PARENT, (char *) NULL); + + if (gparent) + { + gtk_window_group_add_window (ephy_gui_ensure_window_group (gparent), + GTK_WINDOW (dialog)); + } + + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, TRUE, TRUE, 0); + + label = gtk_label_new (NULL); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); + + char *msg = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s", + _("Generating Private Key."), + _("Please wait while a new private key is " + "generated. This process could take a few minutes." )); + gtk_label_set_markup (GTK_LABEL(label), msg); + g_free (msg); + + progress = gtk_progress_bar_new (); + gtk_box_pack_start (GTK_BOX (vbox), progress, TRUE, TRUE, 0); + + /* Create a helper class that just waits for close events + * from the other thread */ + nsCOMPtr<KeyPairObserver> helper = new KeyPairObserver; + + KeyPairInfo callback_data = { progress, dialog, helper }; + timeout_id = g_timeout_add (100, (GSourceFunc)generating_timeout_cb, &callback_data); + + gtk_widget_show_all (dialog); + gtk_widget_hide (GTK_DIALOG (dialog)->action_area); + + begin_busy (dialog); + runnable->StartKeyGeneration (helper); + int res = gtk_dialog_run (GTK_DIALOG (dialog)); + if (res != GTK_RESPONSE_OK && helper->close_called == FALSE) + { + /* Ignore the already_closed flag, our nsIDOMWindowInterna::Close + * function just sets a flag, it doesn't close the window, so we + * dont have a race condition */ + PRBool already_closed = FALSE; + runnable->UserCanceled (&already_closed); + } + + g_source_remove (timeout_id); + end_busy (dialog); + gtk_widget_destroy (dialog); + return NS_OK; +} diff --git a/embed/xulrunner/components/GtkNSSKeyPairDialogs.h b/embed/xulrunner/components/GtkNSSKeyPairDialogs.h new file mode 100644 index 000000000..f4b92d9e1 --- /dev/null +++ b/embed/xulrunner/components/GtkNSSKeyPairDialogs.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2003 Crispin Flowerday <gnome@flowerday.cx> + * + * 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$ + */ + +#ifndef GTKNSSKEYPAIRDIALOGS_H +#define GTKNSSKEYPAIRDIALOGS_H 1 + +#include <nsIGenKeypairInfoDlg.h> + +// 6a8b1aff-ae8b-4751-982e-4ce5ad544100 +#define GTK_NSSKEYPAIRDIALOGS_CID \ + {0x6a8b1aff, 0xae8b, 0x4751, {0x98, 0x2e, 0x4c, 0xe5, 0xad, 0x54, 0x41, 0x10}} + +#define GTK_NSSKEYPAIRDIALOGS_CLASSNAME "Gtk NSS Key Pair Dialogs" + +class GtkNSSKeyPairDialogs +: public nsIGeneratingKeypairInfoDialogs +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGENERATINGKEYPAIRINFODIALOGS + + GtkNSSKeyPairDialogs(); + virtual ~GtkNSSKeyPairDialogs(); +}; + + +#endif /* GTKNSSKEYPAIRDIALOGS_H */ diff --git a/embed/xulrunner/components/GtkNSSSecurityWarningDialogs.cpp b/embed/xulrunner/components/GtkNSSSecurityWarningDialogs.cpp new file mode 100644 index 000000000..08f5e664a --- /dev/null +++ b/embed/xulrunner/components/GtkNSSSecurityWarningDialogs.cpp @@ -0,0 +1,285 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright © 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Terry Hayes <thayes@netscape.com> + * Javier Delgadillo <javi@netscape.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + * + * Copyright © 2005 Christian Persch + * + * $Id$ + */ + +#include <xpcom-config.h> +#include "config.h" + +#include <glib/gi18n.h> +#include <gtk/gtkbox.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkvbox.h> + +#include "ephy-stock-icons.h" + +#include <nsCOMPtr.h> +#include <nsIDOMWindow.h> +#include <nsIInterfaceRequestor.h> +#include <nsIInterfaceRequestorUtils.h> +#include <nsIPrefBranch.h> +#include <nsIPrefService.h> +#include <nsIServiceManager.h> +#include <nsServiceManagerUtils.h> + +#include "AutoJSContextStack.h" +#include "AutoWindowModalState.h" +#include "EphyUtils.h" + +#include "GtkNSSSecurityWarningDialogs.h" + +NS_IMPL_THREADSAFE_ISUPPORTS1 (GtkNSSSecurityWarningDialogs, nsISecurityWarningDialogs) + +#define ENTER_SITE_PREF "security.warn_entering_secure" +#define WEAK_SITE_PREF "security.warn_entering_weak" +#define MIXEDCONTENT_PREF "security.warn_viewing_mixed" +#define INSECURE_SUBMIT_PREF "security.warn_submit_insecure" + +GtkNSSSecurityWarningDialogs::GtkNSSSecurityWarningDialogs() +{ +} + +GtkNSSSecurityWarningDialogs::~GtkNSSSecurityWarningDialogs() +{ +} + +NS_IMETHODIMP +GtkNSSSecurityWarningDialogs::ConfirmEnteringSecure (nsIInterfaceRequestor *aContext, + PRBool *_retval) +{ + DoDialog (aContext, + ENTER_SITE_PREF, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + GTK_RESPONSE_OK, + _("Security Notice"), + _("This page is loaded over a secure connection"), + _("For secure pages, the address entry has a distinct " + "color and a locked padlock icon is displayed.\n\n" + "The padlock icon in the statusbar also indicates " + "whether a page is secure."), + nsnull, _retval); + + *_retval = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +GtkNSSSecurityWarningDialogs::ConfirmEnteringWeak (nsIInterfaceRequestor *aContext, + PRBool *_retval) +{ + DoDialog (aContext, + WEAK_SITE_PREF, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + GTK_RESPONSE_OK, + _("Security Warning"), + _("This page is loaded over a low security connection"), + _("Any information you see or enter on this page could " + "easily be intercepted by a third party."), + nsnull, _retval); + + *_retval = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +GtkNSSSecurityWarningDialogs::ConfirmLeavingSecure (nsIInterfaceRequestor *aContext, + PRBool *_retval) +{ + /* don't prompt */ + *_retval = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +GtkNSSSecurityWarningDialogs::ConfirmMixedMode (nsIInterfaceRequestor *aContext, + PRBool *_retval) +{ + DoDialog (aContext, + MIXEDCONTENT_PREF, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + GTK_RESPONSE_OK, + _("Security Warning"), + _("Some parts of this page are loaded over an insecure connection"), + _("Some information you see or enter will be sent over an insecure " + "connection, and could easily be intercepted by a third party."), + nsnull, _retval); + + *_retval = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +GtkNSSSecurityWarningDialogs::ConfirmPostToInsecure (nsIInterfaceRequestor *aContext, + PRBool* _retval) +{ + DoDialog (aContext, + INSECURE_SUBMIT_PREF, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CANCEL, + GTK_RESPONSE_ACCEPT, + _("Security Warning"), + _("Send this information over an insecure connection?"), + _("The information you have entered will be sent over an " + "insecure connection, and could easily be intercepted " + "by a third party."), + _("_Send"), + _retval); + + return NS_OK; +} + +NS_IMETHODIMP +GtkNSSSecurityWarningDialogs::ConfirmPostToInsecureFromSecure (nsIInterfaceRequestor *aContext, + PRBool* _retval) +{ + DoDialog (aContext, + nsnull, /* No preference for this one - it's too important */ + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CANCEL, + GTK_RESPONSE_CANCEL, + _("Security Warning"), + _("Send this information over an insecure connection?"), + _("Although this page was loaded over a secure connection, " + "the information you have entered will be sent over an " + "insecure connection, and could easily be intercepted by " + "a third party."), + _("_Send"), + _retval); + + return NS_OK; +} + +void +GtkNSSSecurityWarningDialogs::DoDialog (nsIInterfaceRequestor *aContext, + const char *aPrefName, + GtkMessageType aType, + GtkButtonsType aButtons, + int aDefaultResponse, + const char *aTitle, + const char *aPrimary, + const char *aSecondary, + const char *aButtonText, + PRBool *_retval) +{ + *_retval = PR_FALSE; + + nsresult rv; + PRBool show = PR_TRUE; + nsCOMPtr<nsIPrefBranch> prefBranch + (do_GetService (NS_PREFSERVICE_CONTRACTID)); + if (prefBranch && aPrefName) + { + rv = prefBranch->GetBoolPref (aPrefName, &show); + if (NS_FAILED(rv)) show = PR_TRUE; + } + + char *showOncePref = NULL; + PRBool showOnce = PR_FALSE; + if (!show && prefBranch && aPrefName) + { + showOncePref = g_strconcat (aPrefName, ".show_once", (char *) NULL); + rv = prefBranch->GetBoolPref (showOncePref, &showOnce); + if (NS_FAILED (rv)) showOnce = PR_FALSE; + } + + if (!show && !showOnce) + { + g_free (showOncePref); + *_retval = PR_TRUE; + return; + } + + /* On 1.8.0, domWin will be always nsnull, because of + * https://bugzilla.mozilla.org/show_bug.cgi?id=277587 + */ + nsCOMPtr<nsIDOMWindow> domWin (do_GetInterface (aContext)); + GtkWidget *parent = EphyUtils::FindGtkParent (domWin); + + AutoJSContextStack stack; + rv = stack.Init (); + if (NS_FAILED (rv)) return; + + AutoWindowModalState modalState (domWin); + + GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (parent), + GTK_DIALOG_MODAL, aType, + aButtons, aPrimary); + + if (parent && GTK_WINDOW (parent)->group) + { + gtk_window_group_add_window (GTK_WINDOW (parent)->group, + GTK_WINDOW (dialog)); + } + + if (aSecondary) + { + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (dialog), aSecondary); + } + + if (aButtonText) + { + gtk_dialog_add_button (GTK_DIALOG (dialog), aButtonText, + GTK_RESPONSE_ACCEPT); + } + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), aDefaultResponse); + + gtk_window_set_title (GTK_WINDOW (dialog), aTitle); + gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY); + + int response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + *_retval = (response == GTK_RESPONSE_ACCEPT || response == GTK_RESPONSE_OK); + + if (prefBranch && showOncePref && showOnce && *_retval) + { + prefBranch->SetBoolPref (showOncePref, PR_FALSE); + } + + g_free (showOncePref); +} diff --git a/embed/xulrunner/components/GtkNSSSecurityWarningDialogs.h b/embed/xulrunner/components/GtkNSSSecurityWarningDialogs.h new file mode 100644 index 000000000..a84ea6585 --- /dev/null +++ b/embed/xulrunner/components/GtkNSSSecurityWarningDialogs.h @@ -0,0 +1,83 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright © 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Terry Hayes <thayes@netscape.com> + * Javier Delgadillo <javi@netscape.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + * + * Copyright © 2005 Christian Persch + * + * $Id$ + */ + +#ifndef GTK_NSSSECURITYDIALOGS_H +#define GTK_NSSSECURITYDIALOGS_H + +#include <gtk/gtkmessagedialog.h> + +#include <nsISecurityWarningDialogs.h> + +#define GTK_NSSSECURITYWARNINGDIALOGS_CLASSNAME "Epiphany Security Warning Dialogs Class" +#define GTK_NSSSECURITYWARNINGDIALOGS_CID \ +{ \ + /* 1f5eac0a-d7e3-4f8e-b4d5-7240f7cba269 */ \ + 0x1f5eac0a, \ + 0xd7e3, \ + 0x4f8e, \ + { 0xb4, 0xd5, 0x72, 0x40, 0xf7, 0xcb, 0xa2, 0x69 } \ +} + +class GtkNSSSecurityWarningDialogs : public nsISecurityWarningDialogs +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISECURITYWARNINGDIALOGS + + GtkNSSSecurityWarningDialogs(); + virtual ~GtkNSSSecurityWarningDialogs(); + +protected: + void DoDialog (nsIInterfaceRequestor *aContext, + const char *aPrefName, + GtkMessageType aType, + GtkButtonsType aButtons, + int aDefaultResponse, + const char *aTitle, + const char *aPrimary, + const char *aSecondary, + const char *aButtonText, + PRBool *_retval); +}; + +#endif /* !GTK_NSSSECURITYDIALOGS_H */ diff --git a/embed/xulrunner/components/Makefile.am b/embed/xulrunner/components/Makefile.am index 53796b169..ad9b5f348 100644 --- a/embed/xulrunner/components/Makefile.am +++ b/embed/xulrunner/components/Makefile.am @@ -3,11 +3,75 @@ NULL = noinst_LTLIBRARIES = libephycomponents.la libephycomponents_la_SOURCES = \ - GeckoPromptService.cpp \ - GeckoPromptService.h \ + ContentHandler.cpp \ + ContentHandler.h \ + EphyAboutModule.cpp \ + EphyAboutModule.h \ + EphyContentPolicy.cpp \ + EphyContentPolicy.h \ + EphyRedirectChannel.cpp \ + EphyRedirectChannel.h \ + EphySidebar.cpp \ + EphySidebar.h \ + GeckoCookiePromptService.cpp \ + GeckoCookiePromptService.h \ + GeckoFormSigningDialog.cpp \ + GeckoFormSigningDialog.h \ + GeckoPrintService.cpp \ + GeckoPrintService.h \ + GeckoPrintSession.cpp \ + GeckoPrintSession.h \ + GeckoPromptService.cpp \ + GeckoPromptService.h \ + GlobalHistory.cpp \ + GlobalHistory.h \ + MozDownload.cpp \ + MozDownload.h \ + MozRegisterComponents.cpp \ + MozRegisterComponents.h \ $(NULL) +# if ENABLE_FILEPICKER +# libephycomponents_la_SOURCES += \ +# FilePicker.cpp \ +# FilePicker.h +# endif + +# NOTE & FIXME: Most of these are GPL not LGPL +# if HAVE_MOZILLA_PSM +libephycomponents_la_SOURCES += \ + GtkNSSClientAuthDialogs.cpp \ + GtkNSSClientAuthDialogs.h \ + GtkNSSDialogs.cpp \ + GtkNSSDialogs.h \ + GtkNSSKeyPairDialogs.cpp \ + GtkNSSKeyPairDialogs.h \ + GtkNSSSecurityWarningDialogs.cpp\ + GtkNSSSecurityWarningDialogs.h +# endif + +# if ENABLE_SPELLCHECKER +libephycomponents_la_SOURCES += \ + GeckoSpellCheckEngine.cpp \ + GeckoSpellCheckEngine.h +# endif + libephycomponents_la_CPPFLAGS = \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/embed \ + -I$(top_srcdir) \ + -I$(top_srcdir)/embed/xulrunner/src \ + -I$(top_srcdir)/embed/xulrunner/embed \ + -I$(top_srcdir)/embed/xulrunner/utils \ + -I$(top_builddir)/embed/xulrunner/src \ + -I$(top_builddir)/embed/xulrunner/embed \ + -DSHARE_DIR=\"$(pkgdatadir)\" \ + -DPLUGINDIR=\"$(libdir)/epiphany/$(EPIPHANY_MAJOR)/plugins\" \ + -DMOZILLA_HOME=\"$(LIBXUL_LIBDIR)\" \ + -DMOZILLA_PREFIX=\"$(LIBXUL_PREFIX)\" \ + -DMOZILLA_NATIVE_PLUGINSDIR=\"$(libdir)/mozilla/plugins\" \ + -DUA_VERSION=\"$(EPIPHANY_UA_VERSION)\" \ + -DALLOW_PRIVATE_API \ $(LIBXUL_CXXCPPFLAGS) \ $(LIBXUL_INCLUDES) \ $(AM_CPPFLAGS) @@ -15,6 +79,11 @@ libephycomponents_la_CPPFLAGS = \ libephycomponents_la_CXXFLAGS = \ $(LIBXUL_CXXFLAGS) \ $(GTK_CFLAGS) \ + $(GTKPRINT_CFLAGS) \ + $(GNOMEVFS_CFLAGS) \ + $(GCONF_CFLAGS) \ + $(GLADE_CFLAGS) \ + $(GNOME_CFLAGS) \ $(AM_CXXFLAGS) libephycomponents_la_LDFLAGS = \ diff --git a/embed/xulrunner/components/MozDownload.cpp b/embed/xulrunner/components/MozDownload.cpp new file mode 100644 index 000000000..d6d28ec59 --- /dev/null +++ b/embed/xulrunner/components/MozDownload.cpp @@ -0,0 +1,812 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright © 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen <ccarlen@netscape.com> + * + * Adapted for epiphany by Marco Pesenti Gritti <marco@gnome.org> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + * + * $Id$ + */ + +#include <xpcom-config.h> +#include "config.h" + +#include <stdlib.h> + +#include <glib/gi18n.h> + +#include <nsStringGlue.h> + +#include <nsComponentManagerUtils.h> +#include <nsICancelable.h> +#include <nsIChannel.h> +#include <nsIDOMDocument.h> +#include <nsIFileURL.h> +#include <nsIIOService.h> +#include <nsILocalFile.h> +#include <nsIMIMEInfo.h> +#include <nsIMIMEService.h> +#include <nsIObserver.h> +#include <nsIRequest.h> +#include <nsIURI.h> +#include <nsIWritablePropertyBag2.h> +#include <nsIWebBrowserPersist.h> + +#include <nsMemory.h> +#include <nsNetError.h> +#include <nsServiceManagerUtils.h> + +#include "EphyBadCertRejector.h" +#include "EphyUtils.h" + +#include "eel-gconf-extensions.h" +#include "ephy-debug.h" +#include "ephy-file-helpers.h" +#include "ephy-prefs.h" +#include "mozilla-download.h" + +#include "MozDownload.h" + +/* Minimum time between progress updates */ +#define PROGRESS_RATE 500000 /* microsec */ + +const char* const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1"; + +MozDownload::MozDownload() : + mTotalProgress(-1), + mCurrentProgress(0), + mMaxSize(-1), + mAddToRecent(PR_TRUE), + mStatus(NS_OK), + mEmbedPersist(nsnull), + mDownloadState(EPHY_DOWNLOAD_INITIALISING) +{ + LOG ("MozDownload ctor (%p)", (void *) this); +} + +MozDownload::~MozDownload() +{ + LOG ("MozDownload dtor (%p)", (void *) this); + + NS_ASSERTION (!mEphyDownload, "MozillaDownload still alive!"); +} + +NS_IMPL_ISUPPORTS4 (MozDownload, + nsIWebProgressListener, + nsIWebProgressListener2, + nsITransfer, + nsIInterfaceRequestor) + +nsresult +MozDownload::InitForEmbed (nsIURI *aSource, nsIURI *aTarget, const nsAString &aDisplayName, + nsIMIMEInfo *aMIMEInfo, PRTime aStartTime, nsILocalFile *aTempFile, + nsICancelable *aCancelable, MozillaEmbedPersist *aEmbedPersist, + PRInt64 aMaxSize) +{ + mEmbedPersist = aEmbedPersist; + mMaxSize = aMaxSize; + return Init (aSource, aTarget, aDisplayName, aMIMEInfo, aStartTime, aTempFile, aCancelable); +} + +/* void init (in nsIURI aSource, in nsIURI aTarget, in AString aDisplayName, in nsIMIMEInfo aMIMEInfo, in PRTime startTime, in nsILocalFile aTempFile, in nsICancelable aCancelable); */ +NS_IMETHODIMP +MozDownload::Init (nsIURI *aSource, + nsIURI *aTarget, + const nsAString &aDisplayName, + nsIMIMEInfo *aMIMEInfo, + PRTime aStartTime, + nsILocalFile *aTempFile, + nsICancelable *aCancelable) +{ + PRBool addToView = PR_TRUE; + + if (mEmbedPersist) + { + EphyEmbedPersistFlags flags; + + flags = ephy_embed_persist_get_flags (EPHY_EMBED_PERSIST (mEmbedPersist)); + + addToView = !(flags & EPHY_EMBED_PERSIST_NO_VIEW); + } + + mSource = aSource; + mDestination = aTarget; + mStartTime = aStartTime; + mTotalProgress = 0; + mCurrentProgress = 0; + mPercentComplete = 0; + mInterval = PROGRESS_RATE; + mLastUpdate = mStartTime; + mMIMEInfo = aMIMEInfo; + mAddToRecent = addToView; + + /* This will create a refcount cycle, which needs to be broken in ::OnStateChange */ + mCancelable = aCancelable; + + if (addToView) + { + DownloaderView *dview; + dview = EPHY_DOWNLOADER_VIEW + (ephy_embed_shell_get_downloader_view (embed_shell)); + mEphyDownload = mozilla_download_new (this); + g_object_add_weak_pointer (G_OBJECT (mEphyDownload), + (gpointer *) &mEphyDownload); + downloader_view_add_download (dview, mEphyDownload); + g_object_unref (mEphyDownload); + } + else + { + mEphyDownload = nsnull; + } + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetSource(nsIURI **aSource) +{ + NS_ENSURE_ARG_POINTER(aSource); + NS_IF_ADDREF(*aSource = mSource); + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetTargetFile (nsILocalFile** aTargetFile) +{ + nsresult rv; + + nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mDestination, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIFile> file; + rv = fileURL->GetFile(getter_AddRefs(file)); + if (NS_SUCCEEDED(rv)) + rv = CallQueryInterface(file, aTargetFile); + return rv; +} + +NS_IMETHODIMP +MozDownload::GetPercentComplete(PRInt32 *aPercentComplete) +{ + NS_ENSURE_ARG_POINTER(aPercentComplete); + *aPercentComplete = mPercentComplete; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetTotalProgress(PRInt64 *aTotalProgress) +{ + NS_ENSURE_ARG_POINTER(aTotalProgress); + *aTotalProgress = mTotalProgress; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetCurrentProgress(PRInt64 *aCurrentProgress) +{ + NS_ENSURE_ARG_POINTER(aCurrentProgress); + *aCurrentProgress = mCurrentProgress; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetState(EphyDownloadState *aDownloadState) +{ + NS_ENSURE_ARG_POINTER(aDownloadState); + *aDownloadState = mDownloadState; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetElapsedTime(PRInt64 *aElapsedTime) +{ + NS_ENSURE_ARG_POINTER(aElapsedTime); + *aElapsedTime = PR_Now() - mStartTime; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetMIMEInfo(nsIMIMEInfo **aMIMEInfo) +{ + NS_ENSURE_ARG_POINTER(aMIMEInfo); + NS_IF_ADDREF(*aMIMEInfo = mMIMEInfo); + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnStateChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, + PRUint32 aStateFlags, nsresult aStatus) +{ + nsresult rv; + + if (NS_FAILED(aStatus) && NS_SUCCEEDED(mStatus)) + mStatus = aStatus; + + if (aStateFlags & STATE_START) + { + mDownloadState = EPHY_DOWNLOAD_DOWNLOADING; + + if (mEphyDownload) + { + g_signal_emit_by_name (mEphyDownload, "changed"); + } + } + + /* We will get this even in the event of a cancel */ + /* Due to a mozilla bug [https://bugzilla.mozilla.org/show_bug.cgi?id=304353], + * we'll only get STATE_STOP if we're driven from external app handler; elsewhere + * we get STATE_STOP | STATE_IS_NETWORK | STATE_IS_REQUEST. So check first if + * STATE_IS_REQUEST is set. + */ + /* Be careful that download is only completed when STATE_IS_NETWORK is set + * and many lonely STOP events may be triggered before. + */ +#ifdef GNOME_ENABLE_DEBUG +{ + nsCString spec; + if (mSource) mSource->GetSpec(spec); + + LOG ("url %s, status %x, state %x (is-stop:%s, is-network:%s, is-request:%s)", + spec.get(), aStatus, aStateFlags, + aStateFlags & STATE_STOP ? "t" : "f", + aStateFlags & STATE_IS_NETWORK ? "t" : "f", + aStateFlags & STATE_IS_REQUEST ? "t" : "f"); +} +#endif + + if (((aStateFlags & STATE_IS_REQUEST) && + (aStateFlags & STATE_IS_NETWORK) && + (aStateFlags & STATE_STOP)) || + aStateFlags == STATE_STOP) + { + LOG ("STATE_STOP"); + + /* Keep us alive */ + nsCOMPtr<nsITransfer> kungFuDeathGrip(this); + + mDownloadState = NS_SUCCEEDED (aStatus) ? EPHY_DOWNLOAD_COMPLETED : EPHY_DOWNLOAD_FAILED; + if (mEphyDownload) + { + g_signal_emit_by_name (mEphyDownload, "changed"); + } + + /* break refcount cycle */ + mCancelable = nsnull; + + nsCString destSpec; + nsCString mimeType; + + mDestination->GetSpec (destSpec); + + if (NS_SUCCEEDED (aStatus) && mMIMEInfo) + { + rv = mMIMEInfo->GetMIMEType (mimeType); + NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE); + } + + if (mAddToRecent) + { + ephy_file_add_recent_item (destSpec.get(), mimeType.get()); + } + + if (mEmbedPersist) + { + if (NS_SUCCEEDED (aStatus)) + { + mozilla_embed_persist_completed (mEmbedPersist); + } + else + { + mozilla_embed_persist_cancelled (mEmbedPersist); + } + } + else if (NS_SUCCEEDED (aStatus)) + { + /* see http://bugzilla.gnome.org/show_bug.cgi?id=456945 */ +#if 1 //def HAVE_GECKO_1_9 + // FIXMEchpe fix this! + return NS_OK; +#else + GnomeVFSMimeApplication *helperApp; + NS_ENSURE_TRUE (mMIMEInfo, NS_ERROR_FAILURE); + + nsString description; + mMIMEInfo->GetApplicationDescription (description); + + nsCString cDesc; + NS_UTF16ToCString (description, NS_CSTRING_ENCODING_UTF8, cDesc); + + /* HACK we use the application description to decide + if we have to open the saved file */ + if (g_str_has_prefix (cDesc.get(), "gnome-default:")) + { + /* Format gnome-default:<usertime>:<helperapp id> */ + char **str = g_strsplit (cDesc.get(), ":", -1); + g_return_val_if_fail (g_strv_length (str) == 3, NS_ERROR_FAILURE); + + char *end; + guint32 user_time = strtoul (str[1], &end, 0); + + helperApp = gnome_vfs_mime_application_new_from_desktop_id (str[2]); + if (!helperApp) return NS_ERROR_FAILURE; + + nsCString aDest; + rv = mDestination->GetSpec (aDest); + NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE); + + ephy_file_launch_application (helperApp, destSpec.get (), user_time); + + gnome_vfs_mime_application_free (helperApp); + g_strfreev (str); + } + else if (g_str_has_prefix (cDesc.get(), "gnome-browse-to-file:")) + { + /* Format gnome-browse-to-file:<usertime> */ + char **str = g_strsplit (cDesc.get(), ":", -1); + g_return_val_if_fail (g_strv_length (str) == 2, NS_ERROR_FAILURE); + + char *end; + guint32 user_time = strtoul (str[1], &end, 0); + + nsCString aDest; + rv = mDestination->GetSpec (aDest); + NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE); + + ephy_file_browse_to (aDest.get (), user_time); + + g_strfreev (str); + } +#endif /* HAVE_GECKO_1_9 */ + } + } + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnProgressChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt32 aCurSelfProgress, + PRInt32 aMaxSelfProgress, + PRInt32 aCurTotalProgress, + PRInt32 aMaxTotalProgress) +{ + return OnProgressChange64 (aWebProgress, aRequest, + aCurSelfProgress, aMaxSelfProgress, + aCurTotalProgress, aMaxTotalProgress); +} + +/* void onProgressChange64 (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long long aCurSelfProgress, in long long aMaxSelfProgress, in long long aCurTotalProgress, + in long long aMaxTotalProgress); */ +NS_IMETHODIMP +MozDownload::OnProgressChange64 (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt64 aCurSelfProgress, + PRInt64 aMaxSelfProgress, + PRInt64 aCurTotalProgress, + PRInt64 aMaxTotalProgress) +{ + if (mMaxSize >= 0 && + ((aMaxTotalProgress > 0 && mMaxSize < aMaxTotalProgress) || + mMaxSize < aCurTotalProgress)) + { + Cancel (); + } + + if (!mRequest) + mRequest = aRequest; + + PRInt64 now = PR_Now (); + + if ((now - mLastUpdate < mInterval) && + (aMaxTotalProgress == -1 || aCurTotalProgress < aMaxTotalProgress)) + return NS_OK; + + mLastUpdate = now; + + if (aMaxTotalProgress <= 0) + { + mPercentComplete = -1; + } + else + { + /* Make sure not to round up, so we don't display 100% unless + * it's really finished! + */ + mPercentComplete = (PRInt32)(((float)aCurTotalProgress / (float)aMaxTotalProgress) * 100.0); + } + + mTotalProgress = aMaxTotalProgress; + mCurrentProgress = aCurTotalProgress; + + if (mEphyDownload) + { + g_signal_emit_by_name (mEphyDownload, "changed"); + } + + return NS_OK; +} + +/* boolean onRefreshAttempted (in nsIWebProgress aWebProgress, in nsIURI aRefreshURI, in long aDelay, in boolean aSameURI); */ +NS_IMETHODIMP +MozDownload::OnRefreshAttempted(nsIWebProgress *aWebProgress, + nsIURI *aUri, + PRInt32 aDelay, + PRBool aSameUri, + PRBool *allowRefresh) +{ + *allowRefresh = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnLocationChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location) +{ + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnStatusChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, + nsresult aStatus, const PRUnichar *aMessage) +{ + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnSecurityChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 state) +{ + return NS_OK; +} + +/* void getInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */ +NS_IMETHODIMP +MozDownload::GetInterface(const nsIID & uuid, void * *result) +{ + if (uuid.Equals (NS_GET_IID (nsIBadCertListener)) && + mEmbedPersist) + { + EphyEmbedPersistFlags flags; + + g_object_get (mEmbedPersist, "flags", &flags, (char *) NULL); + + if (flags & EPHY_EMBED_PERSIST_NO_CERTDIALOGS) + { + nsIBadCertListener *badCertRejector = new EphyBadCertRejector (); + if (!badCertRejector) return NS_ERROR_OUT_OF_MEMORY; + + *result = badCertRejector; + NS_ADDREF (badCertRejector); + + return NS_OK; + } + } + + return NS_ERROR_NO_INTERFACE; +} + +void +MozDownload::Cancel() +{ + if (mDownloadState != EPHY_DOWNLOAD_DOWNLOADING && + mDownloadState != EPHY_DOWNLOAD_PAUSED) + { + return; + } + + if (mCancelable) + { + /* FIXME: error code? */ + mCancelable->Cancel (NS_BINDING_ABORTED); + } +} + +void +MozDownload::Pause() +{ + if (mRequest) + { + mRequest->Suspend (); + mDownloadState = EPHY_DOWNLOAD_PAUSED; + } +} + +void +MozDownload::Resume() +{ + if (mRequest) + { + mRequest->Resume (); + mDownloadState = EPHY_DOWNLOAD_DOWNLOADING; + } +} + +nsresult InitiateMozillaDownload (nsIDOMDocument *domDocument, nsIURI *sourceURI, + nsILocalFile* inDestFile, const char *contentType, + nsIURI* inOriginalURI, MozillaEmbedPersist *embedPersist, + nsIInputStream *postData, nsISupports *aCacheKey, + PRInt64 aMaxSize) +{ + nsresult rv = NS_OK; + + EphyEmbedPersistFlags ephy_flags; + ephy_flags = ephy_embed_persist_get_flags (EPHY_EMBED_PERSIST (embedPersist)); + + if (!ephy_embed_persist_get_dest (EPHY_EMBED_PERSIST (embedPersist))) + { + nsCString cPath; + inDestFile->GetNativePath (cPath); + + ephy_embed_persist_set_dest (EPHY_EMBED_PERSIST (embedPersist), + cPath.get()); + } + + nsCOMPtr<nsIMIMEService> mimeService (do_GetService ("@mozilla.org/mime;1")); + nsCOMPtr<nsIMIMEInfo> mimeInfo; + if (mimeService) + { + mimeService->GetFromTypeAndExtension (nsCString(contentType), + nsCString(), + getter_AddRefs (mimeInfo)); + } + + PRBool isHTML = (contentType && + (strcmp (contentType, "text/html") == 0 || + strcmp (contentType, "text/xml") == 0 || + strcmp (contentType, "application/xhtml+xml") == 0)); + + nsCOMPtr<nsIWebBrowserPersist> webPersist (do_CreateInstance(persistContractID, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + PRInt64 timeNow = PR_Now(); + + nsString fileDisplayName; + inDestFile->GetLeafName(fileDisplayName); + + nsCOMPtr<nsIIOService> ioService; + rv = EphyUtils::GetIOService (getter_AddRefs (ioService)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIURI> destURI; + ioService->NewFileURI (inDestFile, getter_AddRefs(destURI)); + + MozDownload *downloader = new MozDownload (); + /* dlListener attaches to its progress dialog here, which gains ownership */ + /* FIXME is that still true? */ + rv = downloader->InitForEmbed (inOriginalURI, destURI, fileDisplayName, + mimeInfo, timeNow, nsnull, webPersist, embedPersist, aMaxSize); + NS_ENSURE_SUCCESS (rv, rv); + + rv = webPersist->SetProgressListener (downloader); + NS_ENSURE_SUCCESS (rv, rv); + + PRInt32 flags = nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES; + + if (!domDocument && !isHTML && !(ephy_flags & EPHY_EMBED_PERSIST_COPY_PAGE) && + !(ephy_flags & EPHY_EMBED_PERSIST_DO_CONVERSION)) + { + flags |= nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION; + } + if (ephy_flags & EPHY_EMBED_PERSIST_COPY_PAGE) + { + flags |= nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE; + } + webPersist->SetPersistFlags(flags); + + /* Create a new tagged channel if we need to block cookies from server */ + if (ephy_flags & EPHY_EMBED_PERSIST_NO_COOKIES) + { + nsCOMPtr<nsIChannel> tmpChannel; + rv = ioService->NewChannelFromURI (sourceURI, getter_AddRefs (tmpChannel)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(tmpChannel); + rv = props->SetPropertyAsBool (NS_LITERAL_STRING("epiphany-blocking-cookies"), PR_TRUE); + NS_ENSURE_SUCCESS (rv, rv); + + rv = webPersist->SaveChannel (tmpChannel, inDestFile); + } + else if (!domDocument || !isHTML || ephy_flags & EPHY_EMBED_PERSIST_COPY_PAGE) + { + rv = webPersist->SaveURI (sourceURI, aCacheKey, nsnull, + postData, nsnull, inDestFile); + } + else + { + PRInt32 encodingFlags = 0; + nsCOMPtr<nsILocalFile> filesFolder; + + /** + * Construct a directory path to hold the associated files; mozilla + * will create the directory as needed. + */ + + nsCString cPath; + inDestFile->GetNativePath (cPath); + + char *basename = g_path_get_basename (cPath.get()); + char *dirname = g_path_get_dirname (cPath.get()); + char *dot_pos = strchr (basename, '.'); + if (dot_pos) + { + *dot_pos = 0; + } + /* translators: this is the directory name to store auxilary files when saving html files */ + char *new_basename = g_strdup_printf (_("%s Files"), basename); + char *new_path = g_build_filename (dirname, new_basename, NULL); + g_free (new_basename); + g_free (basename); + g_free (dirname); + + filesFolder = do_CreateInstance ("@mozilla.org/file/local;1"); + filesFolder->InitWithNativePath (nsCString(new_path)); + + g_free (new_path); + + rv = webPersist->SaveDocument (domDocument, inDestFile, filesFolder, + contentType, encodingFlags, 80); + } + + return rv; +} + +static char* +GetFilePath (const char *filename) +{ + const char *home_dir; + char *download_dir, *path; + + download_dir = ephy_file_get_downloads_dir (); + + if (ephy_ensure_dir_exists (download_dir, NULL)) + { + path = g_build_filename (download_dir, filename, (char *) NULL); + } + else + { + home_dir = g_get_home_dir (); + path = g_build_filename (home_dir ? home_dir : "/", filename, (char *) NULL); + } + g_free (download_dir); + + return path; +} + +static const char* +file_is_compressed (const char *filename) +{ + int i; + static const char * const compression[] = {".gz", ".bz2", ".Z", ".lz", NULL}; + + for (i = 0; compression[i] != NULL; i++) + { + if (g_str_has_suffix (filename, compression[i])) + return compression[i]; + } + + return NULL; +} + +static const char* +parse_extension (const char *filename) +{ + const char *compression; + + compression = file_is_compressed (filename); + + /* If the file is compressed we might have a double extension */ + if (compression != NULL) + { + int i; + static const char * const extensions[] = {"tar", "ps", "xcf", "dvi", "txt", "text", NULL}; + + for (i = 0; extensions[i] != NULL; i++) + { + char *suffix; + suffix = g_strdup_printf (".%s%s", extensions[i], + compression); + + if (g_str_has_suffix (filename, suffix)) + { + char *p; + + p = g_strrstr (filename, suffix); + g_free (suffix); + + return p; + } + + g_free (suffix); + } + } + + /* default case */ + return g_strrstr (filename, "."); +} + +nsresult BuildDownloadPath (const char *defaultFileName, nsILocalFile **_retval) +{ + char *path; + + path = GetFilePath (defaultFileName); + + if (g_file_test (path, G_FILE_TEST_EXISTS)) + { + int i = 1; + const char *dot_pos; + char *serial = NULL; + GString *tmp_path; + gssize position; + + dot_pos = parse_extension (defaultFileName); + if (dot_pos) + { + position = dot_pos - defaultFileName; + } + else + { + position = strlen (defaultFileName); + } + tmp_path = g_string_new (NULL); + + do { + g_free (path); + g_string_assign (tmp_path, defaultFileName); + serial = g_strdup_printf ("(%d)", i++); + g_string_insert (tmp_path, position, serial); + g_free (serial); + path = GetFilePath (tmp_path->str); + + } while (g_file_test (path, G_FILE_TEST_EXISTS)); + + g_string_free (tmp_path, TRUE); + } + + nsCOMPtr <nsILocalFile> destFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); + NS_ENSURE_TRUE (destFile, NS_ERROR_FAILURE); + + destFile->InitWithNativePath (nsCString (path)); + g_free (path); + + NS_IF_ADDREF (*_retval = destFile); + return NS_OK; +} diff --git a/embed/xulrunner/components/MozDownload.h b/embed/xulrunner/components/MozDownload.h new file mode 100644 index 000000000..27fef41c7 --- /dev/null +++ b/embed/xulrunner/components/MozDownload.h @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright © 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen <ccarlen@netscape.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + * + * $Id$ + */ + +#ifndef MozDownload_h__ +#define MozDownload_h__ + +#include <libgnomevfs/gnome-vfs-mime-handlers.h> + +#include <nsCOMPtr.h> +#include <nsIInterfaceRequestor.h> +#include <nsITransfer.h> +#include <nsIWebProgressListener.h> + +#include "mozilla-embed-persist.h" +#include "downloader-view.h" +#include "ephy-download.h" +#include "ephy-embed-shell.h" + +class nsICancelable; +class nsIDOMDocument; +class nsIInputStream; +class nsILocalFile; +class nsIMIMEInfo; +class nsIObserver; +class nsIRequest; +class nsIURI; +class nsIWebBrowserPersist; + +/* MozDownload + Holds information used to display a single download in the UI. This object is + created in one of two ways: + (1) By nsExternalHelperAppHandler when Gecko encounters a MIME type which + it doesn't itself handle. In this case, the notifications sent to + nsIDownload are controlled by nsExternalHelperAppHandler. + (2) By the embedding app's file saving code when saving a web page or a link + target. See CHeaderSniffer.cpp. In this case, the notifications sent to + nsIDownload are controlled by the implementation of nsIWebBrowserPersist. +*/ + +#define MOZ_DOWNLOAD_CID \ +{ /* d2a2f743-f126-4f1f-1234-d4e50490f112 */ \ + 0xd2a2f743, \ + 0xf126, \ + 0x4f1f, \ + {0x12, 0x34, 0xd4, 0xe5, 0x04, 0x90, 0xf1, 0x12} \ +} + +#define MOZ_DOWNLOAD_CLASSNAME "Ephy's Download Progress Dialog" + +nsresult InitiateMozillaDownload (nsIDOMDocument *domDocument, nsIURI *sourceUri, + nsILocalFile* inDestFile, const char *contentType, + nsIURI* inOriginalURI, MozillaEmbedPersist *embedPersist, + nsIInputStream *postData, nsISupports *aCacheKey, + PRInt64 aMaxSize); +nsresult BuildDownloadPath (const char *defaultFileName, nsILocalFile **_retval); + +class MozDownload : public nsITransfer, + public nsIInterfaceRequestor +{ +public: + MozDownload(); + virtual ~MozDownload(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIWEBPROGRESSLISTENER + NS_DECL_NSIWEBPROGRESSLISTENER2 + NS_DECL_NSITRANSFER + NS_DECL_NSIINTERFACEREQUESTOR + + nsresult GetMIMEInfo (nsIMIMEInfo **aMIMEInfo); + nsresult GetTargetFile (nsILocalFile **aFile); + nsresult GetSource(nsIURI * *aSource); + nsresult GetPercentComplete(PRInt32 *aPercentComplete); + + virtual void Cancel(); + virtual void Pause(); + virtual void Resume(); + + nsresult GetState (EphyDownloadState *aDownloadState); + nsresult GetCurrentProgress (PRInt64 *aCurrentProgress); + nsresult GetTotalProgress (PRInt64 *aTProgress); + nsresult GetElapsedTime (PRInt64 *aTProgress); + + nsresult InitForEmbed (nsIURI *aSource, nsIURI *aTarget, + const nsAString &aDisplayName, nsIMIMEInfo *aMIMEInfo, + PRTime aStartTime, nsILocalFile *aTempFile, + nsICancelable *aCancelable, MozillaEmbedPersist *aEmbedPersist, + PRInt64 aMaxSize); + +protected: + nsCOMPtr<nsIURI> mSource; + nsCOMPtr<nsIURI> mDestination; + + nsCOMPtr<nsIMIMEInfo> mMIMEInfo; + PRTime mStartTime; + PRTime mLastUpdate; + PRInt64 mElapsed; + PRInt32 mInterval; + PRInt32 mPercentComplete; + PRInt64 mTotalProgress; + PRInt64 mCurrentProgress; + PRInt64 mMaxSize; + PRBool mAddToRecent; + + nsresult mStatus; + + nsCOMPtr<nsICancelable> mCancelable; + nsCOMPtr<nsIRequest> mRequest; + EphyDownload *mEphyDownload; + DownloaderView *mDownloaderView; + MozillaEmbedPersist *mEmbedPersist; + EphyDownloadState mDownloadState; +}; + +#endif // MozDownload_h__ diff --git a/embed/xulrunner/components/MozRegisterComponents.cpp b/embed/xulrunner/components/MozRegisterComponents.cpp new file mode 100644 index 000000000..0ff0014d7 --- /dev/null +++ b/embed/xulrunner/components/MozRegisterComponents.cpp @@ -0,0 +1,330 @@ +/* + * Copyright © 2001,2002,2003 Philip Langdale + * Copyright © 2003 Marco Pesenti Gritti + * Copyright © 2004, 2005, 2006, 2007 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/gmessages.h> + +#include <nsStringGlue.h> + +#include <nsComponentManagerUtils.h> +#include <nsCOMPtr.h> +#include <nsCURILoader.h> +#include <nsDocShellCID.h> +#include <nsICategoryManager.h> +#include <nsIClassInfoImpl.h> +#include <nsIComponentManager.h> +#include <nsIComponentRegistrar.h> +#include <nsIGenericFactory.h> +#include <nsILocalFile.h> +#include <nsIScriptNameSpaceManager.h> +#include <nsIServiceManager.h> +#include <nsMemory.h> +#include <nsNetCID.h> +#include <nsServiceManagerUtils.h> + +#define HAVE_MOZILLA_PSM + +#ifdef HAVE_MOZILLA_PSM +#include <nsISecureBrowserUI.h> +#endif + +#include "ContentHandler.h" +#include "EphyAboutModule.h" +#include "EphyContentPolicy.h" +#include "EphySidebar.h" +#include "GeckoCookiePromptService.h" +#include "GeckoPrintService.h" +#include "GeckoPrintSession.h" +#include "GeckoPromptService.h" +#include "GlobalHistory.h" +#include "MozDownload.h" + +#ifdef ENABLE_FILEPICKER +#include "FilePicker.h" +#endif + +#ifdef ENABLE_SPELLCHECKER +#include "GeckoSpellCheckEngine.h" +#endif + +#ifdef HAVE_MOZILLA_PSM +#include "GtkNSSClientAuthDialogs.h" +#include "GtkNSSDialogs.h" +#include "GtkNSSKeyPairDialogs.h" +#include "GtkNSSSecurityWarningDialogs.h" +#include "GeckoFormSigningDialog.h" +#endif + +NS_GENERIC_FACTORY_CONSTRUCTOR(EphyAboutModule) +NS_GENERIC_FACTORY_CONSTRUCTOR(EphyContentPolicy) +NS_GENERIC_FACTORY_CONSTRUCTOR(EphySidebar) +NS_GENERIC_FACTORY_CONSTRUCTOR(GContentHandler) +NS_GENERIC_FACTORY_CONSTRUCTOR(GeckoCookiePromptService) +NS_GENERIC_FACTORY_CONSTRUCTOR(GeckoPrintService) +NS_GENERIC_FACTORY_CONSTRUCTOR(GeckoPrintSession) +NS_GENERIC_FACTORY_CONSTRUCTOR(GeckoPromptService) +NS_GENERIC_FACTORY_CONSTRUCTOR(MozDownload) +NS_GENERIC_FACTORY_CONSTRUCTOR(MozGlobalHistory) + +#ifdef ENABLE_FILEPICKER +NS_GENERIC_FACTORY_CONSTRUCTOR(GFilePicker) +#endif + +#ifdef ENABLE_SPELLCHECKER +NS_GENERIC_FACTORY_CONSTRUCTOR(GeckoSpellCheckEngine) +#endif + +#ifdef HAVE_MOZILLA_PSM +NS_GENERIC_FACTORY_CONSTRUCTOR(GtkNSSClientAuthDialogs) +NS_GENERIC_FACTORY_CONSTRUCTOR(GtkNSSDialogs) +NS_GENERIC_FACTORY_CONSTRUCTOR(GtkNSSKeyPairDialogs) +NS_GENERIC_FACTORY_CONSTRUCTOR(GtkNSSSecurityWarningDialogs) +NS_GENERIC_FACTORY_CONSTRUCTOR(GeckoFormSigningDialog) +#endif + +#define XPINSTALL_CONTRACTID NS_CONTENT_HANDLER_CONTRACTID_PREFIX "application/x-xpinstall" + +/* class information */ +NS_DECL_CLASSINFO(EphySidebar) + +/* FIXME: uninstall XPI handler */ + +static const nsModuleComponentInfo sAppComps[] = { + { + MOZ_DOWNLOAD_CLASSNAME, + MOZ_DOWNLOAD_CID, +#ifdef NS_TRANSFER_CONTRACTID + NS_TRANSFER_CONTRACTID, +#else + NS_DOWNLOAD_CONTRACTID, +#endif + MozDownloadConstructor + }, +#ifdef ENABLE_FILEPICKER + { + G_FILEPICKER_CLASSNAME, + G_FILEPICKER_CID, + G_FILEPICKER_CONTRACTID, + GFilePickerConstructor + }, +#endif +#ifdef HAVE_MOZILLA_PSM + { + GTK_NSSCLIENTAUTHDIALOGS_CLASSNAME, + GTK_NSSCLIENTAUTHDIALOGS_CID, + NS_CLIENTAUTHDIALOGS_CONTRACTID, + GtkNSSClientAuthDialogsConstructor + }, + { + GTK_NSSDIALOGS_CLASSNAME, + GTK_NSSDIALOGS_CID, + NS_BADCERTLISTENER_CONTRACTID, + GtkNSSDialogsConstructor + }, + { + GTK_NSSDIALOGS_CLASSNAME, + GTK_NSSDIALOGS_CID, + NS_CERTIFICATEDIALOGS_CONTRACTID, + GtkNSSDialogsConstructor + }, + { + GTK_NSSDIALOGS_CLASSNAME, + GTK_NSSDIALOGS_CID, + NS_DOMCRYPTODIALOGS_CONTRACTID, + GtkNSSDialogsConstructor + }, + { + GTK_NSSDIALOGS_CLASSNAME, + GTK_NSSDIALOGS_CID, + NS_TOKENDIALOGS_CONTRACTID, + GtkNSSDialogsConstructor + }, + { + GTK_NSSDIALOGS_CLASSNAME, + GTK_NSSDIALOGS_CID, + NS_TOKENPASSWORDSDIALOG_CONTRACTID, + GtkNSSDialogsConstructor + }, + { + GTK_NSSKEYPAIRDIALOGS_CLASSNAME, + GTK_NSSKEYPAIRDIALOGS_CID, + NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID, + GtkNSSKeyPairDialogsConstructor + }, + { + GTK_NSSSECURITYWARNINGDIALOGS_CLASSNAME, + GTK_NSSSECURITYWARNINGDIALOGS_CID, + NS_SECURITYWARNINGDIALOGS_CONTRACTID, + GtkNSSSecurityWarningDialogsConstructor + }, + { + GECKO_FORMSIGNINGDIALOGS_CLASSNAME, + GECKO_FORMSIGNINGDIALOGS_CID, + NS_FORMSIGNINGDIALOG_CONTRACTID, + GeckoFormSigningDialogConstructor + }, +#endif /* HAVE_MOZILLA_PSM */ + { + NS_IHELPERAPPLAUNCHERDLG_CLASSNAME, + G_CONTENTHANDLER_CID, + NS_IHELPERAPPLAUNCHERDLG_CONTRACTID, + GContentHandlerConstructor + }, + { + EPHY_GLOBALHISTORY_CLASSNAME, + EPHY_GLOBALHISTORY_CID, + NS_GLOBALHISTORY2_CONTRACTID, + MozGlobalHistoryConstructor + }, + { + GECKO_PRINT_SERVICE_CLASSNAME, + GECKO_PRINT_SERVICE_IID, + "@mozilla.org/embedcomp/printingprompt-service;1", + GeckoPrintServiceConstructor + }, + { + GECKO_PRINT_SESSION_CLASSNAME, + GECKO_PRINT_SESSION_IID, + "@mozilla.org/gfx/printsession;1", + GeckoPrintSessionConstructor + }, + { + EPHY_CONTENT_POLICY_CLASSNAME, + EPHY_CONTENT_POLICY_CID, + EPHY_CONTENT_POLICY_CONTRACTID, + EphyContentPolicyConstructor, + EphyContentPolicy::Register, + EphyContentPolicy::Unregister + }, + { + EPHY_SIDEBAR_CLASSNAME, + EPHY_SIDEBAR_CID, + NS_SIDEBAR_CONTRACTID, + EphySidebarConstructor, + EphySidebar::Register, + EphySidebar::Unregister, + nsnull /* no factory destructor */, + NS_CI_INTERFACE_GETTER_NAME(EphySidebar), + nsnull /* no language helper */, + &NS_CLASSINFO_NAME(EphySidebar), + nsIClassInfo::DOM_OBJECT + }, + { + EPHY_ABOUT_EPIPHANY_CLASSNAME, + EPHY_ABOUT_MODULE_CID, + EPHY_ABOUT_EPIPHANY_CONTRACTID, + EphyAboutModuleConstructor + }, + { + EPHY_ABOUT_RECOVER_CLASSNAME, + EPHY_ABOUT_MODULE_CID, + EPHY_ABOUT_RECOVER_CONTRACTID, + EphyAboutModuleConstructor + }, + { + EPHY_ABOUT_NETERROR_CLASSNAME, + EPHY_ABOUT_MODULE_CID, + EPHY_ABOUT_NETERROR_CONTRACTID, + EphyAboutModuleConstructor + }, + { + GECKO_PROMPT_SERVICE_CLASSNAME, + GECKO_PROMPT_SERVICE_CID, + "@mozilla.org/embedcomp/prompt-service;1", + GeckoPromptServiceConstructor + }, + { + GECKO_PROMPT_SERVICE_CLASSNAME, + GECKO_PROMPT_SERVICE_CID, + "@mozilla.org/embedcomp/nbalert-service;1", + GeckoPromptServiceConstructor + }, +#ifdef ENABLE_SPELLCHECKER + { + GECKO_SPELL_CHECK_ENGINE_CLASSNAME, + GECKO_SPELL_CHECK_ENGINE_IID, + GECKO_SPELL_CHECK_ENGINE_CONTRACTID, + GeckoSpellCheckEngineConstructor + }, +#endif /* ENABLE_SPELLCHECK */ + { + EPHY_COOKIEPROMPTSERVICE_CLASSNAME, + EPHY_COOKIEPROMPTSERVICE_CID, + EPHY_COOKIEPROMPTSERVICE_CONTRACTID, + GeckoCookiePromptServiceConstructor + } +}; + +gboolean +mozilla_register_components (void) +{ + gboolean ret = TRUE; + nsresult rv; + + nsCOMPtr<nsIComponentRegistrar> cr; + NS_GetComponentRegistrar(getter_AddRefs(cr)); + NS_ENSURE_TRUE (cr, FALSE); + + nsCOMPtr<nsIComponentManager> cm; + NS_GetComponentManager (getter_AddRefs (cm)); + NS_ENSURE_TRUE (cm, FALSE); + + for (guint i = 0; i < G_N_ELEMENTS (sAppComps); i++) + { + nsCOMPtr<nsIGenericFactory> componentFactory; + rv = NS_NewGenericFactory(getter_AddRefs(componentFactory), + &(sAppComps[i])); + if (NS_FAILED(rv) || !componentFactory) + { + g_warning ("Failed to make a factory for %s\n", sAppComps[i].mDescription); + + ret = FALSE; + continue; // don't abort registering other components + } + + rv = cr->RegisterFactory(sAppComps[i].mCID, + sAppComps[i].mDescription, + sAppComps[i].mContractID, + componentFactory); + if (NS_FAILED(rv)) + { + g_warning ("Failed to register %s\n", sAppComps[i].mDescription); + + ret = FALSE; + } + + if (sAppComps[i].mRegisterSelfProc) + { + rv = sAppComps[i].mRegisterSelfProc (cm, nsnull, nsnull, nsnull, &sAppComps[i]); + + if (NS_FAILED (rv)) + { + g_warning ("Failed to register-self for %s\n", sAppComps[i].mDescription); + ret = FALSE; + } + } + } + + return ret; +} diff --git a/embed/xulrunner/components/MozRegisterComponents.h b/embed/xulrunner/components/MozRegisterComponents.h new file mode 100644 index 000000000..e8e086f3b --- /dev/null +++ b/embed/xulrunner/components/MozRegisterComponents.h @@ -0,0 +1,28 @@ +/* + * Copyright © 2001 Philip Langdale + * + * 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$ + */ + +#ifndef MOZREGISTERCOMPONENTS_H +#define MOZREGISTERCOMPONENTS_H + +#include <glib.h> + +gboolean mozilla_register_components (void); + +#endif /* MOZREGISTERCOMPONENTS_H */ |