aboutsummaryrefslogblamecommitdiffstats
path: root/embed/mozilla/mozilla-embed.cpp
blob: e0622ccad13f4c08bf0c5e336e178b944b6635ab (plain) (tree)
1
2
3
  

                                               












                                                                        
                                                                                  

        

   
                           
                   
 
                        
 
                        

                             
                       
                   
                             
                                   

                     



                                      



                         
                            



                                 
                             
                               
                         



                                
 


                                                                           
                                                                  
                                                                  
                                                                        
 






                                                                       
                                                                    

                                                                       
                                                                    

                                                                       


                                                                       

                                                                        
                                                                   


                                                                       

                                                                       


                                                                            


                                                                       
                                                                    
                                                                       
                                                                    





                                                                       
                                                                         



                                                                      
                                                                      
                                                                     
                                                                     


                                                                       






                                                                       
                                                                      


                                                                                
 
                                                                            
 

                                                                                                                          







                                       
                                 
                                                    

                                                                                      
 

                          
                             
                               
                                         
 

                                              

                                              
                                            
                                           
                   

                   
                           
                             
                                  
 
                           

                            




                             
                           

                           



                                       



                                 



                                                                          

    
               
                     
                           
                                


                          
                           
                         
                        
                            
                      
                            
                   
                           
                 

  






                       
           


                                                     
                                                                  
 
                                            

 
               

                                                         
 
                                                                  
                    
                       
 
                                                                 
 
                                                   


           
                                                                
 

                                                            
 
 
                                                                           



                                                                                 
        
           
                                            
 











                                                                                 

 
           







                                                                 



                                                                  
                                                                        
 
                                                           
                                         
                    
                                                        
                           
         

                                                                                 


         



                                                                    

                                   


                                                                   
                                                                                                      
                                                                                           

 
           






                                                     


                        


                                                          


                                                              



                                                                                    








                                                               


                                                            


                                                              


                                                           



                                                                                      


                                                               





                                                                 


                                                                


                                                      



                                                                           




                                            


                                                
 

                        


                                                                                                    



                                                                                        


                                                                                                   
                          
                                
                                     
                       
                                
                              
                               
                             
                           
                                 
                       





                                                                           


           

                                                   


                                                                     
 
                                                              
                                                        
                                                      

                                                                
 
                                                          
 
                                                            
                                                      
 
                                                                                             
                                                                                         
                                                                           
                                                                                             
                                                                                         
                                                                                       


                                                                                             
                                                                                               
                                                                                           

                                                                                           

                                                                                                       
 
                                                                             


           
                                         
 
                                
                                  









                                                          
                                           
 
                                                   
                                                                                
                                                            
                                                        
                                                                             
                                                            
                                                          
                                                                               
                                                            
                                                         
                                                                              
                                                            
                                                        
                                                                             
                                                            
                                                     
                                                                          
                                                            
                                                          
                                                                               

                                                            
                                                                             

                                                            

                                                                           

                                                                            
                                                            

                                                                            

                                                           

                                                                       

                                                                        
                                                           




                                                                                  
                                                           
 






                                                           
                             
                                                             
                           
                                   


                                 

                                  

                                    

 







                                                                  


                                         
                                                     
 
                                 
         
                                                
         
        
                                                                        


           

                                       
                                                                   




                                                                      





                                                     

                                              

         





                                                   



                                     
                                           

                                            

                                            

                                             
 
                                                                       
 
                                     

 



















































































































































































































































































































                                                                                             
           
                                 
                               
 

                                                                        

 



                                                                      
                           











                                                      







                                                                        
                       




                                                                                                        
                             




                                                                                
           

                                 

                                                                        

 
               

                                   

                                                                        

 
               

                                      

                                                                        

 


                                                   
                                                 
 
                    
                           

                                                           
 


                                                                                      





                                                                      
                         


                                                               
 
                       
                                

                                                          
 



                                                               
         
                                                                   
                                                             



                                                      
                                                            
         
 
                                 

 
               

                                 

                                                     
                        
 
                                                        
                                          
 
                         
                                                                        
                         



                      

                                      
 
                                                     



                                                        

                                         
                    
                         
                                                                
         
                                            
                                           
         
 
                         
 
                                   

 
           

                               

                                                                        

                
           

                                  

                                                                        

 
           

                             
                                                     
                  

                                                    


                                
                             
                                                                    
                     
 


                                                       

 
                   
                                 
 
                                                  
 
 
             

                                     

                                                                        

 
             
                                     
                                     
 
                                                                
                    
 
                             

                     
                                                                           
         
            
         
                                                                                 
         
 



                                                

                                                                                                   
 
                                
                       

                                 
 
                      


                                                          

 
           
                               
                            
 
                                                                        
                                                         
 
                  


                                                                      
 
                                                 

 
           
                                 
                           
 
                             
                    
 

                                      
                                                      
                                           
 
                                     
 
                              


                                                                      

 
            
                                
 
                                                                
                


                                          
        
                              
         
                         
         

                   

 
           

                                    






                                                                

                                    





                                                                









                                                                

                                        
 
                                                                

                         
 


                                                        

 
           
                                         



                                            

                    
                      
                         
                                                                


                        
                                                           

         
                                                       
 
                                                                                 
 
                                                            
 

                  

                                                   

                                                                     





                                       

 

                                        
 
                                                                
                    
                         
 
                                                        
 
                                            

 
           
                                        
                              
 

                                                                
                                               

 
           













                                                                 
                                          

                                                       
 
                                                                 
 
                                                        
                                             
 
                    
                       
                       
                                                            
                                   
 

                                                                 

 
           







                                                                 
                             
 
                                                                

                                 

 
           
                                                                     
 
                                                                
 
                                                           

 

                                             
 
                                                                
                    

                
                                                        
 
                                           

 
           
                                              
                                                               
                                      
 
                                                                
 
                                                         

 
           

                                        
 
                                                                
                    
                          
 

                                            

                                                   
 
                                                    

                                                                              

                                                              

         
                                               
                                                                      

 

                                    
 
                                                                
                    
                           
 
                                                    
 
                                                 
         

                            
 






                                                                
                    
                           
 
                                                          
 
                                                 




                            

 



                                                                
                    

                        
                                                             
 
                                                    

 







                                                                








                                                                
                                                                 
 
                                                








                                                                   








                                                                
                                                                    
 
                                                











                                                                 
           
                                                            
 
                                                 
                                               

                        
                                   



                                           
                                     



                                             
                                        


















                                                                  































                                                                    
                                                                



                                                             


                                          
                                                                

















                                                                
                                                                
 
                                  

 


                                    
                                                                
 
                              




                                         
                                                                
 
                                   

 


                                        
                                                                
 
                                  




                                
                                                                
 
                          

 
           
                                                              
 
                                                




                                           

                                                              













                                                                    
           














































                                                                      
                                                                








                                                             



                                                                   





























































                                                                                                  
                                                        







































                                                                                           
                                                   
 

                                                
                                    
        
                                                                                





                                                             


                                                        
                       
                                            
 

                                                                


















                                                                              
                                                                              



                                                                              

                          
                                                      
                                                      







                                                       
                                                        
                                                          
 
                                                                    

                                                              









                                                                     
 










                                                                                

                                                         














                                                      
                                                    


                                                
                                    














                                                 
                                                                    



                                                                            
 

                                                           
                                      







                                                 
                                               
                                              
                                             
 
                                                         

 



















































                                                                              
           


                                                    
                                                 
 






                                                                          





                                                              
                                                                 
                                                                             
                                                                   

                                 
 











                                                                      
                                                       









                                                                        
                                                                         
                                                                                     
                                                                           



                                                          
                                                                





                                                                  















































                                                                                        
                                                                     







                                                                          
                                                                     
































                                                                           


                                                   




















                                                                                

                                                                      










                                                          

                                                     











































                                                                             
                                                              





                                                    
                                                                     
                                                          

                                                     
                                                            
              
 


                            
                                              


                           







                                                                                   
                                                                             
                                               

          



                                                      
                                                                                                

                 

                                          
                                                                              
        
                                                                                           

 



                                                         
 
                                                 
                                
                                   
                                  
                    
 
                                            
 




                                                                                  
                                                                    
 
                                            
                                                                              
                           
         
                                      


                             
                                        

                                                                      
         
                                                            
 
                                                           
                                                             
                                                     

         


                              

 
















                                                                         




                                                     




















                                                                              

                                                              
 
                           
         

                                   


                           


                                                                                
                                                                                         

          
                          
 
                                                           
         
                                                           
                 
                                                            

                 
 
























                                                                             

                                     
 
                                                      

                                                                   

                                              


           



                                                                                    


                                             
 

                                                                     


           
                                                      
                                                      
                                                 

                                                       
                                                                                        

 












                                                                     






































                                                                               











                                                   





                                                                                   






                                                     

                                                     
 
                                                
























                                                                                        
                                     
 
                                                



























                                                                                
                                                        
                                                          

                                           
                    
 
                                                








                                                               

                                            

                                                















                                                                    
                             
                                             
 
                                     
 
                      






















                                                        

                     

           
                                             
 
                                         
                                 












                                                        

                                        


                                                  



                                                        
                                                  
                                                            
                                                                  
                                  
                                                

                                                                    
                                  
                                                                    

                                                                    
                                                            
                                                          
                                                        
                                                      
                                                                


                                                          




                                                            
 
 
           











                                                                            
           
                                                                          



                                                           





                                         
                                                                 
 
                                                   

 










                                                




                                    

                                   
                                                      
                                                                   
                                            




                                                                         



                                                          








                                                                 


                                                                


                                                            


                                     
/*
 *  Copyright © 2000-2004 Marco Pesenti Gritti
 *  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 "mozilla-config.h"
#include "config.h"

#include <nsStringAPI.h>

#include <gtkmozembed.h>
#include <nsIDOMKeyEvent.h>
#include <nsIDOMMouseEvent.h>
#include <nsIRequest.h>
#include <nsIURI.h>
#include <nsIWebNavigation.h>
#include <nsIWebProgressListener.h>
#include <nsMemory.h>

#include <glib/gi18n.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libgnomevfs/gnome-vfs-uri.h>

#include "EphyBrowser.h"
#include "EphyUtils.h"
#include "EventContext.h"

#include "ephy-base-embed.h"
#include "ephy-command-manager.h"
#include "ephy-debug.h"
#include "ephy-embed-shell.h"
#include "ephy-embed-single.h"
#include "ephy-embed-utils.h"
#include "ephy-favicon-cache.h"
#include "ephy-history.h"
#include "ephy-string.h"
#include "mozilla-embed-event.h"

#include "mozilla-embed.h"

static void mozilla_embed_class_init    (MozillaEmbedClass *klass);
static void mozilla_embed_init      (MozillaEmbed *gs);
static void mozilla_embed_destroy       (GtkObject *object);
static void mozilla_embed_finalize      (GObject *object);
static void mozilla_embed_dispose       (GObject *object);
static void ephy_embed_iface_init       (EphyEmbedIface *iface);

static void mozilla_embed_location_changed_cb   (GtkMozEmbed *embed,
                         MozillaEmbed *membed);
static void mozilla_embed_net_state_all_cb  (GtkMozEmbed *embed,
                         const char *aURI,
                         gint state,
                         guint status,
                         MozillaEmbed *membed);
static gboolean mozilla_embed_dom_mouse_click_cb(GtkMozEmbed *embed,
                         gpointer dom_event,
                         MozillaEmbed *membed);
static gboolean mozilla_embed_dom_mouse_down_cb (GtkMozEmbed *embed,
                         gpointer dom_event, 
                         MozillaEmbed *membed);
static gboolean mozilla_embed_dom_key_press_cb  (GtkMozEmbed *embed,
                         gpointer dom_event, 
                         MozillaEmbed *membed);
static void mozilla_embed_new_window_cb     (GtkMozEmbed *embed, 
                         GtkMozEmbed **newEmbed,
                         guint chrome_mask,
                         MozillaEmbed *membed);
static void mozilla_embed_security_change_cb    (GtkMozEmbed *embed, 
                         gpointer request,
                         PRUint32 state,
                         MozillaEmbed *membed);
static void mozilla_embed_document_type_cb  (EphyEmbed *embed,
                         EphyEmbedDocumentType type,
                         MozillaEmbed *membed);
static void mozilla_embed_zoom_change_cb    (EphyEmbed *embed,
                         float zoom,
                         MozillaEmbed *membed);
static void mozilla_embed_title_change_cb   (GtkMozEmbed *embed,
                         MozillaEmbed *membed);
static void mozilla_embed_link_message_cb   (GtkMozEmbed *embed,
                         MozillaEmbed *membed);
static void mozilla_embed_set_title     (MozillaEmbed *embed,
                         char *title);
static void mozilla_embed_set_loading_title (MozillaEmbed *embed,
                         const char *title,
                         gboolean is_address);
static void mozilla_embed_icon_cache_changed_cb (EphyFaviconCache *cache,
                         const char *address,
                         MozillaEmbed *embed);
static void mozilla_embed_set_icon_address  (MozillaEmbed *embed,
                         const char *address);
static void mozilla_embed_favicon_cb        (MozillaEmbed *membed,
                         const char *address,
                         GtkMozEmbed *embed);
static gboolean mozilla_embed_open_uri_cb   (EphyEmbed *embed,
                         const char *uri,
                         MozillaEmbed *membed);
static void  mozilla_embed_file_monitor_cancel  (MozillaEmbed *embed);
#if 0
static guint popup_blocker_n_hidden     (MozillaEmbed *embed);
static gboolean mozilla_embed_get_popups_allowed (MozillaEmbed *embed);
static void mozilla_embed_set_popups_allowed     (MozillaEmbed *embed,
                          gboolean allowed);
#endif
static void mozilla_embed_file_monitor_cancel   (MozillaEmbed *embed);
static void impl_set_typed_address      (EphyEmbed *embed,
                         const char *address,
                         EphyEmbedAddressExpire expire);

static EphyEmbedSecurityLevel mozilla_embed_security_level (PRUint32 state);

#define MOZILLA_EMBED_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), MOZILLA_TYPE_EMBED, MozillaEmbedPrivate))

typedef enum
{
    MOZILLA_EMBED_LOAD_STARTED,
    MOZILLA_EMBED_LOAD_REDIRECTING,
    MOZILLA_EMBED_LOAD_LOADING,
    MOZILLA_EMBED_LOAD_STOPPED
} MozillaEmbedLoadState;

#define MAX_HIDDEN_POPUPS   5
#define MAX_TITLE_LENGTH    512 /* characters */
#define RELOAD_DELAY        250 /* ms */
#define RELOAD_DELAY_MAX_TICKS  40  /* RELOAD_DELAY * RELOAD_DELAY_MAX_TICKS = 10 s */

struct MozillaEmbedPrivate
{
    EphyBrowser *browser;
    GtkMozEmbed *moz_embed;
    MozillaEmbedLoadState load_state;

    EphyEmbedAddressExpire address_expire;
    /* guint address_expire : 2; ? */
    EphyEmbedSecurityLevel security_level;
    /* guint security_level : 3; ? */
    EphyEmbedDocumentType document_type;
    EphyEmbedNavigationFlags nav_flags;
    float zoom;

    /* Flags */
    guint is_blank : 1;
    guint is_loading : 1;
    guint is_setting_zoom : 1;

    gint8 load_percent;
    char *address;
    char *typed_address;
    char *title;
    char *loading_title;
    int cur_requests;
    int total_requests;
    char *status_message;
    char *link_message;
    char *icon_address;
    GdkPixbuf *icon;

    /* File watch */
    GnomeVFSMonitorHandle *monitor;
    guint reload_scheduled_id;
    guint reload_delay_ticks;

    GSList *hidden_popups;
    GSList *shown_popups;
};

#define WINDOWWATCHER_CONTRACTID "@mozilla.org/embedcomp/window-watcher;1"

enum
{
    PROP_0,
    PROP_ADDRESS,
    PROP_DOCUMENT_TYPE,
    PROP_HIDDEN_POPUP_COUNT,
    PROP_ICON,
    PROP_ICON_ADDRESS,
    PROP_LINK_MESSAGE,
    PROP_LOAD_PROGRESS,
    PROP_LOAD_STATUS,
    PROP_NAVIGATION,
    PROP_POPUPS_ALLOWED,
    PROP_SECURITY,
    PROP_STATUS_MESSAGE,
    PROP_TITLE,
    PROP_TYPED_ADDRESS,
    PROP_ZOOM
};

typedef struct
{
    char *url;
    char *name;
    char *features;
} PopupInfo;

static void
impl_manager_do_command (EphyCommandManager *manager,
             const char *command) 
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(manager)->priv;

    mpriv->browser->DoCommand (command);
}

static gboolean
impl_manager_can_do_command (EphyCommandManager *manager,
                 const char *command) 
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(manager)->priv;
    nsresult rv;
    PRBool enabled;

    rv = mpriv->browser->GetCommandState (command, &enabled);

    return NS_SUCCEEDED (rv) ? enabled : FALSE;
}

static void
ephy_command_manager_iface_init (EphyCommandManagerIface *iface)
{
    iface->do_command = impl_manager_do_command;
    iface->can_do_command = impl_manager_can_do_command;
}

G_DEFINE_TYPE_WITH_CODE (MozillaEmbed, mozilla_embed, EPHY_TYPE_BASE_EMBED,
             G_IMPLEMENT_INTERFACE (EPHY_TYPE_EMBED,
                        ephy_embed_iface_init)
             G_IMPLEMENT_INTERFACE (EPHY_TYPE_COMMAND_MANAGER,
                        ephy_command_manager_iface_init))
    
static void
mozilla_embed_grab_focus (GtkWidget *widget)
{
    GtkWidget *child;

    child = gtk_bin_get_child (GTK_BIN (widget));

    if (child != NULL)
    {
        gtk_widget_grab_focus (child);
    }
    else
    {
        g_warning ("Need to realize the embed before grabbing focus!\n");
    }
}

static void
impl_close (EphyEmbed *embed) 
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED (embed)->priv;

    mpriv->browser->Close ();
}

static void
mozilla_embed_realize (GtkWidget *widget)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED (widget)->priv;

    GTK_WIDGET_CLASS (mozilla_embed_parent_class)->realize (widget);

    gtk_widget_realize (GTK_WIDGET (mpriv->moz_embed));
    /* Initialise our helper class */
    nsresult rv;
    rv = mpriv->browser->Init (EPHY_EMBED (widget));
    if (NS_FAILED (rv))
    {
        g_warning ("EphyBrowser initialization failed for %p\n", widget);
        return;
    }
}

static GObject *
mozilla_embed_constructor (GType type, guint n_construct_properties,
               GObjectConstructParam *construct_params)
{
    g_object_ref (embed_shell);

    /* we depend on single because of mozilla initialization */
    ephy_embed_shell_get_embed_single (embed_shell);

    return G_OBJECT_CLASS (mozilla_embed_parent_class)->constructor (type, n_construct_properties,
                                     construct_params);
}

static void
mozilla_embed_get_property (GObject *object,
                guint prop_id,
                GValue *value,
                GParamSpec *pspec)
{
    MozillaEmbed *embed = MOZILLA_EMBED (object);
    MozillaEmbedPrivate *priv = embed->priv;
    
    switch (prop_id)
    {
    case PROP_ADDRESS:
        g_value_set_string (value, priv->address);
        break;
    case PROP_DOCUMENT_TYPE:
        g_value_set_enum (value, priv->document_type);
        break;
    case PROP_HIDDEN_POPUP_COUNT:
        g_value_set_int (value, 0);
      //            g_value_set_int (value, popup_blocker_n_hidden (embed));
        break;
    case PROP_ICON:
           g_value_set_object (value, priv->icon);
           break;
    case PROP_ICON_ADDRESS:
           g_value_set_string (value, priv->icon_address);
           break;
    case PROP_LINK_MESSAGE:
        g_value_set_string (value, priv->link_message);
        break;
    case PROP_LOAD_PROGRESS:
        g_value_set_int (value, priv->load_percent);
        break;
    case PROP_LOAD_STATUS:
        g_value_set_boolean (value, priv->is_loading);
        break;
    case PROP_NAVIGATION:
        g_value_set_flags (value, priv->nav_flags);
        break;
    case PROP_POPUPS_ALLOWED:
        g_value_set_boolean (value, FALSE);
      //    g_value_set_boolean (value, mozilla_embed_get_popups_allowed (embed));
        break;
    case PROP_SECURITY:
        g_value_set_enum (value, priv->security_level);
        break;
    case PROP_STATUS_MESSAGE:
        g_value_set_string (value, priv->status_message);
        break;
    case PROP_TITLE:
        g_value_set_string (value, priv->title);
        break;
    case PROP_TYPED_ADDRESS:
        g_value_set_string (value, priv->typed_address);
        break;
    case PROP_ZOOM:
        g_value_set_float (value, priv->zoom);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }

}

static void
mozilla_embed_set_property (GObject *object,
                guint prop_id,
                const GValue *value,
                GParamSpec *pspec)
{
    switch (prop_id)
    {
    case PROP_ICON_ADDRESS:
        mozilla_embed_set_icon_address (MOZILLA_EMBED (object), g_value_get_string (value));
        break;
    case PROP_TYPED_ADDRESS:
        impl_set_typed_address (EPHY_EMBED (object), g_value_get_string (value),
                    EPHY_EMBED_ADDRESS_EXPIRE_NOW);
        break;
    case PROP_POPUPS_ALLOWED:
      //mozilla_embed_set_popups_allowed (MOZILLA_EMBED (object), g_value_get_boolean (value));
        break;
    case PROP_ADDRESS:
    case PROP_DOCUMENT_TYPE:
    case PROP_HIDDEN_POPUP_COUNT:
    case PROP_ICON:
    case PROP_LOAD_PROGRESS:
    case PROP_LOAD_STATUS:
    case PROP_LINK_MESSAGE:
    case PROP_NAVIGATION:
    case PROP_SECURITY:
    case PROP_STATUS_MESSAGE:
    case PROP_ZOOM:
        /* read only */
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
mozilla_embed_class_init (MozillaEmbedClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
    GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass); 
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); 

    object_class->constructor = mozilla_embed_constructor;
    object_class->finalize = mozilla_embed_finalize;
    object_class->dispose = mozilla_embed_dispose;
    object_class->get_property = mozilla_embed_get_property;
    object_class->set_property = mozilla_embed_set_property;

    gtk_object_class->destroy = mozilla_embed_destroy;

    widget_class->grab_focus = mozilla_embed_grab_focus;
    widget_class->realize = mozilla_embed_realize;

    g_object_class_override_property (object_class, PROP_DOCUMENT_TYPE, "document-type");
    g_object_class_override_property (object_class, PROP_SECURITY, "security-level");
    g_object_class_override_property (object_class, PROP_ZOOM, "zoom");
    g_object_class_override_property (object_class, PROP_LOAD_PROGRESS, "load-progress");
    g_object_class_override_property (object_class, PROP_LOAD_STATUS, "load-status");
    g_object_class_override_property (object_class, PROP_NAVIGATION, "navigation");
    g_object_class_override_property (object_class, PROP_ADDRESS, "address");
    g_object_class_override_property (object_class, PROP_TYPED_ADDRESS, "typed-address");
    g_object_class_override_property (object_class, PROP_TITLE, "title");
    g_object_class_override_property (object_class, PROP_STATUS_MESSAGE, "status-message");
    g_object_class_override_property (object_class, PROP_LINK_MESSAGE, "link-message");
    g_object_class_override_property (object_class, PROP_ICON, "icon");
    g_object_class_override_property (object_class, PROP_ICON_ADDRESS, "icon-address");
    g_object_class_override_property (object_class, PROP_POPUPS_ALLOWED, "popups-allowed");
    g_object_class_override_property (object_class, PROP_HIDDEN_POPUP_COUNT, "hidden-popup-count");

    g_type_class_add_private (object_class, sizeof(MozillaEmbedPrivate));
}

static void
mozilla_embed_init (MozillaEmbed *membed)
{
    EphyFaviconCache *cache;
    MozillaEmbedPrivate *priv;
    GtkWidget *embed;

    membed->priv = MOZILLA_EMBED_GET_PRIVATE (membed);
    priv = membed->priv;

    embed = gtk_moz_embed_new ();
    priv->moz_embed = GTK_MOZ_EMBED (embed);
    gtk_container_add (GTK_CONTAINER (membed), embed);
    gtk_widget_show (embed);

    priv->browser = new EphyBrowser ();

    g_signal_connect_object (embed, "location",
                 G_CALLBACK (mozilla_embed_location_changed_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (embed, "net_state_all",
                 G_CALLBACK (mozilla_embed_net_state_all_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (embed, "dom_mouse_click",
                 G_CALLBACK (mozilla_embed_dom_mouse_click_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (embed, "dom_mouse_down",
                 G_CALLBACK (mozilla_embed_dom_mouse_down_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (embed, "dom-key-press",
                 G_CALLBACK (mozilla_embed_dom_key_press_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (embed, "new_window",
                 G_CALLBACK (mozilla_embed_new_window_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (embed, "security_change",
                 G_CALLBACK (mozilla_embed_security_change_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (membed, "ge_document_type",
                 G_CALLBACK (mozilla_embed_document_type_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (membed, "ge_zoom_change",
                 G_CALLBACK (mozilla_embed_zoom_change_cb),
                 embed, (GConnectFlags) 0);
    g_signal_connect_object (embed, "title",
                 G_CALLBACK (mozilla_embed_title_change_cb),
                 membed, (GConnectFlags) 0);
    g_signal_connect_object (embed, "link_message",
                 G_CALLBACK (mozilla_embed_link_message_cb),
                 membed, (GConnectFlags)0);
    g_signal_connect_object (membed, "ge_favicon",
                 G_CALLBACK (mozilla_embed_favicon_cb),
                 embed, (GConnectFlags)0);
    g_signal_connect_object (embed, "open_uri",
                 G_CALLBACK (mozilla_embed_open_uri_cb),
                 membed,(GConnectFlags) 0);

    cache = EPHY_FAVICON_CACHE
        (ephy_embed_shell_get_favicon_cache (embed_shell));
    g_signal_connect_object (G_OBJECT (cache), "changed",
                 G_CALLBACK (mozilla_embed_icon_cache_changed_cb),
                 membed, (GConnectFlags)0);

    priv->document_type = EPHY_EMBED_DOCUMENT_HTML;
    priv->security_level = EPHY_EMBED_STATE_IS_UNKNOWN;
    priv->zoom = 1.0;
    priv->is_setting_zoom = FALSE;
    priv->load_percent = 0;
    priv->is_loading = FALSE;
    priv->typed_address = NULL;
    priv->address = NULL;
    priv->address_expire = EPHY_EMBED_ADDRESS_EXPIRE_NOW;
    priv->title = NULL;
    priv->loading_title = NULL;
    priv->is_blank = TRUE;
    priv->total_requests = 0;
    priv->cur_requests = 0;
    priv->icon_address = NULL;
    priv->icon = NULL;
    priv->status_message = NULL;
    priv->link_message = NULL;
}

gpointer
_mozilla_embed_get_ephy_browser (MozillaEmbed *embed)
{
    g_return_val_if_fail (embed->priv->browser != NULL, NULL);
    
    return embed->priv->browser;
}

static void
mozilla_embed_destroy (GtkObject *object)
{
    MozillaEmbed *embed = MOZILLA_EMBED (object);

    if (embed->priv->browser)
    {
        embed->priv->browser->Destroy();
    }
    
    GTK_OBJECT_CLASS (mozilla_embed_parent_class)->destroy (object);
}

static void
mozilla_embed_dispose (GObject *object)
{
    mozilla_embed_file_monitor_cancel (MOZILLA_EMBED (object));

    G_OBJECT_CLASS (mozilla_embed_parent_class)->dispose (object);
}

static void
mozilla_embed_finalize (GObject *object)
{
    MozillaEmbed *embed = MOZILLA_EMBED (object);

    if (embed->priv->browser)
    {
        delete embed->priv->browser;
        embed->priv->browser = nsnull;
    }

    if (embed->priv->icon != NULL)
    {
        g_object_unref (embed->priv->icon);
        embed->priv->icon = NULL;
    }

#if 0
    popups_manager_reset (embed);
#endif

    g_free (embed->priv->icon_address);
    g_free (embed->priv->address);
    g_free (embed->priv->typed_address);
    g_free (embed->priv->title);
    g_free (embed->priv->loading_title);
    g_free (embed->priv->status_message);
    g_free (embed->priv->link_message);

    G_OBJECT_CLASS (mozilla_embed_parent_class)->finalize (object);

    g_object_unref (embed_shell);
}

#if 0
static void
ephy_tab_content_change_cb (EphyEmbed *embed, const char *address, EphyTab *tab)
{
    popups_manager_reset (tab);
    g_object_notify (G_OBJECT (tab), "popups-allowed");
}

static void
ephy_tab_new_window_cb (EphyEmbed *embed,
            EphyEmbed *new_embed,
            EphyTab *tab)
{
    EphyWindow *window;

    g_return_if_fail (new_embed != NULL);

    window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (new_embed)));
    g_return_if_fail (window != NULL);

    popups_manager_add_window (tab, window);
}

static void
ephy_tab_popup_blocked_cb (EphyEmbed *embed,
               const char *url,
               const char *name,
               const char *features,
               EphyTab *tab)
{
    popups_manager_add (tab, url, name, features);
}

static void
popups_manager_free_info (PopupInfo *popup)
{
    g_free (popup->url);
    g_free (popup->name);
    g_free (popup->features);
    g_free (popup);
}

static void
popups_manager_add (MozillaEmbed *embed,
            const char *url,
            const char *name,
            const char *features)
{
    MozillaEmbedPrivate *priv = embed->priv;
    PopupInfo *popup;

    LOG ("popups_manager_add: embed %p, url %s, features %s",
         embed, url, features);

    popup = g_new0 (PopupInfo, 1);

    popup->url = (url == NULL) ? NULL : g_strdup (url);
    popup->name = g_strdup (name);
    popup->features = g_strdup (features);

    priv->hidden_popups = g_slist_prepend (priv->hidden_popups, popup);

    if (popup_blocker_n_hidden (embed) > MAX_HIDDEN_POPUPS) /* bug #160863 */
    {
        /* Remove the oldest popup */
        GSList *l = embed->priv->hidden_popups;

        while (l->next->next != NULL)
        {
            l = l->next;
        }

        popup = (PopupInfo *) l->next->data;
        popups_manager_free_info (popup);

        l->next = NULL;
    }
    else
    {
        g_object_notify (G_OBJECT (embed), "hidden-popup-count");
    }
}

static gboolean
popups_manager_remove_window (MozillaEmbed *embed,
                  EphyWindow *window)
{
    embed->priv->shown_popups = g_slist_remove (embed->priv->shown_popups,
                            window);

    return FALSE;
}

static void
disconnect_popup (EphyWindow *window,
          MozillaEmbed *embed)
{
    g_signal_handlers_disconnect_by_func
        (window, G_CALLBACK (popups_manager_remove_window), embed);
}

static void
popups_manager_add_window (MozillaEmbed *embed,
               EphyWindow *window)
{
    LOG ("popups_manager_add_window: embed %p, window %p", embed, window);

    embed->priv->shown_popups = g_slist_prepend
        (embed->priv->shown_popups, window);

    g_signal_connect_swapped (window, "destroy",
                  G_CALLBACK (popups_manager_remove_window),
                  embed);
}

static gboolean
mozilla_embed_get_popups_allowed (MozillaEmbed *embed)
{
    EphyPermissionManager *permission_manager;
    EphyPermission response;
    EphyEmbed *embed;
    char *location;
    gboolean allow;

    permission_manager = EPHY_PERMISSION_MANAGER
        (ephy_embed_shell_get_embed_single (embed_shell));
    g_return_val_if_fail (EPHY_IS_PERMISSION_MANAGER (permission_manager),
                  FALSE);

    location = ephy_embed_get_location (embed, TRUE);
    if (location == NULL) return FALSE; /* FALSE, TRUE… same thing */

    response = ephy_permission_manager_test_permission
        (permission_manager, location, EPT_POPUP);

    switch (response)
    {
        case EPHY_PERMISSION_ALLOWED:
            allow = TRUE;
            break;
        case EPHY_PERMISSION_DENIED:
            allow = FALSE;
            break;
        case EPHY_PERMISSION_DEFAULT:
        default:
            allow = eel_gconf_get_boolean
                (CONF_SECURITY_ALLOW_POPUPS);
            break;
    }

    g_free (location);

    LOG ("mozilla_embed_get_popups_allowed: embed %p, allowed: %d", embed, allow);

    return allow;
}

static void
popups_manager_show (PopupInfo *popup,
             MozillaEmbed *embed)
{
    EphyEmbed *embed;
    EphyEmbedSingle *single;

    /* Only show popup with non NULL url */
    if (popup->url != NULL)
    {
        embed = ephy_embed_get_embed (embed);

        single = EPHY_EMBED_SINGLE
            (ephy_embed_shell_get_embed_single (embed_shell));

        ephy_embed_single_open_window (single, embed, popup->url,
                popup->name, popup->features);
    }
    popups_manager_free_info (popup);
}

static void
popups_manager_show_all (MozillaEmbed *embed)
{
    LOG ("popup_blocker_show_all: embed %p", embed);

    g_slist_foreach (embed->priv->hidden_popups,
             (GFunc) popups_manager_show, embed);
    g_slist_free (embed->priv->hidden_popups);
    embed->priv->hidden_popups = NULL;

    g_object_notify (G_OBJECT (embed), "hidden-popup-count");
}

static char *
popups_manager_new_window_info (EphyWindow *window)
{
    MozillaEmbed *embed;
    EphyEmbedChrome chrome;
    gboolean is_popup;
    char *features;

    g_object_get (window, "chrome", &chrome, "is-popup", &is_popup, NULL);
    g_return_val_if_fail (is_popup, g_strdup (""));

    embed = ephy_window_get_active_embed (window);
    g_return_val_if_fail (embed != NULL, g_strdup (""));

    features = g_strdup_printf
        ("width=%d,height=%d,menubar=%d,status=%d,toolbar=%d",
         embed->priv->width, embed->priv->height,
         (chrome & EPHY_EMBED_CHROME_MENUBAR) > 0,
         (chrome & EPHY_EMBED_CHROME_STATUSBAR) > 0,
         (chrome & EPHY_EMBED_CHROME_TOOLBAR) > 0);

    return features;
}

static void
popups_manager_hide (EphyWindow *window,
             MozillaEmbed *parent_embed)
{
    EphyEmbed *embed;
    char *location;
    char *features;

    embed = ephy_window_get_active_embed (window);
    g_return_if_fail (EPHY_IS_EMBED (embed));

    location = ephy_embed_get_location (embed, TRUE);
    if (location == NULL) return;

    features = popups_manager_new_window_info (window);

    popups_manager_add (parent_embed, location, "" /* FIXME? maybe _blank? */, features);

    gtk_widget_destroy (GTK_WIDGET (window));

    g_free (location);
    g_free (features);
}

static void
popups_manager_hide_all (MozillaEmbed *embed)
{
    LOG ("popup_blocker_hide_all: embed %p", embed);

    g_slist_foreach (embed->priv->shown_popups,
             (GFunc) popups_manager_hide, embed);
    g_slist_free (embed->priv->shown_popups);
    embed->priv->shown_popups = NULL;
}

static void
mozilla_embed_set_popups_allowed (MozillaEmbed *embed,
                  gboolean allowed)
{
    char *location;
    EphyEmbed *embed;
    EphyPermissionManager *manager;
    EphyPermission permission;

    embed = ephy_embed_get_embed (embed);

    location = ephy_embed_get_location (embed, TRUE);
    g_return_if_fail (location != NULL);

    manager = EPHY_PERMISSION_MANAGER
        (ephy_embed_shell_get_embed_single (embed_shell));
    g_return_if_fail (EPHY_IS_PERMISSION_MANAGER (manager));

    permission = allowed ? EPHY_PERMISSION_ALLOWED
                 : EPHY_PERMISSION_DENIED;

    ephy_permission_manager_add_permission (manager, location, EPT_POPUP, permission);

    if (allowed)
    {
        popups_manager_show_all (embed);
    }
    else
    {
        popups_manager_hide_all (embed);
    }

    g_free (location);
}

static guint
popup_blocker_n_hidden (MozillaEmbed *embed)
{
    return g_slist_length (embed->priv->hidden_popups);
}

static void
popups_manager_reset (MozillaEmbed *embed)
{
    g_slist_foreach (embed->priv->hidden_popups,
             (GFunc) popups_manager_free_info, NULL);
    g_slist_free (embed->priv->hidden_popups);
    embed->priv->hidden_popups = NULL;

    g_slist_foreach (embed->priv->shown_popups,
             (GFunc) disconnect_popup, embed);
    g_slist_free (embed->priv->shown_popups);
    embed->priv->shown_popups = NULL;

    g_object_notify (G_OBJECT (embed), "hidden-popup-count");
}
#endif

static void
impl_load_url (EphyEmbed *embed, 
           const char *url)
{
    GtkMozEmbed *moz_embed = MOZILLA_EMBED (embed)->priv->moz_embed;
    gtk_moz_embed_load_url (moz_embed, url);
}

static char * impl_get_location (EphyEmbed *embed, gboolean toplevel);

static void
impl_load (EphyEmbed *embed, 
       const char *url,
       EphyEmbedLoadFlags flags,
       EphyEmbed *preview_embed)
{
    EphyBrowser *browser;

    browser = MOZILLA_EMBED(embed)->priv->browser;
    g_return_if_fail (browser != NULL);

    nsCOMPtr<nsIURI> uri;
    if (preview_embed != NULL)
    {
        EphyBrowser *pbrowser;

        pbrowser = MOZILLA_EMBED(preview_embed)->priv->browser;
        if (pbrowser != NULL)
        {
            pbrowser->GetDocumentURI (getter_AddRefs (uri));
        }
    }

#ifdef HAVE_GECKO_1_8_1
    if (flags & EPHY_EMBED_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
    {
        browser->LoadURI (url, nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP, uri);  
    }
    else
#endif /* HAVE_GECKO_1_8_1 */
    {
        browser->LoadURI (url, nsIWebNavigation::LOAD_FLAGS_NONE, uri); 
    }
}

static void
impl_stop_load (EphyEmbed *embed)
{
    GtkMozEmbed *moz_embed = MOZILLA_EMBED (embed)->priv->moz_embed;
    gtk_moz_embed_stop_load (moz_embed);    
}

static gboolean
impl_can_go_back (EphyEmbed *embed)
{
    GtkMozEmbed *moz_embed = MOZILLA_EMBED (embed)->priv->moz_embed;
    return gtk_moz_embed_can_go_back (moz_embed);
}

static gboolean
impl_can_go_forward (EphyEmbed *embed)
{
    GtkMozEmbed *moz_embed = MOZILLA_EMBED (embed)->priv->moz_embed;
    return gtk_moz_embed_can_go_forward (moz_embed);
}

static gboolean
mozilla_embed_get_uri_parent (MozillaEmbed *membed,
                  const char *aUri,
                  nsCString &aParent)
{
    nsresult rv;
    nsCString encoding;
    rv = membed->priv->browser->GetEncoding (encoding);
    if (NS_FAILED (rv)) return FALSE;

    nsCOMPtr<nsIURI> uri;
    rv = EphyUtils::NewURI (getter_AddRefs(uri), nsCString(aUri), encoding.get());
    if (NS_FAILED(rv) || !uri) return FALSE;

    /* Don't support going 'up' with chrome url's, mozilla handily
     * fixes them up for us, so it doesn't work properly, see
     * rdf/chrome/src/nsChromeProtocolHandler.cpp::NewURI()
     * (the Canonify() call)
     */
    nsCString scheme;
    rv = uri->GetScheme (scheme);
    if (NS_FAILED(rv) || !scheme.Length()) return FALSE;
    if (strcmp (scheme.get(), "chrome") == 0) return FALSE;

    nsCString path;
    rv = uri->GetPath(path);
    if (NS_FAILED(rv) || !path.Length()) return FALSE;
    if (strcmp (path.get (), "/") == 0) return FALSE;

    const char *slash = strrchr (path.BeginReading(), '/');
    if (!slash) return FALSE;

    if (slash[1] == '\0')
    {
        /* ends with a slash - a directory, go to parent */
        rv = uri->Resolve (nsCString(".."), aParent);
    }
    else
    {
        /* it's a file, go to the directory */
        rv = uri->Resolve (nsCString("."), aParent);
    }

    return NS_SUCCEEDED (rv);
}

static gboolean
impl_can_go_up (EphyEmbed *embed)
{
    MozillaEmbed *membed = MOZILLA_EMBED (embed);
    char *address;
    gboolean result;

    address = ephy_embed_get_location (embed, TRUE);
    if (address == NULL) return FALSE;

    nsCString parent;
    result = mozilla_embed_get_uri_parent (membed, address, parent);
    g_free (address);

    return result;
}

static GSList *
impl_get_go_up_list (EphyEmbed *embed)
{
    MozillaEmbed *membed = MOZILLA_EMBED (embed);
    GSList *l = NULL;
    char *address, *s;

    address = ephy_embed_get_location (embed, TRUE);
    if (address == NULL) return NULL;

    s = address;
    nsCString parent;
    while (mozilla_embed_get_uri_parent (membed, s, parent))
    {
        s = g_strdup (parent.get());
        l = g_slist_prepend (l, s);
    }

    g_free (address);

    return g_slist_reverse (l);
}

static void
impl_go_back (EphyEmbed *embed)
{
    GtkMozEmbed *moz_embed = MOZILLA_EMBED (embed)->priv->moz_embed;
    gtk_moz_embed_go_back (moz_embed);
}
        
static void
impl_go_forward (EphyEmbed *embed)
{
    GtkMozEmbed *moz_embed = MOZILLA_EMBED (embed)->priv->moz_embed;
    gtk_moz_embed_go_forward (moz_embed);
}

static void
impl_go_up (EphyEmbed *embed)
{
    MozillaEmbed *membed = MOZILLA_EMBED (embed);
    char *uri;

    uri = ephy_embed_get_location (embed, TRUE);
    if (uri == NULL) return;

    gboolean rv;
    nsCString parent_uri;
    rv = mozilla_embed_get_uri_parent (membed, uri, parent_uri);
    g_free (uri);

    g_return_if_fail (rv != FALSE);

    ephy_embed_load_url (embed, parent_uri.get ());
}

static const char *
impl_get_title (EphyEmbed *embed)
{
    return MOZILLA_EMBED (embed)->priv->title;
}

static char *
impl_get_js_status (EphyEmbed *embed)
{
    GtkMozEmbed *moz_embed = MOZILLA_EMBED (embed)->priv->moz_embed;
    return gtk_moz_embed_get_js_status (moz_embed);
}

static char *
impl_get_location (EphyEmbed *embed, 
           gboolean toplevel)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    nsresult rv;

    nsCOMPtr<nsIURI> uri;
    if (toplevel)
    {
        rv = mpriv->browser->GetDocumentURI (getter_AddRefs (uri));
    }
    else
    {
        rv = mpriv->browser->GetTargetDocumentURI (getter_AddRefs (uri));
    }

    if (NS_FAILED (rv)) return NULL;

    nsCOMPtr<nsIURI> furi;
    rv = uri->Clone (getter_AddRefs (furi));
    /* Some nsIURI impls return NS_OK even though they didn't put anything in the outparam!! */
    if (NS_FAILED (rv) || !furi) furi.swap(uri);

    /* Hide password part */
    nsCString user;
    furi->GetUsername (user);
    furi->SetUserPass (user);

    nsCString url;
    furi->GetSpec (url);

    return url.Length() ? g_strdup (url.get()) : NULL;
}

static void
impl_reload (EphyEmbed *embed, 
         gboolean force)
{
    GtkMozEmbed *moz_embed = MOZILLA_EMBED (embed)->priv->moz_embed;
    guint32 mflags = GTK_MOZ_EMBED_FLAG_RELOADNORMAL;

    if (force)
    {
        mflags = GTK_MOZ_EMBED_FLAG_RELOADBYPASSPROXYANDCACHE;
    }

    gtk_moz_embed_reload (moz_embed, mflags);
}

static void
impl_set_zoom (EphyEmbed *embed, 
           float zoom) 
{
    EphyBrowser *browser;
    nsresult rv;

    g_return_if_fail (zoom > 0.0);

    browser = MOZILLA_EMBED(embed)->priv->browser;
    g_return_if_fail (browser != NULL);

    rv = browser->SetZoom (zoom);

    if (NS_SUCCEEDED (rv))
    {
        g_signal_emit_by_name (embed, "ge_zoom_change", zoom);
    }
}

static float
impl_get_zoom (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    float f;

    nsresult rv;    
    rv = mpriv->browser->GetZoom (&f);
    
    if (NS_SUCCEEDED (rv))
    {
        return f;
    }

    return 1.0;
}

static void
impl_scroll_lines (EphyEmbed *embed,
           int num_lines)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    mpriv->browser->ScrollLines (num_lines);
}

static void
impl_scroll_pages (EphyEmbed *embed,
           int num_pages)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    mpriv->browser->ScrollPages (num_pages);
}

static void
impl_scroll_pixels (EphyEmbed *embed,
            int dx,
            int dy)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    mpriv->browser->ScrollPixels (dx, dy);
}

static int
impl_shistory_n_items (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    nsresult rv;
    int count, index;

    rv = mpriv->browser->GetSHInfo (&count, &index);

    return NS_SUCCEEDED(rv) ? count : 0;
}

static void
impl_shistory_get_nth (EphyEmbed *embed, 
               int nth,
               gboolean is_relative,
               char **aUrl,
               char **aTitle)
{
    nsresult rv;
    nsCString url;
    PRUnichar *title;
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    if (is_relative)
    {
        nth += ephy_embed_shistory_get_pos (embed);
    }
    
    rv = mpriv->browser->GetSHUrlAtIndex(nth, url);

    *aUrl = (NS_SUCCEEDED (rv) && url.Length()) ? g_strdup(url.get()) : NULL;

    rv = mpriv->browser->GetSHTitleAtIndex(nth, &title);

    if (title)
    {
        nsCString cTitle;
        NS_UTF16ToCString (nsString(title),
                   NS_CSTRING_ENCODING_UTF8, cTitle);
        *aTitle = g_strdup (cTitle.get());
        nsMemory::Free (title);
    }
    else
    {
        *aTitle = NULL;
    }
}

static int
impl_shistory_get_pos (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    nsresult rv;
    int count, index;

    rv = mpriv->browser->GetSHInfo (&count, &index);

    return NS_SUCCEEDED(rv) ? index : 0;
}

static void
impl_shistory_go_nth (EphyEmbed *embed, 
              int nth)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    mpriv->browser->GoToHistoryIndex (nth);
}

static void
impl_shistory_copy (EphyEmbed *source,
            EphyEmbed *dest,
            gboolean copy_back,
            gboolean copy_forward,
            gboolean copy_current)
{
    MozillaEmbedPrivate *spriv = MOZILLA_EMBED(source)->priv;
    MozillaEmbedPrivate *dpriv = MOZILLA_EMBED(dest)->priv;

    spriv->browser->CopySHistory(dpriv->browser, copy_back,
                     copy_forward, copy_current);
}

static void
impl_get_security_level (EphyEmbed *embed,
             EphyEmbedSecurityLevel *level,
             char **description)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED (embed)->priv;

    if (level) *level = EPHY_EMBED_STATE_IS_UNKNOWN;
    if (description) *description = NULL;

    nsresult rv;
    PRUint32 state;
    nsCString desc;
    rv = mpriv->browser->GetSecurityInfo (&state, desc);
    if (NS_FAILED (rv)) return;

    if (level) *level = mozilla_embed_security_level (state);
    if (description) *description = g_strdup (desc.get());
}

static void
impl_show_page_certificate (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED (embed)->priv;

    mpriv->browser->ShowCertificate ();
}
    
static void
impl_print (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
 
    mpriv->browser->Print ();
}

static void
impl_set_print_preview_mode (EphyEmbed *embed, gboolean preview_mode)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    mpriv->browser->SetPrintPreviewMode (preview_mode);
}

static int
impl_print_preview_n_pages (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    nsresult rv;
    int num;

    rv = mpriv->browser->PrintPreviewNumPages(&num);

    return NS_SUCCEEDED (rv) ? num : 0;
}

static void
impl_print_preview_navigate (EphyEmbed *embed,
                 EphyEmbedPrintPreviewNavType type,
                 int page)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    mpriv->browser->PrintPreviewNavigate(type, page);
}

static void
impl_set_encoding (EphyEmbed *embed,
           const char *encoding)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    nsresult rv;
    nsCString currEnc;

    g_return_if_fail (encoding != NULL);

    rv = mpriv->browser->GetEncoding (currEnc);
    if (NS_FAILED (rv)) return;

    if (strcmp (currEnc.get(), encoding) != 0 ||
        encoding[0] == '\0' && !ephy_embed_has_automatic_encoding (embed))
    {
        rv = mpriv->browser->ForceEncoding (encoding);
        if (NS_FAILED (rv)) return;
    }

    gtk_moz_embed_reload (mpriv->moz_embed,
                  GTK_MOZ_EMBED_FLAG_RELOADCHARSETCHANGE);
}

static char *
impl_get_encoding (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    nsresult rv;
    nsCString encoding;

    rv = mpriv->browser->GetEncoding (encoding);

    if (NS_FAILED (rv) || !encoding.Length())
    {
        return NULL;
    }

    return g_strdup (encoding.get());
}

static gboolean
impl_has_automatic_encoding (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    nsresult rv;
    nsCString encoding;

    rv = mpriv->browser->GetForcedEncoding (encoding);

    if (NS_FAILED (rv) || !encoding.Length())
    {
        return TRUE;
    }

    return FALSE;
}

static gboolean
impl_has_modified_forms (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
    nsresult rv;

    PRBool modified;
    rv = mpriv->browser->GetHasModifiedForms (&modified);

    return NS_SUCCEEDED (rv) ? modified : FALSE;
}

static EphyEmbedDocumentType
impl_get_document_type (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    return mpriv->document_type;
}

static int
impl_get_load_percent (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    return mpriv->load_percent;
}

static void
mozilla_embed_set_load_percent (MozillaEmbed *embed, int percent)
{
       MozillaEmbedPrivate *mpriv = embed->priv;

       if (percent != mpriv->load_percent)
       {
           mpriv->load_percent = percent;

           g_object_notify (G_OBJECT (embed), "load-progress");
       }
}

static gboolean
impl_get_load_status (EphyEmbed *embed)
{
    MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;

    return mpriv->is_loading;
}

static void
mozilla_embed_set_load_status (MozillaEmbed *embed, gboolean status)
{
       MozillaEmbedPrivate *mpriv = embed->priv;
       guint is_loading;

       is_loading = status != FALSE;

       if (is_loading != mpriv->is_loading)
       {
           mpriv->is_loading = is_loading;

           g_object_notify (G_OBJECT (embed), "load-status");
       }
}

static void
mozilla_embed_update_navigation_flags (MozillaEmbed *membed)
{
    MozillaEmbedPrivate *priv = membed->priv;
    EphyEmbed *embed = EPHY_EMBED (membed);
    guint flags = 0;

    if (impl_can_go_up (embed))
    {
        flags |= EPHY_EMBED_NAV_UP;
    }

    if (impl_can_go_back (embed))
    {
        flags |= EPHY_EMBED_NAV_BACK;
    }

    if (impl_can_go_forward (embed))
    {
        flags |= EPHY_EMBED_NAV_FORWARD;
    }

    if (priv->nav_flags != (EphyEmbedNavigationFlags)flags)
    {
        priv->nav_flags = (EphyEmbedNavigationFlags)flags;

        g_object_notify (G_OBJECT (embed), "navigation");
    }
}

static EphyEmbedNavigationFlags
impl_get_navigation_flags (EphyEmbed *embed)
{
    MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;
    return priv->nav_flags;
}

static const char*
impl_get_typed_address (EphyEmbed *embed)
{
    return MOZILLA_EMBED (embed)->priv->typed_address;
}

static void
impl_set_typed_address (EphyEmbed *embed,
            const char *address,
            EphyEmbedAddressExpire expire)
{
       MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;
 
       g_free (priv->typed_address);
       priv->typed_address = g_strdup (address);

       if (expire == EPHY_EMBED_ADDRESS_EXPIRE_CURRENT &&
       !priv->is_loading)
       {
           priv->address_expire = EPHY_EMBED_ADDRESS_EXPIRE_NOW;
       }
       else
       {
           priv->address_expire = expire;
       }

       g_object_notify (G_OBJECT (embed), "typed-address");
}

static const char*
impl_get_address (EphyEmbed *embed)
{
    MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;
  
    return priv->address ? priv->address : "about:blank";
}

static const char*
impl_get_status_message (EphyEmbed *embed)
{
    MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;

    if (priv->link_message && priv->link_message[0] != '\0')
    {
        return priv->link_message;
    }
    else if (priv->status_message)
    {
        return priv->status_message;
    }
    else
    {
        return NULL;
    }
}

static const char*
impl_get_link_message (EphyEmbed *embed)
{
    MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;

    return priv->link_message;
}

static gboolean
impl_get_is_blank (EphyEmbed *embed)
{
    MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;

    return priv->is_blank;
}

static const char*
impl_get_loading_title (EphyEmbed *embed)
{
    MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;

    return priv->loading_title;
}

static const char*
impl_get_icon_address (EphyEmbed *embed)
{
    MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;

    return priv->icon_address;
}

static GdkPixbuf*
impl_get_icon (EphyEmbed *embed)
{
    MozillaEmbedPrivate *priv = MOZILLA_EMBED (embed)->priv;

    return priv->icon;
}

static void
mozilla_embed_set_address (MozillaEmbed *embed, char *address)
{
    MozillaEmbedPrivate *priv = embed->priv;
    GObject *object = G_OBJECT (embed);
    
    g_free (priv->address);
    priv->address = address;

    priv->is_blank = address == NULL ||
             strcmp (address, "about:blank") == 0;

    if (priv->is_loading &&
        priv->address_expire == EPHY_EMBED_ADDRESS_EXPIRE_NOW &&
        priv->typed_address != NULL)
    {
        g_free (priv->typed_address);
        priv->typed_address = NULL;

        g_object_notify (object, "typed-address");
    }

    g_object_notify (object, "address");
}

static void
mozilla_embed_file_monitor_cancel (MozillaEmbed *embed)
{
    MozillaEmbedPrivate *priv = embed->priv;

    if (priv->monitor != NULL)
    {
        LOG ("Cancelling file monitor");

        gnome_vfs_monitor_cancel (priv->monitor);
        priv->monitor = NULL;
    }

    if (priv->reload_scheduled_id != 0)
    {
        LOG ("Cancelling scheduled reload");

        g_source_remove (priv->reload_scheduled_id);
        priv->reload_scheduled_id = 0;
    }

    priv->reload_delay_ticks = 0;
}

static gboolean
ephy_file_monitor_reload_cb (MozillaEmbed *embed)
{
    MozillaEmbedPrivate *priv = embed->priv;

    if (priv->reload_delay_ticks > 0)
    {
        priv->reload_delay_ticks--;

        /* Run again */
        return TRUE;
    }

    if (priv->is_loading)
    {
        /* Wait a bit to reload if we're still loading! */
        priv->reload_delay_ticks = RELOAD_DELAY_MAX_TICKS / 2;

        /* Run again */
        return TRUE;
    }

    priv->reload_scheduled_id = 0;

    //LOG ("Reloading file '%s'", impl_get_address (embed));

    impl_reload (EPHY_EMBED (embed), TRUE);

    /* don't run again */
    return FALSE;
}

static void
mozilla_embed_file_monitor_cb (GnomeVFSMonitorHandle *handle,
                   const gchar *monitor_uri,
                   const gchar *info_uri,
                   GnomeVFSMonitorEventType event_type,
                   MozillaEmbed *embed)
{
    gboolean uri_is_directory;
    gboolean should_reload;
    char* local_path;
    MozillaEmbedPrivate *priv = embed->priv;

    LOG ("File '%s' has changed, scheduling reload", monitor_uri);

    local_path = gnome_vfs_get_local_path_from_uri(monitor_uri);
    uri_is_directory = g_file_test(local_path, G_FILE_TEST_IS_DIR);
    g_free(local_path);

    switch (event_type)
    {
        /* These events will always trigger a reload: */
        case GNOME_VFS_MONITOR_EVENT_CHANGED:
        case GNOME_VFS_MONITOR_EVENT_CREATED:
            should_reload = TRUE;
            break;

        /* These events will only trigger a reload for directories: */
        case GNOME_VFS_MONITOR_EVENT_DELETED:
        case GNOME_VFS_MONITOR_EVENT_METADATA_CHANGED:
            should_reload = uri_is_directory;
            break;

        /* These events don't trigger a reload: */
        case GNOME_VFS_MONITOR_EVENT_STARTEXECUTING:
        case GNOME_VFS_MONITOR_EVENT_STOPEXECUTING:
        default:
            should_reload = FALSE;
            break;
    }

    if (should_reload) {
        /* We make a lot of assumptions here, but basically we know
         * that we just have to reload, by construction.
         * Delay the reload a little bit so we don't endlessly
         * reload while a file is written.
         */
        if (priv->reload_delay_ticks == 0)
        {
            priv->reload_delay_ticks = 1;
        }
        else
        {
            /* Exponential backoff */
            priv->reload_delay_ticks = MIN (priv->reload_delay_ticks * 2,
                    RELOAD_DELAY_MAX_TICKS);
        }

        if (priv->reload_scheduled_id == 0)
        {
            priv->reload_scheduled_id =
                g_timeout_add (RELOAD_DELAY,
                        (GSourceFunc) ephy_file_monitor_reload_cb, embed);
        }
    }
}

static void
mozilla_embed_update_file_monitor (MozillaEmbed *embed,
                   const gchar *address)
{
    MozillaEmbedPrivate *priv = embed->priv;
    GnomeVFSMonitorHandle *handle = NULL;
    gboolean local;
    char* local_path;
    GnomeVFSMonitorType monitor_type;

    if (priv->monitor != NULL &&
        priv->address != NULL && address != NULL &&
        strcmp (priv->address, address) == 0)
    
    {
        /* same address, no change needed */
        return;
    }

    mozilla_embed_file_monitor_cancel (embed);

    local = g_str_has_prefix (address, "file://");
    if (local == FALSE) return;
    
    local_path = gnome_vfs_get_local_path_from_uri(address);
    monitor_type = g_file_test(local_path, G_FILE_TEST_IS_DIR)
        ? GNOME_VFS_MONITOR_DIRECTORY
        : GNOME_VFS_MONITOR_FILE;
    g_free(local_path);

    if (gnome_vfs_monitor_add (&handle, address,
                   monitor_type,
                   (GnomeVFSMonitorCallback) mozilla_embed_file_monitor_cb,
                   embed) == GNOME_VFS_OK)
    {
        LOG ("Installed monitor for file '%s'", address);

        priv->monitor = handle;
    }
}

static void
mozilla_embed_set_link_message (MozillaEmbed *embed,
                char *link_message)
{
    MozillaEmbedPrivate *priv = embed->priv;
    
    g_free (priv->link_message);
    
    priv->link_message = ephy_embed_utils_link_message_parse (link_message);

    g_object_notify (G_OBJECT (embed), "status-message");
    g_object_notify (G_OBJECT (embed), "link-message");
}

static void
mozilla_embed_location_changed_cb (GtkMozEmbed *embed, 
                   MozillaEmbed *membed)
{
    char *location;
    GObject *object = G_OBJECT (membed);

    location = gtk_moz_embed_get_location (embed);
    g_signal_emit_by_name (membed, "ge_location", location);

    g_object_freeze_notify (object);

    /* do this up here so we still have the old address around */
    mozilla_embed_update_file_monitor (membed, location);

    /* Do not expose about:blank to the user, an empty address
       bar will do better */
    if (location == NULL || location[0] == '\0' ||
        strcmp (location, "about:blank") == 0)
    {
        mozilla_embed_set_address (membed, NULL);
        mozilla_embed_set_title (membed, NULL);
    }
    else
    {
        char *embed_address;

        /* we do this to get rid of an eventual password in the URL */
        embed_address = impl_get_location (EPHY_EMBED (membed), TRUE);
        mozilla_embed_set_address (membed, embed_address);
        mozilla_embed_set_loading_title (membed, embed_address, TRUE);
    }

    g_free (location);

    mozilla_embed_set_link_message (membed, NULL);
    mozilla_embed_set_icon_address (membed, NULL);
    mozilla_embed_update_navigation_flags (membed);

    g_object_notify (object, "title");

    g_object_thaw_notify (object);
}

static void
mozilla_embed_link_message_cb       (GtkMozEmbed *embed,
                     MozillaEmbed *membed)
{
    char *link_message = gtk_moz_embed_get_link_message (embed);
    mozilla_embed_set_link_message (membed, link_message);
    g_free (link_message);
}

static void
mozilla_embed_load_icon (MozillaEmbed *embed)
{
    MozillaEmbedPrivate *priv = embed->priv;
    EphyEmbedShell *shell;
    EphyFaviconCache *cache;

    if (priv->icon_address == NULL || priv->icon != NULL) return;

    shell = ephy_embed_shell_get_default ();
    cache = EPHY_FAVICON_CACHE (ephy_embed_shell_get_favicon_cache (shell));

    /* ephy_favicon_cache_get returns a reference already */
    priv->icon = ephy_favicon_cache_get (cache, priv->icon_address);

    g_object_notify (G_OBJECT (embed), "icon");
}

static void
mozilla_embed_icon_cache_changed_cb (EphyFaviconCache *cache,
                     const char *address,
                     MozillaEmbed *embed)
{
    MozillaEmbedPrivate *priv = embed->priv;

    g_return_if_fail (address != NULL);

    /* is this for us? */
    if (priv->icon_address != NULL &&
        strcmp (priv->icon_address, address) == 0)
    {
        mozilla_embed_load_icon (embed);
    }
}

static void
mozilla_embed_set_icon_address (MozillaEmbed *embed,
                const char *address)
{
    GObject *object = G_OBJECT (embed);
    MozillaEmbedPrivate *priv = embed->priv;
    /*  EphyBookmarks *eb;*/
    EphyHistory *history;

    g_free (priv->icon_address);
    priv->icon_address = g_strdup (address);

    if (priv->icon != NULL)
    {
        g_object_unref (priv->icon);
        priv->icon = NULL;

        g_object_notify (object, "icon");
    }

    if (priv->icon_address)
    {
        /* FIXME: we need to put this somewhere inside src?/
        history = EPHY_HISTORY
            (ephy_embed_shell_get_global_history (embed_shell));
        ephy_history_set_icon (history, priv->address,
                       priv->icon_address);

        eb = ephy_shell_get_bookmarks (ephy_shell);
        ephy_bookmarks_set_icon (eb, priv->address,
        priv->icon_address);*/

        mozilla_embed_load_icon (embed);
    }

    g_object_notify (object, "icon-address");
}

static void
mozilla_embed_favicon_cb (MozillaEmbed *membed,
              const char *address,
              GtkMozEmbed *embed)
{
    mozilla_embed_set_icon_address (membed, address);
}

static gboolean
address_has_web_scheme (const char *address)
{
    gboolean has_web_scheme;

    if (address == NULL) return FALSE;

    has_web_scheme = (g_str_has_prefix (address, "http:") ||
              g_str_has_prefix (address, "https:") ||
              g_str_has_prefix (address, "ftp:") ||
              g_str_has_prefix (address, "file:") ||
              g_str_has_prefix (address, "data:") ||
              g_str_has_prefix (address, "about:") ||
              g_str_has_prefix (address, "gopher:"));

    return has_web_scheme;
}

static void
mozilla_embed_restore_zoom_level (MozillaEmbed *membed, const char *address)
{
    MozillaEmbedPrivate *priv = membed->priv;

    /* restore zoom level */
    if (address_has_web_scheme (address))
    {
        EphyHistory *history;
        EphyNode *host;
        GValue value = { 0, };
        float zoom = 1.0, current_zoom;

        history = EPHY_HISTORY
            (ephy_embed_shell_get_global_history (embed_shell));
        host = ephy_history_get_host (history, address);

        if (host != NULL && ephy_node_get_property
                     (host, EPHY_NODE_HOST_PROP_ZOOM, &value))
        {
            zoom = g_value_get_float (&value);
            g_value_unset (&value);
        }

        current_zoom = ephy_embed_get_zoom (EPHY_EMBED (membed));
        if (zoom != current_zoom)
        {
            priv->is_setting_zoom = TRUE;
            ephy_embed_set_zoom (EPHY_EMBED (membed), zoom);
            priv->is_setting_zoom = FALSE;
        }
    }
}

static void
update_load_state (MozillaEmbed *membed, gint state)
{
    MozillaEmbedPrivate *priv = membed->priv;
    GtkMozEmbed *moz_embed = priv->moz_embed;

    if (state & GTK_MOZ_EMBED_FLAG_IS_DOCUMENT &&
        state & (GTK_MOZ_EMBED_FLAG_START | GTK_MOZ_EMBED_FLAG_STOP))
    {
        g_signal_emit_by_name (membed, "ge-document-type",
                       priv->browser->GetDocumentType ());
    }

    if (state & GTK_MOZ_EMBED_FLAG_RESTORING &&
        priv->load_state == MOZILLA_EMBED_LOAD_STARTED)
    {
        priv->load_state = MOZILLA_EMBED_LOAD_LOADING;

        char *address;
        address = gtk_moz_embed_get_location (moz_embed);
        g_signal_emit_by_name (membed, "ge-content-change", address);
        mozilla_embed_restore_zoom_level (membed, address);
        g_free (address);
    }

    if (state & GTK_MOZ_EMBED_FLAG_IS_NETWORK)
    {
        if (state & GTK_MOZ_EMBED_FLAG_START)
        {
            priv->load_state = MOZILLA_EMBED_LOAD_STARTED;
        }
        else if (state & GTK_MOZ_EMBED_FLAG_STOP)
        {
            priv->load_state = MOZILLA_EMBED_LOAD_STOPPED;
        }
    }
    else if (state & GTK_MOZ_EMBED_FLAG_START &&
         state & GTK_MOZ_EMBED_FLAG_IS_REQUEST)
    {
        if (priv->load_state == MOZILLA_EMBED_LOAD_REDIRECTING)
        {
            priv->load_state = MOZILLA_EMBED_LOAD_STARTED;
        }
        else if (priv->load_state != MOZILLA_EMBED_LOAD_LOADING)
        {
            priv->load_state = MOZILLA_EMBED_LOAD_LOADING;

            char *address;
            address = gtk_moz_embed_get_location (moz_embed);
            g_signal_emit_by_name (membed, "ge_content_change", address);
            mozilla_embed_restore_zoom_level (membed, address);
            g_free (address);
        }
    }
    else if (state & GTK_MOZ_EMBED_FLAG_REDIRECTING &&
         priv->load_state == MOZILLA_EMBED_LOAD_STARTED)
    {
        priv->load_state = MOZILLA_EMBED_LOAD_REDIRECTING;
    }
}

static void
update_net_state_message (MozillaEmbed *embed, const char *uri, EphyEmbedNetState flags)
{
    GnomeVFSURI *vfs_uri = NULL;
    const char *msg = NULL;
    const char *host = NULL;

    if (uri != NULL)
    {
        vfs_uri = gnome_vfs_uri_new (uri);
    }

    if (vfs_uri != NULL)
    {
        host = gnome_vfs_uri_get_host_name (vfs_uri);
    }

    if (host == NULL || host[0] == '\0') goto out;

    /* IS_REQUEST and IS_NETWORK can be both set */
    if (flags & EPHY_EMBED_STATE_IS_REQUEST)
    {
        if (flags & EPHY_EMBED_STATE_REDIRECTING)
        {
            msg = _("Redirecting to “%s”…");
        }
        else if (flags & EPHY_EMBED_STATE_TRANSFERRING)
        {
            msg = _("Transferring data from “%s”…");
        }
        else if (flags & EPHY_EMBED_STATE_NEGOTIATING)
        {
            msg = _("Waiting for authorization from “%s”…");
        }
    }

    if (flags & EPHY_EMBED_STATE_IS_NETWORK)
    {
        if (flags & EPHY_EMBED_STATE_START)
        {
            msg = _("Loading “%s”…");
        }
    }

    if ((flags & EPHY_EMBED_STATE_IS_NETWORK) &&
        (flags & EPHY_EMBED_STATE_STOP))
    {
        g_free (embed->priv->status_message);
        embed->priv->status_message = NULL;
        g_object_notify (G_OBJECT (embed), "status-message");

    }
    else if (msg != NULL)
    {
        g_free (embed->priv->status_message);
        g_free (embed->priv->loading_title);
        embed->priv->status_message = g_strdup_printf (msg, host);
        embed->priv->loading_title = g_strdup_printf (msg, host);
        g_object_notify (G_OBJECT (embed), "status-message");
        g_object_notify (G_OBJECT (embed), "title");
    }

out:
    if (vfs_uri != NULL)
    {
        gnome_vfs_uri_unref (vfs_uri);
    }
}

static int
build_load_percent (int requests_done, int requests_total)
{
    int percent= 0;

    if (requests_total > 0)
    {
        percent = (requests_done * 100) / requests_total;

        /* Mozilla sometimes report more done requests than
           total requests. Their progress widget clamp the value */
        percent = CLAMP (percent, 0, 100);
    }

    return percent;
}

static void
build_progress_from_requests (MozillaEmbed *embed, EphyEmbedNetState state)
{
    int load_percent;

    if (state & EPHY_EMBED_STATE_IS_REQUEST)
    {
        if (state & EPHY_EMBED_STATE_START)
        {
            embed->priv->total_requests ++;
        }
        else if (state & EPHY_EMBED_STATE_STOP)
        {
            embed->priv->cur_requests ++;
        }

        load_percent = build_load_percent (embed->priv->cur_requests,
                           embed->priv->total_requests);

        mozilla_embed_set_load_percent (embed, load_percent);
    }
}

static void
ensure_page_info (MozillaEmbed *embed, const char *address)
{
    MozillaEmbedPrivate *priv = embed->priv;

    if ((priv->address == NULL || priv->address[0] == '\0') &&
        priv->address_expire == EPHY_EMBED_ADDRESS_EXPIRE_NOW)
    {
        mozilla_embed_set_address (embed, g_strdup (address));
    }

    /* FIXME huh?? */
    if (priv->title == NULL || priv->title[0] == '\0')
    {
        mozilla_embed_set_title (embed, NULL);
    }
}

static void
update_embed_from_net_state (MozillaEmbed *embed,
                 const char *uri,
                 EphyEmbedNetState state)
{
    MozillaEmbedPrivate *priv = embed->priv;

    update_net_state_message (embed, uri, state);

    if (state & EPHY_EMBED_STATE_IS_NETWORK)
    {
        if (state & EPHY_EMBED_STATE_START)
        {
            GObject *object = G_OBJECT (embed);

            g_object_freeze_notify (object);

            priv->total_requests = 0;
            priv->cur_requests = 0;

            mozilla_embed_set_load_percent (embed, 0);
            mozilla_embed_set_load_status (embed, TRUE);

            ensure_page_info (embed, uri);

            g_object_notify (object, "title");

            g_object_thaw_notify (object);
        }
        else if (state & EPHY_EMBED_STATE_STOP)
        {
            GObject *object = G_OBJECT (embed);

            g_object_freeze_notify (object);

            mozilla_embed_set_load_percent (embed, 100);
            mozilla_embed_set_load_status (embed, FALSE);

            g_free (priv->loading_title);
            priv->loading_title = NULL;

            priv->address_expire = EPHY_EMBED_ADDRESS_EXPIRE_NOW;

            g_object_notify (object, "title");

            g_object_thaw_notify (object);
        }

        mozilla_embed_update_navigation_flags (embed);
    }

    build_progress_from_requests (embed, state);
}

static void
mozilla_embed_net_state_all_cb (GtkMozEmbed *embed, const char *aURI,
                gint state, guint status, 
                MozillaEmbed *membed)
{
    EphyEmbedNetState estate = EPHY_EMBED_STATE_UNKNOWN;
    int i;

    struct
    {
        guint state;
        EphyEmbedNetState embed_state;
    }
    conversion_map [] =
    {
        { GTK_MOZ_EMBED_FLAG_START, EPHY_EMBED_STATE_START },
        { GTK_MOZ_EMBED_FLAG_STOP, EPHY_EMBED_STATE_STOP },
        { GTK_MOZ_EMBED_FLAG_REDIRECTING, EPHY_EMBED_STATE_REDIRECTING },
        { GTK_MOZ_EMBED_FLAG_TRANSFERRING, EPHY_EMBED_STATE_TRANSFERRING },
        { GTK_MOZ_EMBED_FLAG_NEGOTIATING, EPHY_EMBED_STATE_NEGOTIATING },
        { GTK_MOZ_EMBED_FLAG_IS_REQUEST, EPHY_EMBED_STATE_IS_REQUEST },
        { GTK_MOZ_EMBED_FLAG_IS_DOCUMENT, EPHY_EMBED_STATE_IS_DOCUMENT },
        { GTK_MOZ_EMBED_FLAG_IS_NETWORK, EPHY_EMBED_STATE_IS_NETWORK },
        { GTK_MOZ_EMBED_FLAG_RESTORING, EPHY_EMBED_STATE_RESTORING },
        { 0, EPHY_EMBED_STATE_UNKNOWN }
    };

    for (i = 0; conversion_map[i].state != 0; i++)
    {
        if (state & conversion_map[i].state)
        {
            estate = (EphyEmbedNetState) (estate | conversion_map[i].embed_state);  
        }
    }

    update_load_state (membed, state);
    update_embed_from_net_state (membed, aURI, (EphyEmbedNetState)estate);
    
    g_signal_emit_by_name (membed, "ge_net_state", aURI, /* FIXME: (gulong) */ estate);
}

static gboolean
mozilla_embed_emit_mouse_signal (MozillaEmbed *embed,
                 gpointer dom_event, 
                 const char *signal_name)
{
    MozillaEmbedPrivate *mpriv = embed->priv;
    MozillaEmbedEvent *info;
    EventContext event_context;
    gint return_value = FALSE;
    nsresult rv;

    if (dom_event == NULL) return FALSE;

    nsCOMPtr<nsIDOMMouseEvent> ev = static_cast<nsIDOMMouseEvent*>(dom_event);
    NS_ENSURE_TRUE (ev, FALSE);
    nsCOMPtr<nsIDOMEvent> dev = do_QueryInterface (ev);
    NS_ENSURE_TRUE (dev, FALSE);

    info = mozilla_embed_event_new (static_cast<gpointer>(dev));

    event_context.Init (mpriv->browser);
    rv = event_context.GetMouseEventInfo (ev, MOZILLA_EMBED_EVENT (info));
    if (NS_FAILED (rv))
    {
        g_object_unref (info);
        return FALSE;
    }

    nsCOMPtr<nsIDOMDocument> domDoc;
    rv = event_context.GetTargetDocument (getter_AddRefs(domDoc));
    if (NS_SUCCEEDED (rv))
    {
        mpriv->browser->PushTargetDocument (domDoc);

        g_signal_emit_by_name (embed, signal_name, 
                       info, &return_value); 
        mpriv->browser->PopTargetDocument ();
    }

    g_object_unref (info);

    return return_value;
}

static gboolean
mozilla_embed_dom_mouse_click_cb (GtkMozEmbed *embed,
                  gpointer dom_event, 
                  MozillaEmbed *membed)
{
    return mozilla_embed_emit_mouse_signal (membed, dom_event,
                        "ge_dom_mouse_click");
}

static gboolean
mozilla_embed_dom_mouse_down_cb (GtkMozEmbed *embed, gpointer dom_event, 
                 MozillaEmbed *membed)
{
    return mozilla_embed_emit_mouse_signal (membed, dom_event,
                        "ge_dom_mouse_down");
}

static gint
mozilla_embed_dom_key_press_cb (GtkMozEmbed *embed,
                gpointer dom_event, 
                MozillaEmbed *membed)
{
    gint retval = FALSE;

    if (dom_event == NULL) return FALSE;

    nsCOMPtr<nsIDOMKeyEvent> ev = static_cast<nsIDOMKeyEvent*>(dom_event);
    NS_ENSURE_TRUE (ev, FALSE);

    if (!EventContext::CheckKeyPress (ev)) return FALSE;

    GdkEvent *event = gtk_get_current_event ();
    if (event == NULL) return FALSE; /* shouldn't happen! */

    g_return_val_if_fail (GDK_KEY_PRESS == event->type, FALSE);

    g_signal_emit_by_name (embed, "ge-search-key-press", event, &retval);

    gdk_event_free (event);

    return retval;
}

EphyEmbedChrome
_mozilla_embed_translate_chrome (GtkMozEmbedChromeFlags flags)
{
    static const struct
    {
        guint mozilla_flag;
        guint ephy_flag;
    }
    conversion_map [] =
    {
        { GTK_MOZ_EMBED_FLAG_MENUBARON, EPHY_EMBED_CHROME_MENUBAR },
        { GTK_MOZ_EMBED_FLAG_TOOLBARON, EPHY_EMBED_CHROME_TOOLBAR },
        { GTK_MOZ_EMBED_FLAG_STATUSBARON, EPHY_EMBED_CHROME_STATUSBAR },
        { GTK_MOZ_EMBED_FLAG_PERSONALTOOLBARON, EPHY_EMBED_CHROME_BOOKMARKSBAR },
    };

    guint mask = 0, i;

    for (i = 0; i < G_N_ELEMENTS (conversion_map); i++)
    {
        if (flags & conversion_map[i].mozilla_flag)
        {
            mask |= conversion_map[i].ephy_flag;
        }
    }

    return (EphyEmbedChrome) mask;
}

static void
mozilla_embed_new_window_cb (GtkMozEmbed *embed, 
                 GtkMozEmbed **newEmbed,
                 guint chrome_mask, 
                 MozillaEmbed *membed)
{
    GtkMozEmbedChromeFlags chrome = (GtkMozEmbedChromeFlags) chrome_mask;
    EphyEmbed *new_embed = NULL;
    GObject *single;
    EphyEmbedChrome mask;

    if (chrome & GTK_MOZ_EMBED_FLAG_OPENASCHROME)
    {
        *newEmbed = _mozilla_embed_new_xul_dialog ();
        return;
    }

    mask = _mozilla_embed_translate_chrome (chrome);

    single = ephy_embed_shell_get_embed_single (embed_shell);
    g_signal_emit_by_name (single, "new-window", embed, mask,
                   &new_embed);

    g_assert (new_embed != NULL);

    gtk_moz_embed_set_chrome_mask (embed, chrome);

    g_signal_emit_by_name (membed, "ge-new-window", new_embed);

    *newEmbed = GTK_MOZ_EMBED (new_embed);
}

static void
mozilla_embed_set_security_level (MozillaEmbed *embed, EphyEmbedSecurityLevel level)
{
    MozillaEmbedPrivate *priv = embed->priv;

    if (priv->security_level != level)
    {
        priv->security_level = level;

        g_object_notify (G_OBJECT (embed), "security-level");
    }
}

static void
mozilla_embed_security_change_cb (GtkMozEmbed *embed, 
                  gpointer requestptr,
                  PRUint32 state,
                  MozillaEmbed *membed)
{
    mozilla_embed_set_security_level (membed, mozilla_embed_security_level (state));
}

static void
mozilla_embed_document_type_cb (EphyEmbed *embed,
                EphyEmbedDocumentType type,
                MozillaEmbed *membed)
{
    if (membed->priv->document_type != type)
    {
        membed->priv->document_type = type;

        g_object_notify (G_OBJECT (membed), "document-type");
    }
}

static void
mozilla_embed_zoom_change_cb (EphyEmbed *embed,
                  float zoom,
                  MozillaEmbed *membed)
{
    char *address;

    if (membed->priv->zoom != zoom)
    {
        if (membed->priv->is_setting_zoom)
          {
            return;
          }

        address = ephy_embed_get_location (embed, TRUE);
        if (address_has_web_scheme (address))
          {
            EphyHistory *history;
            EphyNode *host;
            history = EPHY_HISTORY
              (ephy_embed_shell_get_global_history (embed_shell));
            host = ephy_history_get_host (history, address);

            if (host != NULL)
              {
            ephy_node_set_property_float (host,
                              EPHY_NODE_HOST_PROP_ZOOM,
                              zoom);
              }
          }

        g_free (address);

        membed->priv->zoom = zoom;

        g_object_notify (G_OBJECT (membed), "zoom");
    }
}

static char *
get_title_from_address (const char *address)
{
    GnomeVFSURI *uri;
    char *title;

    if (address == NULL) return NULL;
        
    uri = gnome_vfs_uri_new (address);
    if (uri == NULL) return g_strdup (address);
        
    title = gnome_vfs_uri_to_string (uri,
                     (GnomeVFSURIHideOptions)
                     (GNOME_VFS_URI_HIDE_USER_NAME |
                      GNOME_VFS_URI_HIDE_PASSWORD |
                      GNOME_VFS_URI_HIDE_HOST_PORT |
                      GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD |
                      GNOME_VFS_URI_HIDE_FRAGMENT_IDENTIFIER));
    gnome_vfs_uri_unref (uri);

    return title;
}

static void
mozilla_embed_set_loading_title (MozillaEmbed *embed,
                 const char *title,
                 gboolean is_address)
{
    MozillaEmbedPrivate *priv = embed->priv;
    char *freeme = NULL;

    g_free (priv->loading_title);
    priv->loading_title = NULL;

    if (is_address)
    {
        title = freeme = get_title_from_address (title);    
    }

    if (title != NULL && title[0] != '\0')
    {
        /* translators: %s here is the address of the web page */
        priv->loading_title = g_strdup_printf (_("Loading “%s”…"), title);
    }
    else
    {
        priv->loading_title = g_strdup (_("Loading…"));
    }

    g_free (freeme);
}

static void
mozilla_embed_set_title (MozillaEmbed *embed,
             char *title)
{
    MozillaEmbedPrivate *priv = embed->priv;

    if (!priv->is_blank && (title == NULL || g_strstrip (title)[0] == '\0'))
    {

        g_free (title);
        title = get_title_from_address (priv->address);

        /* Fallback */
        if (title == NULL || title[0] == '\0')
        {
            g_free (title);
            title = NULL;
            priv->is_blank = TRUE;
        }
    }
    else if (priv->is_blank && title != NULL)
    {
        g_free (title);
        title = NULL;
    }

    g_free (priv->title);
    priv->title = ephy_string_shorten (title, MAX_TITLE_LENGTH);

    g_object_notify (G_OBJECT (embed), "title");
}

static void
mozilla_embed_title_change_cb       (GtkMozEmbed *embed,
                     MozillaEmbed *membed)
{
    GObject *object = G_OBJECT (embed);
    char *title;

    title = gtk_moz_embed_get_title (embed);

    g_object_freeze_notify (object);

    mozilla_embed_set_title (membed, title);
    mozilla_embed_set_loading_title (membed, title, FALSE);

    g_object_thaw_notify (object);
}

static gboolean
mozilla_embed_open_uri_cb (EphyEmbed *embed,
               const char *uri,
               MozillaEmbed *membed)
{
    MozillaEmbedPrivate *priv = membed->priv;

    /* Set the address here if we have a blank page.
     * See bug #147840.
     */
    if (priv->is_blank)
    {
        mozilla_embed_set_address (membed, g_strdup (uri));
        mozilla_embed_set_loading_title (membed, uri, TRUE);
    }

    /* allow load to proceed */
    return FALSE;
}

static EphyEmbedSecurityLevel
mozilla_embed_security_level (PRUint32 state)
{
    EphyEmbedSecurityLevel level;

    switch (state)
    {
    case nsIWebProgressListener::STATE_IS_INSECURE:
        level = EPHY_EMBED_STATE_IS_INSECURE;
        break;
    case nsIWebProgressListener::STATE_IS_BROKEN:
        level = EPHY_EMBED_STATE_IS_BROKEN;
        break;
    case nsIWebProgressListener::STATE_IS_SECURE|
         nsIWebProgressListener::STATE_SECURE_HIGH:
        level = EPHY_EMBED_STATE_IS_SECURE_HIGH;
        break;
    case nsIWebProgressListener::STATE_IS_SECURE|
         nsIWebProgressListener::STATE_SECURE_MED:
        level = EPHY_EMBED_STATE_IS_SECURE_MED;
        break;
    case nsIWebProgressListener::STATE_IS_SECURE|
         nsIWebProgressListener::STATE_SECURE_LOW:
        level = EPHY_EMBED_STATE_IS_SECURE_LOW;
        break;
    default:
        level = EPHY_EMBED_STATE_IS_UNKNOWN;
        break;
    }
    return level;
}

static void
ephy_embed_iface_init (EphyEmbedIface *iface)
{
    iface->load_url = impl_load_url; 
    iface->load = impl_load; 
    iface->stop_load = impl_stop_load;
    iface->can_go_back = impl_can_go_back;
    iface->can_go_forward =impl_can_go_forward;
    iface->can_go_up = impl_can_go_up;
    iface->get_go_up_list = impl_get_go_up_list;
    iface->go_back = impl_go_back;
    iface->go_forward = impl_go_forward;
    iface->go_up = impl_go_up;
    iface->get_title = impl_get_title;
    iface->get_location = impl_get_location;
    iface->get_link_message = impl_get_link_message;
    iface->get_js_status = impl_get_js_status;
    iface->reload = impl_reload;
    iface->set_zoom = impl_set_zoom;
    iface->get_zoom = impl_get_zoom;
    iface->scroll_lines = impl_scroll_lines;
    iface->scroll_pages = impl_scroll_pages;
    iface->scroll_pixels = impl_scroll_pixels;
    iface->shistory_n_items = impl_shistory_n_items;
    iface->shistory_get_nth = impl_shistory_get_nth;
    iface->shistory_get_pos = impl_shistory_get_pos;
    iface->shistory_go_nth = impl_shistory_go_nth;
    iface->shistory_copy = impl_shistory_copy;
    iface->get_security_level = impl_get_security_level;
    iface->show_page_certificate = impl_show_page_certificate;
    iface->close = impl_close;
    iface->set_encoding = impl_set_encoding;
    iface->get_encoding = impl_get_encoding;
    iface->has_automatic_encoding = impl_has_automatic_encoding;
    iface->print = impl_print;
    iface->set_print_preview_mode = impl_set_print_preview_mode;
    iface->print_preview_n_pages = impl_print_preview_n_pages;
    iface->print_preview_navigate = impl_print_preview_navigate;
    iface->has_modified_forms = impl_has_modified_forms;
    iface->get_document_type = impl_get_document_type;
    iface->get_load_percent = impl_get_load_percent;
    iface->get_load_status = impl_get_load_status;
    iface->get_navigation_flags = impl_get_navigation_flags;
    iface->get_typed_address = impl_get_typed_address;
    iface->set_typed_address = impl_set_typed_address;
    iface->get_address = impl_get_address;
    iface->get_status_message = impl_get_status_message;
    iface->get_is_blank = impl_get_is_blank;
    iface->get_loading_title = impl_get_loading_title;
    iface->get_icon = impl_get_icon;
    iface->get_icon_address = impl_get_icon_address;
}

static void
xul_visibility_cb (GtkWidget *embed, gboolean visibility, GtkWidget *window)
{
    if (visibility)
    {
        gtk_widget_show (window);
    }
    else
    {
        gtk_widget_hide (window);
    }
}

static void
xul_size_to_cb (GtkWidget *embed, gint width, gint height, gpointer dummy)
{
    gtk_widget_set_size_request (embed, width, height);
}

static void
xul_new_window_cb (GtkMozEmbed *embed,
           GtkMozEmbed **retval, 
           guint chrome_mask,
           gpointer dummy)
{
    g_assert (chrome_mask & GTK_MOZ_EMBED_FLAG_OPENASCHROME);

    *retval = _mozilla_embed_new_xul_dialog ();
}

static void
xul_title_cb (GtkMozEmbed *embed,
          GtkWindow *window)
{
    char *title;

    title = gtk_moz_embed_get_title (embed);
    gtk_window_set_title (window, title);
    g_free (title);
}

GtkMozEmbed *
_mozilla_embed_new_xul_dialog (void)
{
    GtkWidget *window, *embed;

    g_object_ref (embed_shell);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_object_set_data_full (G_OBJECT (window), "EmbedShellRef",
                embed_shell,
                (GDestroyNotify) g_object_unref);
    g_signal_connect_object (embed_shell, "prepare_close",
                 G_CALLBACK (gtk_widget_destroy), window,
                 (GConnectFlags) G_CONNECT_SWAPPED);

    embed = gtk_moz_embed_new ();
    gtk_widget_show (embed);
    gtk_container_add (GTK_CONTAINER (window), embed);

    g_signal_connect_object (embed, "destroy_browser",
                 G_CALLBACK (gtk_widget_destroy),
                 window, G_CONNECT_SWAPPED);
    g_signal_connect_object (embed, "visibility",
                 G_CALLBACK (xul_visibility_cb),
                 window, (GConnectFlags) 0);
    g_signal_connect_object (embed, "size_to",
                 G_CALLBACK (xul_size_to_cb),
                 NULL, (GConnectFlags) 0);
    g_signal_connect_object (embed, "new_window",
                 G_CALLBACK (xul_new_window_cb),
                 NULL, (GConnectFlags) 0);
    g_signal_connect_object (embed, "title",
                 G_CALLBACK (xul_title_cb),
                 window, (GConnectFlags) 0);

    return GTK_MOZ_EMBED (embed);
}