mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-27 19:09:47 +00:00
941 lines
25 KiB
C
941 lines
25 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.
|
|
*/
|
|
/*
|
|
* JS reflection of the current Navigator URL (Location) object.
|
|
*
|
|
* Brendan Eich, 9/8/95
|
|
*/
|
|
#include "lm.h"
|
|
#include "xp.h"
|
|
#include "net.h" /* for URL_Struct */
|
|
#include "shist.h" /* for session history stuff */
|
|
#include "structs.h" /* for MWContext */
|
|
#include "layout.h" /* included via -I../layout */
|
|
#include "mkparse.h" /* included via -I../libnet */
|
|
|
|
/* NB: these named properties use non-negative slots; code below knows that. */
|
|
/* Non-negative slots are protected by a permissions check; others aren't. */
|
|
enum url_slot {
|
|
URL_HREF, /* the entire URL as a string */
|
|
URL_PROTOCOL, /* protocol:... */
|
|
URL_HOST, /* protocol://host/... */
|
|
URL_HOSTNAME, /* protocol://hostname:... */
|
|
URL_PORT, /* protocol://hostname:port/... */
|
|
URL_PATHNAME, /* protocol://host/pathname[#?...] */
|
|
URL_HASH, /* protocol://host/pathname#hash */
|
|
URL_SEARCH, /* protocol://host/pathname?search */
|
|
URL_TARGET, /* target window or null */
|
|
URL_TEXT, /* text within A container */
|
|
URL_X = -1, /* layout X coordinate */
|
|
URL_Y = -2 /* layout Y coordinate */
|
|
};
|
|
|
|
static JSPropertySpec url_props[] = {
|
|
{"href", URL_HREF, JSPROP_ENUMERATE},
|
|
{"protocol", URL_PROTOCOL, JSPROP_ENUMERATE},
|
|
{"host", URL_HOST, JSPROP_ENUMERATE},
|
|
{"hostname", URL_HOSTNAME, JSPROP_ENUMERATE},
|
|
{"port", URL_PORT, JSPROP_ENUMERATE},
|
|
{"pathname", URL_PATHNAME, JSPROP_ENUMERATE},
|
|
{"hash", URL_HASH, JSPROP_ENUMERATE},
|
|
{"search", URL_SEARCH, JSPROP_ENUMERATE},
|
|
{"target", URL_TARGET, JSPROP_ENUMERATE},
|
|
{"text", URL_TEXT, JSPROP_ENUMERATE | JSPROP_READONLY},
|
|
{"x", URL_X, JSPROP_ENUMERATE | JSPROP_READONLY},
|
|
{"y", URL_Y, JSPROP_ENUMERATE | JSPROP_READONLY},
|
|
{0}
|
|
};
|
|
|
|
#define ParseURL(url,part) ((url) ? NET_ParseURL(url,part) : 0)
|
|
|
|
static JSBool
|
|
url_get_coord(JSContext *cx, JSURL *url, jsint slot, jsval *vp)
|
|
{
|
|
int32 coord;
|
|
LO_AnchorData *anchor_data;
|
|
MWContext *context;
|
|
|
|
LO_LockLayout();
|
|
context = url->url_decoder->window_context;
|
|
anchor_data = LO_GetLinkByIndex(context, url->layer_id, url->index);
|
|
if (anchor_data && anchor_data->element) {
|
|
switch (slot) {
|
|
case URL_X:
|
|
coord = anchor_data->element->lo_any.x;
|
|
break;
|
|
|
|
case URL_Y:
|
|
coord = anchor_data->element->lo_any.y;
|
|
break;
|
|
|
|
default:
|
|
LO_UnlockLayout();
|
|
return JS_FALSE;
|
|
}
|
|
|
|
*vp = INT_TO_JSVAL(coord);
|
|
LO_UnlockLayout();
|
|
return JS_TRUE;
|
|
}
|
|
*vp = JSVAL_NULL;
|
|
LO_UnlockLayout();
|
|
return JS_TRUE;
|
|
}
|
|
|
|
extern JSClass lm_url_class;
|
|
extern JSClass lm_location_class;
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
url_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
{
|
|
jsval slot;
|
|
JSURL *url;
|
|
JSString * str;
|
|
char *port;
|
|
const char *cstr, *tmp;
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
return JS_TRUE;
|
|
|
|
slot = JSVAL_TO_INT(id);
|
|
url = JS_GetInstancePrivate(cx, obj, &lm_url_class, NULL);
|
|
if (!url) {
|
|
url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
|
|
if (!url)
|
|
return JS_TRUE;
|
|
}
|
|
|
|
str = 0;
|
|
cstr = 0;
|
|
|
|
switch (slot) {
|
|
case URL_HREF:
|
|
str = url->href;
|
|
break;
|
|
|
|
case URL_PROTOCOL:
|
|
if (url->href)
|
|
cstr = ParseURL(JS_GetStringBytes(url->href), GET_PROTOCOL_PART);
|
|
break;
|
|
|
|
case URL_HOST:
|
|
if (url->href)
|
|
cstr = ParseURL(JS_GetStringBytes(url->href), GET_HOST_PART);
|
|
break;
|
|
|
|
case URL_HOSTNAME:
|
|
if (url->href)
|
|
cstr = ParseURL(JS_GetStringBytes(url->href), GET_HOST_PART);
|
|
if (cstr && (port = XP_STRCHR(cstr, ':')) != 0)
|
|
*port = '\0';
|
|
break;
|
|
|
|
case URL_PORT:
|
|
if (url->href)
|
|
cstr = ParseURL(JS_GetStringBytes(url->href), GET_HOST_PART);
|
|
if (cstr && (port = XP_STRCHR(cstr, ':')) != 0)
|
|
port++;
|
|
else
|
|
port = "";
|
|
tmp = cstr;
|
|
cstr = JS_strdup(cx, port);
|
|
XP_FREE((void *)tmp);
|
|
break;
|
|
|
|
case URL_PATHNAME:
|
|
if (url->href)
|
|
cstr = ParseURL(JS_GetStringBytes(url->href), GET_PATH_PART);
|
|
break;
|
|
|
|
case URL_HASH:
|
|
if (url->href)
|
|
cstr = ParseURL(JS_GetStringBytes(url->href), GET_HASH_PART);
|
|
break;
|
|
|
|
case URL_SEARCH:
|
|
if (url->href)
|
|
cstr = ParseURL(JS_GetStringBytes(url->href), GET_SEARCH_PART);
|
|
break;
|
|
|
|
case URL_TARGET:
|
|
if (!url->target) {
|
|
*vp = JSVAL_NULL;
|
|
return JS_TRUE;
|
|
}
|
|
str = url->target;
|
|
break;
|
|
|
|
case URL_TEXT:
|
|
if (!url->text) {
|
|
*vp = JSVAL_NULL;
|
|
return JS_TRUE;
|
|
}
|
|
str = url->text;
|
|
break;
|
|
|
|
case URL_X:
|
|
case URL_Y:
|
|
return url_get_coord(cx, url, slot, vp);
|
|
|
|
default:
|
|
/* Don't mess with user-defined or method properties. */
|
|
return JS_TRUE;
|
|
}
|
|
|
|
if (!str && cstr)
|
|
str = JS_NewStringCopyZ(cx, cstr);
|
|
if (cstr)
|
|
XP_FREE((char *)cstr);
|
|
if (!str)
|
|
return JS_FALSE;
|
|
*vp = STRING_TO_JSVAL(str);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
url_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
{
|
|
JSURL *url;
|
|
const char *href, *name, *checked_href;
|
|
char *new_href, *prop_name;
|
|
JSBool free_href;
|
|
jsval tmp;
|
|
JSString *str;
|
|
MWContext *context;
|
|
LO_AnchorData *anchor_data;
|
|
JSBool ok;
|
|
jsint slot;
|
|
|
|
url = JS_GetInstancePrivate(cx, obj, &lm_url_class, NULL);
|
|
if (!url) {
|
|
url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
|
|
if (!url)
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* If the property is setting an event handler we find out now so
|
|
* that we can tell the front end to send the event.
|
|
*/
|
|
if (JSVAL_IS_STRING(id)) {
|
|
prop_name = JS_GetStringBytes(JSVAL_TO_STRING(id));
|
|
if (XP_STRCASECMP(prop_name, lm_onClick_str) == 0 ||
|
|
XP_STRCASECMP(prop_name, lm_onMouseDown_str) == 0 ||
|
|
XP_STRCASECMP(prop_name, lm_onMouseOver_str) == 0 ||
|
|
XP_STRCASECMP(prop_name, lm_onMouseOut_str) == 0 ||
|
|
XP_STRCASECMP(prop_name, lm_onMouseUp_str) == 0) {
|
|
context = url->url_decoder->window_context;
|
|
if (context) {
|
|
anchor_data = LO_GetLinkByIndex(context, url->layer_id,
|
|
url->index);
|
|
if (anchor_data)
|
|
anchor_data->event_handler_present = TRUE;
|
|
}
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
XP_ASSERT(JSVAL_IS_INT(id));
|
|
slot = JSVAL_TO_INT(id);
|
|
|
|
if (slot < 0) {
|
|
/* Don't mess with user-defined or method properties. */
|
|
return JS_TRUE;
|
|
}
|
|
|
|
if (!JSVAL_IS_STRING(*vp) &&
|
|
!JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
ok = JS_TRUE;
|
|
|
|
switch (slot) {
|
|
case URL_HREF:
|
|
url->href = JSVAL_TO_STRING(*vp);
|
|
href = JS_GetStringBytes(url->href);
|
|
free_href = JS_FALSE;
|
|
break;
|
|
|
|
case URL_PROTOCOL:
|
|
case URL_HOST:
|
|
case URL_HOSTNAME:
|
|
case URL_PORT:
|
|
case URL_PATHNAME:
|
|
case URL_HASH:
|
|
case URL_SEARCH:
|
|
/* a component property changed -- recompute href. */
|
|
new_href = NULL;
|
|
|
|
#define GET_SLOT(aslot, ptmp) { \
|
|
if (aslot == slot) { \
|
|
*(ptmp) = *vp; \
|
|
} else { \
|
|
if (!JS_GetElement(cx, obj, aslot, ptmp)) { \
|
|
if (new_href) XP_FREE(new_href); \
|
|
return JS_FALSE; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
#define ADD_SLOT(aslot, separator) { \
|
|
GET_SLOT(aslot, &tmp); \
|
|
name = JS_GetStringBytes(JSVAL_TO_STRING(tmp)); \
|
|
if (*name) { \
|
|
if (separator) StrAllocCat(new_href, separator); \
|
|
StrAllocCat(new_href, name); \
|
|
} \
|
|
}
|
|
|
|
GET_SLOT(URL_PROTOCOL, &tmp);
|
|
StrAllocCopy(new_href, JS_GetStringBytes(JSVAL_TO_STRING(tmp)));
|
|
if (slot == URL_HOST) {
|
|
ADD_SLOT(URL_HOST, "//");
|
|
} else {
|
|
ADD_SLOT(URL_HOSTNAME, "//");
|
|
ADD_SLOT(URL_PORT, ":");
|
|
}
|
|
ADD_SLOT(URL_PATHNAME, NULL);
|
|
ADD_SLOT(URL_HASH, NULL);
|
|
ADD_SLOT(URL_SEARCH, NULL);
|
|
|
|
if (!new_href) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
free_href = JS_TRUE;
|
|
href = new_href;
|
|
str = JS_NewStringCopyZ(cx, href);
|
|
if (!str) {
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
url->href = str;
|
|
break;
|
|
|
|
case URL_TARGET:
|
|
url->target = JSVAL_TO_STRING(*vp);
|
|
if (url->index != URL_NOT_INDEXED) {
|
|
context = url->url_decoder->window_context;
|
|
if (context) {
|
|
anchor_data = LO_GetLinkByIndex(context, url->layer_id,
|
|
url->index);
|
|
if (anchor_data) {
|
|
name = JS_GetStringBytes(url->target);
|
|
if (!lm_CheckWindowName(cx, name))
|
|
return JS_FALSE;
|
|
if (!lm_SaveParamString(cx, &anchor_data->target, name))
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
}
|
|
/* Note early return, to bypass href update and freeing. */
|
|
return JS_TRUE;
|
|
|
|
default:
|
|
/* Don't mess with a user-defined property. */
|
|
return ok;
|
|
}
|
|
|
|
if (url->index != URL_NOT_INDEXED) {
|
|
context = url->url_decoder->window_context;
|
|
if (context) {
|
|
anchor_data = LO_GetLinkByIndex(context, url->layer_id,
|
|
url->index);
|
|
if (anchor_data) {
|
|
checked_href = lm_CheckURL(cx, href, JS_FALSE);
|
|
if (!checked_href ||
|
|
!lm_SaveParamString(cx, &anchor_data->anchor,
|
|
checked_href)) {
|
|
ok = JS_FALSE;
|
|
goto out;
|
|
}
|
|
XP_FREE((char *)checked_href);
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (free_href && href)
|
|
XP_FREE((char *)href);
|
|
return ok;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(void)
|
|
url_finalize(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSURL *url;
|
|
MochaDecoder *decoder;
|
|
MWContext *context;
|
|
LO_AnchorData *anchor_data;
|
|
|
|
url = JS_GetPrivate(cx, obj);
|
|
if (!url)
|
|
return;
|
|
decoder = url->url_decoder;
|
|
if (url->index != URL_NOT_INDEXED) {
|
|
context = decoder->window_context;
|
|
if (context) {
|
|
LO_LockLayout();
|
|
anchor_data = LO_GetLinkByIndex(context, url->layer_id,
|
|
url->index);
|
|
if (anchor_data && anchor_data->mocha_object == obj)
|
|
anchor_data->mocha_object = NULL;
|
|
LO_UnlockLayout();
|
|
}
|
|
}
|
|
DROP_BACK_COUNT(decoder);
|
|
JS_RemoveRoot(cx, &url->href);
|
|
JS_RemoveRoot(cx, &url->target);
|
|
JS_RemoveRoot(cx, &url->text);
|
|
XP_DELETE(url);
|
|
}
|
|
|
|
JSClass lm_url_class = {
|
|
"Url", JSCLASS_HAS_PRIVATE,
|
|
JS_PropertyStub, JS_PropertyStub, url_getProperty, url_setProperty,
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, url_finalize
|
|
};
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
Url(JSContext *cx, JSObject *obj,
|
|
uint argc, jsval *argv, jsval *rval)
|
|
{
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
url_toString(JSContext *cx, JSObject *obj,
|
|
uint argc, jsval *argv, jsval *rval)
|
|
{
|
|
return url_getProperty(cx, obj, INT_TO_JSVAL(URL_HREF), rval);
|
|
}
|
|
|
|
static JSFunctionSpec url_methods[] = {
|
|
{lm_toString_str, url_toString, 0},
|
|
{0}
|
|
};
|
|
|
|
JSURL *
|
|
lm_NewURL(JSContext *cx, MochaDecoder *decoder, LO_AnchorData *anchor_data,
|
|
JSObject *document)
|
|
{
|
|
JSObject *obj;
|
|
JSURL *url;
|
|
JSString *str;
|
|
|
|
if (!decoder->url_prototype) {
|
|
obj = JS_InitClass(cx, decoder->window_object,
|
|
decoder->event_receiver_prototype, &lm_url_class,
|
|
Url, 0, url_props, NULL, NULL, NULL);
|
|
if (!obj)
|
|
return NULL;
|
|
decoder->url_prototype = obj;
|
|
}
|
|
|
|
url = JS_malloc(cx, sizeof *url);
|
|
if (!url)
|
|
return NULL;
|
|
XP_BZERO(url, sizeof *url);
|
|
|
|
obj = JS_NewObject(cx, &lm_url_class, decoder->url_prototype,
|
|
lm_GetOuterObject(decoder));
|
|
if (!obj || !JS_SetPrivate(cx, obj, url)) {
|
|
JS_free(cx, url);
|
|
return NULL;
|
|
}
|
|
|
|
if (!JS_DefineFunctions(cx, obj, url_methods))
|
|
return NULL;
|
|
|
|
url->url_decoder = HOLD_BACK_COUNT(decoder);
|
|
url->url_type = FORM_TYPE_NONE;
|
|
url->index = URL_NOT_INDEXED;
|
|
url->url_object = obj;
|
|
|
|
str = JS_NewStringCopyZ(cx, (char *) anchor_data->anchor);
|
|
if (!str)
|
|
return NULL;
|
|
url->href = str;
|
|
if (!JS_AddNamedRoot(cx, &url->href, "url.href"))
|
|
return NULL;
|
|
|
|
if (anchor_data->target) {
|
|
str = JS_NewStringCopyZ(cx, (char *) anchor_data->target);
|
|
if (!str)
|
|
return NULL;
|
|
url->target = str;
|
|
}
|
|
if (!JS_AddNamedRoot(cx, &url->target, "url.target"))
|
|
return NULL;
|
|
|
|
if (anchor_data->element && anchor_data->element->type == LO_TEXT) {
|
|
str = lm_LocalEncodingToStr(decoder->window_context,
|
|
(char *) anchor_data->element->lo_text.text);
|
|
if (!str)
|
|
return NULL;
|
|
url->text = str;
|
|
}
|
|
if (!JS_AddNamedRoot(cx, &url->text, "url.text"))
|
|
return NULL;
|
|
|
|
return url;
|
|
}
|
|
|
|
static const char *
|
|
get_url_string(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSURL *url;
|
|
MochaDecoder *decoder;
|
|
MWContext *context;
|
|
History_entry *he;
|
|
const char *url_string;
|
|
|
|
url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
|
|
if (!url)
|
|
return NULL;
|
|
decoder = url->url_decoder;
|
|
|
|
context = decoder->window_context;
|
|
if (!context)
|
|
return NULL;
|
|
he = SHIST_GetCurrent(&context->hist);
|
|
if (he) {
|
|
url_string = he->address;
|
|
if (NET_URL_Type(url_string) == WYSIWYG_TYPE_URL &&
|
|
!(url_string = LM_SkipWysiwygURLPrefix(url_string))) {
|
|
url_string = he->address;
|
|
}
|
|
return url_string;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Top-level window URL object, a reflection of the "Location:" GUI field.
|
|
*/
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
loc_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
{
|
|
JSURL *url;
|
|
MochaDecoder *decoder;
|
|
MWContext *context;
|
|
const char *url_string;
|
|
JSString *str;
|
|
|
|
if (!JSVAL_IS_INT(id) || JSVAL_TO_INT(id) < 0)
|
|
return JS_TRUE;
|
|
|
|
url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
|
|
if (!url)
|
|
return JS_TRUE;
|
|
decoder = url->url_decoder;
|
|
|
|
context = decoder->window_context;
|
|
if (!context)
|
|
return JS_TRUE;
|
|
if (context->type == MWContextMail ||
|
|
context->type == MWContextNews ||
|
|
context->type == MWContextMailMsg ||
|
|
context->type == MWContextNewsMsg) {
|
|
/*
|
|
* Don't give out the location of a the mail folder to a script
|
|
* embedded in a mail message. Just return the empty string.
|
|
*/
|
|
url->href = JSVAL_TO_STRING(JS_GetEmptyStringValue(cx));
|
|
} else {
|
|
/*
|
|
* Only need to check permissions for native getters
|
|
*/
|
|
if (!lm_CheckPermissions(cx, obj, JSTARGET_UNIVERSAL_BROWSER_READ))
|
|
return JS_FALSE;
|
|
url_string = get_url_string(cx, obj);
|
|
if (url_string && (!url->href || XP_STRCMP(JS_GetStringBytes(url->href),
|
|
url_string) != 0))
|
|
{
|
|
str = JS_NewStringCopyZ(cx, url_string);
|
|
if (!str)
|
|
return JS_FALSE;
|
|
url->href = str;
|
|
}
|
|
}
|
|
return url_getProperty(cx, obj, id, vp);
|
|
}
|
|
|
|
void
|
|
lm_ReplaceURL(MWContext *context, URL_Struct *url_struct)
|
|
{
|
|
History_entry *he;
|
|
|
|
he = SHIST_GetCurrent(&context->hist);
|
|
if (!he)
|
|
return;
|
|
he->history_num = SHIST_GetIndex(&context->hist, he);
|
|
he->replace = TRUE;
|
|
url_struct->history_num = he->history_num;
|
|
}
|
|
|
|
JSBool
|
|
lm_GetURL(JSContext *cx, MochaDecoder *decoder, URL_Struct *url_struct)
|
|
{
|
|
MWContext *context;
|
|
|
|
context = decoder->window_context;
|
|
if (!context) {
|
|
NET_FreeURLStruct(url_struct);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
if (decoder->replace_location) {
|
|
decoder->replace_location = JS_FALSE;
|
|
lm_ReplaceURL(context, url_struct);
|
|
}
|
|
ET_PostGetUrl(context, url_struct);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
const char *
|
|
lm_CheckURL(JSContext *cx, const char *url_string, JSBool checkFile)
|
|
{
|
|
char *protocol, *absolute;
|
|
JSObject *obj;
|
|
MochaDecoder *decoder;
|
|
|
|
protocol = NET_ParseURL(url_string, GET_PROTOCOL_PART);
|
|
if (!protocol || *protocol == '\0' || XP_STRCHR(protocol, '?')) {
|
|
lo_TopState *top_state;
|
|
|
|
obj = JS_GetGlobalObject(cx);
|
|
decoder = JS_GetPrivate(cx, obj);
|
|
|
|
LO_LockLayout();
|
|
top_state = lo_GetMochaTopState(decoder->window_context);
|
|
if (top_state && top_state->base_url) {
|
|
absolute = NET_MakeAbsoluteURL(top_state->base_url,
|
|
(char *)url_string); /*XXX*/
|
|
/*
|
|
* Temporarily unlock layout so that we don't hold the lock
|
|
* across a call (lm_CheckPermissions) that may result in
|
|
* synchronous event handling.
|
|
*/
|
|
LO_UnlockLayout();
|
|
if (!lm_CheckPermissions(cx, obj,
|
|
JSTARGET_UNIVERSAL_BROWSER_READ))
|
|
{
|
|
/* Don't leak information about the url of this page. */
|
|
XP_FREEIF(absolute);
|
|
return NULL;
|
|
}
|
|
LO_LockLayout();
|
|
} else {
|
|
absolute = NULL;
|
|
}
|
|
if (absolute) {
|
|
if (protocol) XP_FREE(protocol);
|
|
protocol = NET_ParseURL(absolute, GET_PROTOCOL_PART);
|
|
}
|
|
LO_UnlockLayout();
|
|
} else {
|
|
absolute = JS_strdup(cx, url_string);
|
|
if (!absolute) {
|
|
XP_FREE(protocol);
|
|
return NULL;
|
|
}
|
|
decoder = NULL;
|
|
}
|
|
|
|
if (absolute) {
|
|
|
|
/* Make sure it's a safe URL type. */
|
|
switch (NET_URL_Type(protocol)) {
|
|
case FILE_TYPE_URL:
|
|
if (checkFile) {
|
|
const char *subjectOrigin = lm_GetSubjectOriginURL(cx);
|
|
if (subjectOrigin == NULL) {
|
|
XP_FREE(protocol);
|
|
return NULL;
|
|
}
|
|
if (NET_URL_Type(subjectOrigin) != FILE_TYPE_URL &&
|
|
!lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_FILE_READ))
|
|
{
|
|
XP_FREE(absolute);
|
|
absolute = NULL;
|
|
}
|
|
}
|
|
break;
|
|
case FTP_TYPE_URL:
|
|
case GOPHER_TYPE_URL:
|
|
case HTTP_TYPE_URL:
|
|
case MAILTO_TYPE_URL:
|
|
case NEWS_TYPE_URL:
|
|
case RLOGIN_TYPE_URL:
|
|
case TELNET_TYPE_URL:
|
|
case TN3270_TYPE_URL:
|
|
case WAIS_TYPE_URL:
|
|
case SECURE_HTTP_TYPE_URL:
|
|
case URN_TYPE_URL:
|
|
case NFS_TYPE_URL:
|
|
case MOCHA_TYPE_URL:
|
|
case VIEW_SOURCE_TYPE_URL:
|
|
case NETHELP_TYPE_URL:
|
|
case WYSIWYG_TYPE_URL:
|
|
case LDAP_TYPE_URL:
|
|
#ifdef JAVA
|
|
case MARIMBA_TYPE_URL:
|
|
#endif
|
|
/* These are "safe". */
|
|
break;
|
|
case ABOUT_TYPE_URL:
|
|
if (XP_STRCASECMP(absolute, "about:blank") == 0)
|
|
break;
|
|
if (XP_STRNCASECMP(absolute, "about:pics", 10) == 0)
|
|
break;
|
|
/* these are OK if we are signed */
|
|
if (lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_BROWSER_READ))
|
|
break;
|
|
/* FALL THROUGH */
|
|
default:
|
|
/* All others are naughty. */
|
|
XP_FREE(absolute);
|
|
absolute = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!absolute) {
|
|
JS_ReportError(cx, "illegal URL method '%s'",
|
|
protocol && *protocol ? protocol : url_string);
|
|
}
|
|
if (protocol)
|
|
XP_FREE(protocol);
|
|
return absolute;
|
|
}
|
|
|
|
static JSBool
|
|
url_load(JSContext *cx, JSObject *obj, const char *url_string,
|
|
NET_ReloadMethod reload_how)
|
|
{
|
|
JSURL *url;
|
|
URL_Struct *url_struct;
|
|
const char *referer;
|
|
|
|
url = JS_GetPrivate(cx, obj);
|
|
if (!url)
|
|
return JS_TRUE;
|
|
url_struct = NET_CreateURLStruct(url_string, reload_how);
|
|
if (!url_struct) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
if (!(referer = lm_GetSubjectOriginURL(cx)) ||
|
|
!(url_struct->referer = JS_strdup(cx, referer))) {
|
|
NET_FreeURLStruct(url_struct);
|
|
return JS_FALSE;
|
|
}
|
|
return lm_GetURL(cx, url->url_decoder, url_struct);
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
loc_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
{
|
|
const char *url_string;
|
|
JSString *str;
|
|
jsval val;
|
|
jsint slot;
|
|
JSURL *url;
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
return JS_TRUE;
|
|
|
|
slot = JSVAL_TO_INT(id);
|
|
/* Setting these properties should not cause a FE_GetURL. */
|
|
if (slot < 0 || slot == URL_TARGET)
|
|
return url_setProperty(cx, obj, id, vp);
|
|
|
|
/* Make sure vp is a string. */
|
|
if (!JSVAL_IS_STRING(*vp) &&
|
|
!JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* Two cases: setting href vs. setting a component property. */
|
|
if (slot == URL_HREF || slot == URL_PROTOCOL) {
|
|
/* Make sure the URL is absolute and sanity-check its protocol. */
|
|
url_string = JS_GetStringBytes(JSVAL_TO_STRING(*vp));
|
|
url_string = lm_CheckURL(cx, url_string, JS_TRUE);
|
|
if (!url_string)
|
|
return JS_FALSE;
|
|
str = JS_NewStringCopyZ(cx, url_string);
|
|
XP_FREE((char *)url_string);
|
|
if (!str)
|
|
return JS_FALSE;
|
|
val = STRING_TO_JSVAL(str);
|
|
vp = &val;
|
|
} else {
|
|
/* Get href from session history before setting a piece of it. */
|
|
if (!loc_getProperty(cx, obj, INT_TO_JSVAL(URL_HREF), &val))
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* Set slot's property. */
|
|
if (!url_setProperty(cx, obj, id, vp))
|
|
return JS_FALSE;
|
|
|
|
url = JS_GetPrivate(cx, obj);
|
|
if (!url)
|
|
return JS_TRUE;
|
|
if (url->href)
|
|
url_string = JS_GetStringBytes(url->href);
|
|
else
|
|
url_string = "";
|
|
return url_load(cx, obj, url_string, NET_DONT_RELOAD);
|
|
}
|
|
|
|
JSClass lm_location_class = {
|
|
"Location", JSCLASS_HAS_PRIVATE,
|
|
JS_PropertyStub, JS_PropertyStub, loc_getProperty, loc_setProperty,
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, url_finalize
|
|
};
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
Location(JSContext *cx, JSObject *obj,
|
|
uint argc, jsval *argv, jsval *rval)
|
|
{
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
loc_toString(JSContext *cx, JSObject *obj,
|
|
uint argc, jsval *argv, jsval *rval)
|
|
{
|
|
return loc_getProperty(cx, obj, INT_TO_JSVAL(URL_HREF), rval);
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
loc_assign(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
{
|
|
JSURL *url;
|
|
jsval v;
|
|
JSObject *locobj;
|
|
|
|
if (!JS_InstanceOf(cx, obj, &lm_location_class, NULL)) {
|
|
if(!JS_LookupProperty(cx, obj, lm_location_str, &v)) {
|
|
return JS_FALSE;
|
|
}
|
|
if(!JSVAL_IS_OBJECT(v) || !JSVAL_TO_OBJECT(v)) {
|
|
return JS_TRUE;
|
|
}
|
|
locobj = JSVAL_TO_OBJECT(v);
|
|
} else {
|
|
locobj = obj;
|
|
}
|
|
if (!(url = JS_GetInstancePrivate(cx, locobj, &lm_location_class, NULL)))
|
|
return JS_FALSE;
|
|
if (!loc_setProperty(cx, locobj, INT_TO_JSVAL(URL_HREF), vp))
|
|
return JS_FALSE;
|
|
*vp = OBJECT_TO_JSVAL(url->url_object);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
loc_reload(JSContext *cx, JSObject *obj,
|
|
uint argc, jsval *argv, jsval *rval)
|
|
{
|
|
if (!JS_InstanceOf(cx, obj, &lm_location_class, argv))
|
|
return JS_FALSE;
|
|
return url_load(cx, obj, get_url_string(cx, obj),
|
|
(JSVAL_IS_BOOLEAN(argv[0]) && JSVAL_TO_BOOLEAN(argv[0]))
|
|
? NET_SUPER_RELOAD
|
|
: NET_NORMAL_RELOAD);
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
loc_replace(JSContext *cx, JSObject *obj,
|
|
uint argc, jsval *argv, jsval *rval)
|
|
{
|
|
JSURL *url;
|
|
JSBool ans;
|
|
|
|
if (!(url = JS_GetInstancePrivate(cx, obj, &lm_location_class, argv)))
|
|
return JS_FALSE;
|
|
url->url_decoder->replace_location = JS_TRUE;
|
|
/* Note: loc_assign ignores the id in favor of its own id anyway */
|
|
ans = loc_assign(cx, obj, 0, argv);
|
|
*rval = *argv;
|
|
return ans;
|
|
}
|
|
|
|
static JSFunctionSpec loc_methods[] = {
|
|
{lm_toString_str, loc_toString, 0},
|
|
{lm_reload_str, loc_reload, 1},
|
|
{lm_replace_str, loc_replace, 1},
|
|
{0}
|
|
};
|
|
|
|
JSObject *
|
|
lm_DefineLocation(MochaDecoder *decoder)
|
|
{
|
|
JSObject *obj;
|
|
JSContext *cx;
|
|
JSURL *url;
|
|
|
|
obj = decoder->location;
|
|
if (obj)
|
|
return obj;
|
|
|
|
cx = decoder->js_context;
|
|
url = JS_malloc(cx, sizeof *url);
|
|
if (!url)
|
|
return NULL;
|
|
XP_BZERO(url, sizeof *url);
|
|
|
|
obj = JS_InitClass(cx, decoder->window_object, NULL, &lm_location_class,
|
|
Location, 0, url_props, loc_methods, NULL, NULL);
|
|
if (!obj || !JS_SetPrivate(cx, obj, url)) {
|
|
JS_free(cx, url);
|
|
return NULL;
|
|
}
|
|
|
|
/* XXX common subroutine this and above with lm_NewURL */
|
|
if (!JS_AddNamedRoot(cx, &url->href, "loc.text"))
|
|
return NULL;
|
|
if (!JS_AddNamedRoot(cx, &url->target, "loc.target"))
|
|
return NULL;
|
|
if (!JS_AddNamedRoot(cx, &url->text, "loc.text"))
|
|
return NULL;
|
|
|
|
if (!JS_DefineProperty(cx, decoder->window_object, lm_location_str,
|
|
OBJECT_TO_JSVAL(obj), NULL, loc_assign,
|
|
JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
|
|
return NULL;
|
|
}
|
|
if (!JS_DefineProperty(cx, decoder->document, lm_location_str,
|
|
OBJECT_TO_JSVAL(obj), NULL, loc_assign,
|
|
JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Define the Location object (the current URL). */
|
|
url->url_decoder = HOLD_BACK_COUNT(decoder);
|
|
url->url_type = FORM_TYPE_NONE;
|
|
url->url_object = obj;
|
|
url->index = URL_NOT_INDEXED;
|
|
decoder->location = obj;
|
|
return obj;
|
|
}
|