home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libmocha / lm_url.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  26.1 KB  |  941 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /*
  19.  * JS reflection of the current Navigator URL (Location) object.
  20.  *
  21.  * Brendan Eich, 9/8/95
  22.  */
  23. #include "lm.h"
  24. #include "xp.h"
  25. #include "net.h"        /* for URL_Struct */
  26. #include "shist.h"        /* for session history stuff */
  27. #include "structs.h"        /* for MWContext */
  28. #include "layout.h"        /* included via -I../layout */
  29. #include "mkparse.h"        /* included via -I../libnet */
  30.  
  31. /* NB: these named properties use non-negative slots; code below knows that. */
  32. /* Non-negative slots are protected by a permissions check; others aren't. */
  33. enum url_slot {
  34.     URL_HREF,            /* the entire URL as a string */
  35.     URL_PROTOCOL,        /* protocol:... */
  36.     URL_HOST,            /* protocol://host/... */
  37.     URL_HOSTNAME,        /* protocol://hostname:... */
  38.     URL_PORT,            /* protocol://hostname:port/... */
  39.     URL_PATHNAME,        /* protocol://host/pathname[#?...] */
  40.     URL_HASH,            /* protocol://host/pathname#hash */
  41.     URL_SEARCH,            /* protocol://host/pathname?search */
  42.     URL_TARGET,            /* target window or null */
  43.     URL_TEXT,            /* text within A container */
  44.     URL_X       = -1,           /* layout X coordinate */
  45.     URL_Y       = -2            /* layout Y coordinate */
  46. };
  47.  
  48. static JSPropertySpec url_props[] = {
  49.     {"href",            URL_HREF,       JSPROP_ENUMERATE},
  50.     {"protocol",        URL_PROTOCOL,   JSPROP_ENUMERATE},
  51.     {"host",            URL_HOST,       JSPROP_ENUMERATE},
  52.     {"hostname",        URL_HOSTNAME,   JSPROP_ENUMERATE},
  53.     {"port",            URL_PORT,       JSPROP_ENUMERATE},
  54.     {"pathname",        URL_PATHNAME,   JSPROP_ENUMERATE},
  55.     {"hash",            URL_HASH,       JSPROP_ENUMERATE},
  56.     {"search",          URL_SEARCH,     JSPROP_ENUMERATE},
  57.     {"target",          URL_TARGET,     JSPROP_ENUMERATE},
  58.     {"text",            URL_TEXT,       JSPROP_ENUMERATE | JSPROP_READONLY},
  59.     {"x",               URL_X,          JSPROP_ENUMERATE | JSPROP_READONLY},
  60.     {"y",               URL_Y,          JSPROP_ENUMERATE | JSPROP_READONLY},
  61.     {0}
  62. };
  63.  
  64. #define ParseURL(url,part)    ((url) ? NET_ParseURL(url,part) : 0)
  65.  
  66. static JSBool
  67. url_get_coord(JSContext *cx, JSURL *url, jsint slot, jsval *vp)
  68. {
  69.     int32 coord;
  70.     LO_AnchorData *anchor_data;
  71.     MWContext *context;
  72.  
  73.     LO_LockLayout();
  74.     context = url->url_decoder->window_context;
  75.     anchor_data = LO_GetLinkByIndex(context, url->layer_id, url->index);
  76.     if (anchor_data && anchor_data->element) {
  77.         switch (slot) {
  78.         case URL_X:
  79.             coord = anchor_data->element->lo_any.x;
  80.             break;
  81.             
  82.         case URL_Y:
  83.             coord = anchor_data->element->lo_any.y;
  84.             break;
  85.  
  86.         default:
  87.             LO_UnlockLayout();
  88.             return JS_FALSE;
  89.         }
  90.  
  91.     *vp = INT_TO_JSVAL(coord);
  92.     LO_UnlockLayout();
  93.     return JS_TRUE;
  94.     }
  95.     *vp = JSVAL_NULL;
  96.     LO_UnlockLayout();
  97.     return JS_TRUE;
  98. }
  99.  
  100. extern JSClass lm_url_class;
  101. extern JSClass lm_location_class;
  102.  
  103. PR_STATIC_CALLBACK(JSBool)
  104. url_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  105. {
  106.     jsval slot;
  107.     JSURL *url;
  108.     JSString * str;
  109.     char *port;
  110.     const char *cstr, *tmp;
  111.  
  112.     if (!JSVAL_IS_INT(id))
  113.     return JS_TRUE;
  114.  
  115.     slot = JSVAL_TO_INT(id);
  116.     url = JS_GetInstancePrivate(cx, obj, &lm_url_class, NULL);
  117.     if (!url) {
  118.     url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
  119.     if (!url)
  120.         return JS_TRUE;
  121.     }
  122.  
  123.     str = 0;
  124.     cstr = 0;
  125.  
  126.     switch (slot) {
  127.       case URL_HREF:
  128.     str = url->href;
  129.     break;
  130.  
  131.       case URL_PROTOCOL:
  132.         if (url->href)
  133.         cstr = ParseURL(JS_GetStringBytes(url->href), GET_PROTOCOL_PART);
  134.     break;
  135.  
  136.       case URL_HOST:
  137.         if (url->href)
  138.         cstr = ParseURL(JS_GetStringBytes(url->href), GET_HOST_PART);
  139.     break;
  140.  
  141.       case URL_HOSTNAME:
  142.         if (url->href)
  143.         cstr = ParseURL(JS_GetStringBytes(url->href), GET_HOST_PART);
  144.     if (cstr && (port = XP_STRCHR(cstr, ':')) != 0)
  145.         *port = '\0';
  146.     break;
  147.  
  148.       case URL_PORT:
  149.     if (url->href)
  150.         cstr = ParseURL(JS_GetStringBytes(url->href), GET_HOST_PART);
  151.     if (cstr && (port = XP_STRCHR(cstr, ':')) != 0)
  152.         port++;
  153.     else
  154.         port = "";
  155.     tmp = cstr;
  156.     cstr = JS_strdup(cx, port);
  157.     XP_FREE((void *)tmp);
  158.     break;
  159.  
  160.       case URL_PATHNAME:
  161.         if (url->href)
  162.         cstr = ParseURL(JS_GetStringBytes(url->href), GET_PATH_PART);
  163.     break;
  164.  
  165.       case URL_HASH:
  166.         if (url->href)
  167.         cstr = ParseURL(JS_GetStringBytes(url->href), GET_HASH_PART);
  168.     break;
  169.  
  170.       case URL_SEARCH:
  171.     if (url->href)
  172.         cstr = ParseURL(JS_GetStringBytes(url->href), GET_SEARCH_PART);
  173.     break;
  174.  
  175.       case URL_TARGET:
  176.     if (!url->target) {
  177.         *vp = JSVAL_NULL;
  178.         return JS_TRUE;
  179.     }
  180.     str = url->target;
  181.     break;
  182.  
  183.       case URL_TEXT:
  184.     if (!url->text) {
  185.         *vp = JSVAL_NULL;
  186.         return JS_TRUE;
  187.     }
  188.     str = url->text;
  189.     break;
  190.  
  191.     case URL_X:
  192.     case URL_Y:
  193.         return url_get_coord(cx, url, slot, vp);
  194.  
  195.       default:
  196.     /* Don't mess with user-defined or method properties. */
  197.           return JS_TRUE;
  198.     }
  199.  
  200.     if (!str && cstr)
  201.     str = JS_NewStringCopyZ(cx, cstr);
  202.     if (cstr)
  203.     XP_FREE((char *)cstr);
  204.     if (!str)        
  205.         return JS_FALSE;
  206.     *vp = STRING_TO_JSVAL(str);
  207.     return JS_TRUE;
  208. }
  209.  
  210. PR_STATIC_CALLBACK(JSBool)
  211. url_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  212. {
  213.     JSURL *url;
  214.     const char *href, *name, *checked_href;
  215.     char *new_href, *prop_name;
  216.     JSBool free_href;
  217.     jsval tmp;
  218.     JSString *str;
  219.     MWContext *context;
  220.     LO_AnchorData *anchor_data;
  221.     JSBool ok;
  222.     jsint slot;
  223.  
  224.     url = JS_GetInstancePrivate(cx, obj, &lm_url_class, NULL);
  225.     if (!url) {
  226.     url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
  227.     if (!url)
  228.         return JS_TRUE;
  229.     }
  230.  
  231.     /* If the property is setting an event handler we find out now so
  232.      * that we can tell the front end to send the event.
  233.      */
  234.     if (JSVAL_IS_STRING(id)) {
  235.     prop_name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  236.     if (XP_STRCASECMP(prop_name, lm_onClick_str) == 0 ||
  237.         XP_STRCASECMP(prop_name, lm_onMouseDown_str) == 0 ||
  238.         XP_STRCASECMP(prop_name, lm_onMouseOver_str) == 0 ||
  239.         XP_STRCASECMP(prop_name, lm_onMouseOut_str) == 0 ||
  240.         XP_STRCASECMP(prop_name, lm_onMouseUp_str) == 0) {
  241.         context = url->url_decoder->window_context;
  242.         if (context) {
  243.         anchor_data = LO_GetLinkByIndex(context, url->layer_id,
  244.                         url->index);
  245.         if (anchor_data)
  246.             anchor_data->event_handler_present = TRUE;
  247.         }
  248.     }
  249.     return JS_TRUE;
  250.     }
  251.     
  252.     XP_ASSERT(JSVAL_IS_INT(id));
  253.     slot = JSVAL_TO_INT(id);
  254.  
  255.     if (slot < 0) {
  256.     /* Don't mess with user-defined or method properties. */
  257.     return JS_TRUE;
  258.     }
  259.  
  260.     if (!JSVAL_IS_STRING(*vp) &&
  261.     !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
  262.     return JS_FALSE;
  263.     }
  264.  
  265.     ok = JS_TRUE;
  266.  
  267.     switch (slot) {
  268.       case URL_HREF:
  269.     url->href = JSVAL_TO_STRING(*vp);
  270.     href = JS_GetStringBytes(url->href);
  271.     free_href = JS_FALSE;
  272.     break;
  273.  
  274.       case URL_PROTOCOL:
  275.       case URL_HOST:
  276.       case URL_HOSTNAME:
  277.       case URL_PORT:
  278.       case URL_PATHNAME:
  279.       case URL_HASH:
  280.       case URL_SEARCH:
  281.     /* a component property changed -- recompute href. */
  282.     new_href = NULL;
  283.  
  284. #define GET_SLOT(aslot, ptmp) {                                               \
  285.     if (aslot == slot) {                                                      \
  286.     *(ptmp) = *vp;                                                        \
  287.     } else {                                                                  \
  288.     if (!JS_GetElement(cx, obj, aslot, ptmp)) {                           \
  289.         if (new_href) XP_FREE(new_href);                                  \
  290.         return JS_FALSE;                                               \
  291.     }                                                                     \
  292.     }                                                                         \
  293. }
  294.  
  295. #define ADD_SLOT(aslot, separator) {                                          \
  296.     GET_SLOT(aslot, &tmp);                                                    \
  297.     name = JS_GetStringBytes(JSVAL_TO_STRING(tmp));                           \
  298.     if (*name) {                                                              \
  299.     if (separator) StrAllocCat(new_href, separator);                      \
  300.     StrAllocCat(new_href, name);                                          \
  301.     }                                                                         \
  302. }
  303.  
  304.     GET_SLOT(URL_PROTOCOL, &tmp);
  305.     StrAllocCopy(new_href, JS_GetStringBytes(JSVAL_TO_STRING(tmp)));
  306.     if (slot == URL_HOST) {
  307.         ADD_SLOT(URL_HOST, "//");
  308.     } else {
  309.         ADD_SLOT(URL_HOSTNAME, "//");
  310.         ADD_SLOT(URL_PORT, ":");
  311.     }
  312.     ADD_SLOT(URL_PATHNAME, NULL);
  313.     ADD_SLOT(URL_HASH, NULL);
  314.     ADD_SLOT(URL_SEARCH, NULL);
  315.  
  316.     if (!new_href) {
  317.         JS_ReportOutOfMemory(cx);
  318.         return JS_FALSE;
  319.     }
  320.  
  321.     free_href = JS_TRUE;
  322.     href = new_href;
  323.     str = JS_NewStringCopyZ(cx, href);
  324.     if (!str) {
  325.         ok = JS_FALSE;
  326.         goto out;
  327.     }
  328.     url->href = str;
  329.     break;
  330.  
  331.       case URL_TARGET:
  332.     url->target = JSVAL_TO_STRING(*vp);
  333.     if (url->index != URL_NOT_INDEXED) {
  334.         context = url->url_decoder->window_context;
  335.         if (context) {
  336.         anchor_data = LO_GetLinkByIndex(context, url->layer_id,
  337.                                                 url->index);
  338.         if (anchor_data) {
  339.             name = JS_GetStringBytes(url->target);
  340.             if (!lm_CheckWindowName(cx, name))
  341.             return JS_FALSE;
  342.             if (!lm_SaveParamString(cx, &anchor_data->target, name))
  343.             return JS_FALSE;
  344.         }
  345.         }
  346.     }
  347.     /* Note early return, to bypass href update and freeing. */
  348.     return JS_TRUE;
  349.  
  350.       default:
  351.     /* Don't mess with a user-defined property. */
  352.     return ok;
  353.     }
  354.  
  355.     if (url->index != URL_NOT_INDEXED) {
  356.     context = url->url_decoder->window_context;
  357.     if (context) {
  358.         anchor_data = LO_GetLinkByIndex(context, url->layer_id, 
  359.                                             url->index);
  360.         if (anchor_data) {
  361.         checked_href = lm_CheckURL(cx, href, JS_FALSE);
  362.         if (!checked_href ||
  363.             !lm_SaveParamString(cx, &anchor_data->anchor,
  364.                     checked_href)) {
  365.             ok = JS_FALSE;
  366.             goto out;
  367.         }
  368.         XP_FREE((char *)checked_href);
  369.         }
  370.     }
  371.     }
  372.  
  373. out:
  374.     if (free_href && href)
  375.     XP_FREE((char *)href);
  376.     return ok;
  377. }
  378.  
  379. PR_STATIC_CALLBACK(void)
  380. url_finalize(JSContext *cx, JSObject *obj)
  381. {
  382.     JSURL *url;
  383.     MochaDecoder *decoder;
  384.     MWContext *context;
  385.     LO_AnchorData *anchor_data;
  386.  
  387.     url = JS_GetPrivate(cx, obj);
  388.     if (!url)
  389.     return;
  390.     decoder = url->url_decoder;
  391.     if (url->index != URL_NOT_INDEXED) {
  392.     context = decoder->window_context;
  393.     if (context) {
  394.         LO_LockLayout();
  395.         anchor_data = LO_GetLinkByIndex(context, url->layer_id, 
  396.                                             url->index);
  397.         if (anchor_data && anchor_data->mocha_object == obj)
  398.         anchor_data->mocha_object = NULL;
  399.         LO_UnlockLayout();
  400.     }
  401.     }
  402.     DROP_BACK_COUNT(decoder);
  403.     JS_RemoveRoot(cx, &url->href);
  404.     JS_RemoveRoot(cx, &url->target);
  405.     JS_RemoveRoot(cx, &url->text);
  406.     XP_DELETE(url);
  407. }
  408.  
  409. JSClass lm_url_class = {
  410.     "Url", JSCLASS_HAS_PRIVATE,
  411.     JS_PropertyStub, JS_PropertyStub, url_getProperty, url_setProperty,
  412.     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, url_finalize
  413. };
  414.  
  415. PR_STATIC_CALLBACK(JSBool)
  416. Url(JSContext *cx, JSObject *obj,
  417.     uint argc, jsval *argv, jsval *rval)
  418. {
  419.     return JS_TRUE;
  420. }
  421.  
  422. PR_STATIC_CALLBACK(JSBool)
  423. url_toString(JSContext *cx, JSObject *obj,
  424.           uint argc, jsval *argv, jsval *rval)
  425. {
  426.     return url_getProperty(cx, obj, INT_TO_JSVAL(URL_HREF), rval);
  427. }
  428.  
  429. static JSFunctionSpec url_methods[] = {
  430.     {lm_toString_str,    url_toString,    0},
  431.     {0}
  432. };
  433.  
  434. JSURL *
  435. lm_NewURL(JSContext *cx, MochaDecoder *decoder, LO_AnchorData *anchor_data,
  436.           JSObject *document)
  437. {
  438.     JSObject *obj;
  439.     JSURL *url;
  440.     JSString *str;
  441.  
  442.     if (!decoder->url_prototype) {
  443.     obj = JS_InitClass(cx, decoder->window_object, 
  444.                decoder->event_receiver_prototype, &lm_url_class, 
  445.                Url, 0, url_props, NULL, NULL, NULL);
  446.     if (!obj)
  447.         return NULL;
  448.     decoder->url_prototype = obj;
  449.     }
  450.  
  451.     url = JS_malloc(cx, sizeof *url);
  452.     if (!url)
  453.     return NULL;
  454.     XP_BZERO(url, sizeof *url);
  455.  
  456.     obj = JS_NewObject(cx, &lm_url_class, decoder->url_prototype, 
  457.                        lm_GetOuterObject(decoder));
  458.     if (!obj || !JS_SetPrivate(cx, obj, url)) {
  459.     JS_free(cx, url);
  460.     return NULL;
  461.     }
  462.  
  463.     if (!JS_DefineFunctions(cx, obj, url_methods))
  464.     return NULL;
  465.  
  466.     url->url_decoder = HOLD_BACK_COUNT(decoder);
  467.     url->url_type = FORM_TYPE_NONE;
  468.     url->index = URL_NOT_INDEXED;
  469.     url->url_object = obj;
  470.  
  471.     str = JS_NewStringCopyZ(cx, (char *) anchor_data->anchor);
  472.     if (!str)
  473.     return NULL;
  474.     url->href = str;
  475.     if (!JS_AddNamedRoot(cx, &url->href, "url.href"))
  476.     return NULL;
  477.  
  478.     if (anchor_data->target) {
  479.     str = JS_NewStringCopyZ(cx, (char *) anchor_data->target);
  480.     if (!str)
  481.         return NULL;
  482.     url->target = str;
  483.     }
  484.     if (!JS_AddNamedRoot(cx, &url->target, "url.target"))
  485.     return NULL;
  486.  
  487.     if (anchor_data->element && anchor_data->element->type == LO_TEXT) {
  488.     str = lm_LocalEncodingToStr(decoder->window_context, 
  489.         (char *) anchor_data->element->lo_text.text);
  490.     if (!str)
  491.         return NULL;
  492.     url->text = str;
  493.     }
  494.     if (!JS_AddNamedRoot(cx, &url->text, "url.text"))
  495.     return NULL;
  496.  
  497.     return url;
  498. }
  499.  
  500. static const char *
  501. get_url_string(JSContext *cx, JSObject *obj)
  502. {
  503.     JSURL *url;
  504.     MochaDecoder *decoder;
  505.     MWContext *context;
  506.     History_entry *he;
  507.     const char *url_string;
  508.  
  509.     url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
  510.     if (!url)
  511.     return NULL;
  512.     decoder = url->url_decoder;
  513.  
  514.     context = decoder->window_context;
  515.     if (!context)
  516.     return NULL;
  517.     he = SHIST_GetCurrent(&context->hist);
  518.     if (he) {
  519.     url_string = he->address;
  520.     if (NET_URL_Type(url_string) == WYSIWYG_TYPE_URL &&
  521.         !(url_string = LM_SkipWysiwygURLPrefix(url_string))) {
  522.         url_string = he->address;
  523.         }
  524.         return url_string;
  525.     }
  526.     return NULL;
  527. }
  528.  
  529. /*
  530.  * Top-level window URL object, a reflection of the "Location:" GUI field.
  531.  */
  532. PR_STATIC_CALLBACK(JSBool)
  533. loc_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  534. {
  535.     JSURL *url;
  536.     MochaDecoder *decoder;
  537.     MWContext *context;
  538.     const char *url_string;
  539.     JSString *str;
  540.  
  541.     if (!JSVAL_IS_INT(id) || JSVAL_TO_INT(id) < 0)
  542.         return JS_TRUE;
  543.  
  544.     url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
  545.     if (!url)
  546.     return JS_TRUE;
  547.     decoder = url->url_decoder;
  548.  
  549.     context = decoder->window_context;
  550.     if (!context)
  551.     return JS_TRUE;
  552.     if (context->type == MWContextMail || 
  553.         context->type == MWContextNews ||
  554.         context->type == MWContextMailMsg || 
  555.         context->type == MWContextNewsMsg) {
  556.         /*
  557.      * Don't give out the location of a the mail folder to a script
  558.          * embedded in a mail message. Just return the empty string.
  559.          */
  560.     url->href = JSVAL_TO_STRING(JS_GetEmptyStringValue(cx));
  561.     } else {
  562.         /*
  563.          * Only need to check permissions for native getters
  564.          */
  565.         if (!lm_CheckPermissions(cx, obj, JSTARGET_UNIVERSAL_BROWSER_READ))
  566.         return JS_FALSE;
  567.         url_string = get_url_string(cx, obj);
  568.         if (url_string && (!url->href || XP_STRCMP(JS_GetStringBytes(url->href), 
  569.                                                    url_string) != 0))
  570.         {
  571.         str = JS_NewStringCopyZ(cx, url_string);
  572.         if (!str)
  573.         return JS_FALSE;
  574.         url->href = str;
  575.         }
  576.     }
  577.     return url_getProperty(cx, obj, id, vp);
  578. }
  579.  
  580. void
  581. lm_ReplaceURL(MWContext *context, URL_Struct *url_struct)
  582. {
  583.     History_entry *he;
  584.  
  585.     he = SHIST_GetCurrent(&context->hist);
  586.     if (!he)
  587.     return;
  588.     he->history_num = SHIST_GetIndex(&context->hist, he);
  589.     he->replace = TRUE;
  590.     url_struct->history_num = he->history_num;
  591. }
  592.  
  593. JSBool
  594. lm_GetURL(JSContext *cx, MochaDecoder *decoder, URL_Struct *url_struct)
  595. {
  596.     MWContext *context;
  597.  
  598.     context = decoder->window_context;
  599.     if (!context) {
  600.         NET_FreeURLStruct(url_struct);
  601.         return JS_TRUE;
  602.     }
  603.  
  604.     if (decoder->replace_location) {
  605.     decoder->replace_location = JS_FALSE;
  606.     lm_ReplaceURL(context, url_struct);
  607.     }
  608.     ET_PostGetUrl(context, url_struct);
  609.     return JS_TRUE;
  610. }
  611.  
  612. const char *
  613. lm_CheckURL(JSContext *cx, const char *url_string, JSBool checkFile)
  614. {
  615.     char *protocol, *absolute;
  616.     JSObject *obj;
  617.     MochaDecoder *decoder;
  618.  
  619.     protocol = NET_ParseURL(url_string, GET_PROTOCOL_PART);
  620.     if (!protocol || *protocol == '\0') {
  621.         lo_TopState *top_state;
  622.  
  623.     obj = JS_GetGlobalObject(cx);
  624.     decoder = JS_GetPrivate(cx, obj);
  625.  
  626.     LO_LockLayout();
  627.     top_state = lo_GetMochaTopState(decoder->window_context);
  628.         if (top_state && top_state->base_url) {
  629.         absolute = NET_MakeAbsoluteURL(top_state->base_url,
  630.                            (char *)url_string);    /*XXX*/
  631.             /* 
  632.          * Temporarily unlock layout so that we don't hold the lock
  633.          * across a call (lm_CheckPermissions) that may result in 
  634.          * synchronous event handling.
  635.          */
  636.         LO_UnlockLayout();
  637.             if (!lm_CheckPermissions(cx, obj, 
  638.                                      JSTARGET_UNIVERSAL_BROWSER_READ))
  639.             {
  640.                 /* Don't leak information about the url of this page. */
  641.                 XP_FREEIF(absolute);
  642.                 return NULL;
  643.             }
  644.         LO_LockLayout();
  645.     } else {
  646.         absolute = NULL;
  647.     }
  648.     if (absolute) {
  649.         if (protocol) XP_FREE(protocol);
  650.         protocol = NET_ParseURL(absolute, GET_PROTOCOL_PART);
  651.     }
  652.     LO_UnlockLayout();
  653.     } else {
  654.     absolute = JS_strdup(cx, url_string);
  655.     if (!absolute) {
  656.         XP_FREE(protocol);
  657.         return NULL;
  658.     }
  659.     decoder = NULL;
  660.     }
  661.  
  662.     if (absolute) {
  663.  
  664.     /* Make sure it's a safe URL type. */
  665.     switch (NET_URL_Type(protocol)) {
  666.       case FILE_TYPE_URL:
  667.             if (checkFile) {
  668.                 const char *subjectOrigin = lm_GetSubjectOriginURL(cx);
  669.                 if (subjectOrigin == NULL) {
  670.                 XP_FREE(protocol);
  671.                 return NULL;
  672.                 }
  673.                 if (NET_URL_Type(subjectOrigin) != FILE_TYPE_URL &&
  674.                     !lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_FILE_READ)) 
  675.                 {
  676.                     XP_FREE(absolute);
  677.                     absolute = NULL;
  678.                 }
  679.             }
  680.             break;
  681.       case FTP_TYPE_URL:
  682.       case GOPHER_TYPE_URL:
  683.       case HTTP_TYPE_URL:
  684.       case MAILTO_TYPE_URL:
  685.       case NEWS_TYPE_URL:
  686.       case RLOGIN_TYPE_URL:
  687.       case TELNET_TYPE_URL:
  688.       case TN3270_TYPE_URL:
  689.       case WAIS_TYPE_URL:
  690.       case SECURE_HTTP_TYPE_URL:
  691.       case URN_TYPE_URL:
  692.       case NFS_TYPE_URL:
  693.       case MOCHA_TYPE_URL:
  694.       case VIEW_SOURCE_TYPE_URL:
  695.       case NETHELP_TYPE_URL:
  696.       case WYSIWYG_TYPE_URL:
  697.       case LDAP_TYPE_URL:
  698. #ifdef JAVA
  699.       case MARIMBA_TYPE_URL:
  700. #endif
  701.         /* These are "safe". */
  702.         break;
  703.       case ABOUT_TYPE_URL:
  704.         if (XP_STRCASECMP(absolute, "about:blank") == 0)
  705.         break;
  706.         if (XP_STRNCASECMP(absolute, "about:pics", 10) == 0)
  707.         break;
  708.         /* these are OK if we are signed */
  709.         if (lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_BROWSER_READ))
  710.         break;
  711.         /* FALL THROUGH */
  712.       default:
  713.         /* All others are naughty. */
  714.         XP_FREE(absolute);
  715.         absolute = NULL;
  716.         break;
  717.     }
  718.     }
  719.  
  720.     if (!absolute) {
  721.     JS_ReportError(cx, "illegal URL method '%s'",
  722.                protocol && *protocol ? protocol : url_string);
  723.     }
  724.     if (protocol)
  725.     XP_FREE(protocol);
  726.     return absolute;
  727. }
  728.  
  729. static JSBool
  730. url_load(JSContext *cx, JSObject *obj, const char *url_string, 
  731.          NET_ReloadMethod reload_how)
  732. {
  733.     JSURL *url;
  734.     URL_Struct *url_struct;
  735.     const char *referer;
  736.  
  737.     url = JS_GetPrivate(cx, obj);
  738.     if (!url)
  739.     return JS_TRUE;
  740.     url_struct = NET_CreateURLStruct(url_string, reload_how);
  741.     if (!url_struct) {
  742.     JS_ReportOutOfMemory(cx);
  743.     return JS_FALSE;
  744.     }
  745.     if (!(referer = lm_GetSubjectOriginURL(cx)) ||
  746.     !(url_struct->referer = JS_strdup(cx, referer))) {
  747.     NET_FreeURLStruct(url_struct);
  748.     return JS_FALSE;
  749.     }
  750.     return lm_GetURL(cx, url->url_decoder, url_struct);
  751. }
  752.  
  753. PR_STATIC_CALLBACK(JSBool)
  754. loc_setProperty(JSContext *cx, JSObject *obj, jsval id,    jsval *vp)
  755. {
  756.     const char *url_string;
  757.     JSString *str;
  758.     jsval val;
  759.     jsint slot;
  760.     JSURL *url;
  761.  
  762.     if (!JSVAL_IS_INT(id))
  763.     return JS_TRUE;
  764.  
  765.     slot = JSVAL_TO_INT(id);
  766.     /* Setting these properties should not cause a FE_GetURL. */
  767.     if (slot < 0 || slot == URL_TARGET)
  768.         return url_setProperty(cx, obj, id, vp);
  769.  
  770.     /* Make sure vp is a string. */
  771.     if (!JSVAL_IS_STRING(*vp) &&
  772.     !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
  773.     return JS_FALSE;
  774.     }
  775.  
  776.     /* Two cases: setting href vs. setting a component property. */
  777.     if (slot == URL_HREF || slot == URL_PROTOCOL) {
  778.     /* Make sure the URL is absolute and sanity-check its protocol. */
  779.     url_string = JS_GetStringBytes(JSVAL_TO_STRING(*vp));
  780.     url_string = lm_CheckURL(cx, url_string, JS_TRUE);
  781.     if (!url_string)
  782.         return JS_FALSE;
  783.     str = JS_NewStringCopyZ(cx, url_string);
  784.     XP_FREE((char *)url_string);
  785.     if (!str)
  786.         return JS_FALSE;
  787.     val = STRING_TO_JSVAL(str);
  788.     vp = &val;
  789.     } else {
  790.     /* Get href from session history before setting a piece of it. */
  791.     if (!loc_getProperty(cx, obj, INT_TO_JSVAL(URL_HREF), &val))
  792.         return JS_FALSE;
  793.     }
  794.  
  795.     /* Set slot's property. */
  796.     if (!url_setProperty(cx, obj, id, vp))
  797.     return JS_FALSE;
  798.  
  799.     url = JS_GetPrivate(cx, obj);
  800.     if (!url)
  801.     return JS_TRUE;
  802.     if (url->href)
  803.         url_string = JS_GetStringBytes(url->href);
  804.     else
  805.     url_string = "";
  806.     return url_load(cx, obj, url_string, NET_DONT_RELOAD);
  807. }
  808.  
  809. JSClass lm_location_class = {
  810.     "Location", JSCLASS_HAS_PRIVATE,
  811.     JS_PropertyStub, JS_PropertyStub, loc_getProperty, loc_setProperty,
  812.     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, url_finalize
  813. };
  814.  
  815. PR_STATIC_CALLBACK(JSBool)
  816. Location(JSContext *cx, JSObject *obj,
  817.      uint argc, jsval *argv, jsval *rval)
  818. {
  819.     return JS_TRUE;
  820. }
  821.  
  822. PR_STATIC_CALLBACK(JSBool)
  823. loc_toString(JSContext *cx, JSObject *obj,
  824.           uint argc, jsval *argv, jsval *rval)
  825. {
  826.     return loc_getProperty(cx, obj, INT_TO_JSVAL(URL_HREF), rval);
  827. }
  828.  
  829. PR_STATIC_CALLBACK(JSBool)
  830. loc_assign(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  831. {
  832.     JSURL *url;
  833.     jsval v;
  834.     JSObject *locobj;
  835.  
  836.     if (!JS_InstanceOf(cx, obj, &lm_location_class, NULL))  {
  837.         if(!JS_LookupProperty(cx, obj, lm_location_str, &v))  {
  838.         return JS_FALSE;
  839.     }
  840.     if(!JSVAL_IS_OBJECT(v) || !JSVAL_TO_OBJECT(v))  {
  841.         return JS_TRUE;
  842.     }
  843.     locobj = JSVAL_TO_OBJECT(v);
  844.     }  else  {
  845.         locobj = obj;
  846.     }
  847.     if (!(url = JS_GetInstancePrivate(cx, locobj, &lm_location_class, NULL)))
  848.         return JS_FALSE;
  849.     if (!loc_setProperty(cx, locobj, INT_TO_JSVAL(URL_HREF), vp))
  850.     return JS_FALSE;
  851.     *vp = OBJECT_TO_JSVAL(url->url_object);
  852.     return JS_TRUE;
  853. }
  854.  
  855. PR_STATIC_CALLBACK(JSBool)
  856. loc_reload(JSContext *cx, JSObject *obj,
  857.        uint argc, jsval *argv, jsval *rval)
  858. {
  859.     if (!JS_InstanceOf(cx, obj, &lm_location_class, argv))
  860.         return JS_FALSE;
  861.     return url_load(cx, obj, get_url_string(cx, obj),
  862.             (JSVAL_IS_BOOLEAN(argv[0]) && JSVAL_TO_BOOLEAN(argv[0]))
  863.             ? NET_SUPER_RELOAD
  864.             : NET_NORMAL_RELOAD);
  865. }
  866.  
  867. PR_STATIC_CALLBACK(JSBool)
  868. loc_replace(JSContext *cx, JSObject *obj,
  869.         uint argc, jsval *argv, jsval *rval)
  870. {
  871.     JSURL *url;
  872.     JSBool ans;
  873.  
  874.     if (!(url = JS_GetInstancePrivate(cx, obj, &lm_location_class, argv)))
  875.         return JS_FALSE;
  876.     url->url_decoder->replace_location = JS_TRUE;
  877.     /* Note: loc_assign ignores the id in favor of its own id anyway */
  878.     ans = loc_assign(cx, obj, 0, argv);
  879.     *rval = *argv;
  880.     return ans;
  881. }
  882.  
  883. static JSFunctionSpec loc_methods[] = {
  884.     {lm_toString_str,   loc_toString,  0},
  885.     {lm_reload_str,     loc_reload,     1},
  886.     {lm_replace_str,    loc_replace,    1},
  887.     {0}
  888. };
  889.  
  890. JSObject *
  891. lm_DefineLocation(MochaDecoder *decoder)
  892. {
  893.     JSObject *obj;
  894.     JSContext *cx;
  895.     JSURL *url;
  896.  
  897.     obj = decoder->location;
  898.     if (obj)
  899.     return obj;
  900.  
  901.     cx = decoder->js_context;
  902.     url = JS_malloc(cx, sizeof *url);
  903.     if (!url)
  904.     return NULL;
  905.     XP_BZERO(url, sizeof *url);
  906.  
  907.     obj = JS_InitClass(cx, decoder->window_object, NULL, &lm_location_class,
  908.                Location, 0, url_props, loc_methods, NULL, NULL);
  909.     if (!obj || !JS_SetPrivate(cx, obj, url)) {
  910.     JS_free(cx, url);
  911.     return NULL;
  912.     }
  913.  
  914.     /* XXX common subroutine this and above with lm_NewURL */
  915.     if (!JS_AddNamedRoot(cx, &url->href, "loc.text"))
  916.     return NULL;
  917.     if (!JS_AddNamedRoot(cx, &url->target, "loc.target"))
  918.     return NULL;
  919.     if (!JS_AddNamedRoot(cx, &url->text, "loc.text"))
  920.     return NULL;
  921.  
  922.     if (!JS_DefineProperty(cx, decoder->window_object, lm_location_str,
  923.                OBJECT_TO_JSVAL(obj), NULL, loc_assign,
  924.                JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
  925.     return NULL;
  926.     }
  927.     if (!JS_DefineProperty(cx, decoder->document, lm_location_str, 
  928.                            OBJECT_TO_JSVAL(obj), NULL, loc_assign, 
  929.                            JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
  930.     return NULL;
  931.     }
  932.  
  933.     /* Define the Location object (the current URL). */
  934.     url->url_decoder = HOLD_BACK_COUNT(decoder);
  935.     url->url_type = FORM_TYPE_NONE;
  936.     url->url_object = obj;
  937.     url->index = URL_NOT_INDEXED;
  938.     decoder->location = obj;
  939.     return obj;
  940. }
  941.