async function onMessage ({ origin, data }) {
      let response = data
      if (!response || !['string', 'object'].includes(typeof response)) {
        // Do nothing
        return
      }
      let id
      let type
      try {
        // Todo: This block can be removed (and the above check changed to
        //   insist on an object) if embedAPI moves away from a string to
        //   an object (if IE9 support not needed)
        response = typeof response === 'object' ? response : JSON.parse(response)
        if (response.namespace !== 'imagelib') {
          return
        }
        if (!allowedImageLibOrigins.includes('*') && !allowedImageLibOrigins.includes(origin)) {
          // Todo: Surface this error to user?
          console.error(`Origin ${origin} not whitelisted for posting to ${window.origin}`)
          return
        }
        const hasName = 'name' in response
        const hasHref = 'href' in response

        if (!hasName && transferStopped) {
          transferStopped = false
          return
        }

        if (hasHref) {
          id = response.href
          response = response.data
        }

        // Hide possible transfer dialog box
        if (document.querySelector('se-elix-alert-dialog')) {
          document.querySelector('se-elix-alert-dialog').remove()
        }
        type = hasName
          ? 'meta'
          : response.charAt(0)
      } catch (e) {
        // This block is for backward compatibility (for IAN and Openclipart);
        //   should otherwise return
        if (typeof response === 'string') {
          const char1 = response.charAt(0)

          if (char1 !== '{' && transferStopped) {
            transferStopped = false
            return
          }

          if (char1 === '|') {
            const secondpos = response.indexOf('|', 1)
            id = response.substr(1, secondpos - 1)
            response = response.substr(secondpos + 1)
            type = response.charAt(0)
          }
        }
      }

      let entry; let curMeta; let svgStr; let imgStr
      switch (type) {
        case 'meta': {
        // Metadata
          transferStopped = false
          curMeta = response

          // Should be safe to add dynamic property as passed metadata
          pending[curMeta.id] = curMeta // lgtm [js/remote-property-injection]

          const name = (curMeta.name || 'file')

          const message = svgEditor.i18next.t('notification.retrieving').replace('%s', name)

          if (mode !== 'm') {
            await seConfirm(message)
            transferStopped = true
          } else {
            entry = document.createElement('div')
            entry.textContent = message
            entry.dataset.id = curMeta.id
            preview.appendChild(entry)
            curMeta.entry = entry
          }

          return
        }
        case '<':
          svgStr = true
          break
        case 'd': {
          if (response.startsWith('data:image/svg+xml')) {
            const pre = 'data:image/svg+xml;base64,'
            const src = response.substring(pre.length)
            response = decode64(src)
            svgStr = true
            break
          } else if (response.startsWith('data:image/')) {
            imgStr = true
            break
          }
        }
        // Else fall through
        default:
          // TODO: See if there's a way to base64 encode the binary data stream
          // Assume it's raw image data
          // importImage(str);

          // Don't give warning as postMessage may have been used by something else
          if (mode !== 'm') {
            closeBrowser()
          } else {
            pending[id].entry.remove()
          }
          // await alert('Unexpected data was returned: ' + response, function() {
          //   if (mode !== 'm') {
          //     closeBrowser();
          //   } else {
          //     pending[id].entry.remove();
          //   }
          // });
          return
      }

      switch (mode) {
        case 's':
        // Import one
          if (svgStr) {
            svgEditor.svgCanvas.importSvgString(response)
          } else if (imgStr) {
            importImage(response)
          }
          closeBrowser()
          break
        case 'm': {
        // Import multiple
          multiArr.push([(svgStr ? 'svg' : 'img'), response])
          curMeta = pending[id]
          let title
          if (svgStr) {
            if (curMeta?.name) {
              title = curMeta.name
            } else {
            // Try to find a title
            // `dropXMLInternalSubset` is to help prevent the billion laughs attack
              const xml = new DOMParser().parseFromString(dropXMLInternalSubset(response), 'text/xml').documentElement // lgtm [js/xml-bomb]
              title = xml.querySelector('title').textContent || '(SVG #' + response.length + ')'
            }
            if (curMeta) {
              Array.from(preview.children).forEach(function (element) {
                if (element.dataset.id === id) {
                  if (curMeta.preview_url) {
                    const img = document.createElement('img')
                    img.src = curMeta.preview_url
                    const span = document.createElement('span')
                    span.appendChild(img)
                    element.append(span)
                  } else {
                    element.textContent = title
                  }
                  submit.removeAttribute('disabled')
                }
              })
            } else {
              const div = document.createElement('div')
              div.textContent = title
              preview.appendChild(div)
              submit.removeAttribute('disabled')
            }
          } else {
            if (curMeta?.preview_url) {
              title = curMeta.name || ''
              entry = document.createElement('span')
              const img = document.createElement('img')
              img.src = curMeta.preview_url
              entry.appendChild(img)
              entry.appendChild(document.createTextNode(title))
            } else {
              entry = document.createElement('img')
              entry.src = response
            }

            if (curMeta) {
              Array.from(preview.children).forEach(function (element) {
                if (element.dataset.id === id) {
                  element.appendChild(entry)
                  submit.removeAttribute('disabled')
                }
              })
            } else {
              const div = document.createElement('div')
              div.appendChild(entry)
              preview.appendChild(div)
              submit.removeAttribute('disabled')
            }
          }
          break
        } case 'o': {
        // Open
          if (!svgStr) { break }
          closeBrowser()
          const ok = await svgEditor.openPrep()
          if (!ok) { return }
          svgCanvas.clear()
          svgCanvas.setSvgString(response)
          // updateCanvas();
          break
        }
      }
    }
@param {PlainObject} cfg
@param {string} cfg.origin
@param {ImageLibMetaMessage|ImageLibMessage|string} cfg.data String is deprecated when parsed to JSON `ImageLibMessage`
@returns {void}