blog dds

2017.11.17

How I Recovered my Firefox Tab Groups

When quit and restarted Firefox today I received an unwelcomed shock. All my tab groups, which I maintained using the Tab Groups by Quicksaver plugin, were gone! This happened because it upgraded to Firefox Quantum (57), whose API does not maintain backward compatibility with the one used by the plugin. Although I knew the plugin would one day stop working, I thought there would be some last-minute warning and chance to export the tab groups.

Frighteningly, I couldn't find a way to obtain the list of the pages I had opened in my tab groups and a web search didn't yield any useful ideas. Currently there is no plugin with similar functionality, let alone one that can import the Tab Group tabs. Installing an older Firefox version seemed an inelegant hassle, so I decided to recover the tab groups from the browser's saved session data. This is stored in a file named sessionstore.js, located in the Firefox profile folder. The file is updated every time you restart Firefox. Consequently, the method I'll outline works if you haven't restarted the new version, or if you have an old version of the file available on backup. (For me both were true.)

The file is stored in JSON format. As a first step I formatted it for human consumption with the following command.

python -m json.tool sessionstore.js

I then explored it by opening it in vim, changing the file type to obtain proper syntax coloring (set filetype=javascript), and navigating its structure with the [{ command. I then used the amazing jq tool and a few Unix utilities to extract the data I wanted into an HTML page.

I got the titles of the lost tab groups, which are stored in a extData.tabview-group entry, with the following command.

jq -j '.windows[0].extData."tabview-group" |
  fromjson |
  .[] |
  .id, "\t", .title, "\n"' sessionstore.js

This obtains from the JSON file the first entry of the windows array, and from it the extData.tabview-group entry. The fromjson operator is required, because the data is stored in JSON format as a string, and needs to be converted to JSON again for further processing. I thus got a list similar to the following.

3       READ
5       MSR Ethics
6       Main
7       dgsh
8       IEEE Software
9       Leadership
10      Bootcamp
11      MOOC
13      LI vs hacking
14      Software in science
19      SWEng
20      eGov

I then extract the titles and URLs of the tabs in each group with the following command.

jq -j '.windows[0].tabs[] |
  ( .extData."tabview-tab" | fromjson| .groupID), "\t",
  .entries[-1].title, "\t",
  .entries[-1].url, "\n"'  sessionstore.js

This gave me another list, similar to this.

19  SWeng students - Google Sheets  https://docs.google.com/spreadsheets/...
3   Why the internet of things ... | Technology | The Guardian  https://www.theguardian.com/...
10  Testing Flask Applications  http://flask.pocoo.org/docs/0.11/testing/

I then sorted the two and joined them with the Unix join command to produce a list of tabs and their group titles.

join -t$'\t' <(
  jq -j '.windows[0].extData."tabview-group" |
    fromjson |
    .[] |
    .id, "\t", .title, "\n"' sessionstore.js | sort) <(
  jq -j '.windows[0].tabs[] |
    ( .extData."tabview-tab" | fromjson| .groupID), "\t",
    .entries[-1].title, "\t",
    .entries[-1].url, "\n"' sessionstore.js | sort)

Although it is possible to perform a relation join within jq, I decided to use the Unix join command, because this approach was quicker and easier to debug.

Finally, I used awk to convert the result into an HTML file, which I could open in my browser.

join -t$'\t' <(
  jq -j '.windows[0].extData."tabview-group" |
    fromjson |
    .[] |
    .id, "\t", .title, "\n"' sessionstore.js | sort) <(
  jq -j '.windows[0].tabs[] |
    ( .extData."tabview-tab" | fromjson| .groupID), "\t",
    .entries[-1].title, "\t",
    .entries[-1].url, "\n"' sessionstore.js | sort) |
awk -F$'\t' '
  BEGIN {
    print "<html<head>"
    print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">"
    print "</head><body>\n"
  }
  $2 != title {
    title = $2
    print "<h1>" title "</h1>\n"
  }
  { print "<a href=\"" $4 "\">" $3 "</a><br>\n"}
  END { print "</body>\n" }' >tab-groups.html

Read and post comments, or share through   


Creative Commons License Last modified: Saturday, November 18, 2017 10:04 am
Unless otherwise expressly stated, all original material on this page created by Diomidis Spinellis is licensed under a Creative Commons Attribution-Share Alike 3.0 Greece License.