1 /* coded by Ketmar // Invisible Vector (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://www.wtfpl.net/txt/copying/ for more details.
11 let EXPORTED_SYMBOLS
= [
15 const {utils
:Cu
, classes
:Cc
, interfaces
:Ci
, results
:Cr
} = Components
;
18 //////////////////////////////////////////////////////////////////////////////
19 Cu
.import("resource://gre/modules/Services.jsm");
21 Cu
.import("chrome://k8-imago-code/content/modules/utils.js");
22 Cu
.import(MODULE_PATH
+"debuglog.js");
23 Cu
.import(MODULE_PATH
+"prefs.js");
24 Cu
.import(MODULE_PATH
+"detector.js");
25 Cu
.import(MODULE_PATH
+"hazard.js");
26 Cu
.import(MODULE_PATH
+"stoplist.js");
29 //////////////////////////////////////////////////////////////////////////////
30 // this listener will collect first several kb of image, detect it's type and
31 // size, and then will decide what to do with it (image, not size)
35 function ImgDetectListener (isoctet
, blacklisted
) {
37 this.receivedData
= ""; // string, incoming data; will become 0
38 this.cancelled
= false;
40 this.stopSent
= false;
41 this.octets
= !!isoctet
;
42 this.blacklisted
= !!blacklisted
;
43 this.maxLength
= PREFS
.maxLength
;
44 this.checker
= new FormatChecker();
46 this.imageInfo
= null;
50 ImgDetectListener
.prototype = {
52 if (!PREFS
.debugLog
) return;
53 if (arguments
.length
) {
54 let s
= ""+this.id
+":: ";
55 for (let idx
= 0; idx
< arguments
.length
; ++idx
) s
+= arguments
[idx
];
56 Services
.console
.logStringMessage(s
);
60 logError: function () {
61 if (!PREFS
.debugLog
) return;
62 if (arguments
.length
) {
63 let s
= ""+this.id
+":: ";
64 for (let idx
= 0; idx
< arguments
.length
; ++idx
) s
+= arguments
[idx
];
69 _markImage: function (request
) {
70 let srcchan
= request
.QueryInterface(Ci
.nsIChannel
);
71 let url
= srcchan
.URI
.spec
;
72 let dw
= getDomWindowForChannel(srcchan
);
76 title
= this.imageInfo
.name
+" "+(this.imageInfo
.valid
? ""+this.imageInfo
.width
+"x"+this.imageInfo
.height
: "<INVALID>");
78 title
= (this.blacklisted
? "<BLACKLISTED>" : "<INVALID>");
80 for (let img
of dw
.document
.querySelectorAll("img:not([k8imago-mark])[src=\""+cssEscape(url
)+"\"]")) {
81 img
.setAttribute("title", title
);
82 img
.setAttribute("k8imago-mark", "tan");
88 _cancel: function (request
, context
, total
, data
) {
89 if (typeof(total
) !== "number") total
= "???";
91 this.cancelled
= true;
92 this.receivedData
= null; // just in case
94 this._markImage(request
);
96 let srcchan
= request
.QueryInterface(Ci
.nsIChannel
);
97 let url
= srcchan
.URI
.spec
;
98 this.log("[", url
, "] total: ", total
, ": CANCELLING!");
100 if (typeof(data
) === "string") {
101 if (PREFS
.showPlaceholder
) {
102 this.log(" sending HAZARD");
103 this.olst
.onDataAvailable(request
, context
, createInputStreamFromString(data
), 0, data
.length
);
105 this.log(" sending HAZARD is blocked");
107 this.stopSent
= true;
108 this.olst
.onStopRequest(request
, context
, 0);
110 // just in case that double cancel will throw
112 request
.cancel(Cr
.NS_BINDING_ABORTED
);
116 _pipe: function (request
, context
) {
118 this.receivedData
= null;
122 url: function (request
) {
123 if (!request
) return "<no-url>";
124 let srcchan
= request
.QueryInterface(Ci
.nsIChannel
);
125 if (!srcchan
) return "<no-url>";
126 return srcchan
.URI
.spec
;
129 // object initialization finished
130 inited: function () {
133 QueryInterface: function (aIID
) {
134 if (aIID
.equals(Ci
.nsIStreamListener
) || aIID
.equals(Ci
.nsISupports
)) return this;
135 throw Components
.results
.NS_NOINTERFACE
;
138 onDataAvailable: function (request
, context
, inputStream
, offset
, count
) {
139 if (this.cancelled
) return;
141 if (!this.piping
&& PREFS
.debugLog
) this.log("[", this.url(request
), "] offset=", offset
, "; count=", count
);
143 // if we aren't piping...
145 // ...and there's no more formats...
146 if (this.checker
.done
) {
148 if (!PREFS
.allowUnknownFormats
) {
149 this._cancel(request
, context
, offset
+count
, hazardPNG
);
152 // send collected data
153 if (this.receivedData
) {
154 if (this.receivedData
.length
) {
155 if (offset
== 0) this.logError("WTF000?! offset is zero, but we have receivedData!");
156 this.olst
.onDataAvailable(request
, context
, createInputStreamFromString(this.receivedData
), 0, this.receivedData
.length
);
157 if (offset
!= this.receivedData
.length
) this.logError("WTF001?! offset is not equal to receivedData length! offset=", offset
, "; length=", this.receivedData
.length
);
159 } else if (offset
> 0) {
160 this.logError("WTF002?! offset is not zero, but we have no receivedData!");
162 this._pipe(request
, context
);
167 // pipe data if we are piping
170 if (this.maxLength
> 0 && !this.octets
) {
171 if (offset
+count
> this.maxLength
) {
172 this._cancel(request
, context
, offset
+count
);
176 this.olst
.onDataAvailable(request
, context
, inputStream
, offset
, count
);
180 // collecting header data and analyzing image formats
181 let ist
= Cc
["@mozilla.org/binaryinputstream;1"].createInstance(Ci
.nsIBinaryInputStream
);
182 ist
.setInputStream(inputStream
);
184 // read up to `this.checker.maxHeaderBytes` kb
185 let maxb
= this.checker
.maxHeaderBytes
;
186 if (this.receivedData
.length
+count
< maxb
) {
187 this.log(" reading ", count
, " bytes (maxb=", maxb
, ")");
188 this.receivedData
+= ist
.readBytes(count
);
191 let left
= maxb
-this.receivedData
.length
;
193 Cu
.reportError("internal error (something is heavily fucked, #000)");
194 throw new Error("internal error (something is heavily fucked, #000)");
197 this.receivedData
+= ist
.readBytes(left
);
200 this.log(" read ", left
, " bytes, ", count
, " bytes left");
203 let res
= this.checker
.process(this.receivedData
);
204 this.log(" checker returned `", res
, "`, done is: ", this.checker
.done
);
206 // no successfull detection yet, need more spice
207 if (this.receivedData
.length
>= maxb
) this._cancel(request
, context
, offset
+count
, hazardPNG
);
211 // detected something
212 this.imageInfo
= res
;
214 this._cancel(request
, context
, offset
+count
, hazardPNG
);
217 this.log(" image: ", res
.name
, ": ", res
.width
, "x", res
.height
);
218 // valid image, check dimensions
219 if (res
.width
< PREFS
.minWidth
|| res
.height
< PREFS
.minHeight
||
220 res
.width
> PREFS
.maxWidth
|| res
.height
> PREFS
.maxHeight
)
222 this.log(" allowed size: min=", PREFS
.minWidth
, "x", PREFS
.minHeight
, "; max=", PREFS
.maxWidth
, "x", PREFS
.maxHeight
);
223 this._cancel(request
, context
, offset
+count
, hazardPNG
);
228 if (!PREFS
.allowUnknownFormats
) {
229 this.log(" unknown format");
230 this._cancel(request
, context
, offset
+count
, hazardPNG
);
235 // here we got something good, transfer collected header and start piping
236 this.olst
.onDataAvailable(request
, context
, createInputStreamFromString(this.receivedData
), 0, this.receivedData
.length
);
239 offset
+= this.receivedData
.length
;
240 this.olst
.onDataAvailable(request
, context
, inputStream
, offset
, count
);
242 this._pipe(request
, context
);
245 onStartRequest: function (request
, context
) {
246 if (PREFS
.debugLog
) this.log("onStartRequest() [", this.url(request
), "]");
247 this.olst
.onStartRequest(request
, context
);
248 if (this.blacklisted
) {
249 this.log("image is blacklisted");
250 this._cancel(request
, context
, 0, hazardPNG
);
255 onStopRequest: function (request
, context
, statusCode
) {
256 if (PREFS
.debugLog
) this.log("onStopRequest() [", this.url(request
), "]: status code=", statusCode
);
257 //this.log("[", this.url(request), "]: status code=", statusCode);
258 if (!this.stopSent
&& this.receivedData
!== null) {
259 // wow, we have some buffered data, check and send it!
260 let res
= this.checker
.process(this.receivedData
);
261 if (typeof(res
) === "object") {
263 this._cancel(request
, context
, 0, hazardPNG
);
266 if (res
.width
< PREFS
.minWidth
|| res
.height
< PREFS
.minHeight
||
267 res
.width
> PREFS
.maxWidth
|| res
.height
> PREFS
.maxHeight
) {
268 this._cancel(request
, context
, 0, hazardPNG
);
273 if (!PREFS
.allowUnknownFormats
) {
274 this._cancel(request
, context
, 0, hazardPNG
);
278 this.olst
.onDataAvailable(request
, context
, createInputStreamFromString(this.receivedData
), 0, this.receivedData
.length
);
280 this.receivedData
= null; // just in case
282 if (!this.stopSent
) {
283 this.olst
.onStopRequest(request
, context
, statusCode
);