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