gecko-dev/lib/libmocha/lm_layer.c
mlm%netscape.com 64e462e071 Branch landing: Multithreading libmocha in mozilla.
- Add multiple window groups to allow windows to execute JS on
   different threads.
- Add new context parameters to JS and libmocha functions for thread
   safety.
1998-09-25 22:04:34 +00:00

2995 lines
90 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.
*/
/*
* Compositor layer reflection and event notification
*
* Scott Furman, 6/20/96
*
*/
#include "lm.h" /* moved out of ifdef for pre compiled headers */
#include "xp.h"
#include "lo_ele.h"
#include "prtypes.h"
#include "pa_tags.h"
#include "layout.h"
#include "jsdbgapi.h"
#include "layers.h"
/* Forward declarations. Can these be static? */
PR_STATIC_CALLBACK(JSBool)
rect_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
PR_STATIC_CALLBACK(JSBool)
rect_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
/* HACK: We don't want to resolve layers when we restore the layer state on
resizes. This boolean gets set to FALSE for that case in
lm_RestoreLayerState() */
static Bool lm_really_resolve_layer = TRUE;
enum layer_array_slot {
LAYER_ARRAY_LENGTH = -1
};
/* Native part of mocha object reflecting children layers of another layer */
typedef struct JSLayerArray {
MochaDecoder *decoder; /* prefix must match JSObjectArray */
jsint length; /* # of entries in array */
int32 parent_layer_id;
} JSLayerArray;
extern JSClass lm_layer_array_class;
PR_STATIC_CALLBACK(JSBool)
layer_array_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSLayerArray *array;
MochaDecoder *decoder;
MWContext *context;
jsint count, slot;
CL_Layer *layer, *parent_layer;
int32 layer_id;
while (!(array = JS_GetInstancePrivate(cx, obj, &lm_layer_array_class,
NULL))) {
obj = JS_GetPrototype(cx, obj);
if (!obj)
return JS_TRUE;
}
decoder = array->decoder;
if (!lm_CheckContainerAccess(cx, obj, decoder,
JSTARGET_UNIVERSAL_BROWSER_READ)) {
return JS_FALSE;
}
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
context = decoder->window_context;
if (!context)
return JS_TRUE;
LO_LockLayout();
if(decoder->doc_id != XP_DOCID(context)) {
LO_UnlockLayout();
return JS_FALSE;
}
parent_layer = LO_GetLayerFromId(context, array->parent_layer_id);
if (!parent_layer) {
LO_UnlockLayout();
return JS_TRUE;
}
switch (slot) {
case LAYER_ARRAY_LENGTH:
count = CL_GetLayerChildCount(parent_layer);
if (count > array->length)
array->length = count;
*vp = INT_TO_JSVAL(count);
break;
default:
if (slot < 0) {
/* Don't mess with user-defined or method properties. */
LO_UnlockLayout();
return JS_TRUE;
}
if (slot >= array->length)
array->length = slot + 1;
layer = CL_GetLayerChildByIndex(parent_layer, slot);
if (!layer) {
JS_ReportError(cx,
"Attempt to access nonexistent slot %d "
"of layers[] array", slot);
LO_UnlockLayout();
return JS_FALSE;
}
layer_id = LO_GetIdFromLayer(context, layer);
*vp = OBJECT_TO_JSVAL(LM_ReflectLayer(context, layer_id,
array->parent_layer_id,
NULL));
break;
}
LO_UnlockLayout();
return JS_TRUE;
}
/* XXXMLM - gross hack until we have JS_HasProperty.
* Do not resolve a property if this static
* flag is set. See lm_reflect_layer_using_existing_obj.
*/
static JSBool layer_array_should_resolve = JS_TRUE;
PR_STATIC_CALLBACK(JSBool)
layer_array_resolve_name(JSContext *cx, JSObject *obj, jsval id)
{
JSLayerArray *array;
MochaDecoder *decoder;
const char * name;
JSObject *layer_obj;
if (!JSVAL_IS_STRING(id))
return JS_TRUE;
/* XXXMLM - see above; wish we had JS_HasProperty */
if (!layer_array_should_resolve) {
return JS_TRUE;
}
name = JS_GetStringBytes(JSVAL_TO_STRING(id));
if (!name)
return JS_TRUE;
array = JS_GetPrivate(cx, obj);
if (!array)
return JS_TRUE;
decoder = array->decoder;
/*
* If the layer exists, we don't have to define the property here,
* since the reflection code will define it for us.
*/
layer_obj = lm_GetNamedLayer(decoder, array->parent_layer_id, name);
return JS_TRUE;
}
PR_STATIC_CALLBACK(void)
layer_array_finalize(JSContext *cx, JSObject *obj)
{
JSLayerArray *array;
array = JS_GetPrivate(cx, obj);
if (!array)
return;
DROP_BACK_COUNT(array->decoder);
JS_free(cx, array);
}
JSClass lm_layer_array_class = {
"LayerArray", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
layer_array_getProperty, layer_array_getProperty, JS_EnumerateStub,
layer_array_resolve_name, JS_ConvertStub, layer_array_finalize
};
static JSPropertySpec layer_array_props[] = {
{lm_length_str, LAYER_ARRAY_LENGTH,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{0}
};
/* Native part of a mocha object that reflects a single compositor layer
*/
typedef struct JSLayer {
JSEventCapturer capturer;
MochaDecoder *decoder;
int32 layer_id;
JSString *name;
JSObject *child_layers_array_obj;
char *source_url;
/*
* Indicates which properties have been modified and should be
* saved across a resize relayout.
*/
uint32 modification_mask;
/*
* The following are properties whose saved values are of a different
* type than the property itself.
*/
JSString *sibling_above;
JSString *sibling_below;
int32 width;
PRPackedBool properties_locked;
PRPackedBool principals_compromised;
JSPrincipals *principals;
} JSLayer;
static JSObject *
reflect_layer_array(MochaDecoder *decoder,
int32 parent_layer_id)
{
JSContext *cx;
JSLayer *js_layer_parent;
JSLayerArray *array;
JSObject *obj, *layer_obj, *parent_doc_obj;
JSClass *clasp;
CL_Layer *parent_layer;
cx = decoder->js_context;
layer_obj = LO_GetLayerMochaObjectFromId(decoder->window_context,
parent_layer_id);
parent_layer = LO_GetLayerFromId(decoder->window_context, parent_layer_id);
parent_doc_obj = lm_GetDocumentFromLayerId(decoder, parent_layer_id);
if (! layer_obj || !parent_layer || !parent_doc_obj) /* paranoia */
return NULL;
js_layer_parent = JS_GetPrivate(cx, layer_obj);
if (!js_layer_parent)
return NULL;
obj = js_layer_parent->child_layers_array_obj;
if (obj) /* Are layer children already reflected ? */
return obj;
clasp = &lm_layer_array_class;
array = JS_malloc(cx, sizeof *array);
if (!array)
return NULL;
XP_BZERO(array, sizeof *array);
obj = JS_NewObject(cx, clasp, NULL, parent_doc_obj);
if (!obj || !JS_SetPrivate(cx, obj, array)) {
JS_free(cx, array);
return NULL;
}
if (!JS_DefineProperties(cx, obj, layer_array_props))
return NULL;
array->decoder = HOLD_BACK_COUNT(decoder);
array->parent_layer_id = parent_layer_id;
return obj;
}
/* Given the mocha object reflecting a compositor layer, return
the mocha object that reflects its child layers in an array. */
PRIVATE JSObject *
lm_GetLayerArray(MochaDecoder *decoder,
JSObject *parent_js_layer_obj)
{
JSLayer *js_layer_parent;
JSObject *obj;
JSContext *cx;
if (! parent_js_layer_obj)
return NULL;
cx = decoder->js_context;
js_layer_parent = JS_GetPrivate(cx, parent_js_layer_obj);
if (!js_layer_parent)
return NULL;
obj = js_layer_parent->child_layers_array_obj;
if (obj)
return obj;
obj = reflect_layer_array(decoder, js_layer_parent->layer_id);
if (obj && !JS_DefineProperty(cx, parent_js_layer_obj, "layers",
OBJECT_TO_JSVAL(obj), NULL, NULL,
JSPROP_ENUMERATE|JSPROP_READONLY|
JSPROP_PERMANENT)) {
return NULL;
}
js_layer_parent->child_layers_array_obj = obj;
return obj;
}
/* The top-level document object contains the distinguished _DOCUMENT
layer. Reflect the array containing children of the _DOCUMENT layer. */
JSObject *
lm_GetDocumentLayerArray(MochaDecoder *decoder, JSObject *document)
{
MWContext *context;
JSObject *obj;
JSLayerArray *array;
JSClass *clasp;
JSContext *cx;
JSDocument *doc;
cx = decoder->js_context;
doc = JS_GetPrivate(cx, document);
if (!doc)
return NULL;
obj = doc->layers;
if (obj)
return obj;
context = decoder->window_context;
/* If this is a layer's document, return the layer's child array */
if (doc->layer_id != LO_DOCUMENT_LAYER_ID) {
JSObject *layer_obj;
layer_obj = LO_GetLayerMochaObjectFromId(context, doc->layer_id);
if (!layer_obj)
return NULL;
doc->layers = lm_GetLayerArray(decoder, layer_obj);
return doc->layers;
}
clasp = &lm_layer_array_class;
array = JS_malloc(cx, sizeof *array);
if (!array)
return NULL;
XP_BZERO(array, sizeof *array);
obj = JS_NewObject(cx, clasp, NULL, document);
if (!obj || !JS_SetPrivate(cx, obj, array)) {
JS_free(cx, array);
return NULL;
}
if (!JS_DefineProperties(cx, obj, layer_array_props)) {
JS_free(cx, array);
return NULL;
}
array->decoder = HOLD_BACK_COUNT(decoder);
array->parent_layer_id = doc->layer_id;
doc->layers = obj;
return obj;
}
#define LM_SET_LAYER_MODIFICATION(layer, bit) \
(layer)->modification_mask |= (1 << (bit))
#define LM_CLEAR_LAYER_MODIFICATION(layer, bit) \
(layer)->modification_mask &= ~(1 << (bit))
#define LM_CHECK_LAYER_MODIFICATION(layer, bit) \
(((layer)->modification_mask & (1 << (bit))) != 0)
enum layer_prop_modification_bits {
LAYER_MOD_LEFT = 0,
LAYER_MOD_TOP,
LAYER_MOD_VISIBILITY,
LAYER_MOD_SRC,
LAYER_MOD_ZINDEX,
LAYER_MOD_BGCOLOR,
LAYER_MOD_BACKGROUND,
LAYER_MOD_PARENT,
LAYER_MOD_SIB_ABOVE,
LAYER_MOD_SIB_BELOW,
LAYER_MOD_CLIP_LEFT,
LAYER_MOD_CLIP_RIGHT,
LAYER_MOD_CLIP_TOP,
LAYER_MOD_CLIP_BOTTOM,
LAYER_MOD_WIDTH
};
/* Static compositor layer property slots */
enum layer_slot {
LAYER_WINDOW = -1,
LAYER_NAME = -2,
LAYER_LEFT = -3,
LAYER_TOP = -4,
LAYER_X = -5,
LAYER_Y = -6,
LAYER_HIDDEN = -7,
LAYER_SIB_ABOVE = -8,
LAYER_SIB_BELOW = -9,
LAYER_PARENT = -10,
LAYER_CHILDREN = -11,
LAYER_SRC = -12,
LAYER_VISIBILITY = -13,
LAYER_ABOVE = -14,
LAYER_BELOW = -15,
LAYER_ZINDEX = -16,
LAYER_BGCOLOR = -17
};
char lm_left_str[] = "left";
char lm_top_str[] = "top";
char lm_right_str[] = "right";
char lm_bottom_str[] = "bottom";
char lm_src_str[] = "src";
char lm_visibility_str[] = "visibility";
char lm_zindex_str[] = "zIndex";
char lm_bgcolor_str[] = "bgColor";
char lm_background_str[] = "background";
char lm_clip_str[] = "clip";
/* Static compositor layer properties */
static JSPropertySpec layer_props[] = {
{"window", LAYER_WINDOW, JSPROP_ENUMERATE | JSPROP_READONLY},
{"id", LAYER_NAME, JSPROP_ENUMERATE | JSPROP_READONLY},
{"name", LAYER_NAME, JSPROP_ENUMERATE},
{lm_left_str, LAYER_LEFT, JSPROP_ENUMERATE},
{"x", LAYER_LEFT, JSPROP_ENUMERATE}, /* Synonym for left */
{lm_top_str, LAYER_TOP, JSPROP_ENUMERATE},
{"y", LAYER_TOP, JSPROP_ENUMERATE}, /* Synonym for top */
{"pageX", LAYER_X, JSPROP_ENUMERATE},
{"pageY", LAYER_Y, JSPROP_ENUMERATE},
{"hidden", LAYER_HIDDEN, JSPROP_ENUMERATE},
{"layers", LAYER_CHILDREN, JSPROP_ENUMERATE | JSPROP_READONLY},
{"siblingAbove", LAYER_SIB_ABOVE, JSPROP_ENUMERATE | JSPROP_READONLY}, /* FIXME - should be writeable */
{"siblingBelow", LAYER_SIB_BELOW, JSPROP_ENUMERATE | JSPROP_READONLY}, /* FIXME - should be writeable */
{lm_parentLayer_str, LAYER_PARENT, JSPROP_ENUMERATE | JSPROP_READONLY},
{lm_src_str, LAYER_SRC, JSPROP_ENUMERATE},
{lm_visibility_str, LAYER_VISIBILITY, JSPROP_ENUMERATE},
{"above", LAYER_ABOVE, JSPROP_ENUMERATE | JSPROP_READONLY},
{"below", LAYER_BELOW, JSPROP_ENUMERATE | JSPROP_READONLY},
{lm_zindex_str, LAYER_ZINDEX, JSPROP_ENUMERATE},
{lm_bgcolor_str, LAYER_BGCOLOR, JSPROP_ENUMERATE},
{0}
};
/*
* Static compositor rect property slots. Declared here since we
* need the ids in some of the methods.
*/
enum rect_slot {
RECT_LEFT = -1,
RECT_TOP = -2,
RECT_RIGHT = -3,
RECT_BOTTOM = -4,
RECT_WIDTH = -5,
RECT_HEIGHT = -6
};
extern JSClass lm_layer_class;
PR_STATIC_CALLBACK(JSBool)
layer_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSLayer *js_layer;
MochaDecoder *decoder;
MWContext *context;
JSString *str;
CL_Layer *layer, *layer_above, *layer_below, *layer_parent;
LO_Color *bg_color;
jsint slot;
char *visibility;
uint32 flags;
while (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, NULL))) {
obj = JS_GetPrototype(cx, obj);
if (!obj)
return JS_TRUE;
}
decoder = js_layer->decoder;
if (!lm_CheckContainerAccess(cx, obj, decoder,
JSTARGET_UNIVERSAL_BROWSER_READ)) {
return JS_FALSE;
}
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
context = decoder->window_context;
if (!context)
return JS_TRUE;
/*
* Not sure if this is enough protection or not...
*/
LO_LockLayout();
if(decoder->doc_id != XP_DOCID(context)) {
LO_UnlockLayout();
return JS_FALSE;
}
layer = LO_GetLayerFromId(context, js_layer->layer_id);
if (!layer) {
LO_UnlockLayout();
return JS_TRUE;
}
switch (slot) {
case LAYER_WINDOW:
*vp = OBJECT_TO_JSVAL(decoder->window_object);
break;
case LAYER_NAME:
if (js_layer->name)
*vp = STRING_TO_JSVAL(js_layer->name);
else
*vp = JSVAL_NULL;
break;
case LAYER_HIDDEN:
*vp = BOOLEAN_TO_JSVAL(CL_GetLayerHidden(layer));
break;
case LAYER_VISIBILITY:
flags = CL_GetLayerFlags(layer);
if (flags & CL_HIDDEN)
visibility = "hide";
else if (flags & CL_OVERRIDE_INHERIT_VISIBILITY)
visibility = "show";
else
visibility = "inherit";
str = JS_NewStringCopyZ(cx, visibility);
if (!str) {
LO_UnlockLayout();
return JS_FALSE;
}
*vp = STRING_TO_JSVAL(str);
break;
case LAYER_LEFT:
*vp = INT_TO_JSVAL(LO_GetLayerXOffset(layer));
break;
case LAYER_TOP:
*vp = INT_TO_JSVAL(LO_GetLayerYOffset(layer));
break;
case LAYER_X:
*vp = INT_TO_JSVAL(CL_GetLayerXOrigin(layer));
break;
case LAYER_Y:
*vp = INT_TO_JSVAL(CL_GetLayerYOrigin(layer));
break;
case LAYER_CHILDREN:
*vp = OBJECT_TO_JSVAL(lm_GetLayerArray(js_layer->decoder, obj));
break;
case LAYER_SIB_ABOVE:
layer_above = CL_GetLayerSiblingAbove(layer);
if (layer_above)
*vp = OBJECT_TO_JSVAL(LO_GetLayerMochaObjectFromLayer(context,
layer_above));
else
*vp = JSVAL_NULL;
break;
case LAYER_SIB_BELOW:
layer_below = CL_GetLayerSiblingBelow(layer);
if (layer_below)
*vp = OBJECT_TO_JSVAL(LO_GetLayerMochaObjectFromLayer(context,
layer_below));
else
*vp = JSVAL_NULL;
break;
case LAYER_PARENT:
layer_parent = CL_GetLayerParent(layer);
/*
* XXX This is a bit controversial - should the parent layer of
* a top-level layer be the window??
*/
if (layer_parent) {
if (CL_GetLayerFlags(layer_parent) & CL_DONT_ENUMERATE)
*vp = OBJECT_TO_JSVAL(decoder->window_object);
else
*vp = OBJECT_TO_JSVAL(LO_GetLayerMochaObjectFromLayer(context,
layer_parent));
}
else
*vp = JSVAL_NULL;
break;
case LAYER_ZINDEX:
*vp = INT_TO_JSVAL(CL_GetLayerZIndex(layer));
break;
case LAYER_ABOVE:
layer_above = CL_GetLayerAbove(layer);
if (layer_above)
*vp = OBJECT_TO_JSVAL(LO_GetLayerMochaObjectFromLayer(context,
layer_above));
else
*vp = JSVAL_NULL;
break;
case LAYER_BELOW:
layer_below = CL_GetLayerBelow(layer);
if (layer_below)
*vp = OBJECT_TO_JSVAL(LO_GetLayerMochaObjectFromLayer(context,
layer_below));
else
*vp = JSVAL_NULL;
break;
case LAYER_BGCOLOR:
bg_color = LO_GetLayerBgColor(layer);
if (bg_color) {
uint32 packed_color =
(bg_color->red << 16) | (bg_color->green << 8) | (bg_color->blue);
*vp = INT_TO_JSVAL(packed_color);
} else
*vp = JSVAL_NULL;
break;
case LAYER_SRC:
if (!lm_CheckPermissions(cx, obj, JSTARGET_UNIVERSAL_BROWSER_READ))
return JS_FALSE;
if (!js_layer->source_url) {
*vp = JSVAL_NULL;
} else {
JSString *url;
url = JS_NewStringCopyZ(cx, js_layer->source_url);
if (!url) {
LO_UnlockLayout();
return JS_FALSE;
}
*vp = STRING_TO_JSVAL(url);
}
break;
default:
/* Don't mess with a user-defined or method property. */
break;
}
LO_UnlockLayout();
return JS_TRUE;
}
JSBool
lm_jsval_to_rgb(JSContext *cx, jsval *vp, LO_Color **rgbp)
{
LO_Color *rgb = NULL;
int32 color;
if (JSVAL_IS_NUMBER(*vp)) {
if (!JS_ValueToInt32(cx, *vp, &color))
return JS_FALSE;
if ((color >> 24) != 0)
return JS_FALSE;
rgb = XP_NEW(LO_Color);
if (!rgb)
return JS_FALSE;
rgb->red = (uint8) (color >> 16);
rgb->green = (uint8) ((color >> 8) & 0xff);
rgb->blue = (uint8) (color & 0xff);
} else {
switch(JS_TypeOfValue(cx, *vp)) {
case JSTYPE_OBJECT:
/* Check for null (transparent) bgcolor */
if (JSVAL_IS_NULL(*vp)) {
rgb = NULL;
break;
}
/* FALL THROUGH */
default:
if (!JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp))
return JS_FALSE;
/* FALL THROUGH */
case JSTYPE_STRING:
rgb = XP_NEW(LO_Color);
if (!rgb)
return JS_FALSE;
if (!LO_ParseRGB((char *)JS_GetStringBytes(JSVAL_TO_STRING(*vp)),
&rgb->red, &rgb->green, &rgb->blue)) {
JS_ReportError(cx, "Invalid color specification %s",
(char *)JS_GetStringBytes(JSVAL_TO_STRING(*vp)));
XP_FREE(rgb);
return JS_FALSE;
}
break;
}
}
*rgbp = rgb;
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
layer_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
int32 val;
int32 val32;
JSBool hidden;
PRBool properties_locked;
JSLayer *js_layer;
MochaDecoder *decoder;
MWContext *context;
CL_Layer *layer, *parent;
char *url;
LO_Color *rgb;
jsint slot;
jsval js_val;
const char *referer;
JSBool unlockp = JS_TRUE;
while (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, NULL))) {
obj = JS_GetPrototype(cx, obj);
if (!obj)
return JS_TRUE;
}
properties_locked = (PRBool)js_layer->properties_locked;
decoder = js_layer->decoder;
if (!lm_CheckContainerAccess(cx, obj, decoder,
JSTARGET_UNIVERSAL_BROWSER_WRITE)) {
return JS_FALSE;
}
context = decoder->window_context;
if (!context)
return JS_TRUE;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
LO_LockLayout();
if(decoder->doc_id != XP_DOCID(context)) {
LO_UnlockLayout();
return JS_FALSE;
}
layer = LO_GetLayerFromId(context, js_layer->layer_id);
if (!layer) {
LO_UnlockLayout();
return JS_TRUE;
}
/* If a layer is dynamically changing, it will probably look
better if it utilizes offscreen compositing. */
CL_ChangeLayerFlag(layer, CL_PREFER_DRAW_OFFSCREEN, PR_TRUE);
switch (slot) {
case LAYER_HIDDEN:
if (!JS_ValueToBoolean(cx, *vp, &hidden))
goto error_exit;
js_val = BOOLEAN_TO_JSVAL(!hidden);
JS_SetProperty(cx, obj, lm_visibility_str, &js_val);
break;
case LAYER_VISIBILITY:
{
JSBool hidden, inherit;
if (JSVAL_IS_BOOLEAN(*vp)) {
hidden = (JSBool)(!JSVAL_TO_BOOLEAN(*vp));
CL_ChangeLayerFlag(layer, CL_HIDDEN, (PRBool)hidden);
} else {
JSString *str;
const char *visibility;
if (!(str = JS_ValueToString(cx, *vp)))
goto error_exit;
visibility = JS_GetStringBytes(str);
/* Accept "hidden" or "hide" */
hidden = (JSBool)(!XP_STRNCASECMP(visibility, "hid", 3));
inherit = (JSBool)(!XP_STRCASECMP(visibility, "inherit"));
if (!hidden && !inherit &&
XP_STRCASECMP(visibility, "show") &&
XP_STRCASECMP(visibility, "visible")) {
JS_ReportError(cx,
"Layer visibility property must be set to "
"one of 'hide', 'show' or 'inherit'");
}
CL_ChangeLayerFlag(layer, CL_HIDDEN, (PRBool)hidden);
CL_ChangeLayerFlag(layer, CL_OVERRIDE_INHERIT_VISIBILITY,
(PRBool)!inherit);
}
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_VISIBILITY);
break;
}
case LAYER_LEFT:
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (properties_locked)
break;
if (!JS_ValueToInt32(cx, *vp, &val))
goto error_exit;
LO_MoveLayer(layer, (int32)val, LO_GetLayerYOffset(layer));
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_LEFT);
break;
case LAYER_TOP:
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (properties_locked)
break;
if (!JS_ValueToInt32(cx, *vp, &val))
goto error_exit;
LO_MoveLayer(layer, LO_GetLayerXOffset(layer), (int32)val);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_TOP);
break;
case LAYER_X:
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (properties_locked)
break;
if (!JS_ValueToInt32(cx, *vp, &val32))
goto error_exit;
parent = CL_GetLayerParent(layer);
XP_ASSERT(parent);
if (parent)
val32 -= CL_GetLayerXOrigin(parent);
js_val = INT_TO_JSVAL(val32);
JS_SetProperty(cx, obj, lm_left_str, &js_val);
break;
case LAYER_Y:
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (properties_locked)
break;
if (!JS_ValueToInt32(cx, *vp, &val32))
goto error_exit;
parent = CL_GetLayerParent(layer);
XP_ASSERT(parent);
if (parent)
val32 -= CL_GetLayerYOrigin(parent);
js_val = INT_TO_JSVAL(val32);
JS_SetProperty(cx, obj, lm_top_str, &js_val);
break;
case LAYER_SRC:
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (properties_locked)
break;
if (!JSVAL_IS_STRING(*vp) &&
!JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
goto error_exit;
}
url = JS_GetStringBytes(JSVAL_TO_STRING(*vp));
LO_UnlockLayout();
url = (char *)lm_CheckURL(cx, url, JS_TRUE);
if (! url)
return JS_FALSE;
referer = lm_GetSubjectOriginURL(cx);
if (! referer) {
XP_FREE(url);
return JS_FALSE;
}
if (ET_TweakLayer(decoder->window_context, layer, 0, 0,
(void *)url, js_layer->layer_id,
CL_SetSrc, referer, decoder->doc_id)) {
lm_NewLayerDocument(decoder, js_layer->layer_id);
XP_FREEIF(js_layer->source_url);
js_layer->source_url = url;
if (js_layer->principals) {
JSPRINCIPALS_DROP(cx, js_layer->principals);
}
js_layer->principals = LM_NewJSPrincipals(NULL, NULL, url);
if (js_layer->principals == NULL) {
XP_FREE(url);
return JS_FALSE;
}
JSPRINCIPALS_HOLD(cx, js_layer->principals);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_SRC);
decoder->stream_owner = js_layer->layer_id;
}
else {
XP_FREE(url);
}
/* Note that the name will be deallocated at the other end
of this call. */
/* Return true here to avoid passing through the getter and
* hitting the additional security checks there. Return
* value has already been stringized here. */
return JS_TRUE;
break;
case LAYER_ZINDEX:
if (!JS_ValueToInt32(cx, *vp, &val))
goto error_exit;
parent = CL_GetLayerParent(layer);
CL_RemoveChild(parent, layer);
CL_InsertChildByZ(parent, layer, (int32)val);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_ZINDEX);
LM_CLEAR_LAYER_MODIFICATION(js_layer, LAYER_MOD_SIB_ABOVE);
LM_CLEAR_LAYER_MODIFICATION(js_layer, LAYER_MOD_SIB_BELOW);
break;
case LAYER_BGCOLOR:
LO_UnlockLayout();
unlockp = JS_FALSE;
if (!lm_jsval_to_rgb(cx, vp, &rgb))
return JS_FALSE;
ET_TweakLayer(decoder->window_context, layer, 0, 0,
rgb, 0, CL_SetBgColor, NULL, decoder->doc_id);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_BGCOLOR);
break;
case LAYER_SIB_ABOVE:
case LAYER_SIB_BELOW:
/* FIXME - these should be writeable */
break;
/* These are immutable. */
case LAYER_NAME:
case LAYER_CHILDREN:
break;
default:
break;
}
if (unlockp)
LO_UnlockLayout();
return layer_getProperty(cx, obj, id, vp);
error_exit:
LO_UnlockLayout();
return JS_FALSE;
}
JSBool
layer_setBgColorProperty(JSContext *cx, JSObject *obj, jsval *vp)
{
return layer_setProperty(cx, obj, INT_TO_JSVAL(LAYER_BGCOLOR), vp);
}
/* Lazily synthesize an Image object for the layer's 'background' property */
PR_STATIC_CALLBACK(JSBool)
layer_resolve_name(JSContext *cx, JSObject *obj, jsval id)
{
JSLayer *js_layer;
MochaDecoder *decoder;
const char * name;
if (!lm_really_resolve_layer)
return JS_FALSE;
js_layer = JS_GetPrivate(cx, obj);
if (!js_layer)
return JS_TRUE;
decoder = js_layer->decoder;
if (!JSVAL_IS_STRING(id))
return JS_TRUE;
name = JS_GetStringBytes(JSVAL_TO_STRING(id));
if (!XP_STRCMP(name, lm_background_str)) {
JSObject *image_obj;
CL_Layer *layer;
layer = LO_GetLayerFromId(decoder->window_context, js_layer->layer_id);
if (!layer)
return JS_FALSE;
image_obj = lm_NewImage(cx, LO_GetLayerBackdropImage(layer));
if (!image_obj)
return JS_FALSE;
return JS_DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(image_obj),
NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY);
}
return lm_ResolveWindowProps(cx, decoder, obj, id);
}
PR_STATIC_CALLBACK(void)
layer_finalize(JSContext *cx, JSObject *obj)
{
JSLayer *js_layer;
PRHashTable *map;
js_layer = JS_GetPrivate(cx, obj);
if (!js_layer)
return;
/* XXX js_layer->layer is sometimes freed by the time the GC runs
CL_SetLayerMochaObject(js_layer->layer, NULL);
*/
DROP_BACK_COUNT(js_layer->decoder);
JS_RemoveRoot(cx, &js_layer->name);
map = lm_GetIdToObjectMap(js_layer->decoder);
if (map)
PR_HashTableRemove(map, LM_GET_MAPPING_KEY(LM_LAYERS, 0, js_layer->layer_id));
JS_RemoveRoot(cx, &js_layer->sibling_above);
JS_RemoveRoot(cx, &js_layer->sibling_below);
XP_FREEIF(js_layer->source_url);
if (js_layer->principals) {
JSPRINCIPALS_DROP(cx, js_layer->principals);
}
JS_free(cx, js_layer);
}
JSBool layer_check_access(JSContext *cx, JSObject *obj, jsval id,
JSAccessMode mode, jsval *vp)
{
if(mode == JSACC_PARENT) {
return lm_CheckSetParentSlot(cx, obj, id, vp);
}
return JS_TRUE;
}
JSClass lm_layer_class = {
"Layer", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, layer_getProperty, layer_setProperty,
JS_EnumerateStub, layer_resolve_name, JS_ConvertStub, layer_finalize,
NULL, layer_check_access
};
/* JS native method:
Translate layer to given XY coordinates, e.g.
document.layers[0].moveto(1, 3); */
static JSBool
move_layer(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval, PRBool is_absolute)
{
int32 x, y;
JSLayer *js_layer;
MochaDecoder *decoder;
jsval val;
CL_Layer *layer;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (js_layer->properties_locked)
return JS_TRUE;
decoder = js_layer->decoder;
LO_LockLayout();
if(!decoder->window_context ||
decoder->doc_id != XP_DOCID(decoder->window_context)) {
LO_UnlockLayout();
return JS_FALSE;
}
layer = LO_GetLayerFromId(decoder->window_context, js_layer->layer_id);
if (!layer)
goto error_exit;
if (argc != 2) {
JS_ReportError(cx, lm_argc_err_str);
goto error_exit;
}
if (!JS_ValueToInt32(cx, argv[0], &x) ||
!JS_ValueToInt32(cx, argv[1], &y)) {
goto error_exit;
}
if (is_absolute) {
CL_Layer *parent_layer = CL_GetLayerParent(layer);
x -= CL_GetLayerXOrigin(parent_layer);
y -= CL_GetLayerYOrigin(parent_layer);
}
/* If a layer is moving, it will probably look better if it
utilizes offscreen compositing. */
CL_ChangeLayerFlag(layer, CL_PREFER_DRAW_OFFSCREEN, PR_TRUE);
LO_MoveLayer(layer, x, y);
/*
* Record that we've side-effected left and top. We do a DefineProperty
* to mutate the property and ensure that we have a unique slot per-object.
*/
if (!LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_LEFT)) {
JS_DefinePropertyWithTinyId(cx, obj, lm_left_str, LAYER_LEFT,
INT_TO_JSVAL(x), layer_getProperty,
layer_setProperty, JSPROP_ENUMERATE);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_LEFT);
}
else
JS_GetProperty(cx, obj, lm_left_str, &val);
if (!LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_TOP)) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_TOP);
JS_DefinePropertyWithTinyId(cx, obj, lm_top_str, LAYER_TOP,
INT_TO_JSVAL(y), layer_getProperty,
layer_setProperty, JSPROP_ENUMERATE);
}
else
JS_GetProperty(cx, obj, lm_top_str, &val);
LO_UnlockLayout();
return JS_TRUE;
error_exit:
LO_UnlockLayout();
return JS_FALSE;
}
/* JS native method:
Translate layer to given XY coordinates relative to BODY document origin, e.g.
document.layers[0].moveto(1, 3); */
PR_STATIC_CALLBACK(JSBool)
move_layer_abs(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return move_layer(cx, obj, argc, argv, rval, PR_TRUE);
}
/* JS native method:
Translate layer to given XY coordinates relative to parent_layer, e.g.
document.layers[0].moveto(1, 3); */
PR_STATIC_CALLBACK(JSBool)
move_layer_rel(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return move_layer(cx, obj, argc, argv, rval, PR_FALSE);
}
/* JS native method:
Stack layer above argument layer, e.g.
document.layers[0].moveAbove(document.layers[2]); */
static JSBool
change_layer_stacking(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval,
CL_LayerPosition position)
{
int32 sibling_parent_layer_id;
JSLayer *js_layer, *sibling_js_layer;
JSObject *sibling_obj;
MochaDecoder *decoder;
CL_Layer *layer, *sibling_layer, *parent, *sibling_layer_parent;
char *new_sibling_name;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
decoder = js_layer->decoder;
if (argc != 1) {
JS_ReportError(cx, lm_argc_err_str);
return JS_FALSE;
}
if (!JS_ValueToObject(cx, argv[0], &sibling_obj))
return JS_FALSE;
/* no-op */
if (!sibling_obj)
return JS_TRUE;
/* could be an object of another class. */
if (!JS_InstanceOf(cx, sibling_obj, &lm_layer_class, argv))
return JS_FALSE;
sibling_js_layer = JS_GetPrivate(cx, sibling_obj);
if (!sibling_js_layer)
return JS_TRUE;
LO_LockLayout();
if(decoder->doc_id != XP_DOCID(decoder->window_context)) {
LO_UnlockLayout();
return JS_FALSE;
}
layer = LO_GetLayerFromId(decoder->window_context, js_layer->layer_id);
sibling_layer = LO_GetLayerFromId(decoder->window_context,
sibling_js_layer->layer_id);
parent = CL_GetLayerParent(layer);
sibling_layer_parent = CL_GetLayerParent(sibling_layer);
sibling_parent_layer_id = LO_GetIdFromLayer(decoder->window_context,
sibling_layer_parent);
if (IS_MESSAGE_WINDOW(decoder->window_context) &&
(sibling_parent_layer_id == LO_DOCUMENT_LAYER_ID)) {
LO_UnlockLayout();
JS_ReportError(cx,
"Disallowed attempt to manipulate top-level layer"
" in a message window");
return JS_FALSE;
}
if (layer == sibling_layer) {
LO_UnlockLayout();
JS_ReportError(cx, "Cannot stack a Layer above or beneath itself");
return JS_FALSE;
}
/* It shouldn't be possible to be passed the
compositor's root layer. */
XP_ASSERT(parent);
XP_ASSERT(sibling_layer_parent);
if (!sibling_layer_parent || !parent) {
LO_UnlockLayout();
return JS_FALSE;
}
/* If a layer is dynamically changing, it will probably look
better if it utilizes offscreen compositing. */
CL_ChangeLayerFlag(layer, CL_PREFER_DRAW_OFFSCREEN, PR_TRUE);
CL_RemoveChild(parent, layer);
CL_InsertChild(sibling_layer_parent, layer,
sibling_layer, position);
/*
* Now store the sibling's name. Note that preservation of zindex,
* sibling_above and sibling_below are mutually exclusive, so if
* we set one, the others are cleared.
*/
LM_CLEAR_LAYER_MODIFICATION(js_layer, LAYER_MOD_ZINDEX);
if (position == CL_ABOVE) {
LM_CLEAR_LAYER_MODIFICATION(js_layer, LAYER_MOD_SIB_ABOVE);
new_sibling_name = CL_GetLayerName(sibling_layer);
if (new_sibling_name) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_SIB_BELOW);
js_layer->sibling_below = JS_NewStringCopyZ(cx, new_sibling_name);
}
}
if (position == CL_BELOW) {
LM_CLEAR_LAYER_MODIFICATION(js_layer, LAYER_MOD_SIB_BELOW);
new_sibling_name = CL_GetLayerName(sibling_layer);
if (new_sibling_name) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_SIB_ABOVE);
js_layer->sibling_above = JS_NewStringCopyZ(cx, new_sibling_name);
}
}
LO_UnlockLayout();
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
move_above(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return change_layer_stacking(cx, obj, argc, argv, rval, CL_ABOVE);
}
PR_STATIC_CALLBACK(JSBool)
move_below(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return change_layer_stacking(cx, obj, argc, argv, rval, CL_BELOW);
}
/* JS native method:
Translate layer by given XY offset, e.g.
document.layers[0].offset(1, 3); */
PR_STATIC_CALLBACK(JSBool)
offset_layer(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
int32 x, y;
JSLayer *js_layer;
MochaDecoder *decoder;
CL_Layer *layer;
jsval val;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
decoder = js_layer->decoder;
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (js_layer->properties_locked)
return JS_TRUE;
LO_LockLayout();
if(!decoder->window_context ||
decoder->doc_id != XP_DOCID(decoder->window_context)) {
LO_UnlockLayout();
return JS_FALSE;
}
layer = LO_GetLayerFromId(decoder->window_context, js_layer->layer_id);
if (!layer)
goto error_exit;
if (argc != 2) {
JS_ReportError(cx, lm_argc_err_str);
goto error_exit;
}
if (!JS_ValueToInt32(cx, argv[0], &x) ||
!JS_ValueToInt32(cx, argv[1], &y)) {
goto error_exit;
}
/* If a layer is dynamically changing, it will probably look
better if it utilizes offscreen compositing. */
CL_ChangeLayerFlag(layer, CL_PREFER_DRAW_OFFSCREEN, PR_TRUE);
CL_OffsetLayer(layer, (int32) x, (int32) y);
/* We record that we've mutated left and top */
if (!LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_LEFT)) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_LEFT);
JS_GetProperty(cx, obj, lm_left_str, &val);
JS_DefinePropertyWithTinyId(cx, obj, lm_left_str, LAYER_LEFT, val,
layer_getProperty, layer_setProperty,
JSPROP_ENUMERATE);
}
else
JS_GetProperty(cx, obj, lm_left_str, &val);
if (!LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_TOP)) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_TOP);
JS_GetProperty(cx, obj, lm_top_str, &val);
JS_DefinePropertyWithTinyId(cx, obj, lm_top_str, LAYER_TOP, val,
layer_getProperty, layer_setProperty,
JSPROP_ENUMERATE);
}
else
JS_GetProperty(cx, obj, lm_top_str, &val);
LO_UnlockLayout();
return JS_TRUE;
error_exit:
LO_UnlockLayout();
return JS_FALSE;
}
#define CLEAR_LAYER_EXPANSION_POLICY(layer, bits) \
lo_SetLayerClipExpansionPolicy(layer, \
lo_GetLayerClipExpansionPolicy(layer) & ~(bits))
PRIVATE JSBool
resize_layer_common(JSContext *cx, JSObject *obj, CL_Layer *layer,
int32 width, int32 height)
{
JSLayer *js_layer;
MochaDecoder * decoder;
JSObject *clip;
jsval val;
if (!(js_layer = JS_GetPrivate(cx, obj)))
return JS_FALSE;
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (js_layer->properties_locked)
return JS_TRUE;
decoder = js_layer->decoder;
/* If a layer is dynamically changing, it will probably look
better if it utilizes offscreen compositing. */
CL_ChangeLayerFlag(layer, CL_PREFER_DRAW_OFFSCREEN, PR_TRUE);
CL_ResizeLayer(layer, (int32) width, (int32) height);
CLEAR_LAYER_EXPANSION_POLICY(layer,
LO_AUTO_EXPAND_CLIP_BOTTOM | LO_AUTO_EXPAND_CLIP_RIGHT);
if (JS_LookupProperty(cx, obj, lm_clip_str, &val) &&
JSVAL_IS_OBJECT(val)) {
clip = JSVAL_TO_OBJECT(val);
if (!LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_RIGHT)) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_RIGHT);
JS_DefinePropertyWithTinyId(cx, clip, lm_right_str, RECT_RIGHT,
INT_TO_JSVAL(width), rect_getProperty,
rect_setProperty, JSPROP_ENUMERATE);
}
else {
JS_GetProperty(cx, clip, lm_right_str, &val);
}
if (!LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_BOTTOM)) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_BOTTOM);
JS_DefinePropertyWithTinyId(cx, clip, lm_bottom_str, RECT_BOTTOM,
INT_TO_JSVAL(height), rect_getProperty,
rect_setProperty, JSPROP_ENUMERATE);
}
else {
JS_GetProperty(cx, clip, lm_bottom_str, &val);
}
}
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
resize_layer_to(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
int32 x, y;
JSLayer *js_layer;
MochaDecoder * decoder;
CL_Layer *layer;
JSBool ret;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
decoder = js_layer->decoder;
LO_LockLayout();
if(!decoder->window_context ||
decoder->doc_id != XP_DOCID(decoder->window_context)) {
LO_UnlockLayout();
return JS_FALSE;
}
layer = LO_GetLayerFromId(decoder->window_context, js_layer->layer_id);
if (!layer)
goto error_exit;
if (argc != 2) {
JS_ReportError(cx, lm_argc_err_str);
goto error_exit;
}
if (!JS_ValueToInt32(cx, argv[0], &x) ||
!JS_ValueToInt32(cx, argv[1], &y)) {
goto error_exit;
}
ret = resize_layer_common(cx, obj, layer, x, y);
LO_UnlockLayout();
return ret;
error_exit:
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(JSBool)
resize_layer_by(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
int32 x, y;
JSLayer *js_layer;
MochaDecoder * decoder;
CL_Layer *layer;
XP_Rect bbox;
JSBool ret;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
decoder = js_layer->decoder;
LO_LockLayout();
if(!decoder->window_context ||
decoder->doc_id != XP_DOCID(decoder->window_context)) {
LO_UnlockLayout();
return JS_FALSE;
}
layer = LO_GetLayerFromId(decoder->window_context, js_layer->layer_id);
if (!layer)
goto error_exit;
if (argc != 2) {
JS_ReportError(cx, lm_argc_err_str);
goto error_exit;
}
if (!JS_ValueToInt32(cx, argv[0], &x) ||
!JS_ValueToInt32(cx, argv[1], &y)) {
goto error_exit;
}
CL_GetLayerBbox(layer, &bbox);
ret = resize_layer_common(cx, obj, layer,
(bbox.right - bbox.left + x),
(bbox.bottom - bbox.top + y));
LO_UnlockLayout();
return ret;
error_exit:
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(JSBool)
load_layer(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
jsdouble width;
JSLayer *js_layer;
MochaDecoder * decoder;
char *url;
JSBool ret;
const char *referer;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (js_layer->properties_locked)
return JS_TRUE;
decoder = js_layer->decoder;
if (argc != 2) {
JS_ReportError(cx, lm_argc_err_str);
return JS_FALSE;
}
if ((!JSVAL_IS_STRING(argv[0]) &&
!JS_ConvertValue(cx, argv[0], JSTYPE_STRING, &argv[0])) ||
!JS_ValueToNumber(cx, argv[1], &width))
return JS_FALSE;
url = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
url = (char *)lm_CheckURL(cx, url, JS_TRUE);
if (! url)
return JS_FALSE;
referer = lm_GetSubjectOriginURL(cx);
if (! referer) {
XP_FREE(url);
return JS_FALSE;
}
ret = (JSBool)ET_TweakLayer(decoder->window_context, NULL,
(int32)width, 0,
(void *)url, js_layer->layer_id,
CL_SetSrcWidth, referer,
decoder->doc_id);
*rval = BOOLEAN_TO_JSVAL(ret);
if (ret) {
lm_NewLayerDocument(decoder, js_layer->layer_id);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_WIDTH);
js_layer->width = (int32)width;
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_SRC);
XP_FREEIF(js_layer->source_url);
js_layer->source_url = url;
decoder->stream_owner = js_layer->layer_id;
if (js_layer->principals)
JSPRINCIPALS_DROP(cx, js_layer->principals);
js_layer->principals = LM_NewJSPrincipals(NULL, NULL, url);
if (js_layer->principals == NULL) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
JSPRINCIPALS_HOLD(cx, js_layer->principals);
}
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
layer_capture(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSLayer *js_layer;
MochaDecoder * decoder;
jsdouble d;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
decoder = js_layer->decoder;
if (!decoder->window_context)
return JS_TRUE;
if (argc != 1)
return JS_TRUE;
if (!JS_ValueToNumber(cx, argv[0], &d))
return JS_FALSE;
js_layer->capturer.event_bit |= (uint32)d;
decoder->window_context->event_bit |= (uint32)d;
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
layer_release(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSLayer *js_layer;
JSEventCapturer *cap;
MochaDecoder *decoder;
jsdouble d;
jsint layer_index;
jsint max_layer_num;
JSObject *cap_obj;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
decoder = js_layer->decoder;
if (!decoder->window_context)
return JS_TRUE;
if (argc != 1)
return JS_TRUE;
if (!JS_ValueToNumber(cx, argv[0], &d))
return JS_FALSE;
js_layer->capturer.event_bit &= ~(uint32)d;
decoder->window_context->event_bit &= ~(uint32)d;
/*Now we have to see if anyone else wanted that bit set. Joy!*/
/*First we check versus window */
decoder->window_context->event_bit |= decoder->event_bit;
/*Now we check versus layers */
max_layer_num = LO_GetNumberOfLayers(decoder->window_context);
for (layer_index=0; layer_index <= max_layer_num; layer_index++) {
cap_obj = LO_GetLayerMochaObjectFromId(decoder->window_context, layer_index);
if (cap_obj && (cap = JS_GetPrivate(cx, cap_obj)) != NULL)
decoder->window_context->event_bit |= cap->event_bit;
cap_obj = lm_GetDocumentFromLayerId(decoder, layer_index);
if (cap_obj && (cap = JS_GetPrivate(cx, cap_obj)) != NULL)
decoder->window_context->event_bit |= cap->event_bit;
}
return JS_TRUE;
}
static PRBool
setExternalCapture(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, JSBool val)
{
JSLayer *js_layer;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
if (argc != 0)
return JS_TRUE;
if (lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_BROWSER_WRITE)) {
JSPrincipals *principals;
principals = lm_GetInnermostPrincipals(cx, obj, NULL);
if (principals == NULL)
return JS_FALSE;
lm_SetExternalCapture(cx, principals, val);
}
return JS_TRUE;
}
PR_STATIC_CALLBACK(PRBool)
layer_enable_external_capture(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return setExternalCapture(cx, obj, argc, argv, JS_TRUE);
}
PR_STATIC_CALLBACK(PRBool)
layer_disable_external_capture(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return setExternalCapture(cx, obj, argc, argv, JS_FALSE);
}
PR_STATIC_CALLBACK(PRBool)
layer_compromise_principals(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSLayer *js_layer;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
js_layer->principals_compromised = JS_TRUE;
return JS_TRUE;
}
PR_STATIC_CALLBACK(PRBool)
layer_downgrade_principals(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSLayer *js_layer;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
if (js_layer->principals)
lm_InvalidateCertPrincipals(js_layer->decoder, js_layer->principals);
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
layer_initStandardObjects(JSContext *cx, JSObject *obj, uint argc, jsval *argv,
jsval *rval)
{
JSLayer *js_layer;
if (!(js_layer = JS_GetInstancePrivate(cx, obj, &lm_layer_class, argv)))
return JS_FALSE;
return (JSBool)(JS_InitStandardClasses(cx, obj) &&
lm_DefineWindowProps(cx, js_layer->decoder));
}
/* The minimum number of arguments for each of the functions in this
table is set to zero, so that argc is the actual number of arguments
passed by the user. */
static JSFunctionSpec layer_methods[] = {
{"offset", offset_layer, 0},
{"moveBy", offset_layer, 0},
{"moveTo", move_layer_rel, 0},
{"moveToAbsolute", move_layer_abs, 0},
{"resize", resize_layer_to, 0},
{"resizeTo", resize_layer_to, 0},
{"resizeBy", resize_layer_by, 0},
{"moveAbove", move_above, 0},
{"moveBelow", move_below, 0},
{"load", load_layer, 0},
{"captureEvents", layer_capture, 0},
{"releaseEvents", layer_release, 0},
{"enableExternalCapture", layer_enable_external_capture, 0 },
{"disableExternalCapture", layer_disable_external_capture, 0 },
{"compromisePrincipals", layer_compromise_principals, 0 },
{"downgradePrincipals", layer_downgrade_principals, 0 },
{"initStandardObjects", layer_initStandardObjects, 0},
{0}
};
static JSObject *
lm_reflect_layer_using_existing_obj(MWContext *context,
int32 layer_id,
int32 parent_layer_id,
PA_Tag *tag,
JSObject *obj);
PR_STATIC_CALLBACK(JSBool)
Layer(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSObject *parent_layer_obj = NULL, *window_obj, *enclosing_obj;
JSLayer *js_layer_parent;
int32 wrap_width = 0;
int32 parent_layer_id, layer_id;
MochaDecoder *decoder;
MWContext *context;
XP_ASSERT(JS_InstanceOf(cx, obj, &lm_layer_class, NULL));
/* XXX - Hack to force constructor not to allocate any layers */
if (argc == 0)
return JS_TRUE;
if (argc < 1) {
JS_ReportError(cx, lm_argc_err_str);
return JS_FALSE;
}
/* Mandatory first argument is layer's wrap width. */
if (!JS_ValueToInt32(cx, argv[0], &wrap_width))
return JS_FALSE;
/* By default, we're creating the layer for the current window. */
window_obj = JS_GetGlobalObject(cx);
/* Get enclosing window or layer of the code that calls the constructor. */
parent_layer_id = LO_DOCUMENT_LAYER_ID; /* Default */
enclosing_obj = JS_GetScopeChain(cx);
while (enclosing_obj) {
if (JS_InstanceOf(cx, enclosing_obj, &lm_layer_class, 0)) {
js_layer_parent = JS_GetPrivate(cx, enclosing_obj);
parent_layer_id = js_layer_parent->layer_id;
break;
}
if (JS_InstanceOf(cx, enclosing_obj, &lm_window_class, 0))
break;
enclosing_obj = JS_GetParent(cx, enclosing_obj);
}
/* Optional second argument is parent layer */
if (argc >= 2) {
/* The parent has to be an object */
if ((JS_TypeOfValue(cx, argv[1]) == JSTYPE_OBJECT) &&
((parent_layer_obj = JSVAL_TO_OBJECT(argv[1])) != NULL)) {
/* If the parent is a layer, we get its layer_id */
if (JS_InstanceOf(cx, parent_layer_obj, &lm_layer_class, 0)) {
js_layer_parent = JS_GetPrivate(cx, parent_layer_obj);
parent_layer_id = js_layer_parent->layer_id;
decoder = js_layer_parent->decoder;
window_obj = decoder->window_object;
}
/*
* If the parent is another window, the new layer is created
* in that window's context as a top-level layer.
*/
else if (JS_InstanceOf(cx, parent_layer_obj,
&lm_window_class, 0)) {
parent_layer_id = LO_DOCUMENT_LAYER_ID;
window_obj = parent_layer_obj;
}
else
return JS_FALSE;
} else {
return JS_FALSE;
}
}
decoder = JS_GetPrivate(cx, window_obj);
context = decoder->window_context;
/* From a security standpoint, it's dangerous to allow new
top-level layers to be created in a mail/news window. */
if (IS_MESSAGE_WINDOW(context) &&
(parent_layer_id == LO_DOCUMENT_LAYER_ID)) {
JS_ReportError(cx,
"Disallowed attempt to create new top-level layer"
" in a message window");
return JS_FALSE;
}
layer_id = ET_PostCreateLayer(context, wrap_width, parent_layer_id);
/* Zero-valued return means layout/parser stream is busy. A -1
return indicates error. */
if (!layer_id || layer_id == -1)
return JS_FALSE;
/* Reflect the layer, but don't create a new object parented by
the layers array. Rather, use the object that we're
constructing now. */
obj = lm_reflect_layer_using_existing_obj(context, layer_id,
parent_layer_id, NULL, obj);
if (!obj)
return JS_FALSE;
return JS_TRUE;
}
static JSObject *
lm_init_layer_clip(MochaDecoder *decoder,
int32 layer_id,
JSObject *parent_layer_obj);
void
lm_RestoreLayerState(MWContext *context, int32 layer_id,
LO_BlockInitializeStruct *param)
{
PRHashTable *map;
MochaDecoder *decoder;
JSContext *cx;
JSLayer *js_layer;
JSObject *obj, *clip_obj;
jsval val;
decoder = LM_GetMochaDecoder(context);
if (!decoder) /* Paranoia */
return;
cx = decoder->js_context;
/*
* Get the mocha object associated with this layer, since it's been
* preserved across a resize_relayout.
*/
map = lm_GetIdToObjectMap(decoder);
if (map)
obj = (JSObject *)PR_HashTableLookup(map,
LM_GET_MAPPING_KEY(LM_LAYERS, 0, layer_id));
if (!obj)
goto out;
js_layer = JS_GetPrivate(cx, obj);
if (!js_layer)
goto out;
/*
* For each property that was modified, set the value for the
* corresponding attribute in the param struct.
*/
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_LEFT) &&
JS_LookupProperty(cx, obj, lm_left_str, &val)) {
param->has_left = TRUE;
param->left = JSVAL_TO_INT(val);
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_TOP) &&
JS_LookupProperty(cx, obj, lm_top_str, &val)) {
param->has_top = TRUE;
param->top = JSVAL_TO_INT(val);
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_WIDTH)) {
param->has_width = TRUE;
param->width = js_layer->width;
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_SIB_ABOVE)) {
XP_FREEIF(param->above);
param->above = XP_STRDUP(JS_GetStringBytes(js_layer->sibling_above));
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_SIB_BELOW)) {
XP_FREEIF(param->below);
param->below = XP_STRDUP(JS_GetStringBytes(js_layer->sibling_below));
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_ZINDEX) &&
JS_LookupProperty(cx, obj, lm_zindex_str, &val)) {
param->has_zindex = TRUE;
param->zindex = JSVAL_TO_INT(val);
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_VISIBILITY) &&
JS_LookupProperty(cx, obj, lm_visibility_str, &val)) {
XP_FREEIF(param->visibility);
param->visibility = XP_STRDUP(JS_GetStringBytes(JSVAL_TO_STRING(val)));
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_BGCOLOR) &&
JS_LookupProperty(cx, obj, lm_bgcolor_str, &val)) {
XP_FREEIF(param->bgcolor);
param->is_style_bgcolor = FALSE;
param->bgcolor = XP_ALLOC(10);
PR_snprintf(param->bgcolor, 10, "%x", JSVAL_TO_INT(val));
}
lm_really_resolve_layer = FALSE;
if (JS_LookupProperty(cx, obj, lm_background_str, &val) &&
JSVAL_IS_OBJECT(val)) {
JSObject *background;
if (JS_ValueToObject(cx, val, &background) && background &&
JS_LookupProperty(cx, background, "src", &val) &&
JSVAL_IS_STRING(val)) {
XP_FREEIF(param->bgimage);
param->bgimage = XP_STRDUP(JS_GetStringBytes(JSVAL_TO_STRING(val)));
}
}
lm_really_resolve_layer = TRUE;
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_SRC) &&
js_layer->source_url) {
XP_FREEIF(param->src);
param->src = XP_STRDUP(js_layer->source_url);
}
if (JS_LookupProperty(cx, obj, lm_clip_str, &val) &&
JSVAL_IS_OBJECT(val)) {
clip_obj = JSVAL_TO_OBJECT(val);
if (!clip_obj)
goto out;
if (!param->clip &&
(LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_LEFT) ||
LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_RIGHT) ||
LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_BOTTOM) ||
LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_TOP))) {
param->clip = XP_NEW_ZAP(XP_Rect);
if (!param->clip)
goto out;
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_LEFT) &&
JS_LookupProperty(cx, clip_obj, lm_left_str, &val)) {
param->clip->left = JSVAL_TO_INT(val);
param->clip_expansion_policy &= ~LO_AUTO_EXPAND_CLIP_LEFT;
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_RIGHT) &&
JS_LookupProperty(cx, clip_obj, lm_right_str, &val)) {
param->clip->right = JSVAL_TO_INT(val);
param->clip_expansion_policy &= ~LO_AUTO_EXPAND_CLIP_RIGHT;
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_TOP) &&
JS_LookupProperty(cx, clip_obj, lm_top_str, &val)) {
param->clip->top = JSVAL_TO_INT(val);
param->clip_expansion_policy &= ~LO_AUTO_EXPAND_CLIP_TOP;
}
if (LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_BOTTOM) &&
JS_LookupProperty(cx, clip_obj, lm_bottom_str, &val)) {
param->clip->bottom = JSVAL_TO_INT(val);
param->clip_expansion_policy &= ~LO_AUTO_EXPAND_CLIP_BOTTOM;
}
}
out:
LM_PutMochaDecoder(decoder);
return;
}
static void
lm_process_layer_tag(MochaDecoder *decoder, PA_Tag *tag, JSObject *obj)
{
MWContext *context = decoder->window_context;
PA_Block onload, onunload, onmouseover, onmouseout, onblur, onfocus, id;
PA_Block properties_locked;
JSLayer *js_layer;
JSContext *cx;
char *source_url;
cx = decoder->js_context;
js_layer = JS_GetPrivate(cx, obj);
if (!js_layer)
return;
if (tag) {
onload = lo_FetchParamValue(context, tag, PARAM_ONLOAD);
onunload = lo_FetchParamValue(context, tag, PARAM_ONUNLOAD);
onmouseover = lo_FetchParamValue(context, tag, PARAM_ONMOUSEOVER);
onmouseout = lo_FetchParamValue(context, tag, PARAM_ONMOUSEOUT);
onblur = lo_FetchParamValue(context, tag, PARAM_ONBLUR);
onfocus = lo_FetchParamValue(context, tag, PARAM_ONFOCUS);
id = lo_FetchParamValue(context, tag, PARAM_ID);
properties_locked = lo_FetchParamValue(context, tag, PARAM_LOCKED);
/* don't hold the layout lock across compiles */
LO_UnlockLayout();
if (properties_locked) {
js_layer->properties_locked = PR_TRUE;
PA_FREE(properties_locked);
}
if (onload != NULL) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONLOAD, onload);
PA_FREE(onload);
}
if (onunload != NULL) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONUNLOAD, onunload);
PA_FREE(onunload);
}
if (onmouseover != NULL) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONMOUSEOVER, onmouseover);
PA_FREE(onmouseover);
}
if (onmouseout != NULL) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONMOUSEOUT, onmouseout);
PA_FREE(onmouseout);
}
if (onblur != NULL) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONBLUR, onblur);
PA_FREE(onblur);
}
if (onfocus != NULL) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONFOCUS, onfocus);
PA_FREE(onfocus);
}
if (id)
PA_FREE(id);
LO_LockLayout();
source_url = (char*)lo_FetchParamValue(context, tag,
PARAM_SRC);
if (source_url) {
/*
* Temporarily unlock layout so that we don't hold the lock
* across a call (lm_CheckURL) that may result in
* synchronous event handling.
*/
LO_UnlockLayout();
js_layer->source_url = (char *)lm_CheckURL(cx, source_url,
JS_TRUE);
if (js_layer->principals)
JSPRINCIPALS_DROP(cx, js_layer->principals);
js_layer->principals = LM_NewJSPrincipals(NULL, NULL,
js_layer->source_url);
if (js_layer->principals)
JSPRINCIPALS_HOLD(cx, js_layer->principals);
LO_LockLayout();
}
}
}
static JSObject *
lm_reflect_layer_using_existing_obj(MWContext *context,
int32 layer_id,
int32 parent_layer_id,
PA_Tag *tag,
JSObject *obj)
{
JSObject *array_obj, *clip_obj, *doc_obj;
MochaDecoder *decoder;
JSContext *cx;
JSLayer *js_layer;
char *name = NULL;
uint flags = JSPROP_READONLY;
JSBool bFreeName = JS_FALSE;
static fake_layer_count = 0;
CL_Layer *layer;
lo_TopState *top_state;
PRHashTable *map;
jsval existing_layer;
char *newname = NULL;
JSLayer *existing_js_layer;
JSObject *existing_layer_object;
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return NULL;
if (!obj) {
obj = LO_GetLayerMochaObjectFromId(context, layer_id);
if (obj) { /* Already reflected ? */
/*
* We might already have been reflected by someone other than the
* layer tag processing code (e.g. as a result of entity
* processing). In that case, we just process the tag if it
* exists.
*/
if (tag)
lm_process_layer_tag(decoder, tag, obj);
LM_PutMochaDecoder(decoder);
return obj;
}
}
cx = decoder->js_context;
top_state = lo_GetMochaTopState(context);
if (!obj && top_state->resize_reload && !decoder->load_event_sent) {
map = lm_GetIdToObjectMap(decoder);
if (map)
obj = (JSObject *)PR_HashTableLookup(map,
LM_GET_MAPPING_KEY(LM_LAYERS, 0, layer_id));
if (obj) {
LO_SetLayerMochaObject(context, layer_id, obj);
LM_PutMochaDecoder(decoder);
return obj;
}
}
/* Layer is accessible by name in child array of parent layer.
For example a layer named "Fred" could be accessed as
document.layers["Fred"] if it is a child of the distinguished
_DOCUMENT layer. */
if (parent_layer_id == LO_DOCUMENT_LAYER_ID)
array_obj = lm_GetDocumentLayerArray(decoder, decoder->document);
else
array_obj =
lm_GetLayerArray(decoder,
LO_GetLayerMochaObjectFromId(context,
parent_layer_id));
if (!array_obj) {
LM_PutMochaDecoder(decoder);
return NULL;
}
js_layer = JS_malloc(cx, sizeof *js_layer);
if (!js_layer) {
LM_PutMochaDecoder(decoder);
return NULL;
}
XP_BZERO(js_layer, sizeof *js_layer);
if (!obj) {
obj = JS_NewObject(cx, &lm_layer_class, decoder->layer_prototype,
array_obj);
} else {
/* Anonymous layers, created with the "new" operator, are
scoped by their parent layer's document */
JS_SetParent(cx, obj, array_obj);
}
LO_SetLayerMochaObject(context, layer_id, obj);
/* Put it in the index to object hash table */
map = lm_GetIdToObjectMap(decoder);
if (map)
PR_HashTableAdd(map,
LM_GET_MAPPING_KEY(LM_LAYERS, 0, layer_id),
obj);
if (obj)
clip_obj = lm_init_layer_clip(decoder, layer_id, obj);
if (obj && clip_obj)
doc_obj = lm_DefineDocument(decoder, layer_id);
if (!obj || !clip_obj || !doc_obj || !JS_SetPrivate(cx, obj, js_layer)) {
JS_free(cx, js_layer);
LM_PutMochaDecoder(decoder);
return NULL;
}
JS_AddNamedRoot(cx, &js_layer->sibling_above, "layer.sibling_above");
JS_AddNamedRoot(cx, &js_layer->sibling_below, "layer.sibling_below");
layer = LO_GetLayerFromId(context, layer_id);
if (layer)
name = CL_GetLayerName(layer);
if (name) {
flags |= JSPROP_ENUMERATE;
} else {
name = PR_smprintf("_js_layer_%d", fake_layer_count++);
bFreeName = JS_TRUE;
if (!name) {
LM_PutMochaDecoder(decoder);
return NULL;
}
}
/* XXXMLM - There's a problem where a user creates two layers that
* have the same parent and the same name. What used to
* happen was that JS_DefineProperty would simply overwrite
* the old property, causing the old property to become
* unrooted, and therefore freed the next time the GC was
* run. Since the CL_Layer keeps a mocha_object pointer
* around, the pointer was stale as soon as the GC was
* run, and it would crash.
*
* We need a function JS_HasProperty so we can figure out
* if the property has been defined yet, without resolving
* the property through native code. We don't have the
* function yet, so we take the backwards approach - if
* we know we're trying to find a property without resolving
* it, we set a static boolean that our resolving function
* sees and knows not to do the resolution.
*/
layer_array_should_resolve = JS_FALSE;
if(!JS_LookupProperty(cx, array_obj, name, &existing_layer)) {
layer_array_should_resolve = JS_TRUE;
obj = NULL;
goto out;
}
layer_array_should_resolve = JS_TRUE;
if(JSVAL_IS_OBJECT(existing_layer)) {
/* Hrmph. Okay, rename the old one. */
existing_layer_object = JSVAL_TO_OBJECT(existing_layer);
if(!existing_layer_object) {
obj = NULL;
goto out;
}
existing_js_layer = JS_GetPrivate(cx, existing_layer_object);
if(!existing_js_layer) {
obj = NULL;
goto out;
}
newname = PR_smprintf("%s_%d", name, fake_layer_count++);
if(!newname) {
obj = NULL;
goto out;
}
existing_js_layer->name = JS_NewStringCopyZ(cx, newname);
JS_DefineProperty(cx, array_obj, newname,
OBJECT_TO_JSVAL(existing_layer_object),
NULL, NULL, flags);
XP_FREE(newname);
}
if (name &&
!JS_DefineProperty(cx, array_obj, name, OBJECT_TO_JSVAL(obj),
NULL, NULL, flags)) {
LM_PutMochaDecoder(decoder);
if (bFreeName)
XP_FREE(name);
return NULL;
}
js_layer->decoder = HOLD_BACK_COUNT(decoder);
js_layer->layer_id = layer_id;
js_layer->name = JS_NewStringCopyZ(cx, name);
js_layer->source_url = NULL;
if (!JS_AddRoot(cx, &js_layer->name)) {
LM_PutMochaDecoder(decoder);
if (bFreeName)
XP_FREE(name);
return NULL;
}
if(tag)
lm_process_layer_tag(decoder, tag, obj);
out:
LM_PutMochaDecoder(decoder);
if (bFreeName)
XP_FREE(name);
return obj;
}
JSObject *
LM_ReflectLayer(MWContext *context, int32 layer_id, int32 parent_layer_id,
PA_Tag *tag)
{
return lm_reflect_layer_using_existing_obj(context, layer_id,
parent_layer_id, tag, NULL);
}
JSObject *
lm_GetNamedLayer(MochaDecoder *decoder, int32 parent_layer_id,
const char *name)
{
MWContext *context = decoder->window_context;
lo_TopState *top_state;
CL_Layer *layer, *parent_layer;
int32 layer_id;
JSObject *layer_obj = NULL;
LO_LockLayout();
top_state = lo_GetMochaTopState(context);
if (top_state == NULL)
goto done;
if(!context || decoder->doc_id != XP_DOCID(context))
goto done;
/* Get the parent layer (the owner of this array) */
parent_layer = LO_GetLayerFromId(context, parent_layer_id);
if (!parent_layer)
goto done;
/* Search for the child layer by name */
layer = CL_GetLayerChildByName(parent_layer, (char *)name);
if (!layer)
goto done;
/* Reflect the child layer if it hasn't been reflected yet */
layer_id = LO_GetIdFromLayer(context, layer);
layer_obj = LM_ReflectLayer(context, layer_id,
parent_layer_id,
NULL);
done:
LO_UnlockLayout();
return layer_obj;
}
void
lm_NewLayerDocument(MochaDecoder *decoder, int32 layer_id)
{
JSObject *layer_obj, *doc_obj, *layer_array_obj;
JSLayer *js_layer;
JSContext *cx;
jsval layer_array_jsval;
cx = decoder->js_context;
LO_LockLayout();
layer_obj = LO_GetLayerMochaObjectFromId(decoder->window_context,
layer_id);
if (!layer_obj) {
LO_UnlockLayout();
return;
}
js_layer = JS_GetPrivate(cx, layer_obj);
if (!js_layer) {
LO_UnlockLayout();
return;
}
/* Throw away the child_layers_array */
js_layer->child_layers_array_obj = NULL;
/* Reset security status, clear watchpoints to close security holes. */
/* XXX must clear scope or evil.org's functions/vars persist into signed
script doc loaded from victim.com! */
js_layer->principals_compromised = JS_FALSE;
JS_ClearWatchPointsForObject(cx, layer_obj);
/*
* Clean out a document i.e. clean out the arrays associated with
* the document, but not all its properties.
*/
doc_obj = lm_GetDocumentFromLayerId(decoder, layer_id);
lm_CleanUpDocument(decoder, doc_obj);
/* We're about to replace the contents of this layer, so sever
all the references to the layer's children, so that they
can be GC'ed. */
if (JS_LookupProperty(cx, layer_obj, "layers", &layer_array_jsval) &&
JSVAL_IS_OBJECT(layer_array_jsval)) {
layer_array_obj = JSVAL_TO_OBJECT(layer_array_jsval);
JS_ClearScope(cx, layer_array_obj);
JS_DefineProperties(cx, layer_array_obj, layer_array_props);
}
LO_UnlockLayout();
}
void
lm_SendLayerLoadEvent(MWContext *context, int32 event, int32 layer_id,
JSBool resize_reload)
{
MochaDecoder *decoder;
JSObject *obj;
JSEvent *pEvent;
jsval rval;
decoder = context->mocha_context ? LM_GetMochaDecoder(context) : 0;
if (resize_reload)
goto out;
LO_LockLayout();
obj = LO_GetLayerMochaObjectFromId(context, layer_id);
LO_UnlockLayout();
if (!obj)
goto out;
/*
* XXX Right now, we send the load event when the layer is finished
* laying out, not when all the streams associated with a layer
* are completed.
*/
switch (event) {
case EVENT_LOAD:
case EVENT_UNLOAD:
pEvent = XP_NEW_ZAP(JSEvent);
if (!pEvent)
goto out;
pEvent->type = event;
(void) lm_SendEvent(context, obj, pEvent, &rval);
if (!pEvent->saved)
XP_FREE(pEvent);
break;
case EVENT_ABORT:
break;
default: ;
}
out:
if (decoder)
LM_PutMochaDecoder(decoder);
return;
}
void
lm_DestroyLayer(MWContext *context, JSObject *obj)
{
jsval val;
JSObject *child_doc_obj, *parent_doc_obj, *layer_array_obj;
JSLayer *js_layer;
JSBool ok;
MochaDecoder *decoder;
JSContext *cx;
char *layer_name;
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return;
cx = decoder->js_context;
if (!JS_GetProperty(cx, obj, lm_document_str, &val) ||
!JSVAL_IS_OBJECT(val)) {
goto end;
}
child_doc_obj = JSVAL_TO_OBJECT(val);
if (!child_doc_obj)
goto end;
/* If we had a layer called "foo", then there was a "document.foo"
property created in the parent document at the time this layer
was reflected. Get rid of it, so that we can GC the layer
object. */
layer_array_obj = JS_GetParent(cx, obj);
if (!layer_array_obj)
goto end;
parent_doc_obj = JS_GetParent(cx, layer_array_obj);
if (!parent_doc_obj)
goto end;
js_layer = JS_GetPrivate(cx, obj);
layer_name = (char *)JS_GetStringBytes(js_layer->name);
ok = JS_DeleteProperty(cx, parent_doc_obj, layer_name);
XP_ASSERT(ok);
lm_CleanUpDocumentRoots(decoder, child_doc_obj);
end:
LM_PutMochaDecoder(decoder);
}
/*
* Called to set the source URL as a result of a document.open() on the
* layer's document.
*/
JSBool
lm_SetLayerSourceURL(MochaDecoder *decoder, int32 layer_id, char *url)
{
MWContext *context;
JSObject *layer_obj;
JSLayer *js_layer;
context = decoder->window_context;
if (!context)
return JS_FALSE;
LO_LockLayout();
layer_obj = LO_GetLayerMochaObjectFromId(context, layer_id);
LO_UnlockLayout();
if (!layer_obj)
return JS_FALSE;
js_layer = JS_GetPrivate(decoder->js_context, layer_obj);
if (!js_layer)
return JS_FALSE;
XP_FREEIF(js_layer->source_url);
js_layer->source_url = XP_STRDUP(url);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_SRC);
decoder->stream_owner = layer_id;
return JS_TRUE;
}
const char *
lm_GetLayerOriginURL(JSContext *cx, JSObject *obj) {
JSLayer *js_layer = JS_GetPrivate(cx, obj);
if (js_layer == NULL || js_layer->source_url == NULL) {
return NULL;
}
return js_layer->source_url;
}
JSBool
lm_InitLayerClass(MochaDecoder *decoder)
{
JSContext *cx;
JSObject *prototype;
cx = decoder->js_context;
prototype = JS_InitClass(cx, decoder->window_object,
decoder->event_capturer_prototype, &lm_layer_class,
Layer, 1, layer_props, layer_methods, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->layer_prototype = prototype;
return JS_TRUE;
}
/* ======================== Rect Class definition ======================= */
typedef struct JSRect {
MochaDecoder *decoder;
int32 layer_id;
} JSRect;
/* Static compositor rect properties */
static JSPropertySpec rect_props[] = {
{lm_left_str, RECT_LEFT, JSPROP_ENUMERATE},
{lm_top_str, RECT_TOP, JSPROP_ENUMERATE},
{lm_right_str, RECT_RIGHT, JSPROP_ENUMERATE},
{lm_bottom_str, RECT_BOTTOM, JSPROP_ENUMERATE},
{"height", RECT_HEIGHT, JSPROP_ENUMERATE},
{"width", RECT_WIDTH, JSPROP_ENUMERATE},
{0}
};
PR_STATIC_CALLBACK(JSBool)
rect_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
MochaDecoder *decoder;
MWContext *context;
JSRect *js_rect;
XP_Rect bbox;
jsint slot;
CL_Layer *layer;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
js_rect = JS_GetPrivate(cx, obj);
if (!js_rect)
return JS_TRUE;
decoder = js_rect->decoder;
context = decoder->window_context;
LO_LockLayout();
if(!context || decoder->doc_id != XP_DOCID(context)) {
LO_UnlockLayout();
return JS_FALSE;
}
layer = LO_GetLayerFromId(context, js_rect->layer_id);
if (!layer) {
LO_UnlockLayout();
return JS_TRUE;
}
CL_GetLayerBbox(layer, &bbox);
switch (slot) {
case RECT_LEFT:
*vp = INT_TO_JSVAL(bbox.left);
break;
case RECT_RIGHT:
*vp = INT_TO_JSVAL(bbox.right);
break;
case RECT_TOP:
*vp = INT_TO_JSVAL(bbox.top);
break;
case RECT_BOTTOM:
*vp = INT_TO_JSVAL(bbox.bottom);
break;
case RECT_WIDTH:
*vp = INT_TO_JSVAL(bbox.right - bbox.left);
break;
case RECT_HEIGHT:
*vp = INT_TO_JSVAL(bbox.bottom - bbox.top);
break;
default:
break;
}
LO_UnlockLayout();
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
rect_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSRect *js_rect;
MochaDecoder *decoder;
MWContext *context;
CL_Layer *layer;
int32 val;
enum rect_slot rect_slot;
jsint slot;
JSObject *layer_obj;
JSLayer *js_layer;
jsval js_val;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
rect_slot = slot = JSVAL_TO_INT(id);
js_rect = JS_GetPrivate(cx, obj);
if (!js_rect)
return JS_TRUE;
decoder = js_rect->decoder;
layer = LO_GetLayerFromId(decoder->window_context, js_rect->layer_id);
if (!layer)
return JS_TRUE;
layer_obj = LO_GetLayerMochaObjectFromId(decoder->window_context,
js_rect->layer_id);
if (!layer_obj)
return JS_TRUE;
js_layer = JS_GetPrivate(cx, layer_obj);
if (!js_layer)
return JS_TRUE;
/* Some layers have mostly read-only properties because we
don't want a malicious/careless JS author to modify them,
e.g. for layers that are used to encapulate a mail
message. In such cases, we fail silently. */
if (js_layer->properties_locked)
return JS_TRUE;
context = decoder->window_context;
if (rect_slot == RECT_LEFT ||
rect_slot == RECT_RIGHT ||
rect_slot == RECT_TOP ||
rect_slot == RECT_BOTTOM ||
rect_slot == RECT_WIDTH ||
rect_slot == RECT_HEIGHT) {
XP_Rect bbox;
if (!JS_ValueToInt32(cx, *vp, &val))
return JS_FALSE;
LO_LockLayout();
if(!context || decoder->doc_id != XP_DOCID(context)) {
LO_UnlockLayout();
return JS_FALSE;
}
CL_GetLayerBbox(layer, &bbox);
/* If a layer is dynamically changing, it will probably look
better if it utilizes offscreen compositing. */
CL_ChangeLayerFlag(layer, CL_PREFER_DRAW_OFFSCREEN, PR_TRUE);
switch (rect_slot) {
case RECT_LEFT:
bbox.left = (int32)val;
LO_SetLayerBbox(layer, &bbox);
CLEAR_LAYER_EXPANSION_POLICY(layer, LO_AUTO_EXPAND_CLIP_LEFT);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_LEFT);
break;
case RECT_RIGHT:
bbox.right = (int32)val;
LO_SetLayerBbox(layer, &bbox);
CLEAR_LAYER_EXPANSION_POLICY(layer, LO_AUTO_EXPAND_CLIP_RIGHT);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_RIGHT);
break;
case RECT_TOP:
bbox.top = (int32)val;
LO_SetLayerBbox(layer, &bbox);
CLEAR_LAYER_EXPANSION_POLICY(layer, LO_AUTO_EXPAND_CLIP_TOP);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_TOP);
break;
case RECT_BOTTOM:
bbox.bottom = (int32)val;
LO_SetLayerBbox(layer, &bbox);
CLEAR_LAYER_EXPANSION_POLICY(layer, LO_AUTO_EXPAND_CLIP_BOTTOM);
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_BOTTOM);
break;
case RECT_WIDTH:
bbox.right = bbox.left + (int32)val;
LO_SetLayerBbox(layer, &bbox);
CLEAR_LAYER_EXPANSION_POLICY(layer, LO_AUTO_EXPAND_CLIP_RIGHT);
if (!LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_RIGHT)) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_RIGHT);
JS_DefinePropertyWithTinyId(cx, obj, lm_right_str, RECT_RIGHT,
INT_TO_JSVAL(bbox.right),
rect_getProperty,
rect_setProperty,
JSPROP_ENUMERATE);
}
else
JS_GetProperty(cx, obj, lm_right_str, &js_val);
break;
case RECT_HEIGHT:
bbox.bottom = bbox.top + (int32)val;
LO_SetLayerBbox(layer, &bbox);
CLEAR_LAYER_EXPANSION_POLICY(layer, LO_AUTO_EXPAND_CLIP_BOTTOM);
if (!LM_CHECK_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_BOTTOM)) {
LM_SET_LAYER_MODIFICATION(js_layer, LAYER_MOD_CLIP_BOTTOM);
JS_DefinePropertyWithTinyId(cx, obj, lm_bottom_str,
RECT_BOTTOM,
INT_TO_JSVAL(bbox.bottom),
rect_getProperty,
rect_setProperty,
JSPROP_ENUMERATE);
}
else
JS_GetProperty(cx, obj, lm_bottom_str, &js_val);
break;
}
}
LO_UnlockLayout();
return rect_getProperty(cx, obj, id, vp);
}
PR_STATIC_CALLBACK(void)
rect_finalize(JSContext *cx, JSObject *obj)
{
JSRect *js_rect;
js_rect = JS_GetPrivate(cx, obj);
if (!js_rect)
return;
DROP_BACK_COUNT(js_rect->decoder);
JS_free(cx, js_rect);
}
static JSClass rect_class = {
"Rect", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, rect_getProperty, rect_setProperty,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, rect_finalize
};
PR_STATIC_CALLBACK(JSBool)
RectConstructor(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
/* Initialize the 'clip' property of a layer to be a Rect object */
static JSObject *
lm_init_layer_clip(MochaDecoder *decoder,
int32 layer_id,
JSObject *parent_layer_obj)
{
JSObject *obj;
JSContext *cx;
JSRect *js_rect;
cx = decoder->js_context;
js_rect = JS_malloc(cx, sizeof *js_rect);
if (!js_rect)
return NULL;
js_rect->decoder = NULL; /* in case of error below */
obj = JS_NewObject(cx, &rect_class, decoder->rect_prototype,
parent_layer_obj);
if (!obj || !JS_SetPrivate(cx, obj, js_rect)) {
JS_free(cx, js_rect);
return NULL;
}
if (!JS_DefineProperty(cx, parent_layer_obj, lm_clip_str,
OBJECT_TO_JSVAL(obj),
NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY)) {
return NULL;
}
js_rect->decoder = HOLD_BACK_COUNT(decoder);
js_rect->layer_id = layer_id;
return obj;
}
JSBool
lm_InitRectClass(MochaDecoder *decoder)
{
JSContext *cx;
JSObject *prototype;
cx = decoder->js_context;
prototype = JS_InitClass(cx, decoder->window_object, NULL, &rect_class,
RectConstructor, 0, rect_props, NULL, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->rect_prototype = prototype;
return JS_TRUE;
}
static JSPrincipals **
getContainerPrincipalsAddress(JSContext *cx, JSObject *container)
{
JSClass *classp = JS_GetClass(cx, container);
if (classp == &lm_window_class) {
MochaDecoder *decoder = JS_GetPrivate(cx, container);
return decoder ? &decoder->principals : NULL;
} else if (classp == &lm_layer_class) {
JSLayer *layer = JS_GetPrivate(cx, container);
return layer ? &layer->principals : NULL;
} else {
return NULL;
}
}
extern JSPrincipals *
lm_GetContainerPrincipals(JSContext *cx, JSObject *container)
{
JSPrincipals **p = getContainerPrincipalsAddress(cx, container);
return p ? *p : NULL;
}
extern void
lm_SetContainerPrincipals(JSContext *cx, JSObject *container,
JSPrincipals *principals)
{
JSPrincipals **p = getContainerPrincipalsAddress(cx, container);
if (p && *p != principals) {
if (*p) {
JSPRINCIPALS_DROP(cx, *p);
}
*p = principals;
JSPRINCIPALS_HOLD(cx, *p);
}
}
extern JSObject *
lm_GetActiveContainer(MochaDecoder *decoder)
{
if (decoder->active_layer_id == LO_DOCUMENT_LAYER_ID) {
/* immediate container is the window */
return decoder->window_object;
} else {
/* immediate container is a layer */
JSObject *result;
LO_LockLayout();
result = LO_GetLayerMochaObjectFromId(decoder->window_context,
decoder->active_layer_id);
LO_UnlockLayout();
return result;
}
}
extern JSBool
lm_GetPrincipalsCompromise(JSContext *cx, JSObject *obj)
{
JSClass *clasp;
MochaDecoder *decoder;
JSLayer *js_layer;
JSBool flag;
clasp = JS_GetClass(cx, obj);
if (clasp == &lm_window_class) {
decoder = JS_GetPrivate(cx, obj);
flag = (JSBool)decoder->principals_compromised;
} else if (clasp == &lm_layer_class) {
js_layer = JS_GetPrivate(cx, obj);
flag = (JSBool)js_layer->principals_compromised;
} else {
flag = JS_FALSE;
}
return flag;
}