/* -*- 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; }