This page documents how to perform custom actions with Firefox Sync via JavaScript.

All of the in this page must be executed in a chrome-privileged console. To access a chrome-privileged console, open an about page (like about:about) then open a Web Console via the Web Developer menu.

Tabs from Other Computers

This snippet shows how to load all tabs from other computers.

Components.utils.import("resource://services-sync/main.js");

// Obtain a reference to the main Firefox window.
let mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIWebNavigation)
                       .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                       .rootTreeItem
                       .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIDOMWindow);  

// Obtain a reference to Sync's Tabs "engine."
let tabsEngine = Weave.Service.engineManager.get("tabs");

// Iterate over each client having data.
for each (let client in tabsEngine.getAllClients()) {
  for each (let tab in client.tabs) {
    let url = tab.urlHistory[0];

    // Load the tab via the tabbed browser API.
    mainWindow.gBrowser.addTab(url);
  }
}

Partially corrupt a server

Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://services-sync/resource.js");

function deletePath(path) {
  let resource = new Resource(Weave.Service.storageURL + path);
  resource.setHeader("X-Confirm-Delete", "1");
  return resource.delete();
}

// Delete meta/global:
deletePath("meta/global");

// Delete keys:
deletePath("crypto/keys");

// Delete server:
deletePath("");

Corrupt a single engine on the server

let ENGINE = "bookmarks";
Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://services-sync/resource.js");
Components.utils.import("resource://services-sync/util.js");
let r = new Resource(Weave.Service.storageURL + "meta/global");
let g = r.get();
let envelope = JSON.parse(g);
let payload  = JSON.parse(envelope.payload);
payload.engines[ENGINE].syncID = Weave.Utils.makeGUID(); // Or any other GUID you like.
payload.engines[ENGINE].version = 0; // Or any other version number you like.
envelope.payload = JSON.stringify(payload);
r.put(JSON.stringify(envelope));

Generate new keys

// Clients always wipe the server when they generate new keys.
Components.utils.import("resource://services-sync/main.js");
Weave.Service._freshStart();

// If you want to do it without wiping the server (which will cause corruption!):
Weave.Service.generateNewSymmetricKeys();
// Change '1000' as appropriate.
Components.utils.import("resource://services-sync/engines.js");
Components.utils.import("resource://services-sync/engines/bookmarks.js");
let bme = Weave.Service.engineManager.get("bookmarks");

let ids = Object.keys(bme._store.getAllIDs());
for each (let id in ids) {
  let record = bme._store.createRecord(id, "bookmarks");
  let len = record.toString().length;
  if (len > 1000) {
    console.log("ID: " + id + ", len = " + len + ", " + record.title);
  }
}
Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://services-sync/resource.js");

let ids = JSON.parse(new Resource(Weave.Service.storageURL + "bookmarks").get());
for each (let id in ids.sort()) {
  console.log("  " + id);
}

Get a count of the number of members of a collection on the server

let collection = "passwords";
Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://services-sync/resource.js");
JSON.parse(new Resource(Weave.Service.storageURL + collection).get()).length;

Dump the cleartext of each record in a collection to the console.

let collection = "forms";

Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://services-sync/record.js");

let recordType = Weave.Engines.get(collection)._recordObj;
let coll = new Collection(Weave.Service.storageURL + collection, recordType);
coll.full = true;
coll.recordHandler = function(item) {
  item.collection = collection;
  item.decrypt();
  console.log(item.cleartext);
};
coll.get();
let collection = "history";
let id = "GUID_GOES_HERE";

Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://services-sync/record.js");

let recordType = Weave.Engines.get(collection)._recordObj;
let coll = new Collection(Weave.Service.storageURL + collection, recordType);
coll.full = true;
coll.ids = [id];
coll.recordHandler = function(item) {
  item.collection = collection;
  item.decrypt();
  console.log(item.cleartext);
};

coll.get();

Count types of bookmark records

  Components.utils.import("resource://services-sync/main.js");
  Components.utils.import("resource://services-sync/record.js");

  let deleted = 0;
  let items   = {};

  let collection = "bookmarks";
  let recordType = Weave.Engines.get(collection)._recordObj;
  let coll = new Collection(Weave.Service.storageURL + collection, recordType);

  coll.full = true;
  coll.limit = null;

  coll.recordHandler = function(item) {
    item.collection = collection;
    item.decrypt();
    if (item.deleted) {
      deleted++;
    } else {
      items[item.type] = 1 + (items[item.type] || 0);
    }
  };
  coll.get();
  console.log("Deleted: " + deleted + ", " + JSON.stringify(items));

Get a log from XUL Fennec

Watch live Sync logs

Bump meta/global's modified time

Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://services-sync/resource.js");

function getPath(path) {
  let r = new Resource(Weave.Service.storageURL + path);
  let g = r.get();
  return [g, r];
};

let [g, r] = getPath("meta/global");
r.put(g);

Delete and restore a record

Components.utils.import("resource://services-sync/main.js");
Components.utils.import("resource://services-sync/resource.js");
Components.utils.import("resource://services-sync/record.js");

// For example:
let id = "iASOkUOZpIxZ"
let collection = "bookmarks";

let resource = new Resource(Weave.Service.storageURL + collection + "/" + id);
let del = new CryptoWrapper(collection, id);

del.deleted = true;
del.encrypt();

// Save the old value.
let old = resource.get();

// Delete.
resource.put(del);

// Restore the old value.
resource.put(old);