1 /* See license.txt for terms of usage */ 2 3 // ************************************************************************************************ 4 // Constants 5 6 const CLASS_ID = Components.ID("{2D92593E-14D0-48ce-B260-A9881BBF9C8B}"); 7 const CLASS_NAME = "Firebug HTTP Observer Service"; 8 const CONTRACT_ID = "@joehewitt.com/firebug-http-observer;1"; 9 10 const Cc = Components.classes; 11 const Ci = Components.interfaces; 12 const Cr = Components.results; 13 14 var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); 15 var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager); 16 17 // ************************************************************************************************ 18 // HTTP Request Observer implementation 19 20 var FBTrace = null; 21 var fbs = null; 22 23 /** 24 * @service This service is intended as the only HTTP observer registered by Firebug. 25 * All FB extensions and Firebug itself should register a listener within this 26 * service in order to listen for http-on-modify-request, http-on-examine-response and 27 * http-on-examine-cached-response events. 28 * 29 * See also: <a href="http://developer.mozilla.org/en/Setting_HTTP_request_headers"> 30 * Setting_HTTP_request_headers</a> 31 */ 32 function HttpRequestObserver() 33 { 34 this.observers = []; 35 this.isObserving = false; 36 37 // Get firebug-trace service for logging (the service should be already 38 // registered at this moment). 39 FBTrace = Cc["@joehewitt.com/firebug-trace-service;1"] 40 .getService(Ci.nsISupports).wrappedJSObject.getTracer("extensions.firebug"); 41 42 // Get firebug-service to listen for suspendFirebug and resumeFirebug events. 43 fbs = Cc["@joehewitt.com/firebug;1"].getService(Ci.nsISupports).wrappedJSObject; 44 45 this.initialize(); 46 } 47 48 /* nsIFireBugClient */ 49 var FirebugClient = 50 { 51 disableXULWindow: function() 52 { 53 if (gHttpObserverSingleton) 54 gHttpObserverSingleton.unregisterObservers(); 55 }, 56 57 enableXULWindow: function() 58 { 59 if (gHttpObserverSingleton) 60 gHttpObserverSingleton.registerObservers(); 61 } 62 } 63 64 HttpRequestObserver.prototype = 65 /** lends HttpRequestObserver */ 66 { 67 initialize: function() 68 { 69 fbs.registerClient(FirebugClient); 70 71 observerService.addObserver(this, "quit-application", false); 72 73 this.registerObservers(); 74 75 if (FBTrace.DBG_HTTPOBSERVER) 76 FBTrace.sysout("httpObserver.initialize OK"); 77 }, 78 79 shutdown: function() 80 { 81 fbs.unregisterClient(FirebugClient); 82 83 observerService.removeObserver(this, "quit-application"); 84 85 if (FBTrace.DBG_HTTPOBSERVER) 86 FBTrace.sysout("httpObserver.shutdown OK"); 87 }, 88 89 registerObservers: function() 90 { 91 if (FBTrace.DBG_HTTPOBSERVER) 92 FBTrace.sysout("httpObserver.registerObservers; wasObserving: " + 93 this.isObserving); 94 95 if (this.isObserving) 96 return; 97 98 observerService.addObserver(this, "http-on-modify-request", false); 99 observerService.addObserver(this, "http-on-examine-response", false); 100 observerService.addObserver(this, "http-on-examine-cached-response", false); 101 102 this.isObserving = true; 103 }, 104 105 unregisterObservers: function() 106 { 107 if (FBTrace.DBG_HTTPOBSERVER) 108 FBTrace.sysout("httpObserver.unregisterObservers; wasObserving: " + 109 this.isObserving); 110 111 if (!this.isObserving) 112 return; 113 114 observerService.removeObserver(this, "http-on-modify-request"); 115 observerService.removeObserver(this, "http-on-examine-response"); 116 observerService.removeObserver(this, "http-on-examine-cached-response"); 117 118 this.isObserving = false; 119 }, 120 121 /* nsIObserve */ 122 observe: function(subject, topic, data) 123 { 124 if (topic == "quit-application") { 125 this.shutdown(); 126 return; 127 } 128 129 try 130 { 131 if (FBTrace.DBG_HTTPOBSERVER) 132 { 133 FBTrace.sysout("httpObserver.observe " + (topic ? topic.toUpperCase() : topic) + 134 ", " + ((subject instanceof Ci.nsIRequest) ? safeGetName(subject) : "")); 135 } 136 137 // Notify all registered observers. 138 if (topic == "http-on-modify-request" || 139 topic == "http-on-examine-response" || 140 topic == "http-on-examine-cached-response") 141 { 142 this.notifyObservers(subject, topic, data); 143 } 144 } 145 catch (err) 146 { 147 if (FBTrace.DBG_ERRORS) 148 FBTrace.sysout("httpObserver.observe EXCEPTION", err); 149 } 150 }, 151 152 /* nsIObserverService */ 153 addObserver: function(observer, topic, weak) 154 { 155 if (topic != "firebug-http-event") 156 throw Cr.NS_ERROR_INVALID_ARG; 157 158 this.observers.push(observer); 159 }, 160 161 removeObserver: function(observer, topic) 162 { 163 if (topic != "firebug-http-event") 164 throw Cr.NS_ERROR_INVALID_ARG; 165 166 for (var i=0; i<this.observers.length; i++) { 167 if (this.observers[i] == observer) { 168 this.observers.splice(i, 1); 169 return; 170 } 171 } 172 173 if (FBTrace.DBG_HTTPOBSERVER) 174 FBTrace.sysout("httpObserver.removeObserver FAILED (no such observer)"); 175 }, 176 177 notifyObservers: function(subject, topic, data) 178 { 179 if (FBTrace.DBG_HTTPOBSERVER) 180 FBTrace.sysout("httpObserver.notifyObservers (" + this.observers.length + ") " + topic); 181 182 for (var i=0; i<this.observers.length; i++) 183 this.observers[i].observe(subject, topic, data); 184 }, 185 186 enumerateObservers: function(topic) 187 { 188 return null; 189 }, 190 191 /* nsISupports */ 192 QueryInterface: function(iid) 193 { 194 if (iid.equals(Ci.nsISupports) || 195 iid.equals(Ci.nsIObserverService) || 196 iid.equals(Ci.nsIObserver)) { 197 return this; 198 } 199 200 throw Cr.NS_ERROR_NO_INTERFACE; 201 } 202 } 203 204 function safeGetName(request) 205 { 206 try 207 { 208 return request.name; 209 } 210 catch (exc) 211 { 212 return null; 213 } 214 } 215 216 // ************************************************************************************************ 217 // Service factory 218 219 var gHttpObserverSingleton = null; 220 var HttpRequestObserverFactory = 221 { 222 createInstance: function (outer, iid) 223 { 224 if (outer != null) 225 throw Cr.NS_ERROR_NO_AGGREGATION; 226 227 if (iid.equals(Ci.nsISupports) || 228 iid.equals(Ci.nsIObserverService) || 229 iid.equals(Ci.nsIObserver)) 230 { 231 if (!gHttpObserverSingleton) 232 gHttpObserverSingleton = new HttpRequestObserver(); 233 return gHttpObserverSingleton.QueryInterface(iid); 234 } 235 236 throw Cr.NS_ERROR_NO_INTERFACE; 237 }, 238 239 QueryInterface: function(iid) 240 { 241 if (iid.equals(Ci.nsISupports) || 242 iid.equals(Ci.nsISupportsWeakReference) || 243 iid.equals(Ci.nsIFactory)) 244 return this; 245 246 throw Cr.NS_ERROR_NO_INTERFACE; 247 } 248 }; 249 250 // ************************************************************************************************ 251 // Module implementation 252 253 var HttpRequestObserverModule = 254 { 255 registerSelf: function (compMgr, fileSpec, location, type) 256 { 257 compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar); 258 compMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, 259 CONTRACT_ID, fileSpec, location, type); 260 }, 261 262 unregisterSelf: function(compMgr, fileSpec, location) 263 { 264 compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar); 265 compMgr.unregisterFactoryLocation(CLASS_ID, location); 266 }, 267 268 getClassObject: function (compMgr, cid, iid) 269 { 270 if (!iid.equals(Ci.nsIFactory)) 271 throw Cr.NS_ERROR_NOT_IMPLEMENTED; 272 273 if (cid.equals(CLASS_ID)) 274 return HttpRequestObserverFactory; 275 276 throw Cr.NS_ERROR_NO_INTERFACE; 277 }, 278 279 canUnload: function(compMgr) 280 { 281 return true; 282 } 283 }; 284 285 // ************************************************************************************************ 286 287 function NSGetModule(compMgr, fileSpec) 288 { 289 return HttpRequestObserverModule; 290 } 291