DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (5350524bb654)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Provides functions to handle remote tabs (ie, tabs known by Sync) in
 * the awesomebar.
 */

"use strict";

this.EXPORTED_SYMBOLS = ["PlacesRemoteTabsAutocompleteProvider"];

const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyGetter(this, "weaveXPCService", function() {
  return Cc["@mozilla.org/weave/service;1"]
           .getService(Ci.nsISupports)
           .wrappedJSObject;
});

XPCOMUtils.defineLazyGetter(this, "Weave", () => {
  try {
    let {Weave} = Cu.import("resource://services-sync/main.js", {});
    return Weave;
  } catch (ex) {
    // The app didn't build Sync.
  }
  return null;
});

// from MDN...
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

// Build the in-memory structure we use.
function buildItems() {
  let clients = new Map(); // keyed by client guid, value is client
  let tabs = new Map(); // keyed by string URL, value is {clientId, tab}

  // If Sync isn't initialized (either due to lag at startup or due to no user
  // being signed in), don't reach in to Weave.Service as that may initialize
  // Sync unnecessarily - we'll get an observer notification later when it
  // becomes ready and has synced a list of tabs.
  if (weaveXPCService.ready) {
    let engine = Weave.Service.engineManager.get("tabs");

    for (let [guid, client] of Object.entries(engine.getAllClients())) {
      clients.set(guid, client);
      for (let tab of client.tabs) {
        let url = tab.urlHistory[0];
        tabs.set(url, { clientId: guid, tab });
      }
    }
  }
  return { clients, tabs };
}

// Manage the cache of the items we use.
// The cache itself.
let _items = null;

// Ensure the cache is good.
function ensureItems() {
  if (!_items) {
    _items = buildItems();
  }
  return _items;
}

// A preference used to disable the showing of icons in remote tab records.
const PREF_SHOW_REMOTE_ICONS = "services.sync.syncedTabs.showRemoteIcons";
let showRemoteIcons;

// An observer to invalidate _items and watch for changed prefs.
function observe(subject, topic, data) {
  switch (topic) {
    case "weave:engine:sync:finish":
      if (data == "tabs") {
        // The tabs engine just finished syncing, so may have a different list
        // of tabs then we previously cached.
        _items = null;
      }
      break;

    case "weave:service:start-over":
      // Sync is being reset due to the user disconnecting - we must invalidate
      // the cache so we don't supply tabs from a different user.
      _items = null;
      break;

    case "nsPref:changed":
      if (data == PREF_SHOW_REMOTE_ICONS) {
        try {
          showRemoteIcons = Services.prefs.getBoolPref(PREF_SHOW_REMOTE_ICONS);
        } catch (_) {
          showRemoteIcons = true; // no such pref - default is to show the icons.
        }
      }
      break;

    default:
      break;
  }
}

Services.obs.addObserver(observe, "weave:engine:sync:finish", false);
Services.obs.addObserver(observe, "weave:service:start-over", false);

// Observe the pref for showing remote icons and prime our bool that reflects its value.
Services.prefs.addObserver(PREF_SHOW_REMOTE_ICONS, observe, false);
observe(null, "nsPref:changed", PREF_SHOW_REMOTE_ICONS);

// This public object is a static singleton.
this.PlacesRemoteTabsAutocompleteProvider = {
  // a promise that resolves with an array of matching remote tabs.
  getMatches(searchString) {
    // If Sync isn't configured we bail early.
    if (Weave === null ||
        !Services.prefs.prefHasUserValue("services.sync.username")) {
      return Promise.resolve([]);
    }

    let re = new RegExp(escapeRegExp(searchString), "i");
    let matches = [];
    let { tabs, clients } = ensureItems();
    for (let [url, { clientId, tab }] of tabs) {
      let title = tab.title;
      if (url.match(re) || (title && title.match(re))) {
        // lookup the client record.
        let client = clients.get(clientId);
        let icon = showRemoteIcons ? tab.icon : null;
        // create the record we return for auto-complete.
        let record = {
          url, title, icon,
          deviceClass: Weave.Service.clientsEngine.isMobile(clientId) ? "mobile" : "desktop",
          deviceName: client.clientName,
        };
        matches.push(record);
      }
    }
    return Promise.resolve(matches);
  },
}