Il modulo Layout nelle vecchie versioni di Firefox

Il modulo Layout nelle vecchie versioni di Firefox

Quella che segue è la traduzione di un articolo allegato al sorgente delle vecchie versioni di Firefox.

Il compito di Layout: fornire la presentazione

Layout si occupa principalmente di fornire una presentazione ai documenti HTML o XML. Questa presentazione viene di solito formattata secondo i requisiti delle specifiche CSS1 e CSS2 del W3C. La formattazione della presentazione viene anche richiesta per la compatibilità con i browser legacy (Microsoft Internet Explorer e Netscape Navigator 4.x) La decisione su quando applicare la formattazione conforme alle specifiche e quando invece applicare quella retrocompatibile viene controllata dalla specifica di DOCTYPE del documento. Le modalità di layout sono rispettivamente denominate 'Standards' e 'NavQuirks'.

La presentazione di solito viene limitata dalla larghezza della finestra di visualizzazione, e da un'altezza che si estende per quanto necessario. Questa viene definita presentazione Galley Mode, ed è quello che ci si aspetta di solito da un browser tipico. Inoltre il layout deve supportare una presentazione a pagine, dove la larghezza della presentazione viene limitata dalle dimensioni dell'output di stampa (carta) e in cui l'altezza di ciascuna pagina è fissa. Questa presentazione a pagine comporta delle sfide non presenti nella presentazione tradizionale, ossia come interrompere gli elementi più grandi di una singola pagina e come gestire i cambiamenti alle dimensioni di pagina.

Il design originale del Layout System permetteva di avere diverse presentazioni multiple per lo stesso modello di contenuto. In altre parole, lo stesso documento poteva essere visto nel modo tradizionale nella finestra del browser ed essere al contempo presentato a pagine per la stampa o anche in modo acustico per una presentazione audio. Oggi l'unico uso reale di questa presentazione multipla si trova nella stampa, dove vengono gestite e riunite insieme le presentazioni multiple (nota: non è chiaro se questo sia realmente un beneficio - potrebbe essere meglio usare una copia del modello di contenuto per ciascuna presentazione, rimuovendo la complessità della gestione di presentazioni separate - qui è necessaria un'analisi). L'idea di supportare una presentazione non-visiva è interessante. Il supporto di Layout alle presentazioni acustiche non è sviluppato, per quanto concettualmente esso sia possibile anche a livello di architettura.

Come Layout svolge il suo lavoro: frame e reflow

Abbiamo detto che Layout si occupa di fornire una presentazione. Dato un modello di contenuto, in che modo il Layout System crea effettivamente la presentazione? Attraverso la creazione e la gestione dei frame. I frame sono un incapsulamento di una regione dello schermo, una regione che ha uno schema geometrico (dimensione, posizione, ordine di stacking). Di solito i frame corrispondono agli elementi del contenuto, sebbene vi sia spesso una corrispondenza multipla tra elementi del contenuto e i frame. Layout crea i frame per il contenuto in base alle specifiche regole HTML per un elemento o in base al tipo di visualizzazione CSS dell'elemento. Nel caso di elementi specifici dell'HTML i tipi di frame sono predeterminati, ma nei casi più generali, in cui si ha bisogno del tipo di visualizzazione, il Layout System deve determinare tale tipo usando lo Style System. Un elemento viene passato allo Style System al fine di stabilire lo stile per tale elemento. Questo fa si che lo Style System applichi tutte le regole di stile che corrispondono all'elemento e produca uno Style Context - i dati di stile specifici per l'elemento. Il modulo Layout esamina la voce 'display' del contesto di stile per determinare quale tipo di frame creare (block, inline, table, ecc.). Il contesto di stile viene associato al frame tramite un riferimento, poiché esso viene richiesto in molti altri calcoli durante la formattazione dei frame.

Una volta che è stato creato un frame per l'elemento, questo deve essere formattato. Ci riferiamo a questo processo come alla "rappresentazione" del frame, o "risistemazione" del frame. Ogni frame implementa un metodo Reflow per calcolare la sua posizione e dimensione. I requisiti della formattazione CSS presentano due distinti modelli di layout: 1) il modello nel flusso, dove la geometria di un elemento viene influenzata dalla geometria degli elementi che lo precedono e 2) il modello posizionato, dove la geometria di un elemento non viene influenzata dalla geometria degli elementi che lo precedono o, in ogni caso, viene influenzato più localmente. Il primo viene considerato come il caso "normale", e corrisponde alla normale formattazione HTML. Il secondo, chiamato anche "fuori dal flusso" da il controllo del layout all'autore, e l'autore deve specificare la posizione e la dimensione di tutti gli elementi posizionati. Ovviamente vi è una maggiore complessità nello gestire i due modelli contemporaneamente...

In generale il flusso globale del layout assomiglia al seguente:

  1. Ottenere il modello del contenuto del documento.
  2. Utilizzare lo Style System per stabilire lo stile di ciascun elemento nel modello di contenuto.
  3. Costruire i frame che corrispondono al modello del contenuto, secondo i dati di stile ottenuti.
  4. Eseguire il layout iniziale, o la risistemazione iniziale, per il frame appena costruito.

Questo è abbastanza semplice, ma viene in qualche misura complicato dal concetto di incrementalismo. Uno degli scopi del Layout System è quello di creare parti di presentazione non appena queste sono disponibili, piuttosto che aspettare che l'intero documento venga letto, analizzato e quindi presentato. Si tratta di un ovvio beneficio per documenti di grandi dimensioni, in quanto l'utente non deve aspettare la duecentesima pagina prima che la prima venga visualizzata. Quindi questa sequenza di operazioni Resolve Style, Create Frame, Layout Frame viene ripetuta più volte man mano che il contenuto diventa disponibile. Nel flusso normale questo è abbastanza naturale, poichè l'aggiunta sequenziale di nuovo contenuto si traduce nell'aggiunta di nuovi frame, e dato che ogni cosa si trova nel flusso, i nuovi frame non influenzano la geometria dei frame precedentemente formattati. Quando sono presenti frame fuori dal flusso ciò diventa problematico, in quanto a volte un elemento viene inserito in modo incrementale e così facendo invalida la formattazione dei frame che lo precedono e che sono già stati formattati. In questo caso il Layout System deve individuare l'impatto e la risistemazione nei confronti dei frame interessati. Ciò viene detto risistemazione incrementale.

Un altro compito del Layout è quello di gestire i cambiamenti dinamici al modello del contenuto, cambiamenti che hanno luogo dopo che il documento è stato caricato e (possibilmente) presentato. Questi cambiamenti dinamici vengono ottenuti con manipolazioni del modello del contenuto tramite il DOM (di solito con JavaScript). Quando un elemento viene modificato, aggiunto o rimosso, Layout viene avvisato. Se vengono inseriti elementi, vengono creati e formattati nuovi frame (e l'impatto su altri frame viene gestito con risistemazioni incrementali). Se del contenuto viene rimosso, i frame corrispondenti vengono distrutti. Se un elemento viene modificato, Layout deve determinare se la modifica influenza la formattazione degli altri elementi, e deve quindi riformattare, o ri-risistemare, gli elementi colpiti. In tutti i casi, l'individuazione dell'impatto è basilare per evitare il problema del mancato aggiornamento degli elementi (dando così una presentazione non valida) o, al contrario, di un aggiornamento eccessivo della presentazione (lavorando troppo), che può causare potenziali problemi di performance nel browser.

Un caso speciale di manipolazione dinamica del contenuto sono gli editor. Layout viene usato sia per implementare un editor "What You See Is What You Get" sia un controllo testuale a riga singola. In entrambi i casi il contenuto è manipolato dall'utente (tramite DOM ), e il risultato visivo deve essere mostrato il più rapidamente possibile, senza sfarfallii o altri artefatti che possono infastidire l'utente. Consideriamo il caso di un campo di testo: l'utente digita il testo su un form. Man mano che l'utente digita un nuovo carattere, questo viene inserito nel modello del contenuto. Questo fa si che Layout venga avvisato del fatto che una nuova porzione di contenuto è stata inserita, creando un nuovo frame e formattandolo. Bisogna che questo accada molto velocemente, in modo che la battitura dell'utente non venga ritardata. Nel caso di un editor visuale, l'utente si aspetta che le modifiche appaiano subito, non secondi dopo. Diventa fondamentale quando l'utente scrive nel documento: sarebbe una cosa assai poco usabile se la battitura di un carattere alla fine di un documento causasse la riformattazione di quest'ultimo (sarebbe troppo lento, almeno su macchine non potenti). Sia gli editor che i controlli testuali hanno quindi bisogno di notevoli requisiti a livello di performance per quel che riguarda la gestione del contenuto dinamico da parte del Layout System.

Le basi dei frame: Block e Line

Ci sono molti tipi di frame concepiti per rappresentare i vari tipi di formattazione degli elementi. I CSS 2 ne definiscono alcuni (block, inline, list-item, marker, run-in, compact, e vari tipi table) e i controlli dei form richiedono i loro tipi speciali di frame per essere formattati come ci si aspetta. I tipi di frame di base sono Block e Inline, che corrispondono ai concetti più importanti del Layout System, Block e Line.

Un blocco è una regione rettangolare composta da una o più righe. Una riga è una singola linea di testo o di altri elementi presentazionali. Tutto avviene nel contesto di un blocco, e tutto il contenuto di un blocco viene formattato in righe all'interno di quel blocco. Quando si cambia la larghezza di un blocco, le righe devono essere riformattate. Si consideri ad esempio un lungo paragrafo di testo all'interno di un paragrafo:

<p>
We need documentation for users, web developers, and developers working
on Mozilla. If you write your own code, document it. Much of the
existing code <b>isn’t very well documented</b>. 
In the process of figuring 
things out, try and document your discoveries. 
If you’d like to contribute, let us know.
</p>

Vi è un blocco che corrisponde all'elemento <p>, e quindi un certo numero di righe che corrispondono al testo. Quando la larghezza del blocco cambia (per esempio ridimensionando la finestra), la lunghezza delle righe cambia, e così appare più testo o meno testo su ogni riga. Il blocco gestisce le righe. Si noti che le righe possono contenere solo elementi inline, mentre il blocco può contenere sia elementi inline che blocchi.

Altri modelli di Layout: XUL

Oltre a gestire la formattazione definita dai fogli di stile, il Layout System fornisce un modo per integrare altri schemi di layout nella presentazione. Attualmente Layout supporta la formattazione degli elementi XUL, che utilizzano un linguaggio di layout basato su restrizioni. Il Box viene introdotto nel modello a frame del Layout System tramite un adattatore (BoxToBlockAdapter), che traduce il normale modello di layout nel modello di formattazione del box. A livello concettuale lo si potrebbe usare per introdurre altri sistemi di layout, ma è importante far notare che non vi è un sistema specifico atto allo scopo. Layout gestisce i frame, ma finché non avrà bisogno di influenzare elementi presentazionali di altri sistemi di layout, lo si può adattare usando un adattatore basato su frame.

Classi Core

Al livello più alto, il Layout System è un gruppo di classi che gestisce la presentazione all'interno di larghezze fisse e altezze illimitate (presentazione su schermo) o altezze di pagina discrete (presentazione a pagine). Andando più a fondo nel sistema scopriamo la sua complessità. L'idea di formattare testo e grafica all'interno di una data area dello schermo sembra semplice, ma l'interazione di elementi fuori/dentro il flusso, le considerazioni sulla resa incrementale, e la performance nel caso del contenuto dinamico rende l'idea di un sistema che ha molti compiti da svolgere e molti dati da gestire. Di seguito riportiamo le classi di alto livello che costituiscono il Layout System. Naturalmente questa è solo una piccola parte di tutte le classi nel Layout (si vedano i documenti sul design dettagliato per i dettagli delle classi, nel contesto del loro ruolo effettivo).

Presentation Shell / Presentation Context

Presentation Shell e Presentation Context forniscono la base per l'attuale presentazione. Il design originale di una singola Presentation Shell è quello di gestire Presentation Context multiple, ossia di permettere ad una singola shell di gestire presentazioni multiple. Non è chiaro se questo sia realmente possibile, ed in generale si presuppone che vi sia corrispondenza biunivoca tra una Presentation Shell ed una Presentation Context. Le due classi dovrebbero probabilmente essere unite, o si dovrebbe formalizzare la loro distinzione e utilizzarla nel codice. La Presentation Shell ha attualmente un riferimento alla Presentation Context. Ulteriori riferimenti alla Presentation Shell e alla Presentation Context saranno fatti usando il termine Presentation Shell.

La Presentation Shell è la base della presentazione, e come tale possiede e gestisce molti oggetti di layout usati per creare e mantenere una presentazione (si noti che Document Viewer possiede Presentation Shell, ed in alcuni casi è il creatore degli oggetti usati dalla Presentation Shell per gestire la presentazione. C'è bisogno di maggiori dettagli su Document Viewer). La Presentation Shell (o PresShell) è in primis colei che possiede gli oggetti di formattazione, i frame. La gestione dei frame viene facilitata dal Frame Manager, un istanza posseduta dalla PresShell. Inoltre la PresShell fornisce un heap speciale per i frame, chiamato Arena, che viene usato per allocare/deallocare i frame più velocemente e con meno probabilità di frammentare l'heap globale.

La PresShell possiede anche la radice dello Style System, lo Style Set. In molti casi la PresShell fornisce metodi allo Style Set, e di solito usa lo Style Set per la risoluzione dello stile e la gestione dello Style Sheet.

Uno degli aspetti critici della PresShell è la gestione della formattazione dei frame, o risistemazione. La PresShell possiede ed amministra un Reflow Queue dove le richieste di risistemazione vengono conservate finché non si deve eseguire una risistemazione, e quindi eseguite.

È importante anche guardare alla PresShell come ad un osservatore dei vari tipi di eventi nel sistema. Per esempio, essa riceve le notifiche degli eventi di caricamento del documento, che vengono usate per aggiornare in alcuni casi la formattazione dei frame. La PresShell riceve notifiche anche a proposito dei cambiamenti nel cursore e negli stati di focus, laddove gli aggiornamenti della selezione possono essere resi visibili.

Ci sono dozzine di altri oggetti gestiti dalla Presentation Shell e dalla Presentation Context, tutti necessari per l'implementazione interna. Questi dati e le loro regole saranno discussi nei documenti sulla Presentation Shell. Ai fini di questa analisi, Frames, Style Set e Reflow Queue sono le parti di alto livello più importanti della Presentation Shell.

Frame Manager

Il Frame Manager serve a gestire i frame. I frame sono oggetti di formattazione di base usati nel layout, e il Frame Manager rende i frame disponibili ai client. Vi sono diverse collezioni di frame gestite dal Frame Manager. La principale è un elenco di tutti i frame partendo dal frame radice. I client di solito non vogliono passare in rassegna tutti i frame partendo dalla radice per trovare il frame che gli interessa, e quindi il Frame Manager fornisce altre correlazioni in base ai bisogni dei client.

La correlazione di base è la Primary Frame Map. Questa collezione da accesso ad un frame designato come frame primario per una parte di contenuto. Quando viene creato un frame per una parte di contenuto, può essere il frame primario per quell'elemento (gli elementi che richiedono frame multipli hanno frame primari e secondari; solo il frame primario viene correlato). Il Frame Manager viene quindi istruito a memorizzare la correlazione tra l'elemento e il frame primario. Tale correlazione facilita l'aggiornamento di quei frame risultanti dai cambiamenti al contenuto (vedi sopra).

Un'altra importante correlazione gestita dal Frame Manager è quella relativa al contenuto non visualizzato. Quando un elemento viene definito senza visualizzazione (con la dichiarazione display: none), esso viene segnato con una entry speciale nella mappa dei non visualizzati. Questo è importante, perché nessun frame viene generato per questi elementi e i cambiamenti ai loro valori di stile e agli elementi devono ancora essere gestiti da Layout, nel caso in cui il loro valore di 'display' dovesse cambiare. La Undisplayed Map registra tutto il contenuto e i dati di stile per quegli elementi che non hanno frame (nota: l'architettura originale del Layout System includeva la creazione di frame per gli elementi privi di visualizzazione. C'è stato un cambiamento nel tempo, ma non c'è indicazione sul perché di tale cambiamento. Presumibilmente si rivela più efficiente in termini di spazio/tempo impedire la creazione di frame per gli elementi senza visualizzazione).

Il Frame Manager gestisce anche un elenco di Forms e Form Controls come nodi di contenuto. Ciò è legato presumibilmente al fatto che Layout è responsabile dell'invio dei form, ma si tratta di un errore che sarà corretto spostando l'invio dei form nel contenuto. Queste collezioni di Forms e Form Controls dovrebbero alla fine essere rimosse.

CSS Frame Constructor

Il Frame Constructor definisce i valori di stile per gli elementi e crea i frame appropriati che corrispondono a tali elementi. Oltre alla gestione della creazione dei frame, il Frame Constructor gestisce le modifiche ai frame. La Frame Construction di solito si ottiene usando metodi stateless, ma in alcuni casi vi è il bisogno di fornire un contesto ai frame creati come figli di un contenitore. Il Frame Manager usa la classe Frame Constructor State per gestire le informazioni importanti sul contenitore di un frame creato (e molte altre cose relative allo stato - da descrivere con maggior compiutezza).

Frame

Il Frame è il più basilare oggetto di layout. La classe nsFrame è la classe di base per tutti i frame, ed eredita dalla classe nsIFrame (nota: nsIFrame NON è un'interfaccia, ma una classe astratta di base. Un tempo era un'interfaccia, ma è stata modificata quando si è modificato lo Style System - il nome non fu cambiato per riflettere il fatto che non è un'interfaccia). Il Frame fornisce delle funzionalità generiche che possono essere usate dalle sottoclassi, ma di per sé non può essere istanziato.

nsFrame: il Frame fornisce un meccanismo per navigare verso un frame genitore così come verso un frame figlio. Tutti i frame hanno un genitore eccetto il frame radice. Il Frame è in grado di fornire sotto richiesta un riferimento al suo genitore e ai suoi figli. I dati fondamentali di tutti i frame comprendono: un rettangolo che descrive le dimensioni dei frame, un puntatore al contenuto che il frame rappresenta, il contesto di stile che rappresenta tutti i dati di stile corrispondenti al frame, un puntatore del frame genitore, un puntatore del frame consanguineo, e una serie di bit di stato.

I frame sono concatenati tramite link consanguinei. Dato un frame, si può andare verso il consanguineo di detto frame e anche all'indietro verso il frame genitore. Le specializzazioni del frame consentono anche la gestione dei frame figli; questa funzionalità è data dal Container Frame.

Container Frame: Il Container Frame è una specializzazione della classe frame di base che comporta la capacità di gestire un elenco di frame figli. Tutti i frame che hanno bisogno di gestire frame figli (per esempio frame che non sono essi stessi frame leaf) derivano dal Container Frame.

Torna su