Working with Safari Reader

Reader is a Safari feature that allows users to read online articles in a continuous, clutter-free view, with no ads or visual distractions. Reader concatenates multipage articles into a single scrolling pane. In Safari 5.1 and later, there are events that fire when Reader is available, activated, or deactivated. In your extension, you can enter and exit Reader programmatically, inspect properties on the Reader object, and inject scripts and style sheets.

Reader Events

Safari generates the following Reader events on the relevant tab:

You can listen for these events on the tab itself, or on the window or app. Only the "available" event bubbles, however, so either listen on the tab or set the capture parameter to true when installing your listener functions. For example:

safari.application.addEventListener("activate", activeHandler, true);

tab.addEventListener("available", availHandler, true);

tab.addEventListener("deactivate", deactivateHandler, false);

In your "available" event handler, you should test the event target to make sure that it is a Reader instance that has become available, and not something else. See Listing 13-1:

Listing 13-1  Reader “available” handler

function availHandle(event) {
    if (event.target instanceof SafariReader) {
        myReader = event.target;
        myReader.enter();
    }
}

Reader Methods and Properties

The Reader object is an instance of the SafariReader class. You can invoke the enter() and exit() methods to create or dismiss a Reader view.

If a SafariReader instance is available, you can obtain it by getting the reader property of the tab that received the "available" event. For example:

theReader = myTab.reader;

Because the event target of a Reader "available" event is a tab, you can also obtain a Reader instance from the event target, as shown in Listing 13-1.

Once you have an instance of Reader, you can open a Reader view by calling theReader.enter();.

To close the Reader view, call theReader.exit();.

You can also inspect properties of a Reader instance, specifically available, visible, and tab. The available property generates an event when it becomes true, and the visible property generates "activate" and "deactivate" events when it changes, so there is normally no need to poll these properties.

The tab property contains the tab the Reader instance is available or active within.

For more information, see SafariReader Class Reference.

Injecting Style Sheets and Scripts into Reader

In Safari 6.0 and later, you can automatically inject style sheets and scripts into Reader by specifying the safari-reader scheme in the whitelist and blacklist in Extension Builder. See “Whitelists and Blacklists” for more information.

If you need to dynamically add a new style sheet or script, you can use the addContentStyleSheet and addContentScript APIs.

Injecting into Reader Dynamically

To inject into Reader dynamically, you can call addContentStyleSheetFromURL and addContentScriptFromURL on the SafariExtension object using the whitelist and blacklist to limit the scope of the style sheet or script to only Reader pages.

safari.extension.addContentStyleSheetFromURL("pathToYourStylesheet", ["safari-reader://*/*"]);

See “Whitelists and Blacklists” to see how to work with whitelists and blacklists.

Similarly, to remove a style sheet or script from a Reader page, you can call removeContentStyleSheet or removeContentScript on the SafariExtension object:

safari.extension.removeContentStyleSheet("pathToYourStylesheet");

Adding or removing style sheets applies to all pages immediately. Adding or removing a script, however, only applies to pages opened or reloaded after that point.

Sending Messages to Scripts Injected into Reader

Like your style sheets, your scripts are injected into every webpage to which your extension has access. When dealing with injected scripts in Reader pages, the only difference is that when you want to message an injected script from, for example, the global HTML page, you must use the dispatchMessage method on the Reader object.

// Example of dispatching a message to a Content Script injected into Reader.
    var browserWindows = safari.application.browserWindows;
    for (var i = 0; i < browserWindows.length; ++i) {
        var tabs = browserWindows[i].tabs;
        for (var j = 0; j < tabs.length; ++j) {
            var tab = tabs[j];
            if (!tab.reader.visible)
                continue;
            tab.reader.dispatchMessage("Message to the Content Scripts injected into Reader");