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