Saving Safari's State

I've finally had it with Safari's inability to properly save its session data, even when exiting cleanly1. It's really frustrating to close the program will 20 windows open, then reopen it, choose History > Reopen All Windows from Last Session, and be greeted with only 8 properly restored windows and 12 'Untitled N' windows. So, here's my barebones first draft solution:

First, at the core is a simple applescript ("grabSafariState.scpt") which queries Safari to get the vital data2:

if appIsRunning("Safari") then
    tell application "Safari" to get URL of every document
end if

on appIsRunning(appName)
    tell application "System Events" to (name of processes) contains appName
end appIsRunning

Note that it checks whether Safari is running first to avoid starting it pointlessly. The applescript is run by a short bash script:

#!/bin/bash
 URLS=`osascript ~/Scripts/grabSafariState.scpt`
 if [ "$URLS" != "" ]
 then
 echo "$URLS" | sed 's|, |
 |g' > ~/Library/Safari/RealLastSession.txt
 fi

The shell script handles the details of correcting the comma separated list to be newline delimited and writes it to a file. The file is only overwritten if the applescript gave back a non-empty list, so as to avoid erasing the stored data on the run after the user closes Safari.

Finally, we politely ask launchd to run the shell script for us at regular intervals3 with a plist file, place in ~/Library/LaunchAgents/:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/YOUR_USER_NAME/Scripts/recordSafariState.sh</string>
    </array>
    <key>Label</key>
    <string>RecordSafariState</string>
    <key>StartInterval</key>
    <integer>60</integer>
</dict>
</plist>

So, the end result that I have a record of what I had open in Safari that is not more than a minute old. For now in order to use it I'll have to manually reopen each URL in the file, and I'll have to be careful to open the record file before reopening Safari, but it's a good start. I may see about extending the shell script to keep two versions of the file so that it has the newest, just recorded state, and the state prior to the last failed update (which would be the last recorded state of the previous Safari session). It's also possible that i could get fancy and generate an actual XML plist file in the format used by Safari itself, so that all I would have to do would be to replace LastSession.plist with RealLastSession.plist and then be able to have Safari restore all of the windows exactly and automatically for me. This would, however, require more work to collect more window data, and require handling replacing the official session with the good one at the right time.


  1. I might consider excusing it if it failed only when the browser crashed, although that would still really ruin the usefulness of the feature. 

  2. Note that according to Ash Searle, from whom I got the URL grabbing expression, this will only yield the URL of a single tab per Safari window. I don't use tabs, so this is perfect for me, but it would have to be corrected for someone who would want to record data from multiple tabs. 

  3. I chose once per minute, which is given by the StartInterval item (which is in units of seconds). 

No Comments

Comment on this post