fphilipe / PageExtender.app

Safari extension that injects custom CSS and JS files based on page host.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Interacting with page script objects

sj26 opened this issue · comments

Firstly, thank you for this extension. I loved dotjs, and sorely miss it — this is a great spiritual successor. I bought it on the app store.

I use FastMail, which is a great email service. They have a pretty slick web UI, which has themes including a light theme and a dark theme. I use a mac which varies between a system-wide light and dark theme depending on the time of day. I'm trying to inject a script into the fastmail web UI which flips the theme from light to dark and back based on the operating system theme. I can do this by injecting some code using the web developer console. I'm trying to get this code working through some sort of automatic injection, and now trying with PageExtender.

The Fastmail themes are specified in JavaScript. Their whole script object model is exposed via the window global FastMail. I can run FastMail.theme.set("dark") in a console to set the theme. But from PageExtender I always get:

TypeError: undefined is not an object (evaluating 'window.FastMail.theme')

If I console.log(FastMail), it's undefined.

Using a setTimeout to delay the execution until the page is fully, visibly loaded doesn't change this.

I also tried doing window.Extended = true in my PageExtender script, then looking for Extended or window.Extended in the web console — but I get:

ReferenceError: Can't find variable: Extended

As far as I can see, the window object is the same Window, it has the same document property which visibly has the same head object with the same title etc. But I can't seem to see the properties added by the document's scripts from PageExtender's scripts, and vice versa.

What am I missing, how can I get this to work?

Ah — from the Safari Extensions Development Guide:

Injected scripts have an implied namespace—you don’t have to worry about your variable or function names conflicting with those of the website author, nor can a website author call functions in your extension. In other words, injected scripts and scripts included in the webpage run in isolated worlds, with no access to each other’s functions or data.

So how does one interactive with the website's namespace?

Unfortunately, there is no way. As you spotted in the docs, this is by design.

I suggest to inspect the DOM of Fastmail when switching themes to see what it entails. Maybe it is just a matter of adding a class to the <html> or <body> tag, which you could do in your injected JS.

Sorry I can't give you any better news.

Oh no! Is there any other option, or avenue?

I tried injecting a <script> tag into the body with content, but most sites now have a Content-Security-Policy without unsafe-inline.

Is this why dotjs used https?

Very late to this topic, but just wanted to say thank you @sj26 for mentioning the <script> tag injection strategy 🙌 This worked for me in overriding some pesky Instagram scaling behaviour – but sorry that the CSP got in the way of your FastMail edit.

Just for reference, this is my PageExtender instagram.com.js (for avoiding layout changes while pinch zooming):

let innerSizeOverride = function() {
  Object.defineProperty(window, 'innerWidth', {
    get: function() { return window.outerWidth; }
  });
  Object.defineProperty(window, 'innerHeight', {
    get: function() { return window.outerHeight; }
  });
};

let script = document.createElement('script');
script.type = 'text/javascript';
script.textContent = `(${innerSizeOverride.toString()})();`;
document.head.appendChild(script);