зеркало из https://github.com/mozilla/pjs.git
2869 строки
85 KiB
C
2869 строки
85 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_tags.h"
|
|
#include "layout.h"
|
|
#include "laylayer.h"
|
|
#include "layers.h"
|
|
#include "libmocha.h"
|
|
#include "libevent.h"
|
|
#include "prefapi.h"
|
|
|
|
#define IL_CLIENT /* XXXM12N Defined by Image Library clients */
|
|
#include "libimg.h" /* Image Library public API. */
|
|
#include "il_icons.h" /* Image icon enumeration. */
|
|
|
|
/* Z-order for internal layers within a group layer */
|
|
#define Z_CONTENT_LAYERS -1 /* HTML blocks, images, blinking text, windowless plugins */
|
|
#define Z_CELL_BACKGROUND_LAYER -1000 /* Table cell backgrounds */
|
|
#define Z_BACKGROUND_LAYER -1001 /* Layer/document backgrounds */
|
|
|
|
|
|
#ifdef PROFILE
|
|
#pragma profile on
|
|
#endif
|
|
|
|
extern Bool UserOverride;
|
|
|
|
static MWContext*
|
|
lo_get_layer_context(CL_Layer* layer)
|
|
{
|
|
lo_AnyLayerClosure *closure = (lo_AnyLayerClosure *)CL_GetLayerClientData(layer);
|
|
return closure->context;
|
|
}
|
|
|
|
static lo_TopState*
|
|
lo_get_layer_top_state(CL_Layer * layer)
|
|
{
|
|
MWContext *context = lo_get_layer_context(layer);
|
|
if (! context) /* Paranoia */
|
|
return NULL;
|
|
|
|
return lo_FetchTopState(XP_DOCID(context));
|
|
}
|
|
|
|
/**********************
|
|
*
|
|
* BLINK tag-related layering code
|
|
*
|
|
**********************/
|
|
|
|
#define LO_BLINK_RATE 750
|
|
|
|
|
|
static void
|
|
lo_blink_destroy_func(CL_Layer *layer)
|
|
{
|
|
CL_RemoveLayerFromGroup(CL_GetLayerCompositor(layer),
|
|
layer, LO_BLINK_GROUP_NAME);
|
|
XP_DELETE(CL_GetLayerClientData(layer));
|
|
}
|
|
|
|
static void
|
|
lo_blink_painter_func(CL_Drawable *drawable,
|
|
CL_Layer *layer,
|
|
FE_Region update_region)
|
|
{
|
|
lo_BlinkLayerClosure *closure;
|
|
int32 layer_x_offset, layer_y_offset;
|
|
LO_TextStruct *text;
|
|
closure = (lo_BlinkLayerClosure *)CL_GetLayerClientData(layer);
|
|
text = closure->text;
|
|
|
|
/* Temporarily change the text element's position to be layer relative */
|
|
layer_x_offset = CL_GetLayerXOffset(layer);
|
|
layer_y_offset = CL_GetLayerYOffset(layer);
|
|
|
|
text->x -= layer_x_offset;
|
|
text->y -= layer_y_offset;
|
|
|
|
FE_SetDrawable(closure->context, drawable);
|
|
|
|
/* Temporarily turn off the blink attribute so that this text
|
|
will be drawn. */
|
|
text->text_attr->attrmask &= ~LO_ATTR_BLINK;
|
|
|
|
/* Draw the text element associated with this layer */
|
|
lo_DisplayText(closure->context, text, FALSE);
|
|
|
|
/* Restore blink attribute */
|
|
text->text_attr->attrmask |= LO_ATTR_BLINK;
|
|
|
|
text->x += layer_x_offset;
|
|
text->y += layer_y_offset;
|
|
|
|
FE_SetDrawable(closure->context, NULL);
|
|
}
|
|
|
|
static void
|
|
lo_blink_callback(void *closure)
|
|
{
|
|
MWContext *context = (MWContext *)closure;
|
|
|
|
if (context->blink_hidden == FALSE) {
|
|
CL_HideLayerGroup(context->compositor, LO_BLINK_GROUP_NAME);
|
|
context->blink_hidden = TRUE;
|
|
}
|
|
else {
|
|
CL_UnhideLayerGroup(context->compositor, LO_BLINK_GROUP_NAME);
|
|
context->blink_hidden = FALSE;
|
|
}
|
|
|
|
/* Set the next timeout */
|
|
context->blink_timeout = FE_SetTimeout(lo_blink_callback,
|
|
(void *)context,
|
|
LO_BLINK_RATE);
|
|
}
|
|
|
|
void
|
|
lo_CreateBlinkLayer (MWContext *context, LO_TextStruct *text,
|
|
CL_Layer *parent)
|
|
{
|
|
CL_Layer *blink_layer;
|
|
lo_BlinkLayerClosure *closure;
|
|
XP_Rect bbox;
|
|
CL_LayerVTable vtable;
|
|
int32 layer_x_offset, layer_y_offset;
|
|
|
|
/* The layer is the size of the text element */
|
|
bbox.left = 0;
|
|
bbox.top = 0;
|
|
bbox.right = text->width;
|
|
bbox.bottom = text->height;
|
|
|
|
layer_x_offset = text->x + text->x_offset;
|
|
layer_y_offset = text->y + text->y_offset;
|
|
|
|
/* Create the client_data */
|
|
closure = XP_NEW_ZAP(lo_BlinkLayerClosure);
|
|
closure->type = LO_BLINK_LAYER;
|
|
closure->context = context;
|
|
closure->text = text;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.painter_func = (CL_PainterFunc)lo_blink_painter_func;
|
|
vtable.destroy_func = (CL_DestroyFunc)lo_blink_destroy_func;
|
|
|
|
blink_layer = CL_NewLayer(NULL, layer_x_offset, layer_y_offset,
|
|
&bbox, &vtable,
|
|
CL_NO_FLAGS | CL_DONT_ENUMERATE, (void *)closure);
|
|
|
|
CL_InsertChildByZ(parent, blink_layer, Z_CONTENT_LAYERS);
|
|
CL_AddLayerToGroup(context->compositor, blink_layer, LO_BLINK_GROUP_NAME);
|
|
|
|
/* If noone has setup the callback yet, start it up */
|
|
if (context->blink_timeout == NULL) {
|
|
context->blink_hidden = FALSE;
|
|
context->blink_timeout = FE_SetTimeout(lo_blink_callback,
|
|
(void *)context,
|
|
LO_BLINK_RATE);
|
|
}
|
|
}
|
|
|
|
/* Update the bounding box of a blink layer to its current text element position. */
|
|
static PRBool
|
|
lo_blink_update_func(CL_Layer *blink_layer, void *dummy)
|
|
{
|
|
lo_BlinkLayerClosure *closure;
|
|
LO_TextStruct *text;
|
|
|
|
closure = ((lo_BlinkLayerClosure *)CL_GetLayerClientData(blink_layer));
|
|
text = closure->text;
|
|
|
|
CL_MoveLayer(blink_layer, text->x + text->x_offset, text->y + text->y_offset);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
void
|
|
lo_UpdateBlinkLayers(MWContext *context)
|
|
{
|
|
CL_ForEachLayerInGroup(context->compositor, LO_BLINK_GROUP_NAME,
|
|
lo_blink_update_func, NULL);
|
|
}
|
|
|
|
void
|
|
lo_DestroyBlinkers(MWContext *context)
|
|
{
|
|
if (context->blink_timeout) {
|
|
FE_ClearTimeout(context->blink_timeout);
|
|
context->blink_timeout = NULL;
|
|
}
|
|
}
|
|
|
|
/**********************
|
|
*
|
|
* HTML and background layer code
|
|
*
|
|
**********************/
|
|
|
|
static void
|
|
lo_html_destroy_func(CL_Layer *layer)
|
|
{
|
|
lo_AnyLayerClosure *closure;
|
|
lo_TopState *top_state = NULL;
|
|
|
|
closure = (lo_AnyLayerClosure *)CL_GetLayerClientData(layer);
|
|
top_state = lo_FetchTopState(XP_DOCID(closure->context));
|
|
|
|
XP_DELETE(closure);
|
|
|
|
if (top_state && top_state->doc_state &&
|
|
top_state->doc_state->selection_layer == layer)
|
|
top_state->doc_state->selection_layer = NULL;
|
|
}
|
|
|
|
static void
|
|
lo_html_block_destroy_func(CL_Layer *layer)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
lo_LayerDocState *layer_state;
|
|
JSObject *obj;
|
|
lo_TopState *top_state = NULL;
|
|
int32 layer_id;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
|
|
top_state = lo_get_layer_top_state(layer);
|
|
|
|
layer_state = closure->layer_state;
|
|
if (!layer_state) /* Paranoia */
|
|
goto done;
|
|
obj = layer_state->mocha_object;
|
|
if (obj) {
|
|
layer_state->mocha_object = NULL;
|
|
ET_DestroyLayer(closure->context, obj);
|
|
}
|
|
layer_id = layer_state->id;
|
|
|
|
if (top_state->layers[layer_id] == layer_state)
|
|
top_state->layers[layer_id] = NULL;
|
|
|
|
/*
|
|
* Don't get rid of the layer associated with this layer_state,
|
|
* since the entire layer tree will be dealt with separately.
|
|
*/
|
|
layer_state->layer = NULL;
|
|
|
|
/*
|
|
* If this layer corresponds to an inflow layer, its cell
|
|
* will be recycled with the rest of the document.
|
|
*/
|
|
if (layer_state->cell && layer_state->cell->cell_inflow_layer)
|
|
layer_state->cell = NULL;
|
|
lo_DeleteLayerState(closure->context, top_state->doc_state, layer_state);
|
|
|
|
done:
|
|
XP_DELETE(closure);
|
|
|
|
if (top_state && top_state->doc_state &&
|
|
top_state->doc_state->selection_layer == layer)
|
|
top_state->doc_state->selection_layer = NULL;
|
|
|
|
}
|
|
|
|
typedef struct html_event_closure {
|
|
CL_Layer *layer;
|
|
CL_Event *event;
|
|
} html_event_closure;
|
|
|
|
static void
|
|
lo_html_event_callback(MWContext * pContext, LO_Element * pEle, int32 event,
|
|
void * pObj, ETEventStatus status)
|
|
{
|
|
html_event_closure *event_closure = (html_event_closure *)pObj;
|
|
|
|
if (status == EVENT_PANIC){
|
|
if (event_closure->event->fe_event)
|
|
XP_FREE(event_closure->event->fe_event);
|
|
XP_FREE(event_closure->event);
|
|
XP_FREE(event_closure);
|
|
return;
|
|
}
|
|
|
|
if (status == EVENT_OK){
|
|
FE_HandleLayerEvent(pContext, event_closure->layer,
|
|
event_closure->event);
|
|
}
|
|
|
|
if (event_closure->event->fe_event)
|
|
XP_FREE(event_closure->event->fe_event);
|
|
XP_FREE(event_closure->event);
|
|
XP_FREE(event_closure);
|
|
return;
|
|
}
|
|
|
|
#define LEFT_BUTTON_DRAG 1
|
|
#define RIGHT_BUTTON_DRAG 2
|
|
|
|
static PRBool
|
|
lo_html_event_handler_func(CL_Layer *layer,
|
|
CL_Event *event)
|
|
{
|
|
html_event_closure *event_closure;
|
|
JSEvent *jsevent, *jskd_event;
|
|
LO_Element *pElement;
|
|
uint32 event_capture_bit = 0;
|
|
MWContext *context;
|
|
int32 client_x_pos, client_y_pos, layer_id;
|
|
LO_AnchorData *anchor = NULL;
|
|
|
|
lo_AnyLayerClosure *closure = (lo_AnyLayerClosure *)CL_GetLayerClientData(layer);
|
|
|
|
if (EDT_IS_EDITOR(closure->context))
|
|
return FE_HandleLayerEvent(closure->context, layer, event);
|
|
|
|
event_capture_bit |= closure->context->event_bit;
|
|
|
|
if (closure->context->is_grid_cell) {
|
|
context = closure->context;
|
|
while (context->grid_parent) {
|
|
context = context->grid_parent;
|
|
event_capture_bit |= context->event_bit;
|
|
}
|
|
}
|
|
|
|
layer_id = (LO_GetLayerType(layer) == LO_GROUP_LAYER) ?
|
|
LO_GetIdFromLayer(closure->context, layer) : LO_DOCUMENT_LAYER_ID;
|
|
|
|
if ((event->type != CL_EVENT_MOUSE_ENTER) &&
|
|
(event->type != CL_EVENT_MOUSE_LEAVE) &&
|
|
(event->type != CL_EVENT_KEY_FOCUS_GAINED) &&
|
|
(event->type != CL_EVENT_KEY_FOCUS_LOST))
|
|
pElement = LO_XYToElement(closure->context, event->x, event->y,
|
|
layer);
|
|
else {
|
|
pElement = NULL;
|
|
|
|
/* These events shouldn't be going to the document. */
|
|
if (layer_id == LO_DOCUMENT_LAYER_ID) {
|
|
return FE_HandleLayerEvent(closure->context, layer, event);
|
|
}
|
|
}
|
|
|
|
if (pElement && pElement->type == LO_FORM_ELE) {
|
|
if ((event->x - pElement->lo_form.x - pElement->lo_form.x_offset > pElement->lo_form.width) ||
|
|
(event->x - pElement->lo_form.x - pElement->lo_form.x_offset < 0) ||
|
|
(event->y - pElement->lo_form.y - pElement->lo_form.y_offset > pElement->lo_form.height) ||
|
|
(event->y - pElement->lo_form.y - pElement->lo_form.y_offset < 0)) {
|
|
pElement = NULL;
|
|
}
|
|
}
|
|
|
|
FE_GetWindowOffset(closure->context, &client_x_pos, &client_y_pos);
|
|
|
|
jsevent = XP_NEW_ZAP(JSEvent);
|
|
switch (event->type)
|
|
{
|
|
case CL_EVENT_MOUSE_BUTTON_DOWN:
|
|
if (pElement) {
|
|
if (pElement->type == LO_TEXT)
|
|
anchor = pElement->lo_text.anchor_href;
|
|
if (pElement->type == LO_IMAGE) {
|
|
anchor = pElement->lo_image.anchor_href;
|
|
}
|
|
|
|
if (anchor && !(event_capture_bit & EVENT_MOUSEDOWN)
|
|
&& anchor->event_handler_present != TRUE) {
|
|
XP_FREE(jsevent);
|
|
return FE_HandleLayerEvent(closure->context, layer, event);
|
|
}
|
|
}
|
|
#ifdef XP_MAC
|
|
/* the mac sets the data to the number of clicks instead of
|
|
sending a multiclick event */
|
|
if (event->data > 1)
|
|
jsevent->type = EVENT_DBLCLICK;
|
|
else
|
|
#endif /* XP_MAC */
|
|
jsevent->type = EVENT_MOUSEDOWN;
|
|
if (closure->context->js_drag_enabled) {
|
|
CL_GrabMouseEvents(closure->context->compositor, layer);
|
|
if (event->which == 1)
|
|
closure->context->js_dragging |= LEFT_BUTTON_DRAG;
|
|
if (event->which == 3)
|
|
closure->context->js_dragging |= RIGHT_BUTTON_DRAG;
|
|
}
|
|
break;
|
|
case CL_EVENT_MOUSE_BUTTON_UP:
|
|
if (pElement) {
|
|
if (pElement->type == LO_TEXT)
|
|
anchor = pElement->lo_text.anchor_href;
|
|
if (pElement->type == LO_IMAGE)
|
|
anchor = pElement->lo_image.anchor_href;
|
|
|
|
if (anchor && !(event_capture_bit & EVENT_MOUSEUP)
|
|
&& anchor->event_handler_present != TRUE) {
|
|
XP_FREE(jsevent);
|
|
return FE_HandleLayerEvent(closure->context, layer, event);
|
|
}
|
|
}
|
|
jsevent->type = EVENT_MOUSEUP;
|
|
if (closure->context->js_dragging) {
|
|
CL_GrabMouseEvents(closure->context->compositor, NULL);
|
|
if (event->which == 1)
|
|
closure->context->js_dragging &= ~LEFT_BUTTON_DRAG;
|
|
if (event->which == 3)
|
|
closure->context->js_dragging &= ~RIGHT_BUTTON_DRAG;
|
|
}
|
|
break;
|
|
case CL_EVENT_MOUSE_BUTTON_MULTI_CLICK:
|
|
jsevent->type = EVENT_DBLCLICK;
|
|
break;
|
|
case CL_EVENT_KEY_DOWN:
|
|
if (!event->data) {
|
|
|
|
/* Key events are handled a little strangely. The initial hitting of the
|
|
* key triggers two events, a KEYDOWN event and a KEYPRESS event. Any
|
|
* key repetition is accomplished through additional KEYPRESS events. So
|
|
* if this is our first time through, we need to create and send an
|
|
* additional event. However, since the main program triggers off of the
|
|
* KEYPRESS only we don't need a callback and closure.
|
|
*/
|
|
jskd_event = XP_NEW_ZAP(JSEvent);
|
|
jskd_event->type = EVENT_KEYDOWN;
|
|
jskd_event->x = event->x;
|
|
jskd_event->y = event->y;
|
|
jskd_event->docx = event->x + CL_GetLayerXOrigin(layer);
|
|
jskd_event->docy = event->y + CL_GetLayerYOrigin(layer);
|
|
jskd_event->screenx = jskd_event->docx + client_x_pos
|
|
- CL_GetCompositorXOffset(closure->context->compositor);
|
|
jskd_event->screeny = jskd_event->docy + client_y_pos
|
|
- CL_GetCompositorXOffset(closure->context->compositor);
|
|
jskd_event->which = event->which;
|
|
jskd_event->modifiers = event->modifiers;
|
|
jskd_event->layer_id = layer_id;
|
|
|
|
ET_SendEvent(closure->context, pElement, jskd_event, 0, 0);
|
|
}
|
|
jsevent->type = EVENT_KEYPRESS;
|
|
break;
|
|
case CL_EVENT_KEY_UP:
|
|
jsevent->type = EVENT_KEYUP;
|
|
break;
|
|
case CL_EVENT_MOUSE_ENTER:
|
|
jsevent->type = EVENT_MOUSEOVER;
|
|
break;
|
|
case CL_EVENT_MOUSE_LEAVE:
|
|
jsevent->type = EVENT_MOUSEOUT;
|
|
break;
|
|
case CL_EVENT_MOUSE_MOVE:
|
|
if (event_capture_bit & EVENT_MOUSEMOVE ||
|
|
closure->context->js_dragging==TRUE)
|
|
jsevent->type = EVENT_MOUSEMOVE;
|
|
break;
|
|
case CL_EVENT_KEY_FOCUS_GAINED:
|
|
jsevent->type = EVENT_FOCUS;
|
|
break;
|
|
case CL_EVENT_KEY_FOCUS_LOST:
|
|
jsevent->type = EVENT_BLUR;
|
|
break;
|
|
default:
|
|
XP_FREE(jsevent);
|
|
return FE_HandleLayerEvent(closure->context, layer, event);
|
|
}
|
|
|
|
if (!jsevent->type) {
|
|
XP_FREE(jsevent);
|
|
return FE_HandleLayerEvent(closure->context, layer, event);
|
|
}
|
|
|
|
jsevent->x = event->x;
|
|
jsevent->y = event->y;
|
|
jsevent->docx = event->x + CL_GetLayerXOrigin(layer);
|
|
jsevent->docy = event->y + CL_GetLayerYOrigin(layer);
|
|
jsevent->screenx = jsevent->docx + client_x_pos
|
|
- CL_GetCompositorXOffset(closure->context->compositor);
|
|
jsevent->screeny = jsevent->docy + client_y_pos
|
|
- CL_GetCompositorYOffset(closure->context->compositor);
|
|
jsevent->which = event->which;
|
|
jsevent->modifiers = event->modifiers;
|
|
jsevent->layer_id = layer_id;
|
|
|
|
/* This probably isn't the best place for these but I don't want to
|
|
* put the setting of all of these at the top of the function because
|
|
* of possible early returns and since this is part of the event loop
|
|
* I'm trying to be stingy with cycles.
|
|
*/
|
|
if (jsevent->type == EVENT_FOCUS || jsevent->type == EVENT_BLUR) {
|
|
jsevent->docx = 0;
|
|
jsevent->docy = 0;
|
|
jsevent->screenx = 0;
|
|
jsevent->screeny = 0;
|
|
jsevent->which = 0;
|
|
}
|
|
if (jsevent->type == EVENT_MOUSEOVER || jsevent->type == EVENT_MOUSEOUT) {
|
|
jsevent->which = 0;
|
|
}
|
|
|
|
event_closure = XP_NEW_ZAP(html_event_closure);
|
|
event_closure->layer = layer;
|
|
event_closure->event = XP_NEW_ZAP(CL_Event);
|
|
XP_MEMCPY(event_closure->event, event, sizeof(CL_Event));
|
|
if (event->fe_event) {
|
|
event_closure->event->fe_event = XP_ALLOC(event->fe_event_size);
|
|
XP_MEMCPY(event_closure->event->fe_event, event->fe_event, event->fe_event_size);
|
|
}
|
|
|
|
ET_SendEvent(closure->context, pElement, jsevent, lo_html_event_callback, event_closure);
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
static PRBool
|
|
lo_html_inflow_layer_event_handler_func(CL_Layer *layer,
|
|
CL_Event *event)
|
|
{
|
|
/* XXX - Doesn't work yet */
|
|
#if 0
|
|
lo_LayerClosure *closure = (lo_LayerClosure *)CL_GetLayerClientData(layer);
|
|
CL_Layer *parent = CL_GetLayerParent(layer);
|
|
int32 x_offset = closure->u.block_closure.x_offset;
|
|
int32 y_offset = closure->u.block_closure.y_offset;
|
|
event->x += x_offset;
|
|
event->y += y_offset;
|
|
if (lo_html_event_handler_func(parent, event))
|
|
return PR_TRUE;
|
|
event->x += x_offset;
|
|
event->y += y_offset;
|
|
#endif
|
|
return PR_FALSE;
|
|
}
|
|
|
|
static PRBool
|
|
lo_is_document_layer(CL_Layer * layer)
|
|
{
|
|
lo_TopState *top_state = lo_get_layer_top_state(layer);
|
|
return (PRBool)(top_state->doc_layer == layer);
|
|
}
|
|
|
|
/* Callback that tells the front-end to expand the scrollable extent
|
|
of the document to encompass the _DOCUMENT layer. */
|
|
static void
|
|
doc_bbox_changed_func(CL_Layer *layer, XP_Rect *new_bbox)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
lo_TopState *top_state = lo_get_layer_top_state(layer);
|
|
lo_LayerDocState *layer_state = top_state->layers[LO_DOCUMENT_LAYER_ID];
|
|
|
|
int32 width = new_bbox->right - new_bbox->left;
|
|
int32 height = new_bbox->bottom - new_bbox->top;
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
|
|
XP_ASSERT(layer_state->id == LO_DOCUMENT_LAYER_ID);
|
|
|
|
if (layer_state->overrideScrollWidth)
|
|
width = layer_state->contentWidth;
|
|
else
|
|
layer_state->contentWidth = width;
|
|
|
|
if (layer_state->overrideScrollHeight)
|
|
height = layer_state->contentHeight;
|
|
else
|
|
layer_state->contentHeight = height;
|
|
|
|
FE_SetDocDimension(closure->context, FE_VIEW, width, height);
|
|
}
|
|
|
|
/* Expand a bbox to contain a given layer.
|
|
(Callback for CL_ForEachChildOfLayer(), below.) */
|
|
static PRBool
|
|
expand_bbox(CL_Layer *layer, void *closure)
|
|
{
|
|
XP_Rect layer_bbox;
|
|
XP_Rect *enclosing_bbox = closure;
|
|
|
|
CL_GetLayerBboxAbsolute(layer, &layer_bbox);
|
|
|
|
/* Expand the enclosing bbox to encompass the layer. */
|
|
XP_RectsBbox(&layer_bbox, enclosing_bbox, enclosing_bbox);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
/* The _BODY layer is treated specially in that if it shrinks, it may
|
|
cause the document to shrink as well. At the moment, the only case
|
|
in which that happens is when editing a document. */
|
|
static void
|
|
body_bbox_changed_func(CL_Layer *layer, XP_Rect *new_bbox)
|
|
{
|
|
XP_Rect bbox = {0, 0, 0, 0};
|
|
CL_Layer *doc_layer = CL_GetLayerParent(layer);
|
|
|
|
/* Set the bbox of the document to be the rectangle that encloses
|
|
the BODY layer and all its siblings. */
|
|
CL_ForEachChildOfLayer(doc_layer, expand_bbox, &bbox);
|
|
CL_SetLayerBbox(doc_layer, &bbox);
|
|
}
|
|
|
|
lo_LayerDocState *
|
|
lo_GetLayerState(CL_Layer *layer)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
lo_TopState *top_state;
|
|
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return NULL;
|
|
|
|
top_state = lo_get_layer_top_state(layer);
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
/* XP_ASSERT(closure->type == LO_GROUP_LAYER); */
|
|
/* Hack for document layer and body layers, necessary for event handling */
|
|
if ((top_state->body_layer == layer) ||
|
|
(top_state->doc_layer == layer) ||
|
|
(closure->type != LO_GROUP_LAYER))
|
|
return top_state->layers[LO_DOCUMENT_LAYER_ID];
|
|
|
|
return closure->layer_state;
|
|
|
|
}
|
|
|
|
PRBool
|
|
lo_InsideLayer(lo_DocState *state)
|
|
{
|
|
int32 layer_id = lo_CurrentLayerId(state);
|
|
if (layer_id != LO_DOCUMENT_LAYER_ID)
|
|
return PR_TRUE;
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool
|
|
lo_InsideInflowLayer(lo_DocState *state)
|
|
{
|
|
lo_LayerDocState *layer_state;
|
|
lo_GroupLayerClosure *closure;
|
|
|
|
layer_state = lo_CurrentLayerState(state);
|
|
|
|
if (layer_state->id == LO_DOCUMENT_LAYER_ID)
|
|
return PR_FALSE;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer_state->layer);
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
if (closure->type != LO_GROUP_LAYER)
|
|
return PR_FALSE;
|
|
|
|
return closure->is_inflow;
|
|
}
|
|
|
|
/* Get XY needed to convert from layer coordinates to layout coordinates */
|
|
void
|
|
lo_GetLayerXYShift(CL_Layer *layer, int32 *xp, int32 *yp)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
|
|
*xp = 0;
|
|
*yp = 0;
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
if (closure->type != LO_GROUP_LAYER)
|
|
return;
|
|
|
|
*xp = closure->x_offset;
|
|
*yp = closure->y_offset;
|
|
}
|
|
|
|
void
|
|
LO_MoveLayer(CL_Layer *layer, int32 x, int32 y)
|
|
{
|
|
CL_Layer *parent_layer;
|
|
lo_GroupLayerClosure *closure, *parent_closure;
|
|
int32 x_offset, y_offset;
|
|
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
parent_layer = CL_GetLayerParent(layer);
|
|
x_offset = closure->x_offset;
|
|
y_offset = closure->y_offset;
|
|
|
|
/* Handle special case of inflow layer nested in another inflow layer. */
|
|
if (closure->is_inflow && parent_layer) {
|
|
parent_closure = CL_GetLayerClientData(parent_layer);
|
|
x_offset -= parent_closure->x_offset;
|
|
y_offset -= parent_closure->y_offset;
|
|
}
|
|
|
|
CL_MoveLayer(layer, x + x_offset, y + y_offset);
|
|
}
|
|
|
|
void
|
|
LO_SetLayerBbox(CL_Layer *layer, XP_Rect *bbox)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_GetLayerState(layer);
|
|
XP_Rect resultBbox;
|
|
XP_IntersectRect(&layer_state->viewRect, bbox, &resultBbox);
|
|
CL_SetLayerBbox(layer, &resultBbox);
|
|
}
|
|
|
|
int32
|
|
LO_GetLayerXOffset(CL_Layer *layer)
|
|
{
|
|
CL_Layer *parent_layer;
|
|
lo_GroupLayerClosure *closure, *parent_closure;
|
|
int32 x_offset;
|
|
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return 0;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
parent_layer = CL_GetLayerParent(layer);
|
|
x_offset = closure->x_offset;
|
|
|
|
/* Handle special case of inflow layer nested in another inflow layer. */
|
|
if (closure->is_inflow && parent_layer) {
|
|
parent_closure = CL_GetLayerClientData(parent_layer);
|
|
x_offset -= parent_closure->x_offset;
|
|
}
|
|
|
|
return CL_GetLayerXOffset(layer) - x_offset;
|
|
}
|
|
|
|
int32
|
|
LO_GetLayerYOffset(CL_Layer *layer)
|
|
{
|
|
CL_Layer *parent_layer;
|
|
lo_GroupLayerClosure *closure, *parent_closure;
|
|
int32 y_offset = 0;
|
|
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return 0;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
parent_layer = CL_GetLayerParent(layer);
|
|
y_offset = closure->y_offset;
|
|
|
|
/* Handle special case of inflow layer nested in another inflow layer. */
|
|
if (closure->is_inflow && parent_layer) {
|
|
parent_closure = CL_GetLayerClientData(parent_layer);
|
|
y_offset -= parent_closure->y_offset;
|
|
}
|
|
|
|
return CL_GetLayerYOffset(layer) - y_offset;
|
|
}
|
|
|
|
int32
|
|
LO_GetLayerScrollWidth(CL_Layer *layer)
|
|
{
|
|
lo_LayerDocState *layer_state;
|
|
XP_ASSERT(layer);
|
|
if (!layer)
|
|
return -1;
|
|
|
|
layer_state = lo_GetLayerState(layer);
|
|
XP_ASSERT(layer_state);
|
|
if (!layer_state)
|
|
return -1;
|
|
return layer_state->contentWidth;
|
|
}
|
|
|
|
int32
|
|
LO_GetLayerScrollHeight(CL_Layer *layer)
|
|
{
|
|
lo_LayerDocState *layer_state;
|
|
XP_ASSERT(layer);
|
|
if (!layer)
|
|
return -1;
|
|
|
|
layer_state = lo_GetLayerState(layer);
|
|
XP_ASSERT(layer_state);
|
|
if (!layer_state)
|
|
return -1;
|
|
return layer_state->contentHeight;
|
|
}
|
|
|
|
void
|
|
LO_SetLayerScrollWidth(CL_Layer *layer, uint32 width)
|
|
{
|
|
uint32 height;
|
|
lo_LayerDocState *layer_state;
|
|
XP_ASSERT(layer);
|
|
if (!layer)
|
|
return;
|
|
|
|
/* The document layer is special. It's the only one with a
|
|
mutable scroll width and height.*/
|
|
if (!lo_is_document_layer(layer))
|
|
return;
|
|
|
|
layer_state = lo_GetLayerState(layer);
|
|
layer_state->contentWidth = width;
|
|
layer_state->overrideScrollWidth = PR_TRUE;
|
|
height = layer_state->contentHeight;
|
|
LO_SetDocumentDimensions(lo_get_layer_context(layer), width, height);
|
|
}
|
|
|
|
void
|
|
LO_SetLayerScrollHeight(CL_Layer *layer, uint32 height)
|
|
{
|
|
uint32 width;
|
|
lo_LayerDocState *layer_state;
|
|
XP_ASSERT(layer);
|
|
if (!layer)
|
|
return;
|
|
|
|
/* The document layer is special. It's the only one with a
|
|
mutable scroll width and height.*/
|
|
if (!lo_is_document_layer(layer))
|
|
return;
|
|
|
|
layer_state = lo_GetLayerState(layer);
|
|
layer_state->contentHeight = height;
|
|
layer_state->overrideScrollHeight = PR_TRUE;
|
|
width = layer_state->contentWidth;
|
|
LO_SetDocumentDimensions(lo_get_layer_context(layer), width, height);
|
|
}
|
|
|
|
int32
|
|
LO_GetLayerWrapWidth(CL_Layer *layer)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return 0;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
|
|
return closure->wrap_width;
|
|
}
|
|
|
|
extern void
|
|
lo_SetLayerClipExpansionPolicy(CL_Layer *layer, int policy)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
|
|
closure->clip_expansion_policy = policy;
|
|
}
|
|
|
|
extern int
|
|
lo_GetLayerClipExpansionPolicy(CL_Layer *layer)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return 0;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
|
|
return closure->clip_expansion_policy;
|
|
}
|
|
|
|
typedef struct lo_BlockRectStruct
|
|
{
|
|
lo_HTMLBlockClosure *closure;
|
|
CL_Layer *layer;
|
|
} lo_BlockRectStruct;
|
|
|
|
/* Called for each rectangle in the update region */
|
|
static void
|
|
lo_block_rect_func(void *closure,
|
|
XP_Rect *rect)
|
|
{
|
|
lo_BlockRectStruct *block_struct = (lo_BlockRectStruct *)closure;
|
|
lo_HTMLBlockClosure *myclosure = block_struct->closure;
|
|
|
|
/* Intersect the layer to be drawn with the layer's bbox */
|
|
CL_WindowToLayerRect(CL_GetLayerCompositor(block_struct->layer),
|
|
block_struct->layer, rect);
|
|
|
|
lo_DisplayCellContents(myclosure->context,
|
|
myclosure->layer_state->cell,
|
|
-myclosure->x_offset,
|
|
-myclosure->y_offset,
|
|
rect->left, rect->top,
|
|
(rect->right - rect->left),
|
|
(rect->bottom - rect->top));
|
|
}
|
|
|
|
/* painter_func for block layers */
|
|
static void
|
|
lo_block_painter_func(CL_Drawable *drawable,
|
|
CL_Layer *layer,
|
|
FE_Region update_region)
|
|
{
|
|
lo_BlockRectStruct block_struct;
|
|
|
|
block_struct.closure = (lo_HTMLBlockClosure *)CL_GetLayerClientData(layer);
|
|
block_struct.layer = layer;
|
|
|
|
FE_SetDrawable(block_struct.closure->context, drawable);
|
|
|
|
CL_ForEachRectCoveringRegion(update_region,
|
|
(FE_RectInRegionFunc)lo_block_rect_func,
|
|
(void *)&block_struct);
|
|
|
|
FE_SetDrawable(block_struct.closure->context, NULL);
|
|
}
|
|
|
|
|
|
/* Called for each rectangle in the update region */
|
|
static void
|
|
lo_body_rect_func(void *closure,
|
|
XP_Rect *rect)
|
|
{
|
|
lo_HTMLBodyClosure *myclosure = (lo_HTMLBodyClosure *)closure;
|
|
|
|
CL_WindowToLayerRect(myclosure->context->compositor,
|
|
myclosure->layer,
|
|
rect);
|
|
LO_RefreshArea(myclosure->context,
|
|
rect->left, rect->top,
|
|
(rect->right - rect->left),
|
|
(rect->bottom - rect->top));
|
|
}
|
|
|
|
/* painter_func for HTML document layers */
|
|
static void
|
|
lo_body_painter_func(CL_Drawable *drawable,
|
|
CL_Layer *layer,
|
|
FE_Region update_region)
|
|
{
|
|
lo_HTMLBodyClosure *closure;
|
|
|
|
closure = (lo_HTMLBodyClosure *)CL_GetLayerClientData(layer);
|
|
FE_SetDrawable(closure->context, drawable);
|
|
|
|
CL_ForEachRectCoveringRegion(update_region,
|
|
(FE_RectInRegionFunc)lo_body_rect_func,
|
|
(void *)closure);
|
|
|
|
FE_SetDrawable(closure->context, NULL);
|
|
}
|
|
|
|
/*
|
|
* Create an grouping layer and make it the parent of the content
|
|
* and background layers specified.
|
|
*/
|
|
static CL_Layer *
|
|
lo_CreateGroupLayer(MWContext *context, char *name,
|
|
int32 x_offset, int32 y_offset,
|
|
Bool enumerate_for_javascript,
|
|
Bool clip_children, CL_Layer *content,
|
|
lo_LayerDocState *layer_state,
|
|
|
|
CL_BboxChangedFunc bbox_changed_func,
|
|
CL_EventHandlerFunc event_handler_func,
|
|
|
|
CL_DestroyFunc destroy_func)
|
|
|
|
{
|
|
CL_Layer *layer;
|
|
CL_LayerVTable vtable;
|
|
lo_GroupLayerClosure *closure;
|
|
|
|
uint32 flags =
|
|
CL_HIDDEN |
|
|
(CL_DONT_ENUMERATE * (enumerate_for_javascript == FALSE)) |
|
|
(CL_CLIP_CHILD_LAYERS * clip_children);
|
|
|
|
/* Bbox values don't matter since they will be changed afterwards. */
|
|
XP_Rect bbox = {0,0,0,0};
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.destroy_func = destroy_func;
|
|
|
|
vtable.bbox_changed_func = bbox_changed_func;
|
|
vtable.event_handler_func = event_handler_func;
|
|
|
|
closure = XP_NEW_ZAP(lo_GroupLayerClosure);
|
|
closure->type = LO_GROUP_LAYER;
|
|
closure->context = context;
|
|
closure->content_layer = content;
|
|
closure->x_offset = x_offset;
|
|
closure->y_offset = y_offset;
|
|
closure->layer_state = layer_state;
|
|
|
|
|
|
/* Initially, layer created at [0,0] and moved to final position later. */
|
|
layer = CL_NewLayer(name, 0, 0, &bbox, &vtable,
|
|
flags, (void *)closure);
|
|
|
|
if (content)
|
|
CL_InsertChildByZ(layer, content, Z_CONTENT_LAYERS);
|
|
|
|
return layer;
|
|
}
|
|
|
|
/* Create the _CONTENT child of a block layer */
|
|
PRIVATE
|
|
CL_Layer *
|
|
lo_CreateBlockContentLayer(MWContext *context,
|
|
PRBool is_inflow,
|
|
int32 x_offset,
|
|
int32 y_offset,
|
|
lo_LayerDocState *layer_state,
|
|
lo_DocState *state)
|
|
{
|
|
CL_Layer *layer;
|
|
lo_HTMLBlockClosure *closure;
|
|
CL_LayerVTable vtable;
|
|
|
|
/* Bbox values don't matter since CL_DONT_CLIP_SELF flag is set */
|
|
XP_Rect bbox = {0,0,0,0};
|
|
|
|
uint32 flags = CL_DONT_ENUMERATE | CL_DONT_CLIP_SELF;
|
|
|
|
closure = XP_NEW_ZAP(lo_HTMLBlockClosure);
|
|
closure->type = LO_HTML_BLOCK_LAYER;
|
|
closure->context = context;
|
|
closure->layer_state = layer_state;
|
|
closure->state = state;
|
|
closure->is_inflow = is_inflow;
|
|
closure->x_offset = x_offset;
|
|
closure->y_offset = y_offset;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.painter_func = (CL_PainterFunc)lo_block_painter_func;
|
|
vtable.destroy_func = (CL_DestroyFunc)lo_html_destroy_func;
|
|
|
|
layer = CL_NewLayer(LO_CONTENT_LAYER_NAME, 0, 0, &bbox, &vtable,
|
|
flags, (void *)closure);
|
|
|
|
return layer;
|
|
}
|
|
|
|
typedef struct lo_BackgroundRectStruct
|
|
{
|
|
lo_BackgroundLayerClosure *closure;
|
|
CL_Layer *layer;
|
|
} lo_BackgroundRectStruct;
|
|
|
|
/* Called for each rectangle in the update region */
|
|
static void
|
|
lo_background_rect_func(void *inclosure,
|
|
XP_Rect *rect)
|
|
{
|
|
LO_ImageStruct *backdrop;
|
|
|
|
LO_Color * bg;
|
|
PRBool is_transparent_backdrop, is_complete;
|
|
lo_TileMode tile_mode;
|
|
PRBool fully_tiled; /* PR_TRUE, if tiled in both X and Y */
|
|
|
|
XP_Rect doc_rect;
|
|
lo_BackgroundLayerClosure *closure = ((lo_BackgroundRectStruct *)inclosure)->closure;
|
|
CL_Layer *layer = ((lo_BackgroundRectStruct *)inclosure)->layer;
|
|
CL_Compositor *compositor = CL_GetLayerCompositor(layer);
|
|
MWContext * context = closure->context;
|
|
static LO_Color white_color = {0xFF, 0xFF, 0xFF};
|
|
|
|
XP_CopyRect(rect, &doc_rect);
|
|
CL_WindowToDocumentRect(compositor, &doc_rect);
|
|
|
|
backdrop = closure->backdrop_image;
|
|
bg = closure->bg_color;
|
|
tile_mode = closure->tile_mode;
|
|
|
|
/* Erase with solid color if required. */
|
|
fully_tiled = (PRBool)(tile_mode == LO_TILE_BOTH);
|
|
is_complete = (PRBool)(backdrop &&
|
|
((backdrop->image_status == IL_IMAGE_COMPLETE) ||
|
|
(backdrop->image_status == IL_FRAME_COMPLETE)));
|
|
is_transparent_backdrop = (PRBool)(backdrop && (!is_complete ||
|
|
backdrop->is_transparent));
|
|
|
|
/*
|
|
* For printing never draw backdrop images. Also, only draw the
|
|
* backgrounds of table cells in their actual color (everything
|
|
* else will be drawn with white.
|
|
*/
|
|
|
|
if ((context->type == MWContextPrint) ||
|
|
(context->type == MWContextPostScript)) {
|
|
#ifdef XP_MAC
|
|
/* the Mac does print background images, if requested */
|
|
XP_Bool backgroundPref;
|
|
if (PREF_GetBoolPref("browser.mac.print_background", &backgroundPref) != PREF_OK || !backgroundPref)
|
|
backdrop = NULL;
|
|
#elif defined(XP_WIN32)
|
|
XP_Bool backgroundPref = FALSE;
|
|
PREF_GetBoolPref("browser.print_background",&backgroundPref);
|
|
if (!backgroundPref)
|
|
backdrop = NULL;
|
|
|
|
#else
|
|
backdrop = NULL;
|
|
#endif
|
|
#ifndef XP_WIN32
|
|
if (closure->bg_type != BG_CELL)
|
|
bg = &white_color;
|
|
#else
|
|
if (!backgroundPref) {
|
|
if (closure->bg_type != BG_CELL)
|
|
bg = &white_color;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (bg && (!backdrop || is_transparent_backdrop || !fully_tiled))
|
|
FE_EraseBackground(closure->context,
|
|
FE_VIEW,
|
|
doc_rect.left,
|
|
doc_rect.top,
|
|
doc_rect.right - doc_rect.left,
|
|
doc_rect.bottom - doc_rect.top,
|
|
bg);
|
|
|
|
/* Paint the backdrop image if there is one. */
|
|
if (backdrop && !backdrop->is_icon && is_complete) {
|
|
XP_Rect tile_area = CL_MAX_RECT;
|
|
|
|
/* Convert to layer coordinates */
|
|
CL_WindowToLayerRect(compositor, layer, rect);
|
|
|
|
/* Constrain tiling area based on tiling mode */
|
|
if ((int)tile_mode & LO_TILE_HORIZ)
|
|
tile_area.bottom = backdrop->height;
|
|
if ((int)tile_mode & LO_TILE_VERT)
|
|
tile_area.right = backdrop->width;
|
|
|
|
XP_IntersectRect(rect, &tile_area, rect);
|
|
|
|
/* Note: if the compositor is ever used with contexts which have a
|
|
scale factor other than unity, then all length arguments should
|
|
be divided by the appropriate context scaling factor i.e.
|
|
convertPixX, convertPixY. The FE callback is responsible for
|
|
context specific scaling. */
|
|
IL_DisplaySubImage(backdrop->image_req, 0, 0,
|
|
rect->left / context->convertPixX,
|
|
rect->top / context->convertPixY,
|
|
(rect->right - rect->left) / context->convertPixX,
|
|
(rect->bottom - rect->top) / context->convertPixY);
|
|
}
|
|
}
|
|
|
|
/* painter_func for a background layer */
|
|
static void
|
|
lo_background_painter_func(CL_Drawable *drawable,
|
|
CL_Layer *layer,
|
|
FE_Region update_region)
|
|
{
|
|
lo_BackgroundRectStruct bgrect;
|
|
lo_BackgroundLayerClosure *closure;
|
|
closure = (lo_BackgroundLayerClosure *)CL_GetLayerClientData(layer);
|
|
|
|
bgrect.closure = closure;
|
|
bgrect.layer = layer;
|
|
|
|
#ifdef XP_MAC
|
|
/* If we're a grid parent, then we don't want to draw our background */
|
|
if (closure->context->grid_children && !XP_ListIsEmpty(closure->context->grid_children))
|
|
return;
|
|
#endif /* XP_MAC */
|
|
|
|
/*
|
|
* For Windows and X, we draw the backgrounds of grid parents
|
|
* since clipping of child windows is done by the windowing system.
|
|
*/
|
|
|
|
FE_SetDrawable(closure->context, drawable);
|
|
|
|
/* XXX - temporary, because Exceed X server has bugs with its
|
|
offscreen tiling of clipped areas. */
|
|
/* NOTE -- this causes a significant performance hit on other
|
|
* Unix platforms. Is there no other way around this Exceed bug?
|
|
*/
|
|
#ifdef XP_UNIX
|
|
FE_ForEachRectInRegion(update_region,
|
|
(FE_RectInRegionFunc)lo_background_rect_func,
|
|
(void *)&bgrect);
|
|
#else
|
|
CL_ForEachRectCoveringRegion(update_region,
|
|
(FE_RectInRegionFunc)lo_background_rect_func,
|
|
(void *)&bgrect);
|
|
#endif
|
|
|
|
FE_SetDrawable(closure->context, NULL);
|
|
}
|
|
|
|
static void
|
|
lo_background_destroy_func(CL_Layer *layer)
|
|
{
|
|
LO_Color *bg_color;
|
|
LO_ImageStruct *backdrop;
|
|
lo_BackgroundLayerClosure *closure;
|
|
closure = (lo_BackgroundLayerClosure *)CL_GetLayerClientData(layer);
|
|
|
|
XP_ASSERT(closure->type == LO_HTML_BACKGROUND_LAYER);
|
|
|
|
bg_color = closure->bg_color;
|
|
if (bg_color)
|
|
XP_FREE(bg_color);
|
|
|
|
backdrop = closure->backdrop_image;
|
|
if (backdrop) {
|
|
if (backdrop->image_req)
|
|
IL_DestroyImage(backdrop->image_req);
|
|
if (backdrop->image_url)
|
|
XP_FREE(backdrop->image_url);
|
|
XP_FREE(backdrop->image_attr);
|
|
XP_FREE(backdrop);
|
|
}
|
|
|
|
XP_DELETE(closure);
|
|
}
|
|
|
|
/* Creates a HTML background layer to be a child of a layer group */
|
|
PRIVATE
|
|
CL_Layer *
|
|
lo_CreateBackgroundLayer(MWContext *context, PRBool is_document_backdrop)
|
|
{
|
|
CL_Layer *layer;
|
|
XP_Rect bbox = {0, 0, 0, 0};
|
|
|
|
CL_LayerVTable vtable;
|
|
lo_BackgroundLayerClosure *closure;
|
|
|
|
uint32 flags = CL_DONT_ENUMERATE | CL_DONT_CLIP_SELF;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.painter_func = lo_background_painter_func;
|
|
vtable.destroy_func = lo_background_destroy_func;
|
|
if (is_document_backdrop)
|
|
vtable.event_handler_func = lo_html_event_handler_func;
|
|
|
|
closure = XP_NEW_ZAP(lo_BackgroundLayerClosure);
|
|
closure->type = LO_HTML_BACKGROUND_LAYER;
|
|
closure->context = context;
|
|
closure->bg_type = is_document_backdrop ? BG_DOCUMENT : BG_LAYER;
|
|
closure->tile_mode = LO_TILE_BOTH; /* Default tiling mode */
|
|
|
|
layer = CL_NewLayer(LO_BACKGROUND_LAYER_NAME, 0, 0, &bbox, &vtable,
|
|
flags, (void *)closure);
|
|
|
|
return layer;
|
|
}
|
|
|
|
/* Retrieve the background sub-layer for a layer group.
|
|
If the <create> argument is TRUE, build the background layer
|
|
if it doesn't exist. */
|
|
PRIVATE
|
|
CL_Layer *
|
|
lo_get_group_background(CL_Layer *layer, PRBool create)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
MWContext *context;
|
|
CL_Layer *background_layer;
|
|
|
|
if (! layer)
|
|
return NULL;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
context = closure->context;
|
|
|
|
XP_ASSERT(closure->type == LO_GROUP_LAYER);
|
|
if (closure->type != LO_GROUP_LAYER)
|
|
return NULL;
|
|
|
|
background_layer = closure->background_layer;
|
|
if (!background_layer && create) {
|
|
PRBool is_document_backdrop = lo_is_document_layer(layer);
|
|
background_layer = lo_CreateBackgroundLayer(context,
|
|
is_document_backdrop);
|
|
|
|
if (! background_layer) /* OOM */
|
|
return NULL;
|
|
CL_InsertChildByZ(layer, background_layer, Z_BACKGROUND_LAYER);
|
|
closure->background_layer = background_layer;
|
|
}
|
|
return background_layer;
|
|
}
|
|
|
|
static void
|
|
lo_set_background_layer_bgcolor(CL_Layer *background_layer, LO_Color *new_bg_color)
|
|
{
|
|
lo_BackgroundLayerClosure *closure;
|
|
LO_Color *bg_color, *old_bg_color;
|
|
LO_ImageStruct *backdrop;
|
|
|
|
closure = (lo_BackgroundLayerClosure *)CL_GetLayerClientData(background_layer);
|
|
|
|
bg_color = NULL;
|
|
if (new_bg_color) {
|
|
bg_color = XP_NEW_ZAP(LO_Color);
|
|
if (!bg_color)
|
|
return;
|
|
*bg_color = *new_bg_color;
|
|
}
|
|
|
|
old_bg_color = closure->bg_color;
|
|
if (old_bg_color)
|
|
XP_FREE(old_bg_color);
|
|
closure->bg_color = bg_color;
|
|
backdrop = closure->backdrop_image;
|
|
|
|
/* Make layer opaque if solid color or backdrop has no transparent areas */
|
|
CL_ChangeLayerFlag(background_layer, CL_OPAQUE,
|
|
(PRBool)(new_bg_color || (backdrop && !backdrop->is_transparent)));
|
|
|
|
/* We can optimize drawing on top of this layer if it's a uniform color */
|
|
if (!backdrop)
|
|
CL_SetLayerUniformColor(background_layer, (CL_Color*)bg_color);
|
|
|
|
/* Redraw the entire layer to reflect its new color. */
|
|
{
|
|
MWContext *context = closure->context;
|
|
PRBool is_document_backdrop = (PRBool)(closure->bg_type == BG_DOCUMENT);
|
|
XP_Rect rect = CL_MAX_RECT;
|
|
|
|
if (is_document_backdrop) {
|
|
XP_ASSERT(bg_color);
|
|
if (bg_color) {
|
|
/* Prevent default doc color from overriding JS-supplied color */
|
|
lo_TopState *top_state = lo_get_layer_top_state(background_layer);
|
|
if (top_state) /* Paranoia */
|
|
top_state->doc_specified_bg = TRUE;
|
|
FE_SetBackgroundColor(context,
|
|
bg_color->red, bg_color->green, bg_color->blue);
|
|
}
|
|
}
|
|
|
|
CL_UpdateLayerRect(context->compositor, background_layer, &rect, PR_FALSE);
|
|
}
|
|
}
|
|
|
|
/* Set the background color for a layer group to the given RGB color.
|
|
If <new_bg_color> is NULL, make the layer transparent. */
|
|
void
|
|
LO_SetLayerBgColor(CL_Layer *layer, LO_Color *new_bg_color)
|
|
{
|
|
CL_Layer *background_layer;
|
|
|
|
background_layer = lo_get_group_background(layer, PR_TRUE);
|
|
if (!background_layer)
|
|
return;
|
|
|
|
lo_set_background_layer_bgcolor(background_layer, new_bg_color);
|
|
}
|
|
|
|
/* Retrieve a layer group's solid background color.
|
|
Returned LO_Color structure must not be free'd or modified. */
|
|
LO_Color *
|
|
LO_GetLayerBgColor(CL_Layer *layer)
|
|
{
|
|
lo_BackgroundLayerClosure *closure;
|
|
CL_Layer *background_layer;
|
|
|
|
background_layer = lo_get_group_background(layer, PR_FALSE);
|
|
if (!background_layer)
|
|
return NULL;
|
|
|
|
closure = (lo_BackgroundLayerClosure *)CL_GetLayerClientData(background_layer);
|
|
return closure->bg_color;
|
|
}
|
|
|
|
static LO_ImageStruct *
|
|
lo_get_background_layer_image(CL_Layer *background_layer, PRBool create)
|
|
{
|
|
lo_BackgroundLayerClosure *closure;
|
|
LO_ImageStruct *backdrop;
|
|
|
|
closure = (lo_BackgroundLayerClosure *)CL_GetLayerClientData(background_layer);
|
|
backdrop = closure->backdrop_image;
|
|
|
|
/* Create the layout image structure if it doesn't exist. */
|
|
if (! backdrop && create) {
|
|
LO_ImageAttr *image_attr;
|
|
PRBool is_cell_backdrop = (PRBool)(closure->bg_type == BG_CELL);
|
|
PRBool is_layer_backdrop = (PRBool)(closure->bg_type == BG_LAYER);
|
|
|
|
backdrop = XP_NEW_ZAP(LO_ImageStruct);
|
|
if (!backdrop)
|
|
return NULL;
|
|
|
|
image_attr = XP_NEW(LO_ImageAttr);
|
|
if (!image_attr) {
|
|
XP_FREE(backdrop);
|
|
return NULL;
|
|
}
|
|
|
|
/* Fake layout ID, guaranteed not to match any real layout element */
|
|
backdrop->ele_id = -1;
|
|
backdrop->type = LO_IMAGE;
|
|
|
|
backdrop->image_attr = image_attr;
|
|
backdrop->image_attr->attrmask = LO_ATTR_BACKDROP;
|
|
if (is_layer_backdrop)
|
|
backdrop->image_attr->attrmask |= LO_ATTR_LAYER_BACKDROP;
|
|
else if (is_cell_backdrop)
|
|
backdrop->image_attr->attrmask |= LO_ATTR_CELL_BACKDROP;
|
|
|
|
backdrop->layer = background_layer;
|
|
closure->backdrop_image = backdrop;
|
|
}
|
|
|
|
return backdrop;
|
|
}
|
|
|
|
/* Retrieve the LO_ImageStruct corresponding to a layer's backdrop,
|
|
creating it if necessary. */
|
|
LO_ImageStruct *
|
|
LO_GetLayerBackdropImage(CL_Layer *layer)
|
|
{
|
|
CL_Layer *background_layer;
|
|
|
|
background_layer = lo_get_group_background(layer, PR_TRUE);
|
|
if (!background_layer)
|
|
return NULL;
|
|
return lo_get_background_layer_image(background_layer, PR_TRUE);
|
|
}
|
|
|
|
void
|
|
LO_SetImageURL(MWContext *context,
|
|
IL_GroupContext *img_cx,
|
|
LO_ImageStruct *image,
|
|
const char *url,
|
|
NET_ReloadMethod reload_policy)
|
|
{
|
|
PRBool replace = PR_FALSE; /* True if we are replacing an
|
|
existing image. */
|
|
XP_ObserverList image_obs_list;
|
|
CL_Layer *image_layer = image->layer;
|
|
|
|
/* Clear out the resources of any previous backdrop image. */
|
|
if (image->image_url)
|
|
XP_FREE(image->image_url);
|
|
image->image_url = NULL;
|
|
if (image->image_req) {
|
|
replace = PR_TRUE;
|
|
IL_DestroyImage(image->image_req);
|
|
image->image_req = NULL;
|
|
}
|
|
image->is_icon = FALSE;
|
|
image->image_status = IL_START_URL;
|
|
|
|
/* If there is a backdrop url to load, start loading the image. */
|
|
if (url) {
|
|
image->image_url = (PA_Block)XP_STRDUP(url);
|
|
if (!image->image_url)
|
|
return;
|
|
|
|
image_obs_list = lo_NewImageObserverList(context, image);
|
|
if (!image_obs_list)
|
|
return;
|
|
|
|
|
|
if (image_layer) {
|
|
/* Is this a backdrop image ? */
|
|
if (LO_GetLayerType(image_layer) == LO_HTML_BACKGROUND_LAYER) {
|
|
|
|
/* Backdrop images are never scaled. */
|
|
image->width = image->height = 0;
|
|
|
|
/* We can't optimize drawing on top of a solid-colored
|
|
background now that we have an image backdrop. */
|
|
CL_SetLayerUniformColor(image_layer, NULL);
|
|
}
|
|
|
|
/* If we are replacing an existing image, then make the layer
|
|
transparent to minimize flicker as the new image comes in. */
|
|
if (replace) {
|
|
CL_ChangeLayerFlag(image_layer, CL_OPAQUE, PR_FALSE);
|
|
CL_ChangeLayerFlag(image_layer, CL_PREFER_DRAW_OFFSCREEN,
|
|
PR_TRUE);
|
|
}
|
|
}
|
|
|
|
/* Initiate the loading of this image. */
|
|
lo_GetImage(context, img_cx, image, image_obs_list, reload_policy);
|
|
} else if (image_layer) {
|
|
XP_Rect rect = CL_MAX_RECT;
|
|
LO_LayerType layer_type = LO_GetLayerType(image_layer);
|
|
|
|
/* No backdrop image. Make the background layer transparent,
|
|
unless the layer consists of a solid color. */
|
|
if (layer_type == LO_HTML_BACKGROUND_LAYER) {
|
|
lo_BackgroundLayerClosure *closure =
|
|
(lo_BackgroundLayerClosure *)CL_GetLayerClientData(image_layer);
|
|
|
|
CL_ChangeLayerFlag(image_layer, CL_OPAQUE,
|
|
(PRBool)(closure->bg_color != NULL));
|
|
|
|
/* Now, either the backdrop is a solid color or it's
|
|
completely transparent. */
|
|
CL_SetLayerUniformColor(image_layer, (CL_Color*)closure->bg_color);
|
|
}
|
|
|
|
/* If there is no URL, refresh the layer because it is
|
|
now transparent. */
|
|
CL_UpdateLayerRect(context->compositor, image_layer, &rect, PR_FALSE);
|
|
}
|
|
}
|
|
|
|
/* set the tile mode of a backdrop image. SetLayerBackdropURL must
|
|
* be called before this function.
|
|
*/
|
|
void
|
|
lo_SetLayerBackdropTileMode(CL_Layer *layer, lo_TileMode tile_mode)
|
|
{
|
|
lo_BackgroundLayerClosure *closure;
|
|
CL_Layer *background_layer = lo_get_group_background(layer, PR_FALSE);
|
|
if (!background_layer)
|
|
return;
|
|
closure = (lo_BackgroundLayerClosure *)CL_GetLayerClientData(background_layer);
|
|
closure->tile_mode = tile_mode;
|
|
}
|
|
|
|
/* Set the backdrop URL for a layer group.
|
|
If <url> is NULL or empty string, then clear the backdrop. */
|
|
void
|
|
LO_SetLayerBackdropURL(CL_Layer *layer, const char *url)
|
|
{
|
|
LO_ImageStruct *image = LO_GetLayerBackdropImage(layer);
|
|
MWContext *context = lo_get_layer_context(layer);
|
|
lo_TopState *top_state = lo_get_layer_top_state(layer);
|
|
IL_GroupContext *img_cx = context->img_cx;
|
|
|
|
if (url && *url) {
|
|
url = NET_MakeAbsoluteURL(top_state->base_url, (char*)url);
|
|
LO_SetImageURL(context, img_cx, image, url, top_state->force_reload);
|
|
} else
|
|
LO_SetImageURL(context, img_cx, image, NULL, top_state->force_reload);
|
|
}
|
|
|
|
/* Get the backdrop URL for a layer group. Return NULL if their is no
|
|
backdrop URL. */
|
|
const char *
|
|
LO_GetLayerBackdropURL(CL_Layer *layer)
|
|
{
|
|
CL_Layer *background_layer;
|
|
LO_ImageStruct *backdrop;
|
|
|
|
background_layer = lo_get_group_background(layer, PR_FALSE);
|
|
if (!background_layer)
|
|
return NULL;
|
|
backdrop = lo_get_background_layer_image(background_layer, PR_FALSE);
|
|
if (!backdrop)
|
|
return NULL;
|
|
|
|
return (const char *)backdrop->image_url;
|
|
}
|
|
|
|
void
|
|
lo_OffsetInflowLayer(CL_Layer *layer, int32 dx, int32 dy)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
lo_HTMLBlockClosure *content_closure;
|
|
CL_Layer *content_layer;
|
|
|
|
CL_OffsetLayer(layer, dx, dy);
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
content_layer = closure->content_layer;
|
|
if (content_layer) {
|
|
content_closure =
|
|
(lo_HTMLBlockClosure *)CL_GetLayerClientData(content_layer);
|
|
content_closure->x_offset += dx;
|
|
content_closure->y_offset += dy;
|
|
}
|
|
|
|
closure->x_offset += dx;
|
|
closure->y_offset += dy;
|
|
}
|
|
|
|
LO_CellStruct *
|
|
lo_GetCellFromLayer(MWContext *context, CL_Layer *layer)
|
|
{
|
|
lo_GroupLayerClosure *closure;
|
|
|
|
if (!layer) /* Paranoia */
|
|
return NULL;
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(layer);
|
|
if (closure->type != LO_GROUP_LAYER)
|
|
return NULL;
|
|
if (!closure->layer_state) /* This must be the BODY layer */
|
|
return NULL;
|
|
return closure->layer_state->cell;
|
|
}
|
|
|
|
|
|
/**********************
|
|
*
|
|
* Cell background layering code
|
|
*
|
|
**********************/
|
|
|
|
static void
|
|
lo_cellbg_destroy_func(CL_Layer *layer)
|
|
{
|
|
lo_BackgroundLayerClosure *closure;
|
|
closure = (lo_BackgroundLayerClosure *)CL_GetLayerClientData(layer);
|
|
/*
|
|
* NULL out the corresponding cell's pointer to this layer so that
|
|
* there won't be double destruction. We assume that if the cell
|
|
* is destroyed first, it will also destroy this layer.
|
|
*/
|
|
if (closure->cell)
|
|
closure->cell->cell_bg_layer = NULL;
|
|
lo_background_destroy_func(layer);
|
|
}
|
|
|
|
/* Cell backgrounds are almost identical to regular backgrounds,
|
|
* except that they are of finite size and they don't do any event
|
|
* handling.
|
|
*/
|
|
CL_Layer *
|
|
lo_CreateCellBackgroundLayer(MWContext *context, LO_CellStruct *cell,
|
|
CL_Layer *parent_layer, int16 table_nesting_level)
|
|
{
|
|
CL_Layer *cellbg_layer;
|
|
XP_Rect bbox;
|
|
lo_BackgroundLayerClosure *closure;
|
|
CL_LayerVTable vtable;
|
|
int32 layer_x_offset, layer_y_offset;
|
|
int32 parent_x_shift, parent_y_shift;
|
|
int32 z_order;
|
|
|
|
|
|
|
|
/* The layer is the size of the cell */
|
|
bbox.left = 0;
|
|
bbox.top = 0;
|
|
bbox.right = cell->width;
|
|
bbox.bottom = cell->height;
|
|
|
|
lo_GetLayerXYShift(parent_layer, &parent_x_shift, &parent_y_shift);
|
|
|
|
layer_x_offset = cell->x + cell->x_offset - parent_x_shift;
|
|
layer_y_offset = cell->y + cell->y_offset - parent_y_shift;
|
|
|
|
/* Create the client_data */
|
|
closure = XP_NEW_ZAP(lo_BackgroundLayerClosure);
|
|
closure->type = LO_HTML_BACKGROUND_LAYER;
|
|
closure->context = context;
|
|
closure->bg_type = BG_CELL;
|
|
closure->tile_mode = cell->backdrop.tile_mode;
|
|
closure->cell = cell;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.painter_func = lo_background_painter_func;
|
|
vtable.destroy_func = lo_cellbg_destroy_func;
|
|
|
|
cellbg_layer = CL_NewLayer(NULL, layer_x_offset, layer_y_offset,
|
|
&bbox, &vtable,
|
|
CL_HIDDEN | CL_DONT_ENUMERATE,
|
|
(void *)closure);
|
|
|
|
if (!cellbg_layer)
|
|
return NULL;
|
|
|
|
/* The z-order of the background layer of cells should increase
|
|
as the cell's nest within tables. This ensures that the innermost
|
|
cell's background ends up with the highest z-order and gets
|
|
displayed
|
|
*/
|
|
z_order = Z_CELL_BACKGROUND_LAYER + table_nesting_level;
|
|
CL_InsertChildByZ(parent_layer, cellbg_layer, z_order);
|
|
|
|
/* Start loading tiled cell backdrop image, if present */
|
|
if (cell->backdrop.url && *cell->backdrop.url) {
|
|
LO_ImageStruct *image;
|
|
|
|
image = lo_get_background_layer_image(cellbg_layer, PR_TRUE);
|
|
if (image) {
|
|
lo_TopState *top_state = lo_get_layer_top_state(parent_layer);
|
|
char *url = NET_MakeAbsoluteURL(top_state->base_url, cell->backdrop.url);
|
|
if (url) {
|
|
XP_FREE(cell->backdrop.url);
|
|
cell->backdrop.url = url;
|
|
/* Note: In the case of nested tables with background
|
|
images, the image request is destroyed before the table
|
|
relayout and will not exist in the cache. Changing reload
|
|
policy if it is NET_CACHE_ONLY_RELOAD to NET_DONT_RELOAD
|
|
allows loading the image again from a file if it has
|
|
been removed from the cache.
|
|
|
|
Trying to use the trash list would cause memory leaks
|
|
as the trash list removes items from the mem arena. Allocating
|
|
the image_request by taking it from the arena (instead of
|
|
explicitly allocating it) could cause problems in
|
|
the editor code.
|
|
*/
|
|
LO_SetImageURL(context, context->img_cx, image, url,
|
|
((top_state->force_reload == NET_CACHE_ONLY_RELOAD)?
|
|
NET_DONT_RELOAD : top_state->force_reload));
|
|
XP_FREE(url);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set cell background color, if the cell isn't transparent. */
|
|
if (cell->backdrop.bg_color) {
|
|
if (UserOverride)
|
|
lo_set_background_layer_bgcolor(cellbg_layer, &lo_master_colors[LO_COLOR_BG]);
|
|
else
|
|
lo_set_background_layer_bgcolor(cellbg_layer, cell->backdrop.bg_color);
|
|
}
|
|
|
|
return cellbg_layer;
|
|
}
|
|
|
|
/* Create a block layer - a layer created by a HTML <LAYER> tag */
|
|
CL_Layer *
|
|
lo_CreateBlockLayer(MWContext *context,
|
|
char *name,
|
|
PRBool is_inflow,
|
|
int32 x_offset, int32 y_offset,
|
|
int32 wrap_width,
|
|
lo_LayerDocState *layer_state,
|
|
lo_DocState *state)
|
|
{
|
|
CL_Layer *content_layer, *block_layer;
|
|
lo_GroupLayerClosure *closure;
|
|
|
|
content_layer = lo_CreateBlockContentLayer(context, is_inflow,
|
|
x_offset, y_offset,
|
|
layer_state, state);
|
|
|
|
if (! content_layer)
|
|
return NULL;
|
|
|
|
block_layer = lo_CreateGroupLayer(context, name,
|
|
x_offset, y_offset,
|
|
TRUE, TRUE, content_layer,
|
|
layer_state,
|
|
|
|
NULL,
|
|
is_inflow ?
|
|
lo_html_inflow_layer_event_handler_func :
|
|
lo_html_event_handler_func,
|
|
|
|
lo_html_block_destroy_func);
|
|
|
|
|
|
if (! block_layer) {
|
|
CL_DestroyLayer(content_layer);
|
|
return NULL;
|
|
}
|
|
|
|
closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(block_layer);
|
|
closure->wrap_width = wrap_width;
|
|
closure->is_inflow = is_inflow;
|
|
|
|
return block_layer;
|
|
}
|
|
|
|
|
|
/* Create the _CONTENT child of the _BODY layer */
|
|
static CL_Layer *
|
|
lo_CreateBodyContentLayer(MWContext *context)
|
|
{
|
|
CL_Layer *layer;
|
|
XP_Rect bbox;
|
|
CL_LayerVTable vtable;
|
|
lo_HTMLBodyClosure *closure;
|
|
|
|
uint32 flags = CL_DONT_ENUMERATE | CL_DONT_CLIP_SELF;
|
|
|
|
bbox.left = 0;
|
|
bbox.top = 0;
|
|
bbox.right = 0;
|
|
bbox.bottom = 0;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.painter_func = lo_body_painter_func;
|
|
vtable.destroy_func = lo_html_destroy_func;
|
|
|
|
closure = XP_NEW_ZAP(lo_HTMLBodyClosure);
|
|
closure->type = LO_HTML_BODY_LAYER;
|
|
closure->context = context;
|
|
|
|
layer = CL_NewLayer(LO_CONTENT_LAYER_NAME, 0, 0, &bbox, &vtable,
|
|
flags, (void *)closure);
|
|
closure->layer = layer;
|
|
|
|
return layer;
|
|
}
|
|
|
|
|
|
/*
|
|
* Creates the HTML body layer
|
|
*/
|
|
static CL_Layer *
|
|
lo_CreateHTMLBodyLayer(MWContext *context)
|
|
{
|
|
CL_Layer *content_layer, *body_layer;
|
|
|
|
/* First create the _CONTENT part of the _BODY layer */
|
|
content_layer = lo_CreateBodyContentLayer(context);
|
|
|
|
/*
|
|
* Now create the abstract _BODY layer. The _CONTENT layer
|
|
* is a child of this layer, but there's no background.
|
|
*/
|
|
body_layer = lo_CreateGroupLayer(context, LO_BODY_LAYER_NAME,
|
|
0, 0,
|
|
FALSE, TRUE, content_layer,
|
|
NULL,
|
|
|
|
body_bbox_changed_func,
|
|
lo_html_event_handler_func,
|
|
|
|
lo_html_destroy_func);
|
|
|
|
CL_ChangeLayerFlag(body_layer, CL_HIDDEN, PR_FALSE);
|
|
return body_layer;
|
|
}
|
|
|
|
void
|
|
LO_SetDocumentDimensions(MWContext *context, int32 width, int32 height)
|
|
{
|
|
XP_Rect bbox;
|
|
int32 current_width, current_height;
|
|
|
|
lo_TopState *top_state = lo_FetchTopState(XP_DOCID(context));
|
|
if (!top_state)
|
|
return;
|
|
|
|
/* If layering is enabled, set the size of the _BODY layer. That
|
|
will enlarge/shrink the _DOCUMENT layer as necessary, and call
|
|
FE_SetDocDimension if the document layer changes size. */
|
|
if (context->compositor) {
|
|
bbox.left = bbox.top = 0;
|
|
bbox.right = width;
|
|
bbox.bottom = height;
|
|
CL_SetLayerBbox(top_state->body_layer, &bbox);
|
|
|
|
/* The document layer is always at least as large as the body
|
|
layer, so if the body expands beyond the document's size,
|
|
tell the FE about it immediately, rather than waiting for
|
|
it to occur as a callback from the timing-driven composite
|
|
cycle. That avoids race conditions in which
|
|
FE_SetDocPosition() is called before FE_SetDocDimension()
|
|
has specified a sufficiently large document to encompass
|
|
the new document position. */
|
|
current_width = LO_GetLayerScrollWidth(top_state->doc_layer);
|
|
current_height = LO_GetLayerScrollHeight(top_state->doc_layer);
|
|
|
|
if ((width > current_width) || (height > current_height) || (width == 0) || (height == 0))
|
|
doc_bbox_changed_func(top_state->doc_layer, &bbox);
|
|
} else {
|
|
FE_SetDocDimension(context, FE_VIEW, width, height);
|
|
}
|
|
}
|
|
|
|
/* Creates the _DOCUMENT, _BODY and _BACKGROUND layers and
|
|
adds them to the layer tree */
|
|
void
|
|
lo_CreateDefaultLayers(MWContext *context,
|
|
CL_Layer **doc_layer,
|
|
CL_Layer **body_layer)
|
|
{
|
|
*doc_layer = NULL;
|
|
|
|
*body_layer = lo_CreateHTMLBodyLayer(context);
|
|
if (!*body_layer)
|
|
return;
|
|
|
|
*doc_layer = lo_CreateGroupLayer(context, LO_DOCUMENT_LAYER_NAME,
|
|
0, 0, FALSE, FALSE,
|
|
*body_layer,
|
|
NULL,
|
|
|
|
doc_bbox_changed_func, NULL,
|
|
|
|
lo_html_destroy_func);
|
|
|
|
|
|
if (!*doc_layer)
|
|
return;
|
|
|
|
CL_ChangeLayerFlag(*doc_layer, CL_HIDDEN, PR_FALSE);
|
|
if (context->compositor)
|
|
CL_SetCompositorRoot(context->compositor, *doc_layer);
|
|
}
|
|
|
|
/* Attaches the new layer into the layer structure */
|
|
void
|
|
lo_AttachHTMLLayer(MWContext *context, CL_Layer *layer, CL_Layer *parent,
|
|
char *above, char *below, int32 z_order)
|
|
{
|
|
CL_Layer *sibling;
|
|
CL_LayerPosition pos;
|
|
|
|
if (parent != NULL)
|
|
{
|
|
if (z_order >= 0)
|
|
{
|
|
CL_InsertChildByZ(parent, layer, z_order);
|
|
}
|
|
else
|
|
{
|
|
sibling = NULL;
|
|
if (above != NULL)
|
|
{
|
|
pos = CL_BELOW;
|
|
sibling = CL_GetLayerChildByName(parent, above);
|
|
}
|
|
if ((sibling == NULL)&&(below != NULL))
|
|
{
|
|
pos = CL_ABOVE;
|
|
sibling = CL_GetLayerChildByName(parent, below);
|
|
}
|
|
if (sibling == NULL)
|
|
{
|
|
pos = CL_ABOVE;
|
|
}
|
|
CL_InsertChild(parent, layer, sibling, pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Destroy all HTML-created layers */
|
|
void
|
|
lo_DestroyLayers(MWContext *context)
|
|
{
|
|
CL_DestroyLayerTree(CL_GetCompositorRoot(context->compositor));
|
|
CL_SetCompositorRoot(context->compositor, NULL);
|
|
}
|
|
|
|
LO_LayerType
|
|
LO_GetLayerType(CL_Layer *layer)
|
|
{
|
|
lo_AnyLayerClosure *closure = (lo_AnyLayerClosure *)CL_GetLayerClientData(layer);
|
|
|
|
return closure->type;
|
|
}
|
|
|
|
/* Push a layer onto the document's layer stack */
|
|
void
|
|
lo_PushLayerState(lo_TopState *top_state, lo_LayerDocState *layer_state)
|
|
{
|
|
lo_LayerStack *lptr;
|
|
|
|
lptr = XP_NEW(lo_LayerStack);
|
|
if (lptr == NULL)
|
|
{
|
|
return;
|
|
}
|
|
lptr->layer_state = layer_state;
|
|
lptr->next = top_state->layer_stack;
|
|
top_state->layer_stack = lptr;
|
|
}
|
|
|
|
/* Pop a layer off the document's layer stack */
|
|
lo_LayerDocState *
|
|
lo_PopLayerState(lo_DocState *state)
|
|
{
|
|
lo_LayerStack *lptr;
|
|
lo_LayerDocState *layer_state;
|
|
lo_TopState *top_state = state->top_state;
|
|
|
|
if (top_state->layer_stack == NULL)
|
|
return NULL;
|
|
|
|
lptr = top_state->layer_stack;
|
|
layer_state = lptr->layer_state;
|
|
top_state->layer_stack = lptr->next;
|
|
|
|
XP_DELETE(lptr);
|
|
|
|
return layer_state;
|
|
}
|
|
|
|
/* Look at the top layer on the document's layer stack, without popping it */
|
|
lo_LayerDocState *
|
|
lo_CurrentLayerState(lo_DocState *state)
|
|
{
|
|
lo_LayerDocState *layer_state;
|
|
lo_TopState *top_state = state->top_state;
|
|
if (top_state->layer_stack == NULL)
|
|
return NULL;
|
|
layer_state = top_state->layer_stack->layer_state;
|
|
return layer_state;
|
|
}
|
|
|
|
int32
|
|
lo_CurrentLayerId(lo_DocState *state)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
return layer_state->id;
|
|
}
|
|
|
|
CL_Layer *
|
|
lo_CurrentLayer(lo_DocState *state)
|
|
{
|
|
lo_LayerDocState *layer_state = lo_CurrentLayerState(state);
|
|
return layer_state->layer;
|
|
}
|
|
|
|
void
|
|
lo_DeleteLayerStack(lo_DocState *state)
|
|
{
|
|
lo_LayerStack *lptr;
|
|
lo_LayerStack *prev;
|
|
lo_TopState *top_state = state->top_state;
|
|
|
|
lptr = top_state->layer_stack;
|
|
while (lptr != NULL) {
|
|
prev = lptr;
|
|
lptr = lptr->next;
|
|
XP_DELETE(prev);
|
|
}
|
|
top_state->layer_stack = NULL;
|
|
}
|
|
|
|
/**********************
|
|
*
|
|
* Embedded object layering code
|
|
*
|
|
**********************/
|
|
|
|
|
|
/* Redraw an embedded object window because its position or visibility may
|
|
have been altered. */
|
|
static void
|
|
lo_update_embedded_object_window(CL_Layer *layer)
|
|
{
|
|
int32 x_save, y_save;
|
|
LO_Any *any;
|
|
MWContext *context;
|
|
lo_EmbeddedObjectClosure *closure;
|
|
LO_Element *tptr;
|
|
XP_Rect rect;
|
|
CL_Compositor *compositor = CL_GetLayerCompositor(layer);
|
|
|
|
closure = (lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure);
|
|
if (! closure)
|
|
return;
|
|
|
|
/* If embedded object is not (yet) a window. There's
|
|
nothing to do. */
|
|
if (!closure->is_windowed)
|
|
return;
|
|
|
|
context = closure->context;
|
|
|
|
tptr = closure->element;
|
|
any = &tptr->lo_any;
|
|
XP_ASSERT(any);
|
|
|
|
/* Convert layer-relative coordinates for element to document
|
|
coordinates, since that's what the FE uses. */
|
|
rect.top = rect.left = rect.right = rect.bottom = 0;
|
|
CL_LayerToWindowRect(compositor, layer, &rect);
|
|
CL_WindowToDocumentRect(compositor, &rect);
|
|
|
|
/* Save old, layer-relative coordinates */
|
|
x_save = any->x;
|
|
y_save = any->y;
|
|
|
|
/* Temporarily shift element to document coordinates */
|
|
any->x = rect.left - any->x_offset;
|
|
any->y = rect.top - any->y_offset;
|
|
|
|
/* Call the platform-specific display code. */
|
|
switch (tptr->type) {
|
|
case LO_FORM_ELE:
|
|
any->x -= tptr->lo_form.border_horiz_space;
|
|
any->y -= tptr->lo_form.border_vert_space;
|
|
FE_DisplayFormElement(context, FE_VIEW, &tptr->lo_form);
|
|
break;
|
|
case LO_EMBED:
|
|
FE_DisplayEmbed(context, FE_VIEW, &tptr->lo_embed);
|
|
break;
|
|
case LO_BUILTIN:
|
|
FE_DisplayBuiltin(context, FE_VIEW, &tptr->lo_builtin);
|
|
break;
|
|
#ifdef JAVA
|
|
case LO_JAVA:
|
|
FE_DisplayJavaApp(context, FE_VIEW, &tptr->lo_java);
|
|
break;
|
|
#endif
|
|
default:
|
|
XP_ASSERT(0);
|
|
}
|
|
|
|
/* Restore layer-relative coordinates */
|
|
any->x = x_save;
|
|
any->y = y_save;
|
|
}
|
|
|
|
#ifdef XP_UNIX
|
|
|
|
/* Same as FE_DisplayFormElement, except coordinates converted
|
|
from layer-relative to document-relative to satisfy FE
|
|
expectations. */
|
|
void
|
|
LO_DisplayFormElement(LO_FormElementStruct *form)
|
|
{
|
|
XP_ASSERT(form->layer);
|
|
if (!form->layer)
|
|
return;
|
|
lo_update_embedded_object_window(form->layer);
|
|
}
|
|
|
|
#endif /* XP_UNIX */
|
|
|
|
static void
|
|
lo_window_layer_visibility_changed(CL_Layer *layer,
|
|
PRBool visible)
|
|
{
|
|
lo_EmbeddedObjectClosure *closure;
|
|
LO_Element *tptr;
|
|
|
|
closure = (lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure);
|
|
|
|
tptr = closure->element;
|
|
XP_ASSERT(tptr);
|
|
|
|
switch (tptr->type) {
|
|
case LO_FORM_ELE:
|
|
tptr->lo_form.ele_attrmask &= ~LO_ELE_INVISIBLE;
|
|
tptr->lo_form.ele_attrmask |= LO_ELE_INVISIBLE * !visible;
|
|
break;
|
|
case LO_EMBED:
|
|
tptr->lo_embed.objTag.ele_attrmask &= ~LO_ELE_INVISIBLE;
|
|
tptr->lo_embed.objTag.ele_attrmask |= LO_ELE_INVISIBLE * !visible;
|
|
break;
|
|
case LO_BUILTIN:
|
|
tptr->lo_builtin.ele_attrmask &= ~LO_ELE_INVISIBLE;
|
|
tptr->lo_builtin.ele_attrmask |= LO_ELE_INVISIBLE * !visible;
|
|
break;
|
|
#ifdef JAVA
|
|
case LO_JAVA:
|
|
tptr->lo_java.objTag.ele_attrmask &= ~LO_ELE_INVISIBLE;
|
|
tptr->lo_java.objTag.ele_attrmask |= LO_ELE_INVISIBLE * !visible;
|
|
break;
|
|
#endif
|
|
default:
|
|
XP_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
lo_update_embedded_object_window(layer);
|
|
}
|
|
|
|
static void
|
|
lo_window_layer_position_changed(CL_Layer *layer,
|
|
int32 x_offset, int32 y_offset)
|
|
{
|
|
lo_update_embedded_object_window(layer);
|
|
}
|
|
|
|
/* Only set if we're a print context */
|
|
static void
|
|
lo_embedded_object_painter_func(CL_Drawable *drawable,
|
|
CL_Layer *layer,
|
|
FE_Region update_region)
|
|
{
|
|
lo_EmbeddedObjectClosure *closure =
|
|
(lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
|
|
FE_SetDrawable(closure->context, drawable);
|
|
|
|
lo_update_embedded_object_window(layer);
|
|
|
|
FE_SetDrawable(closure->context, NULL);
|
|
}
|
|
|
|
/* Create a child layer to contain an embedded object, e.g. a plugin,
|
|
applet or form widget. The child layer may be created as a "cutout"
|
|
layer if the embedded object is a window, to prevent the compositor
|
|
from overdrawing the window's contents. */
|
|
CL_Layer *
|
|
lo_CreateEmbeddedObjectLayer(MWContext *context,
|
|
lo_DocState *state, LO_Element *tptr)
|
|
{
|
|
PRBool is_window;
|
|
LO_Any *any = &tptr->lo_any;
|
|
CL_Layer *layer, *parent_layer, *doc_layer;
|
|
char *name;
|
|
XP_Rect bbox;
|
|
lo_EmbeddedObjectClosure *closure;
|
|
CL_LayerVTable vtable;
|
|
int vspace, hspace;
|
|
int32 layer_x_offset, layer_y_offset;
|
|
|
|
/* Layer is initially hidden, until lo_DisplayWhatever is called. */
|
|
uint32 flags = CL_DONT_ENUMERATE | CL_HIDDEN;
|
|
|
|
if (! context->compositor)
|
|
return NULL;
|
|
|
|
/* These types of elements aren't truly layers (they're embedded
|
|
windows), so we can't hide them directly through the layer API.
|
|
We need to set a flag so that the FE knows not to draw the
|
|
window initially. */
|
|
switch (tptr->type) {
|
|
case LO_FORM_ELE:
|
|
name = "_FORM_ELEMENT";
|
|
tptr->lo_form.ele_attrmask |= LO_ELE_INVISIBLE;
|
|
vspace = tptr->lo_form.border_vert_space;
|
|
hspace = tptr->lo_form.border_horiz_space;
|
|
is_window = PR_TRUE;
|
|
break;
|
|
case LO_EMBED:
|
|
name = "_PLUGIN";
|
|
tptr->lo_embed.objTag.ele_attrmask |= LO_ELE_INVISIBLE;
|
|
/* We don't commit to the type of a plugin layer until
|
|
it's known whether or not the plugin is windowless. */
|
|
vspace = tptr->lo_embed.objTag.border_vert_space;
|
|
hspace = tptr->lo_embed.objTag.border_horiz_space;
|
|
is_window = PR_FALSE;
|
|
break;
|
|
case LO_BUILTIN:
|
|
name = "_BUILTIN";
|
|
tptr->lo_builtin.ele_attrmask |= LO_ELE_INVISIBLE;
|
|
vspace = tptr->lo_builtin.border_vert_space;
|
|
hspace = tptr->lo_builtin.border_horiz_space;
|
|
is_window = PR_TRUE;
|
|
break;
|
|
#ifdef JAVA
|
|
case LO_JAVA:
|
|
name = "_JAVA_APPLET";
|
|
tptr->lo_java.objTag.ele_attrmask |= LO_ELE_INVISIBLE;
|
|
vspace = tptr->lo_java.objTag.border_vert_space;
|
|
hspace = tptr->lo_java.objTag.border_horiz_space;
|
|
is_window = PR_TRUE;
|
|
break;
|
|
#endif
|
|
default:
|
|
XP_ASSERT(0);
|
|
return NULL;
|
|
}
|
|
|
|
/* The layer is the size of the layout element.
|
|
(For a plugin, this may not *really* reflect the size of the element,
|
|
but it will get resized later.) */
|
|
layer_x_offset = any->x + any->x_offset + hspace;
|
|
layer_y_offset = any->y + any->y_offset + vspace;
|
|
|
|
bbox.left = 0;
|
|
bbox.top = 0;
|
|
bbox.right = any->width - 2 * hspace;
|
|
bbox.bottom = any->height - 2 * vspace;
|
|
|
|
closure = XP_NEW_ZAP(lo_EmbeddedObjectClosure);
|
|
closure->type = LO_EMBEDDED_OBJECT_LAYER;
|
|
|
|
closure->context = context;
|
|
closure->element = tptr;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
|
|
if (is_window) {
|
|
if (context->type != MWContextPrint)
|
|
flags |= CL_CUTOUT;
|
|
closure->is_windowed = PR_TRUE;
|
|
}
|
|
|
|
vtable.visibility_changed_func = lo_window_layer_visibility_changed;
|
|
vtable.position_changed_func = lo_window_layer_position_changed;
|
|
vtable.destroy_func = (CL_DestroyFunc)lo_html_destroy_func;
|
|
if (context->type == MWContextPrint)
|
|
vtable.painter_func = (CL_PainterFunc)lo_embedded_object_painter_func;
|
|
|
|
layer = CL_NewLayer(name, layer_x_offset, layer_y_offset,
|
|
&bbox, &vtable, flags, (void *)closure);
|
|
|
|
/*
|
|
* XXX I don't really like this. For embeds that
|
|
* are part of the _BASE layer, we need to special case
|
|
* the parent layer of the embed layer, since
|
|
* the _DOCUMENT layer (and not the _BASE layer) is on
|
|
* the layer stack.
|
|
* This code exists for cell bg creation, too.
|
|
*/
|
|
doc_layer = CL_GetCompositorRoot(context->compositor);
|
|
parent_layer = lo_CurrentLayer(state);
|
|
if (parent_layer == doc_layer) {
|
|
lo_TopState *top_state = lo_get_layer_top_state(layer);
|
|
parent_layer = top_state->body_layer;
|
|
}
|
|
|
|
XP_ASSERT(parent_layer);
|
|
|
|
if (parent_layer)
|
|
CL_InsertChildByZ(parent_layer, layer, Z_CONTENT_LAYERS);
|
|
|
|
return layer;
|
|
}
|
|
|
|
/**********************
|
|
*
|
|
* Java Applet layer code
|
|
*
|
|
**********************/
|
|
|
|
#ifdef TRANSPARENT_APPLET
|
|
#ifdef JAVA
|
|
|
|
#include "java.h"
|
|
|
|
static void
|
|
lo_java_painter_func(CL_Drawable *drawable, CL_Layer *layer, FE_Region update_region)
|
|
{
|
|
lo_EmbeddedObjectClosure *closure;
|
|
LO_JavaAppStruct *javaData;
|
|
|
|
closure = (lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
javaData = (LO_JavaAppStruct*)closure->element;
|
|
|
|
FE_SetDrawable(closure->context, drawable);
|
|
FE_DrawJavaApp(closure->context, FE_VIEW, javaData);
|
|
FE_SetDrawable(closure->context, NULL);
|
|
}
|
|
|
|
static PRBool
|
|
lo_java_event_handler_func(CL_Layer *layer, CL_Event *event)
|
|
{
|
|
NAppletEvent evt;
|
|
lo_EmbeddedObjectClosure *closure =
|
|
(lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
|
|
evt.id = 0;
|
|
evt.data = (void*)event;
|
|
return LJ_HandleEvent(closure->context,
|
|
(LO_JavaAppStruct*)closure->element,
|
|
(void*)&evt);
|
|
}
|
|
|
|
|
|
/* Modify the way in which a plugin's layer draws depending on whether
|
|
or not it is a windowless plugin. */
|
|
void
|
|
LO_SetJavaAppTransparent(LO_JavaAppStruct *javaData)
|
|
{
|
|
XP_Rect bbox;
|
|
CL_LayerVTable vtable;
|
|
lo_EmbeddedObjectClosure *closure;
|
|
CL_Layer *layer = javaData->layer;
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return;
|
|
|
|
closure = (lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure->type == LO_EMBEDDED_OBJECT_LAYER);
|
|
closure->is_windowed = PR_FALSE;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.destroy_func = (CL_DestroyFunc)lo_html_destroy_func;
|
|
|
|
/* Windowless plugin */
|
|
vtable.painter_func = lo_java_painter_func;
|
|
vtable.event_handler_func = lo_java_event_handler_func;
|
|
|
|
CL_SetLayerVTable(layer, &vtable);
|
|
CL_ChangeLayerFlag(layer, CL_CUTOUT, PR_FALSE);
|
|
|
|
/* At this point, the size of the plugin is known. */
|
|
bbox.left = 0;
|
|
bbox.top = 0;
|
|
bbox.right = javaData->width;
|
|
bbox.bottom = javaData->height;
|
|
CL_SetLayerBbox(layer, &bbox);
|
|
}
|
|
|
|
#endif /* JAVA */
|
|
#endif /* TRANSPARENT_APPLET */
|
|
|
|
/**********************
|
|
*
|
|
* Plugin layer code
|
|
*
|
|
**********************/
|
|
|
|
static void
|
|
lo_embed_painter_func(CL_Drawable *drawable, CL_Layer *layer,
|
|
FE_Region update_region)
|
|
{
|
|
lo_EmbeddedObjectClosure *closure;
|
|
LO_EmbedStruct *embed;
|
|
|
|
closure = (lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
embed = (LO_EmbedStruct*)closure->element;
|
|
|
|
FE_SetDrawable(closure->context, drawable);
|
|
FE_DisplayEmbed(closure->context, FE_VIEW, embed);
|
|
FE_SetDrawable(closure->context, NULL);
|
|
}
|
|
|
|
static PRBool
|
|
lo_windowless_embed_event_handler_func(CL_Layer *layer, CL_Event *event)
|
|
{
|
|
lo_EmbeddedObjectClosure *closure =
|
|
(lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
|
|
return FE_HandleEmbedEvent(closure->context,
|
|
(LO_EmbedStruct*)closure->element,
|
|
event);
|
|
}
|
|
|
|
|
|
/* Modify the way in which a plugin's layer draws depending on whether
|
|
or not it is a windowless plugin. */
|
|
void
|
|
LO_SetEmbedType(LO_EmbedStruct *embed, PRBool is_windowed)
|
|
{
|
|
XP_Rect bbox;
|
|
CL_LayerVTable vtable;
|
|
lo_EmbeddedObjectClosure *closure;
|
|
CL_Layer *layer = embed->objTag.layer;
|
|
XP_ASSERT(layer);
|
|
if (! layer)
|
|
return;
|
|
|
|
closure = (lo_EmbeddedObjectClosure *)CL_GetLayerClientData(layer);
|
|
XP_ASSERT(closure->type == LO_EMBEDDED_OBJECT_LAYER);
|
|
closure->is_windowed = is_windowed;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.destroy_func = (CL_DestroyFunc)lo_html_destroy_func;
|
|
if (is_windowed) {
|
|
/* Windowed plugin */
|
|
vtable.visibility_changed_func = lo_window_layer_visibility_changed;
|
|
vtable.position_changed_func = lo_window_layer_position_changed;
|
|
if (closure->context->type == MWContextPrint)
|
|
vtable.painter_func = (CL_PainterFunc)lo_embedded_object_painter_func;
|
|
} else {
|
|
/* Windowless plugin */
|
|
vtable.painter_func = lo_embed_painter_func;
|
|
vtable.event_handler_func = lo_windowless_embed_event_handler_func;
|
|
CL_ChangeLayerFlag(layer, CL_PREFER_DRAW_OFFSCREEN,
|
|
(PRBool)((CL_GetLayerFlags(layer) & CL_OPAQUE) == 0));
|
|
}
|
|
CL_SetLayerVTable(layer, &vtable);
|
|
if (closure->context->type != MWContextPrint)
|
|
CL_ChangeLayerFlag(layer, CL_CUTOUT, is_windowed);
|
|
|
|
/* At this point, the size of the plugin is known. */
|
|
bbox.left = 0;
|
|
bbox.top = 0;
|
|
bbox.right = embed->objTag.width;
|
|
bbox.bottom = embed->objTag.height;
|
|
CL_SetLayerBbox(layer, &bbox);
|
|
|
|
if (is_windowed && !CL_GetLayerHidden(layer))
|
|
lo_update_embedded_object_window(layer);
|
|
}
|
|
|
|
|
|
/**********************
|
|
*
|
|
* Image layering code
|
|
*
|
|
**********************/
|
|
|
|
#define ICON_X_OFFSET 4
|
|
#define ICON_Y_OFFSET 4
|
|
|
|
typedef struct lo_ImageRectStruct
|
|
{
|
|
lo_ImageLayerClosure *closure;
|
|
CL_Layer *layer;
|
|
} lo_ImageRectStruct;
|
|
|
|
|
|
/* Create a text element to be used to display ALT text. */
|
|
static LO_TextStruct *
|
|
lo_AltTextElement(MWContext *context)
|
|
{
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
LO_TextStruct *text;
|
|
|
|
/*
|
|
* Get the unique document ID, and retrieve this
|
|
* documents layout state.
|
|
*/
|
|
top_state = lo_FetchTopState(XP_DOCID(context));
|
|
if ((top_state == NULL) || (top_state->doc_state == NULL)) {
|
|
return NULL;
|
|
}
|
|
state = lo_CurrentSubState(top_state->doc_state);
|
|
|
|
text = XP_NEW_ZAP(LO_TextStruct);
|
|
if (!text)
|
|
return NULL;
|
|
text->text_attr = XP_NEW_ZAP(LO_TextAttr);
|
|
if (!text->text_attr)
|
|
return NULL;
|
|
|
|
lo_SetDefaultFontAttr(state, text->text_attr, context);
|
|
|
|
return text;
|
|
}
|
|
|
|
/* Draw an icon, ALT text and a temporary border. */
|
|
static void
|
|
lo_ImageDelayed(MWContext *context, LO_ImageStruct *image, int icon_number, int x, int y)
|
|
{
|
|
int icon_width, icon_height;
|
|
int x_offset, y_offset;
|
|
PRBool drew_icon = PR_FALSE;
|
|
LO_Color color;
|
|
LO_TextStruct *text;
|
|
IL_GroupContext *img_cx;
|
|
|
|
/* Offsets for drawing the icon and ALT text. */
|
|
x_offset = ICON_X_OFFSET;
|
|
y_offset = ICON_Y_OFFSET;
|
|
|
|
/* Draw the delayed icon. */
|
|
img_cx = context->img_cx;
|
|
IL_GetIconDimensions(img_cx, icon_number, &icon_width, &icon_height);
|
|
if (x_offset + icon_width <= image->width &&
|
|
y_offset + icon_height <= image->height) {
|
|
IL_DisplayIcon(img_cx, icon_number, x + x_offset, y + y_offset);
|
|
drew_icon = PR_TRUE;
|
|
}
|
|
|
|
/* Draw the ALT text. */
|
|
if (image->alt) {
|
|
text = lo_AltTextElement(context);
|
|
if (text) {
|
|
XP_Rect frame;
|
|
int total_width, total_height, text_width, text_height;
|
|
LO_TextInfo text_info;
|
|
|
|
text->text = image->alt;
|
|
text->text_len = image->alt_len;
|
|
FE_GetTextInfo(context, text, &text_info);
|
|
|
|
text->width = text_info.max_width;
|
|
text->height = text_info.ascent + text_info.descent;
|
|
if (drew_icon) {
|
|
int h_space = 3;
|
|
|
|
x_offset += icon_width + h_space;
|
|
y_offset += (icon_height - text->height) / 2;
|
|
}
|
|
text->x = x + x_offset;
|
|
text->y = y + y_offset;
|
|
|
|
FE_GetTextFrame(context, text, 0, text->text_len - 1, &frame);
|
|
|
|
text_width = frame.right - frame.left;
|
|
text_height = frame.bottom - frame.top;
|
|
|
|
if (text_width && text_height) {
|
|
total_width = text_width + x_offset;
|
|
total_height = text_height + y_offset;
|
|
|
|
if (total_width <= image->width &&
|
|
total_height <= image->height)
|
|
FE_DisplayText(context, FE_VIEW, text, FALSE);
|
|
}
|
|
|
|
/* Free the ALT text element. */
|
|
XP_FREE(text->text_attr);
|
|
XP_FREE(text);
|
|
}
|
|
}
|
|
|
|
/* Draw a temporary border within the bounds of the image. We only do this
|
|
if a border has not already been specified for the image. */
|
|
if (!image->border_width) {
|
|
color.red = color.green = color.blue = 0;
|
|
FE_DisplayBorder(context, FE_VIEW, x, y, image->width, image->height,
|
|
1, &color, LO_BEVEL);
|
|
}
|
|
}
|
|
|
|
|
|
/* Called for each rectangle in the update region */
|
|
static void
|
|
lo_image_rect_func(void *inclosure,
|
|
XP_Rect *rect)
|
|
{
|
|
lo_ImageLayerClosure *closure = ((lo_ImageRectStruct *)inclosure)->closure;
|
|
MWContext *context = closure->context;
|
|
CL_Layer *layer = ((lo_ImageRectStruct *)inclosure)->layer;
|
|
LO_ImageStruct *image = closure->image;
|
|
int32 layer_x_origin, layer_y_origin;
|
|
XP_Rect bbox = {0, 0, 0, 0};
|
|
CL_Compositor *compositor = CL_GetLayerCompositor(layer);
|
|
|
|
CL_WindowToDocumentRect(CL_GetLayerCompositor(layer), rect);
|
|
|
|
/* Convert layer origin to document coordinates */
|
|
CL_LayerToWindowRect(compositor, layer, &bbox);
|
|
CL_WindowToDocumentRect(compositor, &bbox);
|
|
layer_x_origin = bbox.left;
|
|
layer_y_origin = bbox.top;
|
|
|
|
/* Draw the image or icon. */
|
|
if (image->is_icon) {
|
|
char *image_url;
|
|
|
|
PA_LOCK(image_url, char *, 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))) {
|
|
IL_DisplayIcon(closure->context->img_cx, image->icon_number, 0, 0);
|
|
}
|
|
else {
|
|
lo_ImageDelayed(closure->context, image, image->icon_number, 0, 0);
|
|
}
|
|
PA_UNLOCK(image->image_url);
|
|
}
|
|
else {
|
|
/* Note: if the compositor is ever used with contexts which have a
|
|
scale factor other than unity, then all length arguments should
|
|
be divided by the appropriate context scaling factor i.e.
|
|
convertPixX, convertPixY. The FE callback is responsible for
|
|
context specific scaling. */
|
|
IL_DisplaySubImage(image->image_req, 0, 0,
|
|
(rect->left - layer_x_origin) / context->convertPixX,
|
|
(rect->top - layer_y_origin) / context->convertPixY,
|
|
(rect->right - rect->left) / context->convertPixX,
|
|
(rect->bottom - rect->top) / context->convertPixY);
|
|
}
|
|
}
|
|
|
|
/* painter_func for an image layer */
|
|
static void
|
|
lo_image_painter_func(CL_Drawable *drawable,
|
|
CL_Layer *layer,
|
|
FE_Region update_region)
|
|
{
|
|
lo_ImageLayerClosure *closure = (lo_ImageLayerClosure *)CL_GetLayerClientData(layer);
|
|
lo_ImageRectStruct image_rect;
|
|
LO_ImageStruct *image = closure->image;
|
|
int32 save_x, save_y, save_y_offset, save_bw;
|
|
int16 save_x_offset;
|
|
|
|
image_rect.closure = closure;
|
|
image_rect.layer = layer;
|
|
|
|
FE_SetDrawable(closure->context, drawable);
|
|
|
|
/* XXX - temporary, because Exceed X server has bugs with its
|
|
offscreen tiling of clipped areas. */
|
|
/* NOTE -- this may cause a significant performance hit on other
|
|
* Unix platforms. Is there no other way around this Exceed bug?
|
|
*/
|
|
#ifdef XP_UNIX
|
|
FE_ForEachRectInRegion(update_region,
|
|
(FE_RectInRegionFunc)lo_image_rect_func,
|
|
(void *)&image_rect);
|
|
#else
|
|
CL_ForEachRectCoveringRegion(update_region,
|
|
(FE_RectInRegionFunc)lo_image_rect_func,
|
|
(void *)&image_rect);
|
|
#endif
|
|
|
|
/* Draw selection feedback. */
|
|
save_x = image->x;
|
|
save_y = image->y;
|
|
save_x_offset = image->x_offset;
|
|
save_y_offset = image->y_offset;
|
|
save_bw = image->border_width;
|
|
image->x = image->y = image->y_offset = image->border_width = 0;
|
|
image->x_offset = 0;
|
|
|
|
FE_DisplayFeedback(closure->context, FE_VIEW, (LO_Element*)image);
|
|
|
|
image->x = save_x;
|
|
image->y = save_y;
|
|
image->x_offset = save_x_offset;
|
|
image->y_offset = save_y_offset;
|
|
image->border_width = save_bw;
|
|
|
|
FE_SetDrawable(closure->context, NULL);
|
|
}
|
|
|
|
static void
|
|
lo_image_destroy_func(CL_Layer *layer)
|
|
{
|
|
lo_ImageLayerClosure *closure = (lo_ImageLayerClosure *)CL_GetLayerClientData(layer);
|
|
|
|
closure->image->layer = NULL;
|
|
|
|
XP_DELETE(closure);
|
|
}
|
|
|
|
/* Create the image layer */
|
|
CL_Layer *
|
|
lo_CreateImageLayer(MWContext *context, LO_ImageStruct *image,
|
|
CL_Layer *parent)
|
|
{
|
|
CL_Layer *layer;
|
|
XP_Rect bbox = {0, 0, 0, 0};
|
|
CL_LayerVTable vtable;
|
|
lo_ImageLayerClosure *closure;
|
|
int32 layer_x_offset, layer_y_offset;
|
|
int bw = image->border_width;
|
|
char *name, *str;
|
|
|
|
uint32 flags = CL_OPAQUE | CL_DONT_ENUMERATE | CL_HIDDEN;
|
|
|
|
layer_x_offset = image->x + image->x_offset + bw;
|
|
layer_y_offset = image->y + image->y_offset + bw;
|
|
|
|
XP_BZERO(&vtable, sizeof(CL_LayerVTable));
|
|
vtable.painter_func = lo_image_painter_func;
|
|
vtable.destroy_func = lo_image_destroy_func;
|
|
|
|
closure = XP_NEW_ZAP(lo_ImageLayerClosure);
|
|
closure->type = LO_IMAGE_LAYER;
|
|
closure->context = context;
|
|
|
|
PA_LOCK(str, char *, image->image_url);
|
|
name = str;
|
|
|
|
PA_UNLOCK(image->image_url);
|
|
|
|
layer = CL_NewLayer(name, layer_x_offset, layer_y_offset, &bbox,
|
|
&vtable, flags, (void *)closure);
|
|
|
|
/* Insert the image layer the _CONTENT layer. */
|
|
if (parent)
|
|
CL_InsertChildByZ(parent, layer, Z_CONTENT_LAYERS);
|
|
|
|
closure->image = image;
|
|
|
|
return layer;
|
|
}
|
|
|
|
/* Activate an image layer, allowing the compositor to start making calls to
|
|
display the image. This routine should be called only after the layout
|
|
position of an image has been finalized, and it should only be called if
|
|
the compositor exists. */
|
|
void
|
|
lo_ActivateImageLayer(MWContext *context, LO_ImageStruct *image)
|
|
{
|
|
PRBool suppress_mode, is_mocha_image, draw_delayed_icon;
|
|
int x, y;
|
|
CL_Layer *layer;
|
|
char *image_url;
|
|
|
|
XP_ASSERT(context->compositor);
|
|
layer = image->layer;
|
|
XP_ASSERT(layer);
|
|
if (! layer) /* Paranoia */
|
|
return;
|
|
|
|
/* Determine the feedback suppression mode for non-icon, non-backdrop
|
|
images. */
|
|
switch (image->suppress_mode) {
|
|
case LO_SUPPRESS_UNDEFINED:
|
|
suppress_mode = (PRBool)image->is_transparent;
|
|
break;
|
|
|
|
case LO_SUPPRESS:
|
|
suppress_mode = PR_TRUE;
|
|
break;
|
|
|
|
case LO_DONT_SUPPRESS:
|
|
suppress_mode = PR_FALSE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Don't draw delayed icon for JavaScript-generated images. */
|
|
PA_LOCK(image_url, char *, image->image_url);
|
|
if (NET_URL_Type(image_url) == MOCHA_TYPE_URL)
|
|
is_mocha_image = PR_TRUE;
|
|
else
|
|
is_mocha_image = PR_FALSE;
|
|
PA_UNLOCK(image->image_url);
|
|
|
|
/* Determine whether to draw the delayed icon, ALT text and a
|
|
temporary border as the image is loading. */
|
|
draw_delayed_icon = (PRBool)(!suppress_mode && !image->is_icon &&
|
|
!is_mocha_image && image->image_attr &&
|
|
!(image->image_attr->attrmask & LO_ATTR_BACKDROP) &&
|
|
(image->image_status != IL_FRAME_COMPLETE) &&
|
|
(image->image_status != IL_IMAGE_COMPLETE));
|
|
|
|
if (!(image->ele_attrmask & LO_ELE_DRAWN)) {
|
|
/* 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 and unhide it. */
|
|
CL_MoveLayer(layer, x, y);
|
|
CL_SetLayerHidden(layer, PR_FALSE);
|
|
image->ele_attrmask |= LO_ELE_DRAWN;
|
|
|
|
/* In order to increase the perceived speed of image loading,
|
|
non-backdrop, non-icon images have the delayed icon, ALT text
|
|
and a temporary border drawn in their place before they come in. */
|
|
if (draw_delayed_icon)
|
|
lo_ImageDelayed(context, image, IL_IMAGE_DELAYED, x, y);
|
|
}
|
|
else {
|
|
/* In order to increase the perceived speed of image loading,
|
|
non-backdrop, non-icon images have the delayed icon, ALT text
|
|
and a temporary border drawn in their place before they come in. */
|
|
XP_Rect *valid_rect = &image->valid_rect;
|
|
int valid_width, valid_height;
|
|
|
|
valid_width = valid_rect->right - valid_rect->left;
|
|
valid_height = valid_rect->bottom - valid_rect->top;
|
|
|
|
/* Get the offset of the image layer relative to its parent layer. */
|
|
x = CL_GetLayerXOffset(image->layer);
|
|
y = CL_GetLayerYOffset(image->layer);
|
|
|
|
if (draw_delayed_icon && !valid_width && !valid_height)
|
|
lo_ImageDelayed(context, image, IL_IMAGE_DELAYED, x, y);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/* Utility function for debugging */
|
|
MWContext *
|
|
LO_CompositorToContext(CL_Compositor *compositor)
|
|
{
|
|
CL_Layer *doc_layer = CL_GetCompositorRoot(compositor);
|
|
lo_GroupLayerClosure *closure = (lo_GroupLayerClosure *)CL_GetLayerClientData(doc_layer);
|
|
return closure->context;
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
#ifdef PROFILE
|
|
#pragma profile off
|
|
#endif
|