1 /* See license.txt for terms of usage */
  2 
  3 FBL.ns(function() { with (FBL) {
  4 
  5 // ************************************************************************************************
  6 // Constants
  7 
  8 const Cc = Components.classes;
  9 const Ci = Components.interfaces;
 10 
 11 const detachCommand = $("cmd_toggleDetachFirebug");
 12 
 13 // ************************************************************************************************
 14 
 15 /**
 16  * @class Implements Firebug activation logic.
 17  *
 18  * 1) Part of the logic is based on annotation service (see components/firebug-annotations.js)
 19  *    in order to remember whether Firebug is activated for given site or not.
 20  *    If there is "firebugged.showFirebug" annotation for a given site Firbug is activated.
 21  *    If there is "firebugged.closed" annotation for a given site Firbug is not activated.
 22  *
 23  * 2) Other part is based on extensions.firebug.allPagesActivation option. This option
 24  *    can be set to the following values:
 25  *    none: The option isn't used (default value)
 26  *    on:   Firebug is activated for all URLs.
 27  *    off:  Firebug is never activated.
 28  *
 29  *    This logic has higher priority over the URL annotations.
 30  *    If "off" options is selected, all existing URL annotations are removed.
 31  */
 32 Firebug.Activation = extend(Firebug.Module,
 33 {
 34     dispatchName: "activation",
 35 
 36     initializeUI: function()  // called once
 37     {
 38         Firebug.Module.initializeUI.apply(this, arguments);
 39 
 40         TabWatcher.addListener(this.TabWatcherListener);
 41 
 42         // The "off" option is removed so make sure to convert previsous prev value
 43         // into "none" if necessary.
 44         if (Firebug.allPagesActivation == "off")
 45             Firebug.allPagesActivation = "none";
 46 
 47         // Update option menu item.
 48         this.updateAllPagesActivation();
 49     },
 50 
 51     getAnnotationService: function()
 52     {
 53         if(!this.annotationSvc)
 54         {
 55             // Create annotation service.
 56             this.annotationSvc = Cc["@joehewitt.com/firebug-annotation-service;1"]
 57                 .getService(Ci.nsISupports).wrappedJSObject;
 58         }
 59         return this.annotationSvc;
 60     },
 61 
 62     shutdown: function()
 63     {
 64         Firebug.Module.shutdown.apply(this, arguments);
 65 
 66         TabWatcher.removeListener(this.TabWatcherListener);
 67 
 68         this.getAnnotationService().flush();
 69     },
 70 
 71     convertToURIKey: function(url, sameOrigin)  // process the URL to canonicalize it. Need not be reversible.
 72     {
 73         var uri = makeURI(normalizeURL(url));
 74 
 75         if (Firebug.filterSystemURLs && isSystemURL(url))
 76             return uri;
 77 
 78         if (url == "about:blank")  // avoid exceptions.
 79             return uri;
 80 
 81         if (uri && sameOrigin)
 82         {
 83             try
 84             {
 85                 var prePath = uri.prePath; // returns the string before the path (such as "scheme://user:password@host:port").
 86                 var shortURI = makeURI(prePath);
 87                 if (!shortURI)
 88                     return uri;
 89 
 90                 var host = shortURI.host;
 91                 if (host)
 92                 {
 93                     var crossDomain = host.split('.').slice(-2)
 94                     shortURI.host = crossDomain.join('.');
 95                     return shortURI
 96                 }
 97             }
 98             catch (exc)
 99             {
100                 if (FBTrace.DBG_ERRORS)
101                     FBTrace.sysout("activation.convertToURIKey returning full URI, activateSameOrigin FAILS for shortURI "+shortURI+" because: "+exc, exc);
102                 return uri;
103             }
104         }
105         return uri;
106     },
107 
108     shouldCreateContext: function(browser, url, userCommands)  // true if the Places annotation the URI "firebugged"
109     {
110         if (FBTrace.DBG_ACTIVATION)
111             FBTrace.sysout("shouldCreateContext allPagesActivation " + Firebug.allPagesActivation);
112 
113         if (Firebug.allPagesActivation == "on")
114             return true;
115 
116         if (Firebug.filterSystemURLs && isSystemURL(url)) // if about:blank gets thru, 1483 fails
117             return false;
118 
119         if (userCommands)
120             return true;
121 
122         if (browser.showFirebug && url.substr(0, 8) === "wyciwyg:")  // document.open on a firebugged page
123             return true;
124 
125         try
126         {
127             var uri = this.convertToURIKey(url, Firebug.activateSameOrigin);
128             if (!uri)
129                 return false;
130 
131             var hasAnnotation = this.getAnnotationService().pageHasAnnotation(uri);
132 
133             if (FBTrace.DBG_ACTIVATION)
134                 FBTrace.sysout("shouldCreateContext hasAnnotation "+hasAnnotation +
135                     " for "+uri.spec+" in "+browser.contentWindow.location +
136                     " using activateSameOrigin: "+Firebug.activateSameOrigin);
137 
138             // Annotated so, return the value.
139             if (hasAnnotation)
140                 return this.checkAnnotation(browser, uri);
141 
142             if (browser.FirebugLink) // then TabWatcher found a connection
143             {
144                 var dst = browser.FirebugLink.dst;
145                 var dstURI = this.convertToURIKey(dst.spec, Firebug.activateSameOrigin);
146                 if (FBTrace.DBG_ACTIVATION)
147                     FBTrace.sysout("shouldCreateContext found FirebugLink pointing to " +
148                         dstURI.spec, browser.FirebugLink);
149 
150                 if (dstURI && dstURI.equals(uri)) // and it matches us now
151                 {
152                     var srcURI = this.convertToURIKey(browser.FirebugLink.src.spec, Firebug.activateSameOrigin);
153                     if (srcURI)
154                     {
155                         if (FBTrace.DBG_ACTIVATION)
156                             FBTrace.sysout("shouldCreateContext found FirebugLink pointing from " +
157                                 srcURI.spec, browser.FirebugLink);
158 
159                         if (srcURI.schemeIs("file") || (dstURI.host == srcURI.host) ) // and it's on the same domain
160                         {
161                             hasAnnotation = this.getAnnotationService().pageHasAnnotation(srcURI);
162                             if (hasAnnotation) // and the source page was annotated.
163                             {
164                                 var srcShow = this.checkAnnotation(browser, srcURI);
165                                 if (srcShow)  // and the source annotation said show it
166                                     this.watchBrowser(browser);  // so we show dst as well.
167                                 return srcShow;
168                             }
169                         }
170                     }
171                 }
172                 else
173                 {
174                     if (FBTrace.DBG_ACTIVATION)
175                         FBTrace.sysout("shouldCreateContext FirebugLink does not match "+uri.spec, browser.FirebugLink);
176                 }
177             }
178             else if (browser.contentWindow.opener)
179             {
180                 var openerContext = TabWatcher.getContextByWindow(browser.contentWindow.opener);
181 
182                 if (FBTrace.DBG_ACTIVATION)
183                     FBTrace.sysout("shouldCreateContext opener found, has "+
184                         (openerContext?"a ":"no ")+" context: "+
185                         browser.contentWindow.opener.location);
186 
187                 if (openerContext)
188                     return true;  // popup windows of Firebugged windows are Firebugged
189             }
190 
191             return false;   // don't createContext
192         }
193         catch (exc)
194         {
195             if (FBTrace.DBG_ERRORS)
196                 FBTrace.sysout("pageHasAnnoation FAILS for url: "+url+" which gave uri "+(uri?uri.spec:"null"), exc);
197         }
198     },
199 
200     checkAnnotation: function(browser, uri)
201     {
202         var annotation = this.getAnnotationService().getPageAnnotation(uri);
203 
204         if (FBTrace.DBG_ACTIVATION)
205             FBTrace.sysout("shouldCreateContext read back annotation "+annotation+" for uri "+uri.spec);
206 
207         // then the user closed Firebug on this page last time
208         if ((Firebug.allPagesActivation != "on") && (annotation.indexOf("closed") > 0))
209             return false; // annotated as 'closed', don't create
210         else
211             return true;    // annotated, createContext
212     },
213 
214     shouldShowContext: function(context)
215     {
216         return this.shouldCreateContext(context.browser, context.getWindowLocation().toString());
217     },
218 
219     watchBrowser: function(browser)  // Firebug is opened in browser
220     {
221         var annotation = "firebugged.showFirebug";
222         this.setPageAnnotation(browser.currentURI.spec, annotation);
223     },
224 
225     unwatchBrowser: function(browser, userCommands)  // Firebug closes in browser
226     {
227         var uri = browser.currentURI.spec;
228         if (userCommands)  // then mark to not open virally.
229             this.setPageAnnotation(uri, "firebugged.closed");
230         else
231             this.removePageAnnotation(uri); // unmark this URI
232     },
233 
234     clearAnnotations: function()
235     {
236         this.getAnnotationService().clear();
237     },
238 
239     setPageAnnotation: function(currentURI, annotation)
240     {
241         var uri = this.convertToURIKey(currentURI, Firebug.activateSameOrigin);
242         if (uri)
243             this.getAnnotationService().setPageAnnotation(uri, annotation);
244 
245         if (Firebug.activateSameOrigin)
246         {
247             uri = this.convertToURIKey(currentURI, false);
248             if (uri)
249                 this.getAnnotationService().setPageAnnotation(uri, annotation);
250         }
251     },
252 
253     removePageAnnotation: function(currentURI)
254     {
255         var uri = this.convertToURIKey(currentURI, Firebug.activateSameOrigin);
256         if (uri)
257             this.getAnnotationService().removePageAnnotation(uri);
258 
259         if (Firebug.activateSameOrigin)
260         {
261             uri = this.convertToURIKey(currentURI, false);
262             if (uri)
263                 this.getAnnotationService().removePageAnnotation(uri);
264         }
265 
266         if (FBTrace.DBG_ACTIVATION)
267             FBTrace.sysout("Firebug.Activation.unwatchBrowser untagged "+uri.spec);
268     },
269 
270     iterateAnnotations: function(fn)  // stops at the first fn(uri) that returns a true value
271     {
272         var annotations = this.getAnnotationService().getAnnotations(this.annotationName);
273         for (var uri in annotations)
274         {
275             var rc = fn(uri, annotations[uri]);
276             if (rc)
277                 return rc;
278         }
279     },
280 
281     toggleAll: function(state)
282     {
283         if (FBTrace.DBG_ACTIVATION)
284             FBTrace.sysout("Firebug.toggleAll("+state+") with allPagesActivation: " +
285                 Firebug.allPagesActivation);
286 
287         if (state == "on")
288         {
289             if (Firebug.allPagesActivation == state) // then we were armed
290                 Firebug.allPagesActivation = "none";
291             else
292                 this.allOn();
293 
294             // don't show Off button if we are always on
295             Firebug.chrome.disableOff(Firebug.allPagesActivation == "on");
296         }
297         else
298         {
299             Firebug.allPagesActivation = "none";
300         }
301 
302         Firebug.setPref(Firebug.prefDomain, "allPagesActivation", Firebug.allPagesActivation);
303         this.updateAllPagesActivation();
304     },
305 
306     updateOption: function(name, value)
307     {
308         if (name = "allPagesActivation")
309             this.updateAllPagesActivation();
310     },
311 
312     updateAllPagesActivation: function()
313     {
314         var menu = $('menu_AllOn');
315         if (menu)
316             menu.setAttribute("checked", (Firebug.allPagesActivation=="on"));
317     },
318 
319     allOn: function()
320     {
321         Firebug.allPagesActivation = "on";  // In future we always create contexts,
322         Firebug.toggleBar(true);  // and we turn on for the current page
323     }
324 });
325 
326 // ************************************************************************************************
327 
328 Firebug.Activation.TabWatcherListener =
329 {
330     watchBrowser: function(browser)
331     {
332         Firebug.Activation.watchBrowser(browser);
333     },
334 
335     unwatchBrowser: function(browser, userCommands)
336     {
337         Firebug.Activation.unwatchBrowser(browser, userCommands);
338     }
339 };
340 
341 // ************************************************************************************************
342 
343 Firebug.registerModule(Firebug.Activation);
344 
345 // ************************************************************************************************
346 }});
347