mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-12 14:37:50 +00:00
3306 lines
94 KiB
C
3306 lines
94 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
|
|
#include "xp.h"
|
|
#include "pa_parse.h"
|
|
#include "pa_tags.h"
|
|
#include "layout.h"
|
|
#include "laylayer.h"
|
|
#include "laystyle.h"
|
|
#include "libmocha.h"
|
|
#include "libevent.h"
|
|
#include "layers.h"
|
|
#include "intl_csi.h"
|
|
#ifdef DOM
|
|
#include "domstyle.h"
|
|
#include "lm_dom.h"
|
|
#include "laydom.h"
|
|
#endif
|
|
|
|
/* This struct is used during the processing of a <LAYER> or <ILAYER>
|
|
* tag, but discarded after the tag is closed. It is used to store the
|
|
* current document state while process the tag and channel everything
|
|
* into the layer.
|
|
*/
|
|
struct lo_Block {
|
|
MWContext *context;
|
|
|
|
/* Saved document state, restored after block is layed out */
|
|
lo_DocState *saved_state;
|
|
|
|
int32 start_x, start_y; /* Initial upper-left corner of block,
|
|
in parent layer coord system */
|
|
char *old_base_url;
|
|
uint8 old_body_attr;
|
|
|
|
char * name; /* Identifier for this layer */
|
|
char * above; /* Name of layer above this layer or NULL */
|
|
char * below; /* Name of layer below this layer or NULL */
|
|
int32 z_order; /* Z-order if above/below unspecified */
|
|
|
|
int32 x_offset, y_offset; /* Layer coordinate translation
|
|
(for in-flow layers) */
|
|
PRPackedBool is_inflow; /* Is this an out-of-flow or in-flow layer ? */
|
|
PRPackedBool is_inline; /* Is this inline or SRCed */
|
|
PRPackedBool is_dynamic; /* Has the SRC been dynamically changed? */
|
|
PRPackedBool uses_ss_positioning; /* Created using style-sheet 'position'
|
|
property instead of tag ? */
|
|
int clip_expansion_policy; /* Expand clip to contain layer content ? */
|
|
|
|
CL_Layer *layer;
|
|
LO_CellStruct *cell;
|
|
|
|
lo_LayerDocState *old_layer_state; /* Used during table relayout */
|
|
char *match_code; /* Value of layer tag's MATCH parameter */
|
|
char *source_url;
|
|
};
|
|
|
|
static int lo_AppendLayerElement(MWContext *context, lo_DocState *state,
|
|
LO_BlockInitializeStruct *param,
|
|
lo_LayerDocState *layerDoc, Bool is_end);
|
|
|
|
static PRBool
|
|
lo_SetupDocStateForLayer(lo_DocState *state,
|
|
lo_LayerDocState *layer_state,
|
|
int32 width,
|
|
int32 height,
|
|
PRBool is_inflow,
|
|
PRBool reflow);
|
|
|
|
#ifdef XP_MAC
|
|
PRIVATE
|
|
#endif
|
|
lo_LayerDocState *
|
|
lo_GetLayerStateFromId(MWContext *context, int32 layer_id)
|
|
{
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
lo_LayerDocState *layer_state;
|
|
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
XP_ASSERT(top_state);
|
|
if (!top_state)
|
|
return NULL;
|
|
|
|
XP_ASSERT(layer_id <= top_state->max_layer_num);
|
|
if (layer_id > top_state->max_layer_num)
|
|
return NULL;
|
|
layer_state = top_state->layers[layer_id];
|
|
/* XP_ASSERT(layer_state); */
|
|
return layer_state;
|
|
}
|
|
|
|
void *
|
|
LO_GetLayerMochaObjectFromId(MWContext *context, int32 layer_id)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_GetLayerStateFromId(context, layer_id);
|
|
if (!layer_state)
|
|
return NULL;
|
|
return layer_state->mocha_object;
|
|
}
|
|
|
|
void *
|
|
LO_GetLayerMochaObjectFromLayer(MWContext *context, CL_Layer *layer)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_GetLayerState(layer);
|
|
if (!layer_state)
|
|
return NULL;
|
|
return layer_state->mocha_object;
|
|
}
|
|
|
|
void
|
|
LO_SetLayerMochaObject(MWContext *context, int32 layer_id, void *mocha_object)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_GetLayerStateFromId(context, layer_id);
|
|
if (!layer_state)
|
|
return;
|
|
layer_state->mocha_object = mocha_object;
|
|
}
|
|
|
|
CL_Layer *
|
|
LO_GetLayerFromId(MWContext *context, int32 layer_id)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_GetLayerStateFromId(context, layer_id);
|
|
if (!layer_state)
|
|
return NULL;
|
|
return layer_state->layer;
|
|
}
|
|
|
|
int32
|
|
LO_GetIdFromLayer(MWContext *context, CL_Layer *layer)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_GetLayerState(layer);
|
|
XP_ASSERT(layer_state);
|
|
if (!layer_state)
|
|
return 0;
|
|
return layer_state->id;
|
|
}
|
|
|
|
int32
|
|
LO_GetNumberOfLayers(MWContext *context)
|
|
{
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if (!top_state)
|
|
return 0;
|
|
|
|
return (top_state->max_layer_num);
|
|
}
|
|
|
|
PRBool
|
|
lo_IsCurrentLayerDynamic(lo_DocState *state)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
lo_Block *block = layer_state->temp_block;
|
|
|
|
if (layer_state->id == LO_DOCUMENT_LAYER_ID)
|
|
return PR_FALSE;
|
|
|
|
XP_ASSERT(block);
|
|
if (!block)
|
|
return PR_FALSE;
|
|
|
|
return (PRBool)block->is_dynamic;
|
|
}
|
|
|
|
PRBool
|
|
lo_IsAnyCurrentAncestorSourced(lo_DocState *state)
|
|
{
|
|
lo_LayerStack *lptr;
|
|
lo_TopState *top_state = state->top_state;
|
|
|
|
if (top_state->layer_stack == NULL)
|
|
return PR_FALSE;
|
|
|
|
lptr = top_state->layer_stack;
|
|
while (lptr && lptr->layer_state &&
|
|
(lptr->layer_state->id != LO_DOCUMENT_LAYER_ID)) {
|
|
lo_Block *block = lptr->layer_state->temp_block;
|
|
if (block && !block->is_inline)
|
|
return PR_TRUE;
|
|
|
|
lptr = lptr->next;
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool
|
|
lo_IsTagInSourcedLayer(lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
PRBool ancestor_sourced = PR_FALSE;
|
|
PRBool current_layer_sourced = PR_FALSE;
|
|
lo_LayerStack *lptr;
|
|
lo_TopState *top_state = state->top_state;
|
|
|
|
if (top_state->layer_stack == NULL)
|
|
return PR_FALSE;
|
|
|
|
lptr = top_state->layer_stack;
|
|
while (lptr && lptr->layer_state &&
|
|
(lptr->layer_state->id != LO_DOCUMENT_LAYER_ID)) {
|
|
lo_Block *block = lptr->layer_state->temp_block;
|
|
if (block && !block->is_inline) {
|
|
if (top_state->layer_stack == lptr)
|
|
current_layer_sourced = PR_TRUE;
|
|
else {
|
|
ancestor_sourced = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
lptr = lptr->next;
|
|
}
|
|
|
|
/*
|
|
* If we're not dealing with a </LAYER> or </ILAYER> tag,
|
|
* then we just return whether any current layer ancestor
|
|
* has a SRC attribute.
|
|
*/
|
|
if (((tag->type != P_LAYER) && (tag->type != P_ILAYER)) ||
|
|
(tag->is_end == FALSE) ||
|
|
(top_state->ignore_layer_nest_level != 0))
|
|
return (PRBool)(ancestor_sourced || current_layer_sourced);
|
|
/*
|
|
* Otherwise, it may be possible that the current close tag
|
|
* actually closes out the sourced layer and isn't actually
|
|
* part of the content of the sourced layer.
|
|
*/
|
|
else
|
|
return ancestor_sourced;
|
|
}
|
|
|
|
|
|
/*
|
|
* Are there any dynamic layers on the current layer_stack?
|
|
*/
|
|
PRBool
|
|
lo_IsAnyCurrentAncestorDynamic(lo_DocState *state)
|
|
{
|
|
lo_LayerStack *lptr;
|
|
lo_TopState *top_state = state->top_state;
|
|
|
|
if (top_state->layer_stack == NULL)
|
|
return PR_FALSE;
|
|
|
|
lptr = top_state->layer_stack;
|
|
while (lptr && lptr->layer_state &&
|
|
(lptr->layer_state->id != LO_DOCUMENT_LAYER_ID)) {
|
|
lo_Block *block = lptr->layer_state->temp_block;
|
|
if (block && block->is_dynamic)
|
|
return PR_TRUE;
|
|
|
|
lptr = lptr->next;
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
static void
|
|
lo_init_block_cell(MWContext *context, lo_DocState *state, lo_Block *block)
|
|
{
|
|
LO_CellStruct *cell;
|
|
|
|
if ((block == NULL)||(block->cell == NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
cell = block->cell;
|
|
|
|
cell->type = LO_CELL;
|
|
cell->ele_id = NEXT_ELEMENT;
|
|
cell->x = state->x;
|
|
cell->x_offset = 0;
|
|
cell->y = state->y;
|
|
cell->y_offset = 0;
|
|
cell->width = 0;
|
|
cell->height = 0;
|
|
cell->line_height = state->line_height;
|
|
cell->next = NULL;
|
|
cell->prev = NULL;
|
|
cell->FE_Data = NULL;
|
|
cell->cell_float_list = NULL;
|
|
cell->backdrop.bg_color = NULL;
|
|
cell->backdrop.url = NULL;
|
|
cell->border_width = 0;
|
|
cell->border_vert_space = 0;
|
|
cell->border_horiz_space = 0;
|
|
cell->ele_attrmask = 0;
|
|
cell->sel_start = -1;
|
|
cell->sel_end = -1;
|
|
cell->cell_list = NULL;
|
|
cell->cell_list_end = NULL;
|
|
cell->cell_float_list = NULL;
|
|
cell->cell_inflow_layer = NULL;
|
|
cell->cell_bg_layer = NULL;
|
|
cell->table_cell = NULL;
|
|
cell->table_row = NULL;
|
|
cell->table = NULL;
|
|
}
|
|
|
|
lo_LayerDocState *
|
|
lo_NewLayerState(MWContext *context)
|
|
{
|
|
lo_LayerDocState *layer_state;
|
|
XP_Rect everything = CL_MAX_RECT;
|
|
|
|
layer_state = XP_NEW_ZAP(lo_LayerDocState);
|
|
if (layer_state == NULL)
|
|
return NULL;
|
|
|
|
layer_state->id = 0; /* This will be filled in later */
|
|
layer_state->cell = NULL;
|
|
layer_state->viewRect = everything;
|
|
layer_state->doc_lists = XP_NEW_ZAP(lo_DocLists);
|
|
if (!layer_state->doc_lists) {
|
|
XP_FREE(layer_state);
|
|
return NULL;
|
|
}
|
|
if (!lo_InitDocLists(context, layer_state->doc_lists)) {
|
|
XP_FREE(layer_state->doc_lists);
|
|
XP_FREE(layer_state);
|
|
return NULL;
|
|
}
|
|
|
|
return layer_state;
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
PRIVATE
|
|
#endif
|
|
void
|
|
lo_DeleteBlock(lo_Block* block)
|
|
{
|
|
XP_FREEIF(block->name);
|
|
XP_FREEIF(block->above);
|
|
XP_FREEIF(block->below);
|
|
XP_FREEIF(block->saved_state);
|
|
XP_FREEIF(block->match_code);
|
|
XP_FREEIF(block->source_url);
|
|
XP_FREE(block);
|
|
}
|
|
|
|
void
|
|
lo_DeleteLayerState(MWContext *context, lo_DocState *state,
|
|
lo_LayerDocState *layer_state)
|
|
{
|
|
/* Get rid of the layer_state's contents */
|
|
if (layer_state->cell)
|
|
lo_RecycleElements(context, state, (LO_Element *)layer_state->cell);
|
|
|
|
/* Get rid of the layer_state's layer */
|
|
if (layer_state->layer) {
|
|
CL_Layer *parent_layer = CL_GetLayerParent(layer_state->layer);
|
|
if (parent_layer)
|
|
CL_RemoveChild(parent_layer, layer_state->layer);
|
|
LO_LockLayout();
|
|
CL_DestroyLayerTree(layer_state->layer);
|
|
LO_UnlockLayout();
|
|
}
|
|
|
|
lo_DeleteDocLists(context, layer_state->doc_lists);
|
|
if (layer_state->id != LO_DOCUMENT_LAYER_ID)
|
|
XP_FREE(layer_state->doc_lists);
|
|
if (layer_state->temp_block)
|
|
lo_DeleteBlock(layer_state->temp_block);
|
|
XP_FREE(layer_state);
|
|
}
|
|
|
|
/* Number of entries to grow layers array when it is too small */
|
|
#define LAYER_ARRAY_GROW_SIZE 50
|
|
|
|
/*
|
|
* Adds the layer_state to the layer list. If one with the same
|
|
* id already exists (because of table relayout), we replace
|
|
* it with the new one. The old layer_state is returned, so that
|
|
* it may be discarded when the new layer is done loading.
|
|
*/
|
|
lo_LayerDocState *
|
|
lo_append_to_layer_array(MWContext *context, lo_TopState *top_state,
|
|
lo_DocState *state,
|
|
lo_LayerDocState *layer_state)
|
|
{
|
|
int32 id;
|
|
|
|
XP_ASSERT(top_state);
|
|
if (!top_state)
|
|
return NULL;
|
|
|
|
id = ++top_state->current_layer_num;
|
|
layer_state->id = id;
|
|
|
|
if (state)
|
|
state->current_layer_num_max = id;
|
|
|
|
/*
|
|
* If we're doing table relayout, we find the corresponding layer in
|
|
* the layer list from a previous pass and replace it with the current
|
|
* layer.
|
|
*/
|
|
if (state && state->in_relayout) {
|
|
lo_LayerDocState **old_layer_statep = &top_state->layers[id];
|
|
lo_LayerDocState *old_layer_state = *old_layer_statep;
|
|
|
|
XP_ASSERT(old_layer_state);
|
|
XP_ASSERT(old_layer_state->id == id);
|
|
|
|
/*
|
|
* Copy over the mocha object, since the reflection might
|
|
* already have happened.
|
|
*/
|
|
layer_state->mocha_object = old_layer_state->mocha_object;
|
|
|
|
/* Out with the old and in with the new */
|
|
*old_layer_statep = layer_state;
|
|
return old_layer_state;
|
|
}
|
|
|
|
/* Extend the layers array if it's full */
|
|
if (id >= top_state->num_layers_allocated) {
|
|
int32 new_count = top_state->num_layers_allocated + LAYER_ARRAY_GROW_SIZE;
|
|
int32 nsize = new_count * sizeof(lo_LayerDocState*);
|
|
lo_LayerDocState **new_layers;
|
|
|
|
if (top_state->num_layers_allocated == 0)
|
|
new_layers = (lo_LayerDocState**)XP_ALLOC(nsize);
|
|
else
|
|
new_layers = (lo_LayerDocState**)XP_REALLOC(top_state->layers, nsize);
|
|
|
|
XP_ASSERT(new_layers);
|
|
if (!new_layers)
|
|
return NULL;
|
|
top_state->layers = new_layers;
|
|
top_state->num_layers_allocated = new_count;
|
|
}
|
|
|
|
top_state->layers[id] = layer_state;
|
|
|
|
if (id > top_state->max_layer_num)
|
|
top_state->max_layer_num = id;
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
lo_block_src_exit_fn(URL_Struct *url_struct, int status, MWContext *context)
|
|
{
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
|
|
/* XXX need state to be subdoc state? */
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if ((top_state == NULL) ||
|
|
((state = top_state->doc_state) == NULL)) {
|
|
return;
|
|
}
|
|
|
|
/* Flush tags blocked by this <LAYER SRC="URL"> tag. We treat
|
|
the special case of an interrupted stream as normal completion,
|
|
as this is the resulting error code when we do
|
|
<LAYER SRC=some.gif> and some.gif is in the image cache. */
|
|
if (status >= 0 || status == MK_INTERRUPTED) {
|
|
top_state->layout_blocking_element = NULL;
|
|
lo_FlushBlockage(context, state, state);
|
|
}
|
|
/* XXX What's the right thing to do when we fail???
|
|
* Presumably we don't want to flush the blockage and
|
|
* continue as if nothing happened. But in some cases,
|
|
* we do want to recover in some way.
|
|
*/
|
|
NET_FreeURLStruct(url_struct);
|
|
}
|
|
|
|
int32
|
|
lo_GetEnclosingLayerWidth(lo_DocState *state)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
|
|
/* Special case 100% value for top-level document so that
|
|
it doesn't include window margins. */
|
|
if (layer_state->id == LO_DOCUMENT_LAYER_ID)
|
|
return state->win_left + state->win_width + state->win_right;
|
|
|
|
return state->right_margin - state->left_margin;
|
|
}
|
|
|
|
/* Convert a string containing a horizontal dimension into layout coordinates,
|
|
handling percentages if necessary. */
|
|
static int32
|
|
lo_parse_horizontal_param(char *value, lo_DocState *state, MWContext *context)
|
|
{
|
|
int32 parent_width = lo_GetEnclosingLayerWidth(state);
|
|
XP_Bool is_percent;
|
|
int32 ival;
|
|
|
|
ival = lo_ValueOrPercent(value, &is_percent);
|
|
if (is_percent)
|
|
{
|
|
if (state->allow_percent_width == FALSE)
|
|
{
|
|
ival = 0;
|
|
}
|
|
else
|
|
{
|
|
ival = (parent_width * ival) / 100;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ival = FEUNITS_X(ival, context);
|
|
}
|
|
return ival;
|
|
}
|
|
|
|
int32
|
|
lo_GetEnclosingLayerHeight(lo_DocState *state)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
return layer_state->height;
|
|
}
|
|
|
|
/* Convert a string containing a vertical dimension into layout coordinates,
|
|
handling percentages if necessary. */
|
|
static int32
|
|
lo_parse_vertical_param(char *value, lo_DocState *state, MWContext *context)
|
|
{
|
|
XP_Bool is_percent;
|
|
int32 ival;
|
|
int32 parent_height = lo_GetEnclosingLayerHeight(state);
|
|
|
|
ival = lo_ValueOrPercent(value, &is_percent);
|
|
if (is_percent)
|
|
ival = (parent_height * ival) / 100;
|
|
else
|
|
ival = FEUNITS_Y(ival, context);
|
|
return ival;
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
PRIVATE
|
|
#endif
|
|
int
|
|
lo_parse_clip(char *str, XP_Rect *clip,
|
|
lo_DocState *state, MWContext *context)
|
|
{
|
|
int32 coord, coords[4];
|
|
int coord_count;
|
|
int clip_expansion_policy = LO_AUTO_EXPAND_NONE;
|
|
for (coord_count = 0; coord_count <= 3; coord_count++) {
|
|
|
|
/* Skip leading whitespace and commas */
|
|
while (XP_IS_SPACE(*str) || (*str == ','))
|
|
str++;
|
|
|
|
if (!*str)
|
|
break;
|
|
|
|
/* Parse distinguished "auto" value */
|
|
if (!XP_STRNCASECMP(str, "auto", 4)) {
|
|
str += 4;
|
|
clip_expansion_policy |= 1;
|
|
coords[coord_count] = 0;
|
|
} else {
|
|
if (coord_count & 1)
|
|
coord = lo_parse_vertical_param(str, state, context);
|
|
else
|
|
coord = lo_parse_horizontal_param(str, state, context);
|
|
coords[coord_count] = coord;
|
|
|
|
/* Skip over the number and percentage sign */
|
|
while (*str && !XP_IS_SPACE(*str) && !(*str == ','))
|
|
str++;
|
|
}
|
|
clip_expansion_policy <<= 1;
|
|
}
|
|
clip_expansion_policy >>= 1;
|
|
|
|
if (coord_count == 2) {
|
|
clip->right = coords[0];
|
|
clip->bottom = coords[1];
|
|
return clip_expansion_policy;
|
|
} else if (coord_count >= 4) {
|
|
clip->left = coords[0];
|
|
clip->top = coords[1];
|
|
clip->right = coords[2];
|
|
clip->bottom = coords[3];
|
|
return clip_expansion_policy;
|
|
}
|
|
|
|
/* Error - don't clip at all */
|
|
return LO_AUTO_EXPAND_CLIP;
|
|
}
|
|
|
|
extern XP_Bool
|
|
lo_BeginLayer(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_BlockInitializeStruct *param,
|
|
XP_Bool is_inflow);
|
|
|
|
|
|
void
|
|
lo_FreeBlockInitializeStruct(LO_BlockInitializeStruct *param)
|
|
{
|
|
if(param)
|
|
{
|
|
XP_FREEIF(param->clip);
|
|
XP_FREEIF(param->overflow);
|
|
XP_FREEIF(param->name);
|
|
XP_FREEIF(param->id);
|
|
XP_FREEIF(param->above);
|
|
XP_FREEIF(param->below);
|
|
XP_FREEIF(param->visibility);
|
|
XP_FREEIF(param->bgcolor);
|
|
XP_FREEIF(param->bgimage);
|
|
XP_FREEIF(param->src);
|
|
|
|
XP_FREE(param);
|
|
}
|
|
}
|
|
|
|
/* Start a <LAYER> or an <ILAYER>. */
|
|
void
|
|
lo_BeginLayerTag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context);
|
|
int16 win_csid = INTL_GetCSIWinCSID(c);
|
|
LO_BlockInitializeStruct *param;
|
|
char *val;
|
|
lo_LayerDocState *layer_state;
|
|
CL_Layer *parent_layer;
|
|
#ifdef DOM
|
|
DOM_AttributeEntry *entry;
|
|
DOM_Node *node = ACTIVE_NODE(state);
|
|
DOM_StyleDatabase *db = state->top_state->style_db;
|
|
JSContext *cx = context->mocha_context;
|
|
#endif
|
|
|
|
if (!context->compositor)
|
|
return;
|
|
|
|
layer_state = lo_CurrentLayerState(state);
|
|
parent_layer = layer_state->layer;
|
|
|
|
param = XP_NEW_ZAP(LO_BlockInitializeStruct);
|
|
|
|
if(!param)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Get the horizontal position of the block
|
|
*/
|
|
val = (char*) lo_FetchParamValue(context, tag, PARAM_LEFT);
|
|
if(val)
|
|
{
|
|
param->has_left = TRUE;
|
|
param->left = lo_parse_horizontal_param(val, state, context);
|
|
XP_FREE(val);
|
|
} else {
|
|
/* No LEFT attribute. Is there an X attribute ? */
|
|
val = (char*) lo_FetchParamValue(context, tag, PARAM_PAGEX);
|
|
if(val)
|
|
{
|
|
param->has_left = TRUE;
|
|
param->left = (int32)XP_ATOI(val);
|
|
|
|
/* Convert from absolute coordinates to layer-relative coordinates */
|
|
param->left -= CL_GetLayerXOrigin(parent_layer);
|
|
XP_FREE(val);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the vertical position of the block
|
|
*/
|
|
val = (char*)lo_FetchParamValue(context, tag, PARAM_TOP);
|
|
if (val)
|
|
{
|
|
param->has_top = TRUE;
|
|
param->top = lo_parse_vertical_param(val, state, context);
|
|
XP_FREE(val);
|
|
} else {
|
|
/* No TOP attribute. Is there a Y attribute ? */
|
|
val = (char*) lo_FetchParamValue(context, tag, PARAM_PAGEY);
|
|
if(val)
|
|
{
|
|
param->has_top = TRUE;
|
|
param->top = (int32)XP_ATOI(val);
|
|
|
|
/* Convert from absolute coordinates to layer-relative coordinates */
|
|
param->top -= CL_GetLayerYOrigin(parent_layer);
|
|
XP_FREE(val);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse the comma separated coordinate list into an
|
|
* array of integers.
|
|
*/
|
|
val = (char*)lo_FetchParamValue(context, tag, PARAM_CLIP);
|
|
if(val)
|
|
{
|
|
param->clip = XP_NEW_ZAP(XP_Rect);
|
|
|
|
if(param->clip)
|
|
{
|
|
XP_BZERO(param->clip, sizeof(XP_Rect));
|
|
param->clip_expansion_policy =
|
|
lo_parse_clip(val, param->clip, state, context);
|
|
}
|
|
}
|
|
else
|
|
param->clip_expansion_policy = LO_AUTO_EXPAND_CLIP;
|
|
|
|
/*
|
|
* Get the width parameter, in absolute or percentage.
|
|
* If percentage, make it absolute.
|
|
*/
|
|
val = (char*)lo_FetchParamValue(context, tag, PARAM_WIDTH);
|
|
if(val)
|
|
{
|
|
Bool is_percent;
|
|
int32 ival;
|
|
|
|
ival = lo_ValueOrPercent(val, &is_percent);
|
|
if (is_percent)
|
|
param->percent_width = (uint8) ival;
|
|
|
|
param->has_width = TRUE;
|
|
param->width = lo_parse_horizontal_param(val, state, context);
|
|
XP_FREE(val);
|
|
}
|
|
|
|
/*
|
|
* Get the height parameter, in absolute or percentage.
|
|
* If percentage, make it absolute.
|
|
*/
|
|
val = (char*)lo_FetchParamValue(context, tag, PARAM_HEIGHT);
|
|
if(val)
|
|
{
|
|
Bool is_percent;
|
|
int32 ival;
|
|
|
|
ival = lo_ValueOrPercent(val, &is_percent);
|
|
if (is_percent)
|
|
param->percent_height = (uint8) ival;
|
|
|
|
param->has_height = TRUE;
|
|
param->height = lo_parse_vertical_param(val, state, context);
|
|
XP_FREE(val);
|
|
}
|
|
|
|
/* Get the OVERFLOW attribute. */
|
|
param->overflow = (char*)lo_FetchParamValue(context, tag, PARAM_OVERFLOW);
|
|
|
|
/*
|
|
* Get the optional name for this layer.
|
|
*/
|
|
param->name = (char*)PA_FetchParamValue(tag, PARAM_NAME, win_csid);
|
|
|
|
/* Use either NAME or ID, since we will probably be switching to
|
|
the latter */
|
|
param->id = (char*)PA_FetchParamValue(tag, PARAM_ID, win_csid);
|
|
|
|
/*
|
|
* Get the optional "above" name for the layer we are above.
|
|
*/
|
|
param->above = (char*)PA_FetchParamValue(tag, PARAM_ABOVE, win_csid);
|
|
|
|
/*
|
|
* Get the optional "below" name for the layer we are below.
|
|
*/
|
|
param->below = (char*)PA_FetchParamValue(tag, PARAM_BELOW, win_csid);
|
|
|
|
/*
|
|
* Get the Z-order of the block
|
|
*/
|
|
val = (char*)lo_FetchParamValue(context, tag, PARAM_ZINDEX);
|
|
if (val) {
|
|
param->zindex = XP_ATOI(val);
|
|
param->has_zindex = TRUE;
|
|
XP_FREE(val);
|
|
}
|
|
else
|
|
param->has_zindex = FALSE;
|
|
|
|
/* Get the VISIBILITY parameter to know if this layer starts hidden. */
|
|
param->visibility = (char*)PA_FetchParamValue(tag, PARAM_VISIBILITY, win_csid);
|
|
|
|
/* Process background color (BGCOLOR) attribute, if present. */
|
|
param->bgcolor = (char*)PA_FetchParamValue(tag, PARAM_BGCOLOR, win_csid);
|
|
|
|
#ifdef DOM
|
|
/* if there was no bgcolor specified for the layer, find one in style */
|
|
if (!param->bgcolor) {
|
|
if (!DOM_StyleGetProperty(cx, db, node, BG_COLOR_STYLE, &entry))
|
|
/* what now? */
|
|
return;
|
|
if (entry)
|
|
param->bgcolor = XP_STRDUP(entry->value);
|
|
}
|
|
#endif
|
|
|
|
/* Process backdrop (BACKGROUND) image attribute, if present. */
|
|
param->bgimage = lo_ParseBackgroundAttribute(context,
|
|
state,
|
|
tag, FALSE);
|
|
|
|
param->src = (char*)PA_FetchParamValue(tag, PARAM_SRC, win_csid);
|
|
|
|
param->tag = tag;
|
|
param->ss_tag = NULL;
|
|
|
|
lo_BeginLayer(context, state, param, tag->type == P_ILAYER);
|
|
|
|
/* lo_FreeBlockInitializeStruct(param); */
|
|
}
|
|
|
|
static int lo_AppendLayerElement(MWContext *context, lo_DocState *state,
|
|
LO_BlockInitializeStruct *param,
|
|
lo_LayerDocState *layerDoc, Bool is_end)
|
|
{
|
|
LO_LayerStruct *layer;
|
|
layer = (LO_LayerStruct*)lo_NewElement(context, state, LO_LAYER, NULL, 0);
|
|
|
|
XP_ASSERT(layer);
|
|
if (!layer)
|
|
return FALSE;
|
|
|
|
layer->lo_any.type = LO_LAYER;
|
|
layer->lo_any.x = state->x;
|
|
layer->lo_any.y = state->y;
|
|
layer->lo_any.x_offset = 0;
|
|
layer->lo_any.y_offset = 0;
|
|
layer->lo_any.width = 0;
|
|
layer->lo_any.height = 0;
|
|
layer->lo_any.line_height = 0;
|
|
layer->lo_any.ele_id = NEXT_ELEMENT;
|
|
|
|
layer->is_end = is_end;
|
|
layer->initParams = param;
|
|
layer->layerDoc = layerDoc;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)layer, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* parse a comma separated list of 2 or 4 values
|
|
*
|
|
* Format is "rect(top, right, bottom, left)"
|
|
* or "rect(right, bottom)"
|
|
* or degenerate case of "top,right,bottom,left" with no rect()
|
|
*/
|
|
#ifdef XP_MAC
|
|
PRIVATE
|
|
#endif
|
|
XP_Rect *
|
|
lo_ParseStyleCoords(MWContext *context,
|
|
lo_DocState *state,
|
|
StyleStruct *style_struct,
|
|
char *coord_string)
|
|
{
|
|
|
|
XP_Rect *coords = XP_NEW_ZAP(XP_Rect);
|
|
int32 val[4];
|
|
int index=0;
|
|
char *value;
|
|
SS_Number *ss_num;
|
|
|
|
if(!coords)
|
|
return(NULL);
|
|
|
|
coord_string = XP_StripLine(coord_string);
|
|
|
|
/* go past "rect(" and kill ")" character */
|
|
#define _RECT "rect"
|
|
if(!strncasecomp(coord_string, _RECT, sizeof(_RECT)-1)) {
|
|
char *t;
|
|
|
|
/* go past "rect" */
|
|
coord_string += sizeof(_RECT)-1;
|
|
|
|
/* go past spaces */
|
|
while(XP_IS_SPACE(*coord_string)) coord_string++;
|
|
|
|
/* go past '(' */
|
|
if(*coord_string == '(')
|
|
coord_string++;
|
|
|
|
/* kill any more spaces */
|
|
while(XP_IS_SPACE(*coord_string)) coord_string++;
|
|
|
|
/* Kill the ending ')' */
|
|
t = XP_STRCHR(coord_string, ')');
|
|
if (t)
|
|
*t = '\0';
|
|
}
|
|
|
|
value = XP_STRTOK(coord_string, ", ");
|
|
|
|
while(value && index < 4)
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, value);
|
|
|
|
/* First and third args are heights. Note that we use
|
|
LAYER_WIDTH_STYLE instead of WIDTH_STYLE, since the two are
|
|
subtly different. (For the top-level DOCUMENT layer, 100%
|
|
WIDTH is the distance between the margins and 100%
|
|
LAYER_WIDTH is the width of the window.) */
|
|
if(index & 1)
|
|
LO_AdjustSSUnits(ss_num, LAYER_WIDTH_STYLE, context, state);
|
|
else
|
|
LO_AdjustSSUnits(ss_num, HEIGHT_STYLE, context, state);
|
|
|
|
val[index++] = (int32)ss_num->value;
|
|
|
|
value = XP_STRTOK(NULL, ",) ");
|
|
}
|
|
|
|
if (index == 2)
|
|
{
|
|
coords->right = FEUNITS_X(val[0], context);
|
|
coords->bottom = FEUNITS_Y(val[1], context);
|
|
}
|
|
else if (index == 4)
|
|
{
|
|
coords->top = FEUNITS_Y(val[0], context);
|
|
coords->right = FEUNITS_X(val[1], context);
|
|
coords->bottom = FEUNITS_Y(val[2], context);
|
|
coords->left = FEUNITS_X(val[3], context);
|
|
}
|
|
else
|
|
{
|
|
XP_FREE(coords);
|
|
return NULL;
|
|
}
|
|
|
|
return coords;
|
|
}
|
|
|
|
#ifdef DOM
|
|
void
|
|
lo_SetStyleSheetLayerProperties(MWContext *context, lo_DocState *state,
|
|
DOM_StyleDatabase *db, DOM_Node *node,
|
|
PA_Tag *tag)
|
|
{
|
|
DOM_Element *element;
|
|
LO_BlockInitializeStruct *param;
|
|
DOM_AttributeEntry *entry;
|
|
JSBool inflow;
|
|
struct SSUnitContext arg;
|
|
JSContext *cx = context->mocha_context;
|
|
char *src_prop;
|
|
|
|
if (node->type != NODE_TYPE_ELEMENT ||
|
|
lo_IsEmptyTag(tag->type))
|
|
/* other code says we can't handle empty tags, so I bail...for now! */
|
|
return;
|
|
|
|
arg.context = context;
|
|
|
|
#ifdef DEBUG_shaver
|
|
fprintf(stderr, "setting layer data on <%s>\n", PA_TagString(tag->type));
|
|
#endif
|
|
|
|
element = (DOM_Element *)node;
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, LAYER_SRC_STYLE, &entry))
|
|
return;
|
|
if (entry) {
|
|
src_prop = (char *)entry->value;
|
|
} else {
|
|
src_prop = NULL;
|
|
}
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, POSITION_STYLE, &entry))
|
|
return;
|
|
|
|
if (entry) {
|
|
if (!DOM_GetCleanEntryData(cx, entry, PositionParser, NULL,
|
|
(uint32 *)&inflow, NULL))
|
|
return;
|
|
} else {
|
|
if (!src_prop)
|
|
return;
|
|
}
|
|
|
|
param = XP_NEW_ZAP(LO_BlockInitializeStruct);
|
|
if (!param)
|
|
return;
|
|
|
|
#define CHECK_PERCENTAGE(entry, arg) \
|
|
if (arg.units == STYLE_UNITS_PERCENT) \
|
|
entry->dirty = JS_TRUE;
|
|
|
|
if (node->name)
|
|
param->name = XP_STRDUP(node->name);
|
|
if (node->type == NODE_TYPE_ELEMENT)
|
|
param->id = element->styleID ? XP_STRDUP(element->styleID) : NULL;
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, LEFT_STYLE, &entry))
|
|
goto error;
|
|
if (entry) {
|
|
arg.axisAdjust = AXIS_X;
|
|
arg.enclosingVal = 0;
|
|
if (!DOM_GetCleanEntryData(cx, entry, lo_ParseSSNumToData, NULL,
|
|
¶m->left, (void *)&arg))
|
|
goto error;
|
|
CHECK_PERCENTAGE(entry, arg);
|
|
param->has_left = TRUE;
|
|
}
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, TOP_STYLE, &entry))
|
|
goto error;
|
|
if (entry) {
|
|
arg.axisAdjust = AXIS_Y;
|
|
arg.enclosingVal = 0;
|
|
if (!DOM_GetCleanEntryData(cx, entry, lo_ParseSSNumToData, NULL, ¶m->top,
|
|
(void *)&arg))
|
|
goto error;
|
|
CHECK_PERCENTAGE(entry, arg);
|
|
param->has_top = TRUE;
|
|
}
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, HEIGHT_STYLE, &entry))
|
|
goto error;
|
|
if (entry) {
|
|
arg.axisAdjust = AXIS_Y;
|
|
arg.enclosingVal = lo_GetEnclosingLayerHeight(state);
|
|
if (!DOM_GetCleanEntryData(cx, entry, lo_ParseSSNumToData, NULL,
|
|
¶m->height, (void *)&arg))
|
|
goto error;
|
|
CHECK_PERCENTAGE(entry, arg);
|
|
param->has_height = TRUE;
|
|
}
|
|
|
|
#if 0 /* waiting on DOM-savvy lo_ParseStyleCoords */
|
|
if (!DOM_StyleGetProperty(cx, db, node, CLIP_STYLE, &entry))
|
|
goto error;
|
|
if (entry) {
|
|
/* XXX GetCleanAttributeData, with coord hash somewhere? */
|
|
param->clip = lo_ParseStyleCoords(context, state, entry->value);
|
|
param->clip_expansion_policy = LO_AUTO_EXPAND_NONE;
|
|
}
|
|
#endif
|
|
|
|
param->above = NULL;
|
|
param->below = NULL;
|
|
if (!DOM_StyleGetProperty(cx, db, node, ZINDEX_STYLE, &entry))
|
|
goto error;
|
|
if (entry) {
|
|
if (!DOM_GetCleanEntryData(cx, entry, lo_atoi, NULL, ¶m->zindex, NULL))
|
|
goto error;
|
|
param->has_zindex = TRUE;
|
|
} else {
|
|
param->has_zindex = FALSE;
|
|
}
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, VISIBILITY_STYLE, &entry))
|
|
goto error;
|
|
if (entry)
|
|
param->visibility = XP_STRDUP(entry->value);
|
|
|
|
if (src_prop)
|
|
param->src = NET_MakeAbsoluteURL(state->top_state->base_url,
|
|
lo_ParseStyleSheetURL(src_prop));
|
|
else
|
|
param->src = NULL;
|
|
|
|
param->tag = NULL;
|
|
param->ss_tag = tag;
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, OVERFLOW_STYLE, &entry))
|
|
goto error;
|
|
|
|
if (entry)
|
|
param->overflow = XP_STRDUP(entry->value);
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, LAYER_BG_COLOR_STYLE, &entry))
|
|
goto error;
|
|
if (entry)
|
|
param->bgcolor = XP_STRDUP(entry->value);
|
|
param->is_style_bgcolor = TRUE;
|
|
|
|
if (!DOM_StyleGetProperty(cx, db, node, LAYER_BG_IMAGE_STYLE, &entry))
|
|
goto error;
|
|
if (entry && strcasecomp(entry->value, "none"))
|
|
param->bgimage = NET_MakeAbsoluteURL(state->top_state->base_url,
|
|
lo_ParseStyleSheetURL(entry->value));
|
|
|
|
if (!LM_SetNodeFlags(node, STYLE_NODE_NEED_TO_POP_LAYER))
|
|
goto error;
|
|
|
|
#ifdef DEBUG_shaver
|
|
fprintf(stderr, "starting layer for <%s>\n", ((DOM_Element *)node)->tagName);
|
|
#endif
|
|
if (lo_BeginLayer(context, state, param, inflow))
|
|
error:
|
|
lo_FreeBlockInitializeStruct(param);
|
|
}
|
|
#else
|
|
|
|
void
|
|
lo_SetStyleSheetLayerProperties(MWContext *context,
|
|
lo_DocState *state,
|
|
StyleStruct *style_struct,
|
|
PA_Tag *tag)
|
|
{
|
|
LO_BlockInitializeStruct *param;
|
|
char *prop;
|
|
char *src_prop;
|
|
XP_Bool is_inflow;
|
|
SS_Number *ss_num;
|
|
|
|
if(!style_struct)
|
|
return;
|
|
|
|
prop = STYLESTRUCT_GetString(style_struct, POSITION_STYLE);
|
|
src_prop = STYLESTRUCT_GetString(style_struct, LAYER_SRC_STYLE);
|
|
|
|
if(!prop && !src_prop)
|
|
return;
|
|
|
|
if(lo_IsEmptyTag(tag->type))
|
|
{
|
|
/* setting positioning on an empty tag will
|
|
* cause things to not work because of blocked image
|
|
* problems, so don't allow it.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
if(prop && !strcasecomp(prop, ABSOLUTE_STYLE))
|
|
{
|
|
is_inflow = FALSE;
|
|
}
|
|
else if(prop && !strcasecomp(prop, RELATIVE_STYLE))
|
|
{
|
|
is_inflow = TRUE;
|
|
}
|
|
else if(src_prop)
|
|
{
|
|
/* not positioned, but an include src attribute */
|
|
is_inflow = TRUE;
|
|
}
|
|
else
|
|
{
|
|
XP_FREEIF(prop);
|
|
return; /* don't do layers */
|
|
}
|
|
|
|
XP_FREEIF(prop);
|
|
|
|
param = XP_NEW_ZAP(LO_BlockInitializeStruct);
|
|
|
|
if(!param)
|
|
return;
|
|
|
|
param->name = (char*)lo_FetchParamValue(context, tag, PARAM_NAME);
|
|
param->id = (char*)lo_FetchParamValue(context, tag, PARAM_ID);
|
|
|
|
ss_num = STYLESTRUCT_GetNumber(style_struct, LEFT_STYLE);
|
|
if(ss_num)
|
|
{
|
|
LO_AdjustSSUnits(ss_num, LEFT_STYLE, context, state);
|
|
param->left = FEUNITS_X((int32)ss_num->value, context);
|
|
param->has_left = TRUE;
|
|
STYLESTRUCT_FreeSSNumber(style_struct, ss_num);
|
|
}
|
|
|
|
ss_num = STYLESTRUCT_GetNumber(style_struct, TOP_STYLE);
|
|
if(ss_num)
|
|
{
|
|
LO_AdjustSSUnits(ss_num, TOP_STYLE, context, state);
|
|
param->top = FEUNITS_Y((int32)ss_num->value, context);
|
|
param->has_top = TRUE;
|
|
STYLESTRUCT_FreeSSNumber(style_struct, ss_num);
|
|
}
|
|
|
|
ss_num = STYLESTRUCT_GetNumber(style_struct, HEIGHT_STYLE);
|
|
if(ss_num)
|
|
{
|
|
if(!XP_STRCMP(ss_num->units, "%"))
|
|
param->percent_height = (uint8) ss_num->value;
|
|
LO_AdjustSSUnits(ss_num, HEIGHT_STYLE, context, state);
|
|
param->height = FEUNITS_Y((int32)ss_num->value, context);
|
|
param->has_height = TRUE;
|
|
STYLESTRUCT_FreeSSNumber(style_struct, ss_num);
|
|
}
|
|
|
|
/* don't set width. normal style sheets will handle it */
|
|
/* NRA: I think we do need to set the width. But, need to check with Scott. */
|
|
ss_num = STYLESTRUCT_GetNumber(style_struct, WIDTH_STYLE);
|
|
if(ss_num)
|
|
{
|
|
if(!XP_STRCMP(ss_num->units, "%"))
|
|
param->percent_width = (uint8) ss_num->value;
|
|
LO_AdjustSSUnits(ss_num, WIDTH_STYLE, context, state);
|
|
param->width = FEUNITS_X((int32)ss_num->value, context);
|
|
param->has_width = TRUE;
|
|
STYLESTRUCT_FreeSSNumber(style_struct, ss_num);
|
|
}
|
|
|
|
prop = STYLESTRUCT_GetString(style_struct, CLIP_STYLE);
|
|
if(prop) {
|
|
param->clip = lo_ParseStyleCoords(context, state, style_struct, prop);
|
|
param->clip_expansion_policy = LO_AUTO_EXPAND_NONE;
|
|
}
|
|
|
|
param->above = NULL;
|
|
param->below = NULL;
|
|
ss_num = STYLESTRUCT_GetNumber(style_struct, ZINDEX_STYLE);
|
|
if (ss_num) {
|
|
param->zindex = (int32)ss_num->value;
|
|
param->has_zindex = TRUE;
|
|
STYLESTRUCT_FreeSSNumber(style_struct, ss_num);
|
|
}
|
|
else
|
|
param->has_zindex = FALSE;
|
|
|
|
param->visibility = STYLESTRUCT_GetString(style_struct, VISIBILITY_STYLE);
|
|
|
|
param->src = src_prop;
|
|
if(param->src)
|
|
{
|
|
char *tmp = param->src;
|
|
|
|
param->src = lo_ParseStyleSheetURL(param->src);
|
|
param->src = NET_MakeAbsoluteURL(state->top_state->base_url, param->src);
|
|
XP_FREE(tmp);
|
|
}
|
|
|
|
param->overflow = STYLESTRUCT_GetString(style_struct, OVERFLOW_STYLE);
|
|
|
|
param->bgcolor = STYLESTRUCT_GetString(style_struct, LAYER_BG_COLOR_STYLE);
|
|
param->is_style_bgcolor = TRUE;
|
|
param->bgimage = STYLESTRUCT_GetString(style_struct, LAYER_BG_IMAGE_STYLE);
|
|
if(param->bgimage)
|
|
{
|
|
char *tmp = param->bgimage;
|
|
if(!strcasecomp(param->bgimage, "none"))
|
|
{
|
|
param->bgimage = NULL;
|
|
}
|
|
else
|
|
{
|
|
param->bgimage = lo_ParseStyleSheetURL(param->bgimage);
|
|
param->bgimage = NET_MakeAbsoluteURL(state->top_state->base_url, param->bgimage);
|
|
}
|
|
XP_FREE(tmp);
|
|
}
|
|
|
|
STYLESTRUCT_SetString(style_struct, STYLE_NEED_TO_POP_LAYER, "1", 0);
|
|
|
|
param->tag = NULL;
|
|
param->ss_tag = tag;
|
|
|
|
lo_BeginLayer(context, state, param, is_inflow);
|
|
}
|
|
#endif /* DOM */
|
|
|
|
static void
|
|
lo_expand_parent_bbox(CL_Layer *layer); /* Forward declaration */
|
|
|
|
|
|
/* Save a copy of the document state, so that we can restore it later. */
|
|
static PRBool
|
|
lo_SaveDocState(lo_Block *block, lo_DocState *state)
|
|
{
|
|
lo_DocState *saved_state;
|
|
|
|
block->start_x = state->x;
|
|
block->start_y = state->y;
|
|
|
|
saved_state = XP_NEW(lo_DocState);
|
|
if (!saved_state)
|
|
return PR_FALSE;
|
|
|
|
block->saved_state = saved_state;
|
|
XP_BCOPY(state, saved_state, sizeof *saved_state);
|
|
if (!block->is_inflow) {
|
|
state->top_state->in_head = TRUE;
|
|
state->top_state->in_body = FALSE;
|
|
}
|
|
block->old_body_attr = state->top_state->body_attr;
|
|
state->top_state->body_attr = 0;
|
|
return PR_TRUE;
|
|
}
|
|
|
|
static void
|
|
lo_RestoreDocState(lo_Block *block, lo_DocState *state, Bool reflow)
|
|
{
|
|
lo_DocState *saved = block->saved_state;
|
|
lo_FontStack *font_stack;
|
|
PA_Tag *subdoc_tags_end, *subdoc_tags;
|
|
LO_TextInfo text_info;
|
|
intn layer_nest_level;
|
|
int32 layer_num_max;
|
|
|
|
/*
|
|
* If there was a BODY tag with a TEXT attribute in this layer,
|
|
* then we pop the font that was pushed to enable layer-specific
|
|
* text coloring.
|
|
*/
|
|
if (state->top_state->body_attr & BODY_ATTR_TEXT) {
|
|
lo_PopFont(state, P_BODY);
|
|
}
|
|
state->top_state->body_attr = block->old_body_attr;
|
|
|
|
if (!reflow)
|
|
lo_SetBaseUrl(state->top_state, block->old_base_url, FALSE);
|
|
|
|
if (block->is_inflow) {
|
|
state->line_num = saved->line_num;
|
|
state->float_list = saved->float_list;
|
|
state->line_list = saved->line_list;
|
|
state->end_last_line = saved->end_last_line;
|
|
|
|
/* Push the cell representing the inflow layer onto the line
|
|
list, so that it appears like any other (non-layer)
|
|
element. */
|
|
if (state->line_list == NULL) {
|
|
state->line_list = (LO_Element*)block->cell;
|
|
} else {
|
|
LO_Element *eptr = state->line_list;
|
|
while (eptr->lo_any.next != NULL)
|
|
eptr = eptr->lo_any.next;
|
|
eptr->lo_any.next = (LO_Element*)block->cell;
|
|
block->cell->prev = eptr;
|
|
}
|
|
|
|
if (state->end_last_line != NULL)
|
|
state->end_last_line->lo_any.next = NULL;
|
|
state->linefeed_state = 0;
|
|
state->text_fg = saved->text_fg;
|
|
state->text_bg = saved->text_bg;
|
|
state->anchor_color = saved->anchor_color;
|
|
state->visited_anchor_color = saved->visited_anchor_color;
|
|
state->active_anchor_color = saved->active_anchor_color;
|
|
return;
|
|
}
|
|
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
/* Save a few special state variables that we *don't* restore.
|
|
See lo_InitDocState(). */
|
|
|
|
/* XXX - Right now, font info is not reset when entering and
|
|
exiting layers. Is that right ? */
|
|
font_stack = state->font_stack;
|
|
text_info = state->text_info;
|
|
layer_nest_level = state->layer_nest_level;
|
|
layer_num_max = state->current_layer_num_max;
|
|
subdoc_tags_end = state->subdoc_tags_end;
|
|
subdoc_tags = state->subdoc_tags;
|
|
|
|
/* First, free up the pieces of the document state that we're
|
|
about to overwrite with their restored values. This gets rid of
|
|
default values or values left on the stack because the document
|
|
author didn't properly close tags inside the layer. */
|
|
state->font_stack = NULL; /* But, don't free the font stack */
|
|
lo_free_layout_state_data(block->context, state);
|
|
|
|
/* Restore the entire document state ... */
|
|
XP_BCOPY(saved, state, sizeof *state);
|
|
|
|
/* ...except for these variables which are retained across LAYERs */
|
|
state->font_stack = font_stack;
|
|
state->text_info = text_info;
|
|
state->layer_nest_level = layer_nest_level;
|
|
state->current_layer_num_max = layer_num_max;
|
|
state->subdoc_tags_end = subdoc_tags_end;
|
|
state->subdoc_tags = subdoc_tags;
|
|
|
|
if (state->end_last_line != NULL)
|
|
state->end_last_line->lo_any.next = NULL;
|
|
|
|
XP_FREE(block->saved_state);
|
|
block->saved_state = NULL;
|
|
}
|
|
|
|
static PRBool
|
|
lo_SetupDocStateForLayer(lo_DocState *state,
|
|
lo_LayerDocState *layer_state,
|
|
int32 width,
|
|
int32 height,
|
|
PRBool is_inflow,
|
|
PRBool reflow)
|
|
{
|
|
lo_Block *block = layer_state->temp_block;
|
|
MWContext *context = block->context;
|
|
lo_DocState *saved_state;
|
|
|
|
block->is_inflow = is_inflow;
|
|
|
|
/* Save old document state so that we can selectively restore
|
|
parts of it after the layer is closed. */
|
|
if (!lo_SaveDocState(block, state))
|
|
return PR_FALSE;
|
|
|
|
state->float_list = NULL;
|
|
state->line_list = NULL;
|
|
|
|
if (state->top_state->base_url && !reflow)
|
|
block->old_base_url = XP_STRDUP(state->top_state->base_url);
|
|
|
|
if (is_inflow)
|
|
return PR_TRUE;
|
|
|
|
/*
|
|
* XXX This doesn't seem right. We're saving some of the doc's
|
|
* layout state and resetting all of it while the block
|
|
* is laid out. This layout state will be restored once the block
|
|
* is completed. However, things like the float_list are part of the
|
|
* incremental layout state as well as a representation of the final
|
|
* document. So, if we're asked to redraw while the block is being laid
|
|
* out, we will probably do the wrong thing for floating elements.
|
|
*/
|
|
|
|
/* Unspecified height won't change state's height property. */
|
|
if (height == 0)
|
|
height = state->win_height;
|
|
|
|
state = lo_InitDocState(state, context,
|
|
width, height,
|
|
0, 0, /* margin_width, margin_height */
|
|
NULL, /* clone_state */
|
|
layer_state->doc_lists, PR_TRUE);
|
|
|
|
if (!state)
|
|
return PR_FALSE;
|
|
|
|
/* Though most state variables are reset to their virgin state (in
|
|
lo_InitDocState, above), there are a few state variables
|
|
related to table layout that are preserved when a layer tag is
|
|
opened. */
|
|
saved_state = block->saved_state;
|
|
state->is_a_subdoc = saved_state->is_a_subdoc;
|
|
state->in_relayout = saved_state->in_relayout;
|
|
state->subdoc_tags = saved_state->subdoc_tags;
|
|
state->subdoc_tags_end = saved_state->subdoc_tags_end;
|
|
|
|
state->text_fg = saved_state->text_fg;
|
|
state->text_bg = saved_state->text_bg;
|
|
state->anchor_color = saved_state->anchor_color;
|
|
state->visited_anchor_color = saved_state->visited_anchor_color;
|
|
state->active_anchor_color = saved_state->active_anchor_color;
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
static char layer_end_tag[] = "</" PT_LAYER ">";
|
|
static char layer_suppress_tag[] = "<" PT_LAYER " suppress>";
|
|
static char display_none_style[] = PARAM_STYLE "='display:none'>";
|
|
|
|
#ifdef XP_MAC
|
|
PRIVATE
|
|
#endif
|
|
void
|
|
lo_insert_suppress_tags(MWContext *context, lo_TopState *top_state,
|
|
LO_BlockInitializeStruct *param)
|
|
{
|
|
PA_Tag *end_tag;
|
|
uint32 i;
|
|
|
|
/*
|
|
* Since we're adding tags to the blocked tags list without going through
|
|
* lo_BlockTag, we need to see if this is a flushed to block transition
|
|
* and, if so, add to the doc_data's ref count. We don't want to do this
|
|
* if we're in the process of flushing blockage - in that case, the count
|
|
* has already been incremented.
|
|
*/
|
|
if (top_state->tags == NULL && top_state->flushing_blockage == FALSE)
|
|
PA_HoldDocData(top_state->doc_data);
|
|
|
|
/* If we can here through a LAYER or ILAYER tag */
|
|
if (param->tag) {
|
|
/*
|
|
* Shove in a </LAYER><LAYER suppress> sequence into the
|
|
* tag stream so that all inline content of this layer is
|
|
* suppressed. The sourced content comes in before the
|
|
* first end tag, the inline conent comes in after the
|
|
* <LAYER suppress> tag.
|
|
*/
|
|
end_tag = pa_CreateMDLTag(top_state->doc_data,
|
|
layer_end_tag,
|
|
sizeof layer_end_tag - 1);
|
|
|
|
if (end_tag) {
|
|
end_tag->newline_count = LO_IGNORE_TAG_MARKER;
|
|
end_tag->next = pa_CreateMDLTag(top_state->doc_data,
|
|
layer_suppress_tag,
|
|
sizeof layer_suppress_tag - 1);
|
|
if (end_tag->next) {
|
|
end_tag->next->newline_count = LO_IGNORE_TAG_MARKER;
|
|
if (top_state->tags == NULL)
|
|
top_state->tags_end = &end_tag->next->next;
|
|
else
|
|
end_tag->next->next = top_state->tags;
|
|
top_state->tags = end_tag;
|
|
|
|
/*
|
|
* If there are any other input_write_levels on the stack
|
|
* that correspond to the front of the tag list, move them
|
|
* to account for the newly inserted tags.
|
|
*/
|
|
for (i = 0; i < top_state->input_write_level; i++)
|
|
if (top_state->input_write_point[i] == &top_state->tags)
|
|
top_state->input_write_point[i] = &end_tag->next->next;
|
|
}
|
|
else {
|
|
top_state->out_of_memory = TRUE;
|
|
PA_FreeTag(end_tag);
|
|
}
|
|
}
|
|
else
|
|
top_state->out_of_memory = TRUE;
|
|
}
|
|
/*
|
|
* If we came here through the include-source style and
|
|
* the tag is a container tag
|
|
*/
|
|
else if (param->ss_tag && !lo_IsEmptyTag(param->ss_tag->type)) {
|
|
/*
|
|
* Shove in a </Foo><Foo STYLE="display:none"> sequence into
|
|
* the tag stream. The sourced content comes in before the
|
|
* first end tag, the inline conent comes in after the
|
|
* display:none tag and is ignored.
|
|
*/
|
|
end_tag = (PA_Tag *)XP_NEW_ZAP(PA_Tag);
|
|
|
|
if (end_tag) {
|
|
end_tag->type = param->ss_tag->type;
|
|
end_tag->is_end = TRUE;
|
|
end_tag->newline_count = LO_IGNORE_TAG_MARKER;
|
|
|
|
end_tag->next = PA_CloneMDLTag(param->ss_tag);
|
|
if (end_tag->next) {
|
|
PA_Tag *begin_tag = end_tag->next;
|
|
|
|
begin_tag->newline_count = LO_IGNORE_TAG_MARKER;
|
|
if (begin_tag->data)
|
|
PA_FREE(begin_tag->data);
|
|
/* Can't do a strdup because it's parser memory */
|
|
begin_tag->data_len = XP_STRLEN(display_none_style);
|
|
begin_tag->true_len = begin_tag->data_len;
|
|
begin_tag->data = PA_ALLOC(begin_tag->data_len + 1);
|
|
if (begin_tag->data) {
|
|
char *buff;
|
|
|
|
/* Copy over the new tag data */
|
|
PA_LOCK(buff, char *, begin_tag->data);
|
|
XP_BCOPY(display_none_style, buff, begin_tag->data_len);
|
|
buff[begin_tag->data_len] = '\0';
|
|
PA_UNLOCK(buff);
|
|
|
|
if (top_state->tags == NULL)
|
|
top_state->tags_end = &begin_tag->next;
|
|
else
|
|
begin_tag->next = top_state->tags;
|
|
top_state->tags = end_tag;
|
|
}
|
|
else {
|
|
top_state->out_of_memory = TRUE;
|
|
PA_FreeTag(begin_tag);
|
|
PA_FreeTag(end_tag);
|
|
}
|
|
}
|
|
else {
|
|
top_state->out_of_memory = TRUE;
|
|
PA_FreeTag(end_tag);
|
|
}
|
|
}
|
|
else
|
|
top_state->out_of_memory = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
/* Start a <LAYER> or an <ILAYER>. */
|
|
#ifdef XP_MAC
|
|
PRIVATE
|
|
#endif
|
|
void
|
|
lo_begin_layer_internal(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_BlockInitializeStruct *param,
|
|
XP_Bool is_inflow)
|
|
{
|
|
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context);
|
|
int16 win_csid = INTL_GetCSIWinCSID(c);
|
|
lo_Block *block;
|
|
lo_LayerDocState *layer_state, *parent_layer_state;
|
|
char *str, *layer_name;
|
|
int32 block_wrap_width, x_parent_offset, y_parent_offset;
|
|
int32 block_x, block_y;
|
|
lo_TopState *top_state;
|
|
XP_Rect bbox;
|
|
PRBool hidden, inherit_visibility;
|
|
CL_Layer *layer, *parent_layer;
|
|
char *backdrop_image_url = NULL;
|
|
LO_Color rgb, *bg_color = NULL;
|
|
char *url = NULL;
|
|
|
|
if (!context->compositor || !param)
|
|
return;
|
|
|
|
/*
|
|
* If this is a nested inflow layer, then flush the line list into our parent
|
|
* (without introducing a line break). This ensures that whatever line changes
|
|
* (specifically shifting of cell origin) that occur while laying out this ilayer
|
|
* are reflected in our parent.
|
|
*/
|
|
if (lo_InsideInflowLayer(state) && is_inflow)
|
|
{
|
|
lo_FlushLineList(context, state, LO_LINEFEED_BREAK_SOFT, LO_CLEAR_NONE, FALSE);
|
|
}
|
|
|
|
top_state = state->top_state;
|
|
block = XP_NEW_ZAP(lo_Block);
|
|
if (block == NULL)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
block->context = context;
|
|
layer_state = lo_NewLayerState(context);
|
|
if (layer_state == NULL) {
|
|
state->top_state->out_of_memory = TRUE;
|
|
lo_DeleteBlock(block);
|
|
return;
|
|
}
|
|
layer_state->temp_block = block;
|
|
|
|
/* Retain MATCH attribute for use in comparison of attribute of
|
|
same name in matching closing layer tag. */
|
|
block->match_code = NULL;
|
|
if (param->tag) {
|
|
char *match =
|
|
(char*)PA_FetchParamValue(param->tag, PARAM_MATCH, win_csid);
|
|
if (match) {
|
|
block->match_code = XP_STRDUP(match);
|
|
PA_FREE(match);
|
|
}
|
|
}
|
|
|
|
block->name = NULL;
|
|
block->above = NULL;
|
|
block->below = NULL;
|
|
hidden = PR_FALSE;
|
|
inherit_visibility = PR_TRUE;
|
|
block->z_order = -1;
|
|
block->old_layer_state = NULL;
|
|
|
|
/* If there is no tag associated with this layer, then it was
|
|
created as a result of a style sheet. */
|
|
block->uses_ss_positioning = (param->tag == NULL);
|
|
|
|
block->is_inflow = is_inflow;
|
|
|
|
/* In-flow layers use coordinates that are relative to their
|
|
"natural", in-flow position. */
|
|
if (block->is_inflow) {
|
|
block->x_offset = state->x;
|
|
block->y_offset = state->y;
|
|
} else {
|
|
block->x_offset = 0;
|
|
block->y_offset = 0;
|
|
}
|
|
|
|
|
|
x_parent_offset = y_parent_offset = 0;
|
|
parent_layer_state = lo_CurrentLayerState(state);
|
|
parent_layer = parent_layer_state->layer;
|
|
if (parent_layer) /* Paranoia */
|
|
lo_GetLayerXYShift(parent_layer, &x_parent_offset, &y_parent_offset);
|
|
|
|
/*
|
|
* Get the X position of the block
|
|
*/
|
|
if (param->has_left)
|
|
{
|
|
block_x = param->left;
|
|
} else {
|
|
if (block->is_inflow) {
|
|
block_x = 0;
|
|
} else {
|
|
block_x = state->x - x_parent_offset;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the Y position of the block
|
|
*/
|
|
if (param->has_top)
|
|
{
|
|
block_y = param->top;
|
|
} else {
|
|
if (block->is_inflow) {
|
|
block_y = 0;
|
|
} else {
|
|
block_y = state->y - y_parent_offset;
|
|
}
|
|
}
|
|
|
|
XP_BZERO(&bbox, sizeof(bbox));
|
|
|
|
/*
|
|
* Get the width parameter, in absolute or percentage.
|
|
* If percentage, make it absolute.
|
|
*/
|
|
if (param->has_width)
|
|
block_wrap_width = param->width;
|
|
else if (param->has_left)
|
|
block_wrap_width = state->right_margin - param->left;
|
|
else
|
|
block_wrap_width = state->right_margin - state->x;
|
|
|
|
/*
|
|
* Parse the comma separated coordinate list into an
|
|
* array of integers.
|
|
*/
|
|
if (param->clip)
|
|
{
|
|
bbox = *param->clip;
|
|
|
|
/* Don't allow the layer's clip to expand */
|
|
block->clip_expansion_policy = param->clip_expansion_policy;
|
|
} else {
|
|
/* Allow the clip to expand to include the layer's contents. */
|
|
block->clip_expansion_policy = LO_AUTO_EXPAND_CLIP;
|
|
if (param->has_width)
|
|
bbox.right = block_wrap_width;
|
|
}
|
|
|
|
if (param->has_height) {
|
|
layer_state->height = param->height;
|
|
/* If no CLIP set, set initial bottom edge of layer clip to be
|
|
the same as HEIGHT. */
|
|
if (block->clip_expansion_policy & LO_AUTO_EXPAND_CLIP_BOTTOM)
|
|
bbox.bottom = param->height;
|
|
}
|
|
|
|
/* Treat any value of OVERFLOW, except "hidden" to be the same as
|
|
"visible". For compatibility with the older CSS positioning
|
|
spec, we also treat "none" specially. Can't set OVERFLOW
|
|
unless HEIGHT is provided. */
|
|
if (param->has_height &&
|
|
param->overflow &&
|
|
XP_STRCASECMP(param->overflow, "none") &&
|
|
XP_STRCASECMP(param->overflow, "visible")) {
|
|
|
|
/* Constrain all drawing to {0, 0, width, height} box */
|
|
XP_Rect *viewRect = &layer_state->viewRect;
|
|
viewRect->left = 0;
|
|
viewRect->top = 0;
|
|
viewRect->right = block_wrap_width;
|
|
viewRect->bottom = param->height;
|
|
}
|
|
|
|
/*
|
|
* Get the optional name for this layer.
|
|
*/
|
|
layer_name = NULL;
|
|
if(param->name)
|
|
layer_name = param->name;
|
|
else if(param->id)
|
|
layer_name = param->id;
|
|
|
|
/* Don't allow layer names that start with a number or underscore */
|
|
if (layer_name && ((layer_name[0] < '0') || (layer_name[0] > '9')) &&
|
|
(layer_name[0] != '_'))
|
|
block->name = XP_STRDUP(layer_name);
|
|
|
|
/*
|
|
* Get the optional "above" name for the layer we are above.
|
|
*/
|
|
if(param->above)
|
|
block->above = XP_STRDUP(param->above);
|
|
|
|
/*
|
|
* Get the optional "below" name for the layer we are below.
|
|
*/
|
|
if (!block->above)
|
|
{
|
|
if(param->below)
|
|
block->below = XP_STRDUP(param->below);
|
|
|
|
if (!block->below)
|
|
{
|
|
/*
|
|
* Get the Z-order of the block
|
|
*/
|
|
if (param->has_zindex)
|
|
{
|
|
block->z_order = param->zindex;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get the VISIBILITY parameter to know if this layer starts hidden. */
|
|
if (param->visibility)
|
|
{
|
|
/* Handle "HIDE", "HIDDEN", etc. */
|
|
hidden = (PRBool)!XP_STRNCASECMP(param->visibility, "hid", 3);
|
|
inherit_visibility = (PRBool)!XP_STRCASECMP(param->visibility, "inherit");
|
|
}
|
|
|
|
/* Add a new LO_LAYER dummy layout element to the line list. Relayout will step through
|
|
the line list and reflow the layer when the LO_LAYER element is encountered. */
|
|
if (!lo_AppendLayerElement(context, state, param, layer_state, FALSE))
|
|
return;
|
|
|
|
/* Reset document layout state, saving old state to be restored
|
|
after we lay out this layer. */
|
|
if (!lo_SetupDocStateForLayer(state, layer_state,
|
|
block_wrap_width, layer_state->height,
|
|
(PRBool)is_inflow, PR_FALSE)) {
|
|
state->top_state->out_of_memory = TRUE;
|
|
lo_DeleteLayerState(context, state, layer_state);
|
|
return;
|
|
}
|
|
|
|
block->cell = layer_state->cell =
|
|
(LO_CellStruct *)lo_NewElement(context, state,
|
|
LO_CELL, NULL, 0);
|
|
if (block->cell == NULL)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
lo_DeleteLayerState(context, state, layer_state);
|
|
return;
|
|
}
|
|
lo_init_block_cell(context, state, block);
|
|
|
|
/* Create the layer that corresponds to this block, initially invisible
|
|
and with empty clip and [0,0] origin. */
|
|
PA_LOCK(str, char *, block->name);
|
|
layer = lo_CreateBlockLayer(context,
|
|
str,
|
|
(PRBool)block->is_inflow,
|
|
block->x_offset,
|
|
block->y_offset,
|
|
block_wrap_width,
|
|
layer_state, state);
|
|
PA_UNLOCK(block->name);
|
|
if (!layer) {
|
|
state->top_state->out_of_memory = TRUE;
|
|
lo_DeleteLayerState(context, state, layer_state);
|
|
return;
|
|
}
|
|
block->layer = layer_state->layer = layer;
|
|
if (block->is_inflow)
|
|
block->cell->cell_inflow_layer = layer;
|
|
|
|
/* Indicate that this layer's contents were not obtained as a
|
|
result of setting the JavaScript 'src' property of the layer. */
|
|
block->is_dynamic = FALSE;
|
|
block->is_inline = TRUE;
|
|
|
|
/*
|
|
* Attach the layer doc_state to the layer list. The return
|
|
* value is the old layer_state if we're in table relayout.
|
|
* We need to wait till we're done processing this layer tag
|
|
* before we actually get rid of the layer_state and its
|
|
* contents.
|
|
*/
|
|
block->old_layer_state = lo_append_to_layer_array(context, state->top_state,
|
|
state,
|
|
layer_state);
|
|
|
|
/* Attach the layer into the layer tree */
|
|
lo_AttachHTMLLayer(context, layer, parent_layer,
|
|
block->above, block->below, block->z_order);
|
|
|
|
/* Reflect the layer into JavaScript */
|
|
ET_ReflectObject(context, NULL, param->tag,
|
|
parent_layer_state->id,
|
|
layer_state->id, LM_LAYERS);
|
|
|
|
/* Process background color (BGCOLOR) attribute, if present. */
|
|
if (param->bgcolor) {
|
|
|
|
XP_Bool rv;
|
|
if(param->is_style_bgcolor)
|
|
rv = LO_ParseStyleSheetRGB(param->bgcolor, &rgb.red, &rgb.green, &rgb.blue);
|
|
else
|
|
rv = LO_ParseRGB(param->bgcolor, &rgb.red, &rgb.green, &rgb.blue);
|
|
if(rv)
|
|
bg_color = &rgb;
|
|
}
|
|
if (bg_color)
|
|
LO_SetLayerBgColor(layer, bg_color);
|
|
|
|
/* Process backdrop (BACKGROUND) image attribute, if present. */
|
|
if(param->bgimage)
|
|
backdrop_image_url = XP_STRDUP(param->bgimage);
|
|
|
|
if (!bg_color && !backdrop_image_url) {
|
|
/* This is a transparent block, so let's go offscreen */
|
|
CL_ChangeLayerFlag(layer, CL_PREFER_DRAW_OFFSCREEN, PR_TRUE);
|
|
}
|
|
if (backdrop_image_url) {
|
|
LO_SetLayerBackdropURL(layer, backdrop_image_url);
|
|
XP_FREE(backdrop_image_url);
|
|
}
|
|
|
|
lo_SetLayerClipExpansionPolicy(layer, block->clip_expansion_policy);
|
|
|
|
/* Move layer into position. Set clip dimensions and initial visibility. */
|
|
LO_MoveLayer(layer, block_x, block_y);
|
|
LO_SetLayerBbox(layer, &bbox);
|
|
lo_expand_parent_bbox(layer);
|
|
CL_ChangeLayerFlag(layer, CL_HIDDEN, hidden);
|
|
#ifdef XP_MAC
|
|
CL_ChangeLayerFlag(layer, CL_OVERRIDE_INHERIT_VISIBILITY,
|
|
(PRBool)!inherit_visibility);
|
|
#else
|
|
CL_ChangeLayerFlag(layer, CL_OVERRIDE_INHERIT_VISIBILITY,
|
|
(int32)!inherit_visibility);
|
|
#endif
|
|
|
|
/* Push this layer on the layer stack */
|
|
lo_PushLayerState(state->top_state, layer_state);
|
|
ET_SetActiveLayer(context, layer_state->id);
|
|
state->layer_nest_level++;
|
|
|
|
if (param->src) {
|
|
url = NET_MakeAbsoluteURL(top_state->base_url, param->src);
|
|
if (url == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((url != NULL) && (top_state->doc_data != NULL)) {
|
|
URL_Struct *url_struct;
|
|
int status;
|
|
|
|
block->source_url = XP_STRDUP(url);
|
|
block->is_inline = FALSE;
|
|
url_struct = NET_CreateURLStruct(url, top_state->force_reload);
|
|
if (block->source_url == NULL || url_struct == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
if (url_struct != NULL)
|
|
NET_FreeURLStruct(url_struct);
|
|
}
|
|
else {
|
|
char *referer;
|
|
lo_LayerStack *lptr;
|
|
|
|
/*
|
|
* The referer for this SRC="url" fetch will be the base document
|
|
* url if there are no enclosing out-of-line or dynamic layer tags,
|
|
* otherwise it will be the nearest enclosing layer block's source
|
|
* url (set via a previous SRC= or by LO_PrepareLayerForWriting).
|
|
*/
|
|
referer = top_state->base_url;
|
|
lptr = top_state->layer_stack->next;
|
|
while (lptr && lptr->layer_state &&
|
|
(lptr->layer_state->id != LO_DOCUMENT_LAYER_ID)) {
|
|
lo_Block *temp_block;
|
|
|
|
temp_block = lptr->layer_state->temp_block;
|
|
if (temp_block &&
|
|
(!temp_block->is_inline || temp_block->is_dynamic)) {
|
|
referer = temp_block->source_url;
|
|
break;
|
|
}
|
|
lptr = lptr->next;
|
|
}
|
|
|
|
url_struct->referer = XP_STRDUP(referer);
|
|
if (url_struct->referer == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
NET_FreeURLStruct(url_struct);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We're blocking on the current element till we
|
|
* read in the new stream.
|
|
*/
|
|
top_state->layout_blocking_element = (LO_Element *)block->cell;
|
|
|
|
lo_insert_suppress_tags(context, top_state, param);
|
|
|
|
/*
|
|
* New tags will be inserted at the start of the
|
|
* blocked tags list.
|
|
*/
|
|
top_state->input_write_point[top_state->input_write_level] =
|
|
&top_state->tags;
|
|
|
|
lo_SetBaseUrl(state->top_state, url, FALSE);
|
|
XP_FREEIF(top_state->inline_stream_blocked_base_url);
|
|
|
|
status = NET_GetURL(url_struct, FO_CACHE_AND_PRESENT_INLINE, context,
|
|
lo_block_src_exit_fn);
|
|
|
|
if (status < 0)
|
|
top_state->layout_blocking_element = NULL;
|
|
|
|
}
|
|
XP_FREE(url);
|
|
}
|
|
}
|
|
|
|
static Bool
|
|
lo_create_layer_blockage(MWContext *context, lo_DocState *state)
|
|
{
|
|
lo_TopState *top_state;
|
|
top_state = state->top_state;
|
|
|
|
top_state->layout_blocking_element
|
|
= lo_NewElement(context, state, LO_CELL, NULL, 0);
|
|
if (top_state->layout_blocking_element == NULL) {
|
|
top_state->out_of_memory = TRUE;
|
|
} else {
|
|
top_state->layout_blocking_element->type = LO_CELL;
|
|
top_state->layout_blocking_element->lo_any.ele_id = NEXT_ELEMENT;
|
|
top_state->layout_blocking_element->lo_cell.cell_list = NULL;
|
|
top_state->layout_blocking_element->lo_cell.cell_list_end = NULL;
|
|
top_state->layout_blocking_element->lo_cell.cell_float_list = NULL;
|
|
top_state->layout_blocking_element->lo_cell.cell_bg_layer = NULL;
|
|
top_state->layout_blocking_element->lo_cell.backdrop.bg_color = NULL;
|
|
}
|
|
|
|
return(top_state->layout_blocking_element != NULL);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
MWContext *context;
|
|
lo_DocState *state;
|
|
XP_Bool is_inflow;
|
|
int32 doc_id;
|
|
} lo_RestoreLayerClosure;
|
|
|
|
static void
|
|
lo_RestoreLayerExitFn(void *myclosure,
|
|
LO_BlockInitializeStruct *param)
|
|
{
|
|
lo_RestoreLayerClosure *closure = (lo_RestoreLayerClosure *)myclosure;
|
|
MWContext *context = closure->context;
|
|
lo_DocState *state = closure->state;
|
|
lo_TopState *top_state = NULL;
|
|
int32 doc_id = closure->doc_id;
|
|
PRBool flushp = (PRBool)(param->src == NULL);
|
|
LO_Element *blocking_element=NULL;
|
|
|
|
if (doc_id == XP_DOCID(context)) {
|
|
top_state = state->top_state;
|
|
/*
|
|
* We check that we're still blocking, since it's possible that
|
|
* something's come along and changed things under us e.g. if
|
|
* the context is interrupted while we were out to mocha.
|
|
*/
|
|
blocking_element = top_state->layout_blocking_element;
|
|
if (blocking_element)
|
|
lo_begin_layer_internal(context, state, param, closure->is_inflow);
|
|
}
|
|
|
|
XP_FREE(closure);
|
|
|
|
/* Get rid of the cloned tag */
|
|
if(param->tag) {
|
|
PA_FreeTag(param->tag);
|
|
}
|
|
else if (param->ss_tag) {
|
|
PA_FreeTag(param->ss_tag);
|
|
}
|
|
|
|
lo_FreeBlockInitializeStruct(param);
|
|
|
|
/* Get rid of the fake layout blocking element */
|
|
if ((doc_id == XP_DOCID(context)) &&
|
|
(blocking_element)) {
|
|
lo_FreeElement(context, blocking_element, FALSE);
|
|
|
|
/*
|
|
* If there was a SRC attribute in the param list, then
|
|
* the block layout will have created a new blocking element.
|
|
* Otherwise, flush the blocked list and carry on as if
|
|
* nothing happened.
|
|
*/
|
|
if (flushp) {
|
|
top_state->layout_blocking_element = NULL;
|
|
lo_FlushBlockage(context, state, state->top_state->doc_state);
|
|
}
|
|
}
|
|
}
|
|
|
|
XP_Bool
|
|
lo_BeginLayer(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_BlockInitializeStruct *param,
|
|
XP_Bool is_inflow)
|
|
{
|
|
if (!context->compositor || !param)
|
|
return TRUE;
|
|
|
|
lo_begin_layer_internal(context, state, param, is_inflow);
|
|
return TRUE;
|
|
|
|
/* We no longer recreate layers on resizes, so the following code that rehooks up
|
|
layout's layer data structures with the JS layer objects can go away. */
|
|
#if 0
|
|
if (state->top_state->resize_reload && !state->in_relayout &&
|
|
!lo_IsAnyCurrentAncestorDynamic(state) && LM_CanDoJS(context)) {
|
|
lo_RestoreLayerClosure *closure;
|
|
PA_Tag *new_tag;
|
|
|
|
/* I guess this whole section can go away, now that we have resize
|
|
without reload a la Mariner??? */
|
|
|
|
closure = XP_NEW_ZAP(lo_RestoreLayerClosure);
|
|
if (!closure)
|
|
return TRUE;
|
|
|
|
closure->context = context;
|
|
closure->state = state;
|
|
closure->is_inflow = is_inflow;
|
|
closure->doc_id = XP_DOCID(context);
|
|
|
|
/*
|
|
* Block layout and send an event mocha to restore the layer
|
|
* state from the preserved mocha object.
|
|
*/
|
|
lo_create_layer_blockage(context, state);
|
|
lo_BlockLayerTag(context, state, NULL);
|
|
|
|
if (param->tag) {
|
|
new_tag = PA_CloneMDLTag(param->tag);
|
|
if (!new_tag) {
|
|
state->top_state->out_of_memory = TRUE;
|
|
return TRUE;
|
|
}
|
|
param->tag = new_tag;
|
|
}
|
|
else if (param->ss_tag) {
|
|
new_tag = PA_CloneMDLTag(param->ss_tag);
|
|
if (!new_tag) {
|
|
state->top_state->out_of_memory = TRUE;
|
|
return TRUE;
|
|
}
|
|
param->ss_tag = new_tag;
|
|
}
|
|
|
|
ET_RestoreLayerState(context, state->top_state->current_layer_num + 1,
|
|
param, lo_RestoreLayerExitFn, closure);
|
|
return FALSE;
|
|
}
|
|
else {
|
|
lo_begin_layer_internal(context, state, param, is_inflow);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
lo_BlockLayerTag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
lo_TopState *top_state;
|
|
pa_DocData *doc_data;
|
|
|
|
top_state = state->top_state;
|
|
doc_data = top_state->doc_data;
|
|
XP_ASSERT(doc_data != NULL && doc_data->url_struct != NULL);
|
|
if (doc_data == NULL || doc_data->url_struct == NULL) /* Paranoia */
|
|
return;
|
|
}
|
|
|
|
void
|
|
lo_UnblockLayerTag(lo_DocState *state)
|
|
{
|
|
/*
|
|
* I guess we don't really need this anymore, since we're not
|
|
* maintaining a blocked_tag count.
|
|
*/
|
|
}
|
|
|
|
static void
|
|
lo_set_layer_bbox(CL_Layer *layer, XP_Rect *bbox, PRBool grow_parent);
|
|
|
|
/* A layer has expanded in order to contain its content. Expand it's
|
|
parent also.*/
|
|
static void
|
|
lo_expand_parent_bbox(CL_Layer *layer)
|
|
{
|
|
XP_Rect parent_bbox, child_bbox;
|
|
CL_Compositor *compositor = CL_GetLayerCompositor(layer);
|
|
|
|
/* Get the parent's and child's bbox */
|
|
CL_Layer *parent_layer = CL_GetLayerParent(layer);
|
|
|
|
if (! parent_layer)
|
|
return;
|
|
|
|
CL_GetLayerBbox(parent_layer, &parent_bbox);
|
|
CL_GetLayerBbox(layer, &child_bbox);
|
|
|
|
/* Convert the child and parent to the same coordinate system. */
|
|
CL_LayerToWindowRect(compositor, parent_layer, &parent_bbox);
|
|
CL_LayerToWindowRect(compositor, layer, &child_bbox);
|
|
|
|
/* Expand the parent's bbox to encompass the child layer. */
|
|
XP_RectsBbox(&child_bbox, &parent_bbox, &parent_bbox);
|
|
CL_WindowToLayerRect(compositor, parent_layer, &parent_bbox);
|
|
lo_set_layer_bbox(parent_layer, &parent_bbox, PR_TRUE);
|
|
|
|
lo_expand_parent_bbox(parent_layer);
|
|
}
|
|
|
|
/* Set the clipping bounds of a layer, but don't override any
|
|
explicit clip set by the user with the CLIP attribute. */
|
|
static void
|
|
lo_set_layer_bbox(CL_Layer *layer, XP_Rect *bbox, PRBool grow_parent)
|
|
{
|
|
XP_Rect new_bbox, old_bbox;
|
|
|
|
int clip_expansion_policy = lo_GetLayerClipExpansionPolicy(layer);
|
|
|
|
CL_GetLayerBbox(layer, &old_bbox);
|
|
XP_CopyRect(&old_bbox, &new_bbox);
|
|
|
|
if (clip_expansion_policy & LO_AUTO_EXPAND_CLIP_LEFT)
|
|
new_bbox.left = bbox->left;
|
|
|
|
if (clip_expansion_policy & LO_AUTO_EXPAND_CLIP_RIGHT)
|
|
new_bbox.right = bbox->right;
|
|
|
|
if (clip_expansion_policy & LO_AUTO_EXPAND_CLIP_TOP)
|
|
new_bbox.top = bbox->top;
|
|
|
|
if (clip_expansion_policy & LO_AUTO_EXPAND_CLIP_BOTTOM)
|
|
new_bbox.bottom = bbox->bottom;
|
|
|
|
/* If the layer's clip didn't change, there's nothing to do */
|
|
if (XP_EqualRect(&old_bbox, &new_bbox))
|
|
return;
|
|
|
|
LO_SetLayerBbox(layer, &new_bbox);
|
|
|
|
/* Expand the parent layer's bbox to contain this child's bbox */
|
|
if (grow_parent)
|
|
lo_expand_parent_bbox(layer);
|
|
}
|
|
|
|
/* Expand the layer associated with an HTML block in case the block
|
|
has grown. Don't expand the layer's width if the width is fixed
|
|
and don't expand the layer's height if the height is fixed. */
|
|
static void
|
|
lo_block_grown(lo_Block *block)
|
|
{
|
|
CL_Layer *layer = block->layer;
|
|
lo_LayerDocState *layer_state;
|
|
LO_CellStruct *cell = block->cell;
|
|
|
|
XP_Rect clip, existing_clip;
|
|
|
|
clip.left = cell->x;
|
|
clip.top = cell->y;
|
|
clip.right = cell->x + cell->width;
|
|
clip.bottom = cell->y + cell->height;
|
|
|
|
layer_state = lo_GetLayerState(layer);
|
|
layer_state->contentWidth = cell->width;
|
|
layer_state->contentHeight = cell->height;
|
|
|
|
/* The coordinates of elements inside in-flow layers is with respect
|
|
to the containing layer. */
|
|
if (block->is_inflow)
|
|
XP_OffsetRect(&clip, -block->start_x, -block->start_y);
|
|
|
|
/* Don't allow a layer's clip to shrink. */
|
|
CL_GetLayerBbox(layer, &existing_clip);
|
|
XP_RectsBbox(&existing_clip, &clip, &clip);
|
|
|
|
lo_set_layer_bbox(layer, &clip, (PRBool)!block->is_dynamic);
|
|
}
|
|
|
|
static LO_CellStruct *
|
|
lo_compose_block(MWContext *context, lo_DocState *state, lo_Block *block)
|
|
{
|
|
int32 shift;
|
|
XP_Rect update_rect, ele_bbox;
|
|
LO_Element *tptr;
|
|
LO_CellStruct *cell;
|
|
|
|
cell = block->cell;
|
|
|
|
XP_BZERO(&update_rect, sizeof(XP_Rect));
|
|
|
|
/* Expands the dimensions of the cell to encompass any floating
|
|
* elements that are outside its bounds.
|
|
*/
|
|
tptr = state->float_list;
|
|
while (tptr != NULL) {
|
|
lo_GetElementBbox(tptr, &ele_bbox);
|
|
|
|
shift = cell->x - ele_bbox.left;
|
|
if (shift > 0) {
|
|
cell->x -= shift;
|
|
cell->width += shift;
|
|
}
|
|
shift = ele_bbox.right - (cell->x + cell->width);
|
|
if (shift > 0)
|
|
cell->width += shift;
|
|
shift = cell->y - ele_bbox.top;
|
|
if (shift > 0) {
|
|
cell->y -= shift;
|
|
cell->height += shift;
|
|
}
|
|
shift = ele_bbox.bottom - (cell->y + cell->height);
|
|
if (shift > 0)
|
|
cell->height += shift;
|
|
|
|
XP_RectsBbox(&ele_bbox, &update_rect, &update_rect);
|
|
|
|
tptr = tptr->lo_any.next;
|
|
}
|
|
tptr = state->float_list;
|
|
cell->cell_float_list = tptr;
|
|
|
|
if (context->compositor) {
|
|
/* Expand the block layer. */
|
|
lo_block_grown(block);
|
|
|
|
if (block->is_inflow)
|
|
XP_OffsetRect(&update_rect, -block->start_x, -block->start_y);
|
|
|
|
/* Update the area occupied by the floating elements. */
|
|
CL_UpdateLayerRect(context->compositor, block->layer, &update_rect,
|
|
PR_FALSE);
|
|
}
|
|
|
|
return(cell);
|
|
}
|
|
|
|
/* Process a </LAYER> or </ILAYER> tag. */
|
|
void
|
|
lo_EndLayerTag(MWContext *context,
|
|
lo_DocState *state,
|
|
PA_Tag *tag)
|
|
{
|
|
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context);
|
|
int16 win_csid = INTL_GetCSIWinCSID(c);
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
lo_Block *block = layer_state->temp_block;
|
|
|
|
if (! block)
|
|
return;
|
|
|
|
/* If opening layer tag had a MATCH attribute. The value of the
|
|
MATCH attribute in the closing tag must be identical or we
|
|
refuse to really close the layer. This capability is used by
|
|
libmime to prevent renegade mail messages from escaping the
|
|
bounds of their layer-enforced encapsulation using extra layer
|
|
end tags in the message. */
|
|
if (block->match_code) {
|
|
char *attempted_match =
|
|
(char*)PA_FetchParamValue(tag, PARAM_MATCH, win_csid);
|
|
if (!attempted_match)
|
|
return;
|
|
if (XP_STRCMP(attempted_match, block->match_code)) {
|
|
XP_TRACE(("Failed attempt to circumvent mail message security"));
|
|
XP_ASSERT(0);
|
|
PA_FREE(attempted_match);
|
|
return;
|
|
}
|
|
PA_FREE(attempted_match);
|
|
}
|
|
|
|
lo_EndLayer(context, state, PR_TRUE);
|
|
}
|
|
|
|
static PRBool
|
|
lo_destroy_ancillary_child_layers(CL_Layer *layer, void *unused)
|
|
{
|
|
LO_LayerType type;
|
|
type = LO_GetLayerType(layer);
|
|
if (type == LO_GROUP_LAYER)
|
|
return PR_TRUE;
|
|
CL_DestroyLayer(layer);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
void
|
|
lo_EndLayer(MWContext *context,
|
|
lo_DocState *state,
|
|
PRBool send_load_event)
|
|
{
|
|
int32 right_edge;
|
|
LO_CellStruct *cell;
|
|
lo_LayerDocState *enclosing_layer_state;
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
lo_Block *block = layer_state->temp_block;
|
|
|
|
/* During destruction, the compositor can disappear out from underneath us. */
|
|
if (!context->compositor)
|
|
return;
|
|
|
|
/*
|
|
* Protect from bad params.
|
|
*/
|
|
if (! block) {
|
|
return;
|
|
}
|
|
|
|
if (! block->is_inflow) {
|
|
lo_CloseOutLayout(context, state);
|
|
}
|
|
else {
|
|
/*
|
|
* Flush out the last line of the block, without
|
|
* introducing a line break.
|
|
* BUGBUG In reality, calling lo_FlushLineList
|
|
* also introduces a linefeed. We've set it up
|
|
* so that linefeeds within blocks are of 0
|
|
* size, so the linefeed is effectively ignored.
|
|
* The 0 linefeed stuff is a hack!
|
|
*/
|
|
lo_FlushLineList(context, state, LO_LINEFEED_BREAK_SOFT, LO_CLEAR_NONE, FALSE);
|
|
}
|
|
|
|
cell = lo_compose_block(context, state, block);
|
|
|
|
/* For an in-flow layer, expand the enclosing cell's size on the
|
|
right and bottom edges to contain any descendant
|
|
layers. However, we only do this for ILAYER tags, not for
|
|
layers created as a result of 'position:relative' applied to
|
|
style-sheet elements. */
|
|
if (block->is_inflow && !block->uses_ss_positioning) {
|
|
int32 grow_width, grow_height;
|
|
XP_Rect bbox;
|
|
|
|
CL_GetLayerBbox(block->layer, &bbox);
|
|
grow_width = (bbox.right - cell->width);
|
|
grow_height = (bbox.bottom - cell->height);
|
|
|
|
/* Position of right-edge of cell containing layer */
|
|
right_edge = state->x;
|
|
|
|
/* If the inflow layer is caused to grow vertically, force a
|
|
line break. */
|
|
if (grow_height > 0) {
|
|
lo_SetSoftLineBreakState(context, state, FALSE, 1);
|
|
cell->height = bbox.bottom;
|
|
state->y += grow_height;
|
|
}
|
|
|
|
/* Compute new position of cell's right edge. */
|
|
if (grow_width > 0) {
|
|
right_edge += grow_width;
|
|
cell->width = bbox.right;
|
|
}
|
|
|
|
/* Set minimum and maximum possible layout widths so that any
|
|
enclosing table cells will be dimensioned properly. */
|
|
if (right_edge + state->win_right > state->max_width)
|
|
state->max_width = right_edge + state->win_right;
|
|
if (right_edge + state->win_right > state->min_width)
|
|
state->min_width = right_edge + state->win_right;
|
|
}
|
|
|
|
/* Restore document layout state, if necessary */
|
|
lo_RestoreDocState(block, state, FALSE);
|
|
|
|
/* Mark the </(I)LAYER> tag by appending a LO_LAYER element to the line list. */
|
|
if (!lo_AppendLayerElement(context, state, NULL, NULL, TRUE))
|
|
return;
|
|
|
|
if (cell != NULL && !block->is_inflow)
|
|
{
|
|
int32 max_y;
|
|
|
|
cell->next = NULL;
|
|
cell->ele_id = NEXT_ELEMENT;
|
|
lo_RenumberCell(state, cell);
|
|
|
|
/* XXX - Do we really want to expand the document size to
|
|
include the layer ? */
|
|
max_y = cell->y + cell->y_offset + cell->height;
|
|
if (max_y > state->max_height)
|
|
{
|
|
state->max_height = max_y;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we're in table relayout, we can now safely delete the
|
|
* layer from a previous layout pass.
|
|
*/
|
|
if (state->in_relayout && block->old_layer_state) {
|
|
/* Get rid of child image layers, cell background layers, embedded window layers and blink layers */
|
|
CL_ForEachChildOfLayer(block->old_layer_state->layer,
|
|
lo_destroy_ancillary_child_layers,
|
|
(void *)block->old_layer_state->layer);
|
|
CL_DestroyLayer(block->old_layer_state->layer);
|
|
}
|
|
|
|
state->layer_nest_level--;
|
|
lo_PopLayerState(state);
|
|
enclosing_layer_state = lo_CurrentLayerState(state);
|
|
ET_SetActiveLayer(context, enclosing_layer_state->id);
|
|
|
|
if (block->is_dynamic) {
|
|
lo_TopState * top_state;
|
|
|
|
top_state = state->top_state;
|
|
top_state->nurl = NULL;
|
|
top_state->layout_status = PA_COMPLETE;
|
|
|
|
lo_CloseMochaWriteStream(top_state, EVENT_LOAD);
|
|
|
|
lo_FreeLayoutData(context, state);
|
|
|
|
/*
|
|
* We force a composite so that the layer is drawn, even
|
|
* if it is immediately changed again (as is sometimes the
|
|
* case when we're dynamically resizing layers.
|
|
*/
|
|
if (context->compositor) {
|
|
XP_Rect bbox;
|
|
|
|
CL_GetLayerBbox(block->layer, &bbox);
|
|
CL_UpdateLayerRect(context->compositor, block->layer,
|
|
&bbox, PR_TRUE);
|
|
}
|
|
}
|
|
|
|
if (send_load_event)
|
|
ET_SendLoadEvent(context, EVENT_LOAD, NULL, NULL, layer_state->id,
|
|
state->top_state->resize_reload);
|
|
|
|
}
|
|
|
|
void
|
|
lo_AddLineListToLayer(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_Element *line_list_end)
|
|
{
|
|
int32 max_y, max_x, min_x, height, width;
|
|
LO_Element *line_list;
|
|
LO_CellStruct *cell;
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
lo_Block *block = layer_state->temp_block;
|
|
|
|
line_list = state->line_list;
|
|
if (line_list == NULL)
|
|
return;
|
|
|
|
cell = block->cell;
|
|
|
|
/*
|
|
* For dynamic src changing, we just refresh the area occupied by
|
|
* the old layer. For the autoexpanding case, we reset the size
|
|
* of the layer to zero.
|
|
*/
|
|
if ((cell->cell_list == NULL) && (cell->cell_float_list == NULL)) {
|
|
|
|
/* If no page background color is specified, we must
|
|
commit to one as soon as any layout element is
|
|
displayed in order to avoid the excessive flashing that
|
|
would occur had we created the background later. */
|
|
if (state->top_state->nothing_displayed != FALSE) {
|
|
lo_use_default_doc_background(context, state);
|
|
state->top_state->nothing_displayed = FALSE;
|
|
}
|
|
|
|
if (block->is_dynamic) {
|
|
XP_Rect bbox;
|
|
|
|
CL_GetLayerBbox(block->layer, &bbox);
|
|
CL_UpdateLayerRect(context->compositor, block->layer, &bbox, PR_FALSE);
|
|
|
|
if (block->clip_expansion_policy == LO_AUTO_EXPAND_CLIP)
|
|
{
|
|
XP_Rect empty_bbox = {0, 0, 0, 0};
|
|
|
|
CL_SetLayerBbox(block->layer, &empty_bbox);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cell->cell_list_end == NULL)
|
|
{
|
|
line_list->lo_any.prev = NULL;
|
|
cell->cell_list = line_list;
|
|
line_list_end->lo_any.next = NULL;
|
|
cell->cell_list_end = line_list_end;
|
|
}
|
|
else
|
|
{
|
|
cell->cell_list_end->lo_any.next = line_list;
|
|
line_list->lo_any.prev = cell->cell_list_end;
|
|
line_list_end->lo_any.next = NULL;
|
|
cell->cell_list_end = line_list_end;
|
|
}
|
|
|
|
max_y = line_list_end->lo_any.y + line_list_end->lo_any.y_offset +
|
|
line_list_end->lo_any.height;
|
|
|
|
if (block->is_inflow)
|
|
height = max_y - cell->y;
|
|
else
|
|
height = max_y;
|
|
if (height > cell->height)
|
|
cell->height = height;
|
|
|
|
/*
|
|
* If the new line extends past the previous bounds of the
|
|
* cell, extend the bounds of the cell.
|
|
*/
|
|
max_x = line_list_end->lo_any.x + line_list_end->lo_any.x_offset +
|
|
line_list_end->lo_any.width;
|
|
|
|
if (block->is_inflow)
|
|
width = max_x - cell->x;
|
|
else
|
|
width = max_x;
|
|
if (width > cell->width)
|
|
cell->width = width;
|
|
|
|
/*
|
|
* This deals with the case where the new line laid out
|
|
* has a start position to the left of the start of the
|
|
* block. This can happen if the layer (with no absolute
|
|
* positioning starts to the right of an element that
|
|
* been aligned left and then its contents flow to the
|
|
* left of the start position below the aligned element i.e.
|
|
*
|
|
* *******
|
|
* ******* -----
|
|
* ******* -----
|
|
* ******* -----
|
|
* -------------
|
|
* -------------
|
|
*
|
|
* where * represents the element aligned left (an image
|
|
* for instance) and - represents the contents of the layer.
|
|
* The layer is shifted left (to the start position of
|
|
* the new line and expanded) i.e.
|
|
*
|
|
* *******
|
|
* ####### -----
|
|
* ####### -----
|
|
* ####### -----
|
|
* -------------
|
|
* -------------
|
|
*
|
|
* where # represents the (transparent) part of the layer
|
|
* overlapping the aligned element.
|
|
*/
|
|
min_x = line_list->lo_any.x + line_list->lo_any.x_offset;
|
|
if (min_x < cell->x) {
|
|
cell->width += cell->x - min_x;
|
|
cell->x = min_x;
|
|
}
|
|
|
|
if (context->compositor && (block->layer != NULL)) {
|
|
int32 min_y;
|
|
XP_Rect update_rect;
|
|
|
|
/* Expand the block layer. */
|
|
lo_block_grown(block);
|
|
|
|
/* Update the area of the line we just added. */
|
|
min_y = max_y - state->line_height;
|
|
update_rect.left = min_x;
|
|
update_rect.top = min_y;
|
|
update_rect.right = max_x;
|
|
update_rect.bottom = max_y;
|
|
|
|
if (block->is_inflow)
|
|
XP_OffsetRect(&update_rect, -block->start_x, -block->start_y);
|
|
|
|
CL_UpdateLayerRect(context->compositor, block->layer, &update_rect,
|
|
PR_FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
lo_FinishLayerLayout(MWContext *context, lo_DocState *state, int mocha_event)
|
|
{
|
|
lo_TopState *top_state = state->top_state;
|
|
PA_Tag *tag;
|
|
|
|
/* End the current block if we're done processing all tags */
|
|
if ((top_state->layout_blocking_element == NULL) &&
|
|
(top_state->tags == NULL))
|
|
lo_EndLayer(context, state, PR_TRUE);
|
|
/* We're still blocked on something, so
|
|
* create a fake </LAYER> tag and add it to the end of the
|
|
* blocked list. Processing this tag will eventually close
|
|
* out layout.
|
|
*/
|
|
else
|
|
{
|
|
tag = pa_CreateMDLTag(top_state->doc_data,
|
|
layer_end_tag,
|
|
sizeof layer_end_tag - 1);
|
|
|
|
if (tag != NULL) {
|
|
/* Put it at the end of the tag list */
|
|
if (top_state->tags == NULL)
|
|
top_state->tags = tag;
|
|
else
|
|
*top_state->tags_end = tag;
|
|
top_state->tags_end = &tag->next;
|
|
}
|
|
else
|
|
top_state->out_of_memory = TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_block_src_setter_exit_fn(URL_Struct *url_struct,
|
|
int status,
|
|
MWContext *context)
|
|
{
|
|
NET_FreeURLStruct(url_struct);
|
|
}
|
|
|
|
static lo_Block *
|
|
lo_init_block(MWContext *context, lo_DocState *state,
|
|
lo_LayerDocState *layer_state, int32 block_x, int32 block_y,
|
|
int32 width)
|
|
{
|
|
lo_Block *block;
|
|
|
|
/* Create a new block and initialize it */
|
|
block = XP_NEW_ZAP(lo_Block);
|
|
if (block == NULL)
|
|
return NULL;
|
|
block->context = context;
|
|
|
|
layer_state->temp_block = block;
|
|
|
|
if (!lo_SetupDocStateForLayer(state, layer_state,
|
|
width, layer_state->height, PR_FALSE, PR_FALSE)) {
|
|
lo_DeleteBlock(block);
|
|
layer_state->temp_block = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
static PRBool
|
|
lo_remove_extra_layers(CL_Layer *child, void *closure)
|
|
{
|
|
LO_LayerType type;
|
|
CL_Layer *parent = (CL_Layer *)closure;
|
|
|
|
type = LO_GetLayerType(child);
|
|
if ((type == LO_HTML_BLOCK_LAYER) || (type == LO_HTML_BACKGROUND_LAYER))
|
|
return PR_TRUE;
|
|
CL_RemoveChild(parent, child);
|
|
LO_LockLayout();
|
|
CL_DestroyLayerTree(child);
|
|
LO_UnlockLayout();
|
|
return PR_TRUE;
|
|
}
|
|
|
|
Bool
|
|
LO_PrepareLayerForWriting(MWContext *context, int32 layer_id,
|
|
const char *referer, int32 width)
|
|
{
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
CL_Layer *layer;
|
|
lo_LayerDocState *layer_state;
|
|
lo_Block *block;
|
|
int32 block_x, block_y;
|
|
|
|
/* Get the top state of the document */
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if ((top_state == NULL)||(top_state->doc_state == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
state = top_state->doc_state;
|
|
|
|
/*
|
|
* XXX For now, if we're still loading, we can't
|
|
* change the src of a layer. Is this the right
|
|
* thing to check that we're done with the original
|
|
* HTML stream?
|
|
*/
|
|
if ((top_state->doc_data != NULL) || lo_InsideLayer(state))
|
|
return FALSE;
|
|
|
|
layer_state = lo_GetLayerStateFromId(context, layer_id);
|
|
if (!layer_state)
|
|
return FALSE;
|
|
layer = layer_state->layer;
|
|
|
|
/* Delete the contents of the old layer */
|
|
lo_SaveFormElementStateInFormList(context,
|
|
layer_state->doc_lists->form_list,
|
|
TRUE);
|
|
if (layer_state->cell)
|
|
lo_RecycleElements(context, state, (LO_Element *)layer_state->cell);
|
|
lo_DeleteDocLists(context, layer_state->doc_lists);
|
|
|
|
/*
|
|
* Get rid of all "extra" layers associated (blink, cell_bg, images, etc.)
|
|
* associated with this layer and its content. Get rid of all group
|
|
* layer descendants, too.
|
|
*/
|
|
CL_ForEachChildOfLayer(layer,
|
|
lo_remove_extra_layers,
|
|
(void *)layer);
|
|
/*
|
|
* XXX We're assuming that FinishLayout has already been called and
|
|
* some of the doc_state has been trashed. Here we reset some of the
|
|
* state so that new tags can be parsed.
|
|
*/
|
|
state->base_font_size = DEFAULT_BASE_FONT_SIZE;
|
|
state->font_stack = lo_DefaultFont(state, context);
|
|
state->line_buf = PA_ALLOC(LINE_BUF_INC * sizeof(char));
|
|
if (state->line_buf == NULL) {
|
|
state->top_state->out_of_memory = TRUE;
|
|
return FALSE;
|
|
}
|
|
state->line_buf_size = LINE_BUF_INC;
|
|
state->line_buf_len = 0;
|
|
|
|
/*
|
|
* We reset the script_tag_count so that we'll reset the
|
|
* mocha decoder stream.
|
|
*/
|
|
top_state->script_tag_count = 0;
|
|
|
|
block_x = CL_GetLayerXOffset(layer);
|
|
block_y = CL_GetLayerYOffset(layer);
|
|
|
|
if (!lo_InitDocLists(context, layer_state->doc_lists)) {
|
|
state->top_state->out_of_memory = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
block = lo_init_block(context, state, layer_state, block_x, block_y,
|
|
width);
|
|
if (block)
|
|
block->cell = (LO_CellStruct *)lo_NewElement(context, state,
|
|
LO_CELL, NULL, 0);
|
|
|
|
if ((block == NULL) || (block->cell == NULL))
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
if (block) {
|
|
lo_DeleteBlock(block);
|
|
layer_state->temp_block = NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
layer_state->cell = block->cell;
|
|
|
|
lo_init_block_cell(context, state, block);
|
|
|
|
block->layer = layer;
|
|
block->is_dynamic = TRUE;
|
|
block->is_inline = TRUE;
|
|
block->source_url = referer ? XP_STRDUP(referer) : NULL;
|
|
|
|
state->layer_nest_level++;
|
|
/* Push this layer on the layer stack */
|
|
lo_PushLayerState(state->top_state, layer_state);
|
|
ET_SetActiveLayer(context, layer_state->id);
|
|
|
|
block->clip_expansion_policy = lo_GetLayerClipExpansionPolicy(layer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
Bool
|
|
LO_SetLayerSrc(MWContext *context, int32 layer_id, char *str,
|
|
const char *referer, int32 width)
|
|
{
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
char *url;
|
|
URL_Struct *url_struct;
|
|
int status;
|
|
lo_LayerDocState *layer_state;
|
|
|
|
/* Get the top state of the document */
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if ((top_state == NULL)||(top_state->doc_state == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
state = top_state->doc_state;
|
|
|
|
if (!LO_PrepareLayerForWriting(context, layer_id, referer, width))
|
|
return FALSE;
|
|
|
|
layer_state = lo_CurrentLayerState(state);
|
|
if (layer_state && layer_state->temp_block)
|
|
layer_state->temp_block->is_inline = FALSE;
|
|
|
|
/* Ask netlib to start loading the new URL */
|
|
url = NET_MakeAbsoluteURL(top_state->base_url, str);
|
|
if (url == NULL) {
|
|
/*lo_Block *block;*/
|
|
|
|
top_state->out_of_memory = TRUE;
|
|
/* XXX block = state->current_block;
|
|
if (block) {
|
|
lo_RecycleElements(context, state, (LO_Element *)block->cell);
|
|
XP_DELETE(block);
|
|
}
|
|
state->current_block = NULL; */
|
|
return FALSE;
|
|
}
|
|
|
|
url_struct = NET_CreateURLStruct(url, top_state->force_reload);
|
|
if (url_struct == NULL)
|
|
top_state->out_of_memory = TRUE;
|
|
else {
|
|
url_struct->referer = XP_STRDUP(referer);
|
|
if (!url_struct->referer) {
|
|
top_state->out_of_memory = TRUE;
|
|
NET_FreeURLStruct(url_struct);
|
|
XP_FREE(url);
|
|
return FALSE;
|
|
}
|
|
|
|
lo_SetBaseUrl(state->top_state, url, FALSE);
|
|
XP_FREEIF(top_state->inline_stream_blocked_base_url);
|
|
|
|
status = NET_GetURL(url_struct, FO_CACHE_AND_PRESENT_INLINE, context,
|
|
lo_block_src_setter_exit_fn);
|
|
}
|
|
|
|
XP_FREE(url);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Create an anonymous layer, not tied to the HTML source.
|
|
Returns -1 on error, 0 if unable to create a new layer because the parser
|
|
stream is still active. */
|
|
int32
|
|
LO_CreateNewLayer(MWContext *context, int32 wrap_width, int32 parent_layer_id)
|
|
{
|
|
int32 doc_id, layer_id;
|
|
LO_BlockInitializeStruct *param;
|
|
CL_Layer *parent_layer;
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
lo_LayerDocState *layer_state;
|
|
CL_Layer *layer;
|
|
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if (!top_state)
|
|
return 0;
|
|
|
|
/*
|
|
* XXX For now, if we're still loading, we can't
|
|
* change the src of a layer. Is this the right
|
|
* thing to check that we're done with the original
|
|
* HTML stream?
|
|
*/
|
|
state = top_state->doc_state;
|
|
if ((top_state->doc_data != NULL) || lo_InsideLayer(state))
|
|
return 0;
|
|
|
|
param = XP_NEW_ZAP(LO_BlockInitializeStruct);
|
|
if(!param)
|
|
return -1;
|
|
|
|
param->has_width = PR_TRUE;
|
|
param->width = wrap_width;
|
|
param->has_left = PR_TRUE;
|
|
param->left = 0;
|
|
param->has_top = PR_TRUE;
|
|
param->top = 0;
|
|
param->visibility = "hide";
|
|
param->clip_expansion_policy = LO_AUTO_EXPAND_CLIP;
|
|
|
|
layer_id = top_state->max_layer_num;
|
|
|
|
/* Create a fake layer and append it to the document */
|
|
lo_begin_layer_internal(context, state, param, FALSE);
|
|
lo_EndLayer(context, state, PR_FALSE);
|
|
XP_FREE(param);
|
|
|
|
/* The only way we know if we succeeded is to see if the
|
|
top_state->layers array has grown. */
|
|
if (layer_id == top_state->max_layer_num)
|
|
return -1;
|
|
|
|
/* Reparent the layer, in case the parent wasn't the _DOCUMENT */
|
|
parent_layer = LO_GetLayerFromId(context, parent_layer_id);
|
|
layer_id = top_state->max_layer_num;
|
|
layer_state = lo_GetLayerStateFromId(context, layer_id);
|
|
layer_state->is_constructed_layer = PR_TRUE;
|
|
layer = layer_state->layer;
|
|
CL_RemoveChild(CL_GetLayerParent(layer), layer);
|
|
CL_InsertChild(parent_layer, layer, NULL, CL_ABOVE);
|
|
|
|
return layer_id;
|
|
}
|
|
|
|
|
|
/* Called when you see a start LO_LAYER element. Reflow version of lo_begin_layer_internal */
|
|
void
|
|
lo_BeginLayerReflow(MWContext *context, lo_DocState *state,
|
|
LO_BlockInitializeStruct *param,
|
|
lo_LayerDocState *layer_state)
|
|
{
|
|
lo_Block *block;
|
|
lo_LayerDocState *parent_layer_state;
|
|
int32 block_wrap_width, x_parent_offset, y_parent_offset;
|
|
CL_Layer *layer, *parent_layer;
|
|
int32 block_x, block_y;
|
|
Bool is_inflow;
|
|
XP_Rect bbox;
|
|
LO_CellStruct *cell;
|
|
lo_GroupLayerClosure *closure;
|
|
lo_HTMLBlockClosure *content_closure;
|
|
|
|
if (!context->compositor || !param || !layer_state)
|
|
return;
|
|
|
|
block = layer_state->temp_block;
|
|
is_inflow = block->is_inflow;
|
|
cell = layer_state->cell;
|
|
layer = layer_state->layer;
|
|
|
|
/*
|
|
* If this is a nested inflow layer, then flush the line list into our parent
|
|
* (without introducing a line break). This ensures that whatever line changes
|
|
* (specifically shifting of cell origin) that occur while laying out this ilayer
|
|
* are reflected in our parent.
|
|
*/
|
|
if (lo_InsideInflowLayer(state) && is_inflow)
|
|
{
|
|
lo_rl_AddBreakAndFlushLine(context, state, LO_LINEFEED_BREAK_SOFT, LO_CLEAR_NONE, FALSE);
|
|
}
|
|
|
|
/* In-flow layers use coordinates that are relative to their
|
|
"natural", in-flow position. */
|
|
if (block->is_inflow) {
|
|
block->x_offset = state->x;
|
|
block->y_offset = state->y;
|
|
} else {
|
|
block->x_offset = 0;
|
|
block->y_offset = 0;
|
|
}
|
|
|
|
x_parent_offset = y_parent_offset = 0;
|
|
parent_layer_state = lo_CurrentLayerState(state);
|
|
parent_layer = parent_layer_state->layer;
|
|
if (parent_layer) /* Paranoia */
|
|
lo_GetLayerXYShift(parent_layer, &x_parent_offset, &y_parent_offset);
|
|
|
|
/*
|
|
* Get the X position of the block
|
|
*/
|
|
if (param->has_left)
|
|
{
|
|
block_x = param->left;
|
|
} else {
|
|
if (block->is_inflow) {
|
|
block_x = 0;
|
|
} else {
|
|
block_x = state->x - x_parent_offset;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the Y position of the block
|
|
*/
|
|
if (param->has_top)
|
|
{
|
|
block_y = param->top;
|
|
} else {
|
|
if (block->is_inflow) {
|
|
block_y = 0;
|
|
} else {
|
|
block_y = state->y - y_parent_offset;
|
|
}
|
|
}
|
|
|
|
XP_BZERO(&bbox, sizeof(bbox));
|
|
|
|
/*
|
|
* Get the width parameter, in absolute or percentage.
|
|
* If percentage, make it absolute.
|
|
*/
|
|
if (param->has_width)
|
|
block_wrap_width = param->width;
|
|
else if (param->has_left)
|
|
block_wrap_width = state->right_margin - param->left;
|
|
else
|
|
block_wrap_width = state->right_margin - state->x;
|
|
|
|
|
|
/*
|
|
* Parse the comma separated coordinate list into an
|
|
* array of integers.
|
|
*/
|
|
if (param->clip)
|
|
{
|
|
bbox = *param->clip;
|
|
|
|
/* Don't allow the layer's clip to expand */
|
|
block->clip_expansion_policy = param->clip_expansion_policy;
|
|
} else {
|
|
/* Allow the clip to expand to include the layer's contents. */
|
|
block->clip_expansion_policy = LO_AUTO_EXPAND_CLIP;
|
|
if (param->has_width)
|
|
bbox.right = block_wrap_width;
|
|
}
|
|
|
|
if (param->has_height) {
|
|
layer_state->height = param->height;
|
|
/* If no CLIP set, set initial bottom edge of layer clip to be
|
|
the same as HEIGHT. */
|
|
if (block->clip_expansion_policy & LO_AUTO_EXPAND_CLIP_BOTTOM)
|
|
bbox.bottom = param->height;
|
|
}
|
|
|
|
/* Treat any value of OVERFLOW, except "hidden" to be the same as
|
|
"visible". For compatibility with the older CSS positioning
|
|
spec, we also treat "none" specially. Can't set OVERFLOW
|
|
unless HEIGHT is provided. */
|
|
if (param->has_height &&
|
|
param->overflow &&
|
|
XP_STRCASECMP(param->overflow, "none") &&
|
|
XP_STRCASECMP(param->overflow, "visible")) {
|
|
|
|
/* Constrain all drawing to {0, 0, width, height} box */
|
|
XP_Rect *viewRect = &layer_state->viewRect;
|
|
viewRect->left = 0;
|
|
viewRect->top = 0;
|
|
viewRect->right = block_wrap_width;
|
|
viewRect->bottom = param->height;
|
|
}
|
|
|
|
/* Reset document layout state, saving old state to be restored
|
|
after we lay out this layer. */
|
|
if (!lo_SetupDocStateForLayer(state, layer_state,
|
|
block_wrap_width, layer_state->height,
|
|
(PRBool)is_inflow, PR_TRUE)) {
|
|
state->top_state->out_of_memory = TRUE;
|
|
lo_DeleteLayerState(context, state, layer_state);
|
|
return;
|
|
}
|
|
|
|
/* Reset cell struct variables that depend on the doc state */
|
|
cell->ele_id = NEXT_ELEMENT;
|
|
cell->x = state->x;
|
|
cell->y = state->y;
|
|
cell->width = 0;
|
|
cell->height = 0;
|
|
cell->x_offset = 0;
|
|
cell->y_offset = 0;
|
|
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
if (closure)
|
|
{
|
|
/* Reset group layer closure's properties */
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
closure->x_offset = block->x_offset;
|
|
closure->y_offset = block->y_offset;
|
|
closure->wrap_width = block_wrap_width;
|
|
}
|
|
|
|
/* Move layer into position. Set clip dimensions and initial visibility. */
|
|
LO_MoveLayer(layer, block_x, block_y);
|
|
LO_SetLayerBbox(layer, &bbox);
|
|
lo_expand_parent_bbox(layer);
|
|
|
|
/* Resize the layer */
|
|
CL_ResizeLayer(layer, block_wrap_width, layer_state->height);
|
|
|
|
/* Push this layer on the layer stack */
|
|
lo_PushLayerState(state->top_state, layer_state);
|
|
state->layer_nest_level++;
|
|
}
|
|
|
|
/* Called when the end LO_LAYER element is seen by the reflow code */
|
|
void
|
|
lo_EndLayerReflow(MWContext *context, lo_DocState *state)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
lo_Block *block = layer_state->temp_block;
|
|
LO_CellStruct *cell = NULL;
|
|
int32 right_edge;
|
|
|
|
/* During destruction, the compositor can disappear out from underneath us. */
|
|
if (!context->compositor || !block)
|
|
return;
|
|
|
|
if (! block->is_inflow) {
|
|
lo_EndLayoutDuringReflow(context, state);
|
|
}
|
|
else {
|
|
lo_rl_FlushLineList(context, state, LO_LINEFEED_BREAK_SOFT, LO_CLEAR_NONE, FALSE);
|
|
}
|
|
|
|
cell = lo_compose_block(context, state, block);
|
|
|
|
/* For an in-flow layer, expand the enclosing cell's size on the
|
|
right and bottom edges to contain any descendant
|
|
layers. However, we only do this for ILAYER tags, not for
|
|
layers created as a result of 'position:relative' applied to
|
|
style-sheet elements. */
|
|
if (block->is_inflow && !block->uses_ss_positioning) {
|
|
int32 grow_width, grow_height;
|
|
XP_Rect bbox;
|
|
|
|
CL_GetLayerBbox(block->layer, &bbox);
|
|
grow_width = (bbox.right - cell->width);
|
|
grow_height = (bbox.bottom - cell->height);
|
|
|
|
/* Position of right-edge of cell containing layer */
|
|
right_edge = state->x;
|
|
|
|
/* If the inflow layer is caused to grow vertically, force a
|
|
line break. */
|
|
if (grow_height > 0) {
|
|
lo_SetLineBreakState ( context, state, FALSE, LO_LINEFEED_BREAK_HARD, 1, TRUE);
|
|
cell->height = bbox.bottom;
|
|
state->y += grow_height;
|
|
}
|
|
|
|
/* Compute new position of cell's right edge. */
|
|
if (grow_width > 0) {
|
|
right_edge += grow_width;
|
|
cell->width = bbox.right;
|
|
}
|
|
|
|
/* Set minimum and maximum possible layout widths so that any
|
|
enclosing table cells will be dimensioned properly. */
|
|
if (right_edge + state->win_right > state->max_width)
|
|
state->max_width = right_edge + state->win_right;
|
|
if (right_edge + state->win_right > state->min_width)
|
|
state->min_width = right_edge + state->win_right;
|
|
}
|
|
|
|
/* Restore document layout state, if necessary */
|
|
lo_RestoreDocState(block, state, TRUE);
|
|
|
|
if (cell != NULL && !block->is_inflow)
|
|
{
|
|
int32 max_y;
|
|
|
|
cell->next = NULL;
|
|
cell->ele_id = NEXT_ELEMENT;
|
|
lo_RenumberCell(state, cell);
|
|
|
|
/* XXX - Do we really want to expand the document size to
|
|
include the layer ? */
|
|
max_y = cell->y + cell->y_offset + cell->height;
|
|
if (max_y > state->max_height)
|
|
{
|
|
state->max_height = max_y;
|
|
}
|
|
}
|
|
state->layer_nest_level--;
|
|
lo_PopLayerState(state);
|
|
|
|
ET_SendLoadEvent(context, EVENT_LOAD, NULL, NULL, layer_state->id,
|
|
TRUE);
|
|
|
|
}
|