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.
10 ////////////////////////////////////////////////////////////////////////////////
11 this.EXPORTED_SYMBOLS
= [];
13 const {utils
:Cu
, classes
:Cc
, interfaces
:Ci
, results
:Cr
} = Components
;
16 ////////////////////////////////////////////////////////////////////////////////
17 // addonName: used in `alert()`
18 function initAddonInternal (global
, addonName
, urlMain
, urlJS
) {
19 const {utils
:Cu
, classes
:Cc
, interfaces
:Ci
, results
:Cr
} = Components
;
21 const promptSvc
= Cc
["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci
.nsIPromptService
);
22 const appInfo
= Cc
["@mozilla.org/xre/app-info;1"].getService(Ci
.nsIXULAppInfo
);
24 case "{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}": break;
25 case "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}": promptSvc
.alert(null, addonName
, "Ff is not supported"); return null;
26 case "{3550f703-e582-4d05-9a08-453d09bdfdc6}": promptSvc
.alert(null, addonName
, "Thunderbird is not supported"); return null;
27 case "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}": promptSvc
.alert(null, addonName
, "Seamonkey is not supported"); return null;
28 default: promptSvc
.alert(null, addonName
, "Unknown application "+appInfo
.ID
+" is not supported"); return null;
31 // bootstrapped addons has no `window`
32 //const principal = (typeof(window) !== "undefined" ? window : Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal));
33 let gsbox
= new Cu
.Sandbox(Cc
["@mozilla.org/systemprincipal;1"].createInstance(Ci
.nsIPrincipal
), {
34 sandboxName
: addonName
+" Internal Sandbox",
35 wantComponents
: false,
37 wantXHRConstructor
: true,
39 Cu
.import("resource://gre/modules/Services.jsm", gsbox
);
41 // bootstrapped addons has no `window`
42 if (typeof(window
) !== "undefined") {
43 gsbox
.window
= window
;
44 gsbox
.document
= window
.document
;
47 for (let name
of ["CSS","indexedDB","XMLHttpRequest","TextEncoder","TextDecoder","URL","URLSearchParams","atob","btoa","Blob","File"]) {
49 //Components.utils.reportError("exporting '"+name+"'");
50 gsbox
[name
] = global
[name
];
54 // add some common API
55 Object
.defineProperty(gsbox
, "jscodeUrl", {get: function () urlJS
});
56 Object
.defineProperty(gsbox
, "contentUrl", {get: function () urlMain
});
58 // `tieto` API: ties method to object, so it always has correct `this`
59 // you may specify additional arguments that will be always passed unchanged
60 function tieto (obj
, method
) {
62 if (typeof(obj
) === "undefined") throw new Error("object or `null` expected");
63 if (obj
!== null && typeof(obj
) !== "object") throw new Error("object or `null` expected");
64 switch (typeof(method
)) {
66 if (!obj
) throw new Error("can't take method by name from `null` object");
67 if (!(method
in obj
)) throw new Error("method '"+method
+"' doesn't exist in object '"+obj
+"'");
69 if (typeof(mt
) !== "function") throw new Error("method '"+method
+"' isn't a function in object '"+obj
+"'");
76 throw new Error("`method` should be function or method name");
78 // hack: replace first array item; avoiding extra arrays
79 let rest
= Array
.prototype.splice
.call(arguments
, 1, arguments
.length
);
82 return mt
.bind
.apply(mt
, rest
);
85 const sb_tieto
= tieto(gsbox
, tieto
);
86 Object
.defineProperty(gsbox
, "tieto", {get: function () sb_tieto
});
88 const sb_Components
= tieto(gsbox
, function (c
) c
, Components
);
89 const sb_Cu
= tieto(gsbox
, function (cu
) cu
, Cu
);
90 const sb_Cc
= tieto(gsbox
, function (cc
) cc
, Cc
);
91 const sb_Ci
= tieto(gsbox
, function (ci
) ci
, Ci
);
92 const sb_Cr
= tieto(gsbox
, function (cr
) cr
, Cr
);
94 Object
.defineProperty(gsbox
, "Components", {get: function () sb_Components()});
95 Object
.defineProperty(gsbox
, "Cu", {get: function () sb_Cu()});
96 Object
.defineProperty(gsbox
, "Cc", {get: function () sb_Cc()});
97 Object
.defineProperty(gsbox
, "Ci", {get: function () sb_Ci()});
98 Object
.defineProperty(gsbox
, "Cr", {get: function () sb_Cr()});
100 // console log functions
101 function logErr (str
) Cu
.reportError(str
);
102 const sb_logError
= tieto(gsbox
, function (logErrMsg
) {
103 if (arguments
.length
> 1) {
105 for (let idx
= 1; idx
< arguments
.length
; ++idx
) s
+= ""+arguments
[idx
];
109 Object
.defineProperty(gsbox
, "logError", {get: function () sb_logError
});
111 const sb_logException
= tieto(gsbox
, function (logErrMsg
, msg
, e
) {
112 if (typeof(e
) === "undefined" && typeof(msg
) === "object") {
116 if (typeof(e
) !== "undefined" /*&& e instanceof global.Error*/ && e
) {
117 logErrMsg(""+msg
+" ERROR: "+e
.name
+": "+e
.message
+" : "+e
.lineNumber
);
118 if (e
.stack
) logErrMsg(e
.stack
);
122 if (typeof(e
) !== "undefined" && e
) logErrMsg("e="+e
);
125 Object
.defineProperty(gsbox
, "logException", {get: function () sb_logException
});
127 // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIConsoleService#logStringMessage() - wstring / wide string
128 const conService
= Cc
["@mozilla.org/consoleservice;1"].getService(Ci
.nsIConsoleService
);
129 function logStr (str
) conService
.logStringMessage(str
);
131 const sb_conlog
= tieto(gsbox
, function (logStrMsg
) {
132 if (arguments
.length
> 1) {
133 let le
= ("addonOptions" in this ? this["addonOptions"].logEnabled
: true);
136 for (let idx
= 1; idx
< arguments
.length
; ++idx
) s
+= ""+arguments
[idx
];
140 Object
.defineProperty(gsbox
, "conlog", {get: function () sb_conlog
});
142 const sb_debuglog
= tieto(gsbox
, function (logStrMsg
) {
143 if (arguments
.length
> 1) {
144 if ("addonOptions" in this) {
145 let ao
= this["addonOptions"];
146 //if (("logEnabled" in ao) && !ao.logEnabled) return;
147 if (!("debugMode" in ao
) || !ao
.debugMode
) return;
150 for (let idx
= 1; idx
< arguments
.length
; ++idx
) s
+= ""+arguments
[idx
];
154 Object
.defineProperty(gsbox
, "debuglog", {get: function () sb_debuglog
});
156 const sb_print
= tieto(gsbox
, function (logStrMsg
) {
157 if (arguments
.length
> 1) {
159 for (let idx
= 1; idx
< arguments
.length
; ++idx
) {
160 let t
= ""+arguments
[idx
];
163 logStrMsg(s
.substr(1));
166 Object
.defineProperty(gsbox
, "print", {get: function () sb_print
});
169 const newURI
= Cc
["@mozilla.org/network/io-service;1"].getService(Ci
.nsIIOService
).newURI
;
170 //const newFileURI = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService).newFileURI;
171 const loadSubScript
= Cc
["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci
.mozIJSSubScriptLoader
).loadSubScript
;
173 const specsSchemes
= [
174 { re
: /^mainjs:(.+)$/, pfx
: urlJS
},
175 { re
: /^main:(.+)$/, pfx
: urlMain
}
177 function toFullUrl (scriptSpec
, name
) {
178 if (/^(?:chrome|resource|file):/.test(name
)) return name
;
179 for (let sp
of specsSchemes
) {
180 let mt
= name
.match(sp
.re
);
181 if (mt
) return sp
.pfx
+mt
[1];
183 return scriptSpec
+name
;
186 // include("incname")
187 const sb_include
= tieto(gsbox
, function (scriptSpec
, newURI
, loadSubScript
, reportError
, name
) {
188 if (name
.length
== 0) throw new Error("invalid include name: '"+name
+"'");
189 if (!(/.\.js$/.test(name
))) name
+= ".js";
190 let fullUrl
= toFullUrl(scriptSpec
, name
);
192 let uri
= newURI(fullUrl
, null, null);
193 loadSubScript(uri
.spec
, gsbox
, "UTF-8");
195 if (typeof(e
) === "object") {
196 reportError("INCLUDE ERROR'"+name
+"': "+e
.name
+": "+e
.message
+" : "+e
.lineNumber
+"\n"+e
.stack
);
199 reportError("INCLUDE ERROR '"+name
+"': "+e
);
203 }, urlJS
+"includes/", newURI
, loadSubScript
, Cu
.reportError
);
204 Object
.defineProperty(gsbox
, "include", {get: function () sb_include
});
207 function loadTextContentsFromUrl (url
, encoding
) {
208 if (typeof(encoding
) !== "string") encoding
= "UTF-8"; else encoding
= encoding
||"UTF-8";
210 function toUnicode (text
) {
211 if (typeof(text
) === "string") {
212 if (text
.length
== 0) return "";
213 } else if (text
instanceof ArrayBuffer
) {
214 if (text
.byteLength
== 0) return "";
215 text
= new Uint8Array(text
);
216 return new TextDecoder(encoding
).decode(text
);
217 } else if ((text
instanceof Uint8Array
) || (text
instanceof Int8Array
)) {
218 if (text
.length
== 0) return "";
219 return new TextDecoder(encoding
).decode(text
);
223 let converter
= Cc
["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci
.nsIScriptableUnicodeConverter
);
224 converter
.charset
= encoding
;
225 let res
= converter
.ConvertToUnicode(text
);
226 if (res
.length
>= 3 && res
.substr(0, 3) == "\u00EF\u00BB\u00BF") res
= res
.substr(3); // fuck BOM
231 let req
= Cc
["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
232 req
.mozBackgroundRequest
= true;
233 req
.open("GET", url
, false);
234 req
.timeout
= 30*1000;
235 const cirq
= Ci
.nsIRequest
;
236 req
.channel
.loadFlags
|= cirq
.INHIBIT_CACHING
|cirq
.INHIBIT_PERSISTENT_CACHING
|cirq
.LOAD_BYPASS_CACHE
|cirq
.LOAD_ANONYMOUS
;
237 req
.responseType
= "arraybuffer";
238 //if (sendCookies) addCookies(req, urlsend);
240 req
.onerror = function () { error
= true; }; // just in case
241 req
.ontimeout = function () { error
= true; }; // just in case
244 error
= (req
.status
!= 0 && Math
.floor(req
.status
/100) != 2);
245 //(/^file:/.test(file.spec) ? (req.status != 0) : (Math.floor(req.status/100) != 2));
247 if (error
) throw new Error("can't load URI contents: status="+req
.status
+"; error="+error
);
248 return toUnicode(req
.response
, encoding
);
252 let modules
= {}; // list of loaded modules
254 // require("modname")
255 const sb_require
= tieto(gsbox
, function (scriptSpec
, newURI
, loadSubScript
, reportError
, import_
, modules
, name
) {
256 if (name
.length
== 0) throw new Error("invalid include name: '"+name
+"'");
257 if (!(/.\.js$/.test(name
))) name
+= ".js";
258 if (name
in modules
) return modules
[name
];
259 let fullUrl
= toFullUrl(scriptSpec
, name
);
260 //else sb_debuglog("requiring '", name, "' : [", fullUrl, "]");
262 let scope
= Object
.create(gsbox
);
264 scope
.exportsGlobal
= {};
267 let uri
= newURI(fullUrl
, null, null);
269 let box
= new Cu
.Sandbox(Cc
["@mozilla.org/systemprincipal;1"].createInstance(Ci
.nsIPrincipal
), {
270 wantComponents
: false,
272 wantXHRConstructor
: true,
273 sandboxPrototype
: scope
,
276 // uses(name[, name]...);
277 let sc_uses
= tieto(box
, function (scope
, import_
) {
278 if (arguments
.length
> 3) {
279 for (let idx
= 2; idx
< arguments
.length
; ++idx
) import_(arguments
[idx
], scope
);
281 } else if (arguments
.length
== 3) {
282 return import_(arguments
[2], scope
);
286 }, scope
, Cu
.import);
287 Object
.defineProperty(box
, "uses", {get: function () sc_uses
});
289 // include("incname")
290 let sc_include
= tieto(box
, function (scriptSpec
, newURI
, loadSubScript
, reportError
, box
, name
) {
291 if (name
.length
== 0) throw new Error("invalid include name: '"+name
+"'");
292 if (!(/.\.js$/.test(name
))) name
+= ".js";
293 let fullUrl
= toFullUrl(scriptSpec
, name
);
295 let uri
= newURI(fullUrl
, null, null);
296 loadSubScript(uri
.spec
, box
, "UTF-8");
298 if (typeof(e
) === "object") {
299 reportError("INCLUDE ERROR'"+name
+"': "+e
.name
+": "+e
.message
+" : "+e
.lineNumber
+"\n"+e
.stack
);
302 reportError("INCLUDE ERROR '"+name
+"': "+e
);
306 }, urlJS
+"includes/", newURI
, loadSubScript
, Cu
.reportError
, box
);
307 Object
.defineProperty(box
, "include", {get: function () sc_include
});
309 let text
= loadTextContentsFromUrl(uri
.spec
);
310 Cu
.evalInSandbox(text
, box
, "ECMAv5", name
, 1);
312 if (typeof(e
) === "object") {
313 reportError("REQUIRE ERROR'"+name
+"': "+e
.name
+": "+e
.message
+" : "+e
.lineNumber
+"\n"+e
.stack
);
316 reportError("REQUIRE ERROR '"+name
+"': "+e
);
321 modules
[name
] = scope
.exports
;
322 if (typeof(scope
.exportsGlobal
) === "object") {
323 for (let k
in scope
.exportsGlobal
) {
324 if (k
!== "__exposedProps__" && Object
.prototype.hasOwnProperty
.call(scope
.exportsGlobal
, k
)) {
325 try { gsbox
[k
] = scope
.exportsGlobal
[k
]; } catch (e
) {}
329 return scope
.exports
;
330 }, urlJS
+"modules/", newURI
, loadSubScript
, Cu
.reportError
, Cu
.import, modules
);
331 Object
.defineProperty(gsbox
, "require", {get: function () sb_require
});
335 let sb_alert
= tieto(gsbox
, function (promptSvc
, addonName
, msg
) {
336 promptSvc
.alert(null, addonName
, ""+msg
);
337 }, promptSvc
, addonName
);
338 Object
.defineProperty(gsbox
, "alert", {get: function () sb_alert
});
341 let startupHooks
= [], shutdownHooks
= [];
342 let winloadHooks
= [], winunloadHooks
= [];
344 let mainLoaded
= false;
346 function runHooks (hooktype
, list
, rev
, firsttime
) {
347 let rest
= Array
.prototype.splice
.call(arguments
, 4, arguments
.length
);
348 let callmain
= false;
350 //Cu.reportError("runHooks ("+hooktype+"): first time");
352 //Cu.reportError("runHooks ("+hooktype+"): main not loaded");
353 // now load main file
355 let loadmain
= ("__dontLoadMainJS" in global
? !global
.__dontLoadMainJS
: true);
357 //Cu.reportError("runHooks ("+hooktype+"): loading main...");
358 //Cu.reportError("runHooks ("+hooktype+"): len="+list.length);
359 gsbox
.include("mainjs:prefs/prefs.js"); // define default extension preferences
360 gsbox
.require("mainjs:prefsengine.js");
362 //Cu.reportError("runHooks ("+hooktype+"): len="+list.length);
363 gsbox
.include("mainjs:main.js");
367 if (hooktype
=== "shutdown" && typeof(gsbox
.main_preshutdown
) === "function") {
369 gsbox
.main_preshutdown();
371 Cu
.reportError("PRESHUTDOWN ERROR: "+e
.name
+": "+e
.message
+" : "+e
.lineNumber
);
372 if (e
.stack
) Cu
.reportError(e
.stack
);
376 let idx
= (rev
? list
.length
: -1);
379 if (--idx
< 0) break;
381 if (++idx
>= list
.length
) break;
385 //conService.logStringMessage("running hook: '"+h.name+"'");
386 let args
= Array
.prototype.slice
.call(rest
);
387 //Array.prototype.push.apply(args, arguments);
388 h
.cback
.apply(gsbox
, args
);
391 Cu
.reportError("HOOK '"+hooktype
+"' ("+h
.name
+") ERROR: "+e
.name
+": "+e
.message
+" : "+e
.lineNumber
+(e
.stack
? "\n"+e
.stack
: ""));
393 if (e
.stack
) Cu
.reportError(e
.stack
);
396 if (callmain
&& typeof(gsbox
.main_postinit
) === "function") gsbox
.main_postinit();
399 function addHook (hooks
, name
, cback
) {
400 if (typeof(cback
) === "undefined") {
401 // register*Hook(hookfn)
405 if (typeof(name
) === "undefined") throw new Error("name or `null` expected");
406 if (!name
) name
= "";
407 if (typeof(name
) !== "string") throw new Error("name or `null` expected");
408 if (typeof(cback
) !== "function") throw new Error("function expected");
409 for (let obj
in hooks
) if (obj
.cback
=== cback
) return; // nothing to do
410 hooks
.push({name
:name
, cback
:cback
});
413 const sb_rsth
= tieto(gsbox
, addHook
, startupHooks
);
414 const sb_rsuh
= tieto(gsbox
, addHook
, shutdownHooks
);
416 // registerStartupHook(name, hookfn)
417 // registerShutdownHook(name, hookfn)
418 Object
.defineProperty(gsbox
, "registerStartupHook", {get: function () sb_rsth
});
419 Object
.defineProperty(gsbox
, "registerShutdownHook", {get: function () sb_rsuh
});
421 const sb_rwlh
= tieto(gsbox
, addHook
, winloadHooks
);
422 const sb_rwuh
= tieto(gsbox
, addHook
, winunloadHooks
);
424 // registerWindowLoadHook(name, hookfn)
425 // registerWindowUnloadHook(name, hookfn)
426 Object
.defineProperty(gsbox
, "registerWindowLoadHook", {get: function () sb_rwlh
});
427 Object
.defineProperty(gsbox
, "registerWindowUnloadHook", {get: function () sb_rwuh
});
432 defProp
: tieto(this, function (mainobj
, name
, ops
) {
433 //Cu.reportError("GS: defining property '"+name+"'");
434 mainobj
.defineProperty(gsbox
, name
, ops
);
436 //onInstall: function () {},
437 //onUnistall: function () {},
438 onStartup
: tieto(this, runHooks
, "startup", startupHooks
, false, true),
439 onShutdown
: tieto(this, runHooks
, "shutdown", shutdownHooks
, true, false),
440 onWindowLoad
: tieto(this, runHooks
, "window opened", winloadHooks
, true, false),
441 onWindowUnload
: tieto(this, runHooks
, "window closed", winunloadHooks
, true, false),
446 ////////////////////////////////////////////////////////////////////////////////
447 const addonName
= "Guerilla Script"; // addon name, used in `alert()`
448 const addonContentUrl
= "chrome://guerilla-script/content/"; // content URL, must end with "/"
449 const addonJSUrl
= "chrome://guerilla-script-jscode/content/"; // addon js code URL, must end with "/"
454 ////////////////////////////////////////////////////////////////////////////////
455 function initAddon (global
) {
457 addonobj
= initAddonInternal(global
, addonName
, addonContentUrl
, addonJSUrl
);
458 if (!addonobj
) throw new Error("can't init addon '"+addonName
+"'"); // alas, something is wrong
459 addonobj
.defProp("gsdoxUrl", {get: function () "chrome://guerilla-script-dox/content/"});
460 addonobj
.onStartup();
462 if (addonobj
) try { addonobj
.onShutdown(); } catch (xx
) {}
464 Cu
.reportError(e
.stack
);
471 ////////////////////////////////////////////////////////////////////////////////
474 function newWindow (global
, win
) {
475 if (addonobj
=== null) initAddon(global
);
477 win
.addEventListener("load", function () {
480 global
.window
.addEventListener("unload", function () {
482 addonobj
.onWindowUnload(win
);
483 if (windowCount
== 0) {
484 // no more registered windows --> shutdown
485 addonobj
.onShutdown();
488 addonobj
.onWindowLoad(win
);
494 ////////////////////////////////////////////////////////////////////////////////
495 function getAddonObj () addonobj
;