mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
3459 lines
84 KiB
C
3459 lines
84 KiB
C
/* -*- Mode: C++; tab-width: 8; 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.
|
|
*/
|
|
|
|
/*
|
|
* layimage.c - Image layout and fetching/prefetching
|
|
*
|
|
*/
|
|
|
|
#include "xp.h"
|
|
#include "net.h"
|
|
#include "xp_rgb.h"
|
|
#include "pa_parse.h"
|
|
#include "layout.h"
|
|
#include "laylayer.h"
|
|
#include "laystyle.h"
|
|
#include "secnav.h"
|
|
#include "prefapi.h"
|
|
#include "xlate.h"
|
|
#include "layers.h"
|
|
|
|
extern int MK_OUT_OF_MEMORY;
|
|
|
|
#define IL_CLIENT /* Defined by Image Library clients */
|
|
#include "libimg.h" /* Image Library public API. */
|
|
|
|
#ifdef MOCHA
|
|
#include "libevent.h"
|
|
#endif /* MOCHA */
|
|
|
|
#ifdef PROFILE
|
|
#pragma profile on
|
|
#endif
|
|
|
|
#define IMAGE_DEF_DIM 50
|
|
#define IMAGE_DEF_BORDER 0
|
|
#define IMAGE_DEF_ANCHOR_BORDER 2
|
|
#define IMAGE_DEF_VERTICAL_SPACE 0
|
|
#define IMAGE_DEF_HORIZONTAL_SPACE 0
|
|
#define IMAGE_DEF_FLOAT_HORIZONTAL_SPACE 3
|
|
|
|
/* Closure data to be passed into lo_ImageObserver. */
|
|
typedef struct lo_ImageObsClosure {
|
|
MWContext *context;
|
|
LO_ImageStruct *lo_image;
|
|
XP_ObserverList obs_list;
|
|
} lo_ImageObsClosure;
|
|
|
|
extern LO_TextStruct *lo_AltTextElement(MWContext *context);
|
|
|
|
PUBLIC char *
|
|
LO_GetBookmarkIconURLForPage(MWContext *context, BMIconType type)
|
|
{
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
|
|
/*
|
|
* Get the unique document ID, and retreive this
|
|
* documents layout state.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if (!top_state)
|
|
return NULL;
|
|
|
|
if(type == SMALL_BM_ICON)
|
|
return top_state->small_bm_icon;
|
|
else if(type == SMALL_BM_ICON)
|
|
return top_state->large_bm_icon;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Primitive image allocator also sets defaults for image structure */
|
|
static LO_ImageStruct *
|
|
lo_new_image_element(MWContext *context, lo_DocState *state,
|
|
void *edit_element, LO_TextAttr *tptr)
|
|
{
|
|
LO_ImageStruct *image;
|
|
image = (LO_ImageStruct *)lo_NewElement(context, state, LO_IMAGE, edit_element, 0);
|
|
if (image == NULL)
|
|
return NULL;
|
|
|
|
/*
|
|
* preserve the edit element and offset across the bzero.
|
|
* The edit_element and edit_offset were set in lo_NewElement.
|
|
*/
|
|
{
|
|
void* save_edit_element = image->edit_element;
|
|
int32 save_edit_offset = image->edit_offset;
|
|
XP_BZERO(image, sizeof(*image));
|
|
image->edit_element = save_edit_element;
|
|
image->edit_offset = save_edit_offset;
|
|
}
|
|
|
|
image->type = LO_IMAGE;
|
|
|
|
/*
|
|
* Fill in default font information.
|
|
*/
|
|
if (tptr == NULL)
|
|
{
|
|
LO_TextAttr tmp_attr;
|
|
|
|
lo_SetDefaultFontAttr(state, &tmp_attr, context);
|
|
tptr = lo_FetchTextAttr(state, &tmp_attr);
|
|
}
|
|
image->text_attr = tptr;
|
|
image->image_attr = XP_NEW(LO_ImageAttr);
|
|
if (image->image_attr == NULL)
|
|
{ /* clean up all previously allocated objects */
|
|
lo_FreeElement(context, (LO_Element *)image, FALSE);
|
|
state->top_state->out_of_memory = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
image->image_attr->attrmask = 0;
|
|
image->image_attr->layer_id = LO_DOCUMENT_LAYER_ID;
|
|
image->image_attr->form_id = -1;
|
|
image->image_attr->alignment = LO_ALIGN_BASELINE;
|
|
image->image_attr->usemap_name = NULL;
|
|
image->image_attr->usemap_ptr = NULL;
|
|
|
|
image->ele_attrmask = 0;
|
|
|
|
image->sel_start = -1;
|
|
image->sel_end = -1;
|
|
|
|
image->is_icon = FALSE;
|
|
image->image_status = IL_START_URL;
|
|
|
|
return image;
|
|
}
|
|
|
|
PRIVATE
|
|
Bool
|
|
lo_parse_rgb(char *rgb, uint8 *red_ptr, uint8 *green_ptr, uint8 *blue_ptr, XP_Bool double_three_byte_codes)
|
|
{
|
|
char *ptr;
|
|
int32 i, j, len;
|
|
int32 val, bval;
|
|
int32 red_val, green_val, blue_val;
|
|
intn bytes_per_val;
|
|
|
|
*red_ptr = 0;
|
|
*green_ptr = 0;
|
|
*blue_ptr = 0;
|
|
red_val = 0;
|
|
green_val = 0;
|
|
blue_val = 0;
|
|
|
|
if (rgb == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
len = XP_STRLEN(rgb);
|
|
if (len == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Strings not starting with a '#' are probably named colors.
|
|
* look them up in the xp lookup table.
|
|
*/
|
|
ptr = rgb;
|
|
if (*ptr == '#')
|
|
{
|
|
ptr++;
|
|
len--;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If we successfully look up a color name, return its RGB.
|
|
*/
|
|
if (XP_ColorNameToRGB(ptr, red_ptr, green_ptr, blue_ptr) == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (len == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
bytes_per_val = (intn)((len + 2) / 3);
|
|
if (bytes_per_val > 4)
|
|
{
|
|
bytes_per_val = 4;
|
|
}
|
|
|
|
for (j=0; j<3; j++)
|
|
{
|
|
val = 0;
|
|
for (i=0; i<bytes_per_val; i++)
|
|
{
|
|
if (*ptr == '\0')
|
|
{
|
|
bval = 0;
|
|
}
|
|
else
|
|
{
|
|
bval = TOLOWER((unsigned char)*ptr);
|
|
if ((bval >= '0')&&(bval <= '9'))
|
|
{
|
|
bval = bval - '0';
|
|
}
|
|
else if ((bval >= 'a')&&(bval <= 'f'))
|
|
{
|
|
bval = bval - 'a' + 10;
|
|
}
|
|
else
|
|
{
|
|
bval = 0;
|
|
}
|
|
ptr++;
|
|
}
|
|
val = (val << 4) + bval;
|
|
}
|
|
if (j == 0)
|
|
{
|
|
red_val = val;
|
|
}
|
|
else if (j == 1)
|
|
{
|
|
green_val = val;
|
|
}
|
|
else
|
|
{
|
|
blue_val = val;
|
|
}
|
|
}
|
|
|
|
if(double_three_byte_codes && bytes_per_val == 1)
|
|
{
|
|
red_val = (red_val << 4) + red_val;
|
|
green_val = (green_val << 4) + green_val;
|
|
blue_val = (blue_val << 4) + blue_val;
|
|
}
|
|
|
|
while ((red_val > 255)||(green_val > 255)||(blue_val > 255))
|
|
{
|
|
red_val = (red_val >> 4);
|
|
green_val = (green_val >> 4);
|
|
blue_val = (blue_val >> 4);
|
|
}
|
|
*red_ptr = (uint8)red_val;
|
|
*green_ptr = (uint8)green_val;
|
|
*blue_ptr = (uint8)blue_val;
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
LO_ParseRGB(char *rgb, uint8 *red_ptr, uint8 *green_ptr, uint8 *blue_ptr)
|
|
{
|
|
return lo_parse_rgb(rgb, red_ptr, green_ptr, blue_ptr, FALSE);
|
|
}
|
|
|
|
|
|
/* parse a comma separated list of 3 values
|
|
*
|
|
* Format is "rgb(red, green, blue)"
|
|
* color values can be in integer 1-255 or percent 1%-100%
|
|
* illegal values must be truncated
|
|
*
|
|
* if there are fewer than three arguments this function will
|
|
* set colors for whatever number exist.
|
|
* if more than 3 args are passed in they will be ignored
|
|
*/
|
|
static Bool
|
|
lo_parse_style_RGB_functional_notation(char *rgb,
|
|
uint8 *red_ptr,
|
|
uint8 *green_ptr,
|
|
uint8 *blue_ptr)
|
|
{
|
|
int index=0;
|
|
char *value, *end;
|
|
int num_value;
|
|
|
|
rgb = XP_StripLine(rgb);
|
|
|
|
/* go past rgb( and remove the last ')' */
|
|
#define _RGB "rgb"
|
|
if(!strncasecomp(rgb, _RGB, sizeof(_RGB)-1))
|
|
{
|
|
char *end;
|
|
|
|
/* go past "RGB" */
|
|
rgb += sizeof(_RGB)-1;
|
|
|
|
/* go past spaces */
|
|
while(XP_IS_SPACE(*rgb)) rgb++;
|
|
|
|
/* go past '(' */
|
|
if(*rgb == '(')
|
|
rgb++;
|
|
|
|
/* kill any more spaces */
|
|
while(XP_IS_SPACE(*rgb)) rgb++;
|
|
|
|
end = &rgb[XP_STRLEN(rgb)-1];
|
|
if(*end == ')')
|
|
*end = '\0';
|
|
}
|
|
|
|
value = XP_STRTOK(rgb, ",");
|
|
|
|
while(value && index < 3)
|
|
{
|
|
XP_Bool is_percentage = FALSE;
|
|
int scaled_value;
|
|
|
|
value = XP_StripLine(value);
|
|
|
|
num_value = XP_ATOI(value);
|
|
|
|
if(num_value < 0)
|
|
num_value = 0;
|
|
|
|
end = &rgb[XP_STRLEN(rgb)-1];
|
|
if(*end == '%')
|
|
{
|
|
/* percentage value */
|
|
|
|
if(num_value > 100)
|
|
num_value = 100;
|
|
|
|
scaled_value = (255 * num_value) / 100;
|
|
|
|
}
|
|
else
|
|
{
|
|
if(num_value > 255)
|
|
num_value = 255;
|
|
|
|
scaled_value = num_value;
|
|
}
|
|
|
|
if(index == 0)
|
|
*red_ptr = scaled_value;
|
|
else if(index == 1)
|
|
*green_ptr = scaled_value;
|
|
else if(index == 2)
|
|
*blue_ptr = scaled_value;
|
|
|
|
index++;
|
|
value = XP_STRTOK(NULL, ",");
|
|
}
|
|
|
|
if(index < 3)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
LO_ParseStyleSheetRGB(char *rgb, uint8 *red_ptr, uint8 *green_ptr, uint8 *blue_ptr)
|
|
{
|
|
/* can take the form of:
|
|
* #f00
|
|
* #ff0000
|
|
* or rgb(255, 0, 0)
|
|
* or rgb(100%, 0%, 0%)
|
|
* all are equivalent values
|
|
* need to truncate illegal values like 265 or 110%
|
|
*/
|
|
|
|
rgb = XP_StripLine(rgb);
|
|
|
|
if(!strcasecomp(rgb, "transparent"))
|
|
{
|
|
*red_ptr = 0;
|
|
*green_ptr = 0;
|
|
*blue_ptr = 0;
|
|
return FALSE;
|
|
}
|
|
else if(!strncasecomp(rgb, _RGB, sizeof(_RGB)-1)) /* functional notation */
|
|
{
|
|
return lo_parse_style_RGB_functional_notation(rgb,
|
|
red_ptr,
|
|
green_ptr,
|
|
blue_ptr);
|
|
}
|
|
else /* assume #FF0000 or #F00 notation */
|
|
{
|
|
return lo_parse_rgb(rgb, red_ptr, green_ptr, blue_ptr, TRUE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parses the BACKGROUND attribute (if one exists) and returns a url.
|
|
* If there is no attribute specified, it returns NULL. For code sharing
|
|
* between tags that have a BACKGROUND attribute.
|
|
*/
|
|
char *
|
|
lo_ParseBackgroundAttribute(MWContext *context, lo_DocState *state,
|
|
PA_Tag *tag, Bool from_user)
|
|
{
|
|
PA_Block buff, image_url;
|
|
char *str;
|
|
|
|
/*
|
|
* Get the required src parameter, and make the resulting
|
|
* url and absolute url.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_BACKGROUND);
|
|
if (buff != NULL)
|
|
{
|
|
PA_Block new_buff;
|
|
char *new_str;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
if (str != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
/*
|
|
* Do not allow BACKGROUND=""
|
|
*/
|
|
if ((str != NULL)&&(*str != '\0'))
|
|
{
|
|
new_str = NET_MakeAbsoluteURL(
|
|
state->top_state->base_url, str);
|
|
}
|
|
else
|
|
{
|
|
new_str = NULL;
|
|
}
|
|
if (new_str == NULL)
|
|
{
|
|
new_buff = NULL;
|
|
}
|
|
else
|
|
{
|
|
char *url;
|
|
|
|
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
|
|
if (new_buff != NULL)
|
|
{
|
|
PA_LOCK(url, char *, new_buff);
|
|
XP_STRCPY(url, new_str);
|
|
PA_UNLOCK(new_buff);
|
|
}
|
|
else
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
XP_FREE(new_str);
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
image_url = new_buff;
|
|
|
|
/*
|
|
* Make sure we don't load insecure images inside
|
|
* a secure document.
|
|
*/
|
|
if (state->top_state->security_level > 0)
|
|
{
|
|
PA_LOCK(str, char *, image_url);
|
|
|
|
if (((from_user == FALSE)&&(str != NULL))||
|
|
((from_user != FALSE)&&(str != NULL)&&
|
|
(XP_STRNCMP(str, "file:/", 6) != 0)))
|
|
{
|
|
if (NET_IsURLSecure(str) == FALSE)
|
|
{
|
|
PA_UNLOCK(image_url);
|
|
PA_FREE(image_url);
|
|
image_url = PA_ALLOC(
|
|
XP_STRLEN("internal-icon-insecure") + 1);
|
|
PA_LOCK(str, char *, image_url);
|
|
if (image_url != NULL)
|
|
{
|
|
XP_STRCPY(str, "internal-icon-insecure");
|
|
}
|
|
else
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
if (state->top_state->insecure_images == FALSE)
|
|
{
|
|
state->top_state->insecure_images = TRUE;
|
|
SECNAV_SecurityDialog(context,
|
|
SD_INSECURE_DOCS_WITHIN_SECURE_DOCS_NOT_SHOWN);
|
|
}
|
|
}
|
|
}
|
|
PA_UNLOCK(image_url);
|
|
}
|
|
|
|
return (char*)image_url;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
lo_BodyForeground(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
uint8 red, green, blue;
|
|
|
|
/*
|
|
* Don't do this is we had a TEXT from a previous BODY tag.
|
|
*/
|
|
if ((state->top_state->body_attr & BODY_ATTR_TEXT) == 0)
|
|
{
|
|
/*
|
|
* Get the document text TEXT parameter.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_TEXT);
|
|
if (buff != NULL)
|
|
{
|
|
LO_Color text_fg;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
LO_ParseRGB(str, &red, &green, &blue);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
|
|
text_fg.red = red;
|
|
text_fg.green = green;
|
|
text_fg.blue = blue;
|
|
lo_ChangeBodyTextFGColor(context, state, &text_fg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Don't do this is we had a LINK from a previous BODY tag.
|
|
*/
|
|
if ((state->top_state->body_attr & BODY_ATTR_LINK) == 0)
|
|
{
|
|
/*
|
|
* Get the anchor text LINK parameter.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_LINK);
|
|
if (buff != NULL)
|
|
{
|
|
state->top_state->body_attr |= BODY_ATTR_LINK;
|
|
PA_LOCK(str, char *, buff);
|
|
LO_ParseRGB(str, &red, &green, &blue);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
|
|
state->anchor_color.red = red;
|
|
state->anchor_color.green = green;
|
|
state->anchor_color.blue = blue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Don't do this is we had a VLINK from a previous BODY tag.
|
|
*/
|
|
if ((state->top_state->body_attr & BODY_ATTR_VLINK) == 0)
|
|
{
|
|
/*
|
|
* Get the visited anchor text VLINK parameter.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_VLINK);
|
|
if (buff != NULL)
|
|
{
|
|
state->top_state->body_attr |= BODY_ATTR_VLINK;
|
|
PA_LOCK(str, char *, buff);
|
|
LO_ParseRGB(str, &red, &green, &blue);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
|
|
state->visited_anchor_color.red = red;
|
|
state->visited_anchor_color.green = green;
|
|
state->visited_anchor_color.blue = blue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Don't do this is we had a ALINK from a previous BODY tag.
|
|
*/
|
|
if ((state->top_state->body_attr & BODY_ATTR_ALINK) == 0)
|
|
{
|
|
/*
|
|
* Get the active anchor text ALINK parameter.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ALINK);
|
|
if (buff != NULL)
|
|
{
|
|
state->top_state->body_attr |= BODY_ATTR_ALINK;
|
|
PA_LOCK(str, char *, buff);
|
|
LO_ParseRGB(str, &red, &green, &blue);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
|
|
state->active_anchor_color.red = red;
|
|
state->active_anchor_color.green = green;
|
|
state->active_anchor_color.blue = blue;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lo_GetStyleSheetBodyBackgroundAndColor(MWContext *context,
|
|
lo_DocState *state,
|
|
char **image_urlp,
|
|
char **color_str,
|
|
lo_TileMode *tile_mode)
|
|
{
|
|
char *image_url;
|
|
char *absolute_image_url;
|
|
char *tile_mode_prop;
|
|
StyleStruct *ss;
|
|
|
|
/* Set defaults in case something goes wrong below. */
|
|
*color_str = NULL;
|
|
*image_urlp = NULL;
|
|
|
|
/* //CLM: This is null in Editor (sometimes?)*/
|
|
if(!state->top_state->style_stack)
|
|
return;
|
|
|
|
/* get the top style */
|
|
ss = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
|
|
|
|
/* see if it has a bgcolor attribute */
|
|
if(!ss)
|
|
return;
|
|
|
|
*color_str = STYLESTRUCT_GetString(ss, BG_COLOR_STYLE);
|
|
|
|
image_url = STYLESTRUCT_GetString(ss, BG_IMAGE_STYLE);
|
|
|
|
if(!image_url)
|
|
return;
|
|
|
|
if(!strcasecomp(image_url, "none"))
|
|
{
|
|
XP_FREE(image_url);
|
|
return;
|
|
}
|
|
|
|
absolute_image_url = NET_MakeAbsoluteURL(state->top_state->base_url,
|
|
lo_ParseStyleSheetURL(image_url));
|
|
XP_FREE(image_url);
|
|
|
|
if(!absolute_image_url)
|
|
return;
|
|
|
|
*image_urlp = absolute_image_url;
|
|
|
|
/* get the tiling mode */
|
|
tile_mode_prop = STYLESTRUCT_GetString(ss, BG_REPEAT_STYLE);
|
|
if(tile_mode_prop)
|
|
{
|
|
if(!strcasecomp(tile_mode_prop, BG_REPEAT_NONE_STYLE))
|
|
*tile_mode = LO_NO_TILE;
|
|
else if(!strcasecomp(tile_mode_prop, BG_REPEAT_X_STYLE))
|
|
*tile_mode = LO_TILE_HORIZ;
|
|
else if(!strcasecomp(tile_mode_prop, BG_REPEAT_Y_STYLE))
|
|
*tile_mode = LO_TILE_VERT;
|
|
else
|
|
*tile_mode = LO_TILE_BOTH;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
lo_BodyBackground(MWContext *context, lo_DocState *state, PA_Tag *tag,
|
|
Bool from_user)
|
|
{
|
|
LO_Color rgb;
|
|
PA_Block buff=NULL;
|
|
char *color_str = NULL;
|
|
char *image_url = NULL;
|
|
CL_Layer *layer;
|
|
lo_TileMode tile_mode = LO_TILE_BOTH;
|
|
|
|
if (! context->compositor)
|
|
return;
|
|
|
|
/* if we are here then we must be in the BODY tag.
|
|
* Get the top state from the style sheet and get BG color and
|
|
* BG Image from it
|
|
*/
|
|
lo_GetStyleSheetBodyBackgroundAndColor(context,
|
|
state,
|
|
&image_url,
|
|
&color_str,
|
|
&tile_mode);
|
|
|
|
/* If there isn't a style sheet backdrop get one from the BODY's
|
|
BACKGROUND attribute, if present */
|
|
if (!image_url)
|
|
image_url = lo_ParseBackgroundAttribute(context, state, tag, from_user);
|
|
|
|
/*
|
|
* Get the BGCOLOR parameter. This is for a color background
|
|
* instead of an image backdrop. BACKGROUND if it exists overrides
|
|
* the BGCOLOR specification.
|
|
*
|
|
* If color_str is already set then we got the color from a stylesheet
|
|
*/
|
|
if(!color_str)
|
|
{
|
|
buff = lo_FetchParamValue(context, tag, PARAM_BGCOLOR);
|
|
if (buff) {
|
|
PA_LOCK(color_str, char *, buff);
|
|
LO_ParseRGB(color_str, &rgb.red, &rgb.green, &rgb.blue);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LO_ParseStyleSheetRGB(color_str, &rgb.red, &rgb.green, &rgb.blue);
|
|
XP_FREE(color_str);
|
|
}
|
|
|
|
layer = lo_CurrentLayer(state);
|
|
|
|
/*
|
|
* Don't do this is we had a BGCOLOR from a previous BODY tag.
|
|
*/
|
|
if (((state->top_state->body_attr & BODY_ATTR_BGCOLOR) == 0)||
|
|
(from_user != FALSE))
|
|
{
|
|
if ((color_str != NULL)&&(!lo_InsideLayer(state))&&(from_user == FALSE))
|
|
{
|
|
state->top_state->body_attr |= BODY_ATTR_BGCOLOR;
|
|
}
|
|
/* Set the document's solid background color */
|
|
if (color_str && !lo_InsideLayer(state)) {
|
|
state->top_state->doc_specified_bg = TRUE;
|
|
state->text_bg.red = rgb.red;
|
|
state->text_bg.green = rgb.green;
|
|
state->text_bg.blue = rgb.blue;
|
|
}
|
|
|
|
if (color_str)
|
|
LO_SetLayerBgColor(layer, &rgb);
|
|
else if (!lo_InsideLayer(state))
|
|
LO_SetLayerBgColor(layer, &state->text_bg);
|
|
}
|
|
|
|
/*
|
|
* Don't do this is we had a BACKGROUND from a previous BODY tag.
|
|
*/
|
|
if (((state->top_state->body_attr & BODY_ATTR_BACKGROUND) == 0)||
|
|
(from_user != FALSE))
|
|
{
|
|
if ((image_url != NULL)&&(!lo_InsideLayer(state))&&(from_user == FALSE))
|
|
{
|
|
state->top_state->body_attr |= BODY_ATTR_BACKGROUND;
|
|
}
|
|
/* Set the document's backdrop */
|
|
if (image_url) {
|
|
if (!lo_InsideLayer(state))
|
|
state->top_state->doc_specified_bg = TRUE;
|
|
LO_SetLayerBackdropURL(layer, image_url);
|
|
lo_SetLayerBackdropTileMode(layer, tile_mode);
|
|
XP_FREE(image_url);
|
|
}
|
|
}
|
|
|
|
lo_BodyForeground(context, state, tag);
|
|
}
|
|
|
|
static void lo_edt_AvoidImageBlock(LO_ImageStruct *image);
|
|
|
|
static void lo_edt_AvoidImageBlock(LO_ImageStruct *image)
|
|
{
|
|
int originalWidth = 0;
|
|
int originalHeight = 0;
|
|
IL_GetNaturalDimensions( image->image_req, &originalWidth, &originalHeight);
|
|
if ( originalWidth <= 0 ) {
|
|
originalWidth = 10;
|
|
}
|
|
if ( originalHeight <= 0 ) {
|
|
originalHeight = 10;
|
|
}
|
|
if (image->width == 0 )
|
|
{
|
|
image->width = originalWidth;
|
|
}
|
|
if (image->height == 0)
|
|
{
|
|
image->height = originalHeight;
|
|
}
|
|
}
|
|
|
|
extern Bool
|
|
lo_FindMochaExpr(char *str, char **expr_start, char **expr_end);
|
|
|
|
void
|
|
lo_BlockedImageLayout(MWContext *context, lo_DocState *state, PA_Tag *tag,
|
|
char *base_url)
|
|
{
|
|
LO_ImageStruct *image;
|
|
PA_Block buff;
|
|
char *str;
|
|
int32 val;
|
|
/* int32 doc_width; */
|
|
PushTagStatus push_status;
|
|
char *start, *end;
|
|
|
|
XP_ObserverList image_obs_list; /* List of observers for an image request. */
|
|
|
|
/* this image layout is going on outside the context
|
|
* of normal layout. We therfore need to manage the
|
|
* style stack in here as well as in normal layout
|
|
* mode so that we can set image widths
|
|
*/
|
|
push_status = LO_PushTagOnStyleStack(context, state, tag);
|
|
|
|
/*
|
|
* if there are any entities in this tag don't prefetch
|
|
* it --- wait until we have had a chance to redo it
|
|
*/
|
|
if (lo_FindMochaExpr((char *)tag->data, &start, &end))
|
|
return;
|
|
|
|
/*
|
|
* Fill in the image structure with default data
|
|
*/
|
|
image = lo_new_image_element(context, state, tag->edit_element, NULL);
|
|
if (!image)
|
|
return;
|
|
image->ele_id = NEXT_ELEMENT;
|
|
|
|
image->border_width = -1;
|
|
image->border_vert_space = IMAGE_DEF_VERTICAL_SPACE;
|
|
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
|
|
|
|
/*
|
|
* Check for the ISMAP parameter
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ISMAP);
|
|
if (buff != NULL)
|
|
{
|
|
image->image_attr->attrmask |= LO_ATTR_ISMAP;
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* Check for the USEMAP parameter. The name is stored in the
|
|
* image attributes immediately, the map pointer will be
|
|
* stored in later.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_USEMAP);
|
|
if (buff != NULL)
|
|
{
|
|
char *new_str;
|
|
char *map_name;
|
|
char *tptr;
|
|
|
|
map_name = NULL;
|
|
PA_LOCK(str, char *, buff);
|
|
|
|
/*
|
|
* Make this an absolute URL
|
|
*/
|
|
if (str != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
/*
|
|
* The IETF draft says evaluate #name relative to real,
|
|
* and not the base url.
|
|
*/
|
|
if ((str != NULL)&&(*str == '#'))
|
|
{
|
|
new_str = NET_MakeAbsoluteURL(
|
|
state->top_state->url, str);
|
|
}
|
|
else
|
|
{
|
|
new_str = NET_MakeAbsoluteURL(
|
|
base_url, str);
|
|
}
|
|
|
|
/*
|
|
* If we have a local URL, we can use the USEMAP.
|
|
*/
|
|
if ((new_str != NULL)&&
|
|
(lo_IsAnchorInDoc(state, new_str) != FALSE))
|
|
{
|
|
tptr = strrchr(new_str, '#');
|
|
if (tptr == NULL)
|
|
{
|
|
tptr = new_str;
|
|
}
|
|
else
|
|
{
|
|
tptr++;
|
|
}
|
|
map_name = XP_STRDUP(tptr);
|
|
XP_FREE(new_str);
|
|
}
|
|
/*
|
|
* Else a non-local URL, right now we don't support this.
|
|
*/
|
|
else if (new_str != NULL)
|
|
{
|
|
XP_FREE(new_str);
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
image->image_attr->usemap_name = map_name;
|
|
}
|
|
|
|
/*
|
|
* Check for an align parameter
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
|
|
if (buff != NULL)
|
|
{
|
|
Bool floating;
|
|
|
|
floating = FALSE;
|
|
PA_LOCK(str, char *, buff);
|
|
image->image_attr->alignment = lo_EvalAlignParam(str,
|
|
&floating);
|
|
if (floating != FALSE)
|
|
{
|
|
image->ele_attrmask |= LO_ELE_FLOATING;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
if(state->top_state->style_stack)
|
|
{
|
|
Bool floating = FALSE;
|
|
|
|
lo_EvalStyleSheetAlignment(
|
|
STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0),
|
|
&(image->image_attr->alignment),
|
|
&floating);
|
|
|
|
if (floating != FALSE)
|
|
{
|
|
image->ele_attrmask |= LO_ELE_FLOATING;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the required src parameter, and make the resulting
|
|
* url and absolute url.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
if (buff != NULL)
|
|
{
|
|
PA_Block new_buff;
|
|
char *new_str;
|
|
|
|
new_buff = NULL;
|
|
PA_LOCK(str, char *, buff);
|
|
if (str != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
new_str = NET_MakeAbsoluteURL(base_url, str);
|
|
if (new_str != NULL)
|
|
{
|
|
char *url;
|
|
|
|
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
|
|
if (new_buff != NULL)
|
|
{
|
|
PA_LOCK(url, char *, new_buff);
|
|
XP_STRCPY(url, new_str);
|
|
PA_UNLOCK(new_buff);
|
|
}
|
|
XP_FREE(new_str);
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
buff = new_buff;
|
|
|
|
if (new_str == NULL || new_buff == NULL)
|
|
{ /* clean up all previously allocated objects */
|
|
lo_FreeElement(context, (LO_Element *)image, TRUE);
|
|
state->top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
image->image_url = buff;
|
|
|
|
/*
|
|
* Get the optional lowsrc parameter, and make the resulting
|
|
* url and absolute url.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_LOWSRC);
|
|
if (buff != NULL)
|
|
{
|
|
PA_Block new_buff;
|
|
char *new_str;
|
|
|
|
new_buff = NULL;
|
|
PA_LOCK(str, char *, buff);
|
|
if (str != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
new_str = NET_MakeAbsoluteURL(base_url, str);
|
|
if (new_str != NULL)
|
|
{
|
|
char *url;
|
|
|
|
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
|
|
if (new_buff != NULL)
|
|
{
|
|
PA_LOCK(url, char *, new_buff);
|
|
XP_STRCPY(url, new_str);
|
|
PA_UNLOCK(new_buff);
|
|
}
|
|
XP_FREE(new_str);
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
buff = new_buff;
|
|
|
|
if (new_str == NULL || new_buff == NULL)
|
|
{ /* clean up all previously allocated objects */
|
|
lo_FreeElement(context, (LO_Element *)image, TRUE);
|
|
state->top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
image->lowres_image_url = buff;
|
|
|
|
/*
|
|
* Get the alt parameter, and store the resulting
|
|
* text, and its length.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ALT);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
image->alt_len = XP_STRLEN(str);
|
|
image->alt_len = (int16)lo_StripTextNewlines(str,
|
|
(int32)image->alt_len);
|
|
PA_UNLOCK(buff);
|
|
}
|
|
image->alt = buff;
|
|
|
|
/*
|
|
* Get the suppress feedback parameter, which suppresses the delayed icon,
|
|
* temporary border and alt text when the image is coming in, and also
|
|
* suppresses keyboard navigation feedback.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SUPPRESS);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
|
|
if (!XP_STRNCASECMP(str, "true", 4))
|
|
image->suppress_mode = LO_SUPPRESS;
|
|
else if (!XP_STRNCASECMP(str, "false", 5))
|
|
image->suppress_mode = LO_DONT_SUPPRESS;
|
|
else
|
|
image->suppress_mode = LO_SUPPRESS_UNDEFINED;
|
|
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
doc_width = state->right_margin - state->left_margin;
|
|
*/
|
|
|
|
/*
|
|
* Get the width parameter, in absolute or percentage.
|
|
* If percentage, make it absolute.
|
|
*/
|
|
/*
|
|
buff = lo_FetchParamValue(context, tag, PARAM_WIDTH);
|
|
if (buff != NULL)
|
|
{
|
|
Bool is_percent;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = lo_ValueOrPercent(str, &is_percent);
|
|
if (is_percent != FALSE)
|
|
{
|
|
if (state->allow_percent_width == FALSE)
|
|
{
|
|
val = 0;
|
|
}
|
|
else
|
|
{
|
|
val = doc_width * val / 100;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
val = FEUNITS_X(val, context);
|
|
}
|
|
image->width = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
*/
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_WIDTH);
|
|
if (buff != NULL)
|
|
{
|
|
Bool is_percent;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = lo_ValueOrPercent(str, &is_percent);
|
|
if (is_percent != FALSE)
|
|
{
|
|
image->percent_width = val;
|
|
}
|
|
else
|
|
{
|
|
image->percent_width = 0;
|
|
val = FEUNITS_X(val, context);
|
|
}
|
|
image->width = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
|
|
/* disabled because it is broken by JS threading
|
|
*
|
|
* val = LO_GetWidthFromStyleSheet(context, state);
|
|
* if(val)
|
|
* image->width = val;
|
|
*/
|
|
|
|
/*
|
|
* Get the height parameter, in absolute or percentage.
|
|
* If percentage, make it absolute.
|
|
*/
|
|
|
|
/*
|
|
buff = lo_FetchParamValue(context, tag, PARAM_HEIGHT);
|
|
if (buff != NULL)
|
|
{
|
|
Bool is_percent;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = lo_ValueOrPercent(str, &is_percent);
|
|
if (is_percent != FALSE)
|
|
{
|
|
if (state->allow_percent_height == FALSE)
|
|
{
|
|
val = 0;
|
|
}
|
|
else
|
|
{
|
|
val = state->win_height * val / 100;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
val = FEUNITS_Y(val, context);
|
|
}
|
|
image->height = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
*/
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_HEIGHT);
|
|
if (buff != NULL)
|
|
{
|
|
Bool is_percent;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = lo_ValueOrPercent(str, &is_percent);
|
|
if (is_percent != FALSE)
|
|
{
|
|
image->percent_height = val;
|
|
}
|
|
else
|
|
{
|
|
image->percent_height = 0;
|
|
val = FEUNITS_Y(val, context);
|
|
}
|
|
image->height = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/* disabled because it is broken by JS threading
|
|
*
|
|
* val = LO_GetHeightFromStyleSheet(context, state);
|
|
* if(val)
|
|
* image->height = val;
|
|
*/
|
|
|
|
/*
|
|
* Get the border parameter.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_BORDER);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
val = XP_ATOI(str);
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
image->border_width = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
image->border_width = FEUNITS_X(image->border_width, context);
|
|
|
|
/*
|
|
* Get the extra vertical space parameter.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_VSPACE);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
val = XP_ATOI(str);
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
image->border_vert_space = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
image->border_vert_space = FEUNITS_Y(image->border_vert_space, context);
|
|
|
|
/*
|
|
* Get the extra horizontal space parameter.
|
|
*/
|
|
if (image->ele_attrmask & LO_ELE_FLOATING)
|
|
{
|
|
image->border_horiz_space = IMAGE_DEF_FLOAT_HORIZONTAL_SPACE;
|
|
}
|
|
else
|
|
{
|
|
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
|
|
}
|
|
buff = lo_FetchParamValue(context, tag, PARAM_HSPACE);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
val = XP_ATOI(str);
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
image->border_horiz_space = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
image->border_horiz_space = FEUNITS_X(image->border_horiz_space,
|
|
context);
|
|
|
|
/*
|
|
* Make sure we don't load insecure images inside
|
|
* a secure document.
|
|
*/
|
|
if (state->top_state->security_level > 0)
|
|
{
|
|
if (image->image_url != NULL)
|
|
{
|
|
PA_LOCK(str, char *, image->image_url);
|
|
if (NET_IsURLSecure(str) == FALSE)
|
|
{
|
|
PA_UNLOCK(image->image_url);
|
|
PA_FREE(image->image_url);
|
|
image->image_url = PA_ALLOC(
|
|
XP_STRLEN("internal-icon-insecure") + 1);
|
|
if (image->image_url != NULL)
|
|
{
|
|
PA_LOCK(str, char *, image->image_url);
|
|
XP_STRCPY(str, "internal-icon-insecure");
|
|
PA_UNLOCK(image->image_url);
|
|
}
|
|
else
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
if (state->top_state->insecure_images == FALSE)
|
|
{
|
|
state->top_state->insecure_images = TRUE;
|
|
SECNAV_SecurityDialog(context, SD_INSECURE_DOCS_WITHIN_SECURE_DOCS_NOT_SHOWN);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PA_UNLOCK(image->image_url);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (context->compositor) {
|
|
image->layer = lo_CreateImageLayer(context, image, NULL);
|
|
}
|
|
|
|
/* Initiate the loading of this image. */
|
|
image_obs_list = lo_NewImageObserverList(context, image);
|
|
if (!image_obs_list)
|
|
return;
|
|
lo_GetImage(context, context->img_cx, image, image_obs_list,
|
|
state->top_state->force_reload);
|
|
|
|
tag->lo_data = (void *)image;
|
|
|
|
if (state->top_state->style_stack)
|
|
STYLESTACK_PopTag(state->top_state->style_stack,
|
|
(char*)pa_PrintTagToken((int32)tag->type));
|
|
|
|
}
|
|
|
|
|
|
void
|
|
lo_PartialFormatImage(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
LO_ImageStruct *image;
|
|
int32 layer_id;
|
|
#ifdef MOCHA
|
|
LM_ImageEvent mocha_event;
|
|
#endif /* MOCHA */
|
|
|
|
image = (LO_ImageStruct *)tag->lo_data;
|
|
if (image == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef MOCHA
|
|
layer_id = lo_CurrentLayerId(state);
|
|
image->layer_id = layer_id;
|
|
/* Reflect the image into Javascript */
|
|
lo_ReflectImage(context, state, tag, image, FALSE, layer_id);
|
|
|
|
/* Send any pending events to JavaScript */
|
|
if ((image->image_status == IL_IMAGE_COMPLETE) ||
|
|
(image->image_status == IL_FRAME_COMPLETE) ||
|
|
(image->image_status == IL_NOT_IN_CACHE)) {
|
|
mocha_event = LM_IMGLOAD;
|
|
ET_SendImageEvent(context, image, mocha_event);
|
|
}
|
|
else if (image->image_status == IL_ABORTED) {
|
|
mocha_event = LM_IMGABORT;
|
|
ET_SendImageEvent(context, image, mocha_event);
|
|
}
|
|
else if ((image->image_status == IL_ERROR_NO_DATA) ||
|
|
(image->image_status == IL_ERROR_IMAGE_DATA_CORRUPT) ||
|
|
(image->image_status == IL_ERROR_IMAGE_DATA_TRUNCATED) ||
|
|
(image->image_status == IL_ERROR_IMAGE_DATA_ILLEGAL) ||
|
|
(image->image_status == IL_ERROR_INTERNAL)) {
|
|
mocha_event = LM_IMGERROR;
|
|
ET_SendImageEvent(context, image, mocha_event);
|
|
}
|
|
#endif
|
|
|
|
tag->lo_data = NULL;
|
|
|
|
/*
|
|
* Assign it a properly sequencial element id.
|
|
*/
|
|
|
|
/*
|
|
image->ele_id = NEXT_ELEMENT;
|
|
image->x = state->x;
|
|
image->y = state->y;
|
|
*/
|
|
|
|
|
|
image->anchor_href = state->current_anchor;
|
|
|
|
if (image->border_width < 0)
|
|
{
|
|
if ((image->anchor_href != NULL)||
|
|
(image->image_attr->usemap_name != NULL))
|
|
{
|
|
image->border_width = IMAGE_DEF_ANCHOR_BORDER;
|
|
}
|
|
else
|
|
{
|
|
image->border_width = IMAGE_DEF_BORDER;
|
|
}
|
|
|
|
image->border_width = FEUNITS_X(image->border_width, context);
|
|
|
|
}
|
|
|
|
if (state->font_stack == NULL)
|
|
{
|
|
LO_TextAttr tmp_attr;
|
|
LO_TextAttr *tptr;
|
|
|
|
/*
|
|
* Fill in default font information.
|
|
*/
|
|
lo_SetDefaultFontAttr(state, &tmp_attr, context);
|
|
tptr = lo_FetchTextAttr(state, &tmp_attr);
|
|
image->text_attr = tptr;
|
|
}
|
|
else
|
|
{
|
|
image->text_attr = state->font_stack->text_attr;
|
|
}
|
|
|
|
if ((image->text_attr != NULL)&&
|
|
(image->text_attr->attrmask & LO_ATTR_ANCHOR))
|
|
{
|
|
image->image_attr->attrmask |= LO_ATTR_ANCHOR;
|
|
}
|
|
|
|
if (image->image_attr->usemap_name != NULL)
|
|
{
|
|
LO_TextAttr tmp_attr;
|
|
|
|
lo_CopyTextAttr(image->text_attr, &tmp_attr);
|
|
tmp_attr.fg.red = STATE_UNVISITED_ANCHOR_RED(state);
|
|
tmp_attr.fg.green = STATE_UNVISITED_ANCHOR_GREEN(state);
|
|
tmp_attr.fg.blue = STATE_UNVISITED_ANCHOR_BLUE(state);
|
|
image->text_attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
}
|
|
|
|
lo_FillInImageGeometry( state, image );
|
|
|
|
/* During relayout the editor is not allowed to block. */
|
|
if ( (image->width == 0)||(image->height == 0))
|
|
{
|
|
if ( EDT_IN_RELAYOUT( context ) ){
|
|
lo_edt_AvoidImageBlock(image);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We may have to block on this image until later
|
|
* when the front end can give us the width and height.
|
|
*/
|
|
if ((image->width == 0)||(image->height == 0))
|
|
{
|
|
state->top_state->layout_blocking_element = (LO_Element *)image;
|
|
}
|
|
else
|
|
{
|
|
lo_FinishImage(context, state, image);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************
|
|
* Function: lo_FormatImage
|
|
*
|
|
* Description: This function does all the work to format
|
|
* and lay out an image on a line of elements.
|
|
* Creates the new image structure. Fills it
|
|
* in based on the parameters in the image tag.
|
|
* Calls the front end to start fetching the image
|
|
* and get image dimentions if necessary.
|
|
* Places image on line, breaking line if necessary.
|
|
*
|
|
* Params: Window context, document state, and the image tag data.
|
|
*
|
|
* Returns: Nothing.
|
|
*************************************/
|
|
void
|
|
lo_FormatImage(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
LO_ImageStruct *image;
|
|
LO_TextAttr *tptr = NULL;
|
|
PA_Block buff;
|
|
char *str;
|
|
int32 val;
|
|
/* int32 doc_width; */
|
|
Bool is_a_form;
|
|
XP_ObserverList image_obs_list; /* List of observers for an image request. */
|
|
lo_DocLists *doc_lists;
|
|
int32 layer_id;
|
|
|
|
doc_lists = lo_GetCurrentDocLists(state);
|
|
|
|
if (state->font_stack)
|
|
tptr = state->font_stack->text_attr;
|
|
|
|
/*
|
|
* Fill in the image structure with default data
|
|
*/
|
|
image = lo_new_image_element(context, state, tag->edit_element, tptr);
|
|
if (!image)
|
|
return;
|
|
|
|
|
|
/*
|
|
image->ele_id = NEXT_ELEMENT;
|
|
image->x = state->x;
|
|
image->y = state->y;
|
|
*/
|
|
image->anchor_href = state->current_anchor;
|
|
|
|
if (image->anchor_href != NULL)
|
|
{
|
|
image->border_width = IMAGE_DEF_ANCHOR_BORDER;
|
|
}
|
|
else
|
|
{
|
|
image->border_width = IMAGE_DEF_BORDER;
|
|
}
|
|
image->border_vert_space = IMAGE_DEF_VERTICAL_SPACE;
|
|
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
|
|
|
|
if ((image->text_attr != NULL)&&
|
|
(image->text_attr->attrmask & LO_ATTR_ANCHOR))
|
|
{
|
|
image->image_attr->attrmask |= LO_ATTR_ANCHOR;
|
|
}
|
|
|
|
if (tag->type == P_INPUT)
|
|
{
|
|
is_a_form = TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_a_form = FALSE;
|
|
}
|
|
|
|
if (is_a_form != FALSE)
|
|
{
|
|
LO_TextAttr tmp_attr;
|
|
|
|
image->image_attr->layer_id = lo_CurrentLayerId(state);
|
|
image->image_attr->form_id = doc_lists->current_form_num;
|
|
image->image_attr->attrmask |= LO_ATTR_ISFORM;
|
|
image->border_width = IMAGE_DEF_ANCHOR_BORDER;
|
|
lo_CopyTextAttr(image->text_attr, &tmp_attr);
|
|
tmp_attr.fg.red = STATE_UNVISITED_ANCHOR_RED(state);
|
|
tmp_attr.fg.green = STATE_UNVISITED_ANCHOR_GREEN(state);
|
|
tmp_attr.fg.blue = STATE_UNVISITED_ANCHOR_BLUE(state);
|
|
image->text_attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
}
|
|
|
|
image->image_attr->usemap_name = NULL;
|
|
image->image_attr->usemap_ptr = NULL;
|
|
|
|
image->ele_attrmask = 0;
|
|
|
|
image->sel_start = -1;
|
|
image->sel_end = -1;
|
|
|
|
/*
|
|
* Check for the ISMAP parameter
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ISMAP);
|
|
if (buff != NULL)
|
|
{
|
|
image->image_attr->attrmask |= LO_ATTR_ISMAP;
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* Check for the USEMAP parameter. The name is stored in the
|
|
* image attributes immediately, the map pointer will be
|
|
* stored in later.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_USEMAP);
|
|
if (buff != NULL)
|
|
{
|
|
char *new_str;
|
|
char *map_name;
|
|
char *tptr;
|
|
|
|
map_name = NULL;
|
|
PA_LOCK(str, char *, buff);
|
|
|
|
/*
|
|
* Make this an absolute URL
|
|
*/
|
|
if (str != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
/*
|
|
* The IETF draft says evaluate #name relative to real,
|
|
* and not the base url.
|
|
*/
|
|
if ((str != NULL)&&(*str == '#'))
|
|
{
|
|
new_str = NET_MakeAbsoluteURL(
|
|
state->top_state->url, str);
|
|
}
|
|
else
|
|
{
|
|
new_str = NET_MakeAbsoluteURL(
|
|
state->top_state->base_url, str);
|
|
}
|
|
|
|
/*
|
|
* If we have a local URL, we can use the USEMAP.
|
|
*/
|
|
if ((new_str != NULL)&&
|
|
(lo_IsAnchorInDoc(state, new_str) != FALSE))
|
|
{
|
|
tptr = strrchr(new_str, '#');
|
|
if (tptr == NULL)
|
|
{
|
|
tptr = new_str;
|
|
}
|
|
else
|
|
{
|
|
tptr++;
|
|
}
|
|
map_name = XP_STRDUP(tptr);
|
|
XP_FREE(new_str);
|
|
}
|
|
/*
|
|
* Else a non-local URL, right now we don't support this.
|
|
*/
|
|
else if (new_str != NULL)
|
|
{
|
|
XP_FREE(new_str);
|
|
}
|
|
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
image->image_attr->usemap_name = map_name;
|
|
if (image->image_attr->usemap_name != NULL)
|
|
{
|
|
LO_TextAttr tmp_attr;
|
|
|
|
image->border_width = IMAGE_DEF_ANCHOR_BORDER;
|
|
lo_CopyTextAttr(image->text_attr, &tmp_attr);
|
|
tmp_attr.fg.red = STATE_UNVISITED_ANCHOR_RED(state);
|
|
tmp_attr.fg.green = STATE_UNVISITED_ANCHOR_GREEN(state);
|
|
tmp_attr.fg.blue = STATE_UNVISITED_ANCHOR_BLUE(state);
|
|
image->text_attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for an align parameter
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
|
|
if (buff != NULL)
|
|
{
|
|
Bool floating;
|
|
|
|
floating = FALSE;
|
|
PA_LOCK(str, char *, buff);
|
|
image->image_attr->alignment = lo_EvalAlignParam(str,
|
|
&floating);
|
|
if (floating != FALSE)
|
|
{
|
|
image->ele_attrmask |= LO_ELE_FLOATING;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
if(state->top_state->style_stack)
|
|
{
|
|
Bool floating = FALSE;
|
|
|
|
lo_EvalStyleSheetAlignment(
|
|
STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0),
|
|
&(image->image_attr->alignment),
|
|
&floating);
|
|
|
|
if (floating != FALSE)
|
|
{
|
|
image->ele_attrmask |= LO_ELE_FLOATING;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the required src parameter, and make the resulting
|
|
* url and absolute url.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
if (buff != NULL)
|
|
{
|
|
PA_Block new_buff;
|
|
char *new_str;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
if (str != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
new_str = NET_MakeAbsoluteURL(state->top_state->base_url, str);
|
|
if (new_str == NULL)
|
|
{
|
|
new_buff = NULL;
|
|
}
|
|
else
|
|
{
|
|
char *url;
|
|
|
|
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
|
|
if (new_buff != NULL)
|
|
{
|
|
PA_LOCK(url, char *, new_buff);
|
|
XP_STRCPY(url, new_str);
|
|
PA_UNLOCK(new_buff);
|
|
}
|
|
else
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
|
|
/* Complete kludge: if this image has the magic SRC,
|
|
* then also parse an HREF from it (the IMG tag doesn't
|
|
* normally have an HREF parameter.)
|
|
*
|
|
* The front ends know that when the user clicks on an
|
|
* image with the magic SRC, that they may find the
|
|
* "real" URL in the HREF... Gag me.
|
|
*/
|
|
if (!XP_STRCMP(new_str, "internal-external-reconnect"))
|
|
{
|
|
PA_Block hbuf;
|
|
|
|
hbuf = lo_FetchParamValue(context, tag, PARAM_HREF);
|
|
if (hbuf != NULL)
|
|
{
|
|
image->anchor_href =
|
|
lo_NewAnchor(state, hbuf, NULL);
|
|
}
|
|
}
|
|
|
|
XP_FREE(new_str);
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
buff = new_buff;
|
|
}
|
|
image->image_url = buff;
|
|
|
|
/*
|
|
* Get the optional lowsrc parameter, and make the resulting
|
|
* url and absolute url.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_LOWSRC);
|
|
if (buff != NULL)
|
|
{
|
|
PA_Block new_buff;
|
|
char *new_str;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
if (str != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
new_str = NET_MakeAbsoluteURL(state->top_state->base_url, str);
|
|
if (new_str == NULL)
|
|
{
|
|
new_buff = NULL;
|
|
}
|
|
else
|
|
{
|
|
char *url;
|
|
|
|
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
|
|
if (new_buff != NULL)
|
|
{
|
|
PA_LOCK(url, char *, new_buff);
|
|
XP_STRCPY(url, new_str);
|
|
PA_UNLOCK(new_buff);
|
|
}
|
|
else
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
XP_FREE(new_str);
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
buff = new_buff;
|
|
}
|
|
image->lowres_image_url = buff;
|
|
|
|
/*
|
|
* Get the alt parameter, and store the resulting
|
|
* text, and its length. Form the special image form
|
|
* element, store the name in alt.
|
|
*/
|
|
if (is_a_form != FALSE)
|
|
{
|
|
buff = lo_FetchParamValue(context, tag, PARAM_NAME);
|
|
}
|
|
else
|
|
{
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ALT);
|
|
}
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
image->alt_len = XP_STRLEN(str);
|
|
image->alt_len = (int16)lo_StripTextNewlines(str,
|
|
(int32)image->alt_len);
|
|
PA_UNLOCK(buff);
|
|
}
|
|
image->alt = buff;
|
|
|
|
/*
|
|
doc_width = state->right_margin - state->left_margin;
|
|
*/
|
|
|
|
/*
|
|
* Get the width parameter, in absolute or percentage.
|
|
* If percentage, make it absolute.
|
|
*/
|
|
|
|
/*
|
|
buff = lo_FetchParamValue(context, tag, PARAM_WIDTH);
|
|
if (buff != NULL)
|
|
{
|
|
Bool is_percent;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = lo_ValueOrPercent(str, &is_percent);
|
|
if (is_percent != FALSE)
|
|
{
|
|
if (state->allow_percent_width == FALSE)
|
|
{
|
|
val = 0;
|
|
}
|
|
else
|
|
{
|
|
val = doc_width * val / 100;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
val = FEUNITS_X(val, context);
|
|
}
|
|
image->width = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
*/
|
|
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_WIDTH);
|
|
if (buff != NULL)
|
|
{
|
|
Bool is_percent;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = lo_ValueOrPercent(str, &is_percent);
|
|
if (is_percent != FALSE)
|
|
{
|
|
image->percent_width = val;
|
|
}
|
|
else
|
|
{
|
|
image->percent_width = 0;
|
|
val = FEUNITS_X(val, context);
|
|
}
|
|
image->width = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
|
|
/* disabled because it is broken by JS threading
|
|
*
|
|
* val = LO_GetWidthFromStyleSheet(context, state);
|
|
* if(val)
|
|
* image->width = val;
|
|
*/
|
|
|
|
/*
|
|
* Get the height parameter, in absolute or percentage.
|
|
* If percentage, make it absolute.
|
|
*/
|
|
/*
|
|
buff = lo_FetchParamValue(context, tag, PARAM_HEIGHT);
|
|
if (buff != NULL)
|
|
{
|
|
Bool is_percent;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = lo_ValueOrPercent(str, &is_percent);
|
|
if (is_percent != FALSE)
|
|
{
|
|
if (state->allow_percent_height == FALSE)
|
|
{
|
|
val = 0;
|
|
}
|
|
else
|
|
{
|
|
val = state->win_height * val / 100;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
val = FEUNITS_Y(val, context);
|
|
}
|
|
image->height = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
*/
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_HEIGHT);
|
|
if (buff != NULL)
|
|
{
|
|
Bool is_percent;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = lo_ValueOrPercent(str, &is_percent);
|
|
if (is_percent != FALSE)
|
|
{
|
|
image->percent_height = val;
|
|
}
|
|
else
|
|
{
|
|
image->percent_height = 0;
|
|
val = FEUNITS_Y(val, context);
|
|
}
|
|
image->height = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/* disabled because it is broken by JS threading
|
|
*
|
|
* val = LO_GetHeightFromStyleSheet(context, state);
|
|
* if(val)
|
|
* image->height = val;
|
|
*/
|
|
|
|
/*
|
|
* Get the border parameter.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_BORDER);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
val = XP_ATOI(str);
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
image->border_width = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
image->border_width = FEUNITS_X(image->border_width, context);
|
|
|
|
/*
|
|
* Get the extra vertical space parameter.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_VSPACE);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
val = XP_ATOI(str);
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
image->border_vert_space = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
image->border_vert_space = FEUNITS_Y(image->border_vert_space, context);
|
|
|
|
/*
|
|
* Get the extra horizontal space parameter.
|
|
*/
|
|
if (image->ele_attrmask & LO_ELE_FLOATING)
|
|
{
|
|
image->border_horiz_space = IMAGE_DEF_FLOAT_HORIZONTAL_SPACE;
|
|
}
|
|
else
|
|
{
|
|
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
|
|
}
|
|
buff = lo_FetchParamValue(context, tag, PARAM_HSPACE);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
val = XP_ATOI(str);
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
image->border_horiz_space = val;
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
image->border_horiz_space = FEUNITS_X(image->border_horiz_space,
|
|
context);
|
|
|
|
|
|
lo_FillInImageGeometry( state, image );
|
|
|
|
/*
|
|
* Make sure we don't load insecure images inside
|
|
* a secure document.
|
|
*/
|
|
if (state->top_state->security_level > 0)
|
|
{
|
|
if (image->image_url != NULL)
|
|
{
|
|
PA_LOCK(str, char *, image->image_url);
|
|
if (NET_IsURLSecure(str) == FALSE)
|
|
{
|
|
PA_UNLOCK(image->image_url);
|
|
PA_FREE(image->image_url);
|
|
image->image_url = PA_ALLOC(
|
|
XP_STRLEN("internal-icon-insecure") + 1);
|
|
if (image->image_url != NULL)
|
|
{
|
|
PA_LOCK(str, char *, image->image_url);
|
|
XP_STRCPY(str, "internal-icon-insecure");
|
|
PA_UNLOCK(image->image_url);
|
|
}
|
|
else
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
if (state->top_state->insecure_images == FALSE)
|
|
{
|
|
state->top_state->insecure_images = TRUE;
|
|
SECNAV_SecurityDialog(context, SD_INSECURE_DOCS_WITHIN_SECURE_DOCS_NOT_SHOWN);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PA_UNLOCK(image->image_url);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MOCHA
|
|
layer_id = lo_CurrentLayerId(state);
|
|
image->layer_id = layer_id;
|
|
|
|
/* Reflect the image into Javascript */
|
|
lo_ReflectImage(context, state, tag, image, FALSE, layer_id);
|
|
#endif
|
|
|
|
if (context->compositor) {
|
|
image->layer = lo_CreateImageLayer(context, image, NULL);
|
|
}
|
|
|
|
/* Initiate the loading of this image. */
|
|
image_obs_list = lo_NewImageObserverList(context, image);
|
|
if (!image_obs_list)
|
|
return;
|
|
lo_GetImage(context, context->img_cx, image, image_obs_list,
|
|
state->top_state->force_reload);
|
|
|
|
/* During relayout the editor is not allowed to block. */
|
|
if ( (image->width == 0)||(image->height == 0))
|
|
{
|
|
if ( EDT_IN_RELAYOUT( context ) ){
|
|
lo_edt_AvoidImageBlock(image);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We may have to block on this image until later
|
|
* when the front end can give us the width and height.
|
|
*/
|
|
if ((image->width == 0)||(image->height == 0))
|
|
{
|
|
state->top_state->layout_blocking_element = (LO_Element *)image;
|
|
}
|
|
else
|
|
{
|
|
lo_FinishImage(context, state, image);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
lo_FinishImage(MWContext *context, lo_DocState *state, LO_ImageStruct *image)
|
|
{
|
|
/* PA_Block buff;
|
|
char *str;
|
|
Bool line_break;
|
|
*/
|
|
int32 baseline_inc;
|
|
int32 line_inc;
|
|
/*
|
|
int32 image_height, image_width;
|
|
LO_TextStruct tmp_text;
|
|
LO_TextInfo text_info;
|
|
*/
|
|
|
|
if (context->compositor && lo_CurrentLayerState(state)) {
|
|
CL_Layer *parent, *doc_layer, *content_layer;
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
|
|
doc_layer = CL_GetCompositorRoot(context->compositor);
|
|
if (layer_state->layer == doc_layer)
|
|
parent = CL_GetLayerChildByName(doc_layer,
|
|
LO_BODY_LAYER_NAME);
|
|
else
|
|
parent = layer_state->layer;
|
|
|
|
content_layer = CL_GetLayerChildByName(parent,
|
|
LO_CONTENT_LAYER_NAME);
|
|
XP_ASSERT(content_layer);
|
|
if (content_layer)
|
|
CL_InsertChild(parent, image->layer, content_layer, CL_ABOVE);
|
|
}
|
|
|
|
/*
|
|
* All this work is to get the text_info filled in for the current
|
|
* font in the font stack. Yuck, there must be a better way.
|
|
*/
|
|
|
|
/*
|
|
memset (&tmp_text, 0, sizeof (tmp_text));
|
|
buff = PA_ALLOC(1);
|
|
if (buff == NULL)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
PA_LOCK(str, char *, buff);
|
|
str[0] = ' ';
|
|
PA_UNLOCK(buff);
|
|
tmp_text.text = buff;
|
|
tmp_text.text_len = 1;
|
|
tmp_text.text_attr =
|
|
state->font_stack->text_attr;
|
|
FE_GetTextInfo(context, &tmp_text, &text_info);
|
|
PA_FREE(buff);
|
|
*/
|
|
|
|
/*
|
|
* Eventually this will never happen since we always
|
|
* have dims here from either the image tag itself or the
|
|
* front end.
|
|
*/
|
|
|
|
/*
|
|
if (image->width == 0)
|
|
{
|
|
image->width = IMAGE_DEF_DIM;
|
|
}
|
|
if (image->height == 0)
|
|
{
|
|
image->height = IMAGE_DEF_DIM;
|
|
}
|
|
|
|
image_width = image->width + (2 * image->border_width) +
|
|
(2 * image->border_horiz_space);
|
|
image_height = image->height + (2 * image->border_width) +
|
|
(2 * image->border_vert_space);
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
* SEVERE FLOW BREAK! This may be a floating image,
|
|
* which means at this point we go do something completely
|
|
* different.
|
|
*/
|
|
if (image->ele_attrmask & LO_ELE_FLOATING)
|
|
{ /*
|
|
if (image->image_attr->alignment == LO_ALIGN_RIGHT)
|
|
{
|
|
if (state->right_margin_stack == NULL)
|
|
{
|
|
image->x = state->right_margin - image_width;
|
|
}
|
|
else
|
|
{
|
|
image->x = state->right_margin_stack->margin -
|
|
image_width;
|
|
}
|
|
if (image->x < 0)
|
|
{
|
|
image->x = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
image->x = state->left_margin;
|
|
}
|
|
|
|
image->y = -1;
|
|
image->x_offset += (int16)image->border_horiz_space;
|
|
image->y_offset += (int32)image->border_vert_space;
|
|
|
|
lo_AddMarginStack(state, image->x, image->y,
|
|
image->width, image->height,
|
|
image->border_width,
|
|
image->border_vert_space, image->border_horiz_space,
|
|
image->image_attr->alignment);
|
|
|
|
*/
|
|
|
|
/*
|
|
* Insert this element into the float list.
|
|
*/
|
|
image->next = state->float_list;
|
|
state->float_list = (LO_Element *)image;
|
|
|
|
/* Update its offsets */
|
|
image->x_offset += (int16)image->border_horiz_space;
|
|
image->y_offset += (int32)image->border_vert_space;
|
|
|
|
/*
|
|
* Append a dummy layout element in the line list. When the relayout engine
|
|
* will see this dummy element, it will call lo_LayoutFloatImage()
|
|
*/
|
|
lo_AppendFloatInLineList( state, (LO_Element *) image, NULL );
|
|
|
|
lo_LayoutFloatImage( context, state, image, TRUE);
|
|
|
|
/*
|
|
if (state->at_begin_line != FALSE)
|
|
{
|
|
lo_FindLineMargins(context, state);
|
|
state->x = state->left_margin;
|
|
}
|
|
*/
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Will this image make the line too wide.
|
|
*/
|
|
/*
|
|
if ((state->x + image_width) > state->right_margin)
|
|
{
|
|
line_break = TRUE;
|
|
}
|
|
else
|
|
{
|
|
line_break = FALSE;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* We cannot break a line if we have no break positions.
|
|
* Usually happens with a single line of unbreakable text.
|
|
*/
|
|
/*
|
|
* This doesn't work right now, I don't know why, seems we
|
|
* have lost the last old_break_pos somehow.
|
|
*/
|
|
#ifdef FIX_THIS
|
|
if ((line_break != FALSE)&&(state->break_pos == -1))
|
|
{
|
|
/*
|
|
* It may be possible to break a previous
|
|
* text element on the same line.
|
|
*/
|
|
if (state->old_break_pos != -1)
|
|
{
|
|
lo_BreakOldElement(context, state);
|
|
line_break = FALSE;
|
|
}
|
|
else
|
|
{
|
|
line_break = FALSE;
|
|
}
|
|
}
|
|
#endif /* FIX_THIS */
|
|
|
|
/*
|
|
* if we are at the beginning of the line. There is
|
|
* no point in breaking, we are just too wide.
|
|
* Also don't break in unwrapped preformatted text.
|
|
* Also can't break inside a NOBR section.
|
|
*/
|
|
/*
|
|
if ((state->at_begin_line != FALSE)||
|
|
(state->preformatted == PRE_TEXT_YES)||
|
|
(state->breakable == FALSE))
|
|
{
|
|
line_break = FALSE;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* break on the image if we have
|
|
* a break.
|
|
*/
|
|
/*
|
|
if (line_break != FALSE)
|
|
{
|
|
*/
|
|
/*
|
|
* We need to make the elements sequential, linefeed
|
|
* before image.
|
|
*/
|
|
/*
|
|
state->top_state->element_id = image->ele_id;
|
|
|
|
lo_SoftLineBreak(context, state, TRUE);
|
|
image->x = state->x;
|
|
image->y = state->y;
|
|
image->ele_id = NEXT_ELEMENT;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* Figure out how to align this image.
|
|
* baseline_inc is how much to increase the baseline
|
|
* of previous element of this line. line_inc is how
|
|
* much to increase the line height below the baseline.
|
|
*/
|
|
/*
|
|
baseline_inc = 0;
|
|
line_inc = 0;
|
|
*/
|
|
|
|
/*
|
|
* If we are at the beginning of a line, with no baseline,
|
|
* we first set baseline and line_height based on the current
|
|
* font, then place the image.
|
|
*/
|
|
if (state->baseline == 0)
|
|
{
|
|
#ifdef ALIGN_IMAGE_WITH_FAKE_TEXT
|
|
state->baseline = text_info.ascent;
|
|
state->line_height = text_info.ascent + text_info.descent;
|
|
#else
|
|
state->baseline = 0;
|
|
/* Why do we need this?
|
|
if (state->line_height < 1)
|
|
{
|
|
state->line_height = 1;
|
|
}
|
|
*/
|
|
#endif /* ALIGN_IMAGE_WITH_FAKE_TEXT */
|
|
}
|
|
|
|
/*
|
|
lo_CalcAlignOffsets(state, &text_info, image->image_attr->alignment,
|
|
image_width, image_height,
|
|
&image->x_offset, &image->y_offset, &line_inc, &baseline_inc);
|
|
|
|
image->x_offset += (int16)image->border_horiz_space;
|
|
image->y_offset += (int32)image->border_vert_space;
|
|
*/
|
|
|
|
lo_LayoutInflowImage( context, state, image, FALSE, &line_inc, &baseline_inc);
|
|
|
|
lo_AppendToLineList(context, state,
|
|
(LO_Element *)image, baseline_inc);
|
|
|
|
lo_UpdateStateAfterImageLayout( state, image, line_inc, baseline_inc );
|
|
|
|
/*
|
|
state->baseline += (intn) baseline_inc;
|
|
state->line_height += (intn) (baseline_inc + line_inc);
|
|
*/
|
|
|
|
/*
|
|
* Clean up state
|
|
*/
|
|
|
|
/*
|
|
state->x = state->x + image->x_offset +
|
|
image_width - image->border_horiz_space;
|
|
state->linefeed_state = 0;
|
|
state->at_begin_line = FALSE;
|
|
state->trailing_space = FALSE;
|
|
state->cur_ele_type = LO_IMAGE;
|
|
*/
|
|
}
|
|
|
|
|
|
Bool
|
|
LO_BlockedOnImage(MWContext *context, LO_ImageStruct *image)
|
|
{
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
|
|
/*
|
|
* Get the unique document ID, and retreive this
|
|
* documents layout state.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if ((top_state == NULL)||(top_state->doc_state == NULL))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
if ((top_state->layout_blocking_element != NULL)&&
|
|
(top_state->layout_blocking_element == (LO_Element *)image))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Make sure the image library has been informed of all possible
|
|
* image URLs that might be loaded by this document.
|
|
*/
|
|
void
|
|
lo_NoMoreImages(MWContext *context)
|
|
{
|
|
PA_Tag *tag_ptr;
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
|
|
/*
|
|
* All blocked tags should be at the top level of state
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if ((top_state == NULL)||(top_state->doc_state == NULL))
|
|
{
|
|
#ifndef M12N /* XXXM12N Fix me. IL_NoMoreImages was used
|
|
by the image library to detect when it
|
|
could install a custom colormap. */
|
|
/*
|
|
* No matter what, we must be done with all images now
|
|
*/
|
|
IL_NoMoreImages(context);
|
|
#else
|
|
/* XXXM12N Need an observer callback. */
|
|
#endif /* M12N */
|
|
return;
|
|
}
|
|
state = top_state->doc_state;
|
|
tag_ptr = top_state->tags;
|
|
|
|
/*
|
|
* Go through all the remaining blocked tags looking
|
|
* for <INPUT TYPE=IMAGE> tags.
|
|
*/
|
|
while (tag_ptr != NULL)
|
|
{
|
|
if (tag_ptr->type == P_INPUT)
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
int32 type;
|
|
|
|
type = FORM_TYPE_TEXT;
|
|
buff = lo_FetchParamValue(context, tag_ptr, PARAM_TYPE);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
type = lo_ResolveInputType(str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* Prefetch this image.
|
|
* The ImageStruct created will get stuck into
|
|
* tag_ptr->lo_data, and freed later in
|
|
* lo_ProcessInputTag().
|
|
*/
|
|
if (type == FORM_TYPE_IMAGE)
|
|
{
|
|
lo_BlockedImageLayout(context, state, tag_ptr,
|
|
top_state->base_url);
|
|
}
|
|
}
|
|
tag_ptr = tag_ptr->next;
|
|
}
|
|
|
|
#ifndef M12N /* XXXM12N Fix me. IL_NoMoreImages was used
|
|
by the image library to detect when it
|
|
could install a custom colormap. */
|
|
/*
|
|
* No matter what, we must be done with all images now
|
|
*/
|
|
IL_NoMoreImages(context);
|
|
#else
|
|
/* XXXM12N Need an observer callback. */
|
|
#endif /* M12N */
|
|
}
|
|
|
|
#ifdef EDITOR
|
|
LO_ImageStruct* LO_NewImageElement( MWContext* context ){
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
LO_ImageStruct *image;
|
|
|
|
/*
|
|
* Get the unique document ID, and retreive this
|
|
* documents layout state.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if ((top_state == NULL)||(top_state->doc_state == NULL))
|
|
{
|
|
return 0;
|
|
}
|
|
state = top_state->doc_state;
|
|
|
|
state->edit_current_element = 0;
|
|
state->edit_current_offset = 0;
|
|
|
|
/*
|
|
* Fill in the image structure with default data
|
|
*/
|
|
image = lo_new_image_element(context, state, NULL, NULL);
|
|
if (!image)
|
|
return NULL;
|
|
|
|
image->border_width = -1;
|
|
image->border_vert_space = IMAGE_DEF_VERTICAL_SPACE;
|
|
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
|
|
|
|
return image;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Create a new observer list which will be passed into to IL_GetImage (via
|
|
lo_GetImage) in order to permit an Image Library request to be
|
|
monitored. The layout image observer is added to this new observer list
|
|
before the function returns. */
|
|
XP_ObserverList
|
|
lo_NewImageObserverList(MWContext *context, LO_ImageStruct *lo_image)
|
|
{
|
|
int32 doc_id;lo_TopState *top_state;
|
|
XP_ObserverList image_obs_list; /* List of observer callbacks. */
|
|
lo_ImageObsClosure *image_obs_closure; /* Closure data to be passed back
|
|
to lo_ImageObserver. */
|
|
NS_Error status;
|
|
|
|
/* Get the unique document ID, and retrieve this document's layout
|
|
state. */
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if (!top_state) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Create an XP_ObserverList for this image. The observable IL_ImageReq
|
|
will be set by the Image Library. */
|
|
status = XP_NewObserverList(NULL, &image_obs_list);
|
|
if (status < 0) {
|
|
if (status == MK_OUT_OF_MEMORY)
|
|
top_state->out_of_memory = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
/* Closure data for the image observer. */
|
|
image_obs_closure = XP_NEW_ZAP(lo_ImageObsClosure);
|
|
if (!image_obs_closure) {
|
|
top_state->out_of_memory = TRUE;
|
|
return NULL;
|
|
}
|
|
image_obs_closure->context = context;
|
|
image_obs_closure->lo_image = lo_image;
|
|
image_obs_closure->obs_list = image_obs_list;
|
|
|
|
/* Add the layout image observer to the observer list. */
|
|
status = XP_AddObserver(image_obs_list, lo_ImageObserver, image_obs_closure);
|
|
if (status < 0) {
|
|
if (status == MK_OUT_OF_MEMORY)
|
|
top_state->out_of_memory = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
return image_obs_list;
|
|
}
|
|
|
|
|
|
static void
|
|
lo_image_pixmap_update(MWContext *context, LO_ImageStruct *lo_image,
|
|
IL_Rect *update_rect)
|
|
{
|
|
#if defined(XP_WIN) || defined(XP_MAC)
|
|
IL_ImageReq *image_req = NULL; // See below at the end of the function.
|
|
#endif
|
|
|
|
/* This lo_image cannot correspond to an icon since we are receiving
|
|
pixmap update messages. */
|
|
lo_image->is_icon = FALSE;
|
|
|
|
/* Update the image layer if it is appropriate to do so. */
|
|
if (context->compositor && lo_image && lo_image->layer &&
|
|
lo_image->image_attr) {
|
|
if (lo_image->image_attr->attrmask & LO_ATTR_BACKDROP) {
|
|
/* Only update backdrops when the image has
|
|
completed decoding, not when the pixmap is updated. */
|
|
}
|
|
else {
|
|
/* Non-backdrop images only respond to pixmap update messages
|
|
so that they can load incrementally. */
|
|
XP_Rect rect, *valid_rect;
|
|
|
|
rect.left = FEUNITS_X(update_rect->x_origin, context);
|
|
rect.top = FEUNITS_Y(update_rect->y_origin, context);
|
|
rect.bottom = rect.top + FEUNITS_Y(update_rect->height, context);
|
|
rect.right = rect.left + FEUNITS_X(update_rect->width, context);
|
|
|
|
valid_rect = &lo_image->valid_rect;
|
|
XP_RectsBbox(&rect, valid_rect, valid_rect);
|
|
CL_ResizeLayer(lo_image->layer, valid_rect->right - valid_rect->left,
|
|
valid_rect->bottom - valid_rect->top);
|
|
CL_UpdateLayerRect(context->compositor, lo_image->layer, &rect,
|
|
PR_TRUE); /* XXX FALSE on the final update? */
|
|
}
|
|
}
|
|
|
|
#if defined(XP_WIN) || defined(XP_MAC)
|
|
|
|
// This is a hack in the update event to allow RDF images to get updates
|
|
// even though they have no compositors in their contexts.
|
|
if (context->type == MWContextIcon)
|
|
{
|
|
image_req = lo_image->image_req;
|
|
if (image_req)
|
|
IL_DisplaySubImage(image_req, 0, 0, 0, 0, 1, 1); // Forces a DisplayPixmap update.
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
lo_frame_complete(MWContext *context, LO_ImageStruct *lo_image)
|
|
{
|
|
PRBool is_anim, is_backdrop, is_cell_backdrop;
|
|
|
|
XP_ASSERT(lo_image && lo_image->image_attr);
|
|
if (!lo_image || !lo_image->image_attr) /* Paranoia */
|
|
return;
|
|
|
|
/* If we've already received a frame-complete message, then this
|
|
must be an animated image. */
|
|
is_anim = (PRBool)(lo_image->image_status == IL_FRAME_COMPLETE);
|
|
is_backdrop = (PRBool)(lo_image->image_attr->attrmask & LO_ATTR_BACKDROP);
|
|
is_cell_backdrop = (PRBool)(lo_image->image_attr->attrmask & LO_ATTR_CELL_BACKDROP);
|
|
|
|
if (!is_cell_backdrop)
|
|
ET_SendImageEvent(context, lo_image, LM_IMGLOAD);
|
|
|
|
if (!lo_image->layer)
|
|
return;
|
|
|
|
if (is_backdrop) {
|
|
/* Only update cell/layer/doc backdrops when the image has completed
|
|
loading, not when the pixmap is updated. */
|
|
XP_Rect rect = CL_MAX_RECT;
|
|
CL_UpdateLayerRect(context->compositor, lo_image->layer, &rect, PR_FALSE);
|
|
|
|
/* Unlike ordinary images, backdrop images are assumed to be transparent
|
|
until they are displayed. (Ordinary images are assumed to be opaque
|
|
until they are determined to be transparent.) */
|
|
CL_ChangeLayerFlag(lo_image->layer, CL_OPAQUE, (PRBool)!lo_image->is_transparent);
|
|
|
|
/* If this animated GIF serves as a backdrop image, force
|
|
offscreen compositing in order to reduce flicker. */
|
|
if (is_anim)
|
|
CL_ChangeLayerFlag(lo_image->layer, CL_PREFER_DRAW_OFFSCREEN, PR_TRUE);
|
|
}
|
|
else {
|
|
/* We may need to reset the layer transparency if the current image
|
|
is opaque and it is replacing a previous image. */
|
|
if (!lo_image->is_transparent) {
|
|
CL_ChangeLayerFlag(lo_image->layer, CL_OPAQUE, PR_TRUE);
|
|
CL_ChangeLayerFlag(lo_image->layer, CL_PREFER_DRAW_OFFSCREEN,
|
|
PR_FALSE);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
lo_image_dimensions(MWContext *context, LO_ImageStruct *lo_image,
|
|
int width, int height)
|
|
{
|
|
if(!lo_image) {
|
|
return;
|
|
}
|
|
|
|
width = FEUNITS_X(width, context);
|
|
height = FEUNITS_Y(height, context);
|
|
lo_image->width = width;
|
|
lo_image->height = height;
|
|
|
|
/* Don't call LO_SetImageInfo() for backdrop images. */
|
|
if (lo_image->image_attr &&
|
|
!(lo_image->image_attr->attrmask & LO_ATTR_BACKDROP))
|
|
{
|
|
LO_SetImageInfo (context, lo_image->ele_id, width, height);
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_icon_box( MWContext *context, LO_ImageStruct *lo_image, int icon_width, int icon_height )
|
|
{
|
|
LO_TextStruct text;
|
|
LO_TextAttr text_attr;
|
|
LO_TextInfo text_info;
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
char *image_url;
|
|
|
|
/* ptn: The code that determines the area needed for the alt text
|
|
will be put in a separate function so that it is the same
|
|
code called for the display alttext as well as the area estimation.
|
|
This code was for an immediate bug fix with time limits */
|
|
|
|
PA_LOCK(image_url, char *, lo_image->image_url);
|
|
/* Determine whether the icon was explicitly requested by URL
|
|
(in which case only the icon is drawn) or whether it needs
|
|
to be drawn as a placeholder (along with the ALT text and
|
|
placeholder border) as a result of a failed image request. */
|
|
if (image_url &&
|
|
(*image_url == 'i' ||
|
|
!XP_STRNCMP(image_url, "/mc-", 4) ||
|
|
!XP_STRNCMP(image_url, "/ns-", 4))) {
|
|
lo_image->width = FEUNITS_X(icon_width, context);
|
|
lo_image->height = FEUNITS_Y(icon_height, context);
|
|
}
|
|
else {
|
|
lo_image->width = FEUNITS_X(icon_width + (2*ICON_X_OFFSET),
|
|
context);
|
|
lo_image->height = FEUNITS_Y(icon_height + (2*ICON_Y_OFFSET),
|
|
context);
|
|
|
|
if(lo_image->alt){
|
|
|
|
/* Get the unique doc ID, and retrieve this doc's layout state.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if (!top_state) {
|
|
return;
|
|
}
|
|
state = lo_CurrentSubState(top_state->doc_state);
|
|
memset (&text, 0, sizeof (text));
|
|
text.text_attr = &text_attr;
|
|
lo_SetDefaultFontAttr(state, text.text_attr, context);
|
|
text.text = lo_image->alt;
|
|
text.text_len = lo_image->alt_len;
|
|
|
|
FE_GetTextInfo(context, &text, &text_info);
|
|
|
|
text.width = text_info.max_width;
|
|
text.height = text_info.ascent + text_info.descent;
|
|
if( text.width && text.height ){
|
|
lo_image->width = FEUNITS_X(icon_width + (4*ICON_X_OFFSET), context) + text.width;
|
|
lo_image->height= (icon_height > text.height) ?
|
|
FEUNITS_Y(icon_height + (2*ICON_Y_OFFSET), context) : text.height + FEUNITS_Y(2*ICON_Y_OFFSET, context);
|
|
}
|
|
}
|
|
}
|
|
PA_UNLOCK(lo_image->image_url);
|
|
}
|
|
|
|
static void
|
|
lo_internal_image(MWContext *context, LO_ImageStruct *lo_image, int icon_number,
|
|
int icon_width, int icon_height)
|
|
{
|
|
int bw = lo_image->border_width;
|
|
|
|
|
|
/* Don't draw icons for mocha images. */
|
|
if (lo_image->image_attr->attrmask & LO_ATTR_MOCHA_IMAGE)
|
|
return;
|
|
|
|
/* Mark this lo_image as an icon, so that drawing calls will use
|
|
IL_DisplayIcon instead of IL_DisplaySubImage. */
|
|
lo_image->is_icon = TRUE;
|
|
lo_image->icon_number = icon_number;
|
|
|
|
/* Set the lo_image->width and lo_image->height so that
|
|
icons and alt text are displayed, if present */
|
|
|
|
if(!lo_image->width || !lo_image->height )
|
|
lo_icon_box( context, lo_image, icon_width, icon_height );
|
|
|
|
/* Tell layout the dimensions of the image. */
|
|
LO_SetImageInfo (context, lo_image->ele_id, lo_image->width, lo_image->height);
|
|
|
|
/* Refresh the area occupied by the image. */
|
|
if (context->compositor && lo_image->layer) {
|
|
XP_Rect update_rect, *valid_rect;
|
|
|
|
update_rect.left = 0;
|
|
update_rect.top = 0;
|
|
update_rect.right = lo_image->width;
|
|
update_rect.bottom = lo_image->height;
|
|
|
|
valid_rect = &lo_image->valid_rect;
|
|
XP_RectsBbox(&update_rect, valid_rect, valid_rect);
|
|
CL_SetLayerBbox(lo_image->layer, valid_rect);
|
|
CL_ChangeLayerFlag(lo_image->layer, CL_OPAQUE, PR_FALSE);
|
|
|
|
CL_UpdateLayerRect(context->compositor, lo_image->layer, &update_rect,
|
|
PR_FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lo_image_incomplete(MWContext *context, LO_ImageStruct *lo_image,
|
|
XP_ObservableMsg message, int icon_number,
|
|
int icon_width, int icon_height, LM_ImageEvent mocha_event)
|
|
{
|
|
|
|
if (lo_image->lowres_image_req) {
|
|
/* If we already have a lowres image, then use that instead. */
|
|
lo_image->image_req = lo_image->lowres_image_req;
|
|
}
|
|
else {
|
|
lo_image->image_status = (uint16)message;
|
|
|
|
if (lo_image->width && lo_image->height) {
|
|
int valid_width, valid_height;
|
|
XP_Rect *valid_rect = &lo_image->valid_rect;
|
|
|
|
valid_width = valid_rect->right - valid_rect->left;
|
|
valid_height = valid_rect->bottom - valid_rect->top;
|
|
if (!valid_height || !valid_width)
|
|
lo_internal_image(context, lo_image, icon_number,
|
|
icon_width, icon_height);
|
|
}
|
|
else {
|
|
lo_internal_image(context, lo_image, icon_number,
|
|
icon_width, icon_height);
|
|
}
|
|
|
|
if (lo_image->image_attr &&
|
|
!(lo_image->image_attr->attrmask & LO_ATTR_CELL_BACKDROP))
|
|
ET_SendImageEvent(context, lo_image, mocha_event);
|
|
}
|
|
}
|
|
|
|
|
|
/* Image observer callback. */
|
|
void
|
|
lo_ImageObserver(XP_Observable observable, XP_ObservableMsg message,
|
|
void *message_data, void *closure)
|
|
{
|
|
IL_MessageData *data = (IL_MessageData*)message_data;
|
|
lo_ImageObsClosure *image_obs_closure = (lo_ImageObsClosure *)closure;
|
|
MWContext *context;
|
|
LO_ImageStruct *lo_image;
|
|
#ifdef MOCHA
|
|
LM_ImageEvent mocha_event;
|
|
#endif /* MOCHA */
|
|
|
|
if (image_obs_closure) {
|
|
context = image_obs_closure->context;
|
|
lo_image = image_obs_closure->lo_image;
|
|
}
|
|
|
|
/* The lo_image may not have a valid image handle at this point, or
|
|
the image's URL property may have been changed in JavaScript,
|
|
so give the lo_image the image handle passed in with the message
|
|
data. */
|
|
if(lo_image) {
|
|
lo_image->image_req = data->image_instance;
|
|
}
|
|
|
|
switch(message) {
|
|
case IL_DIMENSIONS:
|
|
lo_image_dimensions(context, lo_image, data->width, data->height);
|
|
break;
|
|
|
|
case IL_IS_TRANSPARENT:
|
|
lo_image->is_transparent = TRUE;
|
|
if (context->compositor && lo_image->layer) {
|
|
CL_ChangeLayerFlag(lo_image->layer, CL_OPAQUE, PR_FALSE);
|
|
CL_ChangeLayerFlag(lo_image->layer, CL_PREFER_DRAW_OFFSCREEN,
|
|
PR_TRUE);
|
|
}
|
|
break;
|
|
|
|
case IL_DESCRIPTION:
|
|
/* This must be a call to the stand alone image viewer, so
|
|
the document title is set in the Title observer in
|
|
libimg/src/dummy_nc.c.*/
|
|
break;
|
|
|
|
case IL_PIXMAP_UPDATE:
|
|
lo_image_pixmap_update(context, lo_image, &data->update_rect);
|
|
break;
|
|
|
|
case IL_IMAGE_COMPLETE:
|
|
lo_image->image_status = (uint16)message;
|
|
if( (lo_image->lowres_image_url) != NULL ){
|
|
if(!lo_image->lowres_image_req){
|
|
XP_ObserverList tmp_obs_list;
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
|
|
/* Get the unique document ID, and retrieve this document's layout
|
|
state.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if (!top_state) {
|
|
return;
|
|
}
|
|
|
|
/* Hold on to lowres image handle in case the highres image
|
|
fails. */
|
|
lo_image->lowres_image_req = lo_image->image_req;
|
|
|
|
tmp_obs_list = lo_NewImageObserverList(context, lo_image);
|
|
/* Lowres image request is overwritten. Destruction of
|
|
lowres image occurs when document is destroyed. */
|
|
lo_GetImage(context, context->img_cx, lo_image, tmp_obs_list,
|
|
top_state->force_reload);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IL_FRAME_COMPLETE:
|
|
lo_frame_complete(context, lo_image);
|
|
lo_image->image_status = (uint16)message;
|
|
break;
|
|
|
|
case IL_NOT_IN_CACHE:
|
|
if (lo_image->lowres_image_req) {
|
|
/* If we already have a lowres image, then use that instead. */
|
|
lo_image->image_req = lo_image->lowres_image_req;
|
|
}
|
|
else {
|
|
lo_image->image_status = (uint16)message;
|
|
lo_internal_image(context, lo_image, data->icon_number,
|
|
data->icon_width, data->icon_height);
|
|
/* As far as JavaScript events go, we treat an image that wasn't in
|
|
the cache the same as if it was actually loaded. That way, a
|
|
Javascript program will run the same way with images turned on
|
|
or off. */
|
|
mocha_event = LM_IMGLOAD;
|
|
if (lo_image->image_attr &&
|
|
!(lo_image->image_attr->attrmask & LO_ATTR_CELL_BACKDROP))
|
|
ET_SendImageEvent(context, lo_image, mocha_event);
|
|
}
|
|
break;
|
|
|
|
case IL_ABORTED:
|
|
lo_image_incomplete(context, lo_image, message, data->icon_number,
|
|
data->icon_width, data->icon_height, LM_IMGABORT);
|
|
break;
|
|
|
|
case IL_ERROR_NO_DATA:
|
|
case IL_ERROR_IMAGE_DATA_CORRUPT:
|
|
case IL_ERROR_IMAGE_DATA_TRUNCATED:
|
|
case IL_ERROR_IMAGE_DATA_ILLEGAL:
|
|
case IL_ERROR_INTERNAL:
|
|
lo_image_incomplete(context, lo_image, message, data->icon_number,
|
|
data->icon_width, data->icon_height, LM_IMGERROR);
|
|
break;
|
|
|
|
case IL_INTERNAL_IMAGE:
|
|
lo_internal_image(context, lo_image, data->icon_number, data->icon_width,
|
|
data->icon_height);
|
|
break;
|
|
|
|
case IL_IMAGE_DESTROYED:
|
|
/* Remove ourself from the observer callback list. */
|
|
XP_RemoveObserver(image_obs_closure->obs_list, lo_ImageObserver,
|
|
image_obs_closure);
|
|
XP_FREE(image_obs_closure);
|
|
lo_image->image_req = NULL;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We want to write a title in the titlebar and tell all other
|
|
functions not to write over it. This is only used by il_load_image
|
|
for view streams in libimg/dummy_nc.c & external.c. */
|
|
void
|
|
lo_view_title( MWContext *context, char *title_str ){
|
|
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
|
|
/* Get the unique document ID and the layout state.*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
if (!top_state)
|
|
return;
|
|
|
|
FE_SetDocTitle(context, title_str);
|
|
top_state->have_title = TRUE;
|
|
return;
|
|
}
|
|
|
|
#define FORCE_LOAD_ALL_IMAGES ((char *)1)
|
|
|
|
static char *force_load_images = NULL;
|
|
static XP_Bool autoload_images = TRUE;
|
|
static XP_Bool pref_initialized = FALSE;
|
|
|
|
/* remove Mac warning about missing prototype */
|
|
MODULE_PRIVATE int PR_CALLBACK lo_AutoloadPrefChangedFunc(const char *pref,
|
|
void *data);
|
|
|
|
MODULE_PRIVATE int PR_CALLBACK lo_AutoloadPrefChangedFunc(const char *pref,
|
|
void *data)
|
|
{
|
|
int status;
|
|
|
|
if (!XP_STRCASECMP(pref,"general.always_load_images"))
|
|
status = PREF_GetBoolPref("general.always_load_images",
|
|
&autoload_images);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Inform layout of an image URL to be force loaded. If all_images is TRUE,
|
|
this indicates that all images are to be force loaded. Note that this
|
|
function does not actually trigger the loading of images. */
|
|
void LO_SetForceLoadImage(char *url, XP_Bool all_images)
|
|
{
|
|
if (force_load_images &&
|
|
force_load_images != FORCE_LOAD_ALL_IMAGES) {
|
|
XP_FREE(force_load_images);
|
|
}
|
|
|
|
if (all_images) {
|
|
force_load_images = FORCE_LOAD_ALL_IMAGES;
|
|
}
|
|
else {
|
|
if (url)
|
|
force_load_images = XP_STRDUP(url);
|
|
else
|
|
force_load_images = NULL;
|
|
}
|
|
}
|
|
|
|
/* Initiate the loading of an image. */
|
|
void lo_GetImage(MWContext *context, IL_GroupContext *img_cx,
|
|
LO_ImageStruct *lo_image, XP_ObserverList obs_list,
|
|
NET_ReloadMethod requested_reload_method)
|
|
{
|
|
|
|
XP_Bool net_request_allowed; /* Are we willing to make a net request? */
|
|
NET_ReloadMethod reload_method;
|
|
IL_NetContext *net_cx = NULL;
|
|
IL_IRGB *trans_pixel;
|
|
char *image_url, *lowres_image_url, *url_to_fetch;
|
|
IL_ImageReq *dummy_ireq;
|
|
|
|
/* Safety checks. */
|
|
if (!context || !lo_image)
|
|
return;
|
|
|
|
/* Handle the TextFE. */
|
|
if (context->type == MWContextText) {
|
|
XL_GetTextImage(lo_image);
|
|
return;
|
|
}
|
|
|
|
/* More safety checks. */
|
|
if (!img_cx || !obs_list)
|
|
return;
|
|
|
|
/* Initialize the autoload images pref, if necessary. */
|
|
if (!pref_initialized) {
|
|
int status = PREF_GetBoolPref("general.always_load_images",
|
|
&autoload_images);
|
|
if (status == PREF_NOERROR) {
|
|
PREF_RegisterCallback("general.always_load_images",
|
|
lo_AutoloadPrefChangedFunc, NULL);
|
|
pref_initialized = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Fetch the lowres image first if there is one. Layout will call us
|
|
again to fetch the hires image, at which point the lowres_image_url
|
|
will be NULL. */
|
|
if (lo_image->lowres_image_url) {
|
|
PA_LOCK(lowres_image_url, char *, lo_image->lowres_image_url);
|
|
}
|
|
PA_LOCK(image_url, char *, lo_image->image_url);
|
|
if ((context->type == MWContextPostScript) ||
|
|
(context->type == MWContextPrint)) {
|
|
url_to_fetch = image_url;
|
|
}
|
|
else {
|
|
if ((lo_image->lowres_image_url)&&(!lo_image->lowres_image_req))
|
|
url_to_fetch = lowres_image_url;
|
|
else
|
|
url_to_fetch = image_url;
|
|
}
|
|
|
|
/* Determine whether we are willing to load the image from the Network. */
|
|
if ((context->type == MWContextPostScript) ||
|
|
(context->type == MWContextPrint) ||
|
|
(context->type == MWContextDialog) ||
|
|
autoload_images ||
|
|
(force_load_images == FORCE_LOAD_ALL_IMAGES) ||
|
|
(force_load_images && image_url && !XP_STRCMP(force_load_images, image_url))) {
|
|
net_request_allowed = TRUE;
|
|
}
|
|
else {
|
|
net_request_allowed = FALSE;
|
|
}
|
|
|
|
/* JavaScript-generated images can change if the document is reloaded,
|
|
so don't request a cache-only reload, even if layout requested one. */
|
|
if (NET_URL_Type(url_to_fetch) == MOCHA_TYPE_URL)
|
|
requested_reload_method = NET_DONT_RELOAD;
|
|
|
|
/* Create a dummy Net Context for the Image Library to use for network
|
|
operations. This will be replaced by a true Net Context when the
|
|
Network Library is modularized. */
|
|
|
|
if(context->type != MWContextPrint){
|
|
reload_method = net_request_allowed ? requested_reload_method : NET_CACHE_ONLY_RELOAD;
|
|
}else{
|
|
reload_method = NET_NORMAL_RELOAD;
|
|
}
|
|
net_cx = IL_NewDummyNetContext(context, reload_method);
|
|
|
|
/* Determine whether to request a mask if this is a transparent image.
|
|
In the case of a document backdrop, we ask the image library to fill
|
|
in the transparent area with a solid color. For all other transparent
|
|
images, we force the creation of a mask by passing in NULL. */
|
|
if (context->type == MWContextPostScript) {
|
|
trans_pixel = XP_NEW_ZAP(IL_IRGB);
|
|
if (trans_pixel)
|
|
trans_pixel->red = trans_pixel->green = trans_pixel->blue = 0xff;
|
|
}
|
|
#ifndef XP_WIN32
|
|
else if (context->type == MWContextPrint) {
|
|
trans_pixel = context->transparent_pixel;
|
|
}
|
|
#endif
|
|
else {
|
|
if (lo_image->image_attr &&
|
|
(lo_image->image_attr->attrmask & LO_ATTR_BACKDROP) &&
|
|
!(lo_image->image_attr->attrmask & LO_ATTR_CELL_BACKDROP) &&
|
|
!(lo_image->image_attr->attrmask & LO_ATTR_LAYER_BACKDROP)
|
|
) {
|
|
trans_pixel = context->transparent_pixel;
|
|
}
|
|
else {
|
|
#ifdef XP_WIN32
|
|
XP_Bool backgroundPref = FALSE;
|
|
PREF_GetBoolPref("browser.print_background",&backgroundPref);
|
|
if (context->type == MWContextPrint && !backgroundPref)
|
|
trans_pixel = context->transparent_pixel;
|
|
else
|
|
#endif
|
|
trans_pixel = NULL;
|
|
}
|
|
}
|
|
|
|
/* Fetch the image. We ignore the return value and only set the lo_image's
|
|
image handle in the observer. Any context-specific scaling of images,
|
|
e.g. for printing, should be handled by the Front End, so we divide the
|
|
image dimensions by the context scaling factors. */
|
|
dummy_ireq = IL_GetImage(url_to_fetch, img_cx, obs_list,
|
|
trans_pixel,
|
|
lo_image->width / context->convertPixX,
|
|
lo_image->height / context->convertPixY,
|
|
0, net_cx);
|
|
|
|
if(( dummy_ireq != lo_image->lowres_image_req ) && url_to_fetch )
|
|
lo_image->image_req = dummy_ireq;
|
|
|
|
/* Destroy the transparent pixel if this is a PostScript context. */
|
|
if ((context->type == MWContextPostScript) && trans_pixel)
|
|
XP_FREE(trans_pixel);
|
|
|
|
/* The Image Library clones the dummy Net Context, so it safe to destroy
|
|
it. */
|
|
IL_DestroyDummyNetContext(net_cx);
|
|
|
|
if (lo_image->lowres_image_url)
|
|
PA_UNLOCK(lo_image->lowres_image_url);
|
|
PA_UNLOCK(lo_image->image_url);
|
|
}
|
|
|
|
/*
|
|
* Fills in x, y, % width, % height of image
|
|
* Does not change anything for absolute width/height images
|
|
*/
|
|
|
|
void lo_FillInImageGeometry( lo_DocState *state, LO_ImageStruct *image )
|
|
{
|
|
int32 doc_width;
|
|
|
|
image->ele_id = NEXT_ELEMENT;
|
|
image->x = state->x;
|
|
image->y = state->y;
|
|
|
|
/* Set image->width if image has a % width specified */
|
|
doc_width = state->right_margin - state->left_margin;
|
|
if (image->percent_width > 0) {
|
|
int32 val = image->percent_width;
|
|
if (state->allow_percent_width == FALSE) {
|
|
val = 0;
|
|
}
|
|
else {
|
|
val = doc_width * val / 100;
|
|
}
|
|
image->width = val;
|
|
}
|
|
|
|
/* Set image->height if image has a % height specified */
|
|
if (image->percent_height > 0) {
|
|
int32 val = image->percent_height;
|
|
if (state->allow_percent_height == FALSE) {
|
|
val = 0;
|
|
}
|
|
else {
|
|
val = state->win_height * val / 100;
|
|
}
|
|
image->height = val;
|
|
}
|
|
}
|
|
|
|
|
|
/* Look into possibility of reusing for other floating elements? */
|
|
void lo_LayoutFloatImage( MWContext *context, lo_DocState *state, LO_ImageStruct *image, Bool updateFE )
|
|
{
|
|
int32 image_width;
|
|
|
|
image_width = image->width + (2 * image->border_width) +
|
|
(2 * image->border_horiz_space);
|
|
|
|
|
|
if (image->image_attr->alignment == LO_ALIGN_RIGHT)
|
|
{
|
|
if (state->right_margin_stack == NULL)
|
|
{
|
|
image->x = state->right_margin - image_width;
|
|
}
|
|
else
|
|
{
|
|
image->x = state->right_margin_stack->margin -
|
|
image_width;
|
|
}
|
|
if (image->x < 0)
|
|
{
|
|
image->x = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
image->x = state->left_margin;
|
|
}
|
|
|
|
image->y = -1;
|
|
/*
|
|
image->x_offset += (int16)image->border_horiz_space;
|
|
image->y_offset += (int32)image->border_vert_space;
|
|
*/
|
|
lo_AddMarginStack(state, image->x, image->y,
|
|
image->width, image->height,
|
|
image->border_width,
|
|
image->border_vert_space, image->border_horiz_space,
|
|
image->image_attr->alignment);
|
|
|
|
if (state->at_begin_line != FALSE)
|
|
{
|
|
lo_FindLineMargins(context, state, updateFE);
|
|
state->x = state->left_margin;
|
|
}
|
|
|
|
}
|
|
|
|
void lo_LayoutInflowImage(MWContext *context, lo_DocState *state, LO_ImageStruct *image,
|
|
Bool inRelayout, int32 *line_inc, int32 *baseline_inc)
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
LO_TextStruct tmp_text;
|
|
LO_TextInfo text_info;
|
|
int32 image_height, image_width;
|
|
Bool line_break;
|
|
|
|
/*
|
|
int32 baseline_inc;
|
|
int32 line_inc;
|
|
*/
|
|
|
|
/*
|
|
* Eventually this will never happen since we always
|
|
* have dims here from either the image tag itself or the
|
|
* front end.
|
|
*/
|
|
if (image->width == 0)
|
|
{
|
|
image->width = IMAGE_DEF_DIM;
|
|
}
|
|
if (image->height == 0)
|
|
{
|
|
image->height = IMAGE_DEF_DIM;
|
|
}
|
|
|
|
image_width = image->width + (2 * image->border_width) +
|
|
(2 * image->border_horiz_space);
|
|
image_height = image->height + (2 * image->border_width) +
|
|
(2 * image->border_vert_space);
|
|
|
|
/*
|
|
* Will this image make the line too wide.
|
|
*/
|
|
if ((state->x + image_width) > state->right_margin)
|
|
{
|
|
line_break = TRUE;
|
|
}
|
|
else
|
|
{
|
|
line_break = FALSE;
|
|
}
|
|
|
|
/*
|
|
* if we are at the beginning of the line. There is
|
|
* no point in breaking, we are just too wide.
|
|
* Also don't break in unwrapped preformatted text.
|
|
* Also can't break inside a NOBR section.
|
|
*/
|
|
if ((state->at_begin_line != FALSE)||
|
|
(state->preformatted == PRE_TEXT_YES)||
|
|
(state->breakable == FALSE))
|
|
{
|
|
line_break = FALSE;
|
|
}
|
|
|
|
/*
|
|
* break on the image if we have
|
|
* a break.
|
|
*/
|
|
if (line_break != FALSE)
|
|
{
|
|
/*
|
|
* We need to make the elements sequential, linefeed
|
|
* before image.
|
|
*/
|
|
state->top_state->element_id = image->ele_id;
|
|
|
|
if (!inRelayout)
|
|
{
|
|
lo_SoftLineBreak(context, state, TRUE);
|
|
}
|
|
else {
|
|
lo_rl_AddSoftBreakAndFlushLine(context, state);
|
|
}
|
|
image->x = state->x;
|
|
image->y = state->y;
|
|
image->ele_id = NEXT_ELEMENT;
|
|
}
|
|
|
|
/*
|
|
* Figure out how to align this image.
|
|
* baseline_inc is how much to increase the baseline
|
|
* of previous element of this line. line_inc is how
|
|
* much to increase the line height below the baseline.
|
|
*/
|
|
*baseline_inc = 0;
|
|
*line_inc = 0;
|
|
|
|
/*
|
|
* All this work is to get the text_info filled in for the current
|
|
* font in the font stack. Yuck, there must be a better way.
|
|
*/
|
|
memset (&tmp_text, 0, sizeof (tmp_text));
|
|
buff = PA_ALLOC(1);
|
|
if (buff == NULL)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
PA_LOCK(str, char *, buff);
|
|
str[0] = ' ';
|
|
PA_UNLOCK(buff);
|
|
tmp_text.text = buff;
|
|
tmp_text.text_len = 1;
|
|
tmp_text.text_attr =
|
|
state->font_stack->text_attr;
|
|
FE_GetTextInfo(context, &tmp_text, &text_info);
|
|
PA_FREE(buff);
|
|
|
|
lo_CalcAlignOffsets(state, &text_info, image->image_attr->alignment,
|
|
image_width, image_height,
|
|
&image->x_offset, &image->y_offset, line_inc, baseline_inc);
|
|
|
|
image->x_offset += (int16)image->border_horiz_space;
|
|
image->y_offset += (int32)image->border_vert_space;
|
|
}
|
|
|
|
|
|
void lo_UpdateStateAfterImageLayout( lo_DocState *state, LO_ImageStruct *image, int32 line_inc, int32 baseline_inc )
|
|
{
|
|
int32 image_width;
|
|
int32 x, y;
|
|
|
|
image_width = image->width + (2 * image->border_width) +
|
|
(2 * image->border_horiz_space);
|
|
|
|
state->baseline += (intn) baseline_inc;
|
|
state->line_height += (intn) (baseline_inc + line_inc);
|
|
|
|
/*
|
|
* Clean up state
|
|
*/
|
|
state->x = state->x + image->x_offset +
|
|
image_width - image->border_horiz_space;
|
|
state->linefeed_state = 0;
|
|
state->at_begin_line = FALSE;
|
|
state->trailing_space = FALSE;
|
|
state->cur_ele_type = LO_IMAGE;
|
|
|
|
/* Determine the new position of the layer. */
|
|
x = image->x + image->x_offset + image->border_width;
|
|
y = image->y + image->y_offset + image->border_width;
|
|
|
|
/* Move layer to new position */
|
|
if (image->layer != NULL)
|
|
CL_MoveLayer(image->layer, x, y);
|
|
}
|
|
|
|
#ifdef PROFILE
|
|
#pragma profile off
|
|
#endif
|