1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // See https://developers.google.com/maps/documentation/business/geolocation/
7 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
8 Components.utils.import("resource://gre/modules/Services.jsm");
10 const Ci = Components.interfaces;
11 const Cc = Components.classes;
13 let gLoggingEnabled = false;
14 let gTestingEnabled = false;
15 let gUseScanning = true;
20 aMsg = "*** WIFI GEO: " + aMsg + "\n";
21 Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(aMsg);
26 function WifiGeoCoordsObject(lat, lon, acc, alt, altacc) {
31 this.altitudeAccuracy = altacc;
34 WifiGeoCoordsObject.prototype = {
36 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGeoPositionCoords]),
38 classInfo: XPCOMUtils.generateCI({interfaces: [Ci.nsIDOMGeoPositionCoords],
39 flags: Ci.nsIClassInfo.DOM_OBJECT,
40 classDescription: "wifi geo position coords object"}),
43 function WifiGeoPositionObject(lat, lng, acc) {
44 this.coords = new WifiGeoCoordsObject(lat, lng, acc, 0, 0);
46 this.timestamp = Date.now();
49 WifiGeoPositionObject.prototype = {
51 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGeoPosition]),
53 // Class Info is required to be able to pass objects back into the DOM.
54 classInfo: XPCOMUtils.generateCI({interfaces: [Ci.nsIDOMGeoPosition],
55 flags: Ci.nsIClassInfo.DOM_OBJECT,
56 classDescription: "wifi geo location position object"}),
59 function WifiGeoPositionProvider() {
61 gLoggingEnabled = Services.prefs.getBoolPref("geo.wifi.logging.enabled");
65 gTestingEnabled = Services.prefs.getBoolPref("geo.wifi.testing");
69 gUseScanning = Services.prefs.getBoolPref("geo.wifi.scan");
72 this.wifiService = null;
74 this.hasSeenWiFi = false;
76 // this is only used when logging is enabled, to debug interactions with the
77 // geolocation service
78 this.highAccuracy = false;
81 WifiGeoPositionProvider.prototype = {
82 classID: Components.ID("{77DA64D3-7458-4920-9491-86CC9914F904}"),
83 QueryInterface: XPCOMUtils.generateQI([Ci.nsIGeolocationProvider,
85 Ci.nsITimerCallback]),
90 this.hasSeenWiFi = false;
92 LOG("startup called. testing mode is" + gTestingEnabled);
94 // if we don't see anything in 5 seconds, kick of one IP geo lookup.
95 // if we are testing, just hammer this callback so that we are more or less
96 // always sending data. It doesn't matter if we have an access point or not.
97 this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
99 this.timer.initWithCallback(this, 5000, this.timer.TYPE_ONE_SHOT);
101 this.timer.initWithCallback(this, 200, this.timer.TYPE_REPEATING_SLACK);
107 if (!this.wifiService && gUseScanning) {
108 this.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService(Components.interfaces.nsIWifiMonitor);
109 this.wifiService.startWatching(this);
111 if (this.hasSeenWiFi) {
112 this.hasSeenWiFi = false;
114 this.wifiService.stopWatching(this);
115 this.wifiService.startWatching(this);
117 // For testing situations, ensure that we always trigger an update.
118 this.timer.initWithCallback(this, 5000, this.timer.TYPE_ONE_SHOT);
123 shutdown: function() {
124 LOG("shutdown called");
125 if(this.wifiService) {
126 this.wifiService.stopWatching(this);
127 this.wifiService = null;
129 if (this.timer != null) {
134 this.started = false;
137 setHighAccuracy: function(enable) {
138 this.highAccuracy = enable;
139 LOG("setting highAccuracy to " + (this.highAccuracy?"TRUE":"FALSE"));
142 onChange: function(accessPoints) {
143 LOG("onChange called, highAccuracy = " + (this.highAccuracy?"TRUE":"FALSE"));
144 this.hasSeenWiFi = true;
146 let url = Services.urlFormatter.formatURLPref("geo.wifi.uri");
148 function isPublic(ap) {
150 let result = ap.ssid.indexOf(mask, ap.ssid.length - mask.length) == -1;
152 LOG("Filtering out " + ap.ssid);
157 function sort(a, b) {
158 return b.signal - a.signal;
161 function encode(ap) {
162 return { 'macAddress': ap.mac, 'signalStrength': ap.signal };
167 data = JSON.stringify({wifiAccessPoints: accessPoints.filter(isPublic).sort(sort).map(encode)})
170 LOG("************************************* Sending request:\n" + url + "\n");
172 // send our request to a wifi geolocation network provider:
173 let xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
174 .createInstance(Ci.nsIXMLHttpRequest);
176 // This is a background load
178 xhr.open("POST", url, true);
179 xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
180 xhr.responseType = "json";
181 xhr.mozBackgroundRequest = true;
182 xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS;
183 xhr.onerror = function() {
184 LOG("onerror: " + xhr);
187 xhr.onload = function() {
188 LOG("gls returned status: " + xhr.status + " --> " + JSON.stringify(xhr.response));
189 if (xhr.channel instanceof Ci.nsIHttpChannel && xhr.status != 200) {
193 if (!xhr.response || !xhr.response.location) {
197 let newLocation = new WifiGeoPositionObject(xhr.response.location.lat,
198 xhr.response.location.lng,
199 xhr.response.accuracy);
201 Cc["@mozilla.org/geolocation/service;1"].getService(Ci.nsIGeolocationUpdate)
202 .update(newLocation);
205 LOG("************************************* ------>>>> sending " + data);
209 onError: function (code) {
210 LOG("wifi error: " + code);
213 notify: function (timer) {
214 if (gTestingEnabled || !gUseScanning) {
215 // if we are testing, timer is repeating
219 if (!this.hasSeenWiFi)
226 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WifiGeoPositionProvider]);