Jump to content

http-in, content/mime types and CSS


Timothy McGregor
 Share

You are about to reply to a thread that has been inactive for 402 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

I'll try to be brief.

Developing a system to display information on a prim via http-in requests. This will be both for an in-world prim as well as (probably) a HUD. I've already figured out that the text/html content type is limited in a number of ways, including that the person viewing the page must also be the owner of the source script serving the content. I guess this is to prevent a proliferation of in-world web servers accessible to the outside world. I get it. I guess.  Except xhtml evidently does not bear those limitations. I modified my html content to be xhtml compliant, and set that content type for the http-response, and it then renders properly no matter who owns what, and even in external browsers. 

So that's where I've been going with this project. I've no interest in having it deliver to external browsers, and will probably include a user agent check if that's possible, to prevent access other than in-world. 

The problem I'm up against now is that I had been including styles in the header for use in styling the markup, and that is growing a bit much. At one point I hit a stack/heap collision which I suspect was caused by the final html string being too large. So I thought I would try to separate out the style sheet in the usual way we do external css files.

<link rel="stylesheet" type="text/css" href="styles.css" />

The problem I see here is that the only mime type I can serve that with is text/plain, and the browser consequently won't render the styles.

Why is this whole thing so half-ass to begin with?

Is there a javascript injection hack that I can use to get raw CSS into the page to style the markup, without having to include all the CSS in the original document variable?

Why does xhtml not have any of the caveats/restrictions that html does?

Why does LL give us a tool like this that we can only just almost use to accomplish a task to a certain extent, only to have to hack our way through the remaining 70%?

Why after over 20 years do we still not have a method other than this hacky MOAP implementation to render dynamic, styled text on a prim face that doesn't involve texture hacking ala xytext or other inefficient means?

Why, Tigger? Why?

  • Like 1
Link to comment
Share on other sites

1 minute ago, Quistess Alpha said:

Can confirm, not being able to serve separate CSS via LSL is a pain. If you really know your way around CSS, you can cook up some ok short stuff in the preamble, but it really eats into your memory usage.

Have you tried complaining on the jira?

I was interested also in this part of the OP's question, do you know the answer? I seem to remember seeing examples that used JS:

12 hours ago, Timothy McGregor said:

Is there a javascript injection hack that I can use to get raw CSS into the page to style the markup, without having to include all the CSS in the original document variable?

 

Link to comment
Share on other sites

2 minutes ago, Love Zhaoying said:

do you know the answer?

I'm not really fluent with javascript, I just know how to search for examples and apply them in context...

It's almost certainly possible to style individual elements with javascript, and hypothetically you could read a CSS (or conveniently modified personal format) as plaintext and manually apply features, but that would be very involved. Simple duck-duck-go searches for 'load CSS with javascript' return some promising avenues, but I've not personally tested them, and as has been seen before in other threads, you really need a good sense of what you're doing and debugging workflow to make this stuff work.

  • Like 1
Link to comment
Share on other sites

5 minutes ago, Quistess Alpha said:

I'm not really fluent with javascript, I just know how to search for examples and apply them in context...

It's almost certainly possible to style individual elements with javascript, and hypothetically you could read a CSS (or conveniently modified personal format) as plaintext and manually apply features, but that would be very involved. Simple duck-duck-go searches for 'load CSS with javascript' return some promising avenues, but I've not personally tested them, and as has been seen before in other threads, you really need a good sense of what you're doing and debugging workflow to make this stuff work.

I guess my own interests - and I think this would only be in LSL example scripts for obvious reasons - lie in the idea of generally integrating JS with MOAP.

Yep, I can always Google!

Link to comment
Share on other sites

I have searched JIRA, probably wouldn't bother with a feature request there since it would likely never get done. There is some reason they don't want to do this. The whole http-in thing feels like an unfinished project in some respects, but I really suspect that the limitations and omissions (like css content type) are deliberate.

  • Like 1
Link to comment
Share on other sites

In case it's encouraging, I load jquery in one of my http-in apps. On the other hand, I'm not sure we're supposed to encourage this. I only use this stuff myself because I'm not sure how to give anybody a URL they should trust. Doesn't help that the SSL cert is self-signed, but that may be appropriate in that it only discourages users from what might be a risky operation as far as they can know.

  • Thanks 1
Link to comment
Share on other sites

6 hours ago, Qie Niangao said:

In case it's encouraging, I load jquery in one of my http-in apps. On the other hand, I'm not sure we're supposed to encourage this. I only use this stuff myself because I'm not sure how to give anybody a URL they should trust. Doesn't help that the SSL cert is self-signed, but that may be appropriate in that it only discourages users from what might be a risky operation as far as they can know.

I will have to look at this. But in terms of setting css properties with jquery, I would suppose that this would just be another way to consume the limited space available in the document I serve in httpresponse. Whether I put the styles directly in the header or set them up with jquery, I'm not sure it isn't a wash in the end. Unless I can pull the js code in from an external source, which I am trying to avoid, because that external source is bought and paid for by me, and therefore controlled by me, and this application will be passed on to others to manage going forward. I'm already facing that dilemma with an image I'm using because there is no way to leverage in-world textures as image sources. I imagine in a perfect world a hybrid http environment where in-world, something like <img src="<UUID>" ... > would work. Same with external css referenced in a notecard UUID. Just in-world, so there is some assurance that people aren't just standing up public websites in every prim from hell to breakfast. But there is no real effort to think through these things, consider ideas and use cases, it's always "we won't do that because stuff and things". 

So in 2023, we still have no comprehensive methods available to present clean, dynamic, styled, quick rendering text on an object face in-world, without jumping through a million hoops of burning fire, and even if we manage to get through the last hoop, the end result is still not what we wanted. It's just an acceptable compromise because there is nothing better on offer. We just hope that those viewing and consuming the content will be patient and understand that this is Second Life, and this is how things are here.

So I'm left with the prospect of reverting back to an ugly xytext presentation where people have to stand around and wait for every fu(*$ing texture for every letter to download and render.. and oh wait.. it just changed and I didn't finish reading the last one..

The deeper I get into this, the more discouraged I become. It took 20 years to get LSD. 64k of storage. That was still promising, until I began to realize that building this application out the way I envision it is going to require leveraging external resources, because we still just don't have those resources built in to the platform. The limited resources we do have available get us maybe 20% there, and we have to hack our way through the rest of it. And if it's something you have to pass on to someone else, it more often than not just becomes a massive clusterfu*$. 

Anyway...

  • Like 1
Link to comment
Share on other sites

Also, not too hard to find a solution to include things via javascript. I'm sure there are better methods, but this is one I stumbled on that seemed somewhat reasonable, poked into a simple notecard displaying script:

string gNCText;
integer gNCLine;
string gNCName;
key gNCKey;
key ghNC;

key ghRequestURL;
string gsURL;

string gPageText =
"<!DOCTYPE xhtml>
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head>
<script type=\"text/javascript\">
//<![CDATA[
// from https://www.w3schools.com/howto/howto_html_include.asp
function includeHTML() {
  var z, i, elmnt, file, xhttp;
  /* Loop through a collection of all HTML elements: */
  z = document.getElementsByTagName(\"*\");
  for (i = 0; i < z.length; i++) {
    elmnt = z[i];
    /*search for elements with a certain atrribute:*/
    file = elmnt.getAttribute(\"w3-include-html\");
    if (file) {
      /* Make an HTTP request using the attribute value as the file name: */
      xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
        if (this.readyState == 4) {
          if (this.status == 200) {elmnt.innerHTML = this.responseText;}
          if (this.status == 404) {elmnt.innerHTML = \"Page not found.\";}
          /* Remove the attribute, and call this function once more: */
          elmnt.removeAttribute(\"w3-include-html\");
          includeHTML();
        }
      }
      xhttp.open(\"GET\", file, true);
      xhttp.send();
      /* Exit the function: */
      return;
    }
  }
}
//]]>
</script></head>
<body>
    <div w3-include-html=\"style.css\" />
    <div w3-include-html=\"test.txt\" />
    <script>includeHTML();</script>
</body></html>";

default
{
    state_entry()
    {   gNCName = llGetInventoryName(INVENTORY_NOTECARD,0);
        gNCKey = llGetInventoryKey(gNCName);
        ghNC = llGetNotecardLine(gNCName,gNCLine);
        llSay(0, "initializing");
    }
    on_rez(integer i)
    {   llResetScript();
    }
    changed(integer c)
    {   if(c&CHANGED_INVENTORY)
        {   key ncKey = llGetInventoryKey(gNCName);
            if(ncKey!=gNCKey)
            {   
                gNCText = "";
                gNCLine=0;
                gNCKey=ncKey;
                ghNC = llGetNotecardLine(gNCName,gNCLine);
            }
        }
        if(c&(CHANGED_REGION_START|CHANGED_REGION))
        {   llReleaseURL(gsURL);
            ghRequestURL = llRequestURL();
        }
    }
    dataserver(key ID, string data)
    {   if(ID==ghNC)
        {   if(data==EOF)
            {   ghRequestURL = llRequestURL();
            }else
            {   gNCText+="<p>"+data+"</p>";
                ghNC = llGetNotecardLine(gNCName,++gNCLine);
            }
        }
    }
    http_request(key ID, string Method, string Body)
    {   if(ID==ghRequestURL)
        {   gsURL=Body;
            llSetLinkMedia(LINK_THIS,2,
                [   PRIM_MEDIA_HOME_URL, gsURL+"/home",
                    PRIM_MEDIA_CURRENT_URL, gsURL+"/home",
                    PRIM_MEDIA_AUTO_PLAY, TRUE,
                    PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE, // don't show nav-bar.
                    PRIM_MEDIA_PERMS_INTERACT, PRIM_MEDIA_PERM_NONE
                ]);
        }else 
        {   string page = llGetHTTPHeader(ID,"x-path-info");
            if("/home"==page)
            {   llSetContentType(ID,CONTENT_TYPE_XHTML);
                llHTTPResponse(ID,200,gPageText);
            }else if("/test.txt"==page)
            {   llSetContentType(ID,CONTENT_TYPE_TEXT);
                llHTTPResponse(ID,200,gNCText);
            }else if("/style.css"==page)
            {   llSetContentType(ID,CONTENT_TYPE_TEXT);
                llHTTPResponse(ID,200,
                    "<style>body { font-size: 2.5vw; }</style>");
            }
        }
    }
}

I should mention, just a proof of concept. Obviously in an actual application, you would store long string variables in LSD, and/or pass messages from other scripts for extra memory.

Edited by Quistess Alpha
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

4 hours ago, Timothy McGregor said:

I will have to look at this. But in terms of setting css properties with jquery, […]

Oh, sorry, I didn't mean to suggest that. I now realize I was straying from your original topic, instead responding tangentially to:

16 hours ago, Love Zhaoying said:

I guess my own interests - and I think this would only be in LSL example scripts for obvious reasons - lie in the idea of generally integrating JS with MOAP.

Sorry for the confusion.

  • Thanks 1
Link to comment
Share on other sites

3 hours ago, Qie Niangao said:

Not at all. I just responded to the rare bit of the thread I thought I understood, blithely ignoring all else. 🤪

Yeah, signal to noise ratio in my OP not great.

What I am trying to figure out is how to serve a style sheet separately from the initial html page instead of embedding all the styles in the <head> section, which is causing it to grow too large. So in a separate script I did this:

key g_key_fhandle;
string g_str_fname = "stylesheet.css";
integer g_int_row;
key g_key_urlrequest;
string g_str_url;
string g_str_css;

fSetup()
{
    g_str_url = "";
    g_key_urlrequest = llRequestURL();
}

fReadStylesheet()
{
    g_key_fhandle = llGetNotecardLine(g_str_fname, g_int_row);
}
 
default
{
    state_entry()
    {
        fSetup(); 
    }
    
    changed(integer change)
    {
        if(change & CHANGED_INVENTORY)
        { 
            llOwnerSay("File has changed");
            g_int_row = 0;
            g_str_css = "";
            fReadStylesheet();
        }
    }
    
    touch_start(integer n)
    {
        llOwnerSay("asset url is: " + g_str_url);
    }

    http_request(key id, string method, string body)
    {
        if (g_key_urlrequest == id)
        {
            g_key_urlrequest ="";
        
            if (method == URL_REQUEST_GRANTED)
            {
                g_str_url = body + "/styles.css";
                fReadStylesheet();
            }
            else if (method == URL_REQUEST_DENIED)
            {
                llOwnerSay("Something went wrong, no url. " + body);
            }
            else
            {
                llHTTPResponse(id,405,"Method unsupported");
            }
        }
        else
        {
            string str_xpath = llGetHTTPHeader(id, "x-path-info");
            if (method == "GET" && str_xpath == "/styles.css")
            {
                llSetContentType(id, CONTENT_TYPE_TEXT);
                llHTTPResponse(id,200,g_str_css);
            }
        }
    }

    dataserver(key id, string data)
    {
        if (id == g_key_fhandle)
        {
            if (data != EOF)
            {
                g_str_css += data + "\n";
                ++g_int_row;
                g_key_fhandle = llGetNotecardLine(g_str_fname, g_int_row);
            }
            else
            {
                llMessageLinked(LINK_THIS, 100, g_str_url, "");
            }
        }
    }
}

The notecard is just a file containing css. It gets served but the browser will not do anything with it (presumably) due to it being CONTENT_TYPE_TEXT. So I was wondering, lacking a CONTENT_TYPE_CSS mime type, if there was a way to request that same file, and use javascript to convert it on the fly to css that could be injected into the page after it's served. Or any other trick to make this work. I am not very fluent with javascript.

Link to comment
Share on other sites

55 minutes ago, Timothy McGregor said:

if there was a way to request that same file, and use javascript to convert it on the fly to css that could be injected into the page after it's served. Or any other trick to make this work. I am not very fluent with javascript.

That's what I demonstrated in my above post. basically, when you request the file via javascript, it's lazy and ignores mime types. When you get the data, you set the 'innerHTML' property of some element to whatever arbitrary text you receive and it updates the page as if that portion of (x)HTML had been there all along:

so, pre-javascript:

// Main page XHTML:

<!DOCTYPE xhtml>
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head>
<script type=\"text/javascript\">
//<![CDATA[
See above for function code
//]]>
</script></head>
<body>
    <div w3-include-html=\"style.css\" />
    <div w3-include-html=\"test.txt\" />
    <script>includeHTML();</script>
</body></html>

// style.css:
<style>body { font-size: 2.5vw; }</style>
// test.txt:
Some text

post-javascript:

// Main page XHTML:

<!DOCTYPE xhtml>
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head>
<script type=\"text/javascript\">
//<![CDATA[
See above for function code
//]]>
</script></head>
<body>
  <div><style>body {font-size: 2.5vw; } </style> </div>
  <div> Some text </div>
    <script>includeHTML();</script>
</body></html>

Adding an in-line stylesheet like that actually works surprisingly. My train-of-throught that lead me to the method was supposing the CSS was just part of the 'main page' and other content would be loaded in later.

  • Thanks 2
Link to comment
Share on other sites

12 minutes ago, Quistess Alpha said:

That's what I demonstrated in my above post. basically, when you request the file via javascript, it's lazy and ignores mime types. When you get the data, you set the 'innerHTML' property of some element to whatever arbitrary text you receive and it updates the page as if that portion of (x)HTML had been there all along:

so, pre-javascript:

// Main page XHTML:

<!DOCTYPE xhtml>
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head>
<script type=\"text/javascript\">
//<![CDATA[
See above for function code
//]]>
</script></head>
<body>
    <div w3-include-html=\"style.css\" />
    <div w3-include-html=\"test.txt\" />
    <script>includeHTML();</script>
</body></html>

// style.css:
<style>body { font-size: 2.5vw; }</style>
// test.txt:
Some text

post-javascript:

// Main page XHTML:

<!DOCTYPE xhtml>
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head>
<script type=\"text/javascript\">
//<![CDATA[
See above for function code
//]]>
</script></head>
<body>
  <div><style>body {font-size: 2.5vw; } </style> </div>
  <div> Some text </div>
    <script>includeHTML();</script>
</body></html>

Adding an in-line stylesheet like that actually works surprisingly. My train-of-throught that lead me to the method was supposing the CSS was just part of the 'main page' and other content would be loaded in later.

Oh.. Thank you for that. sorry, it's early here and I'm still waking up, catching up, and getting ready for work. I'll take a closer look at this later. it might be the ticket.

  • Like 2
Link to comment
Share on other sites

2 minutes ago, Timothy McGregor said:

and none of the image URLs are even close.

off-topic, but as I understand it, things got more complicated when LL allowed you to upload profile pics directly to the web-profile instead of using an in-world texture.

4 minutes ago, Timothy McGregor said:

the images aren't at all useful as served from that service.

It's not ideal, but I wouldn't call it useless. Many/most in-world visitor boards use it, and I used it as part of an in-world stamp collecting game to make viewing your stamp collection a bit less painful. 256x192 is plenty big for something that only takes up a portion of the page, and you can re-size dimensions of the image where it's displayed in html.

Link to comment
Share on other sites

29 minutes ago, Quistess Alpha said:

It's not ideal, but I wouldn't call it useless. Many/most in-world visitor boards use it, and I used it as part of an in-world stamp collecting game to make viewing your stamp collection a bit less painful. 256x192 is plenty big for something that only takes up a portion of the page, and you can re-size dimensions of the image where it's displayed in html.

Yeah, I'm not considering that an in world display isn't like a desktop browser on a 4k display. I will take a look at this as well this weekend. It may turn out to be an adequate compromise if I don't have to pull assets in from my aws bucket.

  • Like 1
Link to comment
Share on other sites

Okay. I think I have sorted this out in a way that I can live with. This is a bit more straightforward than looping through every html element and applying inline styles to them. You just write your html as you normally would, referencing the styles with class= or id=. Just a very small script that will fetch the stylesheet as a text object, create a <style> element in <head>, and insert the text, which is the complete set of styles.

One thing I noticed is that the content would render first without any styles, and then quickly reformat with my styles once they were inserted and ready. Common issue I guess called Flash Of Unstyled Content. To get around that, I hard code a style at the top that sets visibility of all html to hidden. Then as the last style in my styles.css file, I set it back to visible. You still get the "flash", but you don't get the unstyled content.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<style>html{visibility: hidden;opacity:0;}</style>
 
<script type = "text/javascript">

async function getStylesheet(url)
 {
    var response = await fetch(url);
    var cssText = await response.text();
    var styleElement = document.createElement('style');
    styleElement.textContent = cssText;
    document.head.appendChild(styleElement);
  }

getStylesheet("http://simhost-08a07f2c6a2e1e0a2.agni.secondlife.io:12046/cap/6155b95f-249b-94c5-d9f9-0b8e91f7475f/styles.css");

</script>
</head>
<body>
<div class = "listcontainer">
<div class = "listdetail"><a href = "http://simhost-08a07f2c6a2e1e0a2.agni.secondlife.io:12046/cap/f3190087-cb60-dca1-bdaa-f1e511f6aef2/?event=3818d10b-21bd-77e8-7aea-5d9b81dbb438" id = "biglink">Item 1</a><br />March 22, 2023 - 04:00pm - 05:00pm </div><br />
<div class = "listdetail"><a href = "http://simhost-08a07f2c6a2e1e0a2.agni.secondlife.io:12046/cap/f3190087-cb60-dca1-bdaa-f1e511f6aef2/?event=a52e9760-90f6-6c52-32a9-5821b0007875" id = "biglink">Item 2</a><br />March 24, 2023 - 02:00pm - 03:00pm </div><br />
<div class = "listdetail"><a href = "http://simhost-08a07f2c6a2e1e0a2.agni.secondlife.io:12046/cap/f3190087-cb60-dca1-bdaa-f1e511f6aef2/?event=af5ebfcf-d16a-3897-af9b-dff88d912661" id = "biglink">Item 3</a><br />April 2, 2023 - 08:00am - 10:00am </div><br />
<div class = "listdetail"><a href = "http://simhost-08a07f2c6a2e1e0a2.agni.secondlife.io:12046/cap/f3190087-cb60-dca1-bdaa-f1e511f6aef2/?event=fe0a9259-c695-38bf-806b-cd22853fda00" id = "biglink">Item 4</a><br />April 7, 2023 - 04:00pm - 05:00pm </div><br />
</div>
</body>
</html>

The main http-in script that serves the document goes into a wait state at startup, waiting for a link_message from the css script that contains the URL to get the stylesheet, sets that in a variable and goes to state "running". Then the page is assembled with all the elements and delivered. The css script itself just reads in a notecard of raw styles, formatted exactly like any style sheet that would normally be linked to the document with content type text/css. The advantage to this is I can maintain all of my styles outside of any code. Just a regular text based stylesheet that gets applied to the whole document.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

  • 1 month later...
2 hours ago, Bleuhazenfurfle said:

If that happens, you'll presumably be able to put your HTML, CSS, and JS into notecards, much for this purpose.

Unfortunately, even if you could directly access a notecard via some secondlife API, they would get served with a plain-text mime-type, which means browsers will refuse to interpret them as CSS/JS/(X)HTML or anything that isn't raw text.

Allowing the mime-type to be specified in the notecard would of-course solve the issue, but that's even less likely to happen. . .

  • Thanks 1
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 402 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...