1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/trace",
  5     "firebug/lib/object",
  6     "firebug/chrome/privacy",
  7 ],
  8 function(FBTrace, Obj, Privacy) {
  9 
 10 // ********************************************************************************************* //
 11 // Constants
 12 
 13 const Cc = Components.classes;
 14 const Ci = Components.interfaces;
 15 
 16 var dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
 17 
 18 // ********************************************************************************************* //
 19 // Annotation
 20 
 21 /**
 22  * @class Represents an internal Firebug annotation service. This service is used to
 23  * annotate sites with an info whether Firebug should be activated for them or not.
 24  */
 25 var Annotations = Obj.extend(Firebug.Module,
 26 {
 27     annotations: [],
 28 
 29     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 30 
 31     initialize: function()
 32     {
 33         // Get annotation file stored within the profile directory.
 34         this.file = dirService.get("ProfD", Ci.nsIFile);
 35         this.file.append("firebug");
 36         this.file.append("annotations.json");
 37 
 38         // Load annotations.
 39         this.loadAnnotations();
 40 
 41         Privacy.addListener(this);
 42     },
 43 
 44     shutdown: function()
 45     {
 46         this.flush();
 47 
 48         Privacy.removeListener(this);
 49     },
 50 
 51     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 52     // Public Methods
 53 
 54     setPageAnnotation: function(uri, value)
 55     {
 56         if (FBTrace.DBG_ANNOTATIONS)
 57             FBTrace.sysout("Annotations.setPageAnnotation; " + value + ", " + uri.spec);
 58 
 59         this.annotations[uri.spec] = value;
 60     },
 61 
 62     getPageAnnotation: function(uri)
 63     {
 64         return this.annotations[uri.spec];
 65     },
 66 
 67     pageHasAnnotation: function(uri)
 68     {
 69         return this.annotations[uri.spec] ? true : false;
 70     },
 71 
 72     removePageAnnotation: function(uri)
 73     {
 74         if (FBTrace.DBG_ANNOTATIONS)
 75             FBTrace.sysout("Annotations.removePageAnnotation; " + uri.spec);
 76 
 77         delete this.annotations[uri.spec];
 78     },
 79 
 80     getAnnotations: function()
 81     {
 82         return this.annotations;
 83     },
 84 
 85     clear: function()
 86     {
 87         this.annotations = [];
 88     },
 89 
 90     flush: function(force)
 91     {
 92         // Do not store anything if private-browsing mode is on.
 93         if (!force && Privacy.isPrivateBrowsing())
 94             return;
 95 
 96         try
 97         {
 98             // Initialize output stream.
 99             var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
100                 .createInstance(Ci.nsIFileOutputStream);
101             // write, create, truncate
102             // see https://developer.mozilla.org/en-US/docs/PR_Open#Parameters
103             outputStream.init(this.file, 0x02 | 0x08 | 0x20, 0666, 0);
104 
105             // Convert data to JSON.
106             var arr = [];
107             for (var uri in this.annotations)
108             {
109                 arr.push({
110                     uri: uri,
111                     value: this.annotations[uri]
112                 });
113             }
114 
115             var jsonString = JSON.stringify(arr);
116 
117             // Store annotations
118             outputStream.write(jsonString, jsonString.length);
119             outputStream.close();
120 
121             if (FBTrace.DBG_ANNOTATIONS)
122                 FBTrace.sysout("Annotations.loadAnnotations; Annotations stored to " +
123                     this.file.path, jsonString);
124         }
125         catch (err)
126         {
127             if (FBTrace.DBG_ERRORS || FBTrace.DBG_ANNOTATIONS)
128                 FBTrace.sysout("Annotations.flush; EXCEPTION", err);
129         }
130     },
131 
132     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
133     // Internals
134 
135     // Persistence
136     loadAnnotations: function()
137     {
138         try
139         {
140             this.clear();
141 
142             if (!this.file.exists())
143             {
144                 this.file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
145                 if (FBTrace.DBG_ANNOTATIONS)
146                     FBTrace.sysout("Annotations.loadAnnotations; Annotations file created " +
147                         this.file.path);
148                 return;
149             }
150 
151             var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
152                 .createInstance(Ci.nsIFileInputStream);
153             var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
154                 .createInstance(Ci.nsIConverterInputStream);
155 
156             // loadAnnotations input stream
157             // read, create
158             inputStream.init(this.file, 0x01 | 0x08, 0666, 0);
159             cstream.init(inputStream, "UTF-8", 0, 0);
160 
161             // Load annotations
162             var json = "";
163             var data = {};
164             while (cstream.readString(-1, data) != 0)
165                 json += data.value;
166 
167             if (!json.length)
168                 return;
169 
170             var arr = JSON.parse(json);
171             if (!arr)
172                 return;
173 
174             // convert to map for faster lookup
175             for (var i=0; i<arr.length; i++)
176                 this.annotations[arr[i].uri] = arr[i].value;
177 
178             if (FBTrace.DBG_ANNOTATIONS)
179                 FBTrace.sysout("Annotations.loadAnnotations; Annotations loaded from " +
180                     this.file.path, arr);
181         }
182         catch (err)
183         {
184             if (FBTrace.DBG_ERRORS || FBTrace.DBG_ANNOTATIONS)
185                 FBTrace.sysout("Annotations.loadAnnotations; EXCEPTION", err);
186         }
187     },
188 
189     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
190     // Privacy
191 
192     onPrivateBrowsingChange: function(enabled)
193     {
194         if (enabled)
195         {
196             this.flush(true);
197             this.clear();
198         }
199         else
200         {
201             this.loadAnnotations();
202         }
203     },
204 
205     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
206     // Options
207 
208     resetAllOptions: function()
209     {
210         this.clear();
211         this.flush(true);
212     }
213 });
214 
215 // ********************************************************************************************* //
216 // Registration
217 
218 Firebug.Annotations = Annotations;
219 
220 Firebug.registerModule(Annotations);
221 
222 return Annotations;
223 
224 // ********************************************************************************************* //
225 });
226