Storing staticfiles on a separate subdomain

PDF.js Express Version

|Core version|"7.3.3"|
|Build|"Ni80LzIwMjF8OThhMTM2NjE0"|
|WebViewer Server|false|
|Full API|false|

Detailed description of issue
My static files are hosted on a different subdomain. The iframe src/dest is on the same domain, it’s just the JS files that are on a separate subdomain.

I’m getting the following error:

Uncaught (in promise) Error: Setting up fake worker failed: "Cannot read property 'WorkerMessageHandler' of undefined".
    at PDFJSDocumentType.js:960

So the browser is obviously blocking inter-subdomain communication somewhere and returning undefined.

I’ve seen a similar error with PDF.JS when I was setting that up independently once, I needed something like:

  PDFJS.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/1.10.100/pdf.worker.min.js';

I’ve found other similar github issues where they recommend something like:

pdfjs.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;

How do I access the underlying PDF.js from pdfexpress?

Any ideas?

Expected behaviour
N/A

Does your issue happen with every document, or just one?
All documents.

Link to document
N/A

Code snippet

<!doctype html>
<html moznomarginboxes mozdisallowselectionprint>

<head>
  <base href="https://app-static.onuptick.com/static/pdfjsexpress/public/ui/">
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
  <link rel="stylesheet" href="style.css">
  <title>WebViewer UI</title>
</head>

<body>
  <div id="app"></div>
  <div id="line-connector-wrapper">
    <div id="line-connector-root"></div>
  </div>
  <script src="../core/CoreControls.js"></script>
  <script src="../core/pdfjs/PDFJSDocumentType.js"></script>
  <script src="../core/pdfjs/UIConfig.js"></script>
  <script src="webviewer-ui.min.js"></script>
</body>

</html>

Hey there,

PDF.js Express is loaded inside of an iframe, and there are browser iframe restrictions saying that when JS is loaded in an iframe from a separate domain, that JS cannot be interacted with from the parent window (this is why you are seeing that error).

The best solution is to not host the PDF.js Express files on a different domain. However, if this is absolutely necessary, there is a workaround (this is a PDFTron WebViewer guide but it also applies to Express).

There is no way to access the underlying PDF.js.

Thanks!
Logan

I found this article which discusses another way to get around cross-origin if you’re still on the same domain (vs subdomain):

I created copies of the four linked javascript files and added document.domain = 'mydomain.com' to them.

I modified my viewer to look like this:

<!doctype html>
<html moznomarginboxes mozdisallowselectionprint>

<head>
  <base href="{% static 'pdfjsexpress/public/ui/' %}">
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
  <link rel="stylesheet" href="style.css">
  <title>WebViewer UI</title>
</head>

<body>
  <div id="app"></div>
  <div id="line-connector-wrapper">
    <div id="line-connector-root"></div>
  </div>
  <script>
    // Proxy whether we're running in production and set the domain to enable cross origin iframe access
    // https://pdfjs.community/t/storing-staticfiles-on-a-separate-subdomain/828
    // https://stackoverflow.com/questions/3076414/ways-to-circumvent-the-same-origin-policy
    if (window.location.href.startsWith('https')) {
      document.domain = 'onuptick.com'
    }
  </script>
  <script src="../core/CoreControls.crossdomain.js"></script>
  <script src="../core/pdfjs/PDFJSDocumentType.crossdomain.js"></script>
  <script src="../core/pdfjs/UIConfig.crossdomain.js"></script>
  <script src="webviewer-ui.min.crossdomain.js"></script>
</body>

</html>

And added this useEffect into my react component just before the useEffect call to initialise the WebViewer:

  useEffect(() => {
    if (crossOrigin) {
      document.domain = 'onuptick.com'  // https://stackoverflow.com/questions/3076414/ways-to-circumvent-the-same-origin-policy
    }
  })

However, this didn’t work. I get the same error.

So I looked at the iframe src that the pdfjs.express library was creating, and opened that in a tab directly to eliminate the cross tab communication and see whether the error remains. So to be clear that means I’ve got a tab open that is the /core/ui/index.html with the #d= pointing to a PDF file.

I get the same result “Error: Setting up fake worker failed: “Cannot read property ‘WorkerMessageHandler’ of undefined”.”

So the error is happening in PDF.js / Express somewhere, not the interframe communication.

This means the links you’ve posted aren’t relevant as I can’t use postMessage (because there’s no frame involved). It’s just that the /ui/index.html is on a different domain to the scripts it’s loading (because staticfiles are hosted on a different subdomain).

Any ideas?

Hi there,

Are you able to tell me more about your environment (browser, server stack, etc)? I assume you have a bit a of a unique environment considering the fact that you cannot load assets from the same domain.

Are you able to send me a link where I can reproduce your issue?

Hey Logan,

Not unique- I’m not aware of any modern stack that serves static files and dynamic content from the same server any more. Static files are usually compiled using something like webpack and dropped on a static domain (for us this is app-static.onuptick.com) while the app is served from whatever backend end you’re running like Django, etc.

As an example, this site itself (pdfjs.community) has all the static assets served from (discourse-cdn dot com):

I’ll set up an example, I really appreciate the help. Any chance you can email me aidan at uptickhq dot com so I can send you a private link with the details?

Hi there,

You can send me a private message on this site with details.

Thanks!

@Logan I have sent a message last week, did you receive it?

Thanks,

Hi there!

Sorry about that, I was on vacation last week. Yes I did receive your message and I will investigate shortly.

Hi there,

The issue is still related to loading assets cross origin. However, this can be resolved by the following:

  1. Remove the <base href="//app-static.onuptick.com/static/pdfjsexpress/public/ui/"> as I believe this will cause weird things to happen internally.

  2. Instantiating Express how it is intended and letting Express set up the iframe, instead of manually importing all the scripts. The iframe is important.

  3. After this, you will still have the issue of cross-origin iframes. To work around this, please use the config option as documented here. Since you are loading the config file from a different domain, you will need to follow this step as well.

After all that your code should look something like this:

import Express from "@pdftron/pdfjs-express";

Express(
  {
    path: "https://app-static.onuptick.com/static/pdfjsexpress/public",
   config: "path/to/config.js"
  },
  element
)

Thanks!
Logan

I am trying to apply the workaround for static files hosted on a separate domain (CDN) and I can’t seem to be able to make it work.

It just crashes with

Uncaught (in promise) Viewer is on a different domain, the promise from WebViewer function is rejected and API functions will not work because of cross domain permissions. See https://www.pdftron.com/kb_cross_origin for more information.

If tried to have config file as well, but it never gets to handling VIEWER_LOADED event:

window.onload = function () {
 instance.UI.addEventListener(instance.UI.Events.VIEWER_LOADED, () => {
  console.log("loaded")
 })
}

My instantiation is as simple as:

        WebViewer({
            path,
            licenseKey,
            initialDoc: this.element.dataset.file,
            config: `${host}/pdf/viewer.js`
        }, this.element)

And I have updated the origins file:

http://localhost:3000
https://hackerintro.com

Anything I am potentially missing?

Version information:

Build: "MTAvMjEvMjAyMXwwZTI4M2E1YmQ="
Core version: "8.1.1"
Full API: false
UI version: "8.1.0"
WebViewer Server: false

Hey there!

I believe you did actually have it set up correctly, but your code in the config file is incorrect: you can remove the window.onload as that is redundant. Your code should just look like:

  instance.UI.addEventListener(instance.UI.Events.VIEWER_LOADED, () => {
   console.log("loaded")
  })

(Also note that you will still see the error in the console - this can safely be ignored (we are working on removing this))

Besides that I believe everything else you have done is correct.

Let me know if fixing that code does not work for you.

Thanks!
Logan