Posts
103
Comments
238
Trackbacks
4
Bug di Internet Explorer nel caching di immagini

Chi ne è influenzato

Questo bug è stato introdotto nella versione 5.5 SP2 di Internet Explorer e continua ad essere presente nella 6.0.

Background

Accade di frequente che sia necessario inserire immagini in una pagina web in modo dinamico, via DOM, DHTML o con CSS.
La situazione più comune accade quando si creano dei bottoni con immagini di rollover, utilizzando ad esempio una funzione JavaScript che si occupa dello swap delle immagini, oppure con CSS.

Descrizione del bug

In questo tipo di situazione può accadere che le versioni interessate di Internet Explorer non recuperino le immagini dalla cache, come in teoria dovrebbe accadere, mentre ogni volta che si passa sopra al bottone con il cursore del mouse l'immagine di rollover venga scaricata nuovamente dal server.

Notare che questo succede anche se le immagini vengono scaricate preventivamente dal server utilizzando JavaScript, pratica molto comune.

Intuibilmente è un comportamento quantomeno deleterio, che provoca problemi dal punto di vista della funzionalità di una pagina, nonchè in termini di traffico verso il server.

Di questi tempi, poi, nelle applicazioni che utilizzano massivamente AJAX questo diventa un problema inaccettabile.

Nella maggior parte dei casi il bug si verifica soltanto quando Internet Explorer è stato configurato per ricercare nuove versioni delle risorse ogni volta che una risorsa viene richiesta. Per modificare le impostazioni il percorso è il seguente:

Menu Strumenti di IE -> Opzioni Internet... -> scheda Generale -> Impostazioni...

Ci si ritrova con le seguenti scelte:

  • All'apertura della pagina
  • All'avvio di Internet Explorer
  • Automaticamente
  • Mai

Come anticipato, in molti casi selezionando una qualsiasi opzione che non sia "All'apertura della pagina", questo problema viene (in parte) risolto. Tuttavia il problema permane per quegli utenti che hanno quest'opzione selezionata, cosa che per gli sviluppatori accade di frequente; di conseguenza, per uno sviluppatore in senso generico del termine, è naturalmente necessaria una soluzione che risieda sul server, poichè non è possibile gestire le impostazioni dei client che richiederanno la pagina.

Un documento di Micosoft afferma che questo comportamento è stato introdotto volutamente, "by design". [Considerazioni più avanti]

Riprodurre il bug

Tra le tante situazioni in cui può verificarsi, il bug è riproducibile creando una pagina di questo tipo, che tramite DHTML non fa altro che visualizzare per 500 volte la stessa immagine (notare che ne fa innanzitutto il preload):

<html>
<
head>
    <
title>Untitled</title>
<
/head>
<
script language="JavaScript">

    
function show_pictures()
    {
        var str        =    '';
        var ptrn    =    '
<img src="test.gif">';
        var d        =    document.getElementById('panel');

        for (var i=0; i
<500; i++)
        {
            str    
+=    ptrn;
        }

        
d.innerHTML    =    str;
    }

<
/script>
<
body>

<
img src="test.gif">
<
input type="Button" value="Go" onclick="show_pictures()">
<
div id="panel">

<
/div>

<
/body>
<
/html>

e seguendo precisamente questi passi con una versione di IE affetta dal problema:

PRIMO TENTATIVO:

  1. Visitare la pagina a questo link (si apre in una nuova finestra);
  2. Impostare IE "All'apertura della pagina" e pulire la cache (anche i file non in linea);
  3. Siamo in questa situazione: cache vuota anche l'immagine di preload è andata perduta;
  4. Cliccare sul tasto GO: le immagini appaiono lentamente, una dopo d'altra, malgrado siano tutte uguali!
  5. Cliccare nuovamente sul tasto GO: le immagini appaiono di nuovo lentamente, niente viene recuperato dalla cache.

Conclusione: con l'impostazione "All'apertura della pagina" il browser non recupera mai l'immagine dalla cache, ma la richiede ogni volta al server.

SECONDO TENTATIVO:

  1. In caso fosse ancora aperto chiudere IE;
  2. Visitare la stessa pagina a questo link;
  3. Impostare IE "All'avvio di Internet Explorer" (o una delle altre due) e pulire la cache (anche gli elementi non in linea);
  4. Siamo in questa situazione: cache vuota, anche l'immagina precaricata è andata perduta;
  5. Cliccare sul tasto GO: le immagini appaiono di nuovo lentamente, come prima !!
  6. Cliccare nuovamente sul tasto GO: questa volta le immagini appaiono tutte insieme

Conclusione: il problema è solo parzialmente risolto, perchè la prima volta che il bottone è stato cliccato le immagini si sono caricate lentamente lo stesso, mentre solo alla seconda pressione il browser ha recuperato l'immagine dalla cache. Questo perchè pulendo la cache dopo aver aperto la pagina è stata eliminata anche l'immagine precaricata; nel caso non fosse stato fatto si sarebbe ottenuto l'effetto di comparsa immediata di tutte le immagini già al primo click del bottone -> ecco perchè le immagini vengono precaricate!

Nota: idealmente, anche se l'immagine non è stata precaricata, sarebbe auspicabile che il browser, dopo la valutazione della funzione javascript, inserisse immediatamente l'immagine nella cache, in modo che la visualizzazione possa essere immediata. IE questo evidentemente non lo fa.

TERZO TENTATIVO: Firefox

Poichè Firefox non è soggetto a questo problema, aprendo la pagina precedente e cliccando il bottone GO le immagini compaiono tutte insieme.

Il comportamento interessante si osserva quando, dopo aver aperto la pagina e prima di cliccare il bottone, si pulisce la cache, eliminando anche l'immagine precaricata.

In questa situazione Firefox, diversamente da IE, immediatamente visualizza un segnaposto per ogni immagine, e dopo pochi secondi le visualizza tutte insieme.
Personalmente non ho conferme relativamente all'affermazione che sto per fare, ma questo comportamento mi fa pensare che Firefox, a cache vuota e alla pressione del tasto, dopo aver eseguito la funzione JavaScript, metta immediatamente l'immagine nella cache, in modo che tutti i tag <IMG> recuperino la sorgente dalla cache, invece che dal server.

Soluzione

Esiste una documentazione a dir poco scarna su questo problema, e di conseguenza anche la soluzione non è facile da trovare.

Lato client evidentemente non c'è molto da fare, se non impostare IE in modo che la ricerca delle versioni aggiornate non avvenga ad ogni apertura della pagina, e come soluzione ovviamente non è applicabile in modo centralizzato.

Lato server, invece, una soluzione esiste, e consiste nell'aggiungere l'header EXPIRES, con una data appropriata, alla risposta contenente l'immagine richiesta.

Esistono diversi modi per implementare questa soluzione, limitando naturalmente l'aggiunta dell'header solo alle immagini che vengono utilizzate in questo modo. Si può ad esempio lavorare a livello del server, configurando IIS (o altri) in modo che aggiungano l'header EXPIRES alle immagini richieste, ad esempio richiedendo l'aggiunta dell'header ad una cartella intera, che conterrà le immagini esposte al problema.

Infine, ed è la situazione in cui io stesso mi sono trovato quando ho affrontato il problema per la prima volta, si può lavorare a livello di HttpHandler, nel caso in cui le immagini vengano recuperate, ad esempio, dalle risorse di un assembly.
In questa situazione la soluzione è molto più semplice, e consiste nell'aggiungere, nel metodo ProcessRequest, la direttiva:

Response.Cache.SetExpires()

impostata ad una data futura, ed ottenendo un riferimento all'oggetto Response dall'HttpContext corrente.

Questa direttiva infatti non fa altro che aggiungere l'header EXPIRES alla risposta della singola risorsa richiesta all'HttpHandler.

Theory of conspiracy

Questo strano bug di IE può fare riflettere. In pratica, oltre all'effetto fastidioso che può comparire nelle pagine web, l'unico suo altro effetto è quello di generare un'enorme quantità di traffico di rete, imputabile quindi al browser Internet Explorer.

Poichè non voglio prendermi una denuncia, e per chi non avesse ancora capito dove si vuole andare a parare, consiglio questo link, all'omonimo paragrafo.

Riferimenti

powered by IMHO 1.3

posted on martedì 24 gennaio 2006 07:28 Print
News

Scopri CS2, il mio progetto universitario per l'indicizzazione e la ricerca di codice sorgente personale costruito su Lucene.Net.

Windows Developer Power Tools

Potete trovare il mio progetto BusyBoxDotNet nel libro Windows Developer Power Tools, pubblicato da O'Reilly, per il quale ho scritto l'intero capito dedicato.

Sono stato nominato dVP 2008, un riconoscimento per l'apporto fornito alla comunità del progetto db4o.