aboutsummaryrefslogblamecommitdiffstats
path: root/embed/mozilla/ContentHandler.cpp
blob: ead2ea1387a546789c150511a4cc31ff0d2e5b9e (plain) (tree)
1
2
3
4
5

                                      


                                           













                                                                              

        

   

                           
                   
 

                           
                          

                                 

                          
                        
                                       
                                        
                       
 
                     



                                       
                                      
                         
                       
 



                              



                              
                              
                             
                     


                                 
                        
                      
 


                                                                                      
                     
                                  
              
 
                                                
 
     


                                  
 
                                                

      


                                   
                                                
 
                      



                                           
      

 
                                                               
 
                     





                                                                                                       
                                                                             




                                                       

                    

                                 
 

                                                                   
                            
                              
                     
                                   
 
                                                                                     
                     
                                                                         
                                                     
     
                                                                   


                                                     
                     
         
                                      


            
                     

                                                       
                                     
      
         




                                                                                                                                    
                                                   
                                                                                            
                                                                



                                                                             

                                
                              



                                                                  


                                             
                                                                      

         
                                                                                  
                                                                                          

                                                                
                                                                     

                                                              
                                                                                         
                                                                                         
 





                                                                                                      


                                                                                    
                                            
          
         

                                                                
                                                                                     
                                                
                                                                                       
 

                                            
                                                                                               

                                                            
                                                                         

                                  

                                                   

                                                         



                             
                                                         
                                  
 

                                        

 
                                  


                    

                                                     
                                       


                                                          
                     
                                               

                                                
      
 


                                                   
        
                            
 


                     
























                                                         
                                               
 
                                           
                                 


                                     
                                                                            
                                                                                         
 

                                                              
                                                             
 

















                                                                                         
                                                            







                                                             
                                                             


                                           
                                                          

                                                                     
                                                   



                 
                                                                   
         


                                                                      

                                                                      

                                                        

                                                                              
                                                                             


                                                                               
         
                                                                  
         

                                                                      
                                                                

                                               
                                                        
                                                     




                                                                                                 


            

                                                                      
                                                                

                                                   

                                                        




                                                                                           
         
        


                                                                                   
                                                   


                                                                                          







                                                                               
 



                                                                               



                                                                                 
                                                 



                     
                                                    
 


                                                                                     
 
                                                                     
 
                     
                                                                              
                                                             

                                                                        
                                                       
      
 












                                                                                        







                                                  
 
                                                                    
         

                                                  
 
                                                                  
         
                                     
         






                                
 

                                              


                                                                            



                                                         
                                           
         









                                                                                                 
 

                                                         
                                                                   
                              
 


                                                                 
                     
                                                           
     
                                                                 
      
         

            
                     
                                                                       
     
                                                             
      

         
                                           
         
                                                         


                                                    
                                                                    
         

                                                
                     

                                                       
                                     
      

            
         
                                                         
         
 
                     
 
/*
 *  Copyright (C) 2001 Philip Langdale
 *  Copyright (C) 2003 Marco Pesenti Gritti
 *  Copyright (C) 2003 Xan Lopez
 *  Copyright (C) 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *  $Id$
 */

#include "mozilla-config.h"

#include "config.h"

#include "ContentHandler.h"

#include <gtk/gtkdialog.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkstock.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkmain.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <libgnomevfs/gnome-vfs-utils.h>
#include <glib/gi18n.h>

#include <nsMemory.h>
#include <nsIURL.h>
#include <nsILocalFile.h>
#include <nsIMIMEInfo.h>
#include <nsIInterfaceRequestorUtils.h>
#include <nsCExternalHandlerService.h>
#include <nsIDOMWindow.h>
#include <nsNetError.h>

#ifdef ALLOW_PRIVATE_API
#include <nsIServiceManager.h>
#endif

#include "ephy-prefs.h"
#include "ephy-embed-single.h"
#include "ephy-embed-shell.h"
#include "ephy-file-chooser.h"
#include "ephy-file-helpers.h"
#include "ephy-stock-icons.h"
#include "ephy-gui.h"
#include "ephy-debug.h"
#include "eel-gconf-extensions.h"

#include "MozDownload.h"
#include "EphyUtils.h"

/* FIXME: we don't generally have a timestamp for the user action which initiated this
 * content handler.
 */
#ifdef HAVE_GECKO_1_8
GContentHandler::GContentHandler()
: mUserTime(0)
{
    LOG ("GContentHandler ctor (%p)", this);
}
#else
GContentHandler::GContentHandler()
: mMimeType(nsnull)
, mUserTime(0)
{
    LOG ("GContentHandler ctor (%p)", this);
}
#endif

GContentHandler::~GContentHandler()
{
    LOG ("GContentHandler dtor (%p)", this);

#ifndef HAVE_GECKO_1_8
    if (mMimeType)
    {
        nsMemory::Free (mMimeType);
    }
#endif
}

NS_IMPL_ISUPPORTS1(GContentHandler, nsIHelperAppLauncherDialog)

#ifdef HAVE_GECKO_1_8
/* void show (in nsIHelperAppLauncher aLauncher, in nsISupports aContext, in unsigned long aReason); */
NS_IMETHODIMP
GContentHandler::Show (nsIHelperAppLauncher *aLauncher,
               nsISupports *aContext,
               PRUint32 aReason)
#else
/* void show (in nsIHelperAppLauncher aLauncher, in nsISupports aContext); */
NS_IMETHODIMP
GContentHandler::Show (nsIHelperAppLauncher *aLauncher,
               nsISupports *aContext,
               PRBool aForced)
#endif
{
    nsresult rv;
    EphyEmbedSingle *single;
    gboolean handled = FALSE;

    /* FIXME: handle aForced / aReason argument in some way? */

    mContext = aContext;
    mLauncher = aLauncher;
    rv = Init ();
    NS_ENSURE_SUCCESS (rv, rv);

    single = EPHY_EMBED_SINGLE (ephy_embed_shell_get_embed_single (embed_shell));
#ifdef HAVE_GECKO_1_8
    g_signal_emit_by_name (single, "handle_content", mMimeType.get(),
                   mUrl.get(), &handled);
#else
    g_signal_emit_by_name (single, "handle_content", mMimeType,
                   mUrl.get(), &handled);
#endif

    if (!handled)
    {
        MIMEInitiateAction ();
    }
    else
    {
#ifdef HAVE_GECKO_1_8
        mLauncher->Cancel (NS_BINDING_ABORTED);
#else
        mLauncher->Cancel ();
#endif
    }

    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;
    gint response;
    char *filename = NULL;
    nsEmbedCString defaultFile;

    NS_UTF16ToCString (nsEmbedString (aDefaultFile),
               NS_CSTRING_ENCODING_UTF8, defaultFile);

    if (mAction != CONTENT_ACTION_SAVEAS)
    {
        return BuildDownloadPath (defaultFile.get(), _retval);
    }

    nsCOMPtr<nsIDOMWindow> parentDOMWindow = do_GetInterface (aWindowContext);
    GtkWidget *parentWindow = GTK_WIDGET (EphyUtils::FindGtkParent (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 (nsEmbedCString (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);

#ifdef HAVE_GECKO_1_8
    rv = MIMEInfo->GetMIMEType (mMimeType);
#else
    rv = MIMEInfo->GetMIMEType (&mMimeType);
#endif

    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;
    nsEmbedCString 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 : EPHY_STOCK_DOWNLOAD;

#ifdef HAVE_GECKO_1_8
    mime_description = gnome_vfs_mime_get_description (mMimeType.get());
#else
    mime_description = gnome_vfs_mime_get_description (mMimeType);
#endif
    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 ? */
#ifdef HAVE_GECKO_1_8
    {
        nsEmbedString suggested;
        
        mLauncher->GetSuggestedFileName (suggested);
        NS_UTF16ToCString (
            suggested,
            NS_CSTRING_ENCODING_UTF8, file_name);
    }
#else
    {
        PRUnichar *suggested = nsnull;
            
        mLauncher->GetSuggestedFileName (&suggested);
        if (suggested != nsnull)
        {
            NS_UTF16ToCString (
                nsEmbedString (suggested),
                NS_CSTRING_ENCODING_UTF8, file_name);
                
            nsMemory::Free (suggested);
        }
    }
#endif

    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, CONTENT_ACTION_NONE);
    gtk_dialog_add_button (GTK_DIALOG (dialog),
                   action_label, mAction);

    gtk_window_set_icon_name (GTK_WINDOW (dialog), "web-browser");

    gtk_dialog_set_default_response (GTK_DIALOG (dialog), (guint) mAction);

    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);

#ifdef HAVE_GECKO_1_8
    mHelperApp = gnome_vfs_mime_get_default_application (mMimeType.get());
    mPermission = ephy_file_check_mime (mMimeType.get());
#else
    mHelperApp = gnome_vfs_mime_get_default_application (mMimeType);
    mPermission = ephy_file_check_mime (mMimeType);
#endif

    /* 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);

        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);

    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)
         */
        char *info;
        info = g_strdup_printf ("gnome-default:%d:%s", gtk_get_current_event_time(), id);

        nsEmbedString desc;
        NS_CStringToUTF16 (nsEmbedCString (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 */
#ifdef HAVE_GECKO_1_8
        mimeInfo->SetApplicationDescription (desc);
#else
        mimeInfo->SetApplicationDescription (desc.get());
#endif
    }
    else
    {
#ifdef HAVE_GECKO_1_8
        mimeInfo->SetApplicationDescription (nsEmbedString ());
#else
        mimeInfo->SetApplicationDescription (nsnull);
#endif
    }

    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)
    {
#ifdef HAVE_GECKO_1_8
        mLauncher->Cancel (NS_BINDING_ABORTED);
#else
        mLauncher->Cancel ();
#endif
    }
    else
    {
        mLauncher->SaveToDisk (nsnull, PR_FALSE);
    }

    return NS_OK;
}