[whatwg/xhr] Synchronous XMLHttpRequest should be allowed in prerender scripts (JS in <head>) (#253)

So, I'm dealing with an issue with bridging older browsers with the new `manifest.json` spec. Due to the way the pages are served, `manifest.json` is served based on certain server criteria (hostname) while `index.html` is a static file from a CDN.

Basically, before the UI events start, and any interaction is possible, I use a `prerender.js` script in <head> to style the document before it's rendered. This could include things like setting different content header padding if the page is rendered on an iOS PWA. Also, I use this pre-render state to set some colors based on `prefers-color-scheme:dark`. 

Some properties I want to grab straight from `manifest.json`. This means it has to be intentionally synchronous. For example, let's say I want to style the `background-color` of the document (`<html>`) to be the same as what's in `manifest.json`. Let's say I have the color as `#000` (black). When a PWA loads, it will load a black background, but, if I don't use block the render before changing the `background-color` in `<html>`and, instead do this asynchronously, then the background will flash white (browser default), and then go black.

Here's some short snippets of this configuration:

manifest.json: 
````
{
  "name": "Full App Name",
  "short_name": "App",
  "start_url": "/",
  "background_color": "#000000",
  "display": "standalone",
  "theme_color": "#2196F3",
}
````

index.html:
````
<html> 
  <head>
    <link rel="manifest" href="manifest.json" />
    <script src="prerender.js">
    <!-- Content snip --!>
````

prerender.js
````
import * as Document from '@shortfuse/materialdesignweb/core/document/index';

/** @return {Object} */
function getManifestObject() {
  let manifestObject = {};
  const xhr = new XMLHttpRequest();

  xhr.onreadystatechange = () => {
    if (xhr.readyState === 4) {
      manifestObject = JSON.parse(xhr.responseText);
    }
  };
  xhr.open('GET', 'manifest.json', false);

  try {
    xhr.send();
  } catch (e) {
    console.error('Could not load manifest.json');
  }

  return manifestObject;
}

/**
 * @param {!string} name
 * @param {string} content
 * @return {void}
 */
function setMeta(name, content) {
  let attr = document.head.getElementsByTagName('meta').namedItem(name);
  if (attr) {
    if (content == null) {
      attr.parentElement.removeChild(attr);
    } else {
      attr.setAttribute('content', content);
    }
  } else if (content != null) {
    attr = document.createElement('meta');
    attr.setAttribute('content', content);
    document.head.appendChild(attr);
  }
}

/** @return {void} */
function onDOMContentLoaded() {
  const manifest = getManifestObject();
  setMeta('apple-mobile-web-app-title', manifest.short_name);
  setMeta('apple-mobile-web-app-status-bar-style', manifest.theme_color);
  setMeta('theme-color', manifest.theme_color);
  document.documentElement.style.setProperty('background-color', manifest.background_color);
  document.removeEventListener('DOMContentLoaded', onDOMContentLoaded);
}

Document.onPrerender();
document.addEventListener('DOMContentLoaded', onDOMContentLoaded);
````

I will add, I already have a service worker in place to serve `manifest.json` from cache, so ideally speaking, there's as little load-up time lag as possible here. (I'd imagine first fetch could experience a slight uptick in delay, but I could always add a check to see if the application is running as a PWA before doing the synchronous request.)

Right now, I'm wary about using the synchronous request because of the deprecated nature. The alternative I current have is rewriting the `index.html` file, which complicates the back-end.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/xhr/issues/253

Received on Friday, 23 August 2019 15:06:02 UTC