StopTheScript

October 5 2021 by Jeff Johnson

StopTheScript is my new Safari extension for iOS 15 and iPadOS 15 that stops all JavaScript on your selected websites! It comes just two weeks after I brought my Safari extensions StopTheMadness and Tweaks for Twitter to iOS and iPadOS. StopTheScript is the only Safari extension in existence now that stops inline JavaScript on the web page as well as externally loaded JavaScript. Did you know that Safari content blockers can't actually block inline JavaScript? There's a demo you can test the StopTheScript website.

There are two types of Safari extension for iOS: Safari web extensions and Safari content blockers. StopTheScript is a Safari web extension, support for which was introduced in iOS 15. Safari web extensions, based on the cross-platform WebExtensions API shared by Firefox, Google Chrome, and Safari, are written with the standard web technologies JavaScript, HTML, and CSS. On the other hand, Safari content blockers are an Apple-exclusive technology that has already existed on iOS for several years. A content blocker creates a list of rules, defined in JSON, which Safari uses to block content in web pages. Each rule has a trigger, which may include a url-filter and a resource-type, such as script for JavaScript. And each rule has an action, such as block. Technically, these rules don't even disable JavaScript on a web page! What the rules do is block a web page from loading JavaScript files. If a JavaScript file can't be loaded, then of course its code can't be run. But if the JavaScript code is already inline on the HTML page, then it can't be blocked by the rules. (The rules can block an entire HTML page from loading, as if your internet were disconnected, but the rules can't allow an HTML page to load in Safari while disabling inline JavaScript on the page.)

In HTML, JavaScript comes from a <script> element. Externally loaded JavaScript has a src attribute specifying the URL of the JavaScript file:

<script src="https://example.com/myscript.js"></script>

Whereas inline JavaScript is code contained within the element:

<script>alert("Hello World!");</script>

Safari content blockers can only block the first type of script. StopTheScript blocks both.

If you install StopTheScript, you may find it strange that the extension has no settings. This is a technical limitation. Why? When a Safari extension is loaded into a web page, the extension's settings are not available immediately; any settings must be fetched from storage, an async operation. The problem is that the <head> of an HTML document may contain <script> elements. If StopTheScript had to wait for settings to be fetched from storage, then it might be too late to stop the JavaScript from running already! Thus, StopTheScript has to do its "magic" at the very beginning of the document load, even before elements of the <head> are loaded. This is why StopTheScript relies on Safari's own extension permission system. Safari users can grant an extension access to specific websites, and only those websites. In this way, privacy and security are protected by giving extensions only the permissions they need, rather than granting every extension access to every web page. StopTheScript takes advantage of this permissions system: StopTheScript stops JavaScript on the sites where it has access, but not on the sites where it doesn't have access, and site access is all controlled by the user! Indeed, StopTheScript would not even be possible without this permissions system.

Will StopTheScript be coming to Safari on the Mac too? I hope so, in the future, but right now it can't, unfortunately. The irony is that StopTheScript was written for the Mac. The extension been complete for over a year, but I couldn't ship it for the Mac, and I still can't. I've just been sitting on it, waiting for the opportunity, which presented itself with iOS 15. Technically I could ship the extension on the Mac, but it wouldn't work right. It wouldn't do what I promised. The problem is that "run_at":"document_start" doesn't work right in Safari on macOS, a bug that I blogged about over a year ago. At the time, I couldn't say why exactly I needed document_start, but now I can: for StopTheScript! Strangely, this bug does not affect Safari on iOS, which is why I was able to release the extension on iOS.

There is a Safari extension bug that affects both macOS and iOS: Safari's default preference "Preload Top Hit" breaks "run_at":"document_start". I also blogged about this bug before, earlier this year. Fortunately, there's a workaround for this bug: just change Safari's preference! The installation instructions for StopTheScript strongly recommend that you disable Preload Top Hit (as do the installation instructions for StopTheMadness). Even if you don't install my Safari extensions, you should still disable Preload Top Hit, because, frankly, it sucks. Preload Top Hit is a nightmare not only for Safari extensions but also for your own privacy.

Now you know why you want to disable Preload Top Hit, but why would you want to disable JavaScript? Some people claim that the web doesn't work anymore without JavaScript. To some extent that's true, but it's an exaggeration. In many cases, the web works better without JavaScript. Do you enjoy cookie consent requests, mailing lists signups, and offers to install a site's App Store app? If so, then StopTheScript is probably not for you. But if you don't enjoy them, then you might consider StopTheScript. In general, arbitrary JavaScript loaded in a web page from anywhere is a potential privacy and security threat, so the less JavaScript you have to run, the better. You may not be able to disable JavaScript entirely in Safari, but StopTheScript will help you disable JavaScript selectively.

Jeff Johnson (My apps, PayPal.Me)