1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/PriorityUrlProvider.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,142 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = [ "PriorityUrlProvider" ]; 1.11 + 1.12 +const Ci = Components.interfaces; 1.13 +const Cc = Components.classes; 1.14 +const Cu = Components.utils; 1.15 +const Cr = Components.results; 1.16 + 1.17 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.18 +Cu.import("resource://gre/modules/Services.jsm"); 1.19 +Cu.import("resource://gre/modules/Promise.jsm"); 1.20 +Cu.import("resource://gre/modules/Task.jsm"); 1.21 + 1.22 + 1.23 +/** 1.24 + * Provides search engines matches to the PriorityUrlProvider through the 1.25 + * search engines definitions handled by the Search Service. 1.26 + */ 1.27 +const SEARCH_ENGINE_TOPIC = "browser-search-engine-modified"; 1.28 + 1.29 +let SearchEnginesProvider = { 1.30 + init: function () { 1.31 + this._engines = new Map(); 1.32 + let deferred = Promise.defer(); 1.33 + Services.search.init(rv => { 1.34 + if (Components.isSuccessCode(rv)) { 1.35 + Services.search.getVisibleEngines().forEach(this._addEngine, this); 1.36 + deferred.resolve(); 1.37 + } else { 1.38 + deferred.reject(new Error("Unable to initialize search service.")); 1.39 + } 1.40 + }); 1.41 + Services.obs.addObserver(this, SEARCH_ENGINE_TOPIC, true); 1.42 + return deferred.promise; 1.43 + }, 1.44 + 1.45 + observe: function (engine, topic, verb) { 1.46 + let engine = engine.QueryInterface(Ci.nsISearchEngine); 1.47 + switch (verb) { 1.48 + case "engine-added": 1.49 + this._addEngine(engine); 1.50 + break; 1.51 + case "engine-changed": 1.52 + if (engine.hidden) { 1.53 + this._removeEngine(engine); 1.54 + } else { 1.55 + this._addEngine(engine); 1.56 + } 1.57 + break; 1.58 + case "engine-removed": 1.59 + this._removeEngine(engine); 1.60 + break; 1.61 + } 1.62 + }, 1.63 + 1.64 + _addEngine: function (engine) { 1.65 + if (this._engines.has(engine.name)) { 1.66 + return; 1.67 + } 1.68 + let token = engine.getResultDomain(); 1.69 + if (!token) { 1.70 + return; 1.71 + } 1.72 + let match = { token: token, 1.73 + // TODO (bug 557665): searchForm should provide an usable 1.74 + // url with affiliate code, if available. 1.75 + url: engine.searchForm, 1.76 + title: engine.name, 1.77 + iconUrl: engine.iconURI ? engine.iconURI.spec : null, 1.78 + reason: "search" } 1.79 + this._engines.set(engine.name, match); 1.80 + PriorityUrlProvider.addMatch(match); 1.81 + }, 1.82 + 1.83 + _removeEngine: function (engine) { 1.84 + if (!this._engines.has(engine.name)) { 1.85 + return; 1.86 + } 1.87 + this._engines.delete(engine.name); 1.88 + PriorityUrlProvider.removeMatchByToken(engine.getResultDomain()); 1.89 + }, 1.90 + 1.91 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, 1.92 + Ci.nsISupportsWeakReference]) 1.93 +} 1.94 + 1.95 +/** 1.96 + * The PriorityUrlProvider allows to match a given string to a list of 1.97 + * urls that should have priority in url search components, like autocomplete. 1.98 + * Each returned match is an object with the following properties: 1.99 + * - token: string used to match the search term to the url 1.100 + * - url: url string represented by the match 1.101 + * - title: title describing the match, or an empty string if not available 1.102 + * - iconUrl: url of the icon associated to the match, or null if not available 1.103 + * - reason: a string describing the origin of the match, for example if it 1.104 + * represents a search engine, it will be "search". 1.105 + */ 1.106 +let matches = new Map(); 1.107 + 1.108 +let initialized = false; 1.109 +function promiseInitialized() { 1.110 + if (initialized) { 1.111 + return Promise.resolve(); 1.112 + } 1.113 + return Task.spawn(function* () { 1.114 + try { 1.115 + yield SearchEnginesProvider.init(); 1.116 + } catch (ex) { 1.117 + Cu.reportError(ex); 1.118 + } 1.119 + initialized = true; 1.120 + }); 1.121 +} 1.122 + 1.123 +this.PriorityUrlProvider = Object.freeze({ 1.124 + addMatch: function (match) { 1.125 + matches.set(match.token, match); 1.126 + }, 1.127 + 1.128 + removeMatchByToken: function (token) { 1.129 + matches.delete(token); 1.130 + }, 1.131 + 1.132 + getMatch: function (searchToken) { 1.133 + return Task.spawn(function* () { 1.134 + yield promiseInitialized(); 1.135 + for (let [token, match] of matches.entries()) { 1.136 + // Match at the beginning for now. In future an aOptions argument may 1.137 + // allow to control the matching behavior. 1.138 + if (token.startsWith(searchToken)) { 1.139 + return match; 1.140 + } 1.141 + } 1.142 + return null; 1.143 + }.bind(this)); 1.144 + } 1.145 +});