diff options
Diffstat (limited to 'toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm')
-rw-r--r-- | toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm b/toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm new file mode 100644 index 000000000..d23d5bc6e --- /dev/null +++ b/toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm @@ -0,0 +1,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); + }, +} |