Quella che segue รจ la traduzione del documento originale della Mozilla Foundation che allo stato attuale ha un valore puramente storico per comprendere l'evoluzione che ha portato Gecko, il motore di rendering di Firefox, ad assumere lo stato attuale nel corso degli anni.
Sguardo d'insieme
Il reflow è il processo con cui viene calcolata la geometria degli oggetti di formattazione del motore di layout. Gli oggetti di formattazione HTML vengono chiamati frame: un frame corrisponde all'informazione geometrica per un singolo elemento nel modello di contenuto. I frame vengono organizzati in una gerarchia che rispecchia la gerarchia di contenimento nel modello di contenuto. Un frame è rettangolare, con larghezza, altezza e un offset rispetto al frame genitore che lo contiene.
Possono essere necessari più frame per rappresentare un singolo elemento del modello di contenuto. Per esempio, il testo può essere diviso in diversi frame, uno per riga. In questo caso il frame primario è il frame che contiene la prima riga di testo, con frame di continuazione (o continuazioni) creati per le righe successive.
L'HTML usa un modello di layout basato sul flusso, il che significa che è possibile calcolare la geometria in un unico passaggio. Gli elementi che vengono dopo nel flusso di solito non influenzano la geometria degli elementi che li precedono, così il layout può procedere da sinistra a destra, dall'alto in basso per tutto il documento. Ci sono alcune eccezioni a questa regola: le tabelle HTML possono richiedere più di un passaggio.
Il modello di layout del box XUL, d'altra parte, è basato sulle restrizioni, il che significa che le preferenze e le restrizioni geometriche degli elementi vicini sono prese in considerazione prima del calcolo finale della geometria degli elementi. Il box è il tipo geometrico primitivo per il modello di layout XUL.
Tutto il reflow HTML, compreso il reflow iniziale, comincia dal frame radice, che
corrisponde all'elemento
<html>
del documento HTML. Il reflow procede in modo ricorsivo attraverso una parte o tutta
la gerarchia dei frame, calcolando le informazioni geometriche per ogni oggetto frame che lo richiede.
Il reflow può avere l'effetto collaterale di creare una nuova continuazione di frame, per
esempio per il testo.
Alcuni reflow sono immediati, in risposta alle azioni dell'utente o degli script
(per esempio ridimensionando la finestra o modificando il font del documento).
Questi vengono gestiti direttamente dalla Presentation Shell
(per esempio
nsIPresShell::StyleChangeReflow
), influenzando l'intero albero dei frame.
Altri reflow sono incrementali e sono gestiti in modo asincrono (per esempio quando
il contenuto arriva dalla rete). I reflow incrementali sono messi in coda
dalla Presentation Shell attraverso comandi successivi.
Stato del reflow
L'oggetto dello stato del reflow,
nsHTMLReflowState
, viene usato per passare informazioni
sulle restrizioni dai frame genitori ai frame figli. Per esempio un
<div>
con una data larghezza (impostata
attraverso la proprietà CSS width
) lo notifica
in questo oggetto prima di scendere ai suoi figli.
Quando il reflow comincia, lo stato del reflow radice viene
inizializzato con le informazioni sul contenitore di livello superiore
per la presentazione del documento (per esempio la larghezza e l'altezza
della finestra dell'applicazione). Questo viene passato come argomento al
metodo
Reflow
del frame radice nella gerarchia dei frame.
Ogni frame contenitore costruisce un nuovo oggetto dello stato del reflow (in base al proprio oggetto dello stato del reflow) nel quale il contenitore fa fluire i suoi figli. Molte delle restrizioni nel nuovo stato di reflow vengono calcolate quando viene creato lo stato (per esempio, lo spazio disponibile nel nuovo stato di reflow si calcola sottraendo il bordo e il padding del frame contenitore dallo spazio disponibile dello stato di reflow del genitore).
Motivi del reflow
Tutti i reflow hanno un motivo, che viene conservato dall'oggetto di stato del reflow (e può cambiare, come vedremo). Il motivo del reflow controlla il modo con cui un frame reagisce durante un reflow, ed è uno dei seguenti:
-
Initial
, per la prima volta in cui viene creata la gerarchia dei frame. In questo caso, un frame sa che non vi è uno stato residuo da usare per semplificare il calcolo della geometria. -
Incremental
, quando qualcosa cambia nell'albero dei frame (per esempio quando arriva del contenuto dalla rete o uno script manipola il DOM). Un reflow incrementale viene indirizzato ad un singolo frame nella gerarchia dei frame. Durante un reflow incrementale, un frame può supporre che nessuna delle restrizioni calcolate dall'alto (per esempio, la larghezza disponibile) viene modificata. Al contrario, qualcosa all'interno del frame è cambiato, il che può avere un impatto bottom-up nella gerarchia dei frame. -
Resize
, quando il contenitore circostante della gerarchia dei frame cambia. Durante un reflow di ridimensionamento, il frame può supporre che nessuno degli stati interni del frame (per esempio, il testo in un frame di testo) sia cambiato. Al contrario, è accaduto un cambiamento top-down nelle restrizioni del layout. -
StyleChange
, quando l'intera gerarchia dei frame deve essere attraversata per riparare ad un cambio di stile (per esempio una modifica nela dimensione del font). -
Dirty
, quando un frame contenitore ha consolidato diversi reflowIncremental
indirizzati ai suoi frame figli.
I reflow Initial, Incremental, Resize, e StyleChange possono essere eseguiti come un immediato reflow globale dalla Presentation Shell:
-
Un reflow iniziale viene eseguito quando la Presentation Shell viene inizializzata per far fluire la gerarchia di frame iniziale.
-
I reflow incrementali vengono eseguiti in massa quando la coda dei reflow incrementali della Presentation Shell viene elaborata in modo asincrono.
-
Un reflow di ridimensionamento viene eseguito quando cambiano le dimensioni della Presentation Shell (per esempio dopo che l'utente ha ridimensionato la finestra).
-
Un reflow del cambio di stile viene eseguito quando cambiano le informazioni di stile globali della Presentation Shell (per esempio con l'aggiunta o la rimozione di un foglio di stile o con una modifica al font).
Un reflow sporco non viene mai eseguito direttamente dalla Presentation Shell. Piuttosto, un reflow sporco viene rilevato quando un reflow incrementale raggiunge il suo frame di destinazione, descritto di seguito.
Metriche del reflow
L'oggetto delle metriche del reflow,
nsHTMLReflowMetrics
, viene usato per propagare informazioni
dai frame figli al genitore. Per esempio, le dimensioni di ogni frame figlio di un
<div>
senza dimensioni verrebbero passate al frame del
<div>
attraverso l'oggetto
the nsHTMLReflowMetrics
.
Reflow incrementale
Sebbene tutti i reflow in Gecko cerchino di riutilizzare
quanti più stati esistenti possibile (in questo senso
"incrementale") un reflow
Incremental
corrisponde ad un reflow
indirizzato ad un frame singolo nella gerarchia dei frame. Un frame
richiede un reflow
Incremental
(o ne viene richiesto uno per un frame nel mezzo) quando qualcosa
nel frame stesso viene modificato.
Scheduling. Per richiedere (o distribuire) un reflow incrementale
(per esempio in risposta ad una modifica nel modello del contenuto) viene creato
un oggetto comando e passato alla Presentation Shell tramite il metodo
nsIPresShell::AppendReflowCommand
. La Presentation Shell non elabora
il comando immediatamente, ma mette in coda il comando e lo elabora in modo asincrono
insieme ad altri comandi di reflow in coda.
Fusione. Come vedremo, il comando di reflow ha un tipo e un frame di destinazione. Comandi di reflow multipli dello stesso tipo e con lo stesso frame di destinazione vengono fusi: la Presentation Shell si rifiuta di aggiungere comandi consecutivi dello stesso tipo e per lo stesso frame alla sua coda. Un chiamante può anche cancellare un comando di reflow in coda (per esempio se il frame di destinazione viene distrutto).
Smistamento.
La Presentation Shell elabora la coda di reflow rimuovendo un singolo comando di reflow
dalla coda e smistandolo al suo frame di destinazione
(ma si veda il lavoro
sull'albero di reflow
che rimuove diversi comandi dalla coda in una sola volta).
Viene creato un percorso dal frame di destinazione a quello
radice e memorizzato nel comando di reflow. Viene creato un oggetto di stato
del reflow con motivo
Incremental
, il comando di reflow viene memorizzato nello stato e viene invocato il metodo
Reflow
del frame radice.
Elaborazione. Il frame radice nota il motivo di reflow
Incremental
specificato nello stato di reflow, e analizza il percorso
contenuto nell'oggetto di comando del reflow. Nello specifico, estrae il frame
successivo insieme con il percorso preso dall'oggetto del comando di reflow, crea
il suo stato di reflow, anch'esso con motivo
Incremental
, e invoca il metodo
Reflow
del frame successivo.
Il reflow incrementale procede in modo ricorsivo lungo la gerarchia dei frame. Ogni frame con il percorso del reflow incrementale (come specificato nell'oggetto del comando di reflow) estrae il frame successivo e smista il reflow verso il basso. Al fine di smistare correttamente il reflow al frame figlio, il frame può aver bisogno di eseguire una riparazione di stato (per esempio, un frame di blocco attraverserà il suo elenco di riga per riparare allo spazio occupato dai frame flottati).
Ad un certo punto il reflow incrementale raggiunge il frame di destinazione. A quel punto il tipo di comando del reflow diventa importante.
-
ContentChanged
indica che il contenuto che corrisponde al frame di destinazione è cambiato. Per esempio, il testo associato con un frame di testo è stato modificato. In realtà l'unico frame che risponde a questo tipo di reflow è il frame di blocco. Il frame di blocco tratta questo cambiamento come un 'reflow globale' (ossia come un ridimensionamento). Questo mi fa ritenere che potremmo probabilmente eliminare questa classe. -
StyleChanged
indica che le informazioni di stile relative al frame di destinazione sono cambiate, per esempio con l'aumento delle dimensioni del font. Questo fa in modo che il frame muti il motivo del reflow inStyleChange
, che viene propagato in modo ricorsivo all'intero sottoalbero del frame di destinazione. -
ReflowDirty
indica che il frame contenitore ha deciso di fondere vari reflow incrementali indirizzati ai suoi figli in un unico reflow. Il frame contenitore conserva lo stato necessario a determinare quali figli debbano avere il reflow. -
UserDefined
, per "situazioni speciali". Attualmente viene usato solo dal frame del viewport per lo scheduling di un reflow di tutti i frame del viewport posizionati in modo fisso. Dovremmo cercare di eliminarlo.
Un reflow incrementale può danneggiare altre parti della gerarchia dei frame (per esempio, la modifica alla dimensione del font in un paragrafo può allargare o restringere il paragrafo). Un contenitore deve quindi propagare ogni danno causato dal reflow incrementale del frame figlio, possibilmente eseguendo il reflow degli altri figli.
Reflow sporchi
Se avvengono diverse modifiche incrementali nella stessa parte della
gerarchia dei frame, è possibile avere diversi reflow
Incremental
indirizzati ai frame vicini. In questo caso è
probabile che i singoli reflow incrementali finiranno col fare del lavoro
ridondante. Per esempio, ogni carattere inserito in un input di testo
potrebbe generare un reflow incrementale indirizzato al frame di testo.
Se dovessimo elaborarli singolarmente, l'input di testo verrebbe renderizzato
per ogni inserimento di testo, il che sarebbe dannoso se la progressione di un singolo
reflow superasse la velocità di digitazione. Lo scopo del reflow
Dirty
è di permettere a questi reflow singoli di fondersi
in modo intelligente.
Un frame che decide di aver bisogno di un reflow sporco imposta su se stesso lo stato
NS_FRAME_IS_DIRTY
e quindi chiama il metodo
ReflowDirtyChild
sul suo frame genitore. In
ReflowDirtyChild
il frame genitore imposta lo stato
NS_FRAME_HAS_DIRTY_CHILD
su se stesso, e svolge
ogni lavoro necessario a ricordarsi quale figlio è sporco (per
esempio, il frame di blocco marca il box di riga come sporco, il quale
contiene il frame figlio). Il frame genitore può decidere di fare lo scheduling di un reflow
ReflowDirty
Incremental
indirizzato a se stesso, o delegare
il compito al suo genitore. Se decide di delegare, allora imposta lo stato
NS_FRAME_IS_DIRTY
su se stesso e chiama in modo ricorsivamente
ReflowDirtyChild
.
Alla fine il reflow ReflowDirty
Incremental
viene smistato, e arriva al frame contenitore che ne fa lo scheduling.
Il frame di destinazione ripara le informazioni conservate (per esempio, il frame
di blocco itera attraverso i box di riga sporchi) ed esegue il reflow dei frame figlio
sporchi.
Non vi è perdita di informazioni se un reflow
ReflowDirty
Incremental
fonde
diversi tipi di reflow incrementali (per esempio un
ContentChanged
con un
StyleChanged
)?
No, perché questi tipi di reflow non vengono fusi, ma vengono messi
in coda nella Presentation Shell.
Interazione HTML e XUL
Come detto prima, HTML e XUL hanno diversi modelli di layout, il primo
basato sul flusso e il secondo basato su un modello di restrizioni.
Queste differenze vengono mediate da dua classi:
nsBoxFrame
e
nsBoxToBlockAdaptor
.
nsBoxFrame
nsBoxFrame
è un frame HTML che "circonda" un box XUL.
Il suo scopo è quello di convertire i reflow HTML nei loro analoghi box.
nsBoxToBlockAdaptor
nsBoxToBlockAdaptor
è un box XUL che circonda un frame di blocco
HTML, usato per convertire le modifiche nel layout del box in reflow HTML.