1 /* See license.txt for terms of usage */ 2 3 // ************************************************************************************************ 4 // Constants 5 6 const CLASS_ID = Components.ID("{9589DC0D-9709-4578-883E-D393452B3611}"); 7 const CLASS_NAME = "Firebug Annotation Service"; 8 const CONTRACT_ID = "@joehewitt.com/firebug-annotation-service;1"; 9 10 const Cc = Components.classes; 11 const Ci = Components.interfaces; 12 const Cr = Components.results; 13 14 const dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); 15 16 // ************************************************************************************************ 17 // Annotaion service implementation 18 19 var FBTrace = null; 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 function AnnotationService() 26 { 27 this.wrappedJSObject = this; 28 29 FBTrace = Cc["@joehewitt.com/firebug-trace-service;1"] 30 .getService(Ci.nsISupports).wrappedJSObject.getTracer("extensions.firebug"); 31 32 // Get annotation file stored within the profile directory. 33 this.file = dirService.get("ProfD", Ci.nsIFile); 34 this.file.append("firebug"); 35 this.file.append("annotations.json"); 36 37 // Load annotaions. 38 this.initialize(); 39 } 40 41 AnnotationService.prototype = 42 { 43 annotations: [], 44 45 setPageAnnotation: function(uri, value) 46 { 47 if (FBTrace.DBG_ANNOTATIONS) 48 FBTrace.sysout("AnnotationService.setPageAnnotation; " + value + ", " + uri.spec); 49 50 this.annotations[uri.spec] = value; 51 }, 52 53 getPageAnnotation: function(uri) 54 { 55 return this.annotations[uri.spec]; 56 }, 57 58 pageHasAnnotation: function(uri) 59 { 60 return this.annotations[uri.spec] ? true : false; 61 }, 62 63 removePageAnnotation: function(uri) 64 { 65 if (FBTrace.DBG_ANNOTATIONS) 66 FBTrace.sysout("AnnotationService.removePageAnnotation; " + uri.spec); 67 68 delete this.annotations[uri.spec]; 69 }, 70 71 getAnnotations: function() 72 { 73 return this.annotations; 74 }, 75 76 clear: function() 77 { 78 this.annotations = []; 79 }, 80 81 // Persistence 82 initialize: function() 83 { 84 try 85 { 86 this.clear(); 87 88 if (!this.file.exists()) 89 { 90 this.file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); 91 if (FBTrace.DBG_ANNOTATIONS) 92 FBTrace.sysout("AnnotationService.initialize; Annotaions file created " + 93 this.file.path); 94 return; 95 } 96 97 var inputStream = Cc["@mozilla.org/network/file-input-stream;1"] 98 .createInstance(Ci.nsIFileInputStream); 99 var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"] 100 .createInstance(Ci.nsIConverterInputStream); 101 102 // Initialize input stream. 103 inputStream.init(this.file, 0x01 | 0x08, 0666, 0); // read, create 104 cstream.init(inputStream, "UTF-8", 0, 0); 105 106 // Load annotations. 107 var data = {}; 108 cstream.readString(-1, data); 109 if (!data.value.length) 110 return; 111 112 var nativeJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); 113 var arr = nativeJSON.decode(data.value); 114 if (!arr) 115 return; 116 117 // Convert to map for faster lookup. 118 for (var i=0; i<arr.length; i++) 119 this.annotations[arr[i].uri] = arr[i].value; 120 121 if (FBTrace.DBG_ANNOTATIONS) 122 FBTrace.sysout("AnnotationService.initialize; Annotations loaded from " + 123 this.file.path, arr); 124 } 125 catch (err) 126 { 127 if (FBTrace.DBG_ERRORS || FBTrace.DBG_ANNOTATIONS) 128 FBTrace.sysout("AnnotationService.initialize; EXCEPTION", err); 129 } 130 }, 131 132 flush: function() 133 { 134 try 135 { 136 // Initialize output stream. 137 var outputStream = Cc["@mozilla.org/network/file-output-stream;1"] 138 .createInstance(Ci.nsIFileOutputStream); 139 outputStream.init(this.file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate 140 141 // Convert data to JSON. 142 var arr = []; 143 for (var uri in this.annotations) 144 arr.push({ 145 uri: uri, 146 value: this.annotations[uri] 147 }); 148 149 var nativeJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); 150 var jsonString = nativeJSON.encode(arr); 151 152 // Store annotations. 153 outputStream.write(jsonString, jsonString.length); 154 outputStream.close(); 155 156 if (FBTrace.DBG_ANNOTATIONS) 157 FBTrace.sysout("AnnotationService.initialize; Annotations stored to " + 158 this.file.path, jsonString); 159 } 160 catch (err) 161 { 162 if (FBTrace.DBG_ERRORS || FBTrace.DBG_ANNOTATIONS) 163 FBTrace.sysout("AnnotationService.flush; EXCEPTION", err); 164 } 165 }, 166 167 /* nsISupports */ 168 QueryInterface: function(iid) 169 { 170 if (iid.equals(Ci.nsISupports)) 171 return this; 172 173 throw Cr.NS_ERROR_NO_INTERFACE; 174 } 175 }; 176 177 // ************************************************************************************************ 178 // Service factory 179 180 var gServiceSingleton = null; 181 var AnnotationsFactory = 182 { 183 createInstance: function (outer, iid) 184 { 185 if (outer != null) 186 throw Cr.NS_ERROR_NO_AGGREGATION; 187 188 if (iid.equals(Ci.nsISupports)) 189 { 190 if (!gServiceSingleton) 191 gServiceSingleton = new AnnotationService(); 192 return gServiceSingleton.QueryInterface(iid); 193 } 194 195 throw Cr.NS_ERROR_NO_INTERFACE; 196 }, 197 198 QueryInterface: function(iid) 199 { 200 if (iid.equals(Ci.nsISupports) || 201 iid.equals(Ci.nsISupportsWeakReference) || 202 iid.equals(Ci.nsIFactory)) 203 return this; 204 205 throw Cr.NS_ERROR_NO_INTERFACE; 206 } 207 }; 208 209 // ************************************************************************************************ 210 // Module implementation 211 212 var AnnotationsModule = 213 { 214 registerSelf: function (compMgr, fileSpec, location, type) 215 { 216 compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar); 217 compMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, 218 CONTRACT_ID, fileSpec, location, type); 219 }, 220 221 unregisterSelf: function(compMgr, fileSpec, location) 222 { 223 compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar); 224 compMgr.unregisterFactoryLocation(CLASS_ID, location); 225 }, 226 227 getClassObject: function (compMgr, cid, iid) 228 { 229 if (!iid.equals(Ci.nsIFactory)) 230 throw Cr.NS_ERROR_NOT_IMPLEMENTED; 231 232 if (cid.equals(CLASS_ID)) 233 return AnnotationsFactory; 234 235 throw Cr.NS_ERROR_NO_INTERFACE; 236 }, 237 238 canUnload: function(compMgr) 239 { 240 return true; 241 } 242 }; 243 244 // ************************************************************************************************ 245 246 function NSGetModule(compMgr, fileSpec) 247 { 248 return AnnotationsModule; 249 } 250